From fa7c1d5080f6e7dc4428210b6eac60271f899908 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Tue, 3 Oct 2017 18:17:40 +0200 Subject: vfs: factor sb iteration out of do_emergency_remount The intent is to reduce code duplication with other code iterating the list. No functional changes. Signed-off-by: Mateusz Guzik Signed-off-by: Al Viro --- fs/super.c | 61 ++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/fs/super.c b/fs/super.c index 166c4ee0d0ed..fd9c02f543eb 100644 --- a/fs/super.c +++ b/fs/super.c @@ -574,6 +574,28 @@ void drop_super_exclusive(struct super_block *sb) } EXPORT_SYMBOL(drop_super_exclusive); +static void __iterate_supers(void (*f)(struct super_block *)) +{ + struct super_block *sb, *p = NULL; + + spin_lock(&sb_lock); + list_for_each_entry(sb, &super_blocks, s_list) { + if (hlist_unhashed(&sb->s_instances)) + continue; + sb->s_count++; + spin_unlock(&sb_lock); + + f(sb); + + spin_lock(&sb_lock); + if (p) + __put_super(p); + p = sb; + } + if (p) + __put_super(p); + spin_unlock(&sb_lock); +} /** * iterate_supers - call function for all active superblocks * @f: function to call @@ -881,33 +903,22 @@ cancel_readonly: return retval; } -static void do_emergency_remount(struct work_struct *work) +static void do_emergency_remount_callback(struct super_block *sb) { - struct super_block *sb, *p = NULL; - - spin_lock(&sb_lock); - list_for_each_entry(sb, &super_blocks, s_list) { - if (hlist_unhashed(&sb->s_instances)) - continue; - sb->s_count++; - spin_unlock(&sb_lock); - down_write(&sb->s_umount); - if (sb->s_root && sb->s_bdev && (sb->s_flags & SB_BORN) && - !sb_rdonly(sb)) { - /* - * What lock protects sb->s_flags?? - */ - do_remount_sb(sb, SB_RDONLY, NULL, 1); - } - up_write(&sb->s_umount); - spin_lock(&sb_lock); - if (p) - __put_super(p); - p = sb; + down_write(&sb->s_umount); + if (sb->s_root && sb->s_bdev && (sb->s_flags & SB_BORN) && + !sb_rdonly(sb)) { + /* + * What lock protects sb->s_flags?? + */ + do_remount_sb(sb, SB_RDONLY, NULL, 1); } - if (p) - __put_super(p); - spin_unlock(&sb_lock); + up_write(&sb->s_umount); +} + +static void do_emergency_remount(struct work_struct *work) +{ + __iterate_supers(do_emergency_remount_callback); kfree(work); printk("Emergency Remount complete\n"); } -- cgit v1.3-14-g43fede From 0296c248b440fe0ae3f08c5c2fcded795c59353e Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 12 Dec 2017 12:10:36 +0000 Subject: misc: kgdbts: Display progress of asynchronous tests kgdbts includes a couple of different "thrashing" style tests that may have long runtimes (especially on simulated platforms) and which run asynchronously. This is uncomfortable for interactive use and makes setting timeouts tricky for automatic use. Fix by providing a optional means to show progress during these tests. Selecting 100 is somewhat arbitrary but it matches the step used on the synchronous tests, is large enough to keep the call to printk from invalidating the testing and is human enough to "feel about right". Signed-off-by: Daniel Thompson Signed-off-by: Jason Wessel --- drivers/misc/kgdbts.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 24108bfad889..6193270e7b3d 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -400,10 +400,14 @@ static void skip_back_repeat_test(char *arg) int go_back = simple_strtol(arg, NULL, 10); repeat_test--; - if (repeat_test <= 0) + if (repeat_test <= 0) { ts.idx++; - else + } else { + if (repeat_test % 100 == 0) + v1printk("kgdbts:RUN ... %d remaining\n", repeat_test); + ts.idx -= go_back; + } fill_get_buf(ts.tst[ts.idx].get); } -- cgit v1.3-14-g43fede From 6909e29fdefbb7aa643021279daef6ed10c81528 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 12 Oct 2017 16:06:11 +0200 Subject: kdb: use __ktime_get_real_seconds instead of __current_kernel_time kdb is the only user of the __current_kernel_time() interface, which is not y2038 safe and should be removed at some point. The kdb code also goes to great lengths to print the time in a human-readable format from 'struct timespec', again using a non-y2038-safe re-implementation of the generic time_to_tm() code. Using __current_kernel_time() here is necessary since the regular accessors that require a sequence lock might hang when called during the xtime update. However, this is safe in the particular case since kdb is only interested in the tv_sec field that is updated atomically. In order to make this y2038-safe, I'm converting the code to the generic time64_to_tm helper, but that introduces the problem that we have no interface like __current_kernel_time() that provides a 64-bit timestamp in a lockless, safe and architecture-independent way. I have multiple ideas for how to solve that: - __ktime_get_real_seconds() is lockless, but can return incorrect results on 32-bit architectures in the special case that we are in the process of changing the time across the epoch, either during the timer tick that overflows the seconds in 2038, or while calling settimeofday. - ktime_get_real_fast_ns() would work in this context, but does require a call into the clocksource driver to return a high-resolution timestamp. This may have undesired side-effects in the debugger, since we want to limit the interactions with the rest of the kernel. - Adding a ktime_get_real_fast_seconds() based on tk_fast_mono plus tkr->base_real without the tk_clock_read() delta. Not sure about the value of adding yet another interface here. - Changing the existing ktime_get_real_seconds() to use tk_fast_mono on 32-bit architectures rather than xtime_sec. I think this could work, but am not entirely sure if this is an improvement. I picked the first of those for simplicity here. It's technically not correct but probably good enough as the time is only used for the debugging output and the race will likely never be hit in practice. Another downside is having to move the declaration into a public header file. Let me know if anyone has a different preference. Cc: Andy Shevchenko Link: https://patchwork.kernel.org/patch/9775309/ Signed-off-by: Arnd Bergmann Signed-off-by: Jason Wessel --- include/linux/timekeeping.h | 1 + kernel/debug/kdb/kdb_main.c | 45 +++++--------------------------------- kernel/time/timekeeping_internal.h | 2 -- 3 files changed, 6 insertions(+), 42 deletions(-) diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index b17bcce58bc4..588a0e4b1ab9 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -31,6 +31,7 @@ struct timespec64 get_monotonic_coarse64(void); extern void getrawmonotonic64(struct timespec64 *ts); extern void ktime_get_ts64(struct timespec64 *ts); extern time64_t ktime_get_seconds(void); +extern time64_t __ktime_get_real_seconds(void); extern time64_t ktime_get_real_seconds(void); extern int __getnstimeofday64(struct timespec64 *tv); diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index c8146d53ca67..69e70f4021fe 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -2479,41 +2479,6 @@ static int kdb_kill(int argc, const char **argv) return 0; } -struct kdb_tm { - int tm_sec; /* seconds */ - int tm_min; /* minutes */ - int tm_hour; /* hours */ - int tm_mday; /* day of the month */ - int tm_mon; /* month */ - int tm_year; /* year */ -}; - -static void kdb_gmtime(struct timespec *tv, struct kdb_tm *tm) -{ - /* This will work from 1970-2099, 2100 is not a leap year */ - static int mon_day[] = { 31, 29, 31, 30, 31, 30, 31, - 31, 30, 31, 30, 31 }; - memset(tm, 0, sizeof(*tm)); - tm->tm_sec = tv->tv_sec % (24 * 60 * 60); - tm->tm_mday = tv->tv_sec / (24 * 60 * 60) + - (2 * 365 + 1); /* shift base from 1970 to 1968 */ - tm->tm_min = tm->tm_sec / 60 % 60; - tm->tm_hour = tm->tm_sec / 60 / 60; - tm->tm_sec = tm->tm_sec % 60; - tm->tm_year = 68 + 4*(tm->tm_mday / (4*365+1)); - tm->tm_mday %= (4*365+1); - mon_day[1] = 29; - while (tm->tm_mday >= mon_day[tm->tm_mon]) { - tm->tm_mday -= mon_day[tm->tm_mon]; - if (++tm->tm_mon == 12) { - tm->tm_mon = 0; - ++tm->tm_year; - mon_day[1] = 28; - } - } - ++tm->tm_mday; -} - /* * Most of this code has been lifted from kernel/timer.c::sys_sysinfo(). * I cannot call that code directly from kdb, it has an unconditional @@ -2539,8 +2504,8 @@ static void kdb_sysinfo(struct sysinfo *val) */ static int kdb_summary(int argc, const char **argv) { - struct timespec now; - struct kdb_tm tm; + time64_t now; + struct tm tm; struct sysinfo val; if (argc) @@ -2554,9 +2519,9 @@ static int kdb_summary(int argc, const char **argv) kdb_printf("domainname %s\n", init_uts_ns.name.domainname); kdb_printf("ccversion %s\n", __stringify(CCVERSION)); - now = __current_kernel_time(); - kdb_gmtime(&now, &tm); - kdb_printf("date %04d-%02d-%02d %02d:%02d:%02d " + now = __ktime_get_real_seconds(); + time64_to_tm(now, 0, &tm); + kdb_printf("date %04ld-%02d-%02d %02d:%02d:%02d " "tz_minuteswest %d\n", 1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, diff --git a/kernel/time/timekeeping_internal.h b/kernel/time/timekeeping_internal.h index fdbeeb02dde9..cf5c0828ee31 100644 --- a/kernel/time/timekeeping_internal.h +++ b/kernel/time/timekeeping_internal.h @@ -31,6 +31,4 @@ static inline u64 clocksource_delta(u64 now, u64 last, u64 mask) } #endif -extern time64_t __ktime_get_real_seconds(void); - #endif /* _TIMEKEEPING_INTERNAL_H */ -- cgit v1.3-14-g43fede From 1e0ce03bf142454f38a5fc050bf4fd698d2d36d8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 8 Dec 2017 10:19:19 -0800 Subject: kdb: make "mdr" command repeat The "mdr" command should repeat (continue) when only Enter/Return is pressed, so make it do so. Signed-off-by: Randy Dunlap Cc: Daniel Thompson Cc: Jason Wessel Cc: kgdb-bugreport@lists.sourceforge.net Signed-off-by: Jason Wessel --- kernel/debug/kdb/kdb_main.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 69e70f4021fe..ff6047d3b73f 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -1566,6 +1566,7 @@ static int kdb_md(int argc, const char **argv) int symbolic = 0; int valid = 0; int phys = 0; + int raw = 0; kdbgetintenv("MDCOUNT", &mdcount); kdbgetintenv("RADIX", &radix); @@ -1575,9 +1576,10 @@ static int kdb_md(int argc, const char **argv) repeat = mdcount * 16 / bytesperword; if (strcmp(argv[0], "mdr") == 0) { - if (argc != 2) + if (argc == 2 || (argc == 0 && last_addr != 0)) + valid = raw = 1; + else return KDB_ARGCOUNT; - valid = 1; } else if (isdigit(argv[0][2])) { bytesperword = (int)(argv[0][2] - '0'); if (bytesperword == 0) { @@ -1613,7 +1615,10 @@ static int kdb_md(int argc, const char **argv) radix = last_radix; bytesperword = last_bytesperword; repeat = last_repeat; - mdcount = ((repeat * bytesperword) + 15) / 16; + if (raw) + mdcount = repeat; + else + mdcount = ((repeat * bytesperword) + 15) / 16; } if (argc) { @@ -1630,7 +1635,10 @@ static int kdb_md(int argc, const char **argv) diag = kdbgetularg(argv[nextarg], &val); if (!diag) { mdcount = (int) val; - repeat = mdcount * 16 / bytesperword; + if (raw) + repeat = mdcount; + else + repeat = mdcount * 16 / bytesperword; } } if (argc >= nextarg+1) { @@ -1640,8 +1648,15 @@ static int kdb_md(int argc, const char **argv) } } - if (strcmp(argv[0], "mdr") == 0) - return kdb_mdr(addr, mdcount); + if (strcmp(argv[0], "mdr") == 0) { + int ret; + last_addr = addr; + ret = kdb_mdr(addr, mdcount); + last_addr += mdcount; + last_repeat = mdcount; + last_bytesperword = bytesperword; // to make REPEAT happy + return ret; + } switch (radix) { case 10: -- cgit v1.3-14-g43fede From b0f73bc7f1793997eb48bd14e3db51c3c95e2098 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 8 Dec 2017 10:19:23 -0800 Subject: kdb: drop newline in unknown command output When an unknown command is entered, kdb prints "Unknown kdb command:" and then the unknown text, including the newline character. This causes the ending single-quote mark to be printed on the next line by itself, so just change the ending newline character to a null character (end of string) so that it won't be "printed." Signed-off-by: Randy Dunlap Cc: Daniel Thompson Cc: Jason Wessel Cc: kgdb-bugreport@lists.sourceforge.net Signed-off-by: Jason Wessel --- kernel/debug/kdb/kdb_main.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index ff6047d3b73f..6055231544a0 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -1150,6 +1150,16 @@ void kdb_set_current_task(struct task_struct *p) kdb_current_regs = NULL; } +static void drop_newline(char *buf) +{ + size_t len = strlen(buf); + + if (len == 0) + return; + if (*(buf + len - 1) == '\n') + *(buf + len - 1) = '\0'; +} + /* * kdb_local - The main code for kdb. This routine is invoked on a * specific processor, it is not global. The main kdb() routine @@ -1327,6 +1337,7 @@ do_full_getstr: cmdptr = cmd_head; diag = kdb_parse(cmdbuf); if (diag == KDB_NOTFOUND) { + drop_newline(cmdbuf); kdb_printf("Unknown kdb command: '%s'\n", cmdbuf); diag = 0; } -- cgit v1.3-14-g43fede From 33f765f698895527acd69faf5d54ab07f02683ff Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 8 Dec 2017 10:19:28 -0800 Subject: kdb: bl: don't use tab character in output The "bl" (list breakpoints) command prints a '\t' (tab) character in its output, but on a console (video device), that just prints some odd graphics character. Instead of printing a tab character, just align the output with spaces. Signed-off-by: Randy Dunlap Cc: Daniel Thompson Cc: Jason Wessel Cc: kgdb-bugreport@lists.sourceforge.net Signed-off-by: Jason Wessel --- kernel/debug/kdb/kdb_bp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/debug/kdb/kdb_bp.c b/kernel/debug/kdb/kdb_bp.c index 90ff129c88a2..62c301ad0773 100644 --- a/kernel/debug/kdb/kdb_bp.c +++ b/kernel/debug/kdb/kdb_bp.c @@ -242,11 +242,11 @@ static void kdb_printbp(kdb_bp_t *bp, int i) kdb_symbol_print(bp->bp_addr, NULL, KDB_SP_DEFAULT); if (bp->bp_enabled) - kdb_printf("\n is enabled"); + kdb_printf("\n is enabled "); else kdb_printf("\n is disabled"); - kdb_printf("\taddr at %016lx, hardtype=%d installed=%d\n", + kdb_printf(" addr at %016lx, hardtype=%d installed=%d\n", bp->bp_addr, bp->bp_type, bp->bp_installed); kdb_printf("\n"); -- cgit v1.3-14-g43fede From 40b90efeae9be8702d387dbcbb3aadc57033d4db Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 29 Jan 2018 10:22:51 +0800 Subject: kdb: use ktime_get_mono_fast_ns() instead of ktime_get_ts() The kdb code will print the monotonic time by ktime_get_ts(), but the ktime_get_ts() will be protected by a sequence lock, that will introduce one deadlock risk if the lock was already held in the context from which we entered the debugger. Thus we can use the ktime_get_mono_fast_ns() to get the monotonic time, which is NMI safe access to clock monotonic. Moreover we can remove the 'struct timespec', which is not y2038 safe. Signed-off-by: Baolin Wang Reviewed-by: Daniel Thompson Reviewed-by: Arnd Bergmann Signed-off-by: Jason Wessel --- kernel/debug/kdb/kdb_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 6055231544a0..16140d1aa0c3 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -2512,10 +2512,10 @@ static int kdb_kill(int argc, const char **argv) */ static void kdb_sysinfo(struct sysinfo *val) { - struct timespec uptime; - ktime_get_ts(&uptime); + u64 uptime = ktime_get_mono_fast_ns(); + memset(val, 0, sizeof(*val)); - val->uptime = uptime.tv_sec; + val->uptime = div_u64(uptime, NSEC_PER_SEC); val->loads[0] = avenrun[0]; val->loads[1] = avenrun[1]; val->loads[2] = avenrun[2]; -- cgit v1.3-14-g43fede From 1ef8580539d0b9282b726a2c9b7aa25057040cfe Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 7 Feb 2017 17:07:44 -0800 Subject: Input: psmouse - create helper for reporting standard buttons/motion Many protocol driver re-implement code to parse buttons or motion data from the standard PS/2 protocol. Let's split the parsing into separate functions and reuse them in protocol drivers. Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 30 +++++++----------------------- drivers/input/mouse/elantech.c | 28 ++++++++++------------------ drivers/input/mouse/lifebook.c | 12 ++++-------- drivers/input/mouse/logips2pp.c | 10 ++++------ drivers/input/mouse/psmouse-base.c | 26 ++++++++++++++++++++------ drivers/input/mouse/psmouse.h | 4 ++++ drivers/input/mouse/sentelic.c | 11 +---------- 7 files changed, 50 insertions(+), 71 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index dbe57da8c1a1..f9c7f2452264 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -827,7 +827,7 @@ static void alps_process_packet_v6(struct psmouse *psmouse) unsigned char *packet = psmouse->packet; struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; - int x, y, z, left, right, middle; + int x, y, z; /* * We can use Byte5 to distinguish if the packet is from Touchpad @@ -847,9 +847,6 @@ static void alps_process_packet_v6(struct psmouse *psmouse) x = packet[1] | ((packet[3] & 0x20) << 2); y = packet[2] | ((packet[3] & 0x40) << 1); z = packet[4]; - left = packet[3] & 0x01; - right = packet[3] & 0x02; - middle = packet[3] & 0x04; /* To prevent the cursor jump when finger lifted */ if (x == 0x7F && y == 0x7F && z == 0x7F) @@ -859,9 +856,7 @@ static void alps_process_packet_v6(struct psmouse *psmouse) input_report_rel(dev2, REL_X, (char)x / 4); input_report_rel(dev2, REL_Y, -((char)y / 4)); - input_report_key(dev2, BTN_LEFT, left); - input_report_key(dev2, BTN_RIGHT, right); - input_report_key(dev2, BTN_MIDDLE, middle); + psmouse_report_standard_buttons(dev2, packet[3]); input_sync(dev2); return; @@ -871,8 +866,6 @@ static void alps_process_packet_v6(struct psmouse *psmouse) x = packet[1] | ((packet[3] & 0x78) << 4); y = packet[2] | ((packet[4] & 0x78) << 4); z = packet[5]; - left = packet[3] & 0x01; - right = packet[3] & 0x02; if (z > 30) input_report_key(dev, BTN_TOUCH, 1); @@ -888,8 +881,8 @@ static void alps_process_packet_v6(struct psmouse *psmouse) input_report_key(dev, BTN_TOOL_FINGER, z > 0); /* v6 touchpad does not have middle button */ - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); + packet[3] &= ~BIT(2); + psmouse_report_standard_buttons(dev2, packet[3]); input_sync(dev); } @@ -1098,7 +1091,7 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; struct input_dev *dev2 = priv->dev2; - int x, y, z, left, right, middle; + int x, y, z; /* It should be a DualPoint when received trackstick packet */ if (!(priv->flags & ALPS_DUALPOINT)) { @@ -1112,16 +1105,10 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) ((packet[3] & 0x20) << 1); z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1); - left = (packet[1] & 0x01); - right = (packet[1] & 0x02) >> 1; - middle = (packet[1] & 0x04) >> 2; - input_report_rel(dev2, REL_X, (char)x); input_report_rel(dev2, REL_Y, -((char)y)); - input_report_key(dev2, BTN_LEFT, left); - input_report_key(dev2, BTN_RIGHT, right); - input_report_key(dev2, BTN_MIDDLE, middle); + psmouse_report_standard_buttons(dev2, packet[1]); input_sync(dev2); } @@ -1503,10 +1490,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, alps_report_buttons(dev, dev2, packet[0] & 1, packet[0] & 2, packet[0] & 4); - input_report_rel(dev, REL_X, - packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0); - input_report_rel(dev, REL_Y, - packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0); + psmouse_report_standard_motion(dev, packet); input_sync(dev); } diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index a4aaa748e987..af7fc17c14d9 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -279,8 +279,8 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); - input_report_key(dev, BTN_LEFT, packet[0] & 0x01); - input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + + psmouse_report_standard_buttons(dev, packet[0]); if (etd->fw_version < 0x020000 && (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) { @@ -390,8 +390,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); - input_report_key(dev, BTN_LEFT, packet[0] & 0x01); - input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + psmouse_report_standard_buttons(dev, packet[0]); if (etd->reports_pressure) { input_report_abs(dev, ABS_PRESSURE, pres); input_report_abs(dev, ABS_TOOL_WIDTH, width); @@ -434,9 +433,7 @@ static void elantech_report_trackpoint(struct psmouse *psmouse, x = packet[4] - (int)((packet[1]^0x80) << 1); y = (int)((packet[2]^0x80) << 1) - packet[5]; - input_report_key(tp_dev, BTN_LEFT, packet[0] & 0x01); - input_report_key(tp_dev, BTN_RIGHT, packet[0] & 0x02); - input_report_key(tp_dev, BTN_MIDDLE, packet[0] & 0x04); + psmouse_report_standard_buttons(tp_dev, packet[0]); input_report_rel(tp_dev, REL_X, x); input_report_rel(tp_dev, REL_Y, y); @@ -526,12 +523,10 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse, input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); /* For clickpads map both buttons to BTN_LEFT */ - if (etd->fw_version & 0x001000) { + if (etd->fw_version & 0x001000) input_report_key(dev, BTN_LEFT, packet[0] & 0x03); - } else { - input_report_key(dev, BTN_LEFT, packet[0] & 0x01); - input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); - } + else + psmouse_report_standard_buttons(dev, packet[0]); input_report_abs(dev, ABS_PRESSURE, pres); input_report_abs(dev, ABS_TOOL_WIDTH, width); @@ -546,13 +541,10 @@ static void elantech_input_sync_v4(struct psmouse *psmouse) unsigned char *packet = psmouse->packet; /* For clickpads map both buttons to BTN_LEFT */ - if (etd->fw_version & 0x001000) { + if (etd->fw_version & 0x001000) input_report_key(dev, BTN_LEFT, packet[0] & 0x03); - } else { - input_report_key(dev, BTN_LEFT, packet[0] & 0x01); - input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); - input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04); - } + else + psmouse_report_standard_buttons(dev, packet[0]); input_mt_report_pointer_emulation(dev, true); input_sync(dev); diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 13d324cef7df..65efaade0820 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -188,14 +188,10 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse) } if (dev2) { - if (relative_packet) { - input_report_rel(dev2, REL_X, - ((packet[0] & 0x10) ? packet[1] - 256 : packet[1])); - input_report_rel(dev2, REL_Y, - -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2])); - } - input_report_key(dev2, BTN_LEFT, packet[0] & 0x01); - input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02); + if (relative_packet) + psmouse_report_standard_motion(dev2, packet); + + psmouse_report_standard_buttons(dev2, packet[0]); input_sync(dev2); } diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index ef9c97f5e3d7..b7d17db632fc 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -88,16 +88,14 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) (packet[1] >> 4) | (packet[0] & 0x30)); break; } + + psmouse_report_standard_buttons(dev, packet[0]); + } else { /* Standard PS/2 motion data */ - input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); - input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); + psmouse_report_standard_packet(dev, packet); } - input_report_key(dev, BTN_LEFT, packet[0] & 1); - input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); - input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); - input_sync(dev); return PSMOUSE_FULL_PACKET; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 6a5649e52eed..58a2cc7d592c 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -116,13 +116,30 @@ static DEFINE_MUTEX(psmouse_mutex); static struct workqueue_struct *kpsmoused_wq; -static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons) +void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons) { input_report_key(dev, BTN_LEFT, buttons & BIT(0)); input_report_key(dev, BTN_MIDDLE, buttons & BIT(2)); input_report_key(dev, BTN_RIGHT, buttons & BIT(1)); } +void psmouse_report_standard_motion(struct input_dev *dev, u8 *packet) +{ + int x, y; + + x = packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0; + y = packet[2] ? packet[2] - ((packet[0] << 3) & 0x100) : 0; + + input_report_rel(dev, REL_X, x); + input_report_rel(dev, REL_Y, -y); +} + +void psmouse_report_standard_packet(struct input_dev *dev, u8 *packet) +{ + psmouse_report_standard_buttons(dev, packet[0]); + psmouse_report_standard_motion(dev, packet); +} + /* * psmouse_process_byte() analyzes the PS/2 data stream and reports * relevant events to the input module once full packet has arrived. @@ -195,11 +212,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) } /* Generic PS/2 Mouse */ - psmouse_report_standard_buttons(dev, - packet[0] | psmouse->extra_buttons); - - input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); - input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); + packet[0] |= psmouse->extra_buttons; + psmouse_report_standard_packet(dev, packet); input_sync(dev); diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 8cd453808cc7..8bc99691494e 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -140,6 +140,10 @@ int psmouse_activate(struct psmouse *psmouse); int psmouse_deactivate(struct psmouse *psmouse); bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]); +void psmouse_report_standard_buttons(struct input_dev *, u8 buttons); +void psmouse_report_standard_motion(struct input_dev *, u8 *packet); +void psmouse_report_standard_packet(struct input_dev *, u8 *packet); + struct psmouse_attribute { struct device_attribute dattr; void *data; diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index 11c32ac8234b..1d6010d463e2 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -710,7 +710,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) unsigned char *packet = psmouse->packet; unsigned char button_status = 0, lscroll = 0, rscroll = 0; unsigned short abs_x, abs_y, fgrs = 0; - int rel_x, rel_y; if (psmouse->pktcnt < 4) return PSMOUSE_GOOD_DATA; @@ -840,15 +839,7 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) /* * Standard PS/2 Mouse */ - input_report_key(dev, BTN_LEFT, packet[0] & 1); - input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); - input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); - - rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0; - rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0; - - input_report_rel(dev, REL_X, rel_x); - input_report_rel(dev, REL_Y, rel_y); + psmouse_report_standard_packet(dev, packet); break; } -- cgit v1.3-14-g43fede From ba667650c568d55f6b80be54951b098f86939f2d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 22 Mar 2017 16:28:48 -0700 Subject: Input: psmouse - clean up code - switch to using BIT() macros - use u8 instead of unsigned char for byte data - use input_set_capability() instead of manipulating capabilities bits directly - use sign_extend32() when extracting wheel data. - do not abuse -1 as error code, propagate errors from various calls. Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 140 ++++++++++++++++++++----------------- 1 file changed, 77 insertions(+), 63 deletions(-) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 58a2cc7d592c..074bc647509d 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -14,6 +14,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define psmouse_fmt(fmt) fmt +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include "psmouse.h" #include "synaptics.h" @@ -147,7 +149,7 @@ void psmouse_report_standard_packet(struct input_dev *dev, u8 *packet) psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; - unsigned char *packet = psmouse->packet; + u8 *packet = psmouse->packet; if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA; @@ -157,39 +159,42 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) switch (psmouse->protocol->type) { case PSMOUSE_IMPS: /* IntelliMouse has scroll wheel */ - input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); + input_report_rel(dev, REL_WHEEL, -(s8) packet[3]); break; case PSMOUSE_IMEX: /* Scroll wheel and buttons on IntelliMouse Explorer */ switch (packet[3] & 0xC0) { case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ - input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); + input_report_rel(dev, REL_WHEEL, + -sign_extend32(packet[3], 5)); break; case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ - input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); + input_report_rel(dev, REL_HWHEEL, + -sign_extend32(packet[3], 5)); break; case 0x00: case 0xC0: - input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); - input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1); - input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); + input_report_rel(dev, REL_WHEEL, + -sign_extend32(packet[3], 3)); + input_report_key(dev, BTN_SIDE, BIT(4)); + input_report_key(dev, BTN_EXTRA, BIT(5)); break; } break; case PSMOUSE_GENPS: /* Report scroll buttons on NetMice */ - input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); + input_report_rel(dev, REL_WHEEL, -(s8) packet[3]); /* Extra buttons on Genius NewNet 3D */ - input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); - input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); + input_report_key(dev, BTN_SIDE, BIT(6)); + input_report_key(dev, BTN_EXTRA, BIT(7)); break; case PSMOUSE_THINKPS: /* Extra button on ThinkingMouse */ - input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); + input_report_key(dev, BTN_EXTRA, BIT(3)); /* * Without this bit of weirdness moving up gives wildly @@ -203,8 +208,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) * Cortron PS2 Trackball reports SIDE button in the * 4th bit of the first byte. */ - input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); - packet[0] |= 0x08; + input_report_key(dev, BTN_SIDE, BIT(3)); + packet[0] |= BIT(3); break; default: @@ -269,7 +274,7 @@ static int psmouse_handle_byte(struct psmouse *psmouse) psmouse_notice(psmouse, "issuing reconnect request\n"); serio_reconnect(psmouse->ps2dev.serio); - return -1; + return -EIO; } } psmouse->pktcnt = 0; @@ -320,7 +325,7 @@ static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data) * for normal processing or gathering them as command response. */ static irqreturn_t psmouse_interrupt(struct serio *serio, - unsigned char data, unsigned int flags) + u8 data, unsigned int flags) { struct psmouse *psmouse = serio_get_drvdata(serio); @@ -418,17 +423,20 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu * is the command. */ -int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command) +int psmouse_sliced_command(struct psmouse *psmouse, u8 command) { int i; + int error; - if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) - return -1; + error = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + if (error) + return error; for (i = 6; i >= 0; i -= 2) { - unsigned char d = (command >> i) & 3; - if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES)) - return -1; + u8 d = (command >> i) & 3; + error = ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES); + if (error) + return error; } return 0; @@ -439,13 +447,15 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command) */ int psmouse_reset(struct psmouse *psmouse) { - unsigned char param[2]; + u8 param[2]; + int error; - if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT)) - return -1; + error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT); + if (error) + return error; if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID) - return -1; + return -EIO; return 0; } @@ -455,8 +465,8 @@ int psmouse_reset(struct psmouse *psmouse) */ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) { - static const unsigned char params[] = { 0, 1, 2, 2, 3 }; - unsigned char p; + static const u8 params[] = { 0, 1, 2, 2, 3 }; + u8 p; if (resolution == 0 || resolution > 200) resolution = 200; @@ -471,11 +481,12 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) */ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) { - static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; - unsigned char r; + static const u8 rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; + u8 r; int i = 0; - while (rates[i] > rate) i++; + while (rates[i] > rate) + i++; r = rates[i]; ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); psmouse->rate = r; @@ -547,7 +558,7 @@ bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) static int genius_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[4]; + u8 param[4]; param[0] = 3; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); @@ -557,7 +568,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties) ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55) - return -1; + return -ENODEV; if (set_properties) { __set_bit(BTN_MIDDLE, psmouse->dev->keybit); @@ -579,7 +590,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties) static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[2]; + u8 param[2]; param[0] = 200; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); @@ -590,7 +601,7 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (param[0] != 3) - return -1; + return -ENODEV; if (set_properties) { __set_bit(BTN_MIDDLE, psmouse->dev->keybit); @@ -612,7 +623,7 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[2]; + u8 param[2]; intellimouse_detect(psmouse, 0); @@ -625,7 +636,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (param[0] != 4) - return -1; + return -ENODEV; /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ param[0] = 200; @@ -658,8 +669,8 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) static int thinking_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[2]; - static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; + u8 param[2]; + static const u8 seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; int i; param[0] = 10; @@ -673,7 +684,7 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties) ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (param[0] != 2) - return -1; + return -ENODEV; if (set_properties) { __set_bit(BTN_MIDDLE, psmouse->dev->keybit); @@ -701,7 +712,7 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties) * We have no way of figuring true number of buttons so let's * assume that the device has 3. */ - __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE); } return 0; @@ -956,20 +967,17 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) { struct input_dev *input_dev = psmouse->dev; - memset(input_dev->evbit, 0, sizeof(input_dev->evbit)); - memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); - memset(input_dev->relbit, 0, sizeof(input_dev->relbit)); - memset(input_dev->absbit, 0, sizeof(input_dev->absbit)); - memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit)); - - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(EV_REL, input_dev->evbit); + bitmap_zero(input_dev->evbit, EV_CNT); + bitmap_zero(input_dev->keybit, KEY_CNT); + bitmap_zero(input_dev->relbit, REL_CNT); + bitmap_zero(input_dev->absbit, ABS_CNT); + bitmap_zero(input_dev->mscbit, MSC_CNT); - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_LEFT); + input_set_capability(input_dev, EV_KEY, BTN_RIGHT); - __set_bit(REL_X, input_dev->relbit); - __set_bit(REL_Y, input_dev->relbit); + input_set_capability(input_dev, EV_REL, REL_X); + input_set_capability(input_dev, EV_REL, REL_Y); __set_bit(INPUT_PROP_POINTER, input_dev->propbit); @@ -1231,7 +1239,8 @@ static int psmouse_extensions(struct psmouse *psmouse, static int psmouse_probe(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[2]; + u8 param[2]; + int error; /* * First, we check if it's a mouse. It should send 0x00 or 0x03 in @@ -1240,20 +1249,22 @@ static int psmouse_probe(struct psmouse *psmouse) * subsequent ID queries, probably due to a firmware bug. */ param[0] = 0xa5; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) - return -1; + error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); + if (error) + return error; if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04 && param[0] != 0xff) - return -1; + return -ENODEV; /* * Then we reset and disable the mouse so that it doesn't generate * events. */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) - psmouse_warn(psmouse, "Failed to reset mouse on %s\n", - ps2dev->serio->phys); + error = ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + if (error) + psmouse_warn(psmouse, "Failed to reset mouse on %s: %d\n", + ps2dev->serio->phys, error); return 0; } @@ -1294,10 +1305,13 @@ int psmouse_activate(struct psmouse *psmouse) */ int psmouse_deactivate(struct psmouse *psmouse) { - if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { - psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n", - psmouse->ps2dev.serio->phys); - return -1; + int error; + + error = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE); + if (error) { + psmouse_warn(psmouse, "Failed to deactivate mouse on %s: %d\n", + psmouse->ps2dev.serio->phys, error); + return error; } psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); -- cgit v1.3-14-g43fede From 592c352b95db8db9c0d71795183ec1f6ee5c4213 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 22 Mar 2017 15:27:53 -0700 Subject: Input: logips2pp - clean up code - switch to using BIT() macros - use u8 instead of unsigned char for byte data - use input_set_capability() instead of manipulating capabilities bits directly - use sign_extend32() when extracting wheel data. - do not abuse -1 as error code, propagate errors from various calls. Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/logips2pp.c | 142 +++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 59 deletions(-) diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index b7d17db632fc..3c8d7051ef5e 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -9,9 +9,11 @@ * the Free Software Foundation. */ +#include #include #include #include +#include #include "psmouse.h" #include "logips2pp.h" @@ -22,12 +24,12 @@ #define PS2PP_KIND_TRACKMAN 4 /* Logitech mouse features */ -#define PS2PP_WHEEL 0x01 -#define PS2PP_HWHEEL 0x02 -#define PS2PP_SIDE_BTN 0x04 -#define PS2PP_EXTRA_BTN 0x08 -#define PS2PP_TASK_BTN 0x10 -#define PS2PP_NAV_BTN 0x20 +#define PS2PP_WHEEL BIT(0) +#define PS2PP_HWHEEL BIT(1) +#define PS2PP_SIDE_BTN BIT(2) +#define PS2PP_EXTRA_BTN BIT(3) +#define PS2PP_TASK_BTN BIT(4) +#define PS2PP_NAV_BTN BIT(5) struct ps2pp_info { u8 model; @@ -42,7 +44,7 @@ struct ps2pp_info { static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; - unsigned char *packet = psmouse->packet; + u8 *packet = psmouse->packet; if (psmouse->pktcnt < 3) return PSMOUSE_GOOD_DATA; @@ -58,28 +60,30 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) case 0x0d: /* Mouse extra info */ - input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, - (int) (packet[2] & 8) - (int) (packet[2] & 7)); - input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); - input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); + input_report_rel(dev, + packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, + -sign_extend32(packet[2], 3)); + input_report_key(dev, BTN_SIDE, packet[2] & BIT(4)); + input_report_key(dev, BTN_EXTRA, packet[2] & BIT(5)); break; case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ - input_report_key(dev, BTN_SIDE, (packet[2]) & 1); - input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); - input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); - input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); - input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); + input_report_key(dev, BTN_SIDE, packet[2] & BIT(0)); + input_report_key(dev, BTN_EXTRA, packet[2] & BIT(1)); + input_report_key(dev, BTN_TASK, packet[2] & BIT(2)); + input_report_key(dev, BTN_BACK, packet[2] & BIT(3)); + input_report_key(dev, BTN_FORWARD, packet[2] & BIT(4)); break; case 0x0f: /* TouchPad extra info */ - input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, - (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); - packet[0] = packet[2] | 0x08; + input_report_rel(dev, + packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, + -sign_extend32(packet[2] >> 4, 3)); + packet[0] = packet[2] | BIT(3); break; default: @@ -109,13 +113,17 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) * Ugly. */ -static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command) +static int ps2pp_cmd(struct psmouse *psmouse, u8 *param, u8 command) { - if (psmouse_sliced_command(psmouse, command)) - return -1; + int error; + + error = psmouse_sliced_command(psmouse, command); + if (error) + return error; - if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300)) - return -1; + error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300); + if (error) + return error; return 0; } @@ -131,7 +139,7 @@ static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned cha static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[4]; + u8 param[4]; ps2pp_cmd(psmouse, param, 0x32); @@ -169,7 +177,7 @@ static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, } PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL, - ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll); + ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll); /* * Support 800 dpi resolution _only_ if the user wants it (there are good @@ -177,11 +185,12 @@ PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL, * also good reasons to use it, let the user decide). */ -static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution) +static void ps2pp_set_resolution(struct psmouse *psmouse, + unsigned int resolution) { if (resolution > 400) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param = 3; + u8 param = 3; ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); @@ -194,7 +203,8 @@ static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolutio static void ps2pp_disconnect(struct psmouse *psmouse) { - device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr); + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_smartscroll.dattr); } static const struct ps2pp_info *get_model_info(unsigned char model) @@ -267,24 +277,24 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, struct input_dev *input_dev = psmouse->dev; if (model_info->features & PS2PP_SIDE_BTN) - __set_bit(BTN_SIDE, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_SIDE); if (model_info->features & PS2PP_EXTRA_BTN) - __set_bit(BTN_EXTRA, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_EXTRA); if (model_info->features & PS2PP_TASK_BTN) - __set_bit(BTN_TASK, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_TASK); if (model_info->features & PS2PP_NAV_BTN) { - __set_bit(BTN_FORWARD, input_dev->keybit); - __set_bit(BTN_BACK, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_FORWARD); + input_set_capability(input_dev, EV_KEY, BTN_BACK); } if (model_info->features & PS2PP_WHEEL) - __set_bit(REL_WHEEL, input_dev->relbit); + input_set_capability(input_dev, EV_REL, REL_WHEEL); if (model_info->features & PS2PP_HWHEEL) - __set_bit(REL_HWHEEL, input_dev->relbit); + input_set_capability(input_dev, EV_REL, REL_HWHEEL); switch (model_info->kind) { @@ -316,6 +326,30 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, } } +static int ps2pp_setup_protocol(struct psmouse *psmouse, + const struct ps2pp_info *model_info) +{ + int error; + + psmouse->protocol_handler = ps2pp_process_byte; + psmouse->pktsize = 3; + + if (model_info->kind != PS2PP_KIND_TP3) { + psmouse->set_resolution = ps2pp_set_resolution; + psmouse->disconnect = ps2pp_disconnect; + + error = device_create_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_smartscroll.dattr); + if (error) { + psmouse_err(psmouse, + "failed to create smartscroll sysfs attribute, error: %d\n", + error); + return error; + } + } + + return 0; +} /* * Logitech magic init. Detect whether the mouse is a Logitech one @@ -326,9 +360,9 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, int ps2pp_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[4]; - unsigned char model, buttons; const struct ps2pp_info *model_info; + u8 param[4]; + u8 model, buttons; bool use_ps2pp = false; int error; @@ -344,7 +378,7 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties) buttons = param[1]; if (!model || !buttons) - return -1; + return -ENXIO; model_info = get_model_info(model); if (model_info) { @@ -366,7 +400,8 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties) param[0] = 0; if (!ps2_command(ps2dev, param, 0x13d1) && - param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) { + param[0] == 0x06 && param[1] == 0x00 && + param[2] == 0x14) { use_ps2pp = true; } @@ -385,7 +420,9 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties) } } else { - psmouse_warn(psmouse, "Detected unknown Logitech mouse model %d\n", model); + psmouse_warn(psmouse, + "Detected unknown Logitech mouse model %d\n", + model); } if (set_properties) { @@ -393,31 +430,18 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties) psmouse->model = model; if (use_ps2pp) { - psmouse->protocol_handler = ps2pp_process_byte; - psmouse->pktsize = 3; - - if (model_info->kind != PS2PP_KIND_TP3) { - psmouse->set_resolution = ps2pp_set_resolution; - psmouse->disconnect = ps2pp_disconnect; - - error = device_create_file(&ps2dev->serio->dev, - &psmouse_attr_smartscroll.dattr); - if (error) { - psmouse_err(psmouse, - "failed to create smartscroll sysfs attribute, error: %d\n", - error); - return -1; - } - } + error = ps2pp_setup_protocol(psmouse, model_info); + if (error) + return error; } if (buttons >= 3) - __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE); if (model_info) ps2pp_set_model_properties(psmouse, model_info, use_ps2pp); } - return use_ps2pp ? 0 : -1; + return use_ps2pp ? 0 : -ENXIO; } -- cgit v1.3-14-g43fede From c13b418685bbadaef3a79273095c7217f7941fdf Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 22 Mar 2017 15:18:28 -0700 Subject: Input: lifebook - clean up code - use u8 instead of unsigned char for byte data - use input_set_capability() instead of manipulating capabilities bits directly - do not abuse -1 as error code, propagate errors from various calls. Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/lifebook.c | 50 ++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 65efaade0820..a5765f747c02 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "psmouse.h" #include "lifebook.h" @@ -136,7 +137,7 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse) struct lifebook_data *priv = psmouse->private; struct input_dev *dev1 = psmouse->dev; struct input_dev *dev2 = priv ? priv->dev2 : NULL; - unsigned char *packet = psmouse->packet; + u8 *packet = psmouse->packet; bool relative_packet = packet[0] & 0x08; if (relative_packet || !lifebook_use_6byte_proto) { @@ -201,10 +202,12 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse) static int lifebook_absolute_mode(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param; + u8 param; + int error; - if (psmouse_reset(psmouse)) - return -1; + error = psmouse_reset(psmouse); + if (error) + return error; /* * Enable absolute output -- ps2_command fails always but if @@ -220,15 +223,15 @@ static int lifebook_absolute_mode(struct psmouse *psmouse) static void lifebook_relative_mode(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param = 0x06; + u8 param = 0x06; ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); } static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution) { - static const unsigned char params[] = { 0, 1, 2, 2, 3 }; - unsigned char p; + static const u8 params[] = { 0, 1, 2, 2, 3 }; + u8 p; if (resolution == 0 || resolution > 400) resolution = 400; @@ -253,11 +256,11 @@ static void lifebook_disconnect(struct psmouse *psmouse) int lifebook_detect(struct psmouse *psmouse, bool set_properties) { if (!lifebook_present) - return -1; + return -ENXIO; if (desired_serio_phys && strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys)) - return -1; + return -ENXIO; if (set_properties) { psmouse->vendor = "Fujitsu"; @@ -290,10 +293,10 @@ static int lifebook_create_relative_device(struct psmouse *psmouse) dev2->id.version = 0x0000; dev2->dev.parent = &psmouse->ps2dev.serio->dev; - dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - dev2->keybit[BIT_WORD(BTN_LEFT)] = - BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); + input_set_capability(dev2, EV_REL, REL_X); + input_set_capability(dev2, EV_REL, REL_Y); + input_set_capability(dev2, EV_KEY, BTN_LEFT); + input_set_capability(dev2, EV_KEY, BTN_RIGHT); error = input_register_device(priv->dev2); if (error) @@ -312,21 +315,26 @@ int lifebook_init(struct psmouse *psmouse) { struct input_dev *dev1 = psmouse->dev; int max_coord = lifebook_use_6byte_proto ? 4096 : 1024; + int error; + + error = lifebook_absolute_mode(psmouse); + if (error) + return error; - if (lifebook_absolute_mode(psmouse)) - return -1; + /* Clear default capabilities */ + bitmap_zero(dev1->evbit, EV_CNT); + bitmap_zero(dev1->relbit, REL_CNT); + bitmap_zero(dev1->keybit, KEY_CNT); - dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); - dev1->relbit[0] = 0; - dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0; - dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_capability(dev1, EV_KEY, BTN_TOUCH); input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0); if (!desired_serio_phys) { - if (lifebook_create_relative_device(psmouse)) { + error = lifebook_create_relative_device(psmouse); + if (error) { lifebook_relative_mode(psmouse); - return -1; + return error; } } -- cgit v1.3-14-g43fede From 4c711ef62892e7d11af6cfadab33ad815e040d48 Mon Sep 17 00:00:00 2001 From: Stephen Lyons Date: Wed, 22 Mar 2017 23:20:54 -0700 Subject: Input: psmouse - add support for 2nd wheel on A4Tech Dual-Scroll wheel mice This Far-Eastern company's PS/2 mice use a deviant format for the data relating to movement of the scroll wheels for, at least, their dual wheel mice, such as their "Optical GreatEye Wheelmouse" model "WOP-35". This product has five "buttons" (one of which is the click action on the first wheel) and TWO scroll wheels. However for a byte comprising d0-d7 instead of setting one of d6-7 in the forth byte of the mouse data packet and a twos complement number of scroll steps in the remaining d5-d0 (or d3-d0 should there be a fourth (BTN_SIDE - d4) or fifth (BTN_EXTRA - d5) button to report; they only report a single +/- event for each wheel and use a bit pattern that corresponds to +/-1 for the first wheel and +/- 2 for the second in the lower nibble of the fourth byte. The effect with existing code is that the second mouse wheel merely repeats the effect of the first but providing two steps per click rather than the one of the first wheel - so there is no HORIZONTAL scroll wheel movement detected from the device as far as the rest of the kernel sees it. This patch, if enabled by the "a4tech_workaround" module parameter modifies the handling just for mice of type PSMOUSE_IMEX so that the second scroll wheel movement gets correctly reported as REL_HWHEEL events. Should this module parameter be activated for other mice of the same PSMOUSE_IMEX type then it is possible that at the point where the mouse reports more than a single movement step the user may start seeing horizontal rather than vertical wheel events, but should the movement steps get to be more than two at a time the hack will get immediately deactivated and the behaviour will revert to the past code. This was discussed around *fifteen* *years* *ago* on the LKML and the best summary is in post https://lkml.org/lkml/2002/7/18/111 "Re: PS2 Input Core Support" by Vojtech Pavlik. I was not able to locate any discussion later than this on this topic. Given that most users of the "psmouse" module will NOT want this additional feature enabled I have taken the apparently erroneous step of defaulting the module parameter that enables it to be "disabled" - this functionality may interfere with the operation of "normal" mice of this type (until a large enough scroll wheel movement is detected) so I cannot see how it would want to be enabled for "normal" users - i.e. everyone without this brand of mouse. I am using this patch at the moment and I can confirm that it is working for me as both a module and compiled into the kernel for my mouse that is of the type (WOP-35) described - I note that it is still available from certain on-line retailers and that the manufacturers site does not list GNU/Linux as being supported on the product page - this patch however does enable full use of this product: http://www.a4tech.com/product.asp?cid=3D1&scid=3D8&id=3D22 Signed-off-by: Stephen Lyons Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 074bc647509d..f0b16eb4a32a 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -70,6 +70,10 @@ static bool psmouse_smartscroll = true; module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); +static bool psmouse_a4tech_2wheels; +module_param_named(a4tech_workaround, psmouse_a4tech_2wheels, bool, 0644); +MODULE_PARM_DESC(a4tech_workaround, "A4Tech second scroll wheel workaround, 1 = enabled, 0 = disabled (default)."); + static unsigned int psmouse_resetafter = 5; module_param_named(resetafter, psmouse_resetafter, uint, 0644); MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); @@ -150,6 +154,7 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; u8 *packet = psmouse->packet; + int wheel; if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA; @@ -175,8 +180,18 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) break; case 0x00: case 0xC0: - input_report_rel(dev, REL_WHEEL, - -sign_extend32(packet[3], 3)); + wheel = sign_extend32(packet[3], 3); + + /* + * Some A4Tech mice have two scroll wheels, with first + * one reporting +/-1 in the lower nibble, and second + * one reporting +/-2. + */ + if (psmouse_a4tech_2wheels && abs(wheel) > 1) + input_report_rel(dev, REL_HWHEEL, wheel / 2); + else + input_report_rel(dev, REL_WHEEL, -wheel); + input_report_key(dev, BTN_SIDE, BIT(4)); input_report_key(dev, BTN_EXTRA, BIT(5)); break; -- cgit v1.3-14-g43fede From d5e0d9187abd5e3ce23884c375c4b36f403e42be Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 4 Jan 2018 11:05:30 -0800 Subject: Input: libps2 - fix switch statement formatting Individual labels of switch statements should have the same indentation level as the switch statement itself. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 131 +++++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 66 deletions(-) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 83e9c663aa67..21aea5169a99 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -126,46 +126,46 @@ EXPORT_SYMBOL(ps2_is_keyboard_id); static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) { switch (command) { - case PS2_CMD_RESET_BAT: - /* - * Device has sent the first response byte after - * reset command, reset is thus done, so we can - * shorten the timeout. - * The next byte will come soon (keyboard) or not - * at all (mouse). - */ - if (timeout > msecs_to_jiffies(100)) - timeout = msecs_to_jiffies(100); - break; + case PS2_CMD_RESET_BAT: + /* + * Device has sent the first response byte after + * reset command, reset is thus done, so we can + * shorten the timeout. + * The next byte will come soon (keyboard) or not + * at all (mouse). + */ + if (timeout > msecs_to_jiffies(100)) + timeout = msecs_to_jiffies(100); + break; - case PS2_CMD_GETID: - /* - * Microsoft Natural Elite keyboard responds to - * the GET ID command as it were a mouse, with - * a single byte. Fail the command so atkbd will - * use alternative probe to detect it. - */ - if (ps2dev->cmdbuf[1] == 0xaa) { - serio_pause_rx(ps2dev->serio); - ps2dev->flags = 0; - serio_continue_rx(ps2dev->serio); - timeout = 0; - } - - /* - * If device behind the port is not a keyboard there - * won't be 2nd byte of ID response. - */ - if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { - serio_pause_rx(ps2dev->serio); - ps2dev->flags = ps2dev->cmdcnt = 0; - serio_continue_rx(ps2dev->serio); - timeout = 0; - } - break; + case PS2_CMD_GETID: + /* + * Microsoft Natural Elite keyboard responds to + * the GET ID command as it were a mouse, with + * a single byte. Fail the command so atkbd will + * use alternative probe to detect it. + */ + if (ps2dev->cmdbuf[1] == 0xaa) { + serio_pause_rx(ps2dev->serio); + ps2dev->flags = 0; + serio_continue_rx(ps2dev->serio); + timeout = 0; + } - default: - break; + /* + * If device behind the port is not a keyboard there + * won't be 2nd byte of ID response. + */ + if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { + serio_pause_rx(ps2dev->serio); + ps2dev->flags = ps2dev->cmdcnt = 0; + serio_continue_rx(ps2dev->serio); + timeout = 0; + } + break; + + default: + break; } return timeout; @@ -289,39 +289,38 @@ EXPORT_SYMBOL(ps2_init); int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) { switch (data) { - case PS2_RET_ACK: - ps2dev->nak = 0; + case PS2_RET_ACK: + ps2dev->nak = 0; + break; + + case PS2_RET_NAK: + ps2dev->flags |= PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_NAK; + break; + + case PS2_RET_ERR: + if (ps2dev->flags & PS2_FLAG_NAK) { + ps2dev->flags &= ~PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_ERR; break; + } - case PS2_RET_NAK: - ps2dev->flags |= PS2_FLAG_NAK; - ps2dev->nak = PS2_RET_NAK; + /* + * Workaround for mice which don't ACK the Get ID command. + * These are valid mouse IDs that we recognize. + */ + case 0x00: + case 0x03: + case 0x04: + if (ps2dev->flags & PS2_FLAG_WAITID) { + ps2dev->nak = 0; break; - - case PS2_RET_ERR: - if (ps2dev->flags & PS2_FLAG_NAK) { - ps2dev->flags &= ~PS2_FLAG_NAK; - ps2dev->nak = PS2_RET_ERR; - break; - } - - /* - * Workaround for mice which don't ACK the Get ID command. - * These are valid mouse IDs that we recognize. - */ - case 0x00: - case 0x03: - case 0x04: - if (ps2dev->flags & PS2_FLAG_WAITID) { - ps2dev->nak = 0; - break; - } - /* Fall through */ - default: - return 0; + } + /* Fall through */ + default: + return 0; } - if (!ps2dev->nak) { ps2dev->flags &= ~PS2_FLAG_NAK; if (ps2dev->cmdcnt) -- cgit v1.3-14-g43fede From b28bad65c1fec47076ebee88b51b0dafa31f5065 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 4 Jan 2018 10:58:48 -0800 Subject: Input: libps2 - use u8 for byte data Instead of using unsigned char for the byte data switch to using u8. Also use unsigned int for the command codes and timeouts, and have ps2_handle_ack() and ps2_handle_response() return bool instead of int, as they do not return error codes but rather signal whether a byte was handled or not handled. ps2_is_keyboard_id() now returns bool as well. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 31 ++++++++++++++++--------------- include/linux/libps2.h | 23 +++++++++++++---------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 21aea5169a99..c3712f0a47b5 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -34,7 +34,7 @@ MODULE_LICENSE("GPL"); * ps2_sendbyte() can only be called from a process context. */ -int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) +int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) { serio_pause_rx(ps2dev->serio); ps2dev->nak = 1; @@ -75,7 +75,7 @@ EXPORT_SYMBOL(ps2_end_command); * and discards them. */ -void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) +void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout) { if (maxbytes > sizeof(ps2dev->cmdbuf)) { WARN_ON(1); @@ -102,9 +102,9 @@ EXPORT_SYMBOL(ps2_drain); * known keyboard IDs. */ -int ps2_is_keyboard_id(char id_byte) +bool ps2_is_keyboard_id(u8 id_byte) { - static const char keyboard_ids[] = { + static const u8 keyboard_ids[] = { 0xab, /* Regular keyboards */ 0xac, /* NCD Sun keyboard */ 0x2b, /* Trust keyboard, translated */ @@ -123,7 +123,8 @@ EXPORT_SYMBOL(ps2_is_keyboard_id); * completion. */ -static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) +static int ps2_adjust_timeout(struct ps2dev *ps2dev, + unsigned int command, unsigned int timeout) { switch (command) { case PS2_CMD_RESET_BAT: @@ -178,11 +179,11 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) * ps2_command() can only be called from a process context */ -int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) +int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) { - int timeout; - int send = (command >> 12) & 0xf; - int receive = (command >> 8) & 0xf; + unsigned int timeout; + unsigned int send = (command >> 12) & 0xf; + unsigned int receive = (command >> 8) & 0xf; int rc = -1; int i; @@ -256,7 +257,7 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) } EXPORT_SYMBOL(__ps2_command); -int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) +int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) { int rc; @@ -286,7 +287,7 @@ EXPORT_SYMBOL(ps2_init); * to properly process ACK/NAK of a command from a PS/2 device. */ -int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) +bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data) { switch (data) { case PS2_RET_ACK: @@ -318,7 +319,7 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) } /* Fall through */ default: - return 0; + return false; } if (!ps2dev->nak) { @@ -333,7 +334,7 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) if (data != PS2_RET_ACK) ps2_handle_response(ps2dev, data); - return 1; + return true; } EXPORT_SYMBOL(ps2_handle_ack); @@ -343,7 +344,7 @@ EXPORT_SYMBOL(ps2_handle_ack); * waiting for completion of the command. */ -int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) +bool ps2_handle_response(struct ps2dev *ps2dev, u8 data) { if (ps2dev->cmdcnt) ps2dev->cmdbuf[--ps2dev->cmdcnt] = data; @@ -359,7 +360,7 @@ int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) wake_up(&ps2dev->wait); } - return 1; + return true; } EXPORT_SYMBOL(ps2_handle_response); diff --git a/include/linux/libps2.h b/include/linux/libps2.h index 4ad06e824f76..04a5750f1e4e 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -10,6 +10,9 @@ * the Free Software Foundation. */ +#include +#include +#include #define PS2_CMD_GETID 0x02f2 #define PS2_CMD_RESET_BAT 0x02ff @@ -36,21 +39,21 @@ struct ps2dev { wait_queue_head_t wait; unsigned long flags; - unsigned char cmdbuf[8]; - unsigned char cmdcnt; - unsigned char nak; + u8 cmdbuf[8]; + u8 cmdcnt; + u8 nak; }; void ps2_init(struct ps2dev *ps2dev, struct serio *serio); -int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); -void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); +int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout); +void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout); void ps2_begin_command(struct ps2dev *ps2dev); void ps2_end_command(struct ps2dev *ps2dev); -int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); -int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); -int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data); -int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data); +int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command); +int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command); +bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data); +bool ps2_handle_response(struct ps2dev *ps2dev, u8 data); void ps2_cmd_aborted(struct ps2dev *ps2dev); -int ps2_is_keyboard_id(char id); +bool ps2_is_keyboard_id(u8 id); #endif /* _LIBPS2_H */ -- cgit v1.3-14-g43fede From 3a92dd331f90a33e0f0962b981577eb5078419c4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 4 Jan 2018 11:27:05 -0800 Subject: Input: libps2 - use BIT() for bitmask constants Let's explicitly document bit numbers with BIT() macro. Signed-off-by: Dmitry Torokhov --- include/linux/libps2.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/linux/libps2.h b/include/linux/libps2.h index 04a5750f1e4e..646b581fea56 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -10,6 +10,7 @@ * the Free Software Foundation. */ +#include #include #include #include @@ -23,11 +24,11 @@ #define PS2_RET_NAK 0xfe #define PS2_RET_ERR 0xfc -#define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */ -#define PS2_FLAG_CMD 2 /* Waiting for command to finish */ -#define PS2_FLAG_CMD1 4 /* Waiting for the first byte of command response */ -#define PS2_FLAG_WAITID 8 /* Command execiting is GET ID */ -#define PS2_FLAG_NAK 16 /* Last transmission was NAKed */ +#define PS2_FLAG_ACK BIT(0) /* Waiting for ACK/NAK */ +#define PS2_FLAG_CMD BIT(1) /* Waiting for a command to finish */ +#define PS2_FLAG_CMD1 BIT(2) /* Waiting for the first byte of command response */ +#define PS2_FLAG_WAITID BIT(3) /* Command executing is GET ID */ +#define PS2_FLAG_NAK BIT(4) /* Last transmission was NAKed */ struct ps2dev { struct serio *serio; -- cgit v1.3-14-g43fede From 08be954b7a7de6742d3d47e4dc20e3b086410761 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 2 Jan 2018 12:03:02 -0800 Subject: Input: psmouse - move sliced command implementation to libps2 In preparation to adding some debugging statements to PS/2 control sequences let's move psmouse_sliced_command() into libps2 and rename it to ps2_sliced_command(). Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 12 ++++++------ drivers/input/mouse/logips2pp.c | 2 +- drivers/input/mouse/psmouse-base.c | 26 -------------------------- drivers/input/mouse/psmouse.h | 1 - drivers/input/mouse/synaptics.c | 8 ++++---- drivers/input/serio/libps2.c | 32 ++++++++++++++++++++++++++++++++ include/linux/libps2.h | 3 +++ 7 files changed, 46 insertions(+), 38 deletions(-) diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index af7fc17c14d9..db47a5e1d114 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -35,7 +35,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) { - if (psmouse_sliced_command(psmouse, c) || + if (ps2_sliced_command(&psmouse->ps2dev, c) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); return -1; @@ -107,8 +107,8 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, switch (etd->hw_version) { case 1: - if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || - psmouse_sliced_command(psmouse, reg) || + if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) || + ps2_sliced_command(&psmouse->ps2dev, reg) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { rc = -1; } @@ -162,9 +162,9 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, switch (etd->hw_version) { case 1: - if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || - psmouse_sliced_command(psmouse, reg) || - psmouse_sliced_command(psmouse, val) || + if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) || + ps2_sliced_command(&psmouse->ps2dev, reg) || + ps2_sliced_command(&psmouse->ps2dev, val) || ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { rc = -1; } diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 3c8d7051ef5e..3d5637e6fa5f 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -117,7 +117,7 @@ static int ps2pp_cmd(struct psmouse *psmouse, u8 *param, u8 command) { int error; - error = psmouse_sliced_command(psmouse, command); + error = ps2_sliced_command(&psmouse->ps2dev, command); if (error) return error; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index f0b16eb4a32a..4f9f438e2653 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -431,32 +431,6 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, return IRQ_HANDLED; } -/* - * psmouse_sliced_command() sends an extended PS/2 command to the mouse - * using sliced syntax, understood by advanced devices, such as Logitech - * or Synaptics touchpads. The command is encoded as: - * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu - * is the command. - */ -int psmouse_sliced_command(struct psmouse *psmouse, u8 command) -{ - int i; - int error; - - error = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); - if (error) - return error; - - for (i = 6; i >= 0; i -= 2) { - u8 d = (command >> i) & 3; - error = ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES); - if (error) - return error; - } - - return 0; -} - /* * psmouse_reset() resets the mouse into power-on state. */ diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 8bc99691494e..71ac50082c8b 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -131,7 +131,6 @@ struct psmouse { void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, unsigned long delay); -int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command); int psmouse_reset(struct psmouse *psmouse); void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index cd9f61cb3fc6..89ab77a211b5 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -84,7 +84,7 @@ static int synaptics_mode_cmd(struct psmouse *psmouse, u8 mode) u8 param[1]; int error; - error = psmouse_sliced_command(psmouse, mode); + error = ps2_sliced_command(&psmouse->ps2dev, mode); if (error) return error; @@ -190,7 +190,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, u8 cmd, u8 *param) { int error; - error = psmouse_sliced_command(psmouse, cmd); + error = ps2_sliced_command(&psmouse->ps2dev, cmd); if (error) return error; @@ -547,7 +547,7 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) static u8 param = 0xc8; int error; - error = psmouse_sliced_command(psmouse, SYN_QUE_MODEL); + error = ps2_sliced_command(&psmouse->ps2dev, SYN_QUE_MODEL); if (error) return error; @@ -614,7 +614,7 @@ static int synaptics_pt_write(struct serio *serio, u8 c) u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ int error; - error = psmouse_sliced_command(parent, c); + error = ps2_sliced_command(&parent->ps2dev, c); if (error) return error; diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index c3712f0a47b5..e96ae477f0b5 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -269,6 +269,38 @@ int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) } EXPORT_SYMBOL(ps2_command); +/* + * ps2_sliced_command() sends an extended PS/2 command to the mouse + * using sliced syntax, understood by advanced devices, such as Logitech + * or Synaptics touchpads. The command is encoded as: + * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu + * is the command. + */ + +int ps2_sliced_command(struct ps2dev *ps2dev, u8 command) +{ + int i; + int retval; + + ps2_begin_command(ps2dev); + + retval = __ps2_command(ps2dev, NULL, PS2_CMD_SETSCALE11); + if (retval) + goto out; + + for (i = 6; i >= 0; i -= 2) { + u8 d = (command >> i) & 3; + retval = __ps2_command(ps2dev, &d, PS2_CMD_SETRES); + if (retval) + break; + } + +out: + ps2_end_command(ps2dev); + return retval; +} +EXPORT_SYMBOL(ps2_sliced_command); + /* * ps2_init() initializes ps2dev structure */ diff --git a/include/linux/libps2.h b/include/linux/libps2.h index 646b581fea56..3c69cd796f48 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -15,6 +15,8 @@ #include #include +#define PS2_CMD_SETSCALE11 0x00e6 +#define PS2_CMD_SETRES 0x10e8 #define PS2_CMD_GETID 0x02f2 #define PS2_CMD_RESET_BAT 0x02ff @@ -52,6 +54,7 @@ void ps2_begin_command(struct ps2dev *ps2dev); void ps2_end_command(struct ps2dev *ps2dev); int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command); int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command); +int ps2_sliced_command(struct ps2dev *ps2dev, u8 command); bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data); bool ps2_handle_response(struct ps2dev *ps2dev, u8 data); void ps2_cmd_aborted(struct ps2dev *ps2dev); -- cgit v1.3-14-g43fede From 147b903da65daedc90dbeb66a75dd608a6a41ef2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 2 Jan 2018 12:21:49 -0800 Subject: Input: libps2 - add debugging statements Debugging via i8042.debug and analyzing raw PS/2 data stream may be cumbersome as you need to locate the boundaries of commands, decipher the sliced commands, etc, etc. Let's add a bit more high level debug statements for ps2_sendbyte(), ps2_command(), and ps2_sliced_command(). We do not introduce a new module parameter, but rater rely on the kernel having dynamic debug facility enabled (which most everyone has nowadays). Enable with: echo "file libps2.c +pf" > /sys/kernel/debug/dynamic_debug/control or add "libps2.dyndbg=+pf" to the kernel command line. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 52 +++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index e96ae477f0b5..82befae4dab0 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -26,22 +26,20 @@ MODULE_AUTHOR("Dmitry Torokhov "); MODULE_DESCRIPTION("PS/2 driver library"); MODULE_LICENSE("GPL"); -/* - * ps2_sendbyte() sends a byte to the device and waits for acknowledge. - * It doesn't handle retransmission, though it could - because if there - * is a need for retransmissions device has to be replaced anyway. - * - * ps2_sendbyte() can only be called from a process context. - */ - -int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) +static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) { + int error; + serio_pause_rx(ps2dev->serio); ps2dev->nak = 1; ps2dev->flags |= PS2_FLAG_ACK; serio_continue_rx(ps2dev->serio); - if (serio_write(ps2dev->serio, byte) == 0) + error = serio_write(ps2dev->serio, byte); + if (error) + dev_dbg(&ps2dev->serio->dev, + "failed to write %#02x: %d\n", byte, error); + else wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_ACK), msecs_to_jiffies(timeout)); @@ -52,6 +50,24 @@ int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) return -ps2dev->nak; } + +/* + * ps2_sendbyte() sends a byte to the device and waits for acknowledge. + * It doesn't handle retransmission, though it could - because if there + * is a need for retransmissions device has to be replaced anyway. + * + * ps2_sendbyte() can only be called from a process context. + */ + +int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) +{ + int retval; + + retval = ps2_do_sendbyte(ps2dev, byte, timeout); + dev_dbg(&ps2dev->serio->dev, "%02x - %x\n", byte, ps2dev->nak); + + return retval; +} EXPORT_SYMBOL(ps2_sendbyte); void ps2_begin_command(struct ps2dev *ps2dev) @@ -186,6 +202,7 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) unsigned int receive = (command >> 8) & 0xf; int rc = -1; int i; + u8 send_param[16]; if (receive > sizeof(ps2dev->cmdbuf)) { WARN_ON(1); @@ -197,6 +214,8 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) return -1; } + memcpy(send_param, param, send); + serio_pause_rx(ps2dev->serio); ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; ps2dev->cmdcnt = receive; @@ -210,14 +229,14 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) * ACKing the reset command, and so it can take a long * time before the ACK arrives. */ - if (ps2_sendbyte(ps2dev, command & 0xff, - command == PS2_CMD_RESET_BAT ? 1000 : 200)) { + if (ps2_do_sendbyte(ps2dev, command & 0xff, + command == PS2_CMD_RESET_BAT ? 1000 : 200)) { serio_pause_rx(ps2dev->serio); goto out_reset_flags; } for (i = 0; i < send; i++) { - if (ps2_sendbyte(ps2dev, param[i], 200)) { + if (ps2_do_sendbyte(ps2dev, param[i], 200)) { serio_pause_rx(ps2dev->serio); goto out_reset_flags; } @@ -253,6 +272,12 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) ps2dev->flags = 0; serio_continue_rx(ps2dev->serio); + dev_dbg(&ps2dev->serio->dev, + "%02x [%*ph] - %x/%08lx [%*ph]\n", + command & 0xff, send, send_param, + ps2dev->nak, ps2dev->flags, + receive, param ?: send_param); + return rc; } EXPORT_SYMBOL(__ps2_command); @@ -296,6 +321,7 @@ int ps2_sliced_command(struct ps2dev *ps2dev, u8 command) } out: + dev_dbg(&ps2dev->serio->dev, "%02x - %d\n", command, retval); ps2_end_command(ps2dev); return retval; } -- cgit v1.3-14-g43fede From b99e1f2a1a3f4158bed9b9e9e97ac46678d8c2ac Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 4 Jan 2018 22:01:43 -0800 Subject: Input: libps2 - support retransmission of command data The devices are allowed to respond to either command byte or command parameter with a NAK (0xfe), and the host is supposed to resend the "correct" byte. The device then will either respond with ACK or ERR (0xfc). Let's teach libps2 to handle the NAK responses properly, so that individual drivers do not need to handle them. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 103 +++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 32 deletions(-) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 82befae4dab0..f05c407b31f3 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -26,35 +26,63 @@ MODULE_AUTHOR("Dmitry Torokhov "); MODULE_DESCRIPTION("PS/2 driver library"); MODULE_LICENSE("GPL"); -static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) +static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte, + unsigned int timeout, unsigned int max_attempts) + __releases(&ps2dev->serio->lock) __acquires(&ps2dev->serio->lock) { + int attempt = 0; int error; - serio_pause_rx(ps2dev->serio); - ps2dev->nak = 1; - ps2dev->flags |= PS2_FLAG_ACK; - serio_continue_rx(ps2dev->serio); + lockdep_assert_held(&ps2dev->serio->lock); - error = serio_write(ps2dev->serio, byte); - if (error) - dev_dbg(&ps2dev->serio->dev, - "failed to write %#02x: %d\n", byte, error); - else - wait_event_timeout(ps2dev->wait, - !(ps2dev->flags & PS2_FLAG_ACK), - msecs_to_jiffies(timeout)); + do { + ps2dev->nak = 1; + ps2dev->flags |= PS2_FLAG_ACK; + + serio_continue_rx(ps2dev->serio); + + error = serio_write(ps2dev->serio, byte); + if (error) + dev_dbg(&ps2dev->serio->dev, + "failed to write %#02x: %d\n", byte, error); + else + wait_event_timeout(ps2dev->wait, + !(ps2dev->flags & PS2_FLAG_ACK), + msecs_to_jiffies(timeout)); + + serio_pause_rx(ps2dev->serio); + } while (ps2dev->nak == PS2_RET_NAK && ++attempt < max_attempts); - serio_pause_rx(ps2dev->serio); ps2dev->flags &= ~PS2_FLAG_ACK; - serio_continue_rx(ps2dev->serio); - return -ps2dev->nak; + if (!error) { + switch (ps2dev->nak) { + case 0: + break; + case PS2_RET_NAK: + error = -EAGAIN; + break; + case PS2_RET_ERR: + error = -EPROTO; + break; + default: + error = -EIO; + break; + } + } + + if (error || attempt > 1) + dev_dbg(&ps2dev->serio->dev, + "%02x - %d (%x), attempt %d\n", + byte, error, ps2dev->nak, attempt); + + return error; } /* * ps2_sendbyte() sends a byte to the device and waits for acknowledge. - * It doesn't handle retransmission, though it could - because if there - * is a need for retransmissions device has to be replaced anyway. + * It doesn't handle retransmission, the caller is expected to handle + * it when needed. * * ps2_sendbyte() can only be called from a process context. */ @@ -63,9 +91,13 @@ int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) { int retval; - retval = ps2_do_sendbyte(ps2dev, byte, timeout); + serio_pause_rx(ps2dev->serio); + + retval = ps2_do_sendbyte(ps2dev, byte, timeout, 1); dev_dbg(&ps2dev->serio->dev, "%02x - %x\n", byte, ps2dev->nak); + serio_continue_rx(ps2dev->serio); + return retval; } EXPORT_SYMBOL(ps2_sendbyte); @@ -200,48 +232,48 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) unsigned int timeout; unsigned int send = (command >> 12) & 0xf; unsigned int receive = (command >> 8) & 0xf; - int rc = -1; + int rc; int i; u8 send_param[16]; if (receive > sizeof(ps2dev->cmdbuf)) { WARN_ON(1); - return -1; + return -EINVAL; } if (send && !param) { WARN_ON(1); - return -1; + return -EINVAL; } memcpy(send_param, param, send); serio_pause_rx(ps2dev->serio); + ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; ps2dev->cmdcnt = receive; if (receive && param) for (i = 0; i < receive; i++) ps2dev->cmdbuf[(receive - 1) - i] = param[i]; - serio_continue_rx(ps2dev->serio); /* * Some devices (Synaptics) peform the reset before * ACKing the reset command, and so it can take a long * time before the ACK arrives. */ - if (ps2_do_sendbyte(ps2dev, command & 0xff, - command == PS2_CMD_RESET_BAT ? 1000 : 200)) { - serio_pause_rx(ps2dev->serio); + rc = ps2_do_sendbyte(ps2dev, command & 0xff, + command == PS2_CMD_RESET_BAT ? 1000 : 200, 2); + if (rc) goto out_reset_flags; - } for (i = 0; i < send; i++) { - if (ps2_do_sendbyte(ps2dev, param[i], 200)) { - serio_pause_rx(ps2dev->serio); + rc = ps2_do_sendbyte(ps2dev, param[i], 200, 2); + if (rc) goto out_reset_flags; - } } + serio_continue_rx(ps2dev->serio); + /* * The reset command takes a long time to execute. */ @@ -263,8 +295,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) for (i = 0; i < receive; i++) param[i] = ps2dev->cmdbuf[(receive - 1) - i]; - if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) + if (ps2dev->cmdcnt && + (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) { + rc = -EPROTO; goto out_reset_flags; + } rc = 0; @@ -278,7 +313,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) ps2dev->nak, ps2dev->flags, receive, param ?: send_param); - return rc; + /* + * ps_command() handles resends itself, so do not leak -EAGAIN + * to the callers. + */ + return rc != -EAGAIN ? rc : -EPROTO; } EXPORT_SYMBOL(__ps2_command); -- cgit v1.3-14-g43fede From 29acc42e8e10a4721757af9ed8aec569d30ce39b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 17 Jan 2018 12:00:24 -0800 Subject: Input: libps2 - relax command byte ACK handling When we probe PS/2 devices we first issue "Get ID" command and only if we receive what we consider a valid keyboard or mouse ID we disable the device and continue with protocol detection. That means that the device may be transmitting motion or keystroke data, while we expect ACK response. Instead of signaling failure if we see anything but ACK/NAK let's ignore "garbage" response until we see ACK for the command byte (first byte). The checks for subsequent ACKs of command parameters will continue be strict. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 25 ++++++++++++++++++++++--- include/linux/libps2.h | 1 + 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index f05c407b31f3..e6a07e68d1ff 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -256,16 +256,23 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) for (i = 0; i < receive; i++) ps2dev->cmdbuf[(receive - 1) - i] = param[i]; + /* Signal that we are sending the command byte */ + ps2dev->flags |= PS2_FLAG_ACK_CMD; + /* * Some devices (Synaptics) peform the reset before * ACKing the reset command, and so it can take a long * time before the ACK arrives. */ - rc = ps2_do_sendbyte(ps2dev, command & 0xff, - command == PS2_CMD_RESET_BAT ? 1000 : 200, 2); + timeout = command == PS2_CMD_RESET_BAT ? 1000 : 200; + + rc = ps2_do_sendbyte(ps2dev, command & 0xff, timeout, 2); if (rc) goto out_reset_flags; + /* Now we are sending command parameters, if any */ + ps2dev->flags &= ~PS2_FLAG_ACK_CMD; + for (i = 0; i < send; i++) { rc = ps2_do_sendbyte(ps2dev, param[i], 200, 2); if (rc) @@ -416,7 +423,19 @@ bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data) } /* Fall through */ default: - return false; + /* + * Do not signal errors if we get unexpected reply while + * waiting for an ACK to the initial (first) command byte: + * the device might not be quiesced yet and continue + * delivering data. + * Note that we reset PS2_FLAG_WAITID flag, so the workaround + * for mice not acknowledging the Get ID command only triggers + * on the 1st byte; if device spews data we really want to see + * a real ACK from it. + */ + dev_dbg(&ps2dev->serio->dev, "unexpected %#02x\n", data); + ps2dev->flags &= ~PS2_FLAG_WAITID; + return ps2dev->flags & PS2_FLAG_ACK_CMD; } if (!ps2dev->nak) { diff --git a/include/linux/libps2.h b/include/linux/libps2.h index 3c69cd796f48..5f18fe02ae37 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -31,6 +31,7 @@ #define PS2_FLAG_CMD1 BIT(2) /* Waiting for the first byte of command response */ #define PS2_FLAG_WAITID BIT(3) /* Command executing is GET ID */ #define PS2_FLAG_NAK BIT(4) /* Last transmission was NAKed */ +#define PS2_FLAG_ACK_CMD BIT(5) /* Waiting to ACK the command (first) byte */ struct ps2dev { struct serio *serio; -- cgit v1.3-14-g43fede From 3aceaa34d7723c2556555b05ad04a89ce2d66374 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 4 Jan 2018 22:52:55 -0800 Subject: Input: trackpoint - combine calls to ps2_command() We do not need to call ps2_command() several times in a row, transmitting every byte as it were a command byte, we can often pack it all in a single command. Also, now that ps2_command() handles retransmission, we do not need to do it ourselves in trackpoint_power_on_reset(). Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/trackpoint.c | 60 ++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index bbd29220dbe9..6590d10f166f 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -33,18 +33,15 @@ static const char * const trackpoint_variants[] = { */ static int trackpoint_power_on_reset(struct ps2dev *ps2dev) { - u8 results[2]; - int tries = 0; + u8 param[2] = { TP_POR }; + int err; - /* Issue POR command, and repeat up to once if 0xFC00 received */ - do { - if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || - ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR))) - return -1; - } while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2); + err = ps2_command(ps2dev, param, MAKE_PS2_CMD(1, 2, TP_COMMAND)); + if (err) + return err; /* Check for success response -- 0xAA00 */ - if (results[0] != 0xAA || results[1] != 0x00) + if (param[0] != 0xAA || param[1] != 0x00) return -ENODEV; return 0; @@ -55,49 +52,39 @@ static int trackpoint_power_on_reset(struct ps2dev *ps2dev) */ static int trackpoint_read(struct ps2dev *ps2dev, u8 loc, u8 *results) { - if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || - ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) { - return -1; - } + results[0] = loc; - return 0; + return ps2_command(ps2dev, results, MAKE_PS2_CMD(1, 1, TP_COMMAND)); } static int trackpoint_write(struct ps2dev *ps2dev, u8 loc, u8 val) { - if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || - ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) || - ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) || - ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) { - return -1; - } + u8 param[3] = { TP_WRITE_MEM, loc, val }; - return 0; + return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND)); } static int trackpoint_toggle_bit(struct ps2dev *ps2dev, u8 loc, u8 mask) { + u8 param[3] = { TP_TOGGLE, loc, mask }; + /* Bad things will happen if the loc param isn't in this range */ if (loc < 0x20 || loc >= 0x2F) - return -1; - - if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || - ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) || - ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) || - ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) { - return -1; - } + return -EINVAL; - return 0; + return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND)); } static int trackpoint_update_bit(struct ps2dev *ps2dev, u8 loc, u8 mask, u8 value) { - int retval = 0; + int retval; u8 data; - trackpoint_read(ps2dev, loc, &data); + retval = trackpoint_read(ps2dev, loc, &data); + if (retval) + return retval; + if (((data & mask) == mask) != !!value) retval = trackpoint_toggle_bit(ps2dev, loc, mask); @@ -142,9 +129,9 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data, return err; *field = value; - trackpoint_write(&psmouse->ps2dev, attr->command, value); + err = trackpoint_write(&psmouse->ps2dev, attr->command, value); - return count; + return err ?: count; } #define TRACKPOINT_INT_ATTR(_name, _command, _default) \ @@ -175,10 +162,11 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data, if (*field != value) { *field = value; - trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask); + err = trackpoint_toggle_bit(&psmouse->ps2dev, + attr->command, attr->mask); } - return count; + return err ?: count; } -- cgit v1.3-14-g43fede From 29aa619408adc1f88745feed02265acfbc0b8fea Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 3 Apr 2017 16:37:24 -0700 Subject: Input: synaptics - switch to using input_set_capability Instead of manipulating capability bits directly, use input_set_capability(). Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 49 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 3d2e23a0ae39..feb9c04d0eae 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1235,25 +1235,31 @@ static void set_input_params(struct psmouse *psmouse, struct synaptics_device_info *info = &priv->info; int i; + /* Reset default psmouse capabilities */ + __clear_bit(EV_REL, dev->evbit); + bitmap_zero(dev->relbit, REL_CNT); + bitmap_zero(dev->keybit, KEY_CNT); + /* Things that apply to both modes */ __set_bit(INPUT_PROP_POINTER, dev->propbit); - __set_bit(EV_KEY, dev->evbit); - __set_bit(BTN_LEFT, dev->keybit); - __set_bit(BTN_RIGHT, dev->keybit); - if (SYN_CAP_MIDDLE_BUTTON(info->capabilities)) - __set_bit(BTN_MIDDLE, dev->keybit); + input_set_capability(dev, EV_KEY, BTN_LEFT); + + /* Clickpads report only left button */ + if (!SYN_CAP_CLICKPAD(info->ext_cap_0c)) { + input_set_capability(dev, EV_KEY, BTN_RIGHT); + if (SYN_CAP_MIDDLE_BUTTON(info->capabilities)) + input_set_capability(dev, EV_KEY, BTN_MIDDLE); + } if (!priv->absolute_mode) { /* Relative mode */ - __set_bit(EV_REL, dev->evbit); - __set_bit(REL_X, dev->relbit); - __set_bit(REL_Y, dev->relbit); + input_set_capability(dev, EV_REL, REL_X); + input_set_capability(dev, EV_REL, REL_Y); return; } /* Absolute mode */ - __set_bit(EV_ABS, dev->evbit); set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); @@ -1268,8 +1274,8 @@ static void set_input_params(struct psmouse *psmouse, input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK); /* Image sensors can signal 4 and 5 finger clicks */ - __set_bit(BTN_TOOL_QUADTAP, dev->keybit); - __set_bit(BTN_TOOL_QUINTTAP, dev->keybit); + input_set_capability(dev, EV_KEY, BTN_TOOL_QUADTAP); + input_set_capability(dev, EV_KEY, BTN_TOOL_QUINTTAP); } else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) { set_abs_position_params(dev, info, ABS_MT_POSITION_X, ABS_MT_POSITION_Y); @@ -1296,36 +1302,29 @@ static void set_input_params(struct psmouse *psmouse, if (SYN_CAP_PALMDETECT(info->capabilities)) input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); - __set_bit(BTN_TOUCH, dev->keybit); - __set_bit(BTN_TOOL_FINGER, dev->keybit); + input_set_capability(dev, EV_KEY, BTN_TOUCH); + input_set_capability(dev, EV_KEY, BTN_TOOL_FINGER); if (synaptics_has_multifinger(priv)) { - __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); - __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); + input_set_capability(dev, EV_KEY, BTN_TOOL_DOUBLETAP); + input_set_capability(dev, EV_KEY, BTN_TOOL_TRIPLETAP); } if (SYN_CAP_FOUR_BUTTON(info->capabilities) || SYN_CAP_MIDDLE_BUTTON(info->capabilities)) { - __set_bit(BTN_FORWARD, dev->keybit); - __set_bit(BTN_BACK, dev->keybit); + input_set_capability(dev, EV_KEY, BTN_FORWARD); + input_set_capability(dev, EV_KEY, BTN_BACK); } if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10)) for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++) - __set_bit(BTN_0 + i, dev->keybit); - - __clear_bit(EV_REL, dev->evbit); - __clear_bit(REL_X, dev->relbit); - __clear_bit(REL_Y, dev->relbit); + input_set_capability(dev, EV_KEY, BTN_0 + i); if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) { __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10)) __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); - /* Clickpads report only left button */ - __clear_bit(BTN_RIGHT, dev->keybit); - __clear_bit(BTN_MIDDLE, dev->keybit); } } -- cgit v1.3-14-g43fede From cdc2466df40f26af60f6b18123069bd52336a80c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 3 Apr 2017 16:54:04 -0700 Subject: Input: synaptics - handle errors from input_mt_init_slots() input_mt_init_slots() may fail, we need to handle this condition. Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index feb9c04d0eae..dcb8e0cfaa1a 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1228,12 +1228,13 @@ static void set_abs_position_params(struct input_dev *dev, input_abs_set_res(dev, y_code, info->y_res); } -static void set_input_params(struct psmouse *psmouse, - struct synaptics_data *priv) +static int set_input_params(struct psmouse *psmouse, + struct synaptics_data *priv) { struct input_dev *dev = psmouse->dev; struct synaptics_device_info *info = &priv->info; int i; + int error; /* Reset default psmouse capabilities */ __clear_bit(EV_REL, dev->evbit); @@ -1256,7 +1257,7 @@ static void set_input_params(struct psmouse *psmouse, /* Relative mode */ input_set_capability(dev, EV_REL, REL_X); input_set_capability(dev, EV_REL, REL_Y); - return; + return 0; } /* Absolute mode */ @@ -1271,7 +1272,11 @@ static void set_input_params(struct psmouse *psmouse, ABS_MT_POSITION_X, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); - input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK); + + error = input_mt_init_slots(dev, 2, + INPUT_MT_POINTER | INPUT_MT_TRACK); + if (error) + return error; /* Image sensors can signal 4 and 5 finger clicks */ input_set_capability(dev, EV_KEY, BTN_TOOL_QUADTAP); @@ -1283,10 +1288,13 @@ static void set_input_params(struct psmouse *psmouse, * Profile sensor in CR-48 tracks contacts reasonably well, * other non-image sensors with AGM use semi-mt. */ - input_mt_init_slots(dev, 2, - INPUT_MT_POINTER | - (cr48_profile_sensor ? - INPUT_MT_TRACK : INPUT_MT_SEMI_MT)); + error = input_mt_init_slots(dev, 2, + INPUT_MT_POINTER | + (cr48_profile_sensor ? + INPUT_MT_TRACK : + INPUT_MT_SEMI_MT)); + if (error) + return error; /* * For semi-mt devices we send ABS_X/Y ourselves instead of @@ -1326,6 +1334,8 @@ static void set_input_params(struct psmouse *psmouse, !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10)) __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); } + + return 0; } static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse, @@ -1563,7 +1573,12 @@ static int synaptics_init_ps2(struct psmouse *psmouse, info->capabilities, info->ext_cap, info->ext_cap_0c, info->ext_cap_10, info->board_id, info->firmware_id); - set_input_params(psmouse, priv); + err = set_input_params(psmouse, priv); + if (err) { + psmouse_err(psmouse, + "failed to set up capabilities: %d\n", err); + goto init_fail; + } /* * Encode touchpad model so that it can be used to set -- cgit v1.3-14-g43fede From 2cf2f0d5b91fd1b06a6ae260462fc7945ea84add Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Feb 2018 15:59:40 +0100 Subject: kdb: use memmove instead of overlapping memcpy gcc discovered that the memcpy() arguments in kdbnearsym() overlap, so we should really use memmove(), which is defined to handle that correctly: In function 'memcpy', inlined from 'kdbnearsym' at /git/arm-soc/kernel/debug/kdb/kdb_support.c:132:4: /git/arm-soc/include/linux/string.h:353:9: error: '__builtin_memcpy' accessing 792 bytes at offsets 0 and 8 overlaps 784 bytes at offset 8 [-Werror=restrict] return __builtin_memcpy(p, q, size); Signed-off-by: Arnd Bergmann Signed-off-by: Jason Wessel --- kernel/debug/kdb/kdb_support.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/debug/kdb/kdb_support.c b/kernel/debug/kdb/kdb_support.c index d35cc2d3a4cc..990b3cc526c8 100644 --- a/kernel/debug/kdb/kdb_support.c +++ b/kernel/debug/kdb/kdb_support.c @@ -129,13 +129,13 @@ int kdbnearsym(unsigned long addr, kdb_symtab_t *symtab) } if (i >= ARRAY_SIZE(kdb_name_table)) { debug_kfree(kdb_name_table[0]); - memcpy(kdb_name_table, kdb_name_table+1, + memmove(kdb_name_table, kdb_name_table+1, sizeof(kdb_name_table[0]) * (ARRAY_SIZE(kdb_name_table)-1)); } else { debug_kfree(knt1); knt1 = kdb_name_table[i]; - memcpy(kdb_name_table+i, kdb_name_table+i+1, + memmove(kdb_name_table+i, kdb_name_table+i+1, sizeof(kdb_name_table[0]) * (ARRAY_SIZE(kdb_name_table)-i-1)); } -- cgit v1.3-14-g43fede From 097114aa6eb2aa206c8cf136de77ebffe424234c Mon Sep 17 00:00:00 2001 From: Dave Young Date: Sat, 27 Jan 2018 12:11:29 +0800 Subject: print kdump kernel loaded status in stack dump It is useful to print kdump kernel loaded status in dump_stack() especially when panic happens so that we can differenciate kdump kernel early hang and a normal panic in a bug report. Link: http://lkml.kernel.org/r/20180127041129.GA29016@dhcp-128-65.nay.redhat.com To: Steven Rostedt To: Andi Kleen To: kexec@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: akpm@linux-foundation.org Cc: kexec@lists.infradead.org Signed-off-by: Dave Young Reviewed-by: Sergey Senozhatsky Reviewed-by: Simon Horman Signed-off-by: Petr Mladek --- kernel/printk/printk.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index db4b9b8929eb..fa3de5f10e0e 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -3287,9 +3288,11 @@ void __init dump_stack_set_arch_desc(const char *fmt, ...) */ void dump_stack_print_info(const char *log_lvl) { - printk("%sCPU: %d PID: %d Comm: %.20s %s %s %.*s\n", + printk("%sCPU: %d PID: %d Comm: %.20s %s%s %s %.*s\n", log_lvl, raw_smp_processor_id(), current->pid, current->comm, - print_tainted(), init_utsname()->release, + kexec_crash_loaded() ? "Kdump: loaded " : "", + print_tainted(), + init_utsname()->release, (int)strcspn(init_utsname()->version, " "), init_utsname()->version); -- cgit v1.3-14-g43fede From 040d9e2bce0a5b321c402b79ee43a8e8d2fd3b06 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 23 Jan 2018 01:47:42 -0800 Subject: apparmor: fix display of .ns_name for containers The .ns_name should not be virtualized by the current ns view. It needs to report the ns base name as that is being used during startup as part of determining apparmor policy namespace support. BugLink: http://bugs.launchpad.net/bugs/1746463 Fixes: d9f02d9c237aa ("apparmor: fix display of ns name") Cc: Stable Reported-by: Serge Hallyn Tested-by: Serge Hallyn Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d4fa04d91439..a23b0ca19fd0 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1189,9 +1189,7 @@ static int seq_ns_level_show(struct seq_file *seq, void *v) static int seq_ns_name_show(struct seq_file *seq, void *v) { struct aa_label *label = begin_current_label_crit_section(); - - seq_printf(seq, "%s\n", aa_ns_name(labels_ns(label), - labels_ns(label), true)); + seq_printf(seq, "%s\n", labels_ns(label)->base.name); end_current_label_crit_section(label); return 0; -- cgit v1.3-14-g43fede From b5beb07ad32ab533027aa988d96a44965ec116f7 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 9 Feb 2018 04:57:39 -0800 Subject: apparmor: fix resource audit messages when auditing peer Resource auditing is using the peer field which is not available when the rlim data struct is used, because it is a different element of the same union. Accessing peer during resource auditing could cause garbage log entries or even oops the kernel. Move the rlim data block into the same struct as the peer field so they can be used together. CC: Fixes: 86b92cb782b3 ("apparmor: move resource checks to using labels") Signed-off-by: John Johansen --- security/apparmor/include/audit.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4ac095118717..2ebc00a579fd 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -126,6 +126,10 @@ struct apparmor_audit_data { const char *target; kuid_t ouid; } fs; + struct { + int rlim; + unsigned long max; + } rlim; int signal; }; }; @@ -134,10 +138,6 @@ struct apparmor_audit_data { const char *ns; long pos; } iface; - struct { - int rlim; - unsigned long max; - } rlim; struct { const char *src_name; const char *type; -- cgit v1.3-14-g43fede From 98cf5bbff413eadf1b9cb195a7b80cc61c72a50e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 1 Feb 2018 11:24:10 +0100 Subject: apparmor: fix logging of the existence test for signals The existence test is not being properly logged as the signal mapping maps it to the last entry in the named signal table. This is done to help catch bugs by making the 0 mapped signal value invalid so that we can catch the signal value not being filled in. When fixing the off-by-one comparision logic the reporting of the existence test was broken, because the logic behind the mapped named table was hidden. Fix this by adding a define for the name lookup and using it. Cc: Stable Fixes: f7dc4c9a855a1 ("apparmor: fix off-by-one comparison on MAXMAPPED_SIG") Signed-off-by: John Johansen --- security/apparmor/include/sig_names.h | 4 +++- security/apparmor/ipc.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h index 92e62fe95292..5ca47c50dfa7 100644 --- a/security/apparmor/include/sig_names.h +++ b/security/apparmor/include/sig_names.h @@ -2,6 +2,8 @@ #define SIGUNKNOWN 0 #define MAXMAPPED_SIG 35 +#define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1) + /* provide a mapping of arch signal to internal signal # for mediation * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO * map to the same entry those that may/or may not get a separate entry @@ -56,7 +58,7 @@ static const int sig_map[MAXMAPPED_SIG] = { }; /* this table is ordered post sig_map[sig] mapping */ -static const char *const sig_names[MAXMAPPED_SIG + 1] = { +static const char *const sig_names[MAXMAPPED_SIGNAME] = { "unknown", "hup", "int", diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index b40678f3c1d5..586facd35f7c 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -174,7 +174,7 @@ static void audit_signal_cb(struct audit_buffer *ab, void *va) audit_signal_mask(ab, aad(sa)->denied); } } - if (aad(sa)->signal < MAXMAPPED_SIG) + if (aad(sa)->signal < MAXMAPPED_SIGNAME) audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]); else audit_log_format(ab, " signal=rtmin+%d", -- cgit v1.3-14-g43fede From a6a52579e52b55448326db88bd9a5740e7c1a037 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 3 Feb 2018 20:08:28 +0100 Subject: apparmor: split load data into management struct and data blob Splitting the management struct from the actual data blob will allow us in the future to do some sharing and other data reduction techniques like replacing the the raw data with compressed data. Prepare for this by separating the management struct from the data blob. Signed-off-by: John Johansen --- security/apparmor/include/policy_unpack.h | 2 +- security/apparmor/policy_unpack.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index be6cd69ac319..8db4ab759e80 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -70,7 +70,7 @@ struct aa_loaddata { int abi; unsigned char *hash; - char data[]; + char *data; }; int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 59a1a25b7d43..ece0c246cfe6 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -164,8 +164,9 @@ static void do_loaddata_free(struct work_struct *work) } kzfree(d->hash); - kfree(d->name); - kvfree(d); + kzfree(d->name); + kvfree(d->data); + kzfree(d); } void aa_loaddata_kref(struct kref *kref) @@ -180,10 +181,16 @@ void aa_loaddata_kref(struct kref *kref) struct aa_loaddata *aa_loaddata_alloc(size_t size) { - struct aa_loaddata *d = kvzalloc(sizeof(*d) + size, GFP_KERNEL); + struct aa_loaddata *d; + d = kzalloc(sizeof(*d), GFP_KERNEL); if (d == NULL) return ERR_PTR(-ENOMEM); + d->data = kvzalloc(size, GFP_KERNEL); + if (!d->data) { + kfree(d); + return ERR_PTR(-ENOMEM); + } kref_init(&d->count); INIT_LIST_HEAD(&d->list); -- cgit v1.3-14-g43fede From cf65fabc2a2c8c12031678d86a2bd4a660865011 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 6 Sep 2017 02:53:15 -0700 Subject: apparmor: add first substr match to dfa Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/include/match.h | 4 ++ security/apparmor/match.c | 120 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index add4c6726558..72b9b89670e6 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -129,6 +129,10 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, const char *str); unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, const char c); +unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, + const char *str, const char **retpos); +unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, + const char *str, int n, const char **retpos); void aa_dfa_free_kref(struct kref *kref); diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 72c604350e80..6c6dc1a22f9a 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -457,3 +457,123 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, return state; } + +/** + * aa_dfa_match_until - traverse @dfa until accept state or end of input + * @dfa: the dfa to match @str against (NOT NULL) + * @start: the state of the dfa to start matching in + * @str: the null terminated string of bytes to match against the dfa (NOT NULL) + * @retpos: first character in str after match OR end of string + * + * aa_dfa_match will match @str against the dfa and return the state it + * finished matching in. The final state can be used to look up the accepting + * label, or as the start state of a continuing match. + * + * Returns: final state reached after input is consumed + */ +unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, + const char *str, const char **retpos) +{ + u16 *def = DEFAULT_TABLE(dfa); + u32 *base = BASE_TABLE(dfa); + u16 *next = NEXT_TABLE(dfa); + u16 *check = CHECK_TABLE(dfa); + u32 *accept = ACCEPT_TABLE(dfa); + unsigned int state = start, pos; + + if (state == 0) + return 0; + + /* current state is , matching character *str */ + if (dfa->tables[YYTD_ID_EC]) { + /* Equivalence class table defined */ + u8 *equiv = EQUIV_TABLE(dfa); + /* default is direct to next state */ + while (*str) { + pos = base_idx(base[state]) + equiv[(u8) *str++]; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + if (accept[state]) + break; + } + } else { + /* default is direct to next state */ + while (*str) { + pos = base_idx(base[state]) + (u8) *str++; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + if (accept[state]) + break; + } + } + + *retpos = str; + return state; +} + + +/** + * aa_dfa_matchn_until - traverse @dfa until accept or @n bytes consumed + * @dfa: the dfa to match @str against (NOT NULL) + * @start: the state of the dfa to start matching in + * @str: the string of bytes to match against the dfa (NOT NULL) + * @n: length of the string of bytes to match + * @retpos: first character in str after match OR str + n + * + * aa_dfa_match_len will match @str against the dfa and return the state it + * finished matching in. The final state can be used to look up the accepting + * label, or as the start state of a continuing match. + * + * This function will happily match again the 0 byte and only finishes + * when @n input is consumed. + * + * Returns: final state reached after input is consumed + */ +unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, + const char *str, int n, const char **retpos) +{ + u16 *def = DEFAULT_TABLE(dfa); + u32 *base = BASE_TABLE(dfa); + u16 *next = NEXT_TABLE(dfa); + u16 *check = CHECK_TABLE(dfa); + u32 *accept = ACCEPT_TABLE(dfa); + unsigned int state = start, pos; + + *retpos = NULL; + if (state == 0) + return 0; + + /* current state is , matching character *str */ + if (dfa->tables[YYTD_ID_EC]) { + /* Equivalence class table defined */ + u8 *equiv = EQUIV_TABLE(dfa); + /* default is direct to next state */ + for (; n; n--) { + pos = base_idx(base[state]) + equiv[(u8) *str++]; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + if (accept[state]) + break; + } + } else { + /* default is direct to next state */ + for (; n; n--) { + pos = base_idx(base[state]) + (u8) *str++; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + if (accept[state]) + break; + } + } + + *retpos = str; + return state; +} -- cgit v1.3-14-g43fede From 6e0654d20ed9679cbf75a0ff7cd786e364f7f09a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 6 Sep 2017 14:57:59 -0700 Subject: apparmor: use the dfa to do label parse string splitting The current split scheme is actually wrong in that it splits ///& where that is invalid and should fail. Use the dfa to do a proper bounded split without having to worry about getting the string processing right in code. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/include/label.h | 25 ++++++++ security/apparmor/include/match.h | 1 + security/apparmor/label.c | 12 ++-- security/apparmor/match.c | 29 ++++++++-- security/apparmor/stacksplitdfa.in | 114 +++++++++++++++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 security/apparmor/stacksplitdfa.in diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index af22dcbbcb8a..80e9ba9d172c 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -330,6 +330,31 @@ void aa_label_printk(struct aa_label *label, gfp_t gfp); struct aa_label *aa_label_parse(struct aa_label *base, const char *str, gfp_t gfp, bool create, bool force_stack); +static inline const char *aa_label_strn_split(const char *str, int n) +{ + const char *pos; + unsigned int state; + + state = aa_dfa_matchn_until(stacksplitdfa, DFA_START, str, n, &pos); + if (!ACCEPT_TABLE(stacksplitdfa)[state]) + return NULL; + + return pos - 3; +} + +static inline const char *aa_label_str_split(const char *str) +{ + const char *pos; + unsigned int state; + + state = aa_dfa_match_until(stacksplitdfa, DFA_START, str, &pos); + if (!ACCEPT_TABLE(stacksplitdfa)[state]) + return NULL; + + return pos - 3; +} + + struct aa_perms; int aa_label_match(struct aa_profile *profile, struct aa_label *label, diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 72b9b89670e6..cd8aeab6ac57 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -101,6 +101,7 @@ struct aa_dfa { }; extern struct aa_dfa *nulldfa; +extern struct aa_dfa *stacksplitdfa; #define byte_to_byte(X) (X) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 324fe5c60f87..31e2f701d971 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -1815,7 +1815,9 @@ static int label_count_str_entries(const char *str) AA_BUG(!str); - for (split = strstr(str, "//&"); split; split = strstr(str, "//&")) { + for (split = aa_label_str_split(str); + split; + split = aa_label_str_split(str)) { count++; str = split + 3; } @@ -1859,7 +1861,7 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str, DEFINE_VEC(profile, vec); struct aa_label *label, *currbase = base; int i, len, stack = 0, error; - char *split; + const char *split; AA_BUG(!base); AA_BUG(!str); @@ -1883,7 +1885,8 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str, for (i = 0; i < stack; i++) vec[i] = aa_get_profile(base->vec[i]); - for (split = strstr(str, "//&"), i = stack; split && i < len; i++) { + for (split = aa_label_str_split(str), i = stack; + split && i < len; i++) { vec[i] = fqlookupn_profile(base, currbase, str, split - str); if (!vec[i]) goto fail; @@ -1894,7 +1897,7 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str, if (vec[i]->ns != labels_ns(currbase)) currbase = &vec[i]->label; str = split + 3; - split = strstr(str, "//&"); + split = aa_label_str_split(str); } /* last element doesn't have a split */ if (i < len) { @@ -1930,7 +1933,6 @@ fail: goto out; } - /** * aa_labelset_destroy - remove all labels from the label set * @ls: label set to cleanup (NOT NULL) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 6c6dc1a22f9a..5d95caeddebc 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -30,6 +30,11 @@ static char nulldfa_src[] = { }; struct aa_dfa *nulldfa; +static char stacksplitdfa_src[] = { + #include "stacksplitdfa.in" +}; +struct aa_dfa *stacksplitdfa; + int aa_setup_dfa_engine(void) { int error; @@ -37,19 +42,31 @@ int aa_setup_dfa_engine(void) nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src), TO_ACCEPT1_FLAG(YYTD_DATA32) | TO_ACCEPT2_FLAG(YYTD_DATA32)); - if (!IS_ERR(nulldfa)) - return 0; + if (IS_ERR(nulldfa)) { + error = PTR_ERR(nulldfa); + nulldfa = NULL; + return error; + } - error = PTR_ERR(nulldfa); - nulldfa = NULL; + stacksplitdfa = aa_dfa_unpack(stacksplitdfa_src, + sizeof(stacksplitdfa_src), + TO_ACCEPT1_FLAG(YYTD_DATA32) | + TO_ACCEPT2_FLAG(YYTD_DATA32)); + if (IS_ERR(stacksplitdfa)) { + aa_put_dfa(nulldfa); + nulldfa = NULL; + error = PTR_ERR(stacksplitdfa); + stacksplitdfa = NULL; + return error; + } - return error; + return 0; } void aa_teardown_dfa_engine(void) { + aa_put_dfa(stacksplitdfa); aa_put_dfa(nulldfa); - nulldfa = NULL; } /** diff --git a/security/apparmor/stacksplitdfa.in b/security/apparmor/stacksplitdfa.in new file mode 100644 index 000000000000..4bddd10b62a9 --- /dev/null +++ b/security/apparmor/stacksplitdfa.in @@ -0,0 +1,114 @@ +/* 0x1 [^\000]*[^/\000]//& */ 0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, +0x00, 0x18, 0x00, 0x00, 0x04, 0xD8, 0x00, 0x00, 0x6E, 0x6F, 0x74, +0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, +0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, +0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, +0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, +0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x08, 0x00, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, +0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, +0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, +0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00 -- cgit v1.3-14-g43fede From 95652cac83605d96cf3849e80e3e3f4dce74f5da Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 6 Sep 2017 16:33:56 -0700 Subject: apparmor: provide a bounded version of label_parse some label/context sources might not be guaranteed to be null terminiated provide a size bounded version of label parse to deal with these. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/include/label.h | 3 +++ security/apparmor/label.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 80e9ba9d172c..d871e7ff0952 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -327,6 +327,9 @@ void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp); void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp); void aa_label_printk(struct aa_label *label, gfp_t gfp); +struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, + size_t n, gfp_t gfp, bool create, + bool force_stack); struct aa_label *aa_label_parse(struct aa_label *base, const char *str, gfp_t gfp, bool create, bool force_stack); diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 31e2f701d971..4721338ad551 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -1808,16 +1808,17 @@ void aa_label_printk(struct aa_label *label, gfp_t gfp) aa_put_ns(ns); } -static int label_count_str_entries(const char *str) +static int label_count_strn_entries(const char *str, size_t n) { + const char *end = str + n; const char *split; int count = 1; AA_BUG(!str); - for (split = aa_label_str_split(str); + for (split = aa_label_strn_split(str, end - str); split; - split = aa_label_str_split(str)) { + split = aa_label_strn_split(str, end - str)) { count++; str = split + 3; } @@ -1845,9 +1846,10 @@ static struct aa_profile *fqlookupn_profile(struct aa_label *base, } /** - * aa_label_parse - parse, validate and convert a text string to a label + * aa_label_strn_parse - parse, validate and convert a text string to a label * @base: base label to use for lookups (NOT NULL) * @str: null terminated text string (NOT NULL) + * @n: length of str to parse, will stop at \0 if encountered before n * @gfp: allocation type * @create: true if should create compound labels if they don't exist * @force_stack: true if should stack even if no leading & @@ -1855,19 +1857,23 @@ static struct aa_profile *fqlookupn_profile(struct aa_label *base, * Returns: the matching refcounted label if present * else ERRPTR */ -struct aa_label *aa_label_parse(struct aa_label *base, const char *str, - gfp_t gfp, bool create, bool force_stack) +struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, + size_t n, gfp_t gfp, bool create, + bool force_stack) { DEFINE_VEC(profile, vec); struct aa_label *label, *currbase = base; int i, len, stack = 0, error; + const char *end = str + n; const char *split; AA_BUG(!base); AA_BUG(!str); - str = skip_spaces(str); - len = label_count_str_entries(str); + str = skipn_spaces(str, n); + if (str == NULL) + return ERR_PTR(-EINVAL); + len = label_count_strn_entries(str, end - str); if (*str == '&' || force_stack) { /* stack on top of base */ stack = base->size; @@ -1885,7 +1891,7 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str, for (i = 0; i < stack; i++) vec[i] = aa_get_profile(base->vec[i]); - for (split = aa_label_str_split(str), i = stack; + for (split = aa_label_strn_split(str, end - str), i = stack; split && i < len; i++) { vec[i] = fqlookupn_profile(base, currbase, str, split - str); if (!vec[i]) @@ -1897,11 +1903,11 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str, if (vec[i]->ns != labels_ns(currbase)) currbase = &vec[i]->label; str = split + 3; - split = aa_label_str_split(str); + split = aa_label_strn_split(str, end - str); } /* last element doesn't have a split */ if (i < len) { - vec[i] = fqlookupn_profile(base, currbase, str, strlen(str)); + vec[i] = fqlookupn_profile(base, currbase, str, end - str); if (!vec[i]) goto fail; } @@ -1933,6 +1939,13 @@ fail: goto out; } +struct aa_label *aa_label_parse(struct aa_label *base, const char *str, + gfp_t gfp, bool create, bool force_stack) +{ + return aa_label_strn_parse(base, str, strlen(str), gfp, create, + force_stack); +} + /** * aa_labelset_destroy - remove all labels from the label set * @ls: label set to cleanup (NOT NULL) -- cgit v1.3-14-g43fede From 71fa373b784e13eb8d68f41b68b9482241e5288c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Sep 2017 12:57:39 -0700 Subject: apparmor: cleanup add proper line wrapping to nulldfa.in nulldfa.in makes for a very long unwrapped line, which certain tools do not like. So add line breaks. Signed-off-by: John Johansen --- security/apparmor/nulldfa.in | 108 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/security/apparmor/nulldfa.in b/security/apparmor/nulldfa.in index 3cb38022902e..095f42a24cbc 100644 --- a/security/apparmor/nulldfa.in +++ b/security/apparmor/nulldfa.in @@ -1 +1,107 @@ -0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04, 0x90, 0x00, 0x00, 0x6E, 0x6F, 0x74, 0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04, +0x90, 0x00, 0x00, 0x6E, 0x6F, 0x74, 0x66, 0x6C, 0x65, 0x78, 0x00, +0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00 -- cgit v1.3-14-g43fede From 475bdda1f00074783e18403f3f38a36dd3488430 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 8 Sep 2017 01:13:41 -0700 Subject: apparmor: root view labels should not be under user control The root view of the label parse should not be exposed to user control. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/label.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 4721338ad551..69c7451becef 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -1871,8 +1871,9 @@ struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, AA_BUG(!str); str = skipn_spaces(str, n); - if (str == NULL) + if (str == NULL || (*str == '=' && base != &root_ns->unconfined->label)) return ERR_PTR(-EINVAL); + len = label_count_strn_entries(str, end - str); if (*str == '&' || force_stack) { /* stack on top of base */ @@ -1881,8 +1882,6 @@ struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, if (*str == '&') str++; } - if (*str == '=') - base = &root_ns->unconfined->label; error = vec_setup(profile, vec, len, gfp); if (error) -- cgit v1.3-14-g43fede From 1d6583d9c6723d78e446dd203ffd974f6b85ab76 Mon Sep 17 00:00:00 2001 From: Pravin Shedge Date: Wed, 6 Dec 2017 23:05:59 +0530 Subject: security: apparmor: remove duplicate includes These duplicate includes have been found with scripts/checkincludes.pl but they have been removed manually to avoid removing false positives. Signed-off-by: Pravin Shedge Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index a23b0ca19fd0..00fc4f9f7f14 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -33,7 +33,6 @@ #include "include/context.h" #include "include/crypto.h" #include "include/ipc.h" -#include "include/policy_ns.h" #include "include/label.h" #include "include/policy.h" #include "include/policy_ns.h" -- cgit v1.3-14-g43fede From 3dc6b1ce6861ebf40b68ab4b752a05584a1f99bf Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 12 Dec 2017 01:02:13 -0800 Subject: apparmor: make signal label match work when matching stacked labels Given a label with a profile stack of A//&B or A//&C ... A ptrace rule should be able to specify a generic trace pattern with a rule like signal send A//&**, however this is failing because while the correct label match routine is called, it is being done post label decomposition so it is always being done against a profile instead of the stacked label. To fix this refactor the cross check to pass the full peer label in to the label_match. Signed-off-by: John Johansen --- security/apparmor/ipc.c | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 586facd35f7c..754f2ff8d355 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -184,50 +184,34 @@ static void audit_signal_cb(struct audit_buffer *ab, void *va) FLAGS_NONE, GFP_ATOMIC); } -/* TODO: update to handle compound name&name2, conditionals */ -static void profile_match_signal(struct aa_profile *profile, const char *label, - int signal, struct aa_perms *perms) -{ - unsigned int state; - - /* TODO: secondary cache check */ - state = aa_dfa_next(profile->policy.dfa, - profile->policy.start[AA_CLASS_SIGNAL], - signal); - state = aa_dfa_match(profile->policy.dfa, state, label); - aa_compute_perms(profile->policy.dfa, state, perms); -} - static int profile_signal_perm(struct aa_profile *profile, - struct aa_profile *peer, u32 request, + struct aa_label *peer, u32 request, struct common_audit_data *sa) { struct aa_perms perms; + unsigned int state; if (profile_unconfined(profile) || !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) return 0; - aad(sa)->peer = &peer->label; - profile_match_signal(profile, peer->base.hname, aad(sa)->signal, - &perms); + aad(sa)->peer = peer; + /* TODO: secondary cache check */ + state = aa_dfa_next(profile->policy.dfa, + profile->policy.start[AA_CLASS_SIGNAL], + aad(sa)->signal); + aa_label_match(profile, peer, state, false, request, &perms); aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); } -static int aa_signal_cross_perm(struct aa_profile *sender, - struct aa_profile *target, - struct common_audit_data *sa) -{ - return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa), - profile_signal_perm(target, sender, MAY_READ, sa)); -} - int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig) { + struct aa_profile *profile; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL); aad(&sa)->signal = map_signal_num(sig); - return xcheck_labels_profiles(sender, target, aa_signal_cross_perm, - &sa); + return xcheck_labels(sender, target, profile, + profile_signal_perm(profile, target, MAY_WRITE, &sa), + profile_signal_perm(profile, sender, MAY_READ, &sa)); } -- cgit v1.3-14-g43fede From 3acfd5f54ca16c15c36ac2f218357f2707b7edb8 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 1 Feb 2018 12:32:02 +0100 Subject: apparmor: audit unknown signal numbers Allow apparmor to audit the number of a signal that it does not provide a mapping for and is currently being reported only as unknown. Signed-off-by: John Johansen --- security/apparmor/include/audit.h | 5 ++++- security/apparmor/include/sig_names.h | 1 + security/apparmor/ipc.c | 10 +++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 2ebc00a579fd..41ad2c947bf4 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -130,7 +130,10 @@ struct apparmor_audit_data { int rlim; unsigned long max; } rlim; - int signal; + struct { + int signal; + int unmappedsig; + }; }; }; struct { diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h index 5ca47c50dfa7..cbf7a997ed84 100644 --- a/security/apparmor/include/sig_names.h +++ b/security/apparmor/include/sig_names.h @@ -3,6 +3,7 @@ #define SIGUNKNOWN 0 #define MAXMAPPED_SIG 35 #define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1) +#define SIGRT_BASE 128 /* provide a mapping of arch signal to internal signal # for mediation * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 754f2ff8d355..d7b137d4eb74 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -138,7 +138,7 @@ static inline int map_signal_num(int sig) if (sig > SIGRTMAX) return SIGUNKNOWN; else if (sig >= SIGRTMIN) - return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */ + return sig - SIGRTMIN + SIGRT_BASE; else if (sig < MAXMAPPED_SIG) return sig_map[sig]; return SIGUNKNOWN; @@ -174,11 +174,14 @@ static void audit_signal_cb(struct audit_buffer *ab, void *va) audit_signal_mask(ab, aad(sa)->denied); } } - if (aad(sa)->signal < MAXMAPPED_SIGNAME) + if (aad(sa)->signal == SIGUNKNOWN) + audit_log_format(ab, "signal=unknown(%d)", + aad(sa)->unmappedsig); + else if (aad(sa)->signal < MAXMAPPED_SIGNAME) audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]); else audit_log_format(ab, " signal=rtmin+%d", - aad(sa)->signal - 128); + aad(sa)->signal - SIGRT_BASE); audit_log_format(ab, " peer="); aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, FLAGS_NONE, GFP_ATOMIC); @@ -211,6 +214,7 @@ int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig) DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL); aad(&sa)->signal = map_signal_num(sig); + aad(&sa)->unmappedsig = sig; return xcheck_labels(sender, target, profile, profile_signal_perm(profile, target, MAY_WRITE, &sa), profile_signal_perm(profile, sender, MAY_READ, &sa)); -- cgit v1.3-14-g43fede From 4d2f8ba3e3b76e34f84ae1de456934713e9e59af Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 19 Jan 2017 14:08:36 -0800 Subject: apparmor: rename task_ctx to the more accurate cred_ctx Signed-off-by: John Johansen --- security/apparmor/context.c | 38 ++++++++++++++++++------------------- security/apparmor/domain.c | 6 +++--- security/apparmor/include/context.h | 19 +++++++++---------- security/apparmor/lsm.c | 26 ++++++++++++------------- security/apparmor/policy.c | 2 +- 5 files changed, 45 insertions(+), 46 deletions(-) diff --git a/security/apparmor/context.c b/security/apparmor/context.c index c95f1ac6190b..89c03053303e 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -13,11 +13,11 @@ * License. * * - * AppArmor sets confinement on every task, via the the aa_task_ctx and - * the aa_task_ctx.label, both of which are required and are not allowed - * to be NULL. The aa_task_ctx is not reference counted and is unique + * AppArmor sets confinement on every task, via the the aa_cred_ctx and + * the aa_cred_ctx.label, both of which are required and are not allowed + * to be NULL. The aa_cred_ctx is not reference counted and is unique * to each cred (which is reference count). The label pointed to by - * the task_ctx is reference counted. + * the cred_ctx is reference counted. * * TODO * If a task uses change_hat it currently does not return to the old @@ -30,21 +30,21 @@ #include "include/policy.h" /** - * aa_alloc_task_context - allocate a new task_ctx + * aa_alloc_cred_ctx - allocate a new cred_ctx * @flags: gfp flags for allocation * * Returns: allocated buffer or NULL on failure */ -struct aa_task_ctx *aa_alloc_task_context(gfp_t flags) +struct aa_cred_ctx *aa_alloc_cred_ctx(gfp_t flags) { - return kzalloc(sizeof(struct aa_task_ctx), flags); + return kzalloc(sizeof(struct aa_cred_ctx), flags); } /** - * aa_free_task_context - free a task_ctx - * @ctx: task_ctx to free (MAYBE NULL) + * aa_free_cred_ctx - free a cred_ctx + * @ctx: cred_ctx to free (MAYBE NULL) */ -void aa_free_task_context(struct aa_task_ctx *ctx) +void aa_free_cred_ctx(struct aa_cred_ctx *ctx) { if (ctx) { aa_put_label(ctx->label); @@ -56,11 +56,11 @@ void aa_free_task_context(struct aa_task_ctx *ctx) } /** - * aa_dup_task_context - duplicate a task context, incrementing reference counts + * aa_dup_cred_ctx - duplicate a task context, incrementing reference counts * @new: a blank task context (NOT NULL) * @old: the task context to copy (NOT NULL) */ -void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old) +void aa_dup_cred_ctx(struct aa_cred_ctx *new, const struct aa_cred_ctx *old) { *new = *old; aa_get_label(new->label); @@ -93,7 +93,7 @@ struct aa_label *aa_get_task_label(struct task_struct *task) */ int aa_replace_current_label(struct aa_label *label) { - struct aa_task_ctx *ctx = current_ctx(); + struct aa_cred_ctx *ctx = current_cred_ctx(); struct cred *new; AA_BUG(!label); @@ -112,7 +112,7 @@ int aa_replace_current_label(struct aa_label *label) /* if switching to unconfined or a different label namespace * clear out context state */ - aa_clear_task_ctx_trans(ctx); + aa_clear_cred_ctx_trans(ctx); /* * be careful switching ctx->profile, when racing replacement it @@ -136,14 +136,14 @@ int aa_replace_current_label(struct aa_label *label) */ int aa_set_current_onexec(struct aa_label *label, bool stack) { - struct aa_task_ctx *ctx; + struct aa_cred_ctx *ctx; struct cred *new = prepare_creds(); if (!new) return -ENOMEM; ctx = cred_ctx(new); aa_get_label(label); - aa_clear_task_ctx_trans(ctx); + aa_clear_cred_ctx_trans(ctx); ctx->onexec = label; ctx->token = stack; @@ -163,7 +163,7 @@ int aa_set_current_onexec(struct aa_label *label, bool stack) */ int aa_set_current_hat(struct aa_label *label, u64 token) { - struct aa_task_ctx *ctx; + struct aa_cred_ctx *ctx; struct cred *new = prepare_creds(); if (!new) return -ENOMEM; @@ -201,7 +201,7 @@ int aa_set_current_hat(struct aa_label *label, u64 token) */ int aa_restore_previous_label(u64 token) { - struct aa_task_ctx *ctx; + struct aa_cred_ctx *ctx; struct cred *new = prepare_creds(); if (!new) return -ENOMEM; @@ -221,7 +221,7 @@ int aa_restore_previous_label(u64 token) ctx->label = aa_get_newest_label(ctx->previous); AA_BUG(!ctx->label); /* clear exec && prev information when restoring to previous context */ - aa_clear_task_ctx_trans(ctx); + aa_clear_cred_ctx_trans(ctx); commit_creds(new); return 0; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 6a54d2ffa840..90967de96be0 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -779,7 +779,7 @@ static struct aa_label *handle_onexec(struct aa_label *label, */ int apparmor_bprm_set_creds(struct linux_binprm *bprm) { - struct aa_task_ctx *ctx; + struct aa_cred_ctx *ctx; struct aa_label *label, *new = NULL; struct aa_profile *profile; char *buffer = NULL; @@ -859,7 +859,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) done: /* clear out temporary/transitional state from the context */ - aa_clear_task_ctx_trans(ctx); + aa_clear_cred_ctx_trans(ctx); aa_put_label(label); put_buffers(buffer); @@ -1049,7 +1049,7 @@ build: int aa_change_hat(const char *hats[], int count, u64 token, int flags) { const struct cred *cred; - struct aa_task_ctx *ctx; + struct aa_cred_ctx *ctx; struct aa_label *label, *previous, *new = NULL, *target = NULL; struct aa_profile *profile; struct aa_perms perms = {}; diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index 6ae07e9aaa17..0622fcf2a695 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -23,10 +23,10 @@ #include "policy_ns.h" #define cred_ctx(X) ((X)->security) -#define current_ctx() cred_ctx(current_cred()) +#define current_cred_ctx() cred_ctx(current_cred()) /** - * struct aa_task_ctx - primary label for confined tasks + * struct aa_cred_ctx - primary label for confined tasks * @label: the current label (NOT NULL) * @exec: label to transition to on next exec (MAYBE NULL) * @previous: label the task may return to (MAYBE NULL) @@ -37,17 +37,16 @@ * * TODO: make so a task can be confined by a stack of contexts */ -struct aa_task_ctx { +struct aa_cred_ctx { struct aa_label *label; struct aa_label *onexec; struct aa_label *previous; u64 token; }; -struct aa_task_ctx *aa_alloc_task_context(gfp_t flags); -void aa_free_task_context(struct aa_task_ctx *ctx); -void aa_dup_task_context(struct aa_task_ctx *new, - const struct aa_task_ctx *old); +struct aa_cred_ctx *aa_alloc_cred_ctx(gfp_t flags); +void aa_free_cred_ctx(struct aa_cred_ctx *ctx); +void aa_dup_cred_ctx(struct aa_cred_ctx *new, const struct aa_cred_ctx *old); int aa_replace_current_label(struct aa_label *label); int aa_set_current_onexec(struct aa_label *label, bool stack); int aa_set_current_hat(struct aa_label *label, u64 token); @@ -65,7 +64,7 @@ struct aa_label *aa_get_task_label(struct task_struct *task); */ static inline struct aa_label *aa_cred_raw_label(const struct cred *cred) { - struct aa_task_ctx *ctx = cred_ctx(cred); + struct aa_cred_ctx *ctx = cred_ctx(cred); AA_BUG(!ctx || !ctx->label); return ctx->label; @@ -214,10 +213,10 @@ static inline struct aa_ns *aa_get_current_ns(void) } /** - * aa_clear_task_ctx_trans - clear transition tracking info from the ctx + * aa_clear_cred_ctx_trans - clear transition tracking info from the ctx * @ctx: task context to clear (NOT NULL) */ -static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) +static inline void aa_clear_cred_ctx_trans(struct aa_cred_ctx *ctx) { aa_put_label(ctx->previous); aa_put_label(ctx->onexec); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 9a65eeaf7dfa..0624eb2081f3 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -51,11 +51,11 @@ DEFINE_PER_CPU(struct aa_buffers, aa_buffers); */ /* - * free the associated aa_task_ctx and put its labels + * free the associated aa_cred_ctx and put its labels */ static void apparmor_cred_free(struct cred *cred) { - aa_free_task_context(cred_ctx(cred)); + aa_free_cred_ctx(cred_ctx(cred)); cred_ctx(cred) = NULL; } @@ -65,7 +65,7 @@ static void apparmor_cred_free(struct cred *cred) static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) { /* freed by apparmor_cred_free */ - struct aa_task_ctx *ctx = aa_alloc_task_context(gfp); + struct aa_cred_ctx *ctx = aa_alloc_cred_ctx(gfp); if (!ctx) return -ENOMEM; @@ -75,18 +75,18 @@ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) } /* - * prepare new aa_task_ctx for modification by prepare_cred block + * prepare new aa_cred_ctx for modification by prepare_cred block */ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { /* freed by apparmor_cred_free */ - struct aa_task_ctx *ctx = aa_alloc_task_context(gfp); + struct aa_cred_ctx *ctx = aa_alloc_cred_ctx(gfp); if (!ctx) return -ENOMEM; - aa_dup_task_context(ctx, cred_ctx(old)); + aa_dup_cred_ctx(ctx, cred_ctx(old)); cred_ctx(new) = ctx; return 0; } @@ -96,10 +96,10 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, */ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) { - const struct aa_task_ctx *old_ctx = cred_ctx(old); - struct aa_task_ctx *new_ctx = cred_ctx(new); + const struct aa_cred_ctx *old_ctx = cred_ctx(old); + struct aa_cred_ctx *new_ctx = cred_ctx(new); - aa_dup_task_context(new_ctx, old_ctx); + aa_dup_cred_ctx(new_ctx, old_ctx); } static int apparmor_ptrace_access_check(struct task_struct *child, @@ -577,7 +577,7 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, int error = -ENOENT; /* released below */ const struct cred *cred = get_task_cred(task); - struct aa_task_ctx *ctx = cred_ctx(cred); + struct aa_cred_ctx *ctx = cred_ctx(cred); struct aa_label *label = NULL; if (strcmp(name, "current") == 0) @@ -678,7 +678,7 @@ fail: static void apparmor_bprm_committing_creds(struct linux_binprm *bprm) { struct aa_label *label = aa_current_raw_label(); - struct aa_task_ctx *new_ctx = cred_ctx(bprm->cred); + struct aa_cred_ctx *new_ctx = cred_ctx(bprm->cred); /* bail out if unconfined or not changing profile */ if ((new_ctx->label->proxy == label->proxy) || @@ -1024,9 +1024,9 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) static int __init set_init_ctx(void) { struct cred *cred = (struct cred *)current->real_cred; - struct aa_task_ctx *ctx; + struct aa_cred_ctx *ctx; - ctx = aa_alloc_task_context(GFP_KERNEL); + ctx = aa_alloc_cred_ctx(GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index b0b58848c248..c505d517fa3c 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -845,7 +845,7 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new) * @udata: serialized data stream (NOT NULL) * * unpack and replace a profile on the profile list and uses of that profile - * by any aa_task_ctx. If the profile does not exist on the profile list + * by any aa_cred_ctx. If the profile does not exist on the profile list * it is added. * * Returns: size of data consumed else error code on failure. -- cgit v1.3-14-g43fede From 3b529a7600d834f450ac244f43a7c082687284b4 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 20 Jan 2017 01:59:25 -0800 Subject: apparmor: move task domain change info to task security The task domain change info is task specific and its and abuse of the cred to store the information in there. Now that a task->security field exists store it in the proper place. Signed-off-by: John Johansen --- security/apparmor/context.c | 91 +++++++++++++++++++++++++------------ security/apparmor/domain.c | 14 +++--- security/apparmor/include/context.h | 31 +++++++++---- security/apparmor/lsm.c | 48 ++++++++++++++++--- 4 files changed, 132 insertions(+), 52 deletions(-) diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 89c03053303e..432672b18945 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -48,8 +48,6 @@ void aa_free_cred_ctx(struct aa_cred_ctx *ctx) { if (ctx) { aa_put_label(ctx->label); - aa_put_label(ctx->previous); - aa_put_label(ctx->onexec); kzfree(ctx); } @@ -64,8 +62,6 @@ void aa_dup_cred_ctx(struct aa_cred_ctx *new, const struct aa_cred_ctx *old) { *new = *old; aa_get_label(new->label); - aa_get_label(new->previous); - aa_get_label(new->onexec); } /** @@ -85,6 +81,43 @@ struct aa_label *aa_get_task_label(struct task_struct *task) return p; } +/** + * aa_alloc_task_ctx - allocate a new task_ctx + * @flags: gfp flags for allocation + * + * Returns: allocated buffer or NULL on failure + */ +struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags) +{ + return kzalloc(sizeof(struct aa_task_ctx), flags); +} + +/** + * aa_free_task_ctx - free a task_ctx + * @ctx: task_ctx to free (MAYBE NULL) + */ +void aa_free_task_ctx(struct aa_task_ctx *ctx) +{ + if (ctx) { + aa_put_label(ctx->previous); + aa_put_label(ctx->onexec); + + kzfree(ctx); + } +} + +/** + * aa_dup_task_ctx - duplicate a task context, incrementing reference counts + * @new: a blank task context (NOT NULL) + * @old: the task context to copy (NOT NULL) + */ +void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old) +{ + *new = *old; + aa_get_label(new->previous); + aa_get_label(new->onexec); +} + /** * aa_replace_current_label - replace the current tasks label * @label: new label (NOT NULL) @@ -112,7 +145,7 @@ int aa_replace_current_label(struct aa_label *label) /* if switching to unconfined or a different label namespace * clear out context state */ - aa_clear_cred_ctx_trans(ctx); + aa_clear_task_ctx_trans(current_task_ctx()); /* * be careful switching ctx->profile, when racing replacement it @@ -136,18 +169,13 @@ int aa_replace_current_label(struct aa_label *label) */ int aa_set_current_onexec(struct aa_label *label, bool stack) { - struct aa_cred_ctx *ctx; - struct cred *new = prepare_creds(); - if (!new) - return -ENOMEM; + struct aa_task_ctx *ctx = current_task_ctx(); - ctx = cred_ctx(new); aa_get_label(label); - aa_clear_cred_ctx_trans(ctx); + aa_put_label(ctx->onexec); ctx->onexec = label; ctx->token = stack; - commit_creds(new); return 0; } @@ -163,28 +191,31 @@ int aa_set_current_onexec(struct aa_label *label, bool stack) */ int aa_set_current_hat(struct aa_label *label, u64 token) { + struct aa_task_ctx *tctx = current_task_ctx(); struct aa_cred_ctx *ctx; struct cred *new = prepare_creds(); + if (!new) return -ENOMEM; AA_BUG(!label); ctx = cred_ctx(new); - if (!ctx->previous) { + if (!tctx->previous) { /* transfer refcount */ - ctx->previous = ctx->label; - ctx->token = token; - } else if (ctx->token == token) { + tctx->previous = ctx->label; + tctx->token = token; + } else if (tctx->token == token) { aa_put_label(ctx->label); } else { /* previous_profile && ctx->token != token */ abort_creds(new); return -EACCES; } + ctx->label = aa_get_newest_label(label); /* clear exec on switching context */ - aa_put_label(ctx->onexec); - ctx->onexec = NULL; + aa_put_label(tctx->onexec); + tctx->onexec = NULL; commit_creds(new); return 0; @@ -201,28 +232,28 @@ int aa_set_current_hat(struct aa_label *label, u64 token) */ int aa_restore_previous_label(u64 token) { + struct aa_task_ctx *tctx = current_task_ctx(); struct aa_cred_ctx *ctx; - struct cred *new = prepare_creds(); - if (!new) - return -ENOMEM; + struct cred *new; - ctx = cred_ctx(new); - if (ctx->token != token) { - abort_creds(new); + if (tctx->token != token) return -EACCES; - } /* ignore restores when there is no saved label */ - if (!ctx->previous) { - abort_creds(new); + if (!tctx->previous) return 0; - } + + new = prepare_creds(); + if (!new) + return -ENOMEM; + ctx = cred_ctx(new); aa_put_label(ctx->label); - ctx->label = aa_get_newest_label(ctx->previous); + ctx->label = aa_get_newest_label(tctx->previous); AA_BUG(!ctx->label); /* clear exec && prev information when restoring to previous context */ - aa_clear_cred_ctx_trans(ctx); + aa_clear_task_ctx_trans(tctx); commit_creds(new); + return 0; } diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 90967de96be0..b90759a765b5 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -780,6 +780,7 @@ static struct aa_label *handle_onexec(struct aa_label *label, int apparmor_bprm_set_creds(struct linux_binprm *bprm) { struct aa_cred_ctx *ctx; + struct aa_task_ctx *tctx; struct aa_label *label, *new = NULL; struct aa_profile *profile; char *buffer = NULL; @@ -795,15 +796,17 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) return 0; ctx = cred_ctx(bprm->cred); + tctx = current_task_ctx(); AA_BUG(!ctx); + AA_BUG(!tctx); label = aa_get_newest_label(ctx->label); /* buffer freed below, name is pointer into buffer */ get_buffers(buffer); /* Test for onexec first as onexec override other x transitions. */ - if (ctx->onexec) - new = handle_onexec(label, ctx->onexec, ctx->token, + if (tctx->onexec) + new = handle_onexec(label, tctx->onexec, tctx->token, bprm, buffer, &cond, &unsafe); else new = fn_label_build(label, profile, GFP_ATOMIC, @@ -858,9 +861,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ctx->label = new; done: - /* clear out temporary/transitional state from the context */ - aa_clear_cred_ctx_trans(ctx); - aa_put_label(label); put_buffers(buffer); @@ -1050,6 +1050,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) { const struct cred *cred; struct aa_cred_ctx *ctx; + struct aa_task_ctx *tctx; struct aa_label *label, *previous, *new = NULL, *target = NULL; struct aa_profile *profile; struct aa_perms perms = {}; @@ -1070,8 +1071,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) /* released below */ cred = get_current_cred(); ctx = cred_ctx(cred); + tctx = current_task_ctx(); label = aa_get_newest_cred_label(cred); - previous = aa_get_newest_label(ctx->previous); + previous = aa_get_newest_label(tctx->previous); if (unconfined(label)) { info = "unconfined can not change_hat"; diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index 0622fcf2a695..c3b51d88275b 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -25,20 +25,24 @@ #define cred_ctx(X) ((X)->security) #define current_cred_ctx() cred_ctx(current_cred()) +#define task_ctx(X) ((X)->security) +#define current_task_ctx() (task_ctx(current)) + /** * struct aa_cred_ctx - primary label for confined tasks * @label: the current label (NOT NULL) - * @exec: label to transition to on next exec (MAYBE NULL) - * @previous: label the task may return to (MAYBE NULL) - * @token: magic value the task must know for returning to @previous - * - * Contains the task's current label (which could change due to - * change_hat). Plus the hat_magic needed during change_hat. - * - * TODO: make so a task can be confined by a stack of contexts */ struct aa_cred_ctx { struct aa_label *label; +}; + +/** + * struct aa_task_ctx - information for current task label change + * @onexec: profile to transition to on next exec (MAY BE NULL) + * @previous: profile the task may return to (MAY BE NULL) + * @token: magic value the task must know for returning to @previous_profile + */ +struct aa_task_ctx { struct aa_label *onexec; struct aa_label *previous; u64 token; @@ -47,6 +51,11 @@ struct aa_cred_ctx { struct aa_cred_ctx *aa_alloc_cred_ctx(gfp_t flags); void aa_free_cred_ctx(struct aa_cred_ctx *ctx); void aa_dup_cred_ctx(struct aa_cred_ctx *new, const struct aa_cred_ctx *old); + +struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags); +void aa_free_task_ctx(struct aa_task_ctx *ctx); +void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old); + int aa_replace_current_label(struct aa_label *label); int aa_set_current_onexec(struct aa_label *label, bool stack); int aa_set_current_hat(struct aa_label *label, u64 token); @@ -213,11 +222,13 @@ static inline struct aa_ns *aa_get_current_ns(void) } /** - * aa_clear_cred_ctx_trans - clear transition tracking info from the ctx + * aa_clear_task_ctx_trans - clear transition tracking info from the ctx * @ctx: task context to clear (NOT NULL) */ -static inline void aa_clear_cred_ctx_trans(struct aa_cred_ctx *ctx) +static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) { + AA_BUG(!ctx); + aa_put_label(ctx->previous); aa_put_label(ctx->onexec); ctx->previous = NULL; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 0624eb2081f3..a1d63d93b862 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -102,6 +102,27 @@ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) aa_dup_cred_ctx(new_ctx, old_ctx); } +static void apparmor_task_free(struct task_struct *task) +{ + + aa_free_task_ctx(task_ctx(task)); + task_ctx(task) = NULL; +} + +static int apparmor_task_alloc(struct task_struct *task, + unsigned long clone_flags) +{ + struct aa_task_ctx *new = aa_alloc_task_ctx(GFP_KERNEL); + + if (!new) + return -ENOMEM; + + aa_dup_task_ctx(new, current_task_ctx()); + task_ctx(task) = new; + + return 0; +} + static int apparmor_ptrace_access_check(struct task_struct *child, unsigned int mode) { @@ -577,15 +598,16 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, int error = -ENOENT; /* released below */ const struct cred *cred = get_task_cred(task); + struct aa_task_ctx *tctx = current_task_ctx(); struct aa_cred_ctx *ctx = cred_ctx(cred); struct aa_label *label = NULL; if (strcmp(name, "current") == 0) label = aa_get_newest_label(ctx->label); - else if (strcmp(name, "prev") == 0 && ctx->previous) - label = aa_get_newest_label(ctx->previous); - else if (strcmp(name, "exec") == 0 && ctx->onexec) - label = aa_get_newest_label(ctx->onexec); + else if (strcmp(name, "prev") == 0 && tctx->previous) + label = aa_get_newest_label(tctx->previous); + else if (strcmp(name, "exec") == 0 && tctx->onexec) + label = aa_get_newest_label(tctx->onexec); else error = -EINVAL; @@ -699,7 +721,9 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm) */ static void apparmor_bprm_committed_creds(struct linux_binprm *bprm) { - /* TODO: cleanup signals - ipc mediation */ + /* clear out temporary/transitional state from the context */ + aa_clear_task_ctx_trans(current_task_ctx()); + return; } @@ -779,6 +803,8 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(bprm_committing_creds, apparmor_bprm_committing_creds), LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds), + LSM_HOOK_INIT(task_free, apparmor_task_free), + LSM_HOOK_INIT(task_alloc, apparmor_task_alloc), LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), LSM_HOOK_INIT(task_kill, apparmor_task_kill), }; @@ -1025,15 +1051,25 @@ static int __init set_init_ctx(void) { struct cred *cred = (struct cred *)current->real_cred; struct aa_cred_ctx *ctx; + struct aa_task_ctx *tctx; ctx = aa_alloc_cred_ctx(GFP_KERNEL); if (!ctx) - return -ENOMEM; + goto fail_cred; + tctx = aa_alloc_task_ctx(GFP_KERNEL); + if (!tctx) + goto fail_task; ctx->label = aa_get_label(ns_unconfined(root_ns)); cred_ctx(cred) = ctx; + task_ctx(current) = tctx; return 0; + +fail_task: + aa_free_cred_ctx(ctx); +fail_cred: + return -ENOMEM; } static void destroy_buffers(void) -- cgit v1.3-14-g43fede From d9087c49d4388e3f35f09a5cf7ed6e09c9106604 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 27 Jan 2017 03:53:53 -0800 Subject: apparmor: drop cred_ctx and reference the label directly With the task domain change information now stored in the task->security context, the cred->security context only stores the label. We can get rid of the cred_ctx and directly reference the label, removing a layer of indirection, and unneeded extra allocations. Signed-off-by: John Johansen --- security/apparmor/context.c | 83 ++++++++++--------------------------- security/apparmor/domain.c | 14 +++---- security/apparmor/include/context.h | 24 +++-------- security/apparmor/lsm.c | 55 +++++++----------------- 4 files changed, 47 insertions(+), 129 deletions(-) diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 432672b18945..70e4a094add8 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -13,11 +13,9 @@ * License. * * - * AppArmor sets confinement on every task, via the the aa_cred_ctx and - * the aa_cred_ctx.label, both of which are required and are not allowed - * to be NULL. The aa_cred_ctx is not reference counted and is unique - * to each cred (which is reference count). The label pointed to by - * the cred_ctx is reference counted. + * AppArmor sets confinement on every task, via the cred_label() which + * is required and are not allowed to be NULL. The cred_label is + * reference counted. * * TODO * If a task uses change_hat it currently does not return to the old @@ -29,40 +27,6 @@ #include "include/context.h" #include "include/policy.h" -/** - * aa_alloc_cred_ctx - allocate a new cred_ctx - * @flags: gfp flags for allocation - * - * Returns: allocated buffer or NULL on failure - */ -struct aa_cred_ctx *aa_alloc_cred_ctx(gfp_t flags) -{ - return kzalloc(sizeof(struct aa_cred_ctx), flags); -} - -/** - * aa_free_cred_ctx - free a cred_ctx - * @ctx: cred_ctx to free (MAYBE NULL) - */ -void aa_free_cred_ctx(struct aa_cred_ctx *ctx) -{ - if (ctx) { - aa_put_label(ctx->label); - - kzfree(ctx); - } -} - -/** - * aa_dup_cred_ctx - duplicate a task context, incrementing reference counts - * @new: a blank task context (NOT NULL) - * @old: the task context to copy (NOT NULL) - */ -void aa_dup_cred_ctx(struct aa_cred_ctx *new, const struct aa_cred_ctx *old) -{ - *new = *old; - aa_get_label(new->label); -} /** * aa_get_task_label - Get another task's label @@ -126,11 +90,12 @@ void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old) */ int aa_replace_current_label(struct aa_label *label) { - struct aa_cred_ctx *ctx = current_cred_ctx(); + struct aa_label *old = aa_current_raw_label(); struct cred *new; + AA_BUG(!label); - if (ctx->label == label) + if (old == label) return 0; if (current_cred() != current_real_cred()) @@ -140,22 +105,22 @@ int aa_replace_current_label(struct aa_label *label) if (!new) return -ENOMEM; - ctx = cred_ctx(new); - if (unconfined(label) || (labels_ns(ctx->label) != labels_ns(label))) - /* if switching to unconfined or a different label namespace + if (unconfined(label) || (labels_ns(old) != labels_ns(label))) + /* + * if switching to unconfined or a different label namespace * clear out context state */ aa_clear_task_ctx_trans(current_task_ctx()); /* - * be careful switching ctx->profile, when racing replacement it - * is possible that ctx->profile->proxy->profile is the reference - * keeping @profile valid, so make sure to get its reference before - * dropping the reference on ctx->profile + * be careful switching cred label, when racing replacement it + * is possible that the cred labels's->proxy->label is the reference + * keeping @label valid, so make sure to get its reference before + * dropping the reference on the cred's label */ aa_get_label(label); - aa_put_label(ctx->label); - ctx->label = label; + aa_put_label(cred_label(new)); + cred_label(new) = label; commit_creds(new); return 0; @@ -193,26 +158,26 @@ int aa_set_current_hat(struct aa_label *label, u64 token) { struct aa_task_ctx *tctx = current_task_ctx(); struct aa_cred_ctx *ctx; - struct cred *new = prepare_creds(); + struct cred *new; + new = prepare_creds(); if (!new) return -ENOMEM; AA_BUG(!label); - ctx = cred_ctx(new); if (!tctx->previous) { /* transfer refcount */ - tctx->previous = ctx->label; + tctx->previous = cred_label(new); tctx->token = token; } else if (tctx->token == token) { - aa_put_label(ctx->label); + aa_put_label(cred_label(new)); } else { /* previous_profile && ctx->token != token */ abort_creds(new); return -EACCES; } - ctx->label = aa_get_newest_label(label); + cred_label(new) = aa_get_newest_label(label); /* clear exec on switching context */ aa_put_label(tctx->onexec); tctx->onexec = NULL; @@ -233,7 +198,6 @@ int aa_set_current_hat(struct aa_label *label, u64 token) int aa_restore_previous_label(u64 token) { struct aa_task_ctx *tctx = current_task_ctx(); - struct aa_cred_ctx *ctx; struct cred *new; if (tctx->token != token) @@ -245,11 +209,10 @@ int aa_restore_previous_label(u64 token) new = prepare_creds(); if (!new) return -ENOMEM; - ctx = cred_ctx(new); - aa_put_label(ctx->label); - ctx->label = aa_get_newest_label(tctx->previous); - AA_BUG(!ctx->label); + aa_put_label(cred_label(new)); + cred_label(new) = aa_get_newest_label(tctx->previous); + AA_BUG(!cred_label(new)); /* clear exec && prev information when restoring to previous context */ aa_clear_task_ctx_trans(tctx); diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index b90759a765b5..5285938680e0 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -779,7 +779,6 @@ static struct aa_label *handle_onexec(struct aa_label *label, */ int apparmor_bprm_set_creds(struct linux_binprm *bprm) { - struct aa_cred_ctx *ctx; struct aa_task_ctx *tctx; struct aa_label *label, *new = NULL; struct aa_profile *profile; @@ -795,12 +794,11 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->called_set_creds) return 0; - ctx = cred_ctx(bprm->cred); tctx = current_task_ctx(); - AA_BUG(!ctx); + AA_BUG(!cred_label(bprm->cred)); AA_BUG(!tctx); - label = aa_get_newest_label(ctx->label); + label = aa_get_newest_label(cred_label(bprm->cred)); /* buffer freed below, name is pointer into buffer */ get_buffers(buffer); @@ -856,9 +854,9 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) } bprm->per_clear |= PER_CLEAR_ON_SETID; } - aa_put_label(ctx->label); - /* transfer reference, released when ctx is freed */ - ctx->label = new; + aa_put_label(cred_label(bprm->cred)); + /* transfer reference, released when cred is freed */ + cred_label(bprm->cred) = new; done: aa_put_label(label); @@ -1049,7 +1047,6 @@ build: int aa_change_hat(const char *hats[], int count, u64 token, int flags) { const struct cred *cred; - struct aa_cred_ctx *ctx; struct aa_task_ctx *tctx; struct aa_label *label, *previous, *new = NULL, *target = NULL; struct aa_profile *profile; @@ -1070,7 +1067,6 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) /* released below */ cred = get_current_cred(); - ctx = cred_ctx(cred); tctx = current_task_ctx(); label = aa_get_newest_cred_label(cred); previous = aa_get_newest_label(tctx->previous); diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index c3b51d88275b..8d36c14bc76d 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -22,21 +22,11 @@ #include "label.h" #include "policy_ns.h" -#define cred_ctx(X) ((X)->security) -#define current_cred_ctx() cred_ctx(current_cred()) - #define task_ctx(X) ((X)->security) #define current_task_ctx() (task_ctx(current)) +#define cred_label(X) ((X)->security) -/** - * struct aa_cred_ctx - primary label for confined tasks - * @label: the current label (NOT NULL) - */ -struct aa_cred_ctx { - struct aa_label *label; -}; - -/** +/* * struct aa_task_ctx - information for current task label change * @onexec: profile to transition to on next exec (MAY BE NULL) * @previous: profile the task may return to (MAY BE NULL) @@ -48,10 +38,6 @@ struct aa_task_ctx { u64 token; }; -struct aa_cred_ctx *aa_alloc_cred_ctx(gfp_t flags); -void aa_free_cred_ctx(struct aa_cred_ctx *ctx); -void aa_dup_cred_ctx(struct aa_cred_ctx *new, const struct aa_cred_ctx *old); - struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags); void aa_free_task_ctx(struct aa_task_ctx *ctx); void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old); @@ -73,10 +59,10 @@ struct aa_label *aa_get_task_label(struct task_struct *task); */ static inline struct aa_label *aa_cred_raw_label(const struct cred *cred) { - struct aa_cred_ctx *ctx = cred_ctx(cred); + struct aa_label *label = cred_label(cred); - AA_BUG(!ctx || !ctx->label); - return ctx->label; + AA_BUG(!label); + return label; } /** diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index a1d63d93b862..628c6a07df64 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -51,12 +51,12 @@ DEFINE_PER_CPU(struct aa_buffers, aa_buffers); */ /* - * free the associated aa_cred_ctx and put its labels + * put the associated labels */ static void apparmor_cred_free(struct cred *cred) { - aa_free_cred_ctx(cred_ctx(cred)); - cred_ctx(cred) = NULL; + aa_put_label(cred_label(cred)); + cred_label(cred) = NULL; } /* @@ -64,30 +64,17 @@ static void apparmor_cred_free(struct cred *cred) */ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) { - /* freed by apparmor_cred_free */ - struct aa_cred_ctx *ctx = aa_alloc_cred_ctx(gfp); - - if (!ctx) - return -ENOMEM; - - cred_ctx(cred) = ctx; + cred_label(cred) = NULL; return 0; } /* - * prepare new aa_cred_ctx for modification by prepare_cred block + * prepare new cred label for modification by prepare_cred block */ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { - /* freed by apparmor_cred_free */ - struct aa_cred_ctx *ctx = aa_alloc_cred_ctx(gfp); - - if (!ctx) - return -ENOMEM; - - aa_dup_cred_ctx(ctx, cred_ctx(old)); - cred_ctx(new) = ctx; + cred_label(new) = aa_get_newest_label(cred_label(old)); return 0; } @@ -96,10 +83,7 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, */ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) { - const struct aa_cred_ctx *old_ctx = cred_ctx(old); - struct aa_cred_ctx *new_ctx = cred_ctx(new); - - aa_dup_cred_ctx(new_ctx, old_ctx); + cred_label(new) = aa_get_newest_label(cred_label(old)); } static void apparmor_task_free(struct task_struct *task) @@ -599,11 +583,10 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, /* released below */ const struct cred *cred = get_task_cred(task); struct aa_task_ctx *tctx = current_task_ctx(); - struct aa_cred_ctx *ctx = cred_ctx(cred); struct aa_label *label = NULL; if (strcmp(name, "current") == 0) - label = aa_get_newest_label(ctx->label); + label = aa_get_newest_label(cred_label(cred)); else if (strcmp(name, "prev") == 0 && tctx->previous) label = aa_get_newest_label(tctx->previous); else if (strcmp(name, "exec") == 0 && tctx->onexec) @@ -700,11 +683,11 @@ fail: static void apparmor_bprm_committing_creds(struct linux_binprm *bprm) { struct aa_label *label = aa_current_raw_label(); - struct aa_cred_ctx *new_ctx = cred_ctx(bprm->cred); + struct aa_label *new_label = cred_label(bprm->cred); /* bail out if unconfined or not changing profile */ - if ((new_ctx->label->proxy == label->proxy) || - (unconfined(new_ctx->label))) + if ((new_label->proxy == label->proxy) || + (unconfined(new_label))) return; aa_inherit_files(bprm->cred, current->files); @@ -712,7 +695,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm) current->pdeath_signal = 0; /* reset soft limits and set hard limits for the new label */ - __aa_transition_rlimits(label, new_ctx->label); + __aa_transition_rlimits(label, new_label); } /** @@ -1050,26 +1033,16 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) static int __init set_init_ctx(void) { struct cred *cred = (struct cred *)current->real_cred; - struct aa_cred_ctx *ctx; struct aa_task_ctx *tctx; - ctx = aa_alloc_cred_ctx(GFP_KERNEL); - if (!ctx) - goto fail_cred; tctx = aa_alloc_task_ctx(GFP_KERNEL); if (!tctx) - goto fail_task; + return -ENOMEM; - ctx->label = aa_get_label(ns_unconfined(root_ns)); - cred_ctx(cred) = ctx; + cred_label(cred) = aa_get_label(ns_unconfined(root_ns)); task_ctx(current) = tctx; return 0; - -fail_task: - aa_free_cred_ctx(ctx); -fail_cred: - return -ENOMEM; } static void destroy_buffers(void) -- cgit v1.3-14-g43fede From f175221af35bedf99b201d861a0fe54e19ef36c2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 27 Jan 2017 04:09:40 -0800 Subject: apparmor: rename tctx to ctx now that cred_ctx has been removed we can rename task_ctxs from tctx without causing confusion. Signed-off-by: John Johansen --- security/apparmor/context.c | 25 ++++++++++++------------- security/apparmor/domain.c | 16 ++++++++-------- security/apparmor/lsm.c | 18 +++++++++--------- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 70e4a094add8..d95a3d47cb92 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -156,8 +156,7 @@ int aa_set_current_onexec(struct aa_label *label, bool stack) */ int aa_set_current_hat(struct aa_label *label, u64 token) { - struct aa_task_ctx *tctx = current_task_ctx(); - struct aa_cred_ctx *ctx; + struct aa_task_ctx *ctx = current_task_ctx(); struct cred *new; new = prepare_creds(); @@ -165,11 +164,11 @@ int aa_set_current_hat(struct aa_label *label, u64 token) return -ENOMEM; AA_BUG(!label); - if (!tctx->previous) { + if (!ctx->previous) { /* transfer refcount */ - tctx->previous = cred_label(new); - tctx->token = token; - } else if (tctx->token == token) { + ctx->previous = cred_label(new); + ctx->token = token; + } else if (ctx->token == token) { aa_put_label(cred_label(new)); } else { /* previous_profile && ctx->token != token */ @@ -179,8 +178,8 @@ int aa_set_current_hat(struct aa_label *label, u64 token) cred_label(new) = aa_get_newest_label(label); /* clear exec on switching context */ - aa_put_label(tctx->onexec); - tctx->onexec = NULL; + aa_put_label(ctx->onexec); + ctx->onexec = NULL; commit_creds(new); return 0; @@ -197,13 +196,13 @@ int aa_set_current_hat(struct aa_label *label, u64 token) */ int aa_restore_previous_label(u64 token) { - struct aa_task_ctx *tctx = current_task_ctx(); + struct aa_task_ctx *ctx = current_task_ctx(); struct cred *new; - if (tctx->token != token) + if (ctx->token != token) return -EACCES; /* ignore restores when there is no saved label */ - if (!tctx->previous) + if (!ctx->previous) return 0; new = prepare_creds(); @@ -211,10 +210,10 @@ int aa_restore_previous_label(u64 token) return -ENOMEM; aa_put_label(cred_label(new)); - cred_label(new) = aa_get_newest_label(tctx->previous); + cred_label(new) = aa_get_newest_label(ctx->previous); AA_BUG(!cred_label(new)); /* clear exec && prev information when restoring to previous context */ - aa_clear_task_ctx_trans(tctx); + aa_clear_task_ctx_trans(ctx); commit_creds(new); diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 5285938680e0..b180e10f2b86 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -779,7 +779,7 @@ static struct aa_label *handle_onexec(struct aa_label *label, */ int apparmor_bprm_set_creds(struct linux_binprm *bprm) { - struct aa_task_ctx *tctx; + struct aa_task_ctx *ctx; struct aa_label *label, *new = NULL; struct aa_profile *profile; char *buffer = NULL; @@ -794,17 +794,17 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->called_set_creds) return 0; - tctx = current_task_ctx(); + ctx = current_task_ctx(); AA_BUG(!cred_label(bprm->cred)); - AA_BUG(!tctx); + AA_BUG(!ctx); label = aa_get_newest_label(cred_label(bprm->cred)); /* buffer freed below, name is pointer into buffer */ get_buffers(buffer); /* Test for onexec first as onexec override other x transitions. */ - if (tctx->onexec) - new = handle_onexec(label, tctx->onexec, tctx->token, + if (ctx->onexec) + new = handle_onexec(label, ctx->onexec, ctx->token, bprm, buffer, &cond, &unsafe); else new = fn_label_build(label, profile, GFP_ATOMIC, @@ -1047,7 +1047,7 @@ build: int aa_change_hat(const char *hats[], int count, u64 token, int flags) { const struct cred *cred; - struct aa_task_ctx *tctx; + struct aa_task_ctx *ctx; struct aa_label *label, *previous, *new = NULL, *target = NULL; struct aa_profile *profile; struct aa_perms perms = {}; @@ -1067,9 +1067,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) /* released below */ cred = get_current_cred(); - tctx = current_task_ctx(); + ctx = current_task_ctx(); label = aa_get_newest_cred_label(cred); - previous = aa_get_newest_label(tctx->previous); + previous = aa_get_newest_label(ctx->previous); if (unconfined(label)) { info = "unconfined can not change_hat"; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 628c6a07df64..fda36f3e3820 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -582,15 +582,15 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, int error = -ENOENT; /* released below */ const struct cred *cred = get_task_cred(task); - struct aa_task_ctx *tctx = current_task_ctx(); + struct aa_task_ctx *ctx = current_task_ctx(); struct aa_label *label = NULL; if (strcmp(name, "current") == 0) label = aa_get_newest_label(cred_label(cred)); - else if (strcmp(name, "prev") == 0 && tctx->previous) - label = aa_get_newest_label(tctx->previous); - else if (strcmp(name, "exec") == 0 && tctx->onexec) - label = aa_get_newest_label(tctx->onexec); + else if (strcmp(name, "prev") == 0 && ctx->previous) + label = aa_get_newest_label(ctx->previous); + else if (strcmp(name, "exec") == 0 && ctx->onexec) + label = aa_get_newest_label(ctx->onexec); else error = -EINVAL; @@ -1033,14 +1033,14 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) static int __init set_init_ctx(void) { struct cred *cred = (struct cred *)current->real_cred; - struct aa_task_ctx *tctx; + struct aa_task_ctx *ctx; - tctx = aa_alloc_task_ctx(GFP_KERNEL); - if (!tctx) + ctx = aa_alloc_task_ctx(GFP_KERNEL); + if (!ctx) return -ENOMEM; cred_label(cred) = aa_get_label(ns_unconfined(root_ns)); - task_ctx(current) = tctx; + task_ctx(current) = ctx; return 0; } -- cgit v1.3-14-g43fede From e1a03f627b5254fa1ee83bd0761490f31ea2e382 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 27 Jan 2017 04:36:47 -0800 Subject: apparmor: cleanup fixup description of aa_replace_profiles Signed-off-by: John Johansen --- security/apparmor/policy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index c505d517fa3c..a158af1f1b38 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -845,8 +845,9 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new) * @udata: serialized data stream (NOT NULL) * * unpack and replace a profile on the profile list and uses of that profile - * by any aa_cred_ctx. If the profile does not exist on the profile list - * it is added. + * by any task creds via invalidating the old version of the profile, which + * tasks will notice to update their own cred. If the profile does not exist + * on the profile list it is added. * * Returns: size of data consumed else error code on failure. */ -- cgit v1.3-14-g43fede From d065f2f56522b9240acb8c5ea35e9ee25f1b33e6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 8 Oct 2017 00:25:27 -0700 Subject: apparmor: cleanup, drop unused fn __aa_task_is_confined() Signed-off-by: John Johansen --- security/apparmor/include/context.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index 8d36c14bc76d..b2aeb1da7e77 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -89,17 +89,6 @@ static inline struct aa_label *__aa_task_raw_label(struct task_struct *task) return aa_cred_raw_label(__task_cred(task)); } -/** - * __aa_task_is_confined - determine if @task has any confinement - * @task: task to check confinement of (NOT NULL) - * - * If @task != current needs to be called in RCU safe critical section - */ -static inline bool __aa_task_is_confined(struct task_struct *task) -{ - return !unconfined(__aa_task_raw_label(task)); -} - /** * aa_current_raw_label - find the current tasks confining label * -- cgit v1.3-14-g43fede From de62de59c27881c59c7df2e535cb9e1275cd52cc Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 8 Oct 2017 00:43:02 -0700 Subject: apparmor: move task related defines and fns to task.X files Signed-off-by: John Johansen --- security/apparmor/Makefile | 2 +- security/apparmor/context.c | 221 ------------------------------------ security/apparmor/domain.c | 4 +- security/apparmor/include/context.h | 40 +------ security/apparmor/include/task.h | 90 +++++++++++++++ security/apparmor/lsm.c | 6 +- security/apparmor/task.c | 176 ++++++++++++++++++++++++++++ 7 files changed, 273 insertions(+), 266 deletions(-) delete mode 100644 security/apparmor/context.c create mode 100644 security/apparmor/include/task.h create mode 100644 security/apparmor/task.c diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 9a6b4033d52b..380c8e08174a 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -3,7 +3,7 @@ # obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o -apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ +apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ resource.o secid.o file.o policy_ns.o label.o mount.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o diff --git a/security/apparmor/context.c b/security/apparmor/context.c deleted file mode 100644 index d95a3d47cb92..000000000000 --- a/security/apparmor/context.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * AppArmor security module - * - * This file contains AppArmor functions used to manipulate object security - * contexts. - * - * Copyright (C) 1998-2008 Novell/SUSE - * Copyright 2009-2010 Canonical Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - * - * - * AppArmor sets confinement on every task, via the cred_label() which - * is required and are not allowed to be NULL. The cred_label is - * reference counted. - * - * TODO - * If a task uses change_hat it currently does not return to the old - * cred or task context but instead creates a new one. Ideally the task - * should return to the previous cred if it has not been modified. - * - */ - -#include "include/context.h" -#include "include/policy.h" - - -/** - * aa_get_task_label - Get another task's label - * @task: task to query (NOT NULL) - * - * Returns: counted reference to @task's label - */ -struct aa_label *aa_get_task_label(struct task_struct *task) -{ - struct aa_label *p; - - rcu_read_lock(); - p = aa_get_newest_label(__aa_task_raw_label(task)); - rcu_read_unlock(); - - return p; -} - -/** - * aa_alloc_task_ctx - allocate a new task_ctx - * @flags: gfp flags for allocation - * - * Returns: allocated buffer or NULL on failure - */ -struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags) -{ - return kzalloc(sizeof(struct aa_task_ctx), flags); -} - -/** - * aa_free_task_ctx - free a task_ctx - * @ctx: task_ctx to free (MAYBE NULL) - */ -void aa_free_task_ctx(struct aa_task_ctx *ctx) -{ - if (ctx) { - aa_put_label(ctx->previous); - aa_put_label(ctx->onexec); - - kzfree(ctx); - } -} - -/** - * aa_dup_task_ctx - duplicate a task context, incrementing reference counts - * @new: a blank task context (NOT NULL) - * @old: the task context to copy (NOT NULL) - */ -void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old) -{ - *new = *old; - aa_get_label(new->previous); - aa_get_label(new->onexec); -} - -/** - * aa_replace_current_label - replace the current tasks label - * @label: new label (NOT NULL) - * - * Returns: 0 or error on failure - */ -int aa_replace_current_label(struct aa_label *label) -{ - struct aa_label *old = aa_current_raw_label(); - struct cred *new; - - AA_BUG(!label); - - if (old == label) - return 0; - - if (current_cred() != current_real_cred()) - return -EBUSY; - - new = prepare_creds(); - if (!new) - return -ENOMEM; - - if (unconfined(label) || (labels_ns(old) != labels_ns(label))) - /* - * if switching to unconfined or a different label namespace - * clear out context state - */ - aa_clear_task_ctx_trans(current_task_ctx()); - - /* - * be careful switching cred label, when racing replacement it - * is possible that the cred labels's->proxy->label is the reference - * keeping @label valid, so make sure to get its reference before - * dropping the reference on the cred's label - */ - aa_get_label(label); - aa_put_label(cred_label(new)); - cred_label(new) = label; - - commit_creds(new); - return 0; -} - -/** - * aa_set_current_onexec - set the tasks change_profile to happen onexec - * @label: system label to set at exec (MAYBE NULL to clear value) - * @stack: whether stacking should be done - * Returns: 0 or error on failure - */ -int aa_set_current_onexec(struct aa_label *label, bool stack) -{ - struct aa_task_ctx *ctx = current_task_ctx(); - - aa_get_label(label); - aa_put_label(ctx->onexec); - ctx->onexec = label; - ctx->token = stack; - - return 0; -} - -/** - * aa_set_current_hat - set the current tasks hat - * @label: label to set as the current hat (NOT NULL) - * @token: token value that must be specified to change from the hat - * - * Do switch of tasks hat. If the task is currently in a hat - * validate the token to match. - * - * Returns: 0 or error on failure - */ -int aa_set_current_hat(struct aa_label *label, u64 token) -{ - struct aa_task_ctx *ctx = current_task_ctx(); - struct cred *new; - - new = prepare_creds(); - if (!new) - return -ENOMEM; - AA_BUG(!label); - - if (!ctx->previous) { - /* transfer refcount */ - ctx->previous = cred_label(new); - ctx->token = token; - } else if (ctx->token == token) { - aa_put_label(cred_label(new)); - } else { - /* previous_profile && ctx->token != token */ - abort_creds(new); - return -EACCES; - } - - cred_label(new) = aa_get_newest_label(label); - /* clear exec on switching context */ - aa_put_label(ctx->onexec); - ctx->onexec = NULL; - - commit_creds(new); - return 0; -} - -/** - * aa_restore_previous_label - exit from hat context restoring previous label - * @token: the token that must be matched to exit hat context - * - * Attempt to return out of a hat to the previous label. The token - * must match the stored token value. - * - * Returns: 0 or error of failure - */ -int aa_restore_previous_label(u64 token) -{ - struct aa_task_ctx *ctx = current_task_ctx(); - struct cred *new; - - if (ctx->token != token) - return -EACCES; - /* ignore restores when there is no saved label */ - if (!ctx->previous) - return 0; - - new = prepare_creds(); - if (!new) - return -ENOMEM; - - aa_put_label(cred_label(new)); - cred_label(new) = aa_get_newest_label(ctx->previous); - AA_BUG(!cred_label(new)); - /* clear exec && prev information when restoring to previous context */ - aa_clear_task_ctx_trans(ctx); - - commit_creds(new); - - return 0; -} diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index b180e10f2b86..56d080a6d774 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -794,7 +794,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->called_set_creds) return 0; - ctx = current_task_ctx(); + ctx = task_ctx(current); AA_BUG(!cred_label(bprm->cred)); AA_BUG(!ctx); @@ -1067,7 +1067,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) /* released below */ cred = get_current_cred(); - ctx = current_task_ctx(); + ctx = task_ctx(current); label = aa_get_newest_cred_label(cred); previous = aa_get_newest_label(ctx->previous); diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index b2aeb1da7e77..e287b7d0d4be 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -21,33 +21,10 @@ #include "label.h" #include "policy_ns.h" +#include "task.h" -#define task_ctx(X) ((X)->security) -#define current_task_ctx() (task_ctx(current)) #define cred_label(X) ((X)->security) -/* - * struct aa_task_ctx - information for current task label change - * @onexec: profile to transition to on next exec (MAY BE NULL) - * @previous: profile the task may return to (MAY BE NULL) - * @token: magic value the task must know for returning to @previous_profile - */ -struct aa_task_ctx { - struct aa_label *onexec; - struct aa_label *previous; - u64 token; -}; - -struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags); -void aa_free_task_ctx(struct aa_task_ctx *ctx); -void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old); - -int aa_replace_current_label(struct aa_label *label); -int aa_set_current_onexec(struct aa_label *label, bool stack); -int aa_set_current_hat(struct aa_label *label, u64 token); -int aa_restore_previous_label(u64 cookie); -struct aa_label *aa_get_task_label(struct task_struct *task); - /** * aa_cred_raw_label - obtain cred's label @@ -196,19 +173,4 @@ static inline struct aa_ns *aa_get_current_ns(void) return ns; } -/** - * aa_clear_task_ctx_trans - clear transition tracking info from the ctx - * @ctx: task context to clear (NOT NULL) - */ -static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) -{ - AA_BUG(!ctx); - - aa_put_label(ctx->previous); - aa_put_label(ctx->onexec); - ctx->previous = NULL; - ctx->onexec = NULL; - ctx->token = 0; -} - #endif /* __AA_CONTEXT_H */ diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h new file mode 100644 index 000000000000..d222197db299 --- /dev/null +++ b/security/apparmor/include/task.h @@ -0,0 +1,90 @@ +/* + * AppArmor security module + * + * This file contains AppArmor task related definitions and mediation + * + * Copyright 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_TASK_H +#define __AA_TASK_H + +#define task_ctx(X) ((X)->security) + +/* + * struct aa_task_ctx - information for current task label change + * @onexec: profile to transition to on next exec (MAY BE NULL) + * @previous: profile the task may return to (MAY BE NULL) + * @token: magic value the task must know for returning to @previous_profile + */ +struct aa_task_ctx { + struct aa_label *onexec; + struct aa_label *previous; + u64 token; +}; + +int aa_replace_current_label(struct aa_label *label); +int aa_set_current_onexec(struct aa_label *label, bool stack); +int aa_set_current_hat(struct aa_label *label, u64 token); +int aa_restore_previous_label(u64 cookie); +struct aa_label *aa_get_task_label(struct task_struct *task); + +/** + * aa_alloc_task_ctx - allocate a new task_ctx + * @flags: gfp flags for allocation + * + * Returns: allocated buffer or NULL on failure + */ +static inline struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags) +{ + return kzalloc(sizeof(struct aa_task_ctx), flags); +} + +/** + * aa_free_task_ctx - free a task_ctx + * @ctx: task_ctx to free (MAYBE NULL) + */ +static inline void aa_free_task_ctx(struct aa_task_ctx *ctx) +{ + if (ctx) { + aa_put_label(ctx->previous); + aa_put_label(ctx->onexec); + + kzfree(ctx); + } +} + +/** + * aa_dup_task_ctx - duplicate a task context, incrementing reference counts + * @new: a blank task context (NOT NULL) + * @old: the task context to copy (NOT NULL) + */ +static inline void aa_dup_task_ctx(struct aa_task_ctx *new, + const struct aa_task_ctx *old) +{ + *new = *old; + aa_get_label(new->previous); + aa_get_label(new->onexec); +} + +/** + * aa_clear_task_ctx_trans - clear transition tracking info from the ctx + * @ctx: task context to clear (NOT NULL) + */ +static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) +{ + AA_BUG(!ctx); + + aa_put_label(ctx->previous); + aa_put_label(ctx->onexec); + ctx->previous = NULL; + ctx->onexec = NULL; + ctx->token = 0; +} + +#endif /* __AA_TASK_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index fda36f3e3820..7577cd982230 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -101,7 +101,7 @@ static int apparmor_task_alloc(struct task_struct *task, if (!new) return -ENOMEM; - aa_dup_task_ctx(new, current_task_ctx()); + aa_dup_task_ctx(new, task_ctx(current)); task_ctx(task) = new; return 0; @@ -582,7 +582,7 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, int error = -ENOENT; /* released below */ const struct cred *cred = get_task_cred(task); - struct aa_task_ctx *ctx = current_task_ctx(); + struct aa_task_ctx *ctx = task_ctx(current); struct aa_label *label = NULL; if (strcmp(name, "current") == 0) @@ -705,7 +705,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm) static void apparmor_bprm_committed_creds(struct linux_binprm *bprm) { /* clear out temporary/transitional state from the context */ - aa_clear_task_ctx_trans(current_task_ctx()); + aa_clear_task_ctx_trans(task_ctx(current)); return; } diff --git a/security/apparmor/task.c b/security/apparmor/task.c new file mode 100644 index 000000000000..36eb8707ad89 --- /dev/null +++ b/security/apparmor/task.c @@ -0,0 +1,176 @@ +/* + * AppArmor security module + * + * This file contains AppArmor task related definitions and mediation + * + * Copyright 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * TODO + * If a task uses change_hat it currently does not return to the old + * cred or task context but instead creates a new one. Ideally the task + * should return to the previous cred if it has not been modified. + */ + +#include "include/context.h" +#include "include/task.h" + +/** + * aa_get_task_label - Get another task's label + * @task: task to query (NOT NULL) + * + * Returns: counted reference to @task's label + */ +struct aa_label *aa_get_task_label(struct task_struct *task) +{ + struct aa_label *p; + + rcu_read_lock(); + p = aa_get_newest_label(__aa_task_raw_label(task)); + rcu_read_unlock(); + + return p; +} + +/** + * aa_replace_current_label - replace the current tasks label + * @label: new label (NOT NULL) + * + * Returns: 0 or error on failure + */ +int aa_replace_current_label(struct aa_label *label) +{ + struct aa_label *old = aa_current_raw_label(); + struct cred *new; + + AA_BUG(!label); + + if (old == label) + return 0; + + if (current_cred() != current_real_cred()) + return -EBUSY; + + new = prepare_creds(); + if (!new) + return -ENOMEM; + + if (unconfined(label) || (labels_ns(old) != labels_ns(label))) + /* + * if switching to unconfined or a different label namespace + * clear out context state + */ + aa_clear_task_ctx_trans(task_ctx(current)); + + /* + * be careful switching cred label, when racing replacement it + * is possible that the cred labels's->proxy->label is the reference + * keeping @label valid, so make sure to get its reference before + * dropping the reference on the cred's label + */ + aa_get_label(label); + aa_put_label(cred_label(new)); + cred_label(new) = label; + + commit_creds(new); + return 0; +} + + +/** + * aa_set_current_onexec - set the tasks change_profile to happen onexec + * @label: system label to set at exec (MAYBE NULL to clear value) + * @stack: whether stacking should be done + * Returns: 0 or error on failure + */ +int aa_set_current_onexec(struct aa_label *label, bool stack) +{ + struct aa_task_ctx *ctx = task_ctx(current); + + aa_get_label(label); + aa_put_label(ctx->onexec); + ctx->onexec = label; + ctx->token = stack; + + return 0; +} + +/** + * aa_set_current_hat - set the current tasks hat + * @label: label to set as the current hat (NOT NULL) + * @token: token value that must be specified to change from the hat + * + * Do switch of tasks hat. If the task is currently in a hat + * validate the token to match. + * + * Returns: 0 or error on failure + */ +int aa_set_current_hat(struct aa_label *label, u64 token) +{ + struct aa_task_ctx *ctx = task_ctx(current); + struct cred *new; + + new = prepare_creds(); + if (!new) + return -ENOMEM; + AA_BUG(!label); + + if (!ctx->previous) { + /* transfer refcount */ + ctx->previous = cred_label(new); + ctx->token = token; + } else if (ctx->token == token) { + aa_put_label(cred_label(new)); + } else { + /* previous_profile && ctx->token != token */ + abort_creds(new); + return -EACCES; + } + + cred_label(new) = aa_get_newest_label(label); + /* clear exec on switching context */ + aa_put_label(ctx->onexec); + ctx->onexec = NULL; + + commit_creds(new); + return 0; +} + +/** + * aa_restore_previous_label - exit from hat context restoring previous label + * @token: the token that must be matched to exit hat context + * + * Attempt to return out of a hat to the previous label. The token + * must match the stored token value. + * + * Returns: 0 or error of failure + */ +int aa_restore_previous_label(u64 token) +{ + struct aa_task_ctx *ctx = task_ctx(current); + struct cred *new; + + if (ctx->token != token) + return -EACCES; + /* ignore restores when there is no saved label */ + if (!ctx->previous) + return 0; + + new = prepare_creds(); + if (!new) + return -ENOMEM; + + aa_put_label(cred_label(new)); + cred_label(new) = aa_get_newest_label(ctx->previous); + AA_BUG(!cred_label(new)); + /* clear exec && prev information when restoring to previous context */ + aa_clear_task_ctx_trans(ctx); + + commit_creds(new); + + return 0; +} -- cgit v1.3-14-g43fede From d8889d49e414b371eb235c08c3a759ab3e0cfa51 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 11 Oct 2017 01:04:48 -0700 Subject: apparmor: move context.h to cred.h Now that file contexts have been moved into file, and task context fns() and data have been split from the context, only the cred context remains in context.h so rename to cred.h to better reflect what it deals with. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 2 +- security/apparmor/capability.c | 2 +- security/apparmor/domain.c | 2 +- security/apparmor/file.c | 2 +- security/apparmor/include/context.h | 176 ------------------------------------ security/apparmor/include/cred.h | 176 ++++++++++++++++++++++++++++++++++++ security/apparmor/ipc.c | 2 +- security/apparmor/label.c | 2 +- security/apparmor/lsm.c | 2 +- security/apparmor/mount.c | 2 +- security/apparmor/policy.c | 2 +- security/apparmor/policy_ns.c | 2 +- security/apparmor/policy_unpack.c | 2 +- security/apparmor/procattr.c | 2 +- security/apparmor/resource.c | 2 +- security/apparmor/task.c | 2 +- 16 files changed, 190 insertions(+), 190 deletions(-) delete mode 100644 security/apparmor/include/context.h create mode 100644 security/apparmor/include/cred.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 00fc4f9f7f14..874c1bf6b84a 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -30,7 +30,7 @@ #include "include/apparmor.h" #include "include/apparmorfs.h" #include "include/audit.h" -#include "include/context.h" +#include "include/cred.h" #include "include/crypto.h" #include "include/ipc.h" #include "include/label.h" diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 67e347192a55..253ef6e9d445 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -19,7 +19,7 @@ #include "include/apparmor.h" #include "include/capability.h" -#include "include/context.h" +#include "include/cred.h" #include "include/policy.h" #include "include/audit.h" diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 56d080a6d774..cd58eef4eb8d 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -22,7 +22,7 @@ #include "include/audit.h" #include "include/apparmorfs.h" -#include "include/context.h" +#include "include/cred.h" #include "include/domain.h" #include "include/file.h" #include "include/ipc.h" diff --git a/security/apparmor/file.c b/security/apparmor/file.c index e79bf44396a3..9a67a33904b3 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -18,7 +18,7 @@ #include "include/apparmor.h" #include "include/audit.h" -#include "include/context.h" +#include "include/cred.h" #include "include/file.h" #include "include/match.h" #include "include/path.h" diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h deleted file mode 100644 index e287b7d0d4be..000000000000 --- a/security/apparmor/include/context.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * AppArmor security module - * - * This file contains AppArmor contexts used to associate "labels" to objects. - * - * Copyright (C) 1998-2008 Novell/SUSE - * Copyright 2009-2010 Canonical Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - */ - -#ifndef __AA_CONTEXT_H -#define __AA_CONTEXT_H - -#include -#include -#include - -#include "label.h" -#include "policy_ns.h" -#include "task.h" - -#define cred_label(X) ((X)->security) - - -/** - * aa_cred_raw_label - obtain cred's label - * @cred: cred to obtain label from (NOT NULL) - * - * Returns: confining label - * - * does NOT increment reference count - */ -static inline struct aa_label *aa_cred_raw_label(const struct cred *cred) -{ - struct aa_label *label = cred_label(cred); - - AA_BUG(!label); - return label; -} - -/** - * aa_get_newest_cred_label - obtain the newest label on a cred - * @cred: cred to obtain label from (NOT NULL) - * - * Returns: newest version of confining label - */ -static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred) -{ - return aa_get_newest_label(aa_cred_raw_label(cred)); -} - -/** - * __aa_task_raw_label - retrieve another task's label - * @task: task to query (NOT NULL) - * - * Returns: @task's label without incrementing its ref count - * - * If @task != current needs to be called in RCU safe critical section - */ -static inline struct aa_label *__aa_task_raw_label(struct task_struct *task) -{ - return aa_cred_raw_label(__task_cred(task)); -} - -/** - * aa_current_raw_label - find the current tasks confining label - * - * Returns: up to date confining label or the ns unconfined label (NOT NULL) - * - * This fn will not update the tasks cred to the most up to date version - * of the label so it is safe to call when inside of locks. - */ -static inline struct aa_label *aa_current_raw_label(void) -{ - return aa_cred_raw_label(current_cred()); -} - -/** - * aa_get_current_label - get the newest version of the current tasks label - * - * Returns: newest version of confining label (NOT NULL) - * - * This fn will not update the tasks cred, so it is safe inside of locks - * - * The returned reference must be put with aa_put_label() - */ -static inline struct aa_label *aa_get_current_label(void) -{ - struct aa_label *l = aa_current_raw_label(); - - if (label_is_stale(l)) - return aa_get_newest_label(l); - return aa_get_label(l); -} - -#define __end_current_label_crit_section(X) end_current_label_crit_section(X) - -/** - * end_label_crit_section - put a reference found with begin_current_label.. - * @label: label reference to put - * - * Should only be used with a reference obtained with - * begin_current_label_crit_section and never used in situations where the - * task cred may be updated - */ -static inline void end_current_label_crit_section(struct aa_label *label) -{ - if (label != aa_current_raw_label()) - aa_put_label(label); -} - -/** - * __begin_current_label_crit_section - current's confining label - * - * Returns: up to date confining label or the ns unconfined label (NOT NULL) - * - * safe to call inside locks - * - * The returned reference must be put with __end_current_label_crit_section() - * This must NOT be used if the task cred could be updated within the - * critical section between __begin_current_label_crit_section() .. - * __end_current_label_crit_section() - */ -static inline struct aa_label *__begin_current_label_crit_section(void) -{ - struct aa_label *label = aa_current_raw_label(); - - if (label_is_stale(label)) - label = aa_get_newest_label(label); - - return label; -} - -/** - * begin_current_label_crit_section - current's confining label and update it - * - * Returns: up to date confining label or the ns unconfined label (NOT NULL) - * - * Not safe to call inside locks - * - * The returned reference must be put with end_current_label_crit_section() - * This must NOT be used if the task cred could be updated within the - * critical section between begin_current_label_crit_section() .. - * end_current_label_crit_section() - */ -static inline struct aa_label *begin_current_label_crit_section(void) -{ - struct aa_label *label = aa_current_raw_label(); - - if (label_is_stale(label)) { - label = aa_get_newest_label(label); - if (aa_replace_current_label(label) == 0) - /* task cred will keep the reference */ - aa_put_label(label); - } - - return label; -} - -static inline struct aa_ns *aa_get_current_ns(void) -{ - struct aa_label *label; - struct aa_ns *ns; - - label = __begin_current_label_crit_section(); - ns = aa_get_ns(labels_ns(label)); - __end_current_label_crit_section(label); - - return ns; -} - -#endif /* __AA_CONTEXT_H */ diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h new file mode 100644 index 000000000000..e287b7d0d4be --- /dev/null +++ b/security/apparmor/include/cred.h @@ -0,0 +1,176 @@ +/* + * AppArmor security module + * + * This file contains AppArmor contexts used to associate "labels" to objects. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_CONTEXT_H +#define __AA_CONTEXT_H + +#include +#include +#include + +#include "label.h" +#include "policy_ns.h" +#include "task.h" + +#define cred_label(X) ((X)->security) + + +/** + * aa_cred_raw_label - obtain cred's label + * @cred: cred to obtain label from (NOT NULL) + * + * Returns: confining label + * + * does NOT increment reference count + */ +static inline struct aa_label *aa_cred_raw_label(const struct cred *cred) +{ + struct aa_label *label = cred_label(cred); + + AA_BUG(!label); + return label; +} + +/** + * aa_get_newest_cred_label - obtain the newest label on a cred + * @cred: cred to obtain label from (NOT NULL) + * + * Returns: newest version of confining label + */ +static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred) +{ + return aa_get_newest_label(aa_cred_raw_label(cred)); +} + +/** + * __aa_task_raw_label - retrieve another task's label + * @task: task to query (NOT NULL) + * + * Returns: @task's label without incrementing its ref count + * + * If @task != current needs to be called in RCU safe critical section + */ +static inline struct aa_label *__aa_task_raw_label(struct task_struct *task) +{ + return aa_cred_raw_label(__task_cred(task)); +} + +/** + * aa_current_raw_label - find the current tasks confining label + * + * Returns: up to date confining label or the ns unconfined label (NOT NULL) + * + * This fn will not update the tasks cred to the most up to date version + * of the label so it is safe to call when inside of locks. + */ +static inline struct aa_label *aa_current_raw_label(void) +{ + return aa_cred_raw_label(current_cred()); +} + +/** + * aa_get_current_label - get the newest version of the current tasks label + * + * Returns: newest version of confining label (NOT NULL) + * + * This fn will not update the tasks cred, so it is safe inside of locks + * + * The returned reference must be put with aa_put_label() + */ +static inline struct aa_label *aa_get_current_label(void) +{ + struct aa_label *l = aa_current_raw_label(); + + if (label_is_stale(l)) + return aa_get_newest_label(l); + return aa_get_label(l); +} + +#define __end_current_label_crit_section(X) end_current_label_crit_section(X) + +/** + * end_label_crit_section - put a reference found with begin_current_label.. + * @label: label reference to put + * + * Should only be used with a reference obtained with + * begin_current_label_crit_section and never used in situations where the + * task cred may be updated + */ +static inline void end_current_label_crit_section(struct aa_label *label) +{ + if (label != aa_current_raw_label()) + aa_put_label(label); +} + +/** + * __begin_current_label_crit_section - current's confining label + * + * Returns: up to date confining label or the ns unconfined label (NOT NULL) + * + * safe to call inside locks + * + * The returned reference must be put with __end_current_label_crit_section() + * This must NOT be used if the task cred could be updated within the + * critical section between __begin_current_label_crit_section() .. + * __end_current_label_crit_section() + */ +static inline struct aa_label *__begin_current_label_crit_section(void) +{ + struct aa_label *label = aa_current_raw_label(); + + if (label_is_stale(label)) + label = aa_get_newest_label(label); + + return label; +} + +/** + * begin_current_label_crit_section - current's confining label and update it + * + * Returns: up to date confining label or the ns unconfined label (NOT NULL) + * + * Not safe to call inside locks + * + * The returned reference must be put with end_current_label_crit_section() + * This must NOT be used if the task cred could be updated within the + * critical section between begin_current_label_crit_section() .. + * end_current_label_crit_section() + */ +static inline struct aa_label *begin_current_label_crit_section(void) +{ + struct aa_label *label = aa_current_raw_label(); + + if (label_is_stale(label)) { + label = aa_get_newest_label(label); + if (aa_replace_current_label(label) == 0) + /* task cred will keep the reference */ + aa_put_label(label); + } + + return label; +} + +static inline struct aa_ns *aa_get_current_ns(void) +{ + struct aa_label *label; + struct aa_ns *ns; + + label = __begin_current_label_crit_section(); + ns = aa_get_ns(labels_ns(label)); + __end_current_label_crit_section(label); + + return ns; +} + +#endif /* __AA_CONTEXT_H */ diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index d7b137d4eb74..527ea1557120 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -17,7 +17,7 @@ #include "include/audit.h" #include "include/capability.h" -#include "include/context.h" +#include "include/cred.h" #include "include/policy.h" #include "include/ipc.h" #include "include/sig_names.h" diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 69c7451becef..523250e34837 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -16,7 +16,7 @@ #include #include "include/apparmor.h" -#include "include/context.h" +#include "include/cred.h" #include "include/label.h" #include "include/policy.h" #include "include/secid.h" diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 7577cd982230..ef6334e11597 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -30,7 +30,7 @@ #include "include/apparmorfs.h" #include "include/audit.h" #include "include/capability.h" -#include "include/context.h" +#include "include/cred.h" #include "include/file.h" #include "include/ipc.h" #include "include/path.h" diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index 8c558cbce930..6e8c7ac0b33d 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -18,7 +18,7 @@ #include "include/apparmor.h" #include "include/audit.h" -#include "include/context.h" +#include "include/cred.h" #include "include/domain.h" #include "include/file.h" #include "include/match.h" diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index a158af1f1b38..a8e096a88e62 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -82,7 +82,7 @@ #include "include/apparmor.h" #include "include/capability.h" -#include "include/context.h" +#include "include/cred.h" #include "include/file.h" #include "include/ipc.h" #include "include/match.h" diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index b1e629cba70b..b0f9dc3f765a 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -21,7 +21,7 @@ #include #include "include/apparmor.h" -#include "include/context.h" +#include "include/cred.h" #include "include/policy_ns.h" #include "include/label.h" #include "include/policy.h" diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index ece0c246cfe6..40c8dc617b13 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -23,7 +23,7 @@ #include "include/apparmor.h" #include "include/audit.h" -#include "include/context.h" +#include "include/cred.h" #include "include/crypto.h" #include "include/match.h" #include "include/path.h" diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index d81617379d63..80c34ed373c3 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -13,7 +13,7 @@ */ #include "include/apparmor.h" -#include "include/context.h" +#include "include/cred.h" #include "include/policy.h" #include "include/policy_ns.h" #include "include/domain.h" diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index cf4d234febe9..d022137143b9 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -16,7 +16,7 @@ #include #include "include/audit.h" -#include "include/context.h" +#include "include/cred.h" #include "include/resource.h" #include "include/policy.h" diff --git a/security/apparmor/task.c b/security/apparmor/task.c index 36eb8707ad89..44b9b938e06d 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -16,7 +16,7 @@ * should return to the previous cred if it has not been modified. */ -#include "include/context.h" +#include "include/cred.h" #include "include/task.h" /** -- cgit v1.3-14-g43fede From 9fcf78cca198600b27c44b4e50f00f8af3927f17 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 8 Oct 2017 18:26:19 -0700 Subject: apparmor: update domain transitions that are subsets of confinement at nnp Domain transition so far have been largely blocked by no new privs, unless the transition has been provably a subset of the previous confinement. There was a couple problems with the previous implementations, - transitions that weren't explicitly a stack but resulted in a subset of confinement were disallowed - confinement subsets were only calculated from the previous confinement instead of the confinement being enforced at the time of no new privs, so transitions would have to get progressively tighter. Fix this by detecting and storing a reference to the task's confinement at the "time" no new privs is set. This reference is then used to determine whether a transition is a subsystem of the confinement at the time no new privs was set. Unfortunately the implementation is less than ideal in that we have to detect no new privs after the fact when a task attempts a domain transition. This is adequate for the currently but will not work in a stacking situation where no new privs could be conceivably be set in both the "host" and in the container. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 1 + security/apparmor/domain.c | 163 +++++++++++++++++++++++---------------- security/apparmor/include/task.h | 4 + security/apparmor/task.c | 7 ++ 4 files changed, 110 insertions(+), 65 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 874c1bf6b84a..07623fb41e32 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2156,6 +2156,7 @@ static struct aa_sfs_entry aa_sfs_entry_domain[] = { AA_SFS_FILE_BOOLEAN("change_profile", 1), AA_SFS_FILE_BOOLEAN("stack", 1), AA_SFS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), + AA_SFS_FILE_BOOLEAN("post_nnp_subset", 1), AA_SFS_FILE_STRING("version", "1.2"), { } }; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index cd58eef4eb8d..9d1936519cfd 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -592,22 +592,6 @@ static struct aa_label *profile_transition(struct aa_profile *profile, if (!new) goto audit; - /* Policy has specified a domain transitions. if no_new_privs and - * confined and not transitioning to the current domain fail. - * - * NOTE: Domain transitions from unconfined and to stritly stacked - * subsets are allowed even when no_new_privs is set because this - * aways results in a further reduction of permissions. - */ - if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && - !profile_unconfined(profile) && - !aa_label_is_subset(new, &profile->label)) { - error = -EPERM; - info = "no new privs"; - nonewprivs = true; - perms.allow &= ~MAY_EXEC; - goto audit; - } if (!(perms.xindex & AA_X_UNSAFE)) { if (DEBUG_ON) { @@ -684,21 +668,6 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec, perms.allow &= ~AA_MAY_ONEXEC; goto audit; } - /* Policy has specified a domain transitions. if no_new_privs and - * confined and not transitioning to the current domain fail. - * - * NOTE: Domain transitions from unconfined and to stritly stacked - * subsets are allowed even when no_new_privs is set because this - * aways results in a further reduction of permissions. - */ - if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && - !profile_unconfined(profile) && - !aa_label_is_subset(onexec, &profile->label)) { - error = -EPERM; - info = "no new privs"; - perms.allow &= ~AA_MAY_ONEXEC; - goto audit; - } if (!(perms.xindex & AA_X_UNSAFE)) { if (DEBUG_ON) { @@ -800,6 +769,17 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) label = aa_get_newest_label(cred_label(bprm->cred)); + /* + * Detect no new privs being set, and store the label it + * occurred under. Ideally this would happen when nnp + * is set but there isn't a good way to do that yet. + * + * Testing for unconfined must be done before the subset test + */ + if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && !unconfined(label) && + !ctx->nnp) + ctx->nnp = aa_get_label(label); + /* buffer freed below, name is pointer into buffer */ get_buffers(buffer); /* Test for onexec first as onexec override other x transitions. */ @@ -820,7 +800,20 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) goto done; } - /* TODO: Add ns level no_new_privs subset test */ + /* Policy has specified a domain transitions. If no_new_privs and + * confined ensure the transition is to confinement that is subset + * of the confinement when the task entered no new privs. + * + * NOTE: Domain transitions from unconfined and to stacked + * subsets are allowed even when no_new_privs is set because this + * aways results in a further reduction of permissions. + */ + if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && + !unconfined(label) && !aa_label_is_subset(new, ctx->nnp)) { + error = -EPERM; + info = "no new privs"; + goto audit; + } if (bprm->unsafe & LSM_UNSAFE_SHARE) { /* FIXME: currently don't mediate shared state */ @@ -1047,30 +1040,28 @@ build: int aa_change_hat(const char *hats[], int count, u64 token, int flags) { const struct cred *cred; - struct aa_task_ctx *ctx; + struct aa_task_ctx *ctx = task_ctx(current); struct aa_label *label, *previous, *new = NULL, *target = NULL; struct aa_profile *profile; struct aa_perms perms = {}; const char *info = NULL; int error = 0; - /* - * Fail explicitly requested domain transitions if no_new_privs. - * There is no exception for unconfined as change_hat is not - * available. - */ - if (task_no_new_privs(current)) { - /* not an apparmor denial per se, so don't log it */ - AA_DEBUG("no_new_privs - change_hat denied"); - return -EPERM; - } - /* released below */ cred = get_current_cred(); - ctx = task_ctx(current); label = aa_get_newest_cred_label(cred); previous = aa_get_newest_label(ctx->previous); + /* + * Detect no new privs being set, and store the label it + * occurred under. Ideally this would happen when nnp + * is set but there isn't a good way to do that yet. + * + * Testing for unconfined must be done before the subset test + */ + if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp) + ctx->nnp = aa_get_label(label); + if (unconfined(label)) { info = "unconfined can not change_hat"; error = -EPERM; @@ -1091,6 +1082,18 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) if (error) goto fail; + /* + * no new privs prevents domain transitions that would + * reduce restrictions. + */ + if (task_no_new_privs(current) && !unconfined(label) && + !aa_label_is_subset(new, ctx->nnp)) { + /* not an apparmor denial per se, so don't log it */ + AA_DEBUG("no_new_privs - change_hat denied"); + error = -EPERM; + goto out; + } + if (flags & AA_CHANGE_TEST) goto out; @@ -1100,6 +1103,18 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) /* kill task in case of brute force attacks */ goto kill; } else if (previous && !(flags & AA_CHANGE_TEST)) { + /* + * no new privs prevents domain transitions that would + * reduce restrictions. + */ + if (task_no_new_privs(current) && !unconfined(label) && + !aa_label_is_subset(previous, ctx->nnp)) { + /* not an apparmor denial per se, so don't log it */ + AA_DEBUG("no_new_privs - change_hat denied"); + error = -EPERM; + goto out; + } + /* Return to saved label. Kill task if restore fails * to avoid brute force attacks */ @@ -1142,21 +1157,6 @@ static int change_profile_perms_wrapper(const char *op, const char *name, const char *info = NULL; int error = 0; - /* - * Fail explicitly requested domain transitions when no_new_privs - * and not unconfined OR the transition results in a stack on - * the current label. - * Stacking domain transitions and transitions from unconfined are - * allowed even when no_new_privs is set because this aways results - * in a reduction of permissions. - */ - if (task_no_new_privs(current) && !stack && - !profile_unconfined(profile) && - !aa_label_is_subset(target, &profile->label)) { - info = "no new privs"; - error = -EPERM; - } - if (!error) error = change_profile_perms(profile, target, stack, request, profile->file.start, perms); @@ -1190,10 +1190,23 @@ int aa_change_profile(const char *fqname, int flags) const char *info = NULL; const char *auditname = fqname; /* retain leading & if stack */ bool stack = flags & AA_CHANGE_STACK; + struct aa_task_ctx *ctx = task_ctx(current); int error = 0; char *op; u32 request; + label = aa_get_current_label(); + + /* + * Detect no new privs being set, and store the label it + * occurred under. Ideally this would happen when nnp + * is set but there isn't a good way to do that yet. + * + * Testing for unconfined must be done before the subset test + */ + if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp) + ctx->nnp = aa_get_label(label); + if (!fqname || !*fqname) { AA_DEBUG("no profile name"); return -EINVAL; @@ -1281,14 +1294,28 @@ check: if (flags & AA_CHANGE_TEST) goto out; + /* stacking is always a subset, so only check the nonstack case */ + if (!stack) { + new = fn_label_build_in_ns(label, profile, GFP_KERNEL, + aa_get_label(target), + aa_get_label(&profile->label)); + /* + * no new privs prevents domain transitions that would + * reduce restrictions. + */ + if (task_no_new_privs(current) && !unconfined(label) && + !aa_label_is_subset(new, ctx->nnp)) { + /* not an apparmor denial per se, so don't log it */ + AA_DEBUG("no_new_privs - change_hat denied"); + error = -EPERM; + goto out; + } + } + if (!(flags & AA_CHANGE_ONEXEC)) { /* only transition profiles in the current ns */ if (stack) new = aa_label_merge(label, target, GFP_KERNEL); - else - new = fn_label_build_in_ns(label, profile, GFP_KERNEL, - aa_get_label(target), - aa_get_label(&profile->label)); if (IS_ERR_OR_NULL(new)) { info = "failed to build target label"; error = PTR_ERR(new); @@ -1297,9 +1324,15 @@ check: goto audit; } error = aa_replace_current_label(new); - } else + } else { + if (new) { + aa_put_label(new); + new = NULL; + } + /* full transition will be built in exec path */ error = aa_set_current_onexec(target, stack); + } audit: error = fn_for_each_in_ns(label, profile, diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h index d222197db299..55edaa1d83f8 100644 --- a/security/apparmor/include/task.h +++ b/security/apparmor/include/task.h @@ -18,11 +18,13 @@ /* * struct aa_task_ctx - information for current task label change + * @nnp: snapshot of label at time of no_new_privs * @onexec: profile to transition to on next exec (MAY BE NULL) * @previous: profile the task may return to (MAY BE NULL) * @token: magic value the task must know for returning to @previous_profile */ struct aa_task_ctx { + struct aa_label *nnp; struct aa_label *onexec; struct aa_label *previous; u64 token; @@ -52,6 +54,7 @@ static inline struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags) static inline void aa_free_task_ctx(struct aa_task_ctx *ctx) { if (ctx) { + aa_put_label(ctx->nnp); aa_put_label(ctx->previous); aa_put_label(ctx->onexec); @@ -68,6 +71,7 @@ static inline void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old) { *new = *old; + aa_get_label(new->nnp); aa_get_label(new->previous); aa_get_label(new->onexec); } diff --git a/security/apparmor/task.c b/security/apparmor/task.c index 44b9b938e06d..c6b78a14da91 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -45,6 +45,7 @@ struct aa_label *aa_get_task_label(struct task_struct *task) int aa_replace_current_label(struct aa_label *label) { struct aa_label *old = aa_current_raw_label(); + struct aa_task_ctx *ctx = task_ctx(current); struct cred *new; AA_BUG(!label); @@ -59,6 +60,12 @@ int aa_replace_current_label(struct aa_label *label) if (!new) return -ENOMEM; + if (ctx->nnp && label_is_stale(ctx->nnp)) { + struct aa_label *tmp = ctx->nnp; + + ctx->nnp = aa_get_newest_label(tmp); + aa_put_label(tmp); + } if (unconfined(label) || (labels_ns(old) != labels_ns(label))) /* * if switching to unconfined or a different label namespace -- cgit v1.3-14-g43fede From 074c1cd798cb0b481d7eaa749b64aa416563c053 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 8 Aug 2017 11:58:33 -0700 Subject: apparmor: dfa move character match into a macro Signed-off-by: John Johansen --- security/apparmor/match.c | 74 +++++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 47 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 5d95caeddebc..aeac68c58689 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -329,6 +329,18 @@ fail: return ERR_PTR(error); } +#define match_char(state, def, base, next, check, C) \ +do { \ + u32 b = (base)[(state)]; \ + unsigned int pos = base_idx(b) + (C); \ + if ((check)[pos] != (state)) { \ + (state) = (def)[(state)]; \ + break; \ + } \ + (state) = (next)[pos]; \ + break; \ +} while (1) + /** * aa_dfa_match_len - traverse @dfa to find state @str stops at * @dfa: the dfa to match @str against (NOT NULL) @@ -352,7 +364,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, u32 *base = BASE_TABLE(dfa); u16 *next = NEXT_TABLE(dfa); u16 *check = CHECK_TABLE(dfa); - unsigned int state = start, pos; + unsigned int state = start; if (state == 0) return 0; @@ -361,23 +373,13 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, if (dfa->tables[YYTD_ID_EC]) { /* Equivalence class table defined */ u8 *equiv = EQUIV_TABLE(dfa); - /* default is direct to next state */ - for (; len; len--) { - pos = base_idx(base[state]) + equiv[(u8) *str++]; - if (check[pos] == state) - state = next[pos]; - else - state = def[state]; - } + for (; len; len--) + match_char(state, def, base, next, check, + equiv[(u8) *str++]); } else { /* default is direct to next state */ - for (; len; len--) { - pos = base_idx(base[state]) + (u8) *str++; - if (check[pos] == state) - state = next[pos]; - else - state = def[state]; - } + for (; len; len--) + match_char(state, def, base, next, check, (u8) *str++); } return state; @@ -402,7 +404,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, u32 *base = BASE_TABLE(dfa); u16 *next = NEXT_TABLE(dfa); u16 *check = CHECK_TABLE(dfa); - unsigned int state = start, pos; + unsigned int state = start; if (state == 0) return 0; @@ -412,22 +414,13 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, /* Equivalence class table defined */ u8 *equiv = EQUIV_TABLE(dfa); /* default is direct to next state */ - while (*str) { - pos = base_idx(base[state]) + equiv[(u8) *str++]; - if (check[pos] == state) - state = next[pos]; - else - state = def[state]; - } + while (*str) + match_char(state, def, base, next, check, + equiv[(u8) *str++]); } else { /* default is direct to next state */ - while (*str) { - pos = base_idx(base[state]) + (u8) *str++; - if (check[pos] == state) - state = next[pos]; - else - state = def[state]; - } + while (*str) + match_char(state, def, base, next, check, (u8) *str++); } return state; @@ -450,27 +443,14 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, u32 *base = BASE_TABLE(dfa); u16 *next = NEXT_TABLE(dfa); u16 *check = CHECK_TABLE(dfa); - unsigned int pos; /* current state is , matching character *str */ if (dfa->tables[YYTD_ID_EC]) { /* Equivalence class table defined */ u8 *equiv = EQUIV_TABLE(dfa); - /* default is direct to next state */ - - pos = base_idx(base[state]) + equiv[(u8) c]; - if (check[pos] == state) - state = next[pos]; - else - state = def[state]; - } else { - /* default is direct to next state */ - pos = base_idx(base[state]) + (u8) c; - if (check[pos] == state) - state = next[pos]; - else - state = def[state]; - } + match_char(state, def, base, next, check, equiv[(u8) c]); + } else + match_char(state, def, base, next, check, (u8) c); return state; } -- cgit v1.3-14-g43fede From 031dcc8f4e84fea37dc6f78fdc7288aa7f8386c3 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 8 Aug 2017 12:10:50 -0700 Subject: apparmor: dfa add support for state differential encoding State differential encoding can provide better compression for apparmor policy, without having significant impact on match time. Signed-off-by: John Johansen --- security/apparmor/include/match.h | 4 ++++ security/apparmor/match.c | 26 +++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index cd8aeab6ac57..e0de00bd16a8 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -40,6 +40,7 @@ */ #define YYTH_MAGIC 0x1B5E783D +#define YYTH_FLAG_DIFF_ENCODE 1 struct table_set_header { u32 th_magic; /* YYTH_MAGIC */ @@ -164,4 +165,7 @@ static inline void aa_put_dfa(struct aa_dfa *dfa) kref_put(&dfa->count, aa_dfa_free_kref); } +#define MATCH_FLAG_DIFF_ENCODE 0x80000000 +#define MARK_DIFF_ENCODE 0x40000000 + #endif /* __AA_MATCH_H */ diff --git a/security/apparmor/match.c b/security/apparmor/match.c index aeac68c58689..70cdcb3c3b25 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -185,7 +185,8 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) if (flags & DFA_FLAG_VERIFY_STATES) { for (i = 0; i < state_count; i++) { - if (DEFAULT_TABLE(dfa)[i] >= state_count) + if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) && + (DEFAULT_TABLE(dfa)[i] >= state_count)) goto out; if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " @@ -202,6 +203,24 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) } } + /* Now that all the other tables are verified, verify diffencoding */ + if (flags & DFA_FLAG_VERIFY_STATES) { + size_t j, k; + + for (i = 0; i < state_count; i++) { + for (j = i; + (BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) && + !(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE); + j = k) { + k = DEFAULT_TABLE(dfa)[j]; + if (j == k) + goto out; + if (k < j) + break; /* already verified */ + BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE; + } + } + } error = 0; out: return error; @@ -274,6 +293,9 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) goto fail; dfa->flags = ntohs(*(__be16 *) (data + 12)); + if (dfa->flags != 0 && dfa->flags != YYTH_FLAG_DIFF_ENCODE) + goto fail; + data += hsize; size -= hsize; @@ -335,6 +357,8 @@ do { \ unsigned int pos = base_idx(b) + (C); \ if ((check)[pos] != (state)) { \ (state) = (def)[(state)]; \ + if (b & MATCH_FLAG_DIFF_ENCODE) \ + continue; \ break; \ } \ (state) = (next)[pos]; \ -- cgit v1.3-14-g43fede From d901d6a298dc6e9105b9dc091d65b043e9f8c9a6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 8 Aug 2017 13:01:01 -0700 Subject: apparmor: dfa split verification of table headers separate the different types of verification so they are logically separate and can be reused separate of each other. Signed-off-by: John Johansen --- security/apparmor/match.c | 116 +++++++++++++++++++++++++++------------------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 70cdcb3c3b25..7ae6ed9d69dd 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -136,8 +136,8 @@ fail: } /** - * verify_dfa - verify that transitions and states in the tables are in bounds. - * @dfa: dfa to test (NOT NULL) + * verify_table_headers - verify that the tables headers are as expected + * @tables - array of dfa tables to check (NOT NULL) * @flags: flags controlling what type of accept table are acceptable * * Assumes dfa has gone through the first pass verification done by unpacking @@ -145,83 +145,98 @@ fail: * * Returns: %0 else error code on failure to verify */ -static int verify_dfa(struct aa_dfa *dfa, int flags) +static int verify_table_headers(struct table_header **tables, int flags) { - size_t i, state_count, trans_count; + size_t state_count, trans_count; int error = -EPROTO; /* check that required tables exist */ - if (!(dfa->tables[YYTD_ID_DEF] && - dfa->tables[YYTD_ID_BASE] && - dfa->tables[YYTD_ID_NXT] && dfa->tables[YYTD_ID_CHK])) + if (!(tables[YYTD_ID_DEF] && tables[YYTD_ID_BASE] && + tables[YYTD_ID_NXT] && tables[YYTD_ID_CHK])) goto out; /* accept.size == default.size == base.size */ - state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; + state_count = tables[YYTD_ID_BASE]->td_lolen; if (ACCEPT1_FLAGS(flags)) { - if (!dfa->tables[YYTD_ID_ACCEPT]) + if (!tables[YYTD_ID_ACCEPT]) goto out; - if (state_count != dfa->tables[YYTD_ID_ACCEPT]->td_lolen) + if (state_count != tables[YYTD_ID_ACCEPT]->td_lolen) goto out; } if (ACCEPT2_FLAGS(flags)) { - if (!dfa->tables[YYTD_ID_ACCEPT2]) + if (!tables[YYTD_ID_ACCEPT2]) goto out; - if (state_count != dfa->tables[YYTD_ID_ACCEPT2]->td_lolen) + if (state_count != tables[YYTD_ID_ACCEPT2]->td_lolen) goto out; } - if (state_count != dfa->tables[YYTD_ID_DEF]->td_lolen) + if (state_count != tables[YYTD_ID_DEF]->td_lolen) goto out; /* next.size == chk.size */ - trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; - if (trans_count != dfa->tables[YYTD_ID_CHK]->td_lolen) + trans_count = tables[YYTD_ID_NXT]->td_lolen; + if (trans_count != tables[YYTD_ID_CHK]->td_lolen) goto out; /* if equivalence classes then its table size must be 256 */ - if (dfa->tables[YYTD_ID_EC] && - dfa->tables[YYTD_ID_EC]->td_lolen != 256) + if (tables[YYTD_ID_EC] && tables[YYTD_ID_EC]->td_lolen != 256) goto out; - if (flags & DFA_FLAG_VERIFY_STATES) { - for (i = 0; i < state_count; i++) { - if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) && - (DEFAULT_TABLE(dfa)[i] >= state_count)) - goto out; - if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) { - printk(KERN_ERR "AppArmor DFA next/check upper " - "bounds error\n"); - goto out; - } - } + error = 0; +out: + return error; +} - for (i = 0; i < trans_count; i++) { - if (NEXT_TABLE(dfa)[i] >= state_count) - goto out; - if (CHECK_TABLE(dfa)[i] >= state_count) - goto out; +/** + * verify_dfa - verify that transitions and states in the tables are in bounds. + * @dfa: dfa to test (NOT NULL) + * + * Assumes dfa has gone through the first pass verification done by unpacking + * NOTE: this does not valid accept table values + * + * Returns: %0 else error code on failure to verify + */ +static int verify_dfa(struct aa_dfa *dfa) +{ + size_t i, state_count, trans_count; + int error = EPROTO; + + state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; + trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; + for (i = 0; i < state_count; i++) { + if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) && + (DEFAULT_TABLE(dfa)[i] >= state_count)) + goto out; + if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) { + pr_err("AppArmor DFA next/check upper bounds error\n"); + goto out; } } + for (i = 0; i < trans_count; i++) { + if (NEXT_TABLE(dfa)[i] >= state_count) + goto out; + if (CHECK_TABLE(dfa)[i] >= state_count) + goto out; + } + /* Now that all the other tables are verified, verify diffencoding */ - if (flags & DFA_FLAG_VERIFY_STATES) { + for (i = 0; i < state_count; i++) { size_t j, k; - for (i = 0; i < state_count; i++) { - for (j = i; - (BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) && - !(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE); - j = k) { - k = DEFAULT_TABLE(dfa)[j]; - if (j == k) - goto out; - if (k < j) - break; /* already verified */ - BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE; - } + for (j = i; + (BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) && + !(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE); + j = k) { + k = DEFAULT_TABLE(dfa)[j]; + if (j == k) + goto out; + if (k < j) + break; /* already verified */ + BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE; } } error = 0; + out: return error; } @@ -338,11 +353,16 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) size -= table_size(table->td_lolen, table->td_flags); table = NULL; } - - error = verify_dfa(dfa, flags); + error = verify_table_headers(dfa->tables, flags); if (error) goto fail; + if (flags & DFA_FLAG_VERIFY_STATES) { + error = verify_dfa(dfa); + if (error) + goto fail; + } + return dfa; fail: -- cgit v1.3-14-g43fede From cf91600071a973c28cebf314e618610a20ec4d6d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 5 Feb 2018 09:58:29 +0100 Subject: apparmor: cleanup create_aafs() error path Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 07623fb41e32..8cdab3c5bc63 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2458,34 +2458,26 @@ static int __init aa_create_aafs(void) dent = securityfs_create_file(".load", 0666, aa_sfs_entry.dentry, NULL, &aa_fs_profile_load); - if (IS_ERR(dent)) { - error = PTR_ERR(dent); - goto error; - } + if (IS_ERR(dent)) + goto dent_error; ns_subload(root_ns) = dent; dent = securityfs_create_file(".replace", 0666, aa_sfs_entry.dentry, NULL, &aa_fs_profile_replace); - if (IS_ERR(dent)) { - error = PTR_ERR(dent); - goto error; - } + if (IS_ERR(dent)) + goto dent_error; ns_subreplace(root_ns) = dent; dent = securityfs_create_file(".remove", 0666, aa_sfs_entry.dentry, NULL, &aa_fs_profile_remove); - if (IS_ERR(dent)) { - error = PTR_ERR(dent); - goto error; - } + if (IS_ERR(dent)) + goto dent_error; ns_subremove(root_ns) = dent; dent = securityfs_create_file("revision", 0444, aa_sfs_entry.dentry, NULL, &aa_fs_ns_revision_fops); - if (IS_ERR(dent)) { - error = PTR_ERR(dent); - goto error; - } + if (IS_ERR(dent)) + goto dent_error; ns_subrevision(root_ns) = dent; /* policy tree referenced by magic policy symlink */ @@ -2499,10 +2491,8 @@ static int __init aa_create_aafs(void) /* magic symlink similar to nsfs redirects based on task policy */ dent = securityfs_create_symlink("policy", aa_sfs_entry.dentry, NULL, &policy_link_iops); - if (IS_ERR(dent)) { - error = PTR_ERR(dent); - goto error; - } + if (IS_ERR(dent)) + goto dent_error; error = aa_mk_null_file(aa_sfs_entry.dentry); if (error) @@ -2514,6 +2504,8 @@ static int __init aa_create_aafs(void) aa_info_message("AppArmor Filesystem Enabled"); return 0; +dent_error: + error = PTR_ERR(dent); error: aa_destroy_aafs(); AA_ERROR("Error creating AppArmor securityfs\n"); -- cgit v1.3-14-g43fede From a0781209cb894e5115bb00c269b1d94c4b632d6a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 5 Feb 2018 18:26:46 +0100 Subject: apparmor: cleanup: simplify code to get ns symlink name ns_get_name() is called in only one place and can be folded in. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 8cdab3c5bc63..1e63ff2e5b85 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -119,9 +119,7 @@ static int aafs_count; static int aafs_show_path(struct seq_file *seq, struct dentry *dentry) { - struct inode *inode = d_inode(dentry); - - seq_printf(seq, "%s:[%lu]", AAFS_NAME, inode->i_ino); + seq_printf(seq, "%s:[%lu]", AAFS_NAME, d_inode(dentry)->i_ino); return 0; } @@ -2392,29 +2390,18 @@ static const char *policy_get_link(struct dentry *dentry, return NULL; } -static int ns_get_name(char *buf, size_t size, struct aa_ns *ns, - struct inode *inode) -{ - int res = snprintf(buf, size, "%s:[%lu]", AAFS_NAME, inode->i_ino); - - if (res < 0 || res >= size) - res = -ENOENT; - - return res; -} - static int policy_readlink(struct dentry *dentry, char __user *buffer, int buflen) { - struct aa_ns *ns; char name[32]; int res; - ns = aa_get_current_ns(); - res = ns_get_name(name, sizeof(name), ns, d_inode(dentry)); - if (res >= 0) + res = snprintf(name, sizeof(name), "%s:[%lu]", AAFS_NAME, + d_inode(dentry)->i_ino); + if (res > 0 && res < sizeof(name)) res = readlink_copy(buffer, buflen, name); - aa_put_ns(ns); + else + res = -ENOENT; return res; } -- cgit v1.3-14-g43fede From 8e51f9087f4024d20f70f4d9831e1f45d8088331 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 8 Feb 2018 12:37:19 -0800 Subject: apparmor: Add support for attaching profiles via xattr, presence and value Make it possible to tie Apparmor profiles to the presence of one or more extended attributes, and optionally their values. An example usecase for this is to automatically transition to a more privileged Apparmor profile if an executable has a valid IMA signature, which can then be appraised by the IMA subsystem. Signed-off-by: Matthew Garrett Signed-off-by: John Johansen --- security/apparmor/domain.c | 152 +++++++++++++++++++++++++++++++------ security/apparmor/include/policy.h | 6 ++ security/apparmor/policy.c | 8 ++ security/apparmor/policy_unpack.c | 85 ++++++++++++++++++--- 4 files changed, 217 insertions(+), 34 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 9d1936519cfd..6bcafe8d226d 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "include/audit.h" #include "include/apparmorfs.h" @@ -301,8 +302,57 @@ static int change_profile_perms(struct aa_profile *profile, return label_match(profile, target, stack, start, true, request, perms); } +/** + * aa_xattrs_match - check whether a file matches the xattrs defined in profile + * @bprm: binprm struct for the process to validate + * @profile: profile to match against (NOT NULL) + * + * Returns: number of extended attributes that matched, or < 0 on error + */ +static int aa_xattrs_match(const struct linux_binprm *bprm, + struct aa_profile *profile) +{ + int i; + size_t size; + struct dentry *d; + char *value = NULL; + int value_size = 0, ret = profile->xattr_count; + + if (!bprm || !profile->xattr_count) + return 0; + + d = bprm->file->f_path.dentry; + + for (i = 0; i < profile->xattr_count; i++) { + size = vfs_getxattr_alloc(d, profile->xattrs[i], &value, + value_size, GFP_KERNEL); + if (size < 0) { + ret = -EINVAL; + goto out; + } + + /* Check the xattr value, not just presence */ + if (profile->xattr_lens[i]) { + if (profile->xattr_lens[i] != size) { + ret = -EINVAL; + goto out; + } + + if (memcmp(value, profile->xattr_values[i], size)) { + ret = -EINVAL; + goto out; + } + } + } + +out: + kfree(value); + return ret; +} + /** * __attach_match_ - find an attachment match + * @bprm - binprm structure of transitioning task * @name - to match against (NOT NULL) * @head - profile list to walk (NOT NULL) * @info - info message if there was an error (NOT NULL) @@ -316,11 +366,12 @@ static int change_profile_perms(struct aa_profile *profile, * * Returns: profile or NULL if no match found */ -static struct aa_profile *__attach_match(const char *name, +static struct aa_profile *__attach_match(const struct linux_binprm *bprm, + const char *name, struct list_head *head, const char **info) { - int len = 0; + int len = 0, xattrs = 0; bool conflict = false; struct aa_profile *profile, *candidate = NULL; @@ -329,26 +380,56 @@ static struct aa_profile *__attach_match(const char *name, &profile->label == ns_unconfined(profile->ns)) continue; + /* Find the "best" matching profile. Profiles must + * match the path and extended attributes (if any) + * associated with the file. A more specific path + * match will be preferred over a less specific one, + * and a match with more matching extended attributes + * will be preferred over one with fewer. If the best + * match has both the same level of path specificity + * and the same number of matching extended attributes + * as another profile, signal a conflict and refuse to + * match. + */ if (profile->xmatch) { - if (profile->xmatch_len >= len) { - unsigned int state; - u32 perm; - - state = aa_dfa_match(profile->xmatch, - DFA_START, name); - perm = dfa_user_allow(profile->xmatch, state); - /* any accepting state means a valid match. */ - if (perm & MAY_EXEC) { - if (profile->xmatch_len == len) { + unsigned int state; + u32 perm; + + if (profile->xmatch_len < len) + continue; + + state = aa_dfa_match(profile->xmatch, + DFA_START, name); + perm = dfa_user_allow(profile->xmatch, state); + /* any accepting state means a valid match. */ + if (perm & MAY_EXEC) { + int ret = aa_xattrs_match(bprm, profile); + + /* Fail matching if the xattrs don't match */ + if (ret < 0) + continue; + + /* The new match isn't more specific + * than the current best match + */ + if (profile->xmatch_len == len && + ret <= xattrs) { + /* Match is equivalent, so conflict */ + if (ret == xattrs) conflict = true; - continue; - } - candidate = profile; - len = profile->xmatch_len; - conflict = false; + continue; } + + /* Either the same length with more matching + * xattrs, or a longer match + */ + candidate = profile; + len = profile->xmatch_len; + xattrs = ret; + conflict = false; } - } else if (!strcmp(profile->base.name, name)) + } else if (!strcmp(profile->base.name, name) && + aa_xattrs_match(bprm, profile) >= 0) /* exact non-re match, no more searching required */ return profile; } @@ -363,6 +444,7 @@ static struct aa_profile *__attach_match(const char *name, /** * find_attach - do attachment search for unconfined processes + * @bprm - binprm structure of transitioning task * @ns: the current namespace (NOT NULL) * @list: list to search (NOT NULL) * @name: the executable name to match against (NOT NULL) @@ -370,13 +452,14 @@ static struct aa_profile *__attach_match(const char *name, * * Returns: label or NULL if no match found */ -static struct aa_label *find_attach(struct aa_ns *ns, struct list_head *list, +static struct aa_label *find_attach(const struct linux_binprm *bprm, + struct aa_ns *ns, struct list_head *list, const char *name, const char **info) { struct aa_profile *profile; rcu_read_lock(); - profile = aa_get_profile(__attach_match(name, list, info)); + profile = aa_get_profile(__attach_match(bprm, name, list, info)); rcu_read_unlock(); return profile ? &profile->label : NULL; @@ -432,6 +515,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, /** * x_to_label - get target label for a given xindex * @profile: current profile (NOT NULL) + * @bprm: binprm structure of transitioning task * @name: name to lookup (NOT NULL) * @xindex: index into x transition table * @lookupname: returns: name used in lookup if one was specified (NOT NULL) @@ -441,6 +525,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, * Returns: refcounted label or NULL if not found available */ static struct aa_label *x_to_label(struct aa_profile *profile, + const struct linux_binprm *bprm, const char *name, u32 xindex, const char **lookupname, const char **info) @@ -468,11 +553,11 @@ static struct aa_label *x_to_label(struct aa_profile *profile, case AA_X_NAME: if (xindex & AA_X_CHILD) /* released by caller */ - new = find_attach(ns, &profile->base.profiles, + new = find_attach(bprm, ns, &profile->base.profiles, name, info); else /* released by caller */ - new = find_attach(ns, &ns->base.profiles, + new = find_attach(bprm, ns, &ns->base.profiles, name, info); *lookupname = name; break; @@ -512,6 +597,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile, bool *secure_exec) { struct aa_label *new = NULL; + struct aa_profile *component; + struct label_it i; const char *info = NULL, *name = NULL, *target = NULL; unsigned int state = profile->file.start; struct aa_perms perms = {}; @@ -536,8 +623,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile, } if (profile_unconfined(profile)) { - new = find_attach(profile->ns, &profile->ns->base.profiles, - name, &info); + new = find_attach(bprm, profile->ns, + &profile->ns->base.profiles, name, &info); if (new) { AA_DEBUG("unconfined attached to new label"); return new; @@ -550,7 +637,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile, state = aa_str_perms(profile->file.dfa, state, name, cond, &perms); if (perms.allow & MAY_EXEC) { /* exec permission determine how to transition */ - new = x_to_label(profile, name, perms.xindex, &target, &info); + new = x_to_label(profile, bprm, name, perms.xindex, &target, + &info); if (new && new->proxy == profile->label.proxy && info) { /* hack ix fallback - improve how this is detected */ goto audit; @@ -559,6 +647,20 @@ static struct aa_label *profile_transition(struct aa_profile *profile, info = "profile transition not found"; /* remove MAY_EXEC to audit as failure */ perms.allow &= ~MAY_EXEC; + } else { + /* verify that each component's xattr requirements are + * met, and fail execution otherwise + */ + label_for_each(i, new, component) { + if (aa_xattrs_match(bprm, component) < 0) { + error = -EACCES; + info = "required xattrs not present"; + perms.allow &= ~MAY_EXEC; + aa_put_label(new); + new = NULL; + goto audit; + } + } } } else if (COMPLAIN_MODE(profile)) { /* no exec permission - learning mode */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 17fe41a9cac3..02bde92ebb5c 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -148,6 +148,12 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + + int xattr_count; + char **xattrs; + size_t *xattr_lens; + char **xattr_values; + struct aa_rlimit rlimits; struct aa_loaddata *rawdata; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index a8e096a88e62..7fee546ba10d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -210,6 +210,7 @@ static void aa_free_data(void *ptr, void *arg) void aa_free_profile(struct aa_profile *profile) { struct rhashtable *rht; + int i; AA_DEBUG("%s(%p)\n", __func__, profile); @@ -227,6 +228,13 @@ void aa_free_profile(struct aa_profile *profile) aa_free_cap_rules(&profile->caps); aa_free_rlimit_rules(&profile->rlimits); + for (i = 0; i < profile->xattr_count; i++) { + kzfree(profile->xattrs[i]); + kzfree(profile->xattr_values[i]); + } + kzfree(profile->xattrs); + kzfree(profile->xattr_lens); + kzfree(profile->xattr_values); kzfree(profile->dirname); aa_put_dfa(profile->xmatch); aa_put_dfa(profile->policy.dfa); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 40c8dc617b13..98d019185e57 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -203,6 +203,15 @@ static bool inbounds(struct aa_ext *e, size_t size) return (size <= e->end - e->pos); } +static void *kvmemdup(const void *src, size_t len) +{ + void *p = kvmalloc(len, GFP_KERNEL); + + if (p) + memcpy(p, src, len); + return p; +} + /** * aa_u16_chunck - test and do bounds checking for a u16 size based chunk * @e: serialized data read head (NOT NULL) @@ -522,6 +531,68 @@ fail: return 0; } +static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) +{ + void *pos = e->pos; + + if (unpack_nameX(e, AA_STRUCT, "xattrs")) { + int i, size; + + size = unpack_array(e, NULL); + profile->xattr_count = size; + profile->xattrs = kcalloc(size, sizeof(char *), + GFP_KERNEL); + if (!profile->xattrs) + goto fail; + for (i = 0; i < size; i++) { + if (!unpack_strdup(e, &profile->xattrs[i], NULL)) + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + } + + if (unpack_nameX(e, AA_STRUCT, "xattr_values")) { + int i, size; + + size = unpack_array(e, NULL); + + /* Must be the same number of xattr values as xattrs */ + if (size != profile->xattr_count) + goto fail; + + profile->xattr_lens = kcalloc(size, sizeof(size_t), + GFP_KERNEL); + if (!profile->xattr_lens) + goto fail; + + profile->xattr_values = kcalloc(size, sizeof(char *), + GFP_KERNEL); + if (!profile->xattr_values) + goto fail; + + for (i = 0; i < size; i++) { + profile->xattr_lens[i] = unpack_blob(e, + &profile->xattr_values[i], NULL); + profile->xattr_values[i] = + kvmemdup(profile->xattr_values[i], + profile->xattr_lens[i]); + } + + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + } + return 1; + +fail: + e->pos = pos; + return 0; +} + static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) { void *pos = e->pos; @@ -556,15 +627,6 @@ fail: return 0; } -static void *kvmemdup(const void *src, size_t len) -{ - void *p = kvmalloc(len, GFP_KERNEL); - - if (p) - memcpy(p, src, len); - return p; -} - static u32 strhash(const void *data, u32 len, u32 seed) { const char * const *key = data; @@ -719,6 +781,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + if (!unpack_xattrs(e, profile)) { + info = "failed to unpack profile xattrs"; + goto fail; + } + if (!unpack_rlimits(e, profile)) { info = "failed to unpack profile rlimits"; goto fail; -- cgit v1.3-14-g43fede From 73f488cd903938e78979d50e081a0314ad142351 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 12 Dec 2017 15:28:05 -0800 Subject: apparmor: convert attaching profiles via xattrs to use dfa matching This converts profile attachment based on xattrs to a fixed extended conditional using dfa matching. This has a couple of advantages - pattern matching can be used for the xattr match - xattrs can be optional for an attachment or marked as required - the xattr attachment conditional will be able to be combined with other extended conditionals when the flexible extended conditional work lands. The xattr fixed extended conditional is appended to the xmatch conditional. If an xattr attachment is specified the profile xmatch will be generated regardless of whether there is a pattern match on the executable name. Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/apparmorfs.c | 5 ++++ security/apparmor/domain.c | 52 ++++++++++++++++++++++++++------------ security/apparmor/include/policy.h | 2 -- security/apparmor/policy.c | 6 +---- security/apparmor/policy_unpack.c | 35 +------------------------ 5 files changed, 43 insertions(+), 57 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 1e63ff2e5b85..35e6b240fb14 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2147,6 +2147,10 @@ static struct aa_sfs_entry aa_sfs_entry_signal[] = { { } }; +static struct aa_sfs_entry aa_sfs_entry_attach[] = { + AA_SFS_FILE_BOOLEAN("xattr", 1), + { } +}; static struct aa_sfs_entry aa_sfs_entry_domain[] = { AA_SFS_FILE_BOOLEAN("change_hat", 1), AA_SFS_FILE_BOOLEAN("change_hatv", 1), @@ -2155,6 +2159,7 @@ static struct aa_sfs_entry aa_sfs_entry_domain[] = { AA_SFS_FILE_BOOLEAN("stack", 1), AA_SFS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), AA_SFS_FILE_BOOLEAN("post_nnp_subset", 1), + AA_SFS_DIR("attach_conditions", aa_sfs_entry_attach), AA_SFS_FILE_STRING("version", "1.2"), { } }; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 6bcafe8d226d..6a1279f11fcc 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -306,11 +306,12 @@ static int change_profile_perms(struct aa_profile *profile, * aa_xattrs_match - check whether a file matches the xattrs defined in profile * @bprm: binprm struct for the process to validate * @profile: profile to match against (NOT NULL) + * @state: state to start match in * * Returns: number of extended attributes that matched, or < 0 on error */ static int aa_xattrs_match(const struct linux_binprm *bprm, - struct aa_profile *profile) + struct aa_profile *profile, unsigned int state) { int i; size_t size; @@ -321,27 +322,40 @@ static int aa_xattrs_match(const struct linux_binprm *bprm, if (!bprm || !profile->xattr_count) return 0; + /* transition from exec match to xattr set */ + state = aa_dfa_null_transition(profile->xmatch, state); + d = bprm->file->f_path.dentry; for (i = 0; i < profile->xattr_count; i++) { size = vfs_getxattr_alloc(d, profile->xattrs[i], &value, value_size, GFP_KERNEL); - if (size < 0) { - ret = -EINVAL; - goto out; - } + if (size >= 0) { + u32 perm; - /* Check the xattr value, not just presence */ - if (profile->xattr_lens[i]) { - if (profile->xattr_lens[i] != size) { + /* Check the xattr value, not just presence */ + state = aa_dfa_match_len(profile->xmatch, state, value, + size); + perm = dfa_user_allow(profile->xmatch, state); + if (!(perm & MAY_EXEC)) { ret = -EINVAL; goto out; } - - if (memcmp(value, profile->xattr_values[i], size)) { + } + /* transition to next element */ + state = aa_dfa_null_transition(profile->xmatch, state); + if (size < 0) { + /* + * No xattr match, so verify if transition to + * next element was valid. IFF so the xattr + * was optional. + */ + if (!state) { ret = -EINVAL; goto out; } + /* don't count missing optional xattr as matched */ + ret--; } } @@ -403,13 +417,16 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm, perm = dfa_user_allow(profile->xmatch, state); /* any accepting state means a valid match. */ if (perm & MAY_EXEC) { - int ret = aa_xattrs_match(bprm, profile); + int ret = aa_xattrs_match(bprm, profile, state); /* Fail matching if the xattrs don't match */ if (ret < 0) continue; - /* The new match isn't more specific + /* + * TODO: allow for more flexible best match + * + * The new match isn't more specific * than the current best match */ if (profile->xmatch_len == len && @@ -428,9 +445,11 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm, xattrs = ret; conflict = false; } - } else if (!strcmp(profile->base.name, name) && - aa_xattrs_match(bprm, profile) >= 0) - /* exact non-re match, no more searching required */ + } else if (!strcmp(profile->base.name, name)) + /* + * old exact non-re match, without conditionals such + * as xattrs. no more searching required + */ return profile; } @@ -652,7 +671,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile, * met, and fail execution otherwise */ label_for_each(i, new, component) { - if (aa_xattrs_match(bprm, component) < 0) { + if (aa_xattrs_match(bprm, component, state) < + 0) { error = -EACCES; info = "required xattrs not present"; perms.allow &= ~MAY_EXEC; diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 02bde92ebb5c..c93b9ed55490 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -151,8 +151,6 @@ struct aa_profile { int xattr_count; char **xattrs; - size_t *xattr_lens; - char **xattr_values; struct aa_rlimit rlimits; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7fee546ba10d..c07493ce2376 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -228,13 +228,9 @@ void aa_free_profile(struct aa_profile *profile) aa_free_cap_rules(&profile->caps); aa_free_rlimit_rules(&profile->rlimits); - for (i = 0; i < profile->xattr_count; i++) { + for (i = 0; i < profile->xattr_count; i++) kzfree(profile->xattrs[i]); - kzfree(profile->xattr_values[i]); - } kzfree(profile->xattrs); - kzfree(profile->xattr_lens); - kzfree(profile->xattr_values); kzfree(profile->dirname); aa_put_dfa(profile->xmatch); aa_put_dfa(profile->policy.dfa); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 98d019185e57..8a31ddd474d7 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -540,8 +540,7 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) size = unpack_array(e, NULL); profile->xattr_count = size; - profile->xattrs = kcalloc(size, sizeof(char *), - GFP_KERNEL); + profile->xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL); if (!profile->xattrs) goto fail; for (i = 0; i < size; i++) { @@ -554,38 +553,6 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) goto fail; } - if (unpack_nameX(e, AA_STRUCT, "xattr_values")) { - int i, size; - - size = unpack_array(e, NULL); - - /* Must be the same number of xattr values as xattrs */ - if (size != profile->xattr_count) - goto fail; - - profile->xattr_lens = kcalloc(size, sizeof(size_t), - GFP_KERNEL); - if (!profile->xattr_lens) - goto fail; - - profile->xattr_values = kcalloc(size, sizeof(char *), - GFP_KERNEL); - if (!profile->xattr_values) - goto fail; - - for (i = 0; i < size; i++) { - profile->xattr_lens[i] = unpack_blob(e, - &profile->xattr_values[i], NULL); - profile->xattr_values[i] = - kvmemdup(profile->xattr_values[i], - profile->xattr_lens[i]); - } - - if (!unpack_nameX(e, AA_ARRAYEND, NULL)) - goto fail; - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) - goto fail; - } return 1; fail: -- cgit v1.3-14-g43fede From 21f606610502ef56f9180b1529fc7e02957564c8 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 18 Nov 2017 19:43:13 -0800 Subject: apparmor: improve overlapping domain attachment resolution Overlapping domain attachments using the current longest left exact match fail in some simple cases, and with the fix to ensure consistent behavior by failing unresolvable attachments it becomes important to do a better job. eg. under the current match the following are unresolvable where the alternation is clearly a better match under the most specific left match rule. /** /{bin/,}usr/ Use a counting match that detects when a loop in the state machine is enter, and return the match count to provide a better specific left match resolution. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 1 + security/apparmor/domain.c | 30 ++++++---- security/apparmor/include/match.h | 19 ++++++ security/apparmor/match.c | 122 +++++++++++++++++++++++++++++++++++++- 4 files changed, 158 insertions(+), 14 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 35e6b240fb14..3dcc122234c8 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2159,6 +2159,7 @@ static struct aa_sfs_entry aa_sfs_entry_domain[] = { AA_SFS_FILE_BOOLEAN("stack", 1), AA_SFS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), AA_SFS_FILE_BOOLEAN("post_nnp_subset", 1), + AA_SFS_FILE_BOOLEAN("computed_longest_left", 1), AA_SFS_DIR("attach_conditions", aa_sfs_entry_attach), AA_SFS_FILE_STRING("version", "1.2"), { } diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 6a1279f11fcc..57cc892e05a2 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -385,10 +385,13 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm, struct list_head *head, const char **info) { - int len = 0, xattrs = 0; + int candidate_len = 0, candidate_xattrs = 0; bool conflict = false; struct aa_profile *profile, *candidate = NULL; + AA_BUG(!name); + AA_BUG(!head); + list_for_each_entry_rcu(profile, head, base.list) { if (profile->label.flags & FLAG_NULL && &profile->label == ns_unconfined(profile->ns)) @@ -406,19 +409,20 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm, * match. */ if (profile->xmatch) { - unsigned int state; + unsigned int state, count; u32 perm; - if (profile->xmatch_len < len) - continue; - - state = aa_dfa_match(profile->xmatch, - DFA_START, name); + state = aa_dfa_leftmatch(profile->xmatch, DFA_START, + name, &count); perm = dfa_user_allow(profile->xmatch, state); /* any accepting state means a valid match. */ if (perm & MAY_EXEC) { - int ret = aa_xattrs_match(bprm, profile, state); + int ret; + + if (count < candidate_len) + continue; + ret = aa_xattrs_match(bprm, profile, state); /* Fail matching if the xattrs don't match */ if (ret < 0) continue; @@ -429,10 +433,10 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm, * The new match isn't more specific * than the current best match */ - if (profile->xmatch_len == len && - ret <= xattrs) { + if (count == candidate_len && + ret <= candidate_xattrs) { /* Match is equivalent, so conflict */ - if (ret == xattrs) + if (ret == candidate_xattrs) conflict = true; continue; } @@ -441,8 +445,8 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm, * xattrs, or a longer match */ candidate = profile; - len = profile->xmatch_len; - xattrs = ret; + candidate_len = profile->xmatch_len; + candidate_xattrs = ret; conflict = false; } } else if (!strcmp(profile->base.name, name)) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index e0de00bd16a8..958d2b52a7b7 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -138,6 +138,25 @@ unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, void aa_dfa_free_kref(struct kref *kref); +#define WB_HISTORY_SIZE 8 +struct match_workbuf { + unsigned int count; + unsigned int pos; + unsigned int len; + unsigned int size; /* power of 2, same as history size */ + unsigned int history[WB_HISTORY_SIZE]; +}; +#define DEFINE_MATCH_WB(N) \ +struct match_workbuf N = { \ + .count = 0, \ + .pos = 0, \ + .len = 0, \ + .size = WB_HISTORY_SIZE, \ +} + +unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start, + const char *str, unsigned int *count); + /** * aa_get_dfa - increment refcount on dfa @p * @dfa: dfa (MAYBE NULL) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 7ae6ed9d69dd..dd4c995c5e25 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -556,7 +556,6 @@ unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, return state; } - /** * aa_dfa_matchn_until - traverse @dfa until accept or @n bytes consumed * @dfa: the dfa to match @str against (NOT NULL) @@ -618,3 +617,124 @@ unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, *retpos = str; return state; } + +#define inc_wb_pos(wb) \ +do { \ + wb->pos = (wb->pos + 1) & (wb->size - 1); \ + wb->len = (wb->len + 1) & (wb->size - 1); \ +} while (0) + +/* For DFAs that don't support extended tagging of states */ +static bool is_loop(struct match_workbuf *wb, unsigned int state, + unsigned int *adjust) +{ + unsigned int pos = wb->pos; + unsigned int i; + + if (wb->history[pos] < state) + return false; + + for (i = 0; i <= wb->len; i++) { + if (wb->history[pos] == state) { + *adjust = i; + return true; + } + if (pos == 0) + pos = wb->size; + pos--; + } + + *adjust = i; + return true; +} + +static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start, + const char *str, struct match_workbuf *wb, + unsigned int *count) +{ + u16 *def = DEFAULT_TABLE(dfa); + u32 *base = BASE_TABLE(dfa); + u16 *next = NEXT_TABLE(dfa); + u16 *check = CHECK_TABLE(dfa); + unsigned int state = start, pos; + + AA_BUG(!dfa); + AA_BUG(!str); + AA_BUG(!wb); + AA_BUG(!count); + + *count = 0; + if (state == 0) + return 0; + + /* current state is , matching character *str */ + if (dfa->tables[YYTD_ID_EC]) { + /* Equivalence class table defined */ + u8 *equiv = EQUIV_TABLE(dfa); + /* default is direct to next state */ + while (*str) { + unsigned int adjust; + + wb->history[wb->pos] = state; + pos = base_idx(base[state]) + equiv[(u8) *str++]; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + if (is_loop(wb, state, &adjust)) { + state = aa_dfa_match(dfa, state, str); + *count -= adjust; + goto out; + } + inc_wb_pos(wb); + (*count)++; + } + } else { + /* default is direct to next state */ + while (*str) { + unsigned int adjust; + + wb->history[wb->pos] = state; + pos = base_idx(base[state]) + (u8) *str++; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + if (is_loop(wb, state, &adjust)) { + state = aa_dfa_match(dfa, state, str); + *count -= adjust; + goto out; + } + inc_wb_pos(wb); + (*count)++; + } + } + +out: + if (!state) + *count = 0; + return state; +} + +/** + * aa_dfa_leftmatch - traverse @dfa to find state @str stops at + * @dfa: the dfa to match @str against (NOT NULL) + * @start: the state of the dfa to start matching in + * @str: the null terminated string of bytes to match against the dfa (NOT NULL) + * @count: current count of longest left. + * + * aa_dfa_match will match @str against the dfa and return the state it + * finished matching in. The final state can be used to look up the accepting + * label, or as the start state of a continuing match. + * + * Returns: final state reached after input is consumed + */ +unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start, + const char *str, unsigned int *count) +{ + DEFINE_MATCH_WB(wb); + + /* TODO: match for extended state dfas */ + + return leftmatch_fb(dfa, start, str, &wb, count); +} -- cgit v1.3-14-g43fede From 2e67a0875b11b800f63d19e06d0254bbd8892188 Mon Sep 17 00:00:00 2001 From: Yang Shunyong Date: Fri, 2 Feb 2018 17:51:09 +0800 Subject: dmaengine: dmatest: add norandom option Existing option noverify disables both random src/dst address offset setup and data verification. Sometimes, we need to control random src/dst address setup and verification separately, such as disabling random to make sure that test covers addresses in all interleaving banks, but data verification is still performed. This patch adds option norandom to disable random offset setup. Option noverify has been changed to disable data verification only. Cc: Joey Zheng Signed-off-by: Yang Shunyong Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 80cc2be6483c..b9339524d5bd 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -74,7 +74,11 @@ MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), " static bool noverify; module_param(noverify, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(noverify, "Disable random data setup and verification"); +MODULE_PARM_DESC(noverify, "Disable data verification (default: verify)"); + +static bool norandom; +module_param(norandom, bool, 0644); +MODULE_PARM_DESC(norandom, "Disable random offset setup (default: random)"); static bool verbose; module_param(verbose, bool, S_IRUGO | S_IWUSR); @@ -103,6 +107,7 @@ struct dmatest_params { unsigned int pq_sources; int timeout; bool noverify; + bool norandom; }; /** @@ -575,7 +580,7 @@ static int dmatest_func(void *data) break; } - if (params->noverify) + if (params->norandom) len = params->buf_size; else len = dmatest_random() % params->buf_size + 1; @@ -586,17 +591,19 @@ static int dmatest_func(void *data) total_len += len; - if (params->noverify) { + if (params->norandom) { src_off = 0; dst_off = 0; } else { - start = ktime_get(); src_off = dmatest_random() % (params->buf_size - len + 1); dst_off = dmatest_random() % (params->buf_size - len + 1); src_off = (src_off >> align) << align; dst_off = (dst_off >> align) << align; + } + if (!params->noverify) { + start = ktime_get(); dmatest_init_srcs(thread->srcs, src_off, len, params->buf_size, is_memset); dmatest_init_dsts(thread->dsts, dst_off, len, @@ -975,6 +982,7 @@ static void run_threaded_test(struct dmatest_info *info) params->pq_sources = pq_sources; params->timeout = timeout; params->noverify = noverify; + params->norandom = norandom; request_channels(info, DMA_MEMCPY); request_channels(info, DMA_MEMSET); -- cgit v1.3-14-g43fede From 0c0eb4caf03bb6d3d92c70560e0530c8fdf62284 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Mon, 8 Jan 2018 10:50:50 -0500 Subject: dmaengine: avoid map_cnt overflow with CONFIG_DMA_ENGINE_RAID When CONFIG_DMA_ENGINE_RAID is enabled, unmap pool size can reach to 256. But in struct dmaengine_unmap_data, map_cnt is only u8, wrapping to 0, if the unmap pool is maximally used. This triggers BUG() when struct dmaengine_unmap_data is freed. Use u16 to fix the problem. Signed-off-by: Zi Yan Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index f838764993eb..861be5cab1df 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -470,7 +470,11 @@ typedef void (*dma_async_tx_callback_result)(void *dma_async_param, const struct dmaengine_result *result); struct dmaengine_unmap_data { +#if IS_ENABLED(CONFIG_DMA_ENGINE_RAID) + u16 map_cnt; +#else u8 map_cnt; +#endif u8 to_cnt; u8 from_cnt; u8 bidi_cnt; -- cgit v1.3-14-g43fede From 9bfda6636edd5db9b3cdbb2df6483ceb7618ec6a Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Thu, 1 Feb 2018 22:09:25 +0300 Subject: DT: dmaengine: renesas,rcar-dmac: document R8A77980 support Renesas R-Car V3H SoC has the R-Car gen3 compatible DMA controllers. Document R-Car V3H (also known as R8A77980) SoC bindings. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Reviewed-by: Simon Horman Reviewed-by: Rob Herring Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt index 891db41e9420..2f5167da1afc 100644 --- a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt +++ b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt @@ -26,6 +26,7 @@ Required Properties: - "renesas,dmac-r8a7795" (R-Car H3) - "renesas,dmac-r8a7796" (R-Car M3-W) - "renesas,dmac-r8a77970" (R-Car V3M) + - "renesas,dmac-r8a77980" (R-Car V3H) - reg: base address and length of the registers block for the DMAC -- cgit v1.3-14-g43fede From 4af185038652d69038e5b19db6988f44cf10fdb1 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 5 Jan 2018 03:51:44 +0800 Subject: ARM: imx_v6_v7_defconfig: select the CONFIG_CPUFREQ_DT Select CONFIG_CPUFREQ_DT by default to enable cpu-freq driver for i.MX7D. Signed-off-by: Anson Huang Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/configs/imx_v6_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index 4cb9829fccd1..0b9551f51985 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -60,6 +60,7 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPUFREQ_DT=y CONFIG_ARM_IMX6Q_CPUFREQ=y CONFIG_CPU_IDLE=y CONFIG_VFP=y -- cgit v1.3-14-g43fede From 83bd0aaa3b9d1d1bd72d0687ef6716c25f23305b Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 5 Jan 2018 17:40:35 +0800 Subject: ARM: imx_v6_v7_defconfig: enable OP-TEE This patch enables configs for Trusted Execution Environment(TEE) and Open Portable Trusted Execution Environment(OP-TEE). With Linux running in Trustzone non-secure world, OP-TEE OS runs in secure world, linux could use secure services provided by OP-TEE. With TEE/OP-TEE options selected, use the bindings in Documentation/devicetree/bindings/arm/firmware/linaro,optee-tz.txt to let the driver probe work. On i.MX6/7, now the bootflow is U-Boot->OP-TEE->Linux, OP-TEE will automatically create that node. Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- arch/arm/configs/imx_v6_v7_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index 0b9551f51985..9363cec24fa7 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -375,6 +375,8 @@ CONFIG_PWM=y CONFIG_PWM_FSL_FTM=y CONFIG_PWM_IMX=y CONFIG_NVMEM_IMX_OCOTP=y +CONFIG_TEE=y +CONFIG_OPTEE=y CONFIG_MUX_MMIO=y CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y -- cgit v1.3-14-g43fede From 6648b2efbc52caefdc959e3f83d60818bb58e143 Mon Sep 17 00:00:00 2001 From: Marco Franchi Date: Tue, 9 Jan 2018 09:46:24 -0200 Subject: ARM: imx: Update imx_v6_v7_defconfig for mag3110 support The i.MX 6UL EVK has support for the MAG3110 Magnetometer sensor, included in its base board by default. So add support for this Magnetometer in the imx_v6_v7_defconfig. Signed-off-by: Marco Franchi Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/configs/imx_v6_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index 9363cec24fa7..e17d63f92880 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -370,6 +370,7 @@ CONFIG_COMMON_CLK_PWM=y CONFIG_IIO=y CONFIG_IMX7D_ADC=y CONFIG_VF610_ADC=y +CONFIG_MAG3110=y CONFIG_MPL3115=y CONFIG_PWM=y CONFIG_PWM_FSL_FTM=y -- cgit v1.3-14-g43fede From 41346d9ef80a6c015485b6710ad38b94da85b180 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 18 Jan 2018 10:45:27 -0200 Subject: ARM: imx_v6_v7_defconfig: Enable AC97 codec support imx6qdl udoo board has an AC97 codec. Add support for it by default. Signed-off-by: Fabio Estevam Acked-by: Maciej S. Szmigiero Signed-off-by: Shawn Guo --- arch/arm/configs/imx_v6_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index e17d63f92880..8325ea447454 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -286,6 +286,7 @@ CONFIG_SND_SOC_IMX_SGTL5000=y CONFIG_SND_SOC_IMX_SPDIF=y CONFIG_SND_SOC_IMX_MC13783=y CONFIG_SND_SOC_FSL_ASOC_CARD=y +CONFIG_SND_SOC_AC97_CODEC=y CONFIG_SND_SOC_CS42XX8_I2C=y CONFIG_SND_SOC_TLV320AIC3X=y CONFIG_SND_SOC_WM8960=y -- cgit v1.3-14-g43fede From 44c6d823c18aa0c0b41a9e7f3d03973ab82093cb Mon Sep 17 00:00:00 2001 From: Stefan Riedmueller Date: Fri, 2 Feb 2018 10:34:04 +0100 Subject: ARM: imx_v6_v7_defconfig: Enable Dialog Semiconductor DA9062 driver The phyCORE-i.MX 6 uses the DA9062/63 PMIC, RTC and Watchdog driver. Enable these options by default. Signed-off-by: Stefan Riedmueller Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/configs/imx_v6_v7_defconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index 8325ea447454..f32697d52478 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -221,8 +221,10 @@ CONFIG_THERMAL_WRITABLE_TRIPS=y CONFIG_CPU_THERMAL=y CONFIG_IMX_THERMAL=y CONFIG_WATCHDOG=y +CONFIG_DA9062_WATCHDOG=y CONFIG_IMX2_WDT=y CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_DA9062=y CONFIG_MFD_MC13XXX_SPI=y CONFIG_MFD_MC13XXX_I2C=y CONFIG_MFD_STMPE=y @@ -230,6 +232,7 @@ CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_ANATOP=y CONFIG_REGULATOR_DA9052=y +CONFIG_REGULATOR_DA9062=y CONFIG_REGULATOR_GPIO=y CONFIG_REGULATOR_MC13783=y CONFIG_REGULATOR_MC13892=y @@ -356,6 +359,7 @@ CONFIG_RTC_DRV_ISL1208=y CONFIG_RTC_DRV_PCF8523=y CONFIG_RTC_DRV_PCF8563=y CONFIG_RTC_DRV_M41T80=y +CONFIG_RTC_DRV_DA9063=y CONFIG_RTC_DRV_MC13XXX=y CONFIG_RTC_DRV_MXC=y CONFIG_RTC_DRV_MXC_V2=y -- cgit v1.3-14-g43fede From f40a1e3705be23e845fbeec66acf3a6582524145 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 24 Jan 2018 00:42:13 +0800 Subject: soc: imx: gpc: ARM power domain should be always-on ARM power domain does NOT support runtime off, always-on flag should be set to avoid incorrect power state in pm_genpd_summary: Before: root@imx6qpdlsolox:~# cat /sys/kernel/debug/pm_genpd/pm_genpd_summary domain status slaves /device runtime status ---------------------------------------------------------------------- ARM off-0 After: root@imx6qpdlsolox:~# cat /sys/kernel/debug/pm_genpd/pm_genpd_summary domain status slaves /device runtime status ---------------------------------------------------------------------- ARM on Signed-off-by: Anson Huang Reviewed-by: Lucas Stach Acked-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index 53f7275d6cbd..6cafa9b60bc6 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -254,6 +254,7 @@ static struct imx_pm_domain imx_gpc_domains[] = { { .base = { .name = "ARM", + .flags = GENPD_FLAG_ALWAYS_ON, }, }, { .base = { -- cgit v1.3-14-g43fede From cc223282a41f57d304369c596707dc99beec8825 Mon Sep 17 00:00:00 2001 From: Zhang Ying-22455 Date: Wed, 27 Dec 2017 10:41:51 +0800 Subject: arm64: dts: ls1088a: add DT node of watchdog There are eight cores in ls1088a and each core has an watchdog, ls1088a can use sp805-wdt driver, so we just add DT node for it. Signed-off-by: Zhang Ying-22455 Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi | 56 ++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi index 4fc150cd4ca5..8ff2688bdcad 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi @@ -612,6 +612,62 @@ <0000 0 0 3 &gic 0 0 0 121 IRQ_TYPE_LEVEL_HIGH>, <0000 0 0 4 &gic 0 0 0 122 IRQ_TYPE_LEVEL_HIGH>; }; + + cluster1_core0_watchdog: wdt@c000000 { + compatible = "arm,sp805-wdt", "arm,primecell"; + reg = <0x0 0xc000000 0x0 0x1000>; + clocks = <&clockgen 4 3>, <&clockgen 4 3>; + clock-names = "apb_pclk", "wdog_clk"; + }; + + cluster1_core1_watchdog: wdt@c010000 { + compatible = "arm,sp805-wdt", "arm,primecell"; + reg = <0x0 0xc010000 0x0 0x1000>; + clocks = <&clockgen 4 3>, <&clockgen 4 3>; + clock-names = "apb_pclk", "wdog_clk"; + }; + + cluster1_core2_watchdog: wdt@c020000 { + compatible = "arm,sp805-wdt", "arm,primecell"; + reg = <0x0 0xc020000 0x0 0x1000>; + clocks = <&clockgen 4 3>, <&clockgen 4 3>; + clock-names = "apb_pclk", "wdog_clk"; + }; + + cluster1_core3_watchdog: wdt@c030000 { + compatible = "arm,sp805-wdt", "arm,primecell"; + reg = <0x0 0xc030000 0x0 0x1000>; + clocks = <&clockgen 4 3>, <&clockgen 4 3>; + clock-names = "apb_pclk", "wdog_clk"; + }; + + cluster2_core0_watchdog: wdt@c100000 { + compatible = "arm,sp805-wdt", "arm,primecell"; + reg = <0x0 0xc100000 0x0 0x1000>; + clocks = <&clockgen 4 3>, <&clockgen 4 3>; + clock-names = "apb_pclk", "wdog_clk"; + }; + + cluster2_core1_watchdog: wdt@c110000 { + compatible = "arm,sp805-wdt", "arm,primecell"; + reg = <0x0 0xc110000 0x0 0x1000>; + clocks = <&clockgen 4 3>, <&clockgen 4 3>; + clock-names = "apb_pclk", "wdog_clk"; + }; + + cluster2_core2_watchdog: wdt@c120000 { + compatible = "arm,sp805-wdt", "arm,primecell"; + reg = <0x0 0xc120000 0x0 0x1000>; + clocks = <&clockgen 4 3>, <&clockgen 4 3>; + clock-names = "apb_pclk", "wdog_clk"; + }; + + cluster2_core3_watchdog: wdt@c130000 { + compatible = "arm,sp805-wdt", "arm,primecell"; + reg = <0x0 0xc130000 0x0 0x1000>; + clocks = <&clockgen 4 3>, <&clockgen 4 3>; + clock-names = "apb_pclk", "wdog_clk"; + }; }; firmware { -- cgit v1.3-14-g43fede From b3a151ce1b7bc805eea29fdd2479b8565a889ef5 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Mon, 29 Jan 2018 16:07:21 -0500 Subject: arm64: dts: Add DPAA QBMan portal 9 ARM DPAA1 (LS1043 and LS1046) have 10 QBMan portals (indexed 0-9) Enable the one that is missing in the device trees. Signed-off-by: Roy Pledge Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/qoriq-bman-portals.dtsi | 6 ++++++ arch/arm64/boot/dts/freescale/qoriq-qman-portals.dtsi | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/qoriq-bman-portals.dtsi b/arch/arm64/boot/dts/freescale/qoriq-bman-portals.dtsi index c3c2be4f5072..ae15307f6e8b 100644 --- a/arch/arm64/boot/dts/freescale/qoriq-bman-portals.dtsi +++ b/arch/arm64/boot/dts/freescale/qoriq-bman-portals.dtsi @@ -68,4 +68,10 @@ reg = <0x80000 0x4000>, <0x4080000 0x4000>; interrupts = ; }; + + bman-portal@90000 { + compatible = "fsl,bman-portal"; + reg = <0x90000 0x4000>, <0x4090000 0x4000>; + interrupts = ; + }; }; diff --git a/arch/arm64/boot/dts/freescale/qoriq-qman-portals.dtsi b/arch/arm64/boot/dts/freescale/qoriq-qman-portals.dtsi index 2a9aa060efda..6a93a4a9be0e 100644 --- a/arch/arm64/boot/dts/freescale/qoriq-qman-portals.dtsi +++ b/arch/arm64/boot/dts/freescale/qoriq-qman-portals.dtsi @@ -77,4 +77,11 @@ interrupts = ; cell-index = <8>; }; + + qportal9: qman-portal@90000 { + compatible = "fsl,qman-portal"; + reg = <0x90000 0x4000>, <0x4090000 0x4000>; + interrupts = ; + cell-index = <9>; + }; }; -- cgit v1.3-14-g43fede From 3c0b186ec515071b6872cdafa64a5a6044444fd4 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 5 Feb 2018 16:45:32 -0200 Subject: arm64: dts: ls1012a: Move cpu_thermal out of bus node Move cpu_thermal node from soc node to root node. cpu_thermal node does not have any register properties and thus shouldn't be placed on the bus. This fixes the following build warnings with W=1: arch/arm64/boot/dts/freescale/fsl-ls1012a-frdm.dtb: Warning (simple_bus_reg): Node /soc/thermal-zones missing or empty reg/ranges property Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi | 62 +++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi index 82b272fb41b9..4e98d057253b 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1012a.dtsi @@ -118,6 +118,37 @@ mask = <0x02>; }; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <5000>; + thermal-sensors = <&tmu 0>; + + trips { + cpu_alert: cpu-alert { + temperature = <85000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu_crit: cpu-crit { + temperature = <95000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_alert>; + cooling-device = + <&cpu0 THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; @@ -304,37 +335,6 @@ #thermal-sensor-cells = <1>; }; - thermal-zones { - cpu_thermal: cpu-thermal { - polling-delay-passive = <1000>; - polling-delay = <5000>; - thermal-sensors = <&tmu 0>; - - trips { - cpu_alert: cpu-alert { - temperature = <85000>; - hysteresis = <2000>; - type = "passive"; - }; - - cpu_crit: cpu-crit { - temperature = <95000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu_alert>; - cooling-device = - <&cpu0 THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - }; - }; - }; - }; - i2c0: i2c@2180000 { compatible = "fsl,vf610-i2c"; #address-cells = <1>; -- cgit v1.3-14-g43fede From 0abe2a16aa24b9dc705334d9aada5f762bf580d8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 5 Feb 2018 16:45:33 -0200 Subject: arm64: dts: ls1043a: Move cpu_thermal out of bus node Move cpu_thermal node from soc node to root node. cpu_thermal node does not have any register properties and thus shouldn't be placed on the bus. This fixes the following build warnings with W=1: arch/arm64/boot/dts/freescale/fsl-ls1043a-qds.dtb: Warning (simple_bus_reg): Node /soc/thermal-zones missing or empty reg/ranges property Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi | 62 +++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi index 380e7c713395..4d15e111cf12 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi @@ -159,6 +159,37 @@ mask = <0x02>; }; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <5000>; + + thermal-sensors = <&tmu 3>; + + trips { + cpu_alert: cpu-alert { + temperature = <85000>; + hysteresis = <2000>; + type = "passive"; + }; + cpu_crit: cpu-crit { + temperature = <95000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_alert>; + cooling-device = + <&cpu0 THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupts = <1 13 0xf08>, /* Physical Secure PPI */ @@ -342,37 +373,6 @@ #thermal-sensor-cells = <1>; }; - thermal-zones { - cpu_thermal: cpu-thermal { - polling-delay-passive = <1000>; - polling-delay = <5000>; - - thermal-sensors = <&tmu 3>; - - trips { - cpu_alert: cpu-alert { - temperature = <85000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit: cpu-crit { - temperature = <95000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu_alert>; - cooling-device = - <&cpu0 THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - }; - }; - }; - }; - qman: qman@1880000 { compatible = "fsl,qman"; reg = <0x0 0x1880000 0x0 0x10000>; -- cgit v1.3-14-g43fede From e8567b10f5d212d19decb6441d8af68aaaab0c7b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 5 Feb 2018 16:45:34 -0200 Subject: arm64: dts: ls1046a: Move cpu_thermal out of bus node Move cpu_thermal node from soc node to root node. cpu_thermal node does not have any register properties and thus shouldn't be placed on the bus. This fixes the following build warnings with W=1: arch/arm64/boot/dts/freescale/fsl-ls1046a-qds.dtb: Warning (simple_bus_reg): Node /soc/thermal-zones missing or empty reg/ranges property Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi | 62 +++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi index 06b5e12d04d8..3f8b62010c7c 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi @@ -147,6 +147,37 @@ mask = <0x02>; }; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <5000>; + thermal-sensors = <&tmu 3>; + + trips { + cpu_alert: cpu-alert { + temperature = <85000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu_crit: cpu-crit { + temperature = <95000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_alert>; + cooling-device = + <&cpu0 THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupts = ; }; - thermal-zones { - cpu_thermal: cpu-thermal { - polling-delay-passive = <1000>; - polling-delay = <5000>; - thermal-sensors = <&tmu 3>; - - trips { - cpu_alert: cpu-alert { - temperature = <85000>; - hysteresis = <2000>; - type = "passive"; - }; - - cpu_crit: cpu-crit { - temperature = <95000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu_alert>; - cooling-device = - <&cpu0 THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - }; - }; - }; - }; - dspi: dspi@2100000 { compatible = "fsl,ls1021a-v1.0-dspi"; #address-cells = <1>; -- cgit v1.3-14-g43fede From 85530a7a765335289393a31d4ad1ef9209e8e568 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 5 Feb 2018 16:45:35 -0200 Subject: arm64: dts: ls1088a: Move cpu_thermal out of bus node Move cpu_thermal node from soc node to root node. cpu_thermal node does not have any register properties and thus shouldn't be placed on the bus. This fixes the following build warnings with W=1: arch/arm64/boot/dts/freescale/fsl-ls1088a-qds.dtb: Warning (simple_bus_reg): Node /soc/thermal-zones missing or empty reg/ranges property Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi | 76 +++++++++++++------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi index 8ff2688bdcad..12ffa584d173 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi @@ -158,6 +158,44 @@ }; }; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <5000>; + thermal-sensors = <&tmu 0>; + + trips { + cpu_alert: cpu-alert { + temperature = <85000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu_crit: cpu-crit { + temperature = <95000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_alert>; + cooling-device = + <&cpu0 THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + + map1 { + trip = <&cpu_alert>; + cooling-device = + <&cpu4 THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupts = <1 13 IRQ_TYPE_LEVEL_LOW>,/* Physical Secure PPI */ @@ -315,44 +353,6 @@ #thermal-sensor-cells = <1>; }; - thermal-zones { - cpu_thermal: cpu-thermal { - polling-delay-passive = <1000>; - polling-delay = <5000>; - thermal-sensors = <&tmu 0>; - - trips { - cpu_alert: cpu-alert { - temperature = <85000>; - hysteresis = <2000>; - type = "passive"; - }; - - cpu_crit: cpu-crit { - temperature = <95000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu_alert>; - cooling-device = - <&cpu0 THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - }; - - map1 { - trip = <&cpu_alert>; - cooling-device = - <&cpu4 THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - }; - }; - }; - }; - duart0: serial@21c0500 { compatible = "fsl,ns16550", "ns16550a"; reg = <0x0 0x21c0500 0x0 0x100>; -- cgit v1.3-14-g43fede From dc7eda93318474115dd722c6d3bc49b77e9c9304 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 5 Feb 2018 16:45:36 -0200 Subject: arm64: dts: ls208xa: Move cpu_thermal out of bus node Move cpu_thermal node from soc node to root node. cpu_thermal node does not have any register properties and thus shouldn't be placed on the bus. This fixes the following build warnings with W=1: arch/arm64/boot/dts/freescale/fsl-ls2080a-qds.dtb: Warning (simple_bus_reg): Node /soc/thermal-zones missing or empty reg/ranges property Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi | 98 +++++++++++++------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi index f3a40af33af8..137ef4dfc3e9 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi @@ -111,6 +111,55 @@ mask = <0x2>; }; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <5000>; + + thermal-sensors = <&tmu 4>; + + trips { + cpu_alert: cpu-alert { + temperature = <75000>; + hysteresis = <2000>; + type = "passive"; + }; + cpu_crit: cpu-crit { + temperature = <85000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_alert>; + cooling-device = + <&cpu0 THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu_alert>; + cooling-device = + <&cpu2 THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + map2 { + trip = <&cpu_alert>; + cooling-device = + <&cpu4 THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + map3 { + trip = <&cpu_alert>; + cooling-device = + <&cpu6 THERMAL_NO_LIMIT + THERMAL_NO_LIMIT>; + }; + }; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupts = <1 13 4>, /* Physical Secure PPI, active-low */ @@ -194,55 +243,6 @@ #thermal-sensor-cells = <1>; }; - thermal-zones { - cpu_thermal: cpu-thermal { - polling-delay-passive = <1000>; - polling-delay = <5000>; - - thermal-sensors = <&tmu 4>; - - trips { - cpu_alert: cpu-alert { - temperature = <75000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit: cpu-crit { - temperature = <85000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu_alert>; - cooling-device = - <&cpu0 THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - }; - map1 { - trip = <&cpu_alert>; - cooling-device = - <&cpu2 THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - }; - map2 { - trip = <&cpu_alert>; - cooling-device = - <&cpu4 THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - }; - map3 { - trip = <&cpu_alert>; - cooling-device = - <&cpu6 THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - }; - }; - }; - }; - serial0: serial@21c0500 { compatible = "fsl,ns16550", "ns16550a"; reg = <0x0 0x21c0500 0x0 0x100>; -- cgit v1.3-14-g43fede From 5c1f3d95f7b7a1c0a52e148523a411d153e82c48 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 5 Feb 2018 16:45:37 -0200 Subject: arm64: dts: ls208xa-qds: Pass unit name to dspi child nodes Pass unit name to dspi child nodes to fix the following build warnings with W=1: arch/arm64/boot/dts/freescale/fsl-ls2080a-qds.dtb: Warning (unit_address_vs_reg): Node /soc/dspi@2100000/n25q128a has a reg or ranges property, but no unit name arch/arm64/boot/dts/freescale/fsl-ls2080a-qds.dtb: Warning (unit_address_vs_reg): Node /soc/dspi@2100000/sst25wf040b has a reg or ranges property, but no unit name arch/arm64/boot/dts/freescale/fsl-ls2080a-qds.dtb: Warning (unit_address_vs_reg): Node /soc/dspi@2100000/en25s64 has a reg or ranges property, but no unit name Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi index b2374469a830..a1a5ccbce584 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi @@ -140,21 +140,21 @@ &dspi { status = "okay"; - dflash0: n25q128a { + dflash0: n25q128a@0 { #address-cells = <1>; #size-cells = <1>; compatible = "st,m25p80"; spi-max-frequency = <3000000>; reg = <0>; }; - dflash1: sst25wf040b { + dflash1: sst25wf040b@1 { #address-cells = <1>; #size-cells = <1>; compatible = "st,m25p80"; spi-max-frequency = <3000000>; reg = <1>; }; - dflash2: en25s64 { + dflash2: en25s64@2 { #address-cells = <1>; #size-cells = <1>; compatible = "st,m25p80"; -- cgit v1.3-14-g43fede From 8acb019209a646db73934c52a57d4021e533f679 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 5 Feb 2018 16:45:38 -0200 Subject: arm64: dts: ls208xa-qds: Fix the 'reg' property The 'reg' property should match the corresponding @ address, so fix it accordingly. Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi index a1a5ccbce584..1de618801c73 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi @@ -177,7 +177,7 @@ #size-cells = <1>; compatible = "st,m25p80"; spi-max-frequency = <20000000>; - reg = <0>; + reg = <2>; }; }; -- cgit v1.3-14-g43fede From 1f9d1311a3a98e8f5db509a4ff7bf84f7d6a4ca9 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 9 Jan 2018 13:24:43 -0200 Subject: ARM: imx: Remove epit support Currently there is no user of EPIT, so remove such unused code. If someone wants to add EPIT support back, then the person needs to create a proper support into drivers/clocksource/ and add device tree support, proper bindings, etc. Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Signed-off-by: Shawn Guo --- arch/arm/mach-imx/Kconfig | 13 --- arch/arm/mach-imx/Makefile | 1 - arch/arm/mach-imx/epit.c | 228 --------------------------------------------- 3 files changed, 242 deletions(-) delete mode 100644 arch/arm/mach-imx/epit.c diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 782699e67600..1831319a679c 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -32,18 +32,6 @@ config MXC_DEBUG_BOARD data/address de-multiplexing and decode, signal level shift, interrupt control and various board functions. -config HAVE_EPIT - bool - -config MXC_USE_EPIT - bool "Use EPIT instead of GPT" - depends on HAVE_EPIT - help - Use EPIT as the system timer on systems that have it. Normally you - don't have a reason to do so as the EPIT has the same features and - uses the same clocks as the GPT. Anyway, on some systems the GPT - may be in use for other purposes. - config HAVE_IMX_ANATOP bool @@ -85,7 +73,6 @@ config SOC_IMX31 config SOC_IMX35 bool select ARCH_MXC_IOMUX_V3 - select HAVE_EPIT select MXC_AVIC select PINCTRL_IMX35 diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 8ff71058207d..04ba789ca34c 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o obj-$(CONFIG_MXC_TZIC) += tzic.o obj-$(CONFIG_MXC_AVIC) += avic.o -obj-$(CONFIG_MXC_USE_EPIT) += epit.o obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o ifeq ($(CONFIG_CPU_IDLE),y) diff --git a/arch/arm/mach-imx/epit.c b/arch/arm/mach-imx/epit.c deleted file mode 100644 index fb9a73a57d00..000000000000 --- a/arch/arm/mach-imx/epit.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * linux/arch/arm/plat-mxc/epit.c - * - * Copyright (C) 2010 Sascha Hauer - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#define EPITCR 0x00 -#define EPITSR 0x04 -#define EPITLR 0x08 -#define EPITCMPR 0x0c -#define EPITCNR 0x10 - -#define EPITCR_EN (1 << 0) -#define EPITCR_ENMOD (1 << 1) -#define EPITCR_OCIEN (1 << 2) -#define EPITCR_RLD (1 << 3) -#define EPITCR_PRESC(x) (((x) & 0xfff) << 4) -#define EPITCR_SWR (1 << 16) -#define EPITCR_IOVW (1 << 17) -#define EPITCR_DBGEN (1 << 18) -#define EPITCR_WAITEN (1 << 19) -#define EPITCR_RES (1 << 20) -#define EPITCR_STOPEN (1 << 21) -#define EPITCR_OM_DISCON (0 << 22) -#define EPITCR_OM_TOGGLE (1 << 22) -#define EPITCR_OM_CLEAR (2 << 22) -#define EPITCR_OM_SET (3 << 22) -#define EPITCR_CLKSRC_OFF (0 << 24) -#define EPITCR_CLKSRC_PERIPHERAL (1 << 24) -#define EPITCR_CLKSRC_REF_HIGH (1 << 24) -#define EPITCR_CLKSRC_REF_LOW (3 << 24) - -#define EPITSR_OCIF (1 << 0) - -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "hardware.h" - -static struct clock_event_device clockevent_epit; - -static void __iomem *timer_base; - -static inline void epit_irq_disable(void) -{ - u32 val; - - val = imx_readl(timer_base + EPITCR); - val &= ~EPITCR_OCIEN; - imx_writel(val, timer_base + EPITCR); -} - -static inline void epit_irq_enable(void) -{ - u32 val; - - val = imx_readl(timer_base + EPITCR); - val |= EPITCR_OCIEN; - imx_writel(val, timer_base + EPITCR); -} - -static void epit_irq_acknowledge(void) -{ - imx_writel(EPITSR_OCIF, timer_base + EPITSR); -} - -static int __init epit_clocksource_init(struct clk *timer_clk) -{ - unsigned int c = clk_get_rate(timer_clk); - - return clocksource_mmio_init(timer_base + EPITCNR, "epit", c, 200, 32, - clocksource_mmio_readl_down); -} - -/* clock event */ - -static int epit_set_next_event(unsigned long evt, - struct clock_event_device *unused) -{ - unsigned long tcmp; - - tcmp = imx_readl(timer_base + EPITCNR); - - imx_writel(tcmp - evt, timer_base + EPITCMPR); - - return 0; -} - -/* Left event sources disabled, no more interrupts appear */ -static int epit_shutdown(struct clock_event_device *evt) -{ - unsigned long flags; - - /* - * The timer interrupt generation is disabled at least - * for enough time to call epit_set_next_event() - */ - local_irq_save(flags); - - /* Disable interrupt in GPT module */ - epit_irq_disable(); - - /* Clear pending interrupt */ - epit_irq_acknowledge(); - - local_irq_restore(flags); - - return 0; -} - -static int epit_set_oneshot(struct clock_event_device *evt) -{ - unsigned long flags; - - /* - * The timer interrupt generation is disabled at least - * for enough time to call epit_set_next_event() - */ - local_irq_save(flags); - - /* Disable interrupt in GPT module */ - epit_irq_disable(); - - /* Clear pending interrupt, only while switching mode */ - if (!clockevent_state_oneshot(evt)) - epit_irq_acknowledge(); - - /* - * Do not put overhead of interrupt enable/disable into - * epit_set_next_event(), the core has about 4 minutes - * to call epit_set_next_event() or shutdown clock after - * mode switching - */ - epit_irq_enable(); - local_irq_restore(flags); - - return 0; -} - -/* - * IRQ handler for the timer - */ -static irqreturn_t epit_timer_interrupt(int irq, void *dev_id) -{ - struct clock_event_device *evt = &clockevent_epit; - - epit_irq_acknowledge(); - - evt->event_handler(evt); - - return IRQ_HANDLED; -} - -static struct irqaction epit_timer_irq = { - .name = "i.MX EPIT Timer Tick", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = epit_timer_interrupt, -}; - -static struct clock_event_device clockevent_epit = { - .name = "epit", - .features = CLOCK_EVT_FEAT_ONESHOT, - .set_state_shutdown = epit_shutdown, - .tick_resume = epit_shutdown, - .set_state_oneshot = epit_set_oneshot, - .set_next_event = epit_set_next_event, - .rating = 200, -}; - -static int __init epit_clockevent_init(struct clk *timer_clk) -{ - clockevent_epit.cpumask = cpumask_of(0); - clockevents_config_and_register(&clockevent_epit, - clk_get_rate(timer_clk), - 0x800, 0xfffffffe); - - return 0; -} - -void __init epit_timer_init(void __iomem *base, int irq) -{ - struct clk *timer_clk; - - timer_clk = clk_get_sys("imx-epit.0", NULL); - if (IS_ERR(timer_clk)) { - pr_err("i.MX epit: unable to get clk\n"); - return; - } - - clk_prepare_enable(timer_clk); - - timer_base = base; - - /* - * Initialise to a known state (all timers off, and timing reset) - */ - imx_writel(0x0, timer_base + EPITCR); - - imx_writel(0xffffffff, timer_base + EPITLR); - imx_writel(EPITCR_EN | EPITCR_CLKSRC_REF_HIGH | EPITCR_WAITEN, - timer_base + EPITCR); - - /* init and register the timer to the framework */ - epit_clocksource_init(timer_clk); - epit_clockevent_init(timer_clk); - - /* Make irqs happen */ - setup_irq(irq, &epit_timer_irq); -} -- cgit v1.3-14-g43fede From 49a1a99c530c297aad4e40af8cffa3195f4e1b58 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 10 Jan 2018 22:04:47 +0100 Subject: ARM: imx: add timer stop flag to ARM power off state When the CPU is in ARM power off state the ARM architected timers are stopped. The flag is already present in the higher power WAIT mode. This allows to use the ARM generic timer on i.MX 6UL/6ULL SoC. Without the flag the kernel freezes when the timer enters the first time ARM power off mode. Note: The default timer on i.MX6SX is the i.MX GPT timer which is not disabled during CPU idle. However, the timer is not affected by the CPUIDLE_FLAG_TIMER_STOP flag. The flag only affects CPU local timers. Cc: Anson Huang Signed-off-by: Stefan Agner Reviewed-by: Lucas Stach Signed-off-by: Shawn Guo --- arch/arm/mach-imx/cpuidle-imx6sx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-imx/cpuidle-imx6sx.c b/arch/arm/mach-imx/cpuidle-imx6sx.c index c5a5c3a70ab1..d0f14b761ff7 100644 --- a/arch/arm/mach-imx/cpuidle-imx6sx.c +++ b/arch/arm/mach-imx/cpuidle-imx6sx.c @@ -89,6 +89,7 @@ static struct cpuidle_driver imx6sx_cpuidle_driver = { */ .exit_latency = 300, .target_residency = 500, + .flags = CPUIDLE_FLAG_TIMER_STOP, .enter = imx6sx_enter_wait, .name = "LOW-POWER-IDLE", .desc = "ARM power off", -- cgit v1.3-14-g43fede From 261b350367d729bbfdd4aa92a9e48cfd6500b6ba Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Fri, 2 Feb 2018 16:06:27 +0800 Subject: ARM: imx: Improve the soc revision calculation flow On our i.MX6 SOC, the DIGPROG register is used for representing the SOC ID and silicon revision. The revision has two part: MAJOR and MINOR. each is represented in 8 bits in the register. bits [15:8]: reflect the MAJOR part of the revision; bits [7:0]: reflect the MINOR part of the revision; In our linux kernel, the soc revision is represented in 8 bits. MAJOR part and MINOR each occupy 4 bits. previous method does NOT take care about the MAJOR part in DIGPROG register. So reformat the revision read from the HW to be compatible with the revision format used in kernel. Signed-off-by: Bai Ping Acked-by: Dong Aisheng Tested-by: Christoph Fritz Signed-off-by: Shawn Guo --- arch/arm/mach-imx/anatop.c | 56 ++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/arch/arm/mach-imx/anatop.c b/arch/arm/mach-imx/anatop.c index 649a84c251ad..61f3d94f1633 100644 --- a/arch/arm/mach-imx/anatop.c +++ b/arch/arm/mach-imx/anatop.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2013-2015 Freescale Semiconductor, Inc. + * Copyright 2017-2018 NXP. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -116,6 +117,7 @@ void __init imx_init_revision_from_anatop(void) unsigned int revision; u32 digprog; u16 offset = ANADIG_DIGPROG; + u8 major_part, minor_part; np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); anatop_base = of_iomap(np, 0); @@ -127,45 +129,25 @@ void __init imx_init_revision_from_anatop(void) digprog = readl_relaxed(anatop_base + offset); iounmap(anatop_base); - switch (digprog & 0xff) { - case 0: - /* - * For i.MX6QP, most of the code for i.MX6Q can be resued, - * so internally, we identify it as i.MX6Q Rev 2.0 - */ - if (digprog >> 8 & 0x01) - revision = IMX_CHIP_REVISION_2_0; - else - revision = IMX_CHIP_REVISION_1_0; - break; - case 1: - revision = IMX_CHIP_REVISION_1_1; - break; - case 2: - revision = IMX_CHIP_REVISION_1_2; - break; - case 3: - revision = IMX_CHIP_REVISION_1_3; - break; - case 4: - revision = IMX_CHIP_REVISION_1_4; - break; - case 5: - /* - * i.MX6DQ TO1.5 is defined as Rev 1.3 in Data Sheet, marked - * as 'D' in Part Number last character. - */ - revision = IMX_CHIP_REVISION_1_5; - break; - default: + /* + * On i.MX7D digprog value match linux version format, so + * it needn't map again and we can use register value directly. + */ + if (of_device_is_compatible(np, "fsl,imx7d-anatop")) { + revision = digprog & 0xff; + } else { /* - * Fail back to return raw register value instead of 0xff. - * It will be easy to know version information in SOC if it - * can't be recognized by known version. And some chip's (i.MX7D) - * digprog value match linux version format, so it needn't map - * again and we can use register value directly. + * MAJOR: [15:8], the major silicon revison; + * MINOR: [7: 0], the minor silicon revison; + * + * please refer to the i.MX RM for the detailed + * silicon revison bit define. + * format the major part and minor part to match the + * linux kernel soc version format. */ - revision = digprog & 0xff; + major_part = (digprog >> 8) & 0xf; + minor_part = digprog & 0xf; + revision = ((major_part + 1) << 4) | minor_part; } mxc_set_cpu_type(digprog >> 16 & 0xff); -- cgit v1.3-14-g43fede From e98bbbf3bd59d5283750e8f56031bd1cc2feafd8 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 23 Jan 2018 12:14:43 -0600 Subject: phy: da8xx-usb: rename clock con_ids This renames the clock con_ids in the DA8XX USB PHY driver as well as the matching names in the mach clock registration code. This is in preparation for using device tree clocks where these names will become part of the device tree bindings. The new names more closely match the names used in the USB clock diagram in the SoC TRM. Acked-by: Kishon Vijay Abraham I Signed-off-by: David Lechner Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/usb-da8xx.c | 12 ++++++------ drivers/phy/ti/phy-da8xx-usb.c | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-davinci/usb-da8xx.c b/arch/arm/mach-davinci/usb-da8xx.c index d480a02e1298..fb31f6eeba96 100644 --- a/arch/arm/mach-davinci/usb-da8xx.c +++ b/arch/arm/mach-davinci/usb-da8xx.c @@ -256,14 +256,14 @@ static int usb20_phy_clk_set_parent(struct clk *clk, struct clk *parent) } static struct clk usb20_phy_clk = { - .name = "usb20_phy", + .name = "usb0_clk48", .clk_enable = usb20_phy_clk_enable, .clk_disable = usb20_phy_clk_disable, .set_parent = usb20_phy_clk_set_parent, }; static struct clk_lookup usb20_phy_clk_lookup = - CLK("da8xx-usb-phy", "usb20_phy", &usb20_phy_clk); + CLK("da8xx-usb-phy", "usb0_clk48", &usb20_phy_clk); /** * da8xx_register_usb20_phy_clk - register USB0PHYCLKMUX clock @@ -320,18 +320,18 @@ static int usb11_phy_clk_set_parent(struct clk *clk, struct clk *parent) } static struct clk usb11_phy_clk = { - .name = "usb11_phy", + .name = "usb1_clk48", .set_parent = usb11_phy_clk_set_parent, }; static struct clk_lookup usb11_phy_clk_lookup = - CLK("da8xx-usb-phy", "usb11_phy", &usb11_phy_clk); + CLK("da8xx-usb-phy", "usb1_clk48", &usb11_phy_clk); /** * da8xx_register_usb11_phy_clk - register USB1PHYCLKMUX clock * * @use_usb_refclkin: Selects the parent clock - either "usb_refclkin" if true - * or "usb20_phy" if false. + * or "usb0_clk48" if false. */ int __init da8xx_register_usb11_phy_clk(bool use_usb_refclkin) { @@ -341,7 +341,7 @@ int __init da8xx_register_usb11_phy_clk(bool use_usb_refclkin) if (use_usb_refclkin) parent = clk_get(NULL, "usb_refclkin"); else - parent = clk_get(&da8xx_usb_phy.dev, "usb20_phy"); + parent = clk_get(&da8xx_usb_phy.dev, "usb0_clk48"); if (IS_ERR(parent)) return PTR_ERR(parent); diff --git a/drivers/phy/ti/phy-da8xx-usb.c b/drivers/phy/ti/phy-da8xx-usb.c index 1b82bff6330f..5bd33d06df95 100644 --- a/drivers/phy/ti/phy-da8xx-usb.c +++ b/drivers/phy/ti/phy-da8xx-usb.c @@ -162,15 +162,15 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(d_phy->regmap); } - d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy"); + d_phy->usb11_clk = devm_clk_get(dev, "usb1_clk48"); if (IS_ERR(d_phy->usb11_clk)) { - dev_err(dev, "Failed to get usb11_phy clock\n"); + dev_err(dev, "Failed to get usb1_clk48\n"); return PTR_ERR(d_phy->usb11_clk); } - d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy"); + d_phy->usb20_clk = devm_clk_get(dev, "usb0_clk48"); if (IS_ERR(d_phy->usb20_clk)) { - dev_err(dev, "Failed to get usb20_phy clock\n"); + dev_err(dev, "Failed to get usb0_clk48\n"); return PTR_ERR(d_phy->usb20_clk); } -- cgit v1.3-14-g43fede From e17ee5f08b9849b9f80fadab96364121c6bdd207 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 7 Feb 2018 14:20:03 +0800 Subject: soc: rockchip: disable jtag switching for RK3228/RK3229 SoCs Disable IO function switching between sdmmc and jtag for RK3228 and RK3229 SoCs. Signed-off-by: Shawn Lin Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/grf.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c index 15e71fd6c513..dd81b87d79f0 100644 --- a/drivers/soc/rockchip/grf.c +++ b/drivers/soc/rockchip/grf.c @@ -43,6 +43,17 @@ static const struct rockchip_grf_info rk3036_grf __initconst = { .num_values = ARRAY_SIZE(rk3036_defaults), }; +#define RK3228_GRF_SOC_CON6 0x418 + +static const struct rockchip_grf_value rk3228_defaults[] __initconst = { + { "jtag switching", RK3228_GRF_SOC_CON6, HIWORD_UPDATE(0, 1, 8) }, +}; + +static const struct rockchip_grf_info rk3228_grf __initconst = { + .values = rk3228_defaults, + .num_values = ARRAY_SIZE(rk3228_defaults), +}; + #define RK3288_GRF_SOC_CON0 0x244 static const struct rockchip_grf_value rk3288_defaults[] __initconst = { @@ -91,6 +102,9 @@ static const struct of_device_id rockchip_grf_dt_match[] __initconst = { { .compatible = "rockchip,rk3036-grf", .data = (void *)&rk3036_grf, + }, { + .compatible = "rockchip,rk3228-grf", + .data = (void *)&rk3228_grf, }, { .compatible = "rockchip,rk3288-grf", .data = (void *)&rk3288_grf, -- cgit v1.3-14-g43fede From 99435889bd4a71c0ac04ce3af6911d8b05c62196 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 5 Jan 2018 16:47:55 -0800 Subject: ARM: dts: rockchip: drop veyron's nonstandard 'backlight-boot-off' This was used out-of-tree as a hack for resolving issues where some systems expect the backlight to turn on automatically at boot, while others expect to manage the backlight status via a DRM/panel driver. Those issues have since been fixed upstream in pwm_bl.c without device tree hacks, and so this un-documented property should no longer be useful. Signed-off-by: Brian Norris Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3288-veyron-chromebook.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/boot/dts/rk3288-veyron-chromebook.dtsi b/arch/arm/boot/dts/rk3288-veyron-chromebook.dtsi index d752a315f884..be487111d025 100644 --- a/arch/arm/boot/dts/rk3288-veyron-chromebook.dtsi +++ b/arch/arm/boot/dts/rk3288-veyron-chromebook.dtsi @@ -92,7 +92,6 @@ 248 249 250 251 252 253 254 255>; default-brightness-level = <128>; enable-gpios = <&gpio7 RK_PA2 GPIO_ACTIVE_HIGH>; - backlight-boot-off; pinctrl-names = "default"; pinctrl-0 = <&bl_en>; pwms = <&pwm0 0 1000000 0>; -- cgit v1.3-14-g43fede From 692f492f66585aa625efa70bef7c25f1cfd7408b Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 7 Feb 2018 14:20:02 +0800 Subject: ARM: dts: rockchip: add "rockchip,rk3228-grf" compatible for rk322x grf node rockchip,rk3228-grf is used both for rk3228 and rk3229. Signed-off-by: Shawn Lin Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk322x.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/rk322x.dtsi b/arch/arm/boot/dts/rk322x.dtsi index 780ec3a99b21..3dd2e127af2c 100644 --- a/arch/arm/boot/dts/rk322x.dtsi +++ b/arch/arm/boot/dts/rk322x.dtsi @@ -233,7 +233,7 @@ }; grf: syscon@11000000 { - compatible = "syscon", "simple-mfd"; + compatible = "rockchip,rk3228-grf", "syscon", "simple-mfd"; reg = <0x11000000 0x1000>; #address-cells = <1>; #size-cells = <1>; -- cgit v1.3-14-g43fede From ba2b043e90f264df1969c4428bc89d7121e475e8 Mon Sep 17 00:00:00 2001 From: Shunqian Zheng Date: Tue, 9 Jan 2018 09:42:21 +0800 Subject: arm64: dts: rockchip: Add cif test clocks for rk3399 There are three pins can act as cif test clock for rk3399. They're sourced from 24M and output 24M by default and some boards may use them as camera 24M xvclk. Signed-off-by: Shunqian Zheng Reviewed-by: Brian Norris Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index 7aa2144e0d47..85b30c984acf 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi @@ -2293,6 +2293,23 @@ }; }; + testclk { + test_clkout0: test-clkout0 { + rockchip,pins = + <0 0 RK_FUNC_1 &pcfg_pull_none>; + }; + + test_clkout1: test-clkout1 { + rockchip,pins = + <2 25 RK_FUNC_2 &pcfg_pull_none>; + }; + + test_clkout2: test-clkout2 { + rockchip,pins = + <0 8 RK_FUNC_3 &pcfg_pull_none>; + }; + }; + tsadc { otp_gpio: otp-gpio { rockchip,pins = <1 6 RK_FUNC_GPIO &pcfg_pull_none>; -- cgit v1.3-14-g43fede From 2171f4fdac060e05826f9972a19bfdb569f5dcfe Mon Sep 17 00:00:00 2001 From: Levin Du Date: Tue, 23 Jan 2018 10:10:17 +0800 Subject: arm64: dts: rockchip: add roc-rk3328-cc board The roc-rk3328-cc is a credit card size single board computer using the Rockchip RK3328 Quad-Core ARM Cortex A53 64-Bit Processor and supporting up to 2GB 2133MHz LPDDR4 memory. It provides eMMC module socket, MicroSD Card slot, USB 2.0/3.0, Gigabit Ethernet, HDMI/CVBS, Infrared Receiver, SPDIF/I2S, and SPI/I2C/UART/PWM interfaces. The devicetree currently supports basic peripherals. Signed-off-by: Levin Du Reviewed-by: Rob Herring Signed-off-by: Heiko Stuebner --- Documentation/devicetree/bindings/arm/rockchip.txt | 4 + arch/arm64/boot/dts/rockchip/Makefile | 1 + arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts | 267 +++++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt index 326d24bca1a9..0f76a621015c 100644 --- a/Documentation/devicetree/bindings/arm/rockchip.txt +++ b/Documentation/devicetree/bindings/arm/rockchip.txt @@ -50,6 +50,10 @@ Rockchip platforms device tree bindings Required root node properties: - compatible = "firefly,firefly-rk3399", "rockchip,rk3399"; +- Firefly roc-rk3328-cc board: + Required root node properties: + - compatible = "firefly,roc-rk3328-cc", "rockchip,rk3328"; + - ChipSPARK PopMetal-RK3288 board: Required root node properties: - compatible = "chipspark,popmetal-rk3288", "rockchip,rk3288"; diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile index ce2701e37d00..cf14b8679eac 100644 --- a/arch/arm64/boot/dts/rockchip/Makefile +++ b/arch/arm64/boot/dts/rockchip/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-evb.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock64.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-roc-cc.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68-meta.dtb diff --git a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts new file mode 100644 index 000000000000..246c317f6a68 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2017 T-Chip Intelligent Technology Co., Ltd + */ + +/dts-v1/; +#include "rk3328.dtsi" + +/ { + model = "Firefly roc-rk3328-cc"; + compatible = "firefly,roc-rk3328-cc", "rockchip,rk3328"; + + chosen { + stdout-path = "serial2:1500000n8"; + }; + + gmac_clkin: external-gmac-clock { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "gmac_clkin"; + #clock-cells = <0>; + }; + + dc_12v: dc-12v { + compatible = "regulator-fixed"; + regulator-name = "dc_12v"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + }; + + vcc_sd: sdmmc-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0m1_gpio>; + regulator-name = "vcc_sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_io>; + }; + + vcc_host1_5v: vcc_otg_5v: vcc-host1-5v-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb20_host_drv>; + regulator-name = "vcc_host1_5v"; + regulator-always-on; + vin-supply = <&vcc_sys>; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&dc_12v>; + }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; +}; + +&cpu0 { + cpu-supply = <&vdd_arm>; +}; + +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>; + status = "okay"; +}; + +&gmac2io { + assigned-clocks = <&cru SCLK_MAC2IO>, <&cru SCLK_MAC2IO_EXT>; + assigned-clock-parents = <&gmac_clkin>, <&gmac_clkin>; + clock_in_out = "input"; + phy-supply = <&vcc_phy>; + phy-mode = "rgmii"; + pinctrl-names = "default"; + pinctrl-0 = <&rgmiim1_pins>; + snps,reset-gpio = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 50000>; + tx_delay = <0x25>; + rx_delay = <0x11>; + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + rk805: pmic@18 { + compatible = "rockchip,rk805"; + reg = <0x18>; + interrupt-parent = <&gpio1>; + interrupts = <24 IRQ_TYPE_LEVEL_LOW>; + #clock-cells = <1>; + clock-output-names = "xin32k", "rk805-clkout2"; + gpio-controller; + #gpio-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>; + rockchip,system-power-controller; + wakeup-source; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc5-supply = <&vcc_io>; + vcc6-supply = <&vcc_io>; + + regulators { + vdd_logic: DCDC_REG1 { + regulator-name = "vdd_logic"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + + vdd_arm: DCDC_REG2 { + regulator-name = "vdd_arm"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <950000>; + }; + }; + + vcc_ddr: DCDC_REG3 { + regulator-name = "vcc_ddr"; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_io: DCDC_REG4 { + regulator-name = "vcc_io"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vcc_18: LDO_REG1 { + regulator-name = "vcc_18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc18_emmc: LDO_REG2 { + regulator-name = "vcc18_emmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd_10: LDO_REG3 { + regulator-name = "vdd_10"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + }; + }; +}; + +&pinctrl { + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = <1 RK_PD0 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + usb2 { + usb20_host_drv: usb20-host-drv { + rockchip,pins = <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + max-frequency = <150000000>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_dectn &sdmmc0_bus4>; + vmmc-supply = <&vcc_sd>; + status = "okay"; +}; + +&tsadc { + status = "okay"; +}; + +&u2phy { + status = "okay"; +}; + +&u2phy_host { + status = "okay"; +}; + +&u2phy_otg { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&usb20_otg { + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; -- cgit v1.3-14-g43fede From 32c79915e081c82dce4e0ae8fc92d4169a829bff Mon Sep 17 00:00:00 2001 From: Klaus Goger Date: Tue, 30 Jan 2018 11:15:55 +0100 Subject: arm64: dts: rockchip: enable tsadc on rk3399-puma Enable the SoC thermal sensor on RK3399-Q7 (Puma). As we want to do do a full board reset instead of just a SoC one, set hw-tshut-mode to GPIO. Signed-off-by: Klaus Goger Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi index 1fc5060d7027..ce15a57a5b85 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi @@ -505,6 +505,12 @@ }; }; +&tsadc { + rockchip,hw-tshut-mode = <1>; + rockchip,hw-tshut-polarity = <1>; + status = "okay"; +}; + &u2phy1 { status = "okay"; -- cgit v1.3-14-g43fede From 0efaf807839375b3a20366b8a057694c0ccad37b Mon Sep 17 00:00:00 2001 From: Klaus Goger Date: Sat, 3 Feb 2018 16:50:15 +0100 Subject: arm64: dts: rockchip: add i2s0-2ch-bus pins on rk3399 Add pin definition for I2S0 if used as a 2-channel only bus. Signed-off-by: Klaus Goger Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index 85b30c984acf..f689b1363f9c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi @@ -2043,6 +2043,16 @@ }; i2s0 { + i2s0_2ch_bus: i2s0-2ch-bus { + rockchip,pins = + <3 24 RK_FUNC_1 &pcfg_pull_none>, + <3 25 RK_FUNC_1 &pcfg_pull_none>, + <3 26 RK_FUNC_1 &pcfg_pull_none>, + <3 27 RK_FUNC_1 &pcfg_pull_none>, + <3 31 RK_FUNC_1 &pcfg_pull_none>, + <4 0 RK_FUNC_1 &pcfg_pull_none>; + }; + i2s0_8ch_bus: i2s0-8ch-bus { rockchip,pins = <3 24 RK_FUNC_1 &pcfg_pull_none>, -- cgit v1.3-14-g43fede From 840e1a73ccbb2096d1286d72c18ccadd3956b0ae Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:55:21 +0100 Subject: clk: meson: check pll rate param table before using it Make sure the rate param table is available before using it. Some read-only plls don't provide it, which is ok since the table is not used by read-only clocks. R/W clocks are supposed to provide it, but it does not hurt check it. Signed-off-by: Jerome Brunet --- drivers/clk/meson/clk-pll.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 01341553f50b..2614341fc4ad 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -94,6 +94,13 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, const struct pll_rate_table *rate_table = pll->rate_table; int i; + /* + * if the table is missing, just return the current rate + * since we don't have the other available frequencies + */ + if (!rate_table) + return meson_clk_pll_recalc_rate(hw, *parent_rate); + for (i = 0; i < pll->rate_count; i++) { if (rate <= rate_table[i].rate) return rate_table[i].rate; @@ -109,6 +116,9 @@ static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_ const struct pll_rate_table *rate_table = pll->rate_table; int i; + if (!rate_table) + return NULL; + for (i = 0; i < pll->rate_count; i++) { if (rate == rate_table[i].rate) return &rate_table[i]; -- cgit v1.3-14-g43fede From 4ed98e9572ad24a5286d80d71221372b55fa9df5 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:55:22 +0100 Subject: clk: meson: remove useless pll rate params tables Read-only plls don't need param table to recalculate the rate. Providing them with a param table is just a waste of memory. Remove the useless tables from sys_pll on gxbb and axg. Signed-off-by: Jerome Brunet --- drivers/clk/meson/axg.c | 94 ------------------------------------------------ drivers/clk/meson/gxbb.c | 94 ------------------------------------------------ 2 files changed, 188 deletions(-) diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index 1294f3ad7cd5..8e37bbf305e9 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -21,98 +21,6 @@ static DEFINE_SPINLOCK(meson_clk_lock); -static const struct pll_rate_table sys_pll_rate_table[] = { - PLL_RATE(24000000, 56, 1, 2), - PLL_RATE(48000000, 64, 1, 2), - PLL_RATE(72000000, 72, 1, 2), - PLL_RATE(96000000, 64, 1, 2), - PLL_RATE(120000000, 80, 1, 2), - PLL_RATE(144000000, 96, 1, 2), - PLL_RATE(168000000, 56, 1, 1), - PLL_RATE(192000000, 64, 1, 1), - PLL_RATE(216000000, 72, 1, 1), - PLL_RATE(240000000, 80, 1, 1), - PLL_RATE(264000000, 88, 1, 1), - PLL_RATE(288000000, 96, 1, 1), - PLL_RATE(312000000, 52, 1, 2), - PLL_RATE(336000000, 56, 1, 2), - PLL_RATE(360000000, 60, 1, 2), - PLL_RATE(384000000, 64, 1, 2), - PLL_RATE(408000000, 68, 1, 2), - PLL_RATE(432000000, 72, 1, 2), - PLL_RATE(456000000, 76, 1, 2), - PLL_RATE(480000000, 80, 1, 2), - PLL_RATE(504000000, 84, 1, 2), - PLL_RATE(528000000, 88, 1, 2), - PLL_RATE(552000000, 92, 1, 2), - PLL_RATE(576000000, 96, 1, 2), - PLL_RATE(600000000, 50, 1, 1), - PLL_RATE(624000000, 52, 1, 1), - PLL_RATE(648000000, 54, 1, 1), - PLL_RATE(672000000, 56, 1, 1), - PLL_RATE(696000000, 58, 1, 1), - PLL_RATE(720000000, 60, 1, 1), - PLL_RATE(744000000, 62, 1, 1), - PLL_RATE(768000000, 64, 1, 1), - PLL_RATE(792000000, 66, 1, 1), - PLL_RATE(816000000, 68, 1, 1), - PLL_RATE(840000000, 70, 1, 1), - PLL_RATE(864000000, 72, 1, 1), - PLL_RATE(888000000, 74, 1, 1), - PLL_RATE(912000000, 76, 1, 1), - PLL_RATE(936000000, 78, 1, 1), - PLL_RATE(960000000, 80, 1, 1), - PLL_RATE(984000000, 82, 1, 1), - PLL_RATE(1008000000, 84, 1, 1), - PLL_RATE(1032000000, 86, 1, 1), - PLL_RATE(1056000000, 88, 1, 1), - PLL_RATE(1080000000, 90, 1, 1), - PLL_RATE(1104000000, 92, 1, 1), - PLL_RATE(1128000000, 94, 1, 1), - PLL_RATE(1152000000, 96, 1, 1), - PLL_RATE(1176000000, 98, 1, 1), - PLL_RATE(1200000000, 50, 1, 0), - PLL_RATE(1224000000, 51, 1, 0), - PLL_RATE(1248000000, 52, 1, 0), - PLL_RATE(1272000000, 53, 1, 0), - PLL_RATE(1296000000, 54, 1, 0), - PLL_RATE(1320000000, 55, 1, 0), - PLL_RATE(1344000000, 56, 1, 0), - PLL_RATE(1368000000, 57, 1, 0), - PLL_RATE(1392000000, 58, 1, 0), - PLL_RATE(1416000000, 59, 1, 0), - PLL_RATE(1440000000, 60, 1, 0), - PLL_RATE(1464000000, 61, 1, 0), - PLL_RATE(1488000000, 62, 1, 0), - PLL_RATE(1512000000, 63, 1, 0), - PLL_RATE(1536000000, 64, 1, 0), - PLL_RATE(1560000000, 65, 1, 0), - PLL_RATE(1584000000, 66, 1, 0), - PLL_RATE(1608000000, 67, 1, 0), - PLL_RATE(1632000000, 68, 1, 0), - PLL_RATE(1656000000, 68, 1, 0), - PLL_RATE(1680000000, 68, 1, 0), - PLL_RATE(1704000000, 68, 1, 0), - PLL_RATE(1728000000, 69, 1, 0), - PLL_RATE(1752000000, 69, 1, 0), - PLL_RATE(1776000000, 69, 1, 0), - PLL_RATE(1800000000, 69, 1, 0), - PLL_RATE(1824000000, 70, 1, 0), - PLL_RATE(1848000000, 70, 1, 0), - PLL_RATE(1872000000, 70, 1, 0), - PLL_RATE(1896000000, 70, 1, 0), - PLL_RATE(1920000000, 71, 1, 0), - PLL_RATE(1944000000, 71, 1, 0), - PLL_RATE(1968000000, 71, 1, 0), - PLL_RATE(1992000000, 71, 1, 0), - PLL_RATE(2016000000, 72, 1, 0), - PLL_RATE(2040000000, 72, 1, 0), - PLL_RATE(2064000000, 72, 1, 0), - PLL_RATE(2088000000, 72, 1, 0), - PLL_RATE(2112000000, 73, 1, 0), - { /* sentinel */ }, -}; - static struct meson_clk_pll axg_fixed_pll = { .m = { .reg_off = HHI_MPLL_CNTL, @@ -154,8 +62,6 @@ static struct meson_clk_pll axg_sys_pll = { .shift = 10, .width = 2, }, - .rate_table = sys_pll_rate_table, - .rate_count = ARRAY_SIZE(sys_pll_rate_table), .lock = &meson_clk_lock, .hw.init = &(struct clk_init_data){ .name = "sys_pll", diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index af24455af5b4..4b5229f656e3 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -29,98 +29,6 @@ static DEFINE_SPINLOCK(meson_clk_lock); -static const struct pll_rate_table sys_pll_rate_table[] = { - PLL_RATE(24000000, 56, 1, 2), - PLL_RATE(48000000, 64, 1, 2), - PLL_RATE(72000000, 72, 1, 2), - PLL_RATE(96000000, 64, 1, 2), - PLL_RATE(120000000, 80, 1, 2), - PLL_RATE(144000000, 96, 1, 2), - PLL_RATE(168000000, 56, 1, 1), - PLL_RATE(192000000, 64, 1, 1), - PLL_RATE(216000000, 72, 1, 1), - PLL_RATE(240000000, 80, 1, 1), - PLL_RATE(264000000, 88, 1, 1), - PLL_RATE(288000000, 96, 1, 1), - PLL_RATE(312000000, 52, 1, 2), - PLL_RATE(336000000, 56, 1, 2), - PLL_RATE(360000000, 60, 1, 2), - PLL_RATE(384000000, 64, 1, 2), - PLL_RATE(408000000, 68, 1, 2), - PLL_RATE(432000000, 72, 1, 2), - PLL_RATE(456000000, 76, 1, 2), - PLL_RATE(480000000, 80, 1, 2), - PLL_RATE(504000000, 84, 1, 2), - PLL_RATE(528000000, 88, 1, 2), - PLL_RATE(552000000, 92, 1, 2), - PLL_RATE(576000000, 96, 1, 2), - PLL_RATE(600000000, 50, 1, 1), - PLL_RATE(624000000, 52, 1, 1), - PLL_RATE(648000000, 54, 1, 1), - PLL_RATE(672000000, 56, 1, 1), - PLL_RATE(696000000, 58, 1, 1), - PLL_RATE(720000000, 60, 1, 1), - PLL_RATE(744000000, 62, 1, 1), - PLL_RATE(768000000, 64, 1, 1), - PLL_RATE(792000000, 66, 1, 1), - PLL_RATE(816000000, 68, 1, 1), - PLL_RATE(840000000, 70, 1, 1), - PLL_RATE(864000000, 72, 1, 1), - PLL_RATE(888000000, 74, 1, 1), - PLL_RATE(912000000, 76, 1, 1), - PLL_RATE(936000000, 78, 1, 1), - PLL_RATE(960000000, 80, 1, 1), - PLL_RATE(984000000, 82, 1, 1), - PLL_RATE(1008000000, 84, 1, 1), - PLL_RATE(1032000000, 86, 1, 1), - PLL_RATE(1056000000, 88, 1, 1), - PLL_RATE(1080000000, 90, 1, 1), - PLL_RATE(1104000000, 92, 1, 1), - PLL_RATE(1128000000, 94, 1, 1), - PLL_RATE(1152000000, 96, 1, 1), - PLL_RATE(1176000000, 98, 1, 1), - PLL_RATE(1200000000, 50, 1, 0), - PLL_RATE(1224000000, 51, 1, 0), - PLL_RATE(1248000000, 52, 1, 0), - PLL_RATE(1272000000, 53, 1, 0), - PLL_RATE(1296000000, 54, 1, 0), - PLL_RATE(1320000000, 55, 1, 0), - PLL_RATE(1344000000, 56, 1, 0), - PLL_RATE(1368000000, 57, 1, 0), - PLL_RATE(1392000000, 58, 1, 0), - PLL_RATE(1416000000, 59, 1, 0), - PLL_RATE(1440000000, 60, 1, 0), - PLL_RATE(1464000000, 61, 1, 0), - PLL_RATE(1488000000, 62, 1, 0), - PLL_RATE(1512000000, 63, 1, 0), - PLL_RATE(1536000000, 64, 1, 0), - PLL_RATE(1560000000, 65, 1, 0), - PLL_RATE(1584000000, 66, 1, 0), - PLL_RATE(1608000000, 67, 1, 0), - PLL_RATE(1632000000, 68, 1, 0), - PLL_RATE(1656000000, 68, 1, 0), - PLL_RATE(1680000000, 68, 1, 0), - PLL_RATE(1704000000, 68, 1, 0), - PLL_RATE(1728000000, 69, 1, 0), - PLL_RATE(1752000000, 69, 1, 0), - PLL_RATE(1776000000, 69, 1, 0), - PLL_RATE(1800000000, 69, 1, 0), - PLL_RATE(1824000000, 70, 1, 0), - PLL_RATE(1848000000, 70, 1, 0), - PLL_RATE(1872000000, 70, 1, 0), - PLL_RATE(1896000000, 70, 1, 0), - PLL_RATE(1920000000, 71, 1, 0), - PLL_RATE(1944000000, 71, 1, 0), - PLL_RATE(1968000000, 71, 1, 0), - PLL_RATE(1992000000, 71, 1, 0), - PLL_RATE(2016000000, 72, 1, 0), - PLL_RATE(2040000000, 72, 1, 0), - PLL_RATE(2064000000, 72, 1, 0), - PLL_RATE(2088000000, 72, 1, 0), - PLL_RATE(2112000000, 73, 1, 0), - { /* sentinel */ }, -}; - static const struct pll_rate_table gxbb_gp0_pll_rate_table[] = { PLL_RATE(96000000, 32, 1, 3), PLL_RATE(99000000, 33, 1, 3), @@ -356,8 +264,6 @@ static struct meson_clk_pll gxbb_sys_pll = { .shift = 10, .width = 2, }, - .rate_table = sys_pll_rate_table, - .rate_count = ARRAY_SIZE(sys_pll_rate_table), .lock = &meson_clk_lock, .hw.init = &(struct clk_init_data){ .name = "sys_pll", -- cgit v1.3-14-g43fede From 94aa8a41f1bc807db78567e7031d75998c166150 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:55:23 +0100 Subject: clk: meson: remove unnecessary rounding in the pll clock The pll driver performs the rate calculation in Mhz, which adds an unnecessary rounding down to the Mhz of the rate. Use 64bits long integers to perform this calculation safely on meson8b and perform the calculation in Hz instead Fixes: 7a29a869434e ("clk: meson: Add support for Meson clock controller") Signed-off-by: Jerome Brunet --- drivers/clk/meson/clk-pll.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 2614341fc4ad..087dfc532ba8 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -51,8 +52,7 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, { struct meson_clk_pll *pll = to_meson_clk_pll(hw); struct parm *p; - unsigned long parent_rate_mhz = parent_rate / 1000000; - unsigned long rate_mhz; + u64 rate; u16 n, m, frac = 0, od, od2 = 0; u32 reg; @@ -74,17 +74,18 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, od2 = PARM_GET(p->width, p->shift, reg); } + rate = (u64)parent_rate * m; + p = &pll->frac; if (p->width) { reg = readl(pll->base + p->reg_off); frac = PARM_GET(p->width, p->shift, reg); - rate_mhz = (parent_rate_mhz * m + \ - (parent_rate_mhz * frac >> 12)) * 2 / n; - rate_mhz = rate_mhz >> od >> od2; - } else - rate_mhz = (parent_rate_mhz * m / n) >> od >> od2; - return rate_mhz * 1000000; + rate += mul_u64_u32_shr(parent_rate, frac, 12); + rate *= 2; + } + + return div_u64(rate, n) >> od >> od2; } static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, -- cgit v1.3-14-g43fede From 4c5f67b7ea329ed8b3cf708fde4656b2d3b27dbf Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:55:24 +0100 Subject: clk: meson: use the frac parameter width instead of a constant Use the fractional part width in the calculation instead of 12, which happens to be the witdh right now. This is safer in case the field width ever change in the future Signed-off-by: Jerome Brunet --- drivers/clk/meson/clk-pll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 087dfc532ba8..50923d004d96 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -81,7 +81,7 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, reg = readl(pll->base + p->reg_off); frac = PARM_GET(p->width, p->shift, reg); - rate += mul_u64_u32_shr(parent_rate, frac, 12); + rate += mul_u64_u32_shr(parent_rate, frac, p->width); rate *= 2; } -- cgit v1.3-14-g43fede From 7d3142e5d64a2bdcd382dac979407f6afc83d685 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:55:25 +0100 Subject: clk: meson: add od3 to the pll driver Some meson plls, such as the hdmi pll, are using a 3rd od parameter, which is yet another "power of 2" post divider. Add it to fix the calculation of the hdmi_pll rate Fixes: 738f66d3211d ("clk: gxbb: add AmLogic GXBB clk controller driver") Signed-off-by: Jerome Brunet --- drivers/clk/meson/clk-pll.c | 19 ++++++++++++++++--- drivers/clk/meson/clkc.h | 2 ++ drivers/clk/meson/gxbb.c | 5 +++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 50923d004d96..1595f02f610f 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -53,7 +53,7 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, struct meson_clk_pll *pll = to_meson_clk_pll(hw); struct parm *p; u64 rate; - u16 n, m, frac = 0, od, od2 = 0; + u16 n, m, frac = 0, od, od2 = 0, od3 = 0; u32 reg; p = &pll->n; @@ -74,7 +74,13 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, od2 = PARM_GET(p->width, p->shift, reg); } - rate = (u64)parent_rate * m; + p = &pll->od3; + if (p->width) { + reg = readl(pll->base + p->reg_off); + od3 = PARM_GET(p->width, p->shift, reg); + } + + rate = (u64)m * parent_rate; p = &pll->frac; if (p->width) { @@ -85,7 +91,7 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, rate *= 2; } - return div_u64(rate, n) >> od >> od2; + return div_u64(rate, n) >> od >> od2 >> od3; } static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, @@ -226,6 +232,13 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, writel(reg, pll->base + p->reg_off); } + p = &pll->od3; + if (p->width) { + reg = readl(pll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, rate_set->od3); + writel(reg, pll->base + p->reg_off); + } + p = &pll->frac; if (p->width) { reg = readl(pll->base + p->reg_off); diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h index c2ff0520ce53..4acb35bda669 100644 --- a/drivers/clk/meson/clkc.h +++ b/drivers/clk/meson/clkc.h @@ -41,6 +41,7 @@ struct pll_rate_table { u16 n; u16 od; u16 od2; + u16 od3; u16 frac; }; @@ -92,6 +93,7 @@ struct meson_clk_pll { struct parm frac; struct parm od; struct parm od2; + struct parm od3; const struct pll_setup_params params; const struct pll_rate_table *rate_table; unsigned int rate_count; diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 4b5229f656e3..e83573b457fc 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -238,6 +238,11 @@ static struct meson_clk_pll gxbb_hdmi_pll = { .shift = 22, .width = 2, }, + .od3 = { + .reg_off = HHI_HDMI_PLL_CNTL2, + .shift = 18, + .width = 2, + }, .lock = &meson_clk_lock, .hw.init = &(struct clk_init_data){ .name = "hdmi_pll", -- cgit v1.3-14-g43fede From 69d92293274b3ae60c54271bf6e029e3d1d582e8 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:55:26 +0100 Subject: clk: meson: add the gxl hdmi pll The hdmi pll used in the gxl family is actually different from the gxbb one. The register layout is completely different, which explain why the hdmi pll rate has always been rubbish on the gxl family. Adding the correct register field is the first part of the fix to get a correct rate out the hdmi pll Fixes: 0d48fc558d01 ("clk: meson-gxbb: Add GXL/GXM GP0 Variant") Signed-off-by: Jerome Brunet --- drivers/clk/meson/gxbb.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index e83573b457fc..de1cea7a47fb 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -253,6 +253,52 @@ static struct meson_clk_pll gxbb_hdmi_pll = { }, }; +static struct meson_clk_pll gxl_hdmi_pll = { + .m = { + .reg_off = HHI_HDMI_PLL_CNTL, + .shift = 0, + .width = 9, + }, + .n = { + .reg_off = HHI_HDMI_PLL_CNTL, + .shift = 9, + .width = 5, + }, + .frac = { + /* + * On gxl, there is a register shift due to HHI_HDMI_PLL_CNTL1 + * which does not exist on gxbb, so we compute the register + * offset based on the PLL base to get it right + */ + .reg_off = HHI_HDMI_PLL_CNTL + 4, + .shift = 0, + .width = 12, + }, + .od = { + .reg_off = HHI_HDMI_PLL_CNTL + 8, + .shift = 21, + .width = 2, + }, + .od2 = { + .reg_off = HHI_HDMI_PLL_CNTL + 8, + .shift = 23, + .width = 2, + }, + .od3 = { + .reg_off = HHI_HDMI_PLL_CNTL + 8, + .shift = 19, + .width = 2, + }, + .lock = &meson_clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll", + .ops = &meson_clk_pll_ro_ops, + .parent_names = (const char *[]){ "xtal" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + static struct meson_clk_pll gxbb_sys_pll = { .m = { .reg_off = HHI_SYS_PLL_CNTL, @@ -1520,7 +1566,7 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = { static struct clk_hw_onecell_data gxl_hw_onecell_data = { .hws = { [CLKID_SYS_PLL] = &gxbb_sys_pll.hw, - [CLKID_HDMI_PLL] = &gxbb_hdmi_pll.hw, + [CLKID_HDMI_PLL] = &gxl_hdmi_pll.hw, [CLKID_FIXED_PLL] = &gxbb_fixed_pll.hw, [CLKID_FCLK_DIV2] = &gxbb_fclk_div2.hw, [CLKID_FCLK_DIV3] = &gxbb_fclk_div3.hw, @@ -1675,7 +1721,7 @@ static struct meson_clk_pll *const gxbb_clk_plls[] = { static struct meson_clk_pll *const gxl_clk_plls[] = { &gxbb_fixed_pll, - &gxbb_hdmi_pll, + &gxl_hdmi_pll, &gxbb_sys_pll, &gxl_gp0_pll, }; -- cgit v1.3-14-g43fede From 3c4fe763d64db93c0c8ec359cf394cfc491f91f3 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:55:27 +0100 Subject: clk: meson: fix rate calculation of plls with a fractional part The rate of the parent should not be multiplied by 2 when the pll has a fractional part. This is making the rate calculation of the gxl_hdmi_pll wrong (and others as well). This multiplication is specific to the hdmi_pll of gxbb and is most likely due to a multiplier sitting in front of this particular pll. Add a fixed factor clock in front on the gxbb pll and remove this constant from the calculation to fix the problem Fixes: 4a47295144dd ("clk: meson: fractional pll support") Signed-off-by: Jerome Brunet --- drivers/clk/meson/clk-pll.c | 1 - drivers/clk/meson/gxbb.c | 14 +++++++++++++- drivers/clk/meson/gxbb.h | 3 ++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 1595f02f610f..218c769c6d50 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -88,7 +88,6 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, frac = PARM_GET(p->width, p->shift, reg); rate += mul_u64_u32_shr(parent_rate, frac, p->width); - rate *= 2; } return div_u64(rate, n) >> od >> od2 >> od3; diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index de1cea7a47fb..119babfa8d14 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -212,6 +212,17 @@ static struct meson_clk_pll gxbb_fixed_pll = { }, }; +static struct clk_fixed_factor gxbb_hdmi_pll_pre_mult = { + .mult = 2, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll_pre_mult", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "xtal" }, + .num_parents = 1, + }, +}; + static struct meson_clk_pll gxbb_hdmi_pll = { .m = { .reg_off = HHI_HDMI_PLL_CNTL, @@ -247,7 +258,7 @@ static struct meson_clk_pll gxbb_hdmi_pll = { .hw.init = &(struct clk_init_data){ .name = "hdmi_pll", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ "hdmi_pll_pre_mult" }, .num_parents = 1, .flags = CLK_GET_RATE_NOCACHE, }, @@ -1558,6 +1569,7 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = { [CLKID_VAPB_1] = &gxbb_vapb_1.hw, [CLKID_VAPB_SEL] = &gxbb_vapb_sel.hw, [CLKID_VAPB] = &gxbb_vapb.hw, + [CLKID_HDMI_PLL_PRE_MULT] = &gxbb_hdmi_pll_pre_mult.hw, [NR_CLKS] = NULL, }, .num = NR_CLKS, diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h index aee6fbba2004..42573b28a137 100644 --- a/drivers/clk/meson/gxbb.h +++ b/drivers/clk/meson/gxbb.h @@ -194,8 +194,9 @@ #define CLKID_VPU_1_DIV 130 #define CLKID_VAPB_0_DIV 134 #define CLKID_VAPB_1_DIV 137 +#define CLKID_HDMI_PLL_PRE_MULT 141 -#define NR_CLKS 141 +#define NR_CLKS 142 /* include the CLKIDs that have been made part of the DT binding */ #include -- cgit v1.3-14-g43fede From 07f45e2ecc1ba1ce75d80768caf2267256cd135d Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:55:28 +0100 Subject: clk: meson: gxbb: add the fractional part of the fixed_pll The fixed_pll of gxbb and gxl also has a fractional parameter. This has not been a problem so far because the fractional part is actually set to 0 on these platforms, so the rate remains correct when it is ignored. Still, it is better represent the pll the way it is, so add the frac parameter now Signed-off-by: Jerome Brunet --- drivers/clk/meson/gxbb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 119babfa8d14..a5f25cc1944c 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -202,6 +202,11 @@ static struct meson_clk_pll gxbb_fixed_pll = { .shift = 16, .width = 2, }, + .frac = { + .reg_off = HHI_MPLL_CNTL2, + .shift = 0, + .width = 12, + }, .lock = &meson_clk_lock, .hw.init = &(struct clk_init_data){ .name = "fixed_pll", -- cgit v1.3-14-g43fede From 6b71aceceb09918daf37a40a1221077599040be3 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:55:29 +0100 Subject: clk: meson: axg: add the fractional part of the fixed_pll The fixed_pll also has a fractional part. On axg s400 board, without this parameter, the calculated rate is off by ~8Mhz (0,4%). The fixed_pll being the root of the peripheral clock tree, this error is propagated to the rest of the clocks Adding the definition of the parameter fixes the problem Fixes: 78b4af312f91 ("clk: meson-axg: add clock controller drivers") Signed-off-by: Jerome Brunet --- drivers/clk/meson/axg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index 8e37bbf305e9..a1ac0ff67e5f 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -37,6 +37,11 @@ static struct meson_clk_pll axg_fixed_pll = { .shift = 16, .width = 2, }, + .frac = { + .reg_off = HHI_MPLL_CNTL2, + .shift = 0, + .width = 12, + }, .lock = &meson_clk_lock, .hw.init = &(struct clk_init_data){ .name = "fixed_pll", -- cgit v1.3-14-g43fede From 2fa9b361e500a0e092a9525afbd6a3a363ffa5f0 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Fri, 19 Jan 2018 10:09:26 +0800 Subject: clk: meson: axg: fix the od shift of the sys_pll According to the datasheet, the od shift of sys_pll is actually 16. Fixes: 78b4af312f91 ('clk: meson-axg: add clock controller drivers') Signed-off-by: Yixun Lan [fixed commit message] Signed-off-by: Jerome Brunet --- drivers/clk/meson/axg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index a1ac0ff67e5f..455d4d8962bb 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -64,7 +64,7 @@ static struct meson_clk_pll axg_sys_pll = { }, .od = { .reg_off = HHI_SYS_PLL_CNTL, - .shift = 10, + .shift = 16, .width = 2, }, .lock = &meson_clk_lock, -- cgit v1.3-14-g43fede From 6c00e7b76021fcf4ddb64191ccdf62c722adf0d1 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 19 Jan 2018 16:42:36 +0100 Subject: clk: meson: add axg misc bit to the mpll driver On axg, the rate of the mpll is stuck as if sdm value was 4 and could not change (expect for mpll2 strangely). Looking at the vendor kernel, it turns out a new magic bit from the undocumented HHI_PLL_TOP_MISC register is required. Setting this bit solves the problem and the mpll rates are back to normal Fixes: 78b4af312f91 ("clk: meson-axg: add clock controller drivers") Signed-off-by: Jerome Brunet --- drivers/clk/meson/axg.c | 20 ++++++++++++++++++++ drivers/clk/meson/clk-mpll.c | 7 +++++++ drivers/clk/meson/clkc.h | 1 + 3 files changed, 28 insertions(+) diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index 455d4d8962bb..2dc70e0e925c 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -292,6 +292,11 @@ static struct meson_clk_mpll axg_mpll0 = { .shift = 25, .width = 1, }, + .misc = { + .reg_off = HHI_PLL_TOP_MISC, + .shift = 0, + .width = 1, + }, .lock = &meson_clk_lock, .hw.init = &(struct clk_init_data){ .name = "mpll0", @@ -322,6 +327,11 @@ static struct meson_clk_mpll axg_mpll1 = { .shift = 14, .width = 1, }, + .misc = { + .reg_off = HHI_PLL_TOP_MISC, + .shift = 1, + .width = 1, + }, .lock = &meson_clk_lock, .hw.init = &(struct clk_init_data){ .name = "mpll1", @@ -352,6 +362,11 @@ static struct meson_clk_mpll axg_mpll2 = { .shift = 14, .width = 1, }, + .misc = { + .reg_off = HHI_PLL_TOP_MISC, + .shift = 2, + .width = 1, + }, .lock = &meson_clk_lock, .hw.init = &(struct clk_init_data){ .name = "mpll2", @@ -382,6 +397,11 @@ static struct meson_clk_mpll axg_mpll3 = { .shift = 0, .width = 1, }, + .misc = { + .reg_off = HHI_PLL_TOP_MISC, + .shift = 3, + .width = 1, + }, .lock = &meson_clk_lock, .hw.init = &(struct clk_init_data){ .name = "mpll3", diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c index 5144360e2c80..6d79d6daadc4 100644 --- a/drivers/clk/meson/clk-mpll.c +++ b/drivers/clk/meson/clk-mpll.c @@ -173,6 +173,13 @@ static int mpll_set_rate(struct clk_hw *hw, reg = PARM_SET(p->width, p->shift, reg, n2); writel(reg, mpll->base + p->reg_off); + p = &mpll->misc; + if (p->width != 0) { + reg = readl(mpll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, 1); + writel(reg, mpll->base + p->reg_off); + } + if (mpll->lock) spin_unlock_irqrestore(mpll->lock, flags); else diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h index 4acb35bda669..07aaba26a857 100644 --- a/drivers/clk/meson/clkc.h +++ b/drivers/clk/meson/clkc.h @@ -121,6 +121,7 @@ struct meson_clk_mpll { struct parm n2; struct parm en; struct parm ssen; + struct parm misc; spinlock_t *lock; }; -- cgit v1.3-14-g43fede From e6e620f0e15e3bc4b3bc3078c9f3285830f2fd3c Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 22 Jan 2018 10:38:01 +0100 Subject: mtd: Make sure the device supports erase operations in mtd_erase() Some devices do not implement ->_erase() or have an invalid ->erasesize value. In this case, mtd_erase() should return -ENOTSUPP. Note that the test is not done on the MTD_NO_ERASE flag because this flag means 'erasing a block before writing to it is unnecessary', not 'the erase operation is not supported'. Actually, some drivers are setting the MTD_NO_ERASE flag but still implementing the ->_erase() hook and setting a valid ->erasesize value. Signed-off-by: Boris Brezillon Reviewed-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 28553c840d32..56cb037a1052 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -971,10 +971,14 @@ EXPORT_SYMBOL_GPL(__put_mtd_device); */ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) { + if (!mtd->erasesize || !mtd->_erase) + return -ENOTSUPP; + if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr) return -EINVAL; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; if (!instr->len) { instr->state = MTD_ERASE_DONE; -- cgit v1.3-14-g43fede From 2c77c57d22adb05b21cdb333a0c42bdfa0e19835 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 16 Jan 2018 16:45:41 +0100 Subject: mtd: move code adding master MTD out of mtd_add_device_partitions() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change is a small cleanup of mtd_device_parse_register(). When using MTD_PARTITIONED_MASTER it makes sure a master MTD is registered before dealing with partitions. The advantage of this is not mixing code handling master MTD with code handling partitions. This commit doesn't change any behavior except from a slightly different failure code path. The new code may need to call del_mtd_device when something goes wrong. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 56cb037a1052..c6a53a1e55d9 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -646,20 +646,12 @@ static int mtd_add_device_partitions(struct mtd_info *mtd, { const struct mtd_partition *real_parts = parts->parts; int nbparts = parts->nr_parts; - int ret; - if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) { - ret = add_mtd_device(mtd); - if (ret) - return ret; - } + if (!nbparts && !device_is_registered(&mtd->dev)) + return add_mtd_device(mtd); - if (nbparts > 0) { - ret = add_mtd_partitions(mtd, real_parts, nbparts); - if (ret && IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) - del_mtd_device(mtd); - return ret; - } + if (nbparts > 0) + return add_mtd_partitions(mtd, real_parts, nbparts); return 0; } @@ -719,6 +711,12 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, mtd_set_dev_defaults(mtd); + if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) { + ret = add_mtd_device(mtd); + if (ret) + return ret; + } + memset(&parsed, 0, sizeof(parsed)); ret = parse_mtd_partitions(mtd, types, &parsed, parser_data); @@ -758,6 +756,9 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, out: /* Cleanup any parsed partitions */ mtd_part_parser_cleanup(&parsed); + if (ret && device_is_registered(&mtd->dev)) + del_mtd_device(mtd); + return ret; } EXPORT_SYMBOL_GPL(mtd_device_parse_register); -- cgit v1.3-14-g43fede From 0dbe4ea78d69756efeb0bba0764f6bd4a9ee9567 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 16 Jan 2018 16:45:42 +0100 Subject: mtd: get rid of the mtd_add_device_partitions() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies code a bit by: 1) Avoiding an extra (tiny) function 2) Checking for amount of parsed (found) partitions just once 3) Avoiding clearing/filling struct mtd_partitions manually With this commit proper functions are called directly from the mtd_device_parse_register(). It doesn't need to use minor tricks like memsetting struct to 0 to trigger an expected mtd_add_device_partitions() behavior. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 43 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c6a53a1e55d9..a1c94526fb88 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -641,21 +641,6 @@ out_error: return ret; } -static int mtd_add_device_partitions(struct mtd_info *mtd, - struct mtd_partitions *parts) -{ - const struct mtd_partition *real_parts = parts->parts; - int nbparts = parts->nr_parts; - - if (!nbparts && !device_is_registered(&mtd->dev)) - return add_mtd_device(mtd); - - if (nbparts > 0) - return add_mtd_partitions(mtd, real_parts, nbparts); - - return 0; -} - /* * Set a few defaults based on the parent devices, if not provided by the * driver @@ -706,7 +691,7 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, const struct mtd_partition *parts, int nr_parts) { - struct mtd_partitions parsed; + struct mtd_partitions parsed = { }; int ret; mtd_set_dev_defaults(mtd); @@ -717,24 +702,20 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, return ret; } - memset(&parsed, 0, sizeof(parsed)); - + /* Prefer parsed partitions over driver-provided fallback */ ret = parse_mtd_partitions(mtd, types, &parsed, parser_data); - if ((ret < 0 || parsed.nr_parts == 0) && parts && nr_parts) { - /* Fall back to driver-provided partitions */ - parsed = (struct mtd_partitions){ - .parts = parts, - .nr_parts = nr_parts, - }; - } else if (ret < 0) { - /* Didn't come up with parsed OR fallback partitions */ - pr_info("mtd: failed to find partitions; one or more parsers reports errors (%d)\n", - ret); - /* Don't abort on errors; we can still use unpartitioned MTD */ - memset(&parsed, 0, sizeof(parsed)); + if (!ret && parsed.nr_parts) { + parts = parsed.parts; + nr_parts = parsed.nr_parts; } - ret = mtd_add_device_partitions(mtd, &parsed); + if (nr_parts) + ret = add_mtd_partitions(mtd, parts, nr_parts); + else if (!device_is_registered(&mtd->dev)) + ret = add_mtd_device(mtd); + else + ret = 0; + if (ret) goto out; -- cgit v1.3-14-g43fede From 707aa45d2612778e2f3b6c5e1950ed9fa48974aa Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 19 Dec 2017 16:54:44 +0100 Subject: soc: renesas: rcar-sysc: Mark rcar_sysc_matches[] __initconst rcar_sysc_matches[] is used only by rcar_sysc_pd_init(), which is __init. Hence mark rcar_sysc_matches[] __initconst. This frees another 1764 bytes (arm32/shmobile_defconfig) or 1000 bytes (arm64/renesas_defconfig) of memory after kernel init. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/rcar-sysc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 52c25a5e2646..636872bc2416 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -254,7 +254,7 @@ finalize: pm_genpd_init(genpd, gov, false); } -static const struct of_device_id rcar_sysc_matches[] = { +static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A7743 { .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info }, #endif -- cgit v1.3-14-g43fede From 8447756d1e582a401cdae33c9ed5d68fdb6e0410 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 2 Feb 2018 21:22:14 +0300 Subject: soc: renesas: identify R-Car V3H Add support for identifying the R-Car V3H (R8A77980) SoC. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/renesas-soc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 926b7fd6db2d..000834321774 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -149,6 +149,11 @@ static const struct renesas_soc soc_rcar_v3m __initconst __maybe_unused = { .id = 0x54, }; +static const struct renesas_soc soc_rcar_v3h __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x56, +}; + static const struct renesas_soc soc_rcar_d3 __initconst __maybe_unused = { .family = &fam_rcar_gen3, .id = 0x58, @@ -212,6 +217,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A77970 { .compatible = "renesas,r8a77970", .data = &soc_rcar_v3m }, #endif +#ifdef CONFIG_ARCH_R8A77980 + { .compatible = "renesas,r8a77980", .data = &soc_rcar_v3h }, +#endif #ifdef CONFIG_ARCH_R8A77995 { .compatible = "renesas,r8a77995", .data = &soc_rcar_d3 }, #endif -- cgit v1.3-14-g43fede From 7d7b619e16d420b723d6618c60a0aaf0ba4e3666 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 2 Feb 2018 21:27:01 +0300 Subject: soc: renesas: rcar-rst: add R8A77980 support Add support for R-Car V3H (R8A77980) to the R-Car RST driver -- this driver is needed for the clock driver to work. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- Documentation/devicetree/bindings/reset/renesas,rst.txt | 1 + drivers/soc/renesas/Kconfig | 2 +- drivers/soc/renesas/rcar-rst.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/reset/renesas,rst.txt b/Documentation/devicetree/bindings/reset/renesas,rst.txt index a8014f3ab8ba..a55b88658446 100644 --- a/Documentation/devicetree/bindings/reset/renesas,rst.txt +++ b/Documentation/devicetree/bindings/reset/renesas,rst.txt @@ -27,6 +27,7 @@ Required properties: - "renesas,r8a7795-rst" (R-Car H3) - "renesas,r8a7796-rst" (R-Car M3-W) - "renesas,r8a77970-rst" (R-Car V3M) + - "renesas,r8a77980-rst" (R-Car V3H) - "renesas,r8a77995-rst" (R-Car D3) - reg: Address start and address range for the device. diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 09550b1da56d..6efd7bef8577 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -4,7 +4,7 @@ config SOC_RENESAS select SOC_BUS select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \ ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77970 || \ - ARCH_R8A77995 + ARCH_R8A77980 || ARCH_R8A77995 select SYSC_R8A7743 if ARCH_R8A7743 select SYSC_R8A7745 if ARCH_R8A7745 select SYSC_R8A7779 if ARCH_R8A7779 diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index 3316b028f231..e2340eb9ea9c 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -42,6 +42,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen2 }, { /* sentinel */ } }; -- cgit v1.3-14-g43fede From 61ad2b1ab4b1ce4e495854c4faa75ca95e5e8ecd Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 20 Dec 2017 13:14:43 +0100 Subject: ARM: dts: r8a7790: Reduce size of thermal registers Reduce size of thermal registers in DT for r8a7790 (R-Car H2) SoC. According to the "User's Manual: Hardware" v2.00 the registers at base 0xe61f0000 extend to an offset of 0x10, rather than 0x14 which is the case on the r8a73a4 (R-Mobile APE6). This should not have any runtime affect as mapping granularity is PAGE_SIZE. Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7790.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index ed9a68538a55..13926fc7abfa 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -294,7 +294,7 @@ compatible = "renesas,thermal-r8a7790", "renesas,rcar-gen2-thermal", "renesas,rcar-thermal"; - reg = <0 0xe61f0000 0 0x14>, <0 0xe61f0100 0 0x38>; + reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; interrupts = ; clocks = <&cpg CPG_MOD 522>; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; -- cgit v1.3-14-g43fede From 3f599906744ecc2ece7ed0418a5822348000c0e2 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 20 Dec 2017 13:14:44 +0100 Subject: ARM: dts: r8a7791: Reduce size of thermal registers Reduce size of thermal registers in DT for r8a7791 (R-Car M3-W) SoC. According to the "User's Manual: Hardware" v2.00 the registers at base 0xe61f0000 extend to an offset of 0x10, rather than 0x14 which is the case on the r8a73a4 (R-Mobile APE6). This should not have any runtime affect as mapping granularity is PAGE_SIZE. Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7791.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index 008a260f86a5..8266a9b7cafd 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -240,7 +240,7 @@ compatible = "renesas,thermal-r8a7791", "renesas,rcar-gen2-thermal", "renesas,rcar-thermal"; - reg = <0 0xe61f0000 0 0x14>, <0 0xe61f0100 0 0x38>; + reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; interrupts = ; clocks = <&cpg CPG_MOD 522>; power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; -- cgit v1.3-14-g43fede From 1cfe2186c708c8f954097086e3da0cffd1e75f4e Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 20 Dec 2017 13:14:45 +0100 Subject: ARM: dts: r8a7793: Reduce size of thermal registers Reduce size of thermal registers in DT for r8a7793 (R-Car M3-N) SoC. According to the "User's Manual: Hardware" v2.00 the registers at base 0xe61f0000 extend to an offset of 0x10, rather than 0x14 which is the case on the r8a73a4 (R-Mobile APE6). This should not have any runtime affect as mapping granularity is PAGE_SIZE. Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7793.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/r8a7793.dtsi b/arch/arm/boot/dts/r8a7793.dtsi index 039b22517526..e95f4fb44dc4 100644 --- a/arch/arm/boot/dts/r8a7793.dtsi +++ b/arch/arm/boot/dts/r8a7793.dtsi @@ -231,7 +231,7 @@ compatible = "renesas,thermal-r8a7793", "renesas,rcar-gen2-thermal", "renesas,rcar-thermal"; - reg = <0 0xe61f0000 0 0x14>, <0 0xe61f0100 0 0x38>; + reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; interrupts = ; clocks = <&cpg CPG_MOD 522>; power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; -- cgit v1.3-14-g43fede From c5aa87977626e778e1ee3bd03b14a300d9338a3a Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Tue, 9 Jan 2018 17:49:04 +0100 Subject: ARM: dts: lager: Add CEC clock for HDMI transmitter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adv7511 on the Lager board has a 12 MHz fixed clock for the CEC block. Specify this in the dts to enable CEC support. Based on a similar patch for Koelsch by Hans Verkuil. Signed-off-by: Niklas Söderlund Tested-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7790-lager.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/r8a7790-lager.dts b/arch/arm/boot/dts/r8a7790-lager.dts index f2ea632381e7..7892b113ecaa 100644 --- a/arch/arm/boot/dts/r8a7790-lager.dts +++ b/arch/arm/boot/dts/r8a7790-lager.dts @@ -669,11 +669,19 @@ }; }; + cec_clock: cec-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <12000000>; + }; + hdmi@39 { compatible = "adi,adv7511w"; reg = <0x39>; interrupt-parent = <&gpio1>; interrupts = <15 IRQ_TYPE_LEVEL_LOW>; + clocks = <&cec_clock>; + clock-names = "cec"; adi,input-depth = <8>; adi,input-colorspace = "rgb"; -- cgit v1.3-14-g43fede From cbeb319b9f220982acc2f533ffee5042a6763c39 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 11 Jan 2018 20:59:38 +0000 Subject: ARM: dts: iwg20m: Enable cmt0 This patch enables cmt0 support from within the iwg20m SoM dtsi. Signed-off-by: Fabrizio Castro Reviewed-by: Biju Das Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7743-iwg20m.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/r8a7743-iwg20m.dtsi b/arch/arm/boot/dts/r8a7743-iwg20m.dtsi index 75a8ca571846..1d3e9503c5bd 100644 --- a/arch/arm/boot/dts/r8a7743-iwg20m.dtsi +++ b/arch/arm/boot/dts/r8a7743-iwg20m.dtsi @@ -34,6 +34,10 @@ }; }; +&cmt0 { + status = "okay"; +}; + &extal_clk { clock-frequency = <20000000>; }; -- cgit v1.3-14-g43fede From 7d1671828696650db6123ab1d7a592a0bbf42ff2 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 11 Jan 2018 20:59:39 +0000 Subject: ARM: dts: iwg22m: Enable cmt0 This patch enables cmt0 support from within the iwg22m SoM dtsi. Signed-off-by: Fabrizio Castro Reviewed-by: Biju Das Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7745-iwg22m.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/r8a7745-iwg22m.dtsi b/arch/arm/boot/dts/r8a7745-iwg22m.dtsi index ed9a8cf3fe36..8d0a392b6811 100644 --- a/arch/arm/boot/dts/r8a7745-iwg22m.dtsi +++ b/arch/arm/boot/dts/r8a7745-iwg22m.dtsi @@ -29,6 +29,10 @@ }; }; +&cmt0 { + status = "okay"; +}; + &extal_clk { clock-frequency = <20000000>; }; -- cgit v1.3-14-g43fede From d4b78db6ac3e084e2bdc57d5518bd247c727f396 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 13 Jan 2018 01:14:23 +0200 Subject: ARM: dts: porter: Fix HDMI output routing The HDMI encoder is connected to the RGB output of the DU, which is port@0, not port@1. Fix the incorrect DT description. Fixes: c5af8a4248d3 ("ARM: dts: porter: add DU DT support") Signed-off-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7791-porter.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/r8a7791-porter.dts b/arch/arm/boot/dts/r8a7791-porter.dts index eb374956294f..9a02d03b23c2 100644 --- a/arch/arm/boot/dts/r8a7791-porter.dts +++ b/arch/arm/boot/dts/r8a7791-porter.dts @@ -425,7 +425,7 @@ "dclkin.0", "dclkin.1"; ports { - port@1 { + port@0 { endpoint { remote-endpoint = <&adv7511_in>; }; -- cgit v1.3-14-g43fede From 41b3568dc4f11e555aecf37fff5a08f398f97a03 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:02 +0100 Subject: ARM: dts: r8a7790: consistently use single space after = MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consistently use a single space after a =. This patch removes instances where a tab or multiple spaces are used instead. It also avoids running over 80 columns in width in one of the lines where whitespace is updated. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7790.dtsi | 75 +++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index 13926fc7abfa..b4306f7c1bbb 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -291,9 +291,9 @@ }; thermal: thermal@e61f0000 { - compatible = "renesas,thermal-r8a7790", - "renesas,rcar-gen2-thermal", - "renesas,rcar-thermal"; + compatible = "renesas,thermal-r8a7790", + "renesas,rcar-gen2-thermal", + "renesas,rcar-thermal"; reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; interrupts = ; clocks = <&cpg CPG_MOD 522>; @@ -423,20 +423,20 @@ audma0: dma-controller@ec700000 { compatible = "renesas,dmac-r8a7790", "renesas,rcar-dmac"; reg = <0 0xec700000 0 0x10000>; - interrupts = ; + interrupts = ; interrupt-names = "error", "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7", @@ -453,20 +453,20 @@ audma1: dma-controller@ec720000 { compatible = "renesas,dmac-r8a7790", "renesas,rcar-dmac"; reg = <0 0xec720000 0 0x10000>; - interrupts = ; + interrupts = ; interrupt-names = "error", "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7", @@ -1437,12 +1437,13 @@ * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; */ - compatible = "renesas,rcar_sound-r8a7790", "renesas,rcar_sound-gen2"; - reg = <0 0xec500000 0 0x1000>, /* SCU */ - <0 0xec5a0000 0 0x100>, /* ADG */ - <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x280>, /* SSI */ - <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ + compatible = "renesas,rcar_sound-r8a7790", + "renesas,rcar_sound-gen2"; + reg = <0 0xec500000 0 0x1000>, /* SCU */ + <0 0xec5a0000 0 0x100>, /* ADG */ + <0 0xec540000 0 0x1000>, /* SSIU */ + <0 0xec541000 0 0x280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; clocks = <&cpg CPG_MOD 1005>, -- cgit v1.3-14-g43fede From 4bdb7aa7dcd0cbf49298dc73adf27639bf792161 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:03 +0100 Subject: ARM: dts: r8a7790: add soc node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add soc node to represent the bus and move all nodes with a base address into this node. This is consistent with handling of R-Car Gen3, RZ/G1, and R-Car V2H (R8A77920) SoCs upstream. It is intended to migrate other R-Car Gen2 SoCs to this scheme. The ordering is derived from simply moving each node with an address up to before any nodes without a base address that occur before the soc node. To improve maintainability follow-up patches will sort subnodes of both the new soc node and the root node. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7790.dtsi | 2794 ++++++++++++++++++++-------------------- 1 file changed, 1432 insertions(+), 1362 deletions(-) diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index b4306f7c1bbb..1f4b3a4ed287 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -17,7 +17,6 @@ / { compatible = "renesas,r8a7790"; - interrupt-parent = <&gic>; #address-cells = <2>; #size-cells = <2>; @@ -159,981 +158,1524 @@ }; }; - thermal-zones { - cpu_thermal: cpu-thermal { - polling-delay-passive = <0>; - polling-delay = <0>; + soc { + compatible = "simple-bus"; + interrupt-parent = <&gic>; - thermal-sensors = <&thermal>; + #address-cells = <2>; + #size-cells = <2>; + ranges; - trips { - cpu-crit { - temperature = <95000>; - hysteresis = <0>; - type = "critical"; - }; - }; - cooling-maps { - }; + apmu@e6151000 { + compatible = "renesas,r8a7790-apmu", "renesas,apmu"; + reg = <0 0xe6151000 0 0x188>; + cpus = <&cpu4 &cpu5 &cpu6 &cpu7>; }; - }; - apmu@e6151000 { - compatible = "renesas,r8a7790-apmu", "renesas,apmu"; - reg = <0 0xe6151000 0 0x188>; - cpus = <&cpu4 &cpu5 &cpu6 &cpu7>; - }; + apmu@e6152000 { + compatible = "renesas,r8a7790-apmu", "renesas,apmu"; + reg = <0 0xe6152000 0 0x188>; + cpus = <&cpu0 &cpu1 &cpu2 &cpu3>; + }; - apmu@e6152000 { - compatible = "renesas,r8a7790-apmu", "renesas,apmu"; - reg = <0 0xe6152000 0 0x188>; - cpus = <&cpu0 &cpu1 &cpu2 &cpu3>; - }; + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 408>; + }; - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <0>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, - <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, - <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; + gpio0: gpio@e6050000 { + compatible = "renesas,gpio-r8a7790", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6050000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 0 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 912>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 912>; + }; - gpio0: gpio@e6050000 { - compatible = "renesas,gpio-r8a7790", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6050000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 0 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 912>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 912>; - }; + gpio1: gpio@e6051000 { + compatible = "renesas,gpio-r8a7790", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6051000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 32 30>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 911>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 911>; + }; - gpio1: gpio@e6051000 { - compatible = "renesas,gpio-r8a7790", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6051000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 32 30>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 911>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 911>; - }; + gpio2: gpio@e6052000 { + compatible = "renesas,gpio-r8a7790", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6052000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 64 30>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 910>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 910>; + }; - gpio2: gpio@e6052000 { - compatible = "renesas,gpio-r8a7790", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6052000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 64 30>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 910>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 910>; - }; + gpio3: gpio@e6053000 { + compatible = "renesas,gpio-r8a7790", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6053000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 96 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 909>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 909>; + }; - gpio3: gpio@e6053000 { - compatible = "renesas,gpio-r8a7790", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6053000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 96 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 909>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 909>; - }; + gpio4: gpio@e6054000 { + compatible = "renesas,gpio-r8a7790", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6054000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 128 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 908>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 908>; + }; - gpio4: gpio@e6054000 { - compatible = "renesas,gpio-r8a7790", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6054000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 128 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 908>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 908>; - }; + gpio5: gpio@e6055000 { + compatible = "renesas,gpio-r8a7790", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6055000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 160 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 907>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 907>; + }; - gpio5: gpio@e6055000 { - compatible = "renesas,gpio-r8a7790", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6055000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 160 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 907>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 907>; - }; + thermal: thermal@e61f0000 { + compatible = "renesas,thermal-r8a7790", + "renesas,rcar-gen2-thermal", + "renesas,rcar-thermal"; + reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; + interrupts = ; + clocks = <&cpg CPG_MOD 522>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 522>; + #thermal-sensor-cells = <0>; + }; - thermal: thermal@e61f0000 { - compatible = "renesas,thermal-r8a7790", - "renesas,rcar-gen2-thermal", - "renesas,rcar-thermal"; - reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; - interrupts = ; - clocks = <&cpg CPG_MOD 522>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 522>; - #thermal-sensor-cells = <0>; - }; + cmt0: timer@ffca0000 { + compatible = "renesas,r8a7790-cmt0", + "renesas,rcar-gen2-cmt0"; + reg = <0 0xffca0000 0 0x1004>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 124>; + clock-names = "fck"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 124>; + + status = "disabled"; + }; - timer { - compatible = "arm,armv7-timer"; - interrupts = , - , - , - ; - }; + cmt1: timer@e6130000 { + compatible = "renesas,r8a7790-cmt1", + "renesas,rcar-gen2-cmt1"; + reg = <0 0xe6130000 0 0x1004>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 329>; + clock-names = "fck"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 329>; + + status = "disabled"; + }; - cmt0: timer@ffca0000 { - compatible = "renesas,r8a7790-cmt0", "renesas,rcar-gen2-cmt0"; - reg = <0 0xffca0000 0 0x1004>; - interrupts = , - ; - clocks = <&cpg CPG_MOD 124>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 124>; - - status = "disabled"; - }; + irqc0: interrupt-controller@e61c0000 { + compatible = "renesas,irqc-r8a7790", "renesas,irqc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0 0xe61c0000 0 0x200>; + interrupts = , + , + , + ; + clocks = <&cpg CPG_MOD 407>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 407>; + }; - cmt1: timer@e6130000 { - compatible = "renesas,r8a7790-cmt1", "renesas,rcar-gen2-cmt1"; - reg = <0 0xe6130000 0 0x1004>; - interrupts = , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 329>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 329>; - - status = "disabled"; - }; + dmac0: dma-controller@e6700000 { + compatible = "renesas,dmac-r8a7790", + "renesas,rcar-dmac"; + reg = <0 0xe6700000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 219>; + clock-names = "fck"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 219>; + #dma-cells = <1>; + dma-channels = <15>; + }; - irqc0: interrupt-controller@e61c0000 { - compatible = "renesas,irqc-r8a7790", "renesas,irqc"; - #interrupt-cells = <2>; - interrupt-controller; - reg = <0 0xe61c0000 0 0x200>; - interrupts = , - , - , - ; - clocks = <&cpg CPG_MOD 407>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 407>; - }; + dmac1: dma-controller@e6720000 { + compatible = "renesas,dmac-r8a7790", + "renesas,rcar-dmac"; + reg = <0 0xe6720000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 218>; + clock-names = "fck"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 218>; + #dma-cells = <1>; + dma-channels = <15>; + }; - dmac0: dma-controller@e6700000 { - compatible = "renesas,dmac-r8a7790", "renesas,rcar-dmac"; - reg = <0 0xe6700000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 219>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 219>; - #dma-cells = <1>; - dma-channels = <15>; - }; + audma0: dma-controller@ec700000 { + compatible = "renesas,dmac-r8a7790", + "renesas,rcar-dmac"; + reg = <0 0xec700000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 502>; + clock-names = "fck"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 502>; + #dma-cells = <1>; + dma-channels = <13>; + }; - dmac1: dma-controller@e6720000 { - compatible = "renesas,dmac-r8a7790", "renesas,rcar-dmac"; - reg = <0 0xe6720000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 218>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 218>; - #dma-cells = <1>; - dma-channels = <15>; - }; + audma1: dma-controller@ec720000 { + compatible = "renesas,dmac-r8a7790", + "renesas,rcar-dmac"; + reg = <0 0xec720000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 501>; + clock-names = "fck"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 501>; + #dma-cells = <1>; + dma-channels = <13>; + }; - audma0: dma-controller@ec700000 { - compatible = "renesas,dmac-r8a7790", "renesas,rcar-dmac"; - reg = <0 0xec700000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 502>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 502>; - #dma-cells = <1>; - dma-channels = <13>; - }; + usb_dmac0: dma-controller@e65a0000 { + compatible = "renesas,r8a7790-usb-dmac", + "renesas,usb-dmac"; + reg = <0 0xe65a0000 0 0x100>; + interrupts = ; + interrupt-names = "ch0", "ch1"; + clocks = <&cpg CPG_MOD 330>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 330>; + #dma-cells = <1>; + dma-channels = <2>; + }; - audma1: dma-controller@ec720000 { - compatible = "renesas,dmac-r8a7790", "renesas,rcar-dmac"; - reg = <0 0xec720000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 501>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 501>; - #dma-cells = <1>; - dma-channels = <13>; - }; + usb_dmac1: dma-controller@e65b0000 { + compatible = "renesas,r8a7790-usb-dmac", + "renesas,usb-dmac"; + reg = <0 0xe65b0000 0 0x100>; + interrupts = ; + interrupt-names = "ch0", "ch1"; + clocks = <&cpg CPG_MOD 331>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 331>; + #dma-cells = <1>; + dma-channels = <2>; + }; - usb_dmac0: dma-controller@e65a0000 { - compatible = "renesas,r8a7790-usb-dmac", "renesas,usb-dmac"; - reg = <0 0xe65a0000 0 0x100>; - interrupts = ; - interrupt-names = "ch0", "ch1"; - clocks = <&cpg CPG_MOD 330>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 330>; - #dma-cells = <1>; - dma-channels = <2>; - }; + i2c0: i2c@e6508000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7790", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6508000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 931>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 931>; + i2c-scl-internal-delay-ns = <110>; + status = "disabled"; + }; - usb_dmac1: dma-controller@e65b0000 { - compatible = "renesas,r8a7790-usb-dmac", "renesas,usb-dmac"; - reg = <0 0xe65b0000 0 0x100>; - interrupts = ; - interrupt-names = "ch0", "ch1"; - clocks = <&cpg CPG_MOD 331>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 331>; - #dma-cells = <1>; - dma-channels = <2>; - }; + i2c1: i2c@e6518000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7790", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6518000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 930>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 930>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c0: i2c@e6508000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7790", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6508000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 931>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 931>; - i2c-scl-internal-delay-ns = <110>; - status = "disabled"; - }; + i2c2: i2c@e6530000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7790", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6530000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 929>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 929>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c1: i2c@e6518000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7790", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6518000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 930>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 930>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c3: i2c@e6540000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7790", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6540000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 928>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 928>; + i2c-scl-internal-delay-ns = <110>; + status = "disabled"; + }; - i2c2: i2c@e6530000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7790", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6530000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 929>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 929>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + iic0: i2c@e6500000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7790", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6500000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 318>; + dmas = <&dmac0 0x61>, <&dmac0 0x62>, + <&dmac1 0x61>, <&dmac1 0x62>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 318>; + status = "disabled"; + }; - i2c3: i2c@e6540000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7790", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6540000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 928>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 928>; - i2c-scl-internal-delay-ns = <110>; - status = "disabled"; - }; + iic1: i2c@e6510000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7790", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6510000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 323>; + dmas = <&dmac0 0x65>, <&dmac0 0x66>, + <&dmac1 0x65>, <&dmac1 0x66>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 323>; + status = "disabled"; + }; - iic0: i2c@e6500000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7790", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6500000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 318>; - dmas = <&dmac0 0x61>, <&dmac0 0x62>, - <&dmac1 0x61>, <&dmac1 0x62>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 318>; - status = "disabled"; - }; + iic2: i2c@e6520000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7790", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6520000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 300>; + dmas = <&dmac0 0x69>, <&dmac0 0x6a>, + <&dmac1 0x69>, <&dmac1 0x6a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 300>; + status = "disabled"; + }; - iic1: i2c@e6510000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7790", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6510000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 323>; - dmas = <&dmac0 0x65>, <&dmac0 0x66>, - <&dmac1 0x65>, <&dmac1 0x66>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 323>; - status = "disabled"; - }; + iic3: i2c@e60b0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7790", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe60b0000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 926>; + dmas = <&dmac0 0x77>, <&dmac0 0x78>, + <&dmac1 0x77>, <&dmac1 0x78>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 926>; + status = "disabled"; + }; - iic2: i2c@e6520000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7790", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6520000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 300>; - dmas = <&dmac0 0x69>, <&dmac0 0x6a>, - <&dmac1 0x69>, <&dmac1 0x6a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 300>; - status = "disabled"; - }; + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7790", + "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 315>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 315>; + reg-io-width = <4>; + status = "disabled"; + max-frequency = <97500000>; + }; - iic3: i2c@e60b0000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7790", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe60b0000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 926>; - dmas = <&dmac0 0x77>, <&dmac0 0x78>, - <&dmac1 0x77>, <&dmac1 0x78>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 926>; - status = "disabled"; - }; + mmcif1: mmc@ee220000 { + compatible = "renesas,mmcif-r8a7790", + "renesas,sh-mmcif"; + reg = <0 0xee220000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 305>; + dmas = <&dmac0 0xe1>, <&dmac0 0xe2>, + <&dmac1 0xe1>, <&dmac1 0xe2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 305>; + reg-io-width = <4>; + status = "disabled"; + max-frequency = <97500000>; + }; - mmcif0: mmc@ee200000 { - compatible = "renesas,mmcif-r8a7790", "renesas,sh-mmcif"; - reg = <0 0xee200000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 315>; - dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, - <&dmac1 0xd1>, <&dmac1 0xd2>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 315>; - reg-io-width = <4>; - status = "disabled"; - max-frequency = <97500000>; - }; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7790"; + reg = <0 0xe6060000 0 0x250>; + }; - mmcif1: mmc@ee220000 { - compatible = "renesas,mmcif-r8a7790", "renesas,sh-mmcif"; - reg = <0 0xee220000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 305>; - dmas = <&dmac0 0xe1>, <&dmac0 0xe2>, - <&dmac1 0xe1>, <&dmac1 0xe2>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 305>; - reg-io-width = <4>; - status = "disabled"; - max-frequency = <97500000>; - }; + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7790", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7790"; - reg = <0 0xe6060000 0 0x250>; - }; + sdhi1: sd@ee120000 { + compatible = "renesas,sdhi-r8a7790", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee120000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 313>; + dmas = <&dmac0 0xc9>, <&dmac0 0xca>, + <&dmac1 0xc9>, <&dmac1 0xca>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 313>; + status = "disabled"; + }; - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7790", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 314>; - status = "disabled"; - }; + sdhi2: sd@ee140000 { + compatible = "renesas,sdhi-r8a7790", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee140000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, + <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 312>; + status = "disabled"; + }; - sdhi1: sd@ee120000 { - compatible = "renesas,sdhi-r8a7790", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee120000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 313>; - dmas = <&dmac0 0xc9>, <&dmac0 0xca>, - <&dmac1 0xc9>, <&dmac1 0xca>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 313>; - status = "disabled"; - }; + sdhi3: sd@ee160000 { + compatible = "renesas,sdhi-r8a7790", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee160000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, + <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 311>; + status = "disabled"; + }; - sdhi2: sd@ee140000 { - compatible = "renesas,sdhi-r8a7790", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee140000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, - <&dmac1 0xc1>, <&dmac1 0xc2>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 312>; - status = "disabled"; - }; + scifa0: serial@e6c40000 { + compatible = "renesas,scifa-r8a7790", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c40000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 204>; + clock-names = "fck"; + dmas = <&dmac0 0x21>, <&dmac0 0x22>, + <&dmac1 0x21>, <&dmac1 0x22>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 204>; + status = "disabled"; + }; - sdhi3: sd@ee160000 { - compatible = "renesas,sdhi-r8a7790", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee160000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, - <&dmac1 0xd3>, <&dmac1 0xd4>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 311>; - status = "disabled"; - }; + scifa1: serial@e6c50000 { + compatible = "renesas,scifa-r8a7790", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c50000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 203>; + clock-names = "fck"; + dmas = <&dmac0 0x25>, <&dmac0 0x26>, + <&dmac1 0x25>, <&dmac1 0x26>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 203>; + status = "disabled"; + }; - scifa0: serial@e6c40000 { - compatible = "renesas,scifa-r8a7790", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c40000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 204>; - clock-names = "fck"; - dmas = <&dmac0 0x21>, <&dmac0 0x22>, - <&dmac1 0x21>, <&dmac1 0x22>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 204>; - status = "disabled"; - }; + scifa2: serial@e6c60000 { + compatible = "renesas,scifa-r8a7790", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 202>; + clock-names = "fck"; + dmas = <&dmac0 0x27>, <&dmac0 0x28>, + <&dmac1 0x27>, <&dmac1 0x28>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 202>; + status = "disabled"; + }; - scifa1: serial@e6c50000 { - compatible = "renesas,scifa-r8a7790", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c50000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 203>; - clock-names = "fck"; - dmas = <&dmac0 0x25>, <&dmac0 0x26>, - <&dmac1 0x25>, <&dmac1 0x26>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 203>; - status = "disabled"; - }; + scifb0: serial@e6c20000 { + compatible = "renesas,scifb-r8a7790", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c20000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 206>; + clock-names = "fck"; + dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, + <&dmac1 0x3d>, <&dmac1 0x3e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 206>; + status = "disabled"; + }; - scifa2: serial@e6c60000 { - compatible = "renesas,scifa-r8a7790", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 202>; - clock-names = "fck"; - dmas = <&dmac0 0x27>, <&dmac0 0x28>, - <&dmac1 0x27>, <&dmac1 0x28>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 202>; - status = "disabled"; - }; + scifb1: serial@e6c30000 { + compatible = "renesas,scifb-r8a7790", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c30000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 207>; + clock-names = "fck"; + dmas = <&dmac0 0x19>, <&dmac0 0x1a>, + <&dmac1 0x19>, <&dmac1 0x1a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 207>; + status = "disabled"; + }; - scifb0: serial@e6c20000 { - compatible = "renesas,scifb-r8a7790", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c20000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 206>; - clock-names = "fck"; - dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, - <&dmac1 0x3d>, <&dmac1 0x3e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 206>; - status = "disabled"; - }; + scifb2: serial@e6ce0000 { + compatible = "renesas,scifb-r8a7790", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6ce0000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 216>; + clock-names = "fck"; + dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, + <&dmac1 0x1d>, <&dmac1 0x1e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 216>; + status = "disabled"; + }; - scifb1: serial@e6c30000 { - compatible = "renesas,scifb-r8a7790", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c30000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 207>; - clock-names = "fck"; - dmas = <&dmac0 0x19>, <&dmac0 0x1a>, - <&dmac1 0x19>, <&dmac1 0x1a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 207>; - status = "disabled"; - }; + scif0: serial@e6e60000 { + compatible = "renesas,scif-r8a7790", + "renesas,rcar-gen2-scif", + "renesas,scif"; + reg = <0 0xe6e60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 721>, + <&cpg CPG_CORE R8A7790_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x29>, <&dmac0 0x2a>, + <&dmac1 0x29>, <&dmac1 0x2a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 721>; + status = "disabled"; + }; - scifb2: serial@e6ce0000 { - compatible = "renesas,scifb-r8a7790", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6ce0000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 216>; - clock-names = "fck"; - dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, - <&dmac1 0x1d>, <&dmac1 0x1e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 216>; - status = "disabled"; - }; + scif1: serial@e6e68000 { + compatible = "renesas,scif-r8a7790", + "renesas,rcar-gen2-scif", + "renesas,scif"; + reg = <0 0xe6e68000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 720>, + <&cpg CPG_CORE R8A7790_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, + <&dmac1 0x2d>, <&dmac1 0x2e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 720>; + status = "disabled"; + }; - scif0: serial@e6e60000 { - compatible = "renesas,scif-r8a7790", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7790_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x29>, <&dmac0 0x2a>, - <&dmac1 0x29>, <&dmac1 0x2a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 721>; - status = "disabled"; - }; + scif2: serial@e6e56000 { + compatible = "renesas,scif-r8a7790", + "renesas,rcar-gen2-scif", + "renesas,scif"; + reg = <0 0xe6e56000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 310>, + <&cpg CPG_CORE R8A7790_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, + <&dmac1 0x2b>, <&dmac1 0x2c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 310>; + status = "disabled"; + }; - scif1: serial@e6e68000 { - compatible = "renesas,scif-r8a7790", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e68000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 720>, <&cpg CPG_CORE R8A7790_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, - <&dmac1 0x2d>, <&dmac1 0x2e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 720>; - status = "disabled"; - }; + hscif0: serial@e62c0000 { + compatible = "renesas,hscif-r8a7790", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c0000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 717>, + <&cpg CPG_CORE R8A7790_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x39>, <&dmac0 0x3a>, + <&dmac1 0x39>, <&dmac1 0x3a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 717>; + status = "disabled"; + }; - scif2: serial@e6e56000 { - compatible = "renesas,scif-r8a7790", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e56000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 310>, <&cpg CPG_CORE R8A7790_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, - <&dmac1 0x2b>, <&dmac1 0x2c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 310>; - status = "disabled"; - }; + hscif1: serial@e62c8000 { + compatible = "renesas,hscif-r8a7790", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c8000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 716>, + <&cpg CPG_CORE R8A7790_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, + <&dmac1 0x4d>, <&dmac1 0x4e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 716>; + status = "disabled"; + }; - hscif0: serial@e62c0000 { - compatible = "renesas,hscif-r8a7790", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c0000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 717>, <&cpg CPG_CORE R8A7790_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x39>, <&dmac0 0x3a>, - <&dmac1 0x39>, <&dmac1 0x3a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 717>; - status = "disabled"; - }; + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; + }; - hscif1: serial@e62c8000 { - compatible = "renesas,hscif-r8a7790", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c8000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 716>, <&cpg CPG_CORE R8A7790_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, - <&dmac1 0x4d>, <&dmac1 0x4e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 716>; - status = "disabled"; - }; + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; + }; - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; + ether: ethernet@ee700000 { + compatible = "renesas,ether-r8a7790", + "renesas,rcar-gen2-ether"; + reg = <0 0xee700000 0 0x400>; + interrupts = ; + clocks = <&cpg CPG_MOD 813>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 813>; + phy-mode = "rmii"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; + avb: ethernet@e6800000 { + compatible = "renesas,etheravb-r8a7790", + "renesas,etheravb-rcar-gen2"; + reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; + interrupts = ; + clocks = <&cpg CPG_MOD 812>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 812>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; - }; - ether: ethernet@ee700000 { - compatible = "renesas,ether-r8a7790", - "renesas,rcar-gen2-ether"; - reg = <0 0xee700000 0 0x400>; - interrupts = ; - clocks = <&cpg CPG_MOD 813>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 813>; - phy-mode = "rmii"; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + sata0: sata@ee300000 { + compatible = "renesas,sata-r8a7790", + "renesas,rcar-gen2-sata"; + reg = <0 0xee300000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 815>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 815>; + status = "disabled"; + }; - avb: ethernet@e6800000 { - compatible = "renesas,etheravb-r8a7790", - "renesas,etheravb-rcar-gen2"; - reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; - interrupts = ; - clocks = <&cpg CPG_MOD 812>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 812>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + sata1: sata@ee500000 { + compatible = "renesas,sata-r8a7790", + "renesas,rcar-gen2-sata"; + reg = <0 0xee500000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 814>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 814>; + status = "disabled"; + }; - sata0: sata@ee300000 { - compatible = "renesas,sata-r8a7790", "renesas,rcar-gen2-sata"; - reg = <0 0xee300000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 815>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 815>; - status = "disabled"; - }; + hsusb: usb@e6590000 { + compatible = "renesas,usbhs-r8a7790", + "renesas,rcar-gen2-usbhs"; + reg = <0 0xe6590000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 704>; + dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, + <&usb_dmac1 0>, <&usb_dmac1 1>; + dma-names = "ch0", "ch1", "ch2", "ch3"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 704>; + renesas,buswait = <4>; + phys = <&usb0 1>; + phy-names = "usb"; + status = "disabled"; + }; - sata1: sata@ee500000 { - compatible = "renesas,sata-r8a7790", "renesas,rcar-gen2-sata"; - reg = <0 0xee500000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 814>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 814>; - status = "disabled"; - }; + usbphy: usb-phy@e6590100 { + compatible = "renesas,usb-phy-r8a7790", + "renesas,rcar-gen2-usb-phy"; + reg = <0 0xe6590100 0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&cpg CPG_MOD 704>; + clock-names = "usbhs"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 704>; + status = "disabled"; - hsusb: usb@e6590000 { - compatible = "renesas,usbhs-r8a7790", "renesas,rcar-gen2-usbhs"; - reg = <0 0xe6590000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 704>; - dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, - <&usb_dmac1 0>, <&usb_dmac1 1>; - dma-names = "ch0", "ch1", "ch2", "ch3"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 704>; - renesas,buswait = <4>; - phys = <&usb0 1>; - phy-names = "usb"; - status = "disabled"; - }; + usb0: usb-channel@0 { + reg = <0>; + #phy-cells = <1>; + }; + usb2: usb-channel@2 { + reg = <2>; + #phy-cells = <1>; + }; + }; - usbphy: usb-phy@e6590100 { - compatible = "renesas,usb-phy-r8a7790", - "renesas,rcar-gen2-usb-phy"; - reg = <0 0xe6590100 0 0x100>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&cpg CPG_MOD 704>; - clock-names = "usbhs"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 704>; - status = "disabled"; + vin0: video@e6ef0000 { + compatible = "renesas,vin-r8a7790", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef0000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 811>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 811>; + status = "disabled"; + }; - usb0: usb-channel@0 { - reg = <0>; - #phy-cells = <1>; + vin1: video@e6ef1000 { + compatible = "renesas,vin-r8a7790", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef1000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 810>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 810>; + status = "disabled"; }; - usb2: usb-channel@2 { - reg = <2>; - #phy-cells = <1>; + + vin2: video@e6ef2000 { + compatible = "renesas,vin-r8a7790", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef2000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 809>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 809>; + status = "disabled"; }; - }; - vin0: video@e6ef0000 { - compatible = "renesas,vin-r8a7790", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef0000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 811>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 811>; - status = "disabled"; - }; + vin3: video@e6ef3000 { + compatible = "renesas,vin-r8a7790", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef3000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 808>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 808>; + status = "disabled"; + }; - vin1: video@e6ef1000 { - compatible = "renesas,vin-r8a7790", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef1000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 810>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 810>; - status = "disabled"; - }; + vsp@fe920000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe920000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 130>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 130>; + }; - vin2: video@e6ef2000 { - compatible = "renesas,vin-r8a7790", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef2000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 809>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 809>; - status = "disabled"; - }; + vsp@fe928000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe928000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 131>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 131>; + }; - vin3: video@e6ef3000 { - compatible = "renesas,vin-r8a7790", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef3000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 808>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 808>; - status = "disabled"; - }; + vsp@fe930000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe930000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 128>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 128>; + }; - vsp@fe920000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe920000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 130>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 130>; - }; + vsp@fe938000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe938000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 127>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 127>; + }; - vsp@fe928000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe928000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 131>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 131>; - }; + du: display@feb00000 { + compatible = "renesas,du-r8a7790"; + reg = <0 0xfeb00000 0 0x70000>, + <0 0xfeb90000 0 0x1c>, + <0 0xfeb94000 0 0x1c>; + reg-names = "du", "lvds.0", "lvds.1"; + interrupts = , + , + ; + clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, + <&cpg CPG_MOD 722>, <&cpg CPG_MOD 726>, + <&cpg CPG_MOD 725>; + clock-names = "du.0", "du.1", "du.2", "lvds.0", + "lvds.1"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb: endpoint { + }; + }; + port@1 { + reg = <1>; + du_out_lvds0: endpoint { + }; + }; + port@2 { + reg = <2>; + du_out_lvds1: endpoint { + }; + }; + }; + }; - vsp@fe930000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe930000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 128>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 128>; - }; + can0: can@e6e80000 { + compatible = "renesas,can-r8a7790", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e80000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 916>, + <&cpg CPG_CORE R8A7790_CLK_RCAN>, <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 916>; + status = "disabled"; + }; - vsp@fe938000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe938000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 127>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 127>; - }; + can1: can@e6e88000 { + compatible = "renesas,can-r8a7790", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e88000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 915>, + <&cpg CPG_CORE R8A7790_CLK_RCAN>, <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 915>; + status = "disabled"; + }; - du: display@feb00000 { - compatible = "renesas,du-r8a7790"; - reg = <0 0xfeb00000 0 0x70000>, - <0 0xfeb90000 0 0x1c>, - <0 0xfeb94000 0 0x1c>; - reg-names = "du", "lvds.0", "lvds.1"; - interrupts = , - , - ; - clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, - <&cpg CPG_MOD 722>, <&cpg CPG_MOD 726>, - <&cpg CPG_MOD 725>; - clock-names = "du.0", "du.1", "du.2", "lvds.0", "lvds.1"; - status = "disabled"; - - ports { + jpu: jpeg-codec@fe980000 { + compatible = "renesas,jpu-r8a7790", + "renesas,rcar-gen2-jpu"; + reg = <0 0xfe980000 0 0x10300>; + interrupts = ; + clocks = <&cpg CPG_MOD 106>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 106>; + }; + + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7790-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&usb_extal_clk>; + clock-names = "extal", "usb_extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; + + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; + }; + + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7790-rst"; + reg = <0 0xe6160000 0 0x0100>; + }; + + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7790-sysc"; + reg = <0 0xe6180000 0 0x0200>; + #power-domain-cells = <1>; + }; + + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7790", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 917>; + num-cs = <1>; #address-cells = <1>; #size-cells = <0>; + status = "disabled"; + }; - port@0 { - reg = <0>; - du_out_rgb: endpoint { + msiof0: spi@e6e20000 { + compatible = "renesas,msiof-r8a7790", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6e20000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 0>; + dmas = <&dmac0 0x51>, <&dmac0 0x52>, + <&dmac1 0x51>, <&dmac1 0x52>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 0>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + msiof1: spi@e6e10000 { + compatible = "renesas,msiof-r8a7790", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6e10000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 208>; + dmas = <&dmac0 0x55>, <&dmac0 0x56>, + <&dmac1 0x55>, <&dmac1 0x56>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 208>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + msiof2: spi@e6e00000 { + compatible = "renesas,msiof-r8a7790", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6e00000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 205>; + dmas = <&dmac0 0x41>, <&dmac0 0x42>, + <&dmac1 0x41>, <&dmac1 0x42>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 205>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + msiof3: spi@e6c90000 { + compatible = "renesas,msiof-r8a7790", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6c90000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 215>; + dmas = <&dmac0 0x45>, <&dmac0 0x46>, + <&dmac1 0x45>, <&dmac1 0x46>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 215>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + xhci: usb@ee000000 { + compatible = "renesas,xhci-r8a7790", + "renesas,rcar-gen2-xhci"; + reg = <0 0xee000000 0 0xc00>; + interrupts = ; + clocks = <&cpg CPG_MOD 328>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 328>; + phys = <&usb2 1>; + phy-names = "usb"; + status = "disabled"; + }; + + pci0: pci@ee090000 { + compatible = "renesas,pci-r8a7790", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee090000 0 0xc00>, + <0 0xee080000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x800 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x1000 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + }; + + pci1: pci@ee0b0000 { + compatible = "renesas,pci-r8a7790", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee0b0000 0 0xc00>, + <0 0xee0a0000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <1 1>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee0a0000 0 0xee0a0000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>; + }; + + pci2: pci@ee0d0000 { + compatible = "renesas,pci-r8a7790", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 703>; + reg = <0 0xee0d0000 0 0xc00>, + <0 0xee0c0000 0 0x1100>; + interrupts = ; + status = "disabled"; + + bus-range = <2 2>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x20800 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x21000 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + }; + + pciec: pcie@fe000000 { + compatible = "renesas,pcie-r8a7790", + "renesas,pcie-rcar-gen2"; + reg = <0 0xfe000000 0 0x80000>; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; + device_type = "pci"; + ranges = <0x01000000 0 0x00000000 0 0xfe100000 0 0x00100000 + 0x02000000 0 0xfe200000 0 0xfe200000 0 0x00200000 + 0x02000000 0 0x30000000 0 0x30000000 0 0x08000000 + 0x42000000 0 0x38000000 0 0x38000000 0 0x08000000>; + /* Map all possible DDR as inbound ranges */ + dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 0 0x80000000 + 0x43000000 1 0x80000000 1 0x80000000 0 0x80000000>; + interrupts = , + , + ; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 319>, <&pcie_bus_clk>; + clock-names = "pcie", "pcie_bus"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 319>; + status = "disabled"; + }; + + rcar_sound: sound@ec500000 { + /* + * #sound-dai-cells is required + * + * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; + * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; + */ + compatible = "renesas,rcar_sound-r8a7790", + "renesas,rcar_sound-gen2"; + reg = <0 0xec500000 0 0x1000>, /* SCU */ + <0 0xec5a0000 0 0x100>, /* ADG */ + <0 0xec540000 0 0x1000>, /* SSIU */ + <0 0xec541000 0 0x280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ + reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; + + clocks = <&cpg CPG_MOD 1005>, + <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, + <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, + <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, + <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, + <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, + <&cpg CPG_MOD 1022>, <&cpg CPG_MOD 1023>, + <&cpg CPG_MOD 1024>, <&cpg CPG_MOD 1025>, + <&cpg CPG_MOD 1026>, <&cpg CPG_MOD 1027>, + <&cpg CPG_MOD 1028>, <&cpg CPG_MOD 1029>, + <&cpg CPG_MOD 1030>, <&cpg CPG_MOD 1031>, + <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, + <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, + <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, + <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, + <&cpg CPG_CORE R8A7790_CLK_M2>; + clock-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0", + "src.9", "src.8", "src.7", "src.6", + "src.5", "src.4", "src.3", "src.2", + "src.1", "src.0", + "ctu.0", "ctu.1", + "mix.0", "mix.1", + "dvc.0", "dvc.1", + "clk_a", "clk_b", "clk_c", "clk_i"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 1005>, + <&cpg 1006>, <&cpg 1007>, + <&cpg 1008>, <&cpg 1009>, + <&cpg 1010>, <&cpg 1011>, + <&cpg 1012>, <&cpg 1013>, + <&cpg 1014>, <&cpg 1015>; + reset-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0"; + + status = "disabled"; + + rcar_sound,dvc { + dvc0: dvc-0 { + dmas = <&audma1 0xbc>; + dma-names = "tx"; + }; + dvc1: dvc-1 { + dmas = <&audma1 0xbe>; + dma-names = "tx"; }; }; - port@1 { - reg = <1>; - du_out_lvds0: endpoint { + + rcar_sound,mix { + mix0: mix-0 { }; + mix1: mix-1 { }; + }; + + rcar_sound,ctu { + ctu00: ctu-0 { }; + ctu01: ctu-1 { }; + ctu02: ctu-2 { }; + ctu03: ctu-3 { }; + ctu10: ctu-4 { }; + ctu11: ctu-5 { }; + ctu12: ctu-6 { }; + ctu13: ctu-7 { }; + }; + + rcar_sound,src { + src0: src-0 { + interrupts = ; + dmas = <&audma0 0x85>, <&audma1 0x9a>; + dma-names = "rx", "tx"; + }; + src1: src-1 { + interrupts = ; + dmas = <&audma0 0x87>, <&audma1 0x9c>; + dma-names = "rx", "tx"; + }; + src2: src-2 { + interrupts = ; + dmas = <&audma0 0x89>, <&audma1 0x9e>; + dma-names = "rx", "tx"; + }; + src3: src-3 { + interrupts = ; + dmas = <&audma0 0x8b>, <&audma1 0xa0>; + dma-names = "rx", "tx"; + }; + src4: src-4 { + interrupts = ; + dmas = <&audma0 0x8d>, <&audma1 0xb0>; + dma-names = "rx", "tx"; + }; + src5: src-5 { + interrupts = ; + dmas = <&audma0 0x8f>, <&audma1 0xb2>; + dma-names = "rx", "tx"; + }; + src6: src-6 { + interrupts = ; + dmas = <&audma0 0x91>, <&audma1 0xb4>; + dma-names = "rx", "tx"; + }; + src7: src-7 { + interrupts = ; + dmas = <&audma0 0x93>, <&audma1 0xb6>; + dma-names = "rx", "tx"; + }; + src8: src-8 { + interrupts = ; + dmas = <&audma0 0x95>, <&audma1 0xb8>; + dma-names = "rx", "tx"; + }; + src9: src-9 { + interrupts = ; + dmas = <&audma0 0x97>, <&audma1 0xba>; + dma-names = "rx", "tx"; }; }; - port@2 { - reg = <2>; - du_out_lvds1: endpoint { + + rcar_sound,ssi { + ssi0: ssi-0 { + interrupts = ; + dmas = <&audma0 0x01>, <&audma1 0x02>, + <&audma0 0x15>, <&audma1 0x16>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi1: ssi-1 { + interrupts = ; + dmas = <&audma0 0x03>, <&audma1 0x04>, + <&audma0 0x49>, <&audma1 0x4a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi2: ssi-2 { + interrupts = ; + dmas = <&audma0 0x05>, <&audma1 0x06>, + <&audma0 0x63>, <&audma1 0x64>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi3: ssi-3 { + interrupts = ; + dmas = <&audma0 0x07>, <&audma1 0x08>, + <&audma0 0x6f>, <&audma1 0x70>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi4: ssi-4 { + interrupts = ; + dmas = <&audma0 0x09>, <&audma1 0x0a>, + <&audma0 0x71>, <&audma1 0x72>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi5: ssi-5 { + interrupts = ; + dmas = <&audma0 0x0b>, <&audma1 0x0c>, + <&audma0 0x73>, <&audma1 0x74>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi6: ssi-6 { + interrupts = ; + dmas = <&audma0 0x0d>, <&audma1 0x0e>, + <&audma0 0x75>, <&audma1 0x76>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi7: ssi-7 { + interrupts = ; + dmas = <&audma0 0x0f>, <&audma1 0x10>, + <&audma0 0x79>, <&audma1 0x7a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi8: ssi-8 { + interrupts = ; + dmas = <&audma0 0x11>, <&audma1 0x12>, + <&audma0 0x7b>, <&audma1 0x7c>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi9: ssi-9 { + interrupts = ; + dmas = <&audma0 0x13>, <&audma1 0x14>, + <&audma0 0x7d>, <&audma1 0x7e>; + dma-names = "rx", "tx", "rxu", "txu"; }; }; }; - }; - can0: can@e6e80000 { - compatible = "renesas,can-r8a7790", "renesas,rcar-gen2-can"; - reg = <0 0xe6e80000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 916>, <&cpg CPG_CORE R8A7790_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 916>; - status = "disabled"; + ipmmu_sy0: mmu@e6280000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6280000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_sy1: mmu@e6290000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6290000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_ds: mmu@e6740000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6740000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mp: mmu@ec680000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xec680000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mx: mmu@fe951000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xfe951000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_rt: mmu@ffc80000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xffc80000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; }; - can1: can@e6e88000 { - compatible = "renesas,can-r8a7790", "renesas,rcar-gen2-can"; - reg = <0 0xe6e88000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 915>, <&cpg CPG_CORE R8A7790_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 915>; - status = "disabled"; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + + thermal-sensors = <&thermal>; + + trips { + cpu-crit { + temperature = <95000>; + hysteresis = <0>; + type = "critical"; + }; + }; + cooling-maps { + }; + }; }; - jpu: jpeg-codec@fe980000 { - compatible = "renesas,jpu-r8a7790", "renesas,rcar-gen2-jpu"; - reg = <0 0xfe980000 0 0x10300>; - interrupts = ; - clocks = <&cpg CPG_MOD 106>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 106>; + timer { + compatible = "arm,armv7-timer"; + interrupts-extended = <&gic GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>; }; /* External root clock */ @@ -1194,476 +1736,4 @@ /* This value must be overridden by the board. */ clock-frequency = <0>; }; - - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7790-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>, <&usb_extal_clk>; - clock-names = "extal", "usb_extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; - }; - - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; - }; - - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7790-rst"; - reg = <0 0xe6160000 0 0x0100>; - }; - - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7790-sysc"; - reg = <0 0xe6180000 0 0x0200>; - #power-domain-cells = <1>; - }; - - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7790", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 917>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - msiof0: spi@e6e20000 { - compatible = "renesas,msiof-r8a7790", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6e20000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 0>; - dmas = <&dmac0 0x51>, <&dmac0 0x52>, - <&dmac1 0x51>, <&dmac1 0x52>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 0>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - msiof1: spi@e6e10000 { - compatible = "renesas,msiof-r8a7790", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6e10000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 208>; - dmas = <&dmac0 0x55>, <&dmac0 0x56>, - <&dmac1 0x55>, <&dmac1 0x56>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 208>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - msiof2: spi@e6e00000 { - compatible = "renesas,msiof-r8a7790", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6e00000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 205>; - dmas = <&dmac0 0x41>, <&dmac0 0x42>, - <&dmac1 0x41>, <&dmac1 0x42>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 205>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - msiof3: spi@e6c90000 { - compatible = "renesas,msiof-r8a7790", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6c90000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 215>; - dmas = <&dmac0 0x45>, <&dmac0 0x46>, - <&dmac1 0x45>, <&dmac1 0x46>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 215>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - xhci: usb@ee000000 { - compatible = "renesas,xhci-r8a7790", "renesas,rcar-gen2-xhci"; - reg = <0 0xee000000 0 0xc00>; - interrupts = ; - clocks = <&cpg CPG_MOD 328>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 328>; - phys = <&usb2 1>; - phy-names = "usb"; - status = "disabled"; - }; - - pci0: pci@ee090000 { - compatible = "renesas,pci-r8a7790", "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee090000 0 0xc00>, - <0 0xee080000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <0 0>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x800 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x1000 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; - }; - - pci1: pci@ee0b0000 { - compatible = "renesas,pci-r8a7790", "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee0b0000 0 0xc00>, - <0 0xee0a0000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <1 1>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee0a0000 0 0xee0a0000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>; - }; - - pci2: pci@ee0d0000 { - compatible = "renesas,pci-r8a7790", "renesas,pci-rcar-gen2"; - device_type = "pci"; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 703>; - reg = <0 0xee0d0000 0 0xc00>, - <0 0xee0c0000 0 0x1100>; - interrupts = ; - status = "disabled"; - - bus-range = <2 2>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x20800 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x21000 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - }; - - pciec: pcie@fe000000 { - compatible = "renesas,pcie-r8a7790", "renesas,pcie-rcar-gen2"; - reg = <0 0xfe000000 0 0x80000>; - #address-cells = <3>; - #size-cells = <2>; - bus-range = <0x00 0xff>; - device_type = "pci"; - ranges = <0x01000000 0 0x00000000 0 0xfe100000 0 0x00100000 - 0x02000000 0 0xfe200000 0 0xfe200000 0 0x00200000 - 0x02000000 0 0x30000000 0 0x30000000 0 0x08000000 - 0x42000000 0 0x38000000 0 0x38000000 0 0x08000000>; - /* Map all possible DDR as inbound ranges */ - dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 0 0x80000000 - 0x43000000 1 0x80000000 1 0x80000000 0 0x80000000>; - interrupts = , - , - ; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&cpg CPG_MOD 319>, <&pcie_bus_clk>; - clock-names = "pcie", "pcie_bus"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 319>; - status = "disabled"; - }; - - rcar_sound: sound@ec500000 { - /* - * #sound-dai-cells is required - * - * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; - * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; - */ - compatible = "renesas,rcar_sound-r8a7790", - "renesas,rcar_sound-gen2"; - reg = <0 0xec500000 0 0x1000>, /* SCU */ - <0 0xec5a0000 0 0x100>, /* ADG */ - <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x280>, /* SSI */ - <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ - reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; - - clocks = <&cpg CPG_MOD 1005>, - <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, - <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, - <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, - <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, - <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, - <&cpg CPG_MOD 1022>, <&cpg CPG_MOD 1023>, - <&cpg CPG_MOD 1024>, <&cpg CPG_MOD 1025>, - <&cpg CPG_MOD 1026>, <&cpg CPG_MOD 1027>, - <&cpg CPG_MOD 1028>, <&cpg CPG_MOD 1029>, - <&cpg CPG_MOD 1030>, <&cpg CPG_MOD 1031>, - <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, - <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, - <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, - <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, - <&cpg CPG_CORE R8A7790_CLK_M2>; - clock-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", - "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0", - "src.9", "src.8", "src.7", "src.6", "src.5", - "src.4", "src.3", "src.2", "src.1", "src.0", - "ctu.0", "ctu.1", - "mix.0", "mix.1", - "dvc.0", "dvc.1", - "clk_a", "clk_b", "clk_c", "clk_i"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 1005>, - <&cpg 1006>, <&cpg 1007>, <&cpg 1008>, <&cpg 1009>, - <&cpg 1010>, <&cpg 1011>, <&cpg 1012>, <&cpg 1013>, - <&cpg 1014>, <&cpg 1015>; - reset-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", - "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0"; - - status = "disabled"; - - rcar_sound,dvc { - dvc0: dvc-0 { - dmas = <&audma1 0xbc>; - dma-names = "tx"; - }; - dvc1: dvc-1 { - dmas = <&audma1 0xbe>; - dma-names = "tx"; - }; - }; - - rcar_sound,mix { - mix0: mix-0 { }; - mix1: mix-1 { }; - }; - - rcar_sound,ctu { - ctu00: ctu-0 { }; - ctu01: ctu-1 { }; - ctu02: ctu-2 { }; - ctu03: ctu-3 { }; - ctu10: ctu-4 { }; - ctu11: ctu-5 { }; - ctu12: ctu-6 { }; - ctu13: ctu-7 { }; - }; - - rcar_sound,src { - src0: src-0 { - interrupts = ; - dmas = <&audma0 0x85>, <&audma1 0x9a>; - dma-names = "rx", "tx"; - }; - src1: src-1 { - interrupts = ; - dmas = <&audma0 0x87>, <&audma1 0x9c>; - dma-names = "rx", "tx"; - }; - src2: src-2 { - interrupts = ; - dmas = <&audma0 0x89>, <&audma1 0x9e>; - dma-names = "rx", "tx"; - }; - src3: src-3 { - interrupts = ; - dmas = <&audma0 0x8b>, <&audma1 0xa0>; - dma-names = "rx", "tx"; - }; - src4: src-4 { - interrupts = ; - dmas = <&audma0 0x8d>, <&audma1 0xb0>; - dma-names = "rx", "tx"; - }; - src5: src-5 { - interrupts = ; - dmas = <&audma0 0x8f>, <&audma1 0xb2>; - dma-names = "rx", "tx"; - }; - src6: src-6 { - interrupts = ; - dmas = <&audma0 0x91>, <&audma1 0xb4>; - dma-names = "rx", "tx"; - }; - src7: src-7 { - interrupts = ; - dmas = <&audma0 0x93>, <&audma1 0xb6>; - dma-names = "rx", "tx"; - }; - src8: src-8 { - interrupts = ; - dmas = <&audma0 0x95>, <&audma1 0xb8>; - dma-names = "rx", "tx"; - }; - src9: src-9 { - interrupts = ; - dmas = <&audma0 0x97>, <&audma1 0xba>; - dma-names = "rx", "tx"; - }; - }; - - rcar_sound,ssi { - ssi0: ssi-0 { - interrupts = ; - dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi1: ssi-1 { - interrupts = ; - dmas = <&audma0 0x03>, <&audma1 0x04>, <&audma0 0x49>, <&audma1 0x4a>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi2: ssi-2 { - interrupts = ; - dmas = <&audma0 0x05>, <&audma1 0x06>, <&audma0 0x63>, <&audma1 0x64>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi3: ssi-3 { - interrupts = ; - dmas = <&audma0 0x07>, <&audma1 0x08>, <&audma0 0x6f>, <&audma1 0x70>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi4: ssi-4 { - interrupts = ; - dmas = <&audma0 0x09>, <&audma1 0x0a>, <&audma0 0x71>, <&audma1 0x72>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi5: ssi-5 { - interrupts = ; - dmas = <&audma0 0x0b>, <&audma1 0x0c>, <&audma0 0x73>, <&audma1 0x74>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi6: ssi-6 { - interrupts = ; - dmas = <&audma0 0x0d>, <&audma1 0x0e>, <&audma0 0x75>, <&audma1 0x76>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi7: ssi-7 { - interrupts = ; - dmas = <&audma0 0x0f>, <&audma1 0x10>, <&audma0 0x79>, <&audma1 0x7a>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi8: ssi-8 { - interrupts = ; - dmas = <&audma0 0x11>, <&audma1 0x12>, <&audma0 0x7b>, <&audma1 0x7c>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi9: ssi-9 { - interrupts = ; - dmas = <&audma0 0x13>, <&audma1 0x14>, <&audma0 0x7d>, <&audma1 0x7e>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - }; - }; - - ipmmu_sy0: mmu@e6280000 { - compatible = "renesas,ipmmu-r8a7790", "renesas,ipmmu-vmsa"; - reg = <0 0xe6280000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_sy1: mmu@e6290000 { - compatible = "renesas,ipmmu-r8a7790", "renesas,ipmmu-vmsa"; - reg = <0 0xe6290000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_ds: mmu@e6740000 { - compatible = "renesas,ipmmu-r8a7790", "renesas,ipmmu-vmsa"; - reg = <0 0xe6740000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mp: mmu@ec680000 { - compatible = "renesas,ipmmu-r8a7790", "renesas,ipmmu-vmsa"; - reg = <0 0xec680000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mx: mmu@fe951000 { - compatible = "renesas,ipmmu-r8a7790", "renesas,ipmmu-vmsa"; - reg = <0 0xfe951000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_rt: mmu@ffc80000 { - compatible = "renesas,ipmmu-r8a7790", "renesas,ipmmu-vmsa"; - reg = <0 0xffc80000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; }; -- cgit v1.3-14-g43fede From 5923abc19cd76b63652401f2851b863b8f5deffa Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:04 +0100 Subject: ARM: dts: r8a7790: sort subnodes of soc node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort the subnodes of the soc node to improve maintainability. The sort key is the addresss on the bus with instances of the same IP block grouped together. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7790.dtsi | 1592 ++++++++++++++++++++-------------------- 1 file changed, 796 insertions(+), 796 deletions(-) diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index 1f4b3a4ed287..54c5a2d7ea89 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -166,32 +166,6 @@ #size-cells = <2>; ranges; - apmu@e6151000 { - compatible = "renesas,r8a7790-apmu", "renesas,apmu"; - reg = <0 0xe6151000 0 0x188>; - cpus = <&cpu4 &cpu5 &cpu6 &cpu7>; - }; - - apmu@e6152000 { - compatible = "renesas,r8a7790-apmu", "renesas,apmu"; - reg = <0 0xe6152000 0 0x188>; - cpus = <&cpu0 &cpu1 &cpu2 &cpu3>; - }; - - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <0>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; - gpio0: gpio@e6050000 { compatible = "renesas,gpio-r8a7790", "renesas,rcar-gen2-gpio"; @@ -282,50 +256,42 @@ resets = <&cpg 907>; }; - thermal: thermal@e61f0000 { - compatible = "renesas,thermal-r8a7790", - "renesas,rcar-gen2-thermal", - "renesas,rcar-thermal"; - reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; - interrupts = ; - clocks = <&cpg CPG_MOD 522>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 522>; - #thermal-sensor-cells = <0>; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7790"; + reg = <0 0xe6060000 0 0x250>; }; - cmt0: timer@ffca0000 { - compatible = "renesas,r8a7790-cmt0", - "renesas,rcar-gen2-cmt0"; - reg = <0 0xffca0000 0 0x1004>; - interrupts = , - ; - clocks = <&cpg CPG_MOD 124>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 124>; + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7790-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&usb_extal_clk>; + clock-names = "extal", "usb_extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; - status = "disabled"; + apmu@e6151000 { + compatible = "renesas,r8a7790-apmu", "renesas,apmu"; + reg = <0 0xe6151000 0 0x188>; + cpus = <&cpu4 &cpu5 &cpu6 &cpu7>; }; - cmt1: timer@e6130000 { - compatible = "renesas,r8a7790-cmt1", - "renesas,rcar-gen2-cmt1"; - reg = <0 0xe6130000 0 0x1004>; - interrupts = , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 329>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 329>; + apmu@e6152000 { + compatible = "renesas,r8a7790-apmu", "renesas,apmu"; + reg = <0 0xe6152000 0 0x188>; + cpus = <&cpu0 &cpu1 &cpu2 &cpu3>; + }; - status = "disabled"; + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7790-rst"; + reg = <0 0xe6160000 0 0x0100>; + }; + + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7790-sysc"; + reg = <0 0xe6180000 0 0x0200>; + #power-domain-cells = <1>; }; irqc0: interrupt-controller@e61c0000 { @@ -342,160 +308,91 @@ resets = <&cpg 407>; }; - dmac0: dma-controller@e6700000 { - compatible = "renesas,dmac-r8a7790", - "renesas,rcar-dmac"; - reg = <0 0xe6700000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 219>; - clock-names = "fck"; + thermal: thermal@e61f0000 { + compatible = "renesas,thermal-r8a7790", + "renesas,rcar-gen2-thermal", + "renesas,rcar-thermal"; + reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; + interrupts = ; + clocks = <&cpg CPG_MOD 522>; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 219>; - #dma-cells = <1>; - dma-channels = <15>; + resets = <&cpg 522>; + #thermal-sensor-cells = <0>; }; - dmac1: dma-controller@e6720000 { - compatible = "renesas,dmac-r8a7790", - "renesas,rcar-dmac"; - reg = <0 0xe6720000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 218>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 218>; - #dma-cells = <1>; - dma-channels = <15>; + ipmmu_sy0: mmu@e6280000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6280000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; }; - audma0: dma-controller@ec700000 { - compatible = "renesas,dmac-r8a7790", - "renesas,rcar-dmac"; - reg = <0 0xec700000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 502>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 502>; - #dma-cells = <1>; - dma-channels = <13>; + ipmmu_sy1: mmu@e6290000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6290000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; }; - audma1: dma-controller@ec720000 { - compatible = "renesas,dmac-r8a7790", - "renesas,rcar-dmac"; - reg = <0 0xec720000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 501>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 501>; - #dma-cells = <1>; - dma-channels = <13>; + ipmmu_ds: mmu@e6740000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6740000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; }; - usb_dmac0: dma-controller@e65a0000 { - compatible = "renesas,r8a7790-usb-dmac", - "renesas,usb-dmac"; - reg = <0 0xe65a0000 0 0x100>; - interrupts = ; - interrupt-names = "ch0", "ch1"; - clocks = <&cpg CPG_MOD 330>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 330>; - #dma-cells = <1>; - dma-channels = <2>; + ipmmu_mp: mmu@ec680000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xec680000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; }; - usb_dmac1: dma-controller@e65b0000 { - compatible = "renesas,r8a7790-usb-dmac", - "renesas,usb-dmac"; - reg = <0 0xe65b0000 0 0x100>; - interrupts = ; - interrupt-names = "ch0", "ch1"; - clocks = <&cpg CPG_MOD 331>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 331>; - #dma-cells = <1>; - dma-channels = <2>; + ipmmu_mx: mmu@fe951000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xfe951000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_rt: mmu@ffc80000 { + compatible = "renesas,ipmmu-r8a7790", + "renesas,ipmmu-vmsa"; + reg = <0 0xffc80000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; + }; + + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; + + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; }; i2c0: i2c@e6508000 { @@ -622,107 +519,172 @@ status = "disabled"; }; - mmcif0: mmc@ee200000 { - compatible = "renesas,mmcif-r8a7790", - "renesas,sh-mmcif"; - reg = <0 0xee200000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 315>; - dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, - <&dmac1 0xd1>, <&dmac1 0xd2>; - dma-names = "tx", "rx", "tx", "rx"; + hsusb: usb@e6590000 { + compatible = "renesas,usbhs-r8a7790", + "renesas,rcar-gen2-usbhs"; + reg = <0 0xe6590000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 704>; + dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, + <&usb_dmac1 0>, <&usb_dmac1 1>; + dma-names = "ch0", "ch1", "ch2", "ch3"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 315>; - reg-io-width = <4>; + resets = <&cpg 704>; + renesas,buswait = <4>; + phys = <&usb0 1>; + phy-names = "usb"; status = "disabled"; - max-frequency = <97500000>; }; - mmcif1: mmc@ee220000 { - compatible = "renesas,mmcif-r8a7790", - "renesas,sh-mmcif"; - reg = <0 0xee220000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 305>; - dmas = <&dmac0 0xe1>, <&dmac0 0xe2>, - <&dmac1 0xe1>, <&dmac1 0xe2>; - dma-names = "tx", "rx", "tx", "rx"; + usbphy: usb-phy@e6590100 { + compatible = "renesas,usb-phy-r8a7790", + "renesas,rcar-gen2-usb-phy"; + reg = <0 0xe6590100 0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&cpg CPG_MOD 704>; + clock-names = "usbhs"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 305>; - reg-io-width = <4>; + resets = <&cpg 704>; status = "disabled"; - max-frequency = <97500000>; + + usb0: usb-channel@0 { + reg = <0>; + #phy-cells = <1>; + }; + usb2: usb-channel@2 { + reg = <2>; + #phy-cells = <1>; + }; }; - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7790"; - reg = <0 0xe6060000 0 0x250>; + usb_dmac0: dma-controller@e65a0000 { + compatible = "renesas,r8a7790-usb-dmac", + "renesas,usb-dmac"; + reg = <0 0xe65a0000 0 0x100>; + interrupts = ; + interrupt-names = "ch0", "ch1"; + clocks = <&cpg CPG_MOD 330>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 330>; + #dma-cells = <1>; + dma-channels = <2>; }; - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7790", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; + usb_dmac1: dma-controller@e65b0000 { + compatible = "renesas,r8a7790-usb-dmac", + "renesas,usb-dmac"; + reg = <0 0xe65b0000 0 0x100>; + interrupts = ; + interrupt-names = "ch0", "ch1"; + clocks = <&cpg CPG_MOD 331>; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 314>; - status = "disabled"; + resets = <&cpg 331>; + #dma-cells = <1>; + dma-channels = <2>; }; - sdhi1: sd@ee120000 { - compatible = "renesas,sdhi-r8a7790", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee120000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 313>; - dmas = <&dmac0 0xc9>, <&dmac0 0xca>, - <&dmac1 0xc9>, <&dmac1 0xca>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; + dmac0: dma-controller@e6700000 { + compatible = "renesas,dmac-r8a7790", + "renesas,rcar-dmac"; + reg = <0 0xe6700000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 219>; + clock-names = "fck"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 313>; - status = "disabled"; + resets = <&cpg 219>; + #dma-cells = <1>; + dma-channels = <15>; }; - sdhi2: sd@ee140000 { - compatible = "renesas,sdhi-r8a7790", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee140000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, - <&dmac1 0xc1>, <&dmac1 0xc2>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; + dmac1: dma-controller@e6720000 { + compatible = "renesas,dmac-r8a7790", + "renesas,rcar-dmac"; + reg = <0 0xe6720000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 218>; + clock-names = "fck"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 312>; - status = "disabled"; + resets = <&cpg 218>; + #dma-cells = <1>; + dma-channels = <15>; }; - sdhi3: sd@ee160000 { - compatible = "renesas,sdhi-r8a7790", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee160000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, - <&dmac1 0xd3>, <&dmac1 0xd4>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; + avb: ethernet@e6800000 { + compatible = "renesas,etheravb-r8a7790", + "renesas,etheravb-rcar-gen2"; + reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; + interrupts = ; + clocks = <&cpg CPG_MOD 812>; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 311>; + resets = <&cpg 812>; + #address-cells = <1>; + #size-cells = <0>; status = "disabled"; }; - scifa0: serial@e6c40000 { - compatible = "renesas,scifa-r8a7790", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c40000 0 64>; + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7790", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 917>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + scifa0: serial@e6c40000 { + compatible = "renesas,scifa-r8a7790", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c40000 0 64>; interrupts = ; clocks = <&cpg CPG_MOD 204>; clock-names = "fck"; @@ -892,110 +854,94 @@ status = "disabled"; }; - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; - - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; - - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; - }; - }; - - ether: ethernet@ee700000 { - compatible = "renesas,ether-r8a7790", - "renesas,rcar-gen2-ether"; - reg = <0 0xee700000 0 0x400>; - interrupts = ; - clocks = <&cpg CPG_MOD 813>; + msiof0: spi@e6e20000 { + compatible = "renesas,msiof-r8a7790", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6e20000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 0>; + dmas = <&dmac0 0x51>, <&dmac0 0x52>, + <&dmac1 0x51>, <&dmac1 0x52>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 813>; - phy-mode = "rmii"; + resets = <&cpg 0>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; }; - avb: ethernet@e6800000 { - compatible = "renesas,etheravb-r8a7790", - "renesas,etheravb-rcar-gen2"; - reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; - interrupts = ; - clocks = <&cpg CPG_MOD 812>; + msiof1: spi@e6e10000 { + compatible = "renesas,msiof-r8a7790", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6e10000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 208>; + dmas = <&dmac0 0x55>, <&dmac0 0x56>, + <&dmac1 0x55>, <&dmac1 0x56>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 812>; + resets = <&cpg 208>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; }; - sata0: sata@ee300000 { - compatible = "renesas,sata-r8a7790", - "renesas,rcar-gen2-sata"; - reg = <0 0xee300000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 815>; + msiof2: spi@e6e00000 { + compatible = "renesas,msiof-r8a7790", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6e00000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 205>; + dmas = <&dmac0 0x41>, <&dmac0 0x42>, + <&dmac1 0x41>, <&dmac1 0x42>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 815>; + resets = <&cpg 205>; + #address-cells = <1>; + #size-cells = <0>; status = "disabled"; }; - sata1: sata@ee500000 { - compatible = "renesas,sata-r8a7790", - "renesas,rcar-gen2-sata"; - reg = <0 0xee500000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 814>; + msiof3: spi@e6c90000 { + compatible = "renesas,msiof-r8a7790", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6c90000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 215>; + dmas = <&dmac0 0x45>, <&dmac0 0x46>, + <&dmac1 0x45>, <&dmac1 0x46>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 814>; + resets = <&cpg 215>; + #address-cells = <1>; + #size-cells = <0>; status = "disabled"; }; - hsusb: usb@e6590000 { - compatible = "renesas,usbhs-r8a7790", - "renesas,rcar-gen2-usbhs"; - reg = <0 0xe6590000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 704>; - dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, - <&usb_dmac1 0>, <&usb_dmac1 1>; - dma-names = "ch0", "ch1", "ch2", "ch3"; + can0: can@e6e80000 { + compatible = "renesas,can-r8a7790", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e80000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 916>, + <&cpg CPG_CORE R8A7790_CLK_RCAN>, <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 704>; - renesas,buswait = <4>; - phys = <&usb0 1>; - phy-names = "usb"; + resets = <&cpg 916>; status = "disabled"; }; - usbphy: usb-phy@e6590100 { - compatible = "renesas,usb-phy-r8a7790", - "renesas,rcar-gen2-usb-phy"; - reg = <0 0xe6590100 0 0x100>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&cpg CPG_MOD 704>; - clock-names = "usbhs"; + can1: can@e6e88000 { + compatible = "renesas,can-r8a7790", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e88000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 915>, + <&cpg CPG_CORE R8A7790_CLK_RCAN>, <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 704>; + resets = <&cpg 915>; status = "disabled"; - - usb0: usb-channel@0 { - reg = <0>; - #phy-cells = <1>; - }; - usb2: usb-channel@2 { - reg = <2>; - #phy-cells = <1>; - }; }; vin0: video@e6ef0000 { @@ -1042,220 +988,267 @@ status = "disabled"; }; - vsp@fe920000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe920000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 130>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 130>; - }; + rcar_sound: sound@ec500000 { + /* + * #sound-dai-cells is required + * + * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; + * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; + */ + compatible = "renesas,rcar_sound-r8a7790", + "renesas,rcar_sound-gen2"; + reg = <0 0xec500000 0 0x1000>, /* SCU */ + <0 0xec5a0000 0 0x100>, /* ADG */ + <0 0xec540000 0 0x1000>, /* SSIU */ + <0 0xec541000 0 0x280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ + reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; - vsp@fe928000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe928000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 131>; + clocks = <&cpg CPG_MOD 1005>, + <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, + <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, + <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, + <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, + <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, + <&cpg CPG_MOD 1022>, <&cpg CPG_MOD 1023>, + <&cpg CPG_MOD 1024>, <&cpg CPG_MOD 1025>, + <&cpg CPG_MOD 1026>, <&cpg CPG_MOD 1027>, + <&cpg CPG_MOD 1028>, <&cpg CPG_MOD 1029>, + <&cpg CPG_MOD 1030>, <&cpg CPG_MOD 1031>, + <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, + <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, + <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, + <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, + <&cpg CPG_CORE R8A7790_CLK_M2>; + clock-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0", + "src.9", "src.8", "src.7", "src.6", + "src.5", "src.4", "src.3", "src.2", + "src.1", "src.0", + "ctu.0", "ctu.1", + "mix.0", "mix.1", + "dvc.0", "dvc.1", + "clk_a", "clk_b", "clk_c", "clk_i"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 131>; - }; + resets = <&cpg 1005>, + <&cpg 1006>, <&cpg 1007>, + <&cpg 1008>, <&cpg 1009>, + <&cpg 1010>, <&cpg 1011>, + <&cpg 1012>, <&cpg 1013>, + <&cpg 1014>, <&cpg 1015>; + reset-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0"; - vsp@fe930000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe930000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 128>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 128>; - }; + status = "disabled"; - vsp@fe938000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe938000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 127>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 127>; - }; + rcar_sound,dvc { + dvc0: dvc-0 { + dmas = <&audma1 0xbc>; + dma-names = "tx"; + }; + dvc1: dvc-1 { + dmas = <&audma1 0xbe>; + dma-names = "tx"; + }; + }; - du: display@feb00000 { - compatible = "renesas,du-r8a7790"; - reg = <0 0xfeb00000 0 0x70000>, - <0 0xfeb90000 0 0x1c>, - <0 0xfeb94000 0 0x1c>; - reg-names = "du", "lvds.0", "lvds.1"; - interrupts = , - , - ; - clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, - <&cpg CPG_MOD 722>, <&cpg CPG_MOD 726>, - <&cpg CPG_MOD 725>; - clock-names = "du.0", "du.1", "du.2", "lvds.0", - "lvds.1"; - status = "disabled"; + rcar_sound,mix { + mix0: mix-0 { }; + mix1: mix-1 { }; + }; - ports { - #address-cells = <1>; - #size-cells = <0>; + rcar_sound,ctu { + ctu00: ctu-0 { }; + ctu01: ctu-1 { }; + ctu02: ctu-2 { }; + ctu03: ctu-3 { }; + ctu10: ctu-4 { }; + ctu11: ctu-5 { }; + ctu12: ctu-6 { }; + ctu13: ctu-7 { }; + }; - port@0 { - reg = <0>; - du_out_rgb: endpoint { - }; + rcar_sound,src { + src0: src-0 { + interrupts = ; + dmas = <&audma0 0x85>, <&audma1 0x9a>; + dma-names = "rx", "tx"; }; - port@1 { - reg = <1>; - du_out_lvds0: endpoint { - }; + src1: src-1 { + interrupts = ; + dmas = <&audma0 0x87>, <&audma1 0x9c>; + dma-names = "rx", "tx"; }; - port@2 { - reg = <2>; - du_out_lvds1: endpoint { - }; + src2: src-2 { + interrupts = ; + dmas = <&audma0 0x89>, <&audma1 0x9e>; + dma-names = "rx", "tx"; + }; + src3: src-3 { + interrupts = ; + dmas = <&audma0 0x8b>, <&audma1 0xa0>; + dma-names = "rx", "tx"; + }; + src4: src-4 { + interrupts = ; + dmas = <&audma0 0x8d>, <&audma1 0xb0>; + dma-names = "rx", "tx"; + }; + src5: src-5 { + interrupts = ; + dmas = <&audma0 0x8f>, <&audma1 0xb2>; + dma-names = "rx", "tx"; + }; + src6: src-6 { + interrupts = ; + dmas = <&audma0 0x91>, <&audma1 0xb4>; + dma-names = "rx", "tx"; + }; + src7: src-7 { + interrupts = ; + dmas = <&audma0 0x93>, <&audma1 0xb6>; + dma-names = "rx", "tx"; + }; + src8: src-8 { + interrupts = ; + dmas = <&audma0 0x95>, <&audma1 0xb8>; + dma-names = "rx", "tx"; + }; + src9: src-9 { + interrupts = ; + dmas = <&audma0 0x97>, <&audma1 0xba>; + dma-names = "rx", "tx"; }; }; - }; - - can0: can@e6e80000 { - compatible = "renesas,can-r8a7790", - "renesas,rcar-gen2-can"; - reg = <0 0xe6e80000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 916>, - <&cpg CPG_CORE R8A7790_CLK_RCAN>, <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 916>; - status = "disabled"; - }; - - can1: can@e6e88000 { - compatible = "renesas,can-r8a7790", - "renesas,rcar-gen2-can"; - reg = <0 0xe6e88000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 915>, - <&cpg CPG_CORE R8A7790_CLK_RCAN>, <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 915>; - status = "disabled"; - }; - - jpu: jpeg-codec@fe980000 { - compatible = "renesas,jpu-r8a7790", - "renesas,rcar-gen2-jpu"; - reg = <0 0xfe980000 0 0x10300>; - interrupts = ; - clocks = <&cpg CPG_MOD 106>; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 106>; - }; - - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7790-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>, <&usb_extal_clk>; - clock-names = "extal", "usb_extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; - }; - - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; - }; - - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7790-rst"; - reg = <0 0xe6160000 0 0x0100>; - }; - - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7790-sysc"; - reg = <0 0xe6180000 0 0x0200>; - #power-domain-cells = <1>; - }; - - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7790", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 917>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - msiof0: spi@e6e20000 { - compatible = "renesas,msiof-r8a7790", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6e20000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 0>; - dmas = <&dmac0 0x51>, <&dmac0 0x52>, - <&dmac1 0x51>, <&dmac1 0x52>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 0>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - msiof1: spi@e6e10000 { - compatible = "renesas,msiof-r8a7790", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6e10000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 208>; - dmas = <&dmac0 0x55>, <&dmac0 0x56>, - <&dmac1 0x55>, <&dmac1 0x56>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 208>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; + rcar_sound,ssi { + ssi0: ssi-0 { + interrupts = ; + dmas = <&audma0 0x01>, <&audma1 0x02>, + <&audma0 0x15>, <&audma1 0x16>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi1: ssi-1 { + interrupts = ; + dmas = <&audma0 0x03>, <&audma1 0x04>, + <&audma0 0x49>, <&audma1 0x4a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi2: ssi-2 { + interrupts = ; + dmas = <&audma0 0x05>, <&audma1 0x06>, + <&audma0 0x63>, <&audma1 0x64>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi3: ssi-3 { + interrupts = ; + dmas = <&audma0 0x07>, <&audma1 0x08>, + <&audma0 0x6f>, <&audma1 0x70>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi4: ssi-4 { + interrupts = ; + dmas = <&audma0 0x09>, <&audma1 0x0a>, + <&audma0 0x71>, <&audma1 0x72>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi5: ssi-5 { + interrupts = ; + dmas = <&audma0 0x0b>, <&audma1 0x0c>, + <&audma0 0x73>, <&audma1 0x74>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi6: ssi-6 { + interrupts = ; + dmas = <&audma0 0x0d>, <&audma1 0x0e>, + <&audma0 0x75>, <&audma1 0x76>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi7: ssi-7 { + interrupts = ; + dmas = <&audma0 0x0f>, <&audma1 0x10>, + <&audma0 0x79>, <&audma1 0x7a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi8: ssi-8 { + interrupts = ; + dmas = <&audma0 0x11>, <&audma1 0x12>, + <&audma0 0x7b>, <&audma1 0x7c>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi9: ssi-9 { + interrupts = ; + dmas = <&audma0 0x13>, <&audma1 0x14>, + <&audma0 0x7d>, <&audma1 0x7e>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + }; }; - msiof2: spi@e6e00000 { - compatible = "renesas,msiof-r8a7790", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6e00000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 205>; - dmas = <&dmac0 0x41>, <&dmac0 0x42>, - <&dmac1 0x41>, <&dmac1 0x42>; - dma-names = "tx", "rx", "tx", "rx"; + audma0: dma-controller@ec700000 { + compatible = "renesas,dmac-r8a7790", + "renesas,rcar-dmac"; + reg = <0 0xec700000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 502>; + clock-names = "fck"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 205>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; + resets = <&cpg 502>; + #dma-cells = <1>; + dma-channels = <13>; }; - - msiof3: spi@e6c90000 { - compatible = "renesas,msiof-r8a7790", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6c90000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 215>; - dmas = <&dmac0 0x45>, <&dmac0 0x46>, - <&dmac1 0x45>, <&dmac1 0x46>; - dma-names = "tx", "rx", "tx", "rx"; + + audma1: dma-controller@ec720000 { + compatible = "renesas,dmac-r8a7790", + "renesas,rcar-dmac"; + reg = <0 0xec720000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 501>; + clock-names = "fck"; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 215>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; + resets = <&cpg 501>; + #dma-cells = <1>; + dma-channels = <13>; }; xhci: usb@ee000000 { @@ -1364,6 +1357,148 @@ }; }; + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7790", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; + + sdhi1: sd@ee120000 { + compatible = "renesas,sdhi-r8a7790", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee120000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 313>; + dmas = <&dmac0 0xc9>, <&dmac0 0xca>, + <&dmac1 0xc9>, <&dmac1 0xca>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 313>; + status = "disabled"; + }; + + sdhi2: sd@ee140000 { + compatible = "renesas,sdhi-r8a7790", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee140000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, + <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 312>; + status = "disabled"; + }; + + sdhi3: sd@ee160000 { + compatible = "renesas,sdhi-r8a7790", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee160000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, + <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 311>; + status = "disabled"; + }; + + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7790", + "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 315>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 315>; + reg-io-width = <4>; + status = "disabled"; + max-frequency = <97500000>; + }; + + mmcif1: mmc@ee220000 { + compatible = "renesas,mmcif-r8a7790", + "renesas,sh-mmcif"; + reg = <0 0xee220000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 305>; + dmas = <&dmac0 0xe1>, <&dmac0 0xe2>, + <&dmac1 0xe1>, <&dmac1 0xe2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 305>; + reg-io-width = <4>; + status = "disabled"; + max-frequency = <97500000>; + }; + + sata0: sata@ee300000 { + compatible = "renesas,sata-r8a7790", + "renesas,rcar-gen2-sata"; + reg = <0 0xee300000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 815>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 815>; + status = "disabled"; + }; + + sata1: sata@ee500000 { + compatible = "renesas,sata-r8a7790", + "renesas,rcar-gen2-sata"; + reg = <0 0xee500000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 814>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 814>; + status = "disabled"; + }; + + ether: ethernet@ee700000 { + compatible = "renesas,ether-r8a7790", + "renesas,rcar-gen2-ether"; + reg = <0 0xee700000 0 0x400>; + interrupts = ; + clocks = <&cpg CPG_MOD 813>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 813>; + phy-mode = "rmii"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 408>; + }; + pciec: pcie@fe000000 { compatible = "renesas,pcie-r8a7790", "renesas,pcie-rcar-gen2"; @@ -1392,261 +1527,126 @@ status = "disabled"; }; - rcar_sound: sound@ec500000 { - /* - * #sound-dai-cells is required - * - * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; - * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; - */ - compatible = "renesas,rcar_sound-r8a7790", - "renesas,rcar_sound-gen2"; - reg = <0 0xec500000 0 0x1000>, /* SCU */ - <0 0xec5a0000 0 0x100>, /* ADG */ - <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x280>, /* SSI */ - <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ - reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; + vsp@fe920000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe920000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 130>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 130>; + }; - clocks = <&cpg CPG_MOD 1005>, - <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, - <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, - <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, - <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, - <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, - <&cpg CPG_MOD 1022>, <&cpg CPG_MOD 1023>, - <&cpg CPG_MOD 1024>, <&cpg CPG_MOD 1025>, - <&cpg CPG_MOD 1026>, <&cpg CPG_MOD 1027>, - <&cpg CPG_MOD 1028>, <&cpg CPG_MOD 1029>, - <&cpg CPG_MOD 1030>, <&cpg CPG_MOD 1031>, - <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, - <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, - <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, - <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, - <&cpg CPG_CORE R8A7790_CLK_M2>; - clock-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", - "ssi.5", "ssi.4", "ssi.3", "ssi.2", - "ssi.1", "ssi.0", - "src.9", "src.8", "src.7", "src.6", - "src.5", "src.4", "src.3", "src.2", - "src.1", "src.0", - "ctu.0", "ctu.1", - "mix.0", "mix.1", - "dvc.0", "dvc.1", - "clk_a", "clk_b", "clk_c", "clk_i"; + vsp@fe928000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe928000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 131>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 131>; + }; + + vsp@fe930000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe930000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 128>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 128>; + }; + + vsp@fe938000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe938000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 127>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 127>; + }; + + jpu: jpeg-codec@fe980000 { + compatible = "renesas,jpu-r8a7790", + "renesas,rcar-gen2-jpu"; + reg = <0 0xfe980000 0 0x10300>; + interrupts = ; + clocks = <&cpg CPG_MOD 106>; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 1005>, - <&cpg 1006>, <&cpg 1007>, - <&cpg 1008>, <&cpg 1009>, - <&cpg 1010>, <&cpg 1011>, - <&cpg 1012>, <&cpg 1013>, - <&cpg 1014>, <&cpg 1015>; - reset-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", - "ssi.5", "ssi.4", "ssi.3", "ssi.2", - "ssi.1", "ssi.0"; + resets = <&cpg 106>; + }; + du: display@feb00000 { + compatible = "renesas,du-r8a7790"; + reg = <0 0xfeb00000 0 0x70000>, + <0 0xfeb90000 0 0x1c>, + <0 0xfeb94000 0 0x1c>; + reg-names = "du", "lvds.0", "lvds.1"; + interrupts = , + , + ; + clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, + <&cpg CPG_MOD 722>, <&cpg CPG_MOD 726>, + <&cpg CPG_MOD 725>; + clock-names = "du.0", "du.1", "du.2", "lvds.0", + "lvds.1"; status = "disabled"; - rcar_sound,dvc { - dvc0: dvc-0 { - dmas = <&audma1 0xbc>; - dma-names = "tx"; - }; - dvc1: dvc-1 { - dmas = <&audma1 0xbe>; - dma-names = "tx"; - }; - }; - - rcar_sound,mix { - mix0: mix-0 { }; - mix1: mix-1 { }; - }; - - rcar_sound,ctu { - ctu00: ctu-0 { }; - ctu01: ctu-1 { }; - ctu02: ctu-2 { }; - ctu03: ctu-3 { }; - ctu10: ctu-4 { }; - ctu11: ctu-5 { }; - ctu12: ctu-6 { }; - ctu13: ctu-7 { }; - }; - - rcar_sound,src { - src0: src-0 { - interrupts = ; - dmas = <&audma0 0x85>, <&audma1 0x9a>; - dma-names = "rx", "tx"; - }; - src1: src-1 { - interrupts = ; - dmas = <&audma0 0x87>, <&audma1 0x9c>; - dma-names = "rx", "tx"; - }; - src2: src-2 { - interrupts = ; - dmas = <&audma0 0x89>, <&audma1 0x9e>; - dma-names = "rx", "tx"; - }; - src3: src-3 { - interrupts = ; - dmas = <&audma0 0x8b>, <&audma1 0xa0>; - dma-names = "rx", "tx"; - }; - src4: src-4 { - interrupts = ; - dmas = <&audma0 0x8d>, <&audma1 0xb0>; - dma-names = "rx", "tx"; - }; - src5: src-5 { - interrupts = ; - dmas = <&audma0 0x8f>, <&audma1 0xb2>; - dma-names = "rx", "tx"; - }; - src6: src-6 { - interrupts = ; - dmas = <&audma0 0x91>, <&audma1 0xb4>; - dma-names = "rx", "tx"; - }; - src7: src-7 { - interrupts = ; - dmas = <&audma0 0x93>, <&audma1 0xb6>; - dma-names = "rx", "tx"; - }; - src8: src-8 { - interrupts = ; - dmas = <&audma0 0x95>, <&audma1 0xb8>; - dma-names = "rx", "tx"; - }; - src9: src-9 { - interrupts = ; - dmas = <&audma0 0x97>, <&audma1 0xba>; - dma-names = "rx", "tx"; - }; - }; + ports { + #address-cells = <1>; + #size-cells = <0>; - rcar_sound,ssi { - ssi0: ssi-0 { - interrupts = ; - dmas = <&audma0 0x01>, <&audma1 0x02>, - <&audma0 0x15>, <&audma1 0x16>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi1: ssi-1 { - interrupts = ; - dmas = <&audma0 0x03>, <&audma1 0x04>, - <&audma0 0x49>, <&audma1 0x4a>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi2: ssi-2 { - interrupts = ; - dmas = <&audma0 0x05>, <&audma1 0x06>, - <&audma0 0x63>, <&audma1 0x64>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi3: ssi-3 { - interrupts = ; - dmas = <&audma0 0x07>, <&audma1 0x08>, - <&audma0 0x6f>, <&audma1 0x70>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi4: ssi-4 { - interrupts = ; - dmas = <&audma0 0x09>, <&audma1 0x0a>, - <&audma0 0x71>, <&audma1 0x72>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi5: ssi-5 { - interrupts = ; - dmas = <&audma0 0x0b>, <&audma1 0x0c>, - <&audma0 0x73>, <&audma1 0x74>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi6: ssi-6 { - interrupts = ; - dmas = <&audma0 0x0d>, <&audma1 0x0e>, - <&audma0 0x75>, <&audma1 0x76>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi7: ssi-7 { - interrupts = ; - dmas = <&audma0 0x0f>, <&audma1 0x10>, - <&audma0 0x79>, <&audma1 0x7a>; - dma-names = "rx", "tx", "rxu", "txu"; + port@0 { + reg = <0>; + du_out_rgb: endpoint { + }; }; - ssi8: ssi-8 { - interrupts = ; - dmas = <&audma0 0x11>, <&audma1 0x12>, - <&audma0 0x7b>, <&audma1 0x7c>; - dma-names = "rx", "tx", "rxu", "txu"; + port@1 { + reg = <1>; + du_out_lvds0: endpoint { + }; }; - ssi9: ssi-9 { - interrupts = ; - dmas = <&audma0 0x13>, <&audma1 0x14>, - <&audma0 0x7d>, <&audma1 0x7e>; - dma-names = "rx", "tx", "rxu", "txu"; + port@2 { + reg = <2>; + du_out_lvds1: endpoint { + }; }; }; }; - ipmmu_sy0: mmu@e6280000 { - compatible = "renesas,ipmmu-r8a7790", - "renesas,ipmmu-vmsa"; - reg = <0 0xe6280000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_sy1: mmu@e6290000 { - compatible = "renesas,ipmmu-r8a7790", - "renesas,ipmmu-vmsa"; - reg = <0 0xe6290000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; }; - ipmmu_ds: mmu@e6740000 { - compatible = "renesas,ipmmu-r8a7790", - "renesas,ipmmu-vmsa"; - reg = <0 0xe6740000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; + cmt0: timer@ffca0000 { + compatible = "renesas,r8a7790-cmt0", + "renesas,rcar-gen2-cmt0"; + reg = <0 0xffca0000 0 0x1004>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 124>; + clock-names = "fck"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 124>; - ipmmu_mp: mmu@ec680000 { - compatible = "renesas,ipmmu-r8a7790", - "renesas,ipmmu-vmsa"; - reg = <0 0xec680000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; status = "disabled"; }; - ipmmu_mx: mmu@fe951000 { - compatible = "renesas,ipmmu-r8a7790", - "renesas,ipmmu-vmsa"; - reg = <0 0xfe951000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; + cmt1: timer@e6130000 { + compatible = "renesas,r8a7790-cmt1", + "renesas,rcar-gen2-cmt1"; + reg = <0 0xe6130000 0 0x1004>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 329>; + clock-names = "fck"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 329>; - ipmmu_rt: mmu@ffc80000 { - compatible = "renesas,ipmmu-r8a7790", - "renesas,ipmmu-vmsa"; - reg = <0 0xffc80000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; status = "disabled"; }; }; -- cgit v1.3-14-g43fede From 003d2d68b2a6fbf242506d4d579f3b5506224c88 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:05 +0100 Subject: ARM: dts: r8a7790: sort subnodes of root node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort subnodes of root node to aid maintenance. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7790.dtsi | 104 ++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index 54c5a2d7ea89..3bbcc0b93f1c 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -40,6 +40,35 @@ vin3 = &vin3; }; + /* + * The external audio clocks are configured as 0 Hz fixed frequency + * clocks by default. + * Boards that provide audio clocks should override them. + */ + audio_clk_a: audio_clk_a { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + audio_clk_b: audio_clk_b { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + audio_clk_c: audio_clk_c { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + /* External CAN clock */ + can_clk: can { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + cpus { #address-cells = <1>; #size-cells = <0>; @@ -158,6 +187,29 @@ }; }; + /* External root clock */ + extal_clk: extal { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + + /* External PCIe clock - can be overridden by the board */ + pcie_bus_clk: pcie_bus { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + /* External SCIF clock */ + scif_clk: scif { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + soc { compatible = "simple-bus"; interrupt-parent = <&gic>; @@ -1678,62 +1730,10 @@ <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>; }; - /* External root clock */ - extal_clk: extal { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; - - /* External PCIe clock - can be overridden by the board */ - pcie_bus_clk: pcie_bus { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - - /* - * The external audio clocks are configured as 0 Hz fixed frequency - * clocks by default. - * Boards that provide audio clocks should override them. - */ - audio_clk_a: audio_clk_a { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - audio_clk_b: audio_clk_b { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - audio_clk_c: audio_clk_c { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - - /* External SCIF clock */ - scif_clk: scif { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; - /* External USB clock - can be overridden by the board */ usb_extal_clk: usb_extal { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <48000000>; }; - - /* External CAN clock */ - can_clk: can { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; }; -- cgit v1.3-14-g43fede From 5025c78b3b69914d4da8e75cf2390329cf04ca95 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:06 +0100 Subject: ARM: dts: r8a7791: consistently use single space after = MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consistently use a single space after a =. This patch removes instances where a tab or multiple spaces are used instead. It also avoids running over 80 columns in width in one of the lines where whitespace is updated. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7791.dtsi | 75 +++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index 8266a9b7cafd..38a9b8cb736d 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -237,9 +237,9 @@ }; thermal: thermal@e61f0000 { - compatible = "renesas,thermal-r8a7791", - "renesas,rcar-gen2-thermal", - "renesas,rcar-thermal"; + compatible = "renesas,thermal-r8a7791", + "renesas,rcar-gen2-thermal", + "renesas,rcar-thermal"; reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; interrupts = ; clocks = <&cpg CPG_MOD 522>; @@ -375,20 +375,20 @@ audma0: dma-controller@ec700000 { compatible = "renesas,dmac-r8a7791", "renesas,rcar-dmac"; reg = <0 0xec700000 0 0x10000>; - interrupts = ; + interrupts = ; interrupt-names = "error", "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7", @@ -405,20 +405,20 @@ audma1: dma-controller@ec720000 { compatible = "renesas,dmac-r8a7791", "renesas,rcar-dmac"; reg = <0 0xec720000 0 0x10000>; - interrupts = ; + interrupts = ; interrupt-names = "error", "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7", @@ -1487,12 +1487,13 @@ * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; */ - compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2"; - reg = <0 0xec500000 0 0x1000>, /* SCU */ - <0 0xec5a0000 0 0x100>, /* ADG */ - <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x280>, /* SSI */ - <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ + compatible = "renesas,rcar_sound-r8a7791", + "renesas,rcar_sound-gen2"; + reg = <0 0xec500000 0 0x1000>, /* SCU */ + <0 0xec5a0000 0 0x100>, /* ADG */ + <0 0xec540000 0 0x1000>, /* SSIU */ + <0 0xec541000 0 0x280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; clocks = <&cpg CPG_MOD 1005>, -- cgit v1.3-14-g43fede From bb21803ea44076525a97e8a247acfa442b2bf115 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:07 +0100 Subject: ARM: dts: r8a7791: add soc node Add soc node to represent the bus and move all nodes with a base address into this node. This is consistent with handling of R-Car Gen3, RZ/G1, and R-Car V2H (R8A77920) SoCs upstream. It is intended to migrate other R-Car Gen2 SoCs to this scheme. The ordering is derived from simply moving each node with an address up to before any nodes without a base address that occur before the soc node. To improve maintainability follow-up patches will sort subnodes of both the new soc node and the root node. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7791.dtsi | 2909 ++++++++++++++++++++-------------------- 1 file changed, 1489 insertions(+), 1420 deletions(-) diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index 38a9b8cb736d..de2af2199731 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -17,7 +17,6 @@ / { compatible = "renesas,r8a7791"; - interrupt-parent = <&gic>; #address-cells = <2>; #size-cells = <2>; @@ -102,1066 +101,1579 @@ }; }; - apmu@e6152000 { - compatible = "renesas,r8a7791-apmu", "renesas,apmu"; - reg = <0 0xe6152000 0 0x188>; - cpus = <&cpu0 &cpu1>; - }; + soc { + compatible = "simple-bus"; + interrupt-parent = <&gic>; - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <0>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, - <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, - <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gpio0: gpio@e6050000 { + compatible = "renesas,gpio-r8a7791", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6050000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 0 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 912>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 912>; + }; - gpio0: gpio@e6050000 { - compatible = "renesas,gpio-r8a7791", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6050000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 0 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 912>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 912>; - }; + gpio1: gpio@e6051000 { + compatible = "renesas,gpio-r8a7791", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6051000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 32 26>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 911>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 911>; + }; - gpio1: gpio@e6051000 { - compatible = "renesas,gpio-r8a7791", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6051000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 32 26>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 911>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 911>; - }; + gpio2: gpio@e6052000 { + compatible = "renesas,gpio-r8a7791", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6052000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 64 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 910>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 910>; + }; - gpio2: gpio@e6052000 { - compatible = "renesas,gpio-r8a7791", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6052000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 64 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 910>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 910>; - }; + gpio3: gpio@e6053000 { + compatible = "renesas,gpio-r8a7791", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6053000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 96 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 909>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 909>; + }; - gpio3: gpio@e6053000 { - compatible = "renesas,gpio-r8a7791", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6053000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 96 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 909>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 909>; - }; + gpio4: gpio@e6054000 { + compatible = "renesas,gpio-r8a7791", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6054000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 128 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 908>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 908>; + }; - gpio4: gpio@e6054000 { - compatible = "renesas,gpio-r8a7791", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6054000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 128 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 908>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 908>; - }; + gpio5: gpio@e6055000 { + compatible = "renesas,gpio-r8a7791", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6055000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 160 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 907>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 907>; + }; - gpio5: gpio@e6055000 { - compatible = "renesas,gpio-r8a7791", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6055000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 160 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 907>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 907>; - }; + gpio6: gpio@e6055400 { + compatible = "renesas,gpio-r8a7791", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6055400 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 192 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 905>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 905>; + }; - gpio6: gpio@e6055400 { - compatible = "renesas,gpio-r8a7791", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6055400 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 192 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 905>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 905>; - }; + gpio7: gpio@e6055800 { + compatible = "renesas,gpio-r8a7791", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6055800 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 224 26>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 904>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 904>; + }; - gpio7: gpio@e6055800 { - compatible = "renesas,gpio-r8a7791", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6055800 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 224 26>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 904>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 904>; - }; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7791"; + reg = <0 0xe6060000 0 0x250>; + }; - thermal: thermal@e61f0000 { - compatible = "renesas,thermal-r8a7791", - "renesas,rcar-gen2-thermal", - "renesas,rcar-thermal"; - reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; - interrupts = ; - clocks = <&cpg CPG_MOD 522>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 522>; - #thermal-sensor-cells = <0>; - }; + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7791-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&usb_extal_clk>; + clock-names = "extal", "usb_extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; - timer { - compatible = "arm,armv7-timer"; - interrupts = , - , - , - ; - }; + apmu@e6152000 { + compatible = "renesas,r8a7791-apmu", "renesas,apmu"; + reg = <0 0xe6152000 0 0x188>; + cpus = <&cpu0 &cpu1>; + }; - cmt0: timer@ffca0000 { - compatible = "renesas,r8a7791-cmt0", "renesas,rcar-gen2-cmt0"; - reg = <0 0xffca0000 0 0x1004>; - interrupts = , - ; - clocks = <&cpg CPG_MOD 124>; - clock-names = "fck"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 124>; - - status = "disabled"; - }; + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7791-rst"; + reg = <0 0xe6160000 0 0x0100>; + }; - cmt1: timer@e6130000 { - compatible = "renesas,r8a7791-cmt1", "renesas,rcar-gen2-cmt1"; - reg = <0 0xe6130000 0 0x1004>; - interrupts = , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 329>; - clock-names = "fck"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 329>; - - status = "disabled"; - }; + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7791-sysc"; + reg = <0 0xe6180000 0 0x0200>; + #power-domain-cells = <1>; + }; - irqc0: interrupt-controller@e61c0000 { - compatible = "renesas,irqc-r8a7791", "renesas,irqc"; - #interrupt-cells = <2>; - interrupt-controller; - reg = <0 0xe61c0000 0 0x200>; - interrupts = , - , - , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 407>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 407>; - }; + irqc0: interrupt-controller@e61c0000 { + compatible = "renesas,irqc-r8a7791", "renesas,irqc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0 0xe61c0000 0 0x200>; + interrupts = , + , + , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 407>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 407>; + }; - dmac0: dma-controller@e6700000 { - compatible = "renesas,dmac-r8a7791", "renesas,rcar-dmac"; - reg = <0 0xe6700000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 219>; - clock-names = "fck"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 219>; - #dma-cells = <1>; - dma-channels = <15>; - }; + thermal: thermal@e61f0000 { + compatible = "renesas,thermal-r8a7791", + "renesas,rcar-gen2-thermal", + "renesas,rcar-thermal"; + reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; + interrupts = ; + clocks = <&cpg CPG_MOD 522>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 522>; + #thermal-sensor-cells = <0>; + }; - dmac1: dma-controller@e6720000 { - compatible = "renesas,dmac-r8a7791", "renesas,rcar-dmac"; - reg = <0 0xe6720000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 218>; - clock-names = "fck"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 218>; - #dma-cells = <1>; - dma-channels = <15>; - }; + ipmmu_sy0: mmu@e6280000 { + compatible = "renesas,ipmmu-r8a7791", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6280000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; - audma0: dma-controller@ec700000 { - compatible = "renesas,dmac-r8a7791", "renesas,rcar-dmac"; - reg = <0 0xec700000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 502>; - clock-names = "fck"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 502>; - #dma-cells = <1>; - dma-channels = <13>; - }; + ipmmu_sy1: mmu@e6290000 { + compatible = "renesas,ipmmu-r8a7791", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6290000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; - audma1: dma-controller@ec720000 { - compatible = "renesas,dmac-r8a7791", "renesas,rcar-dmac"; - reg = <0 0xec720000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 501>; - clock-names = "fck"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 501>; - #dma-cells = <1>; - dma-channels = <13>; - }; + ipmmu_ds: mmu@e6740000 { + compatible = "renesas,ipmmu-r8a7791", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6740000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; - usb_dmac0: dma-controller@e65a0000 { - compatible = "renesas,r8a7791-usb-dmac", "renesas,usb-dmac"; - reg = <0 0xe65a0000 0 0x100>; - interrupts = ; - interrupt-names = "ch0", "ch1"; - clocks = <&cpg CPG_MOD 330>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 330>; - #dma-cells = <1>; - dma-channels = <2>; - }; + ipmmu_mp: mmu@ec680000 { + compatible = "renesas,ipmmu-r8a7791", + "renesas,ipmmu-vmsa"; + reg = <0 0xec680000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; - usb_dmac1: dma-controller@e65b0000 { - compatible = "renesas,r8a7791-usb-dmac", "renesas,usb-dmac"; - reg = <0 0xe65b0000 0 0x100>; - interrupts = ; - interrupt-names = "ch0", "ch1"; - clocks = <&cpg CPG_MOD 331>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 331>; - #dma-cells = <1>; - dma-channels = <2>; - }; + ipmmu_mx: mmu@fe951000 { + compatible = "renesas,ipmmu-r8a7791", + "renesas,ipmmu-vmsa"; + reg = <0 0xfe951000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; - /* The memory map in the User's Manual maps the cores to bus numbers */ - i2c0: i2c@e6508000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7791", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6508000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 931>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 931>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + ipmmu_rt: mmu@ffc80000 { + compatible = "renesas,ipmmu-r8a7791", + "renesas,ipmmu-vmsa"; + reg = <0 0xffc80000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; - i2c1: i2c@e6518000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7791", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6518000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 930>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 930>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + ipmmu_gp: mmu@e62a0000 { + compatible = "renesas,ipmmu-r8a7791", + "renesas,ipmmu-vmsa"; + reg = <0 0xe62a0000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; - i2c2: i2c@e6530000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7791", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6530000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 929>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 929>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; + }; - i2c3: i2c@e6540000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7791", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6540000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 928>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 928>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; - i2c4: i2c@e6520000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7791", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6520000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 927>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 927>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; + }; - i2c5: i2c@e6528000 { - /* doesn't need pinmux */ - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7791", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6528000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 925>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 925>; - i2c-scl-internal-delay-ns = <110>; - status = "disabled"; - }; + /* The memory map in the User's Manual maps the cores to + * bus numbers + */ + i2c0: i2c@e6508000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7791", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6508000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 931>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 931>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c6: i2c@e60b0000 { - /* doesn't need pinmux */ - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7791", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe60b0000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 926>; - dmas = <&dmac0 0x77>, <&dmac0 0x78>, - <&dmac1 0x77>, <&dmac1 0x78>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 926>; - status = "disabled"; - }; + i2c1: i2c@e6518000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7791", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6518000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 930>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 930>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c7: i2c@e6500000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7791", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6500000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 318>; - dmas = <&dmac0 0x61>, <&dmac0 0x62>, - <&dmac1 0x61>, <&dmac1 0x62>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 318>; - status = "disabled"; - }; + i2c2: i2c@e6530000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7791", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6530000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 929>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 929>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c8: i2c@e6510000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7791", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6510000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 323>; - dmas = <&dmac0 0x65>, <&dmac0 0x66>, - <&dmac1 0x65>, <&dmac1 0x66>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 323>; - status = "disabled"; - }; + i2c3: i2c@e6540000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7791", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6540000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 928>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 928>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7791"; - reg = <0 0xe6060000 0 0x250>; - }; + i2c4: i2c@e6520000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7791", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6520000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 927>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 927>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - mmcif0: mmc@ee200000 { - compatible = "renesas,mmcif-r8a7791", "renesas,sh-mmcif"; - reg = <0 0xee200000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 315>; - dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, - <&dmac1 0xd1>, <&dmac1 0xd2>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 315>; - reg-io-width = <4>; - status = "disabled"; - max-frequency = <97500000>; - }; + i2c5: i2c@e6528000 { + /* doesn't need pinmux */ + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7791", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6528000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 925>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 925>; + i2c-scl-internal-delay-ns = <110>; + status = "disabled"; + }; - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7791", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 314>; - status = "disabled"; - }; + i2c6: i2c@e60b0000 { + /* doesn't need pinmux */ + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7791", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe60b0000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 926>; + dmas = <&dmac0 0x77>, <&dmac0 0x78>, + <&dmac1 0x77>, <&dmac1 0x78>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 926>; + status = "disabled"; + }; - sdhi1: sd@ee140000 { - compatible = "renesas,sdhi-r8a7791", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee140000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, - <&dmac1 0xc1>, <&dmac1 0xc2>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 312>; - status = "disabled"; - }; + i2c7: i2c@e6500000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7791", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6500000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 318>; + dmas = <&dmac0 0x61>, <&dmac0 0x62>, + <&dmac1 0x61>, <&dmac1 0x62>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 318>; + status = "disabled"; + }; - sdhi2: sd@ee160000 { - compatible = "renesas,sdhi-r8a7791", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee160000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, - <&dmac1 0xd3>, <&dmac1 0xd4>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 311>; - status = "disabled"; - }; + i2c8: i2c@e6510000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7791", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6510000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 323>; + dmas = <&dmac0 0x65>, <&dmac0 0x66>, + <&dmac1 0x65>, <&dmac1 0x66>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 323>; + status = "disabled"; + }; - scifa0: serial@e6c40000 { - compatible = "renesas,scifa-r8a7791", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c40000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 204>; - clock-names = "fck"; - dmas = <&dmac0 0x21>, <&dmac0 0x22>, - <&dmac1 0x21>, <&dmac1 0x22>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 204>; - status = "disabled"; - }; + hsusb: usb@e6590000 { + compatible = "renesas,usbhs-r8a7791", + "renesas,rcar-gen2-usbhs"; + reg = <0 0xe6590000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 704>; + dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, + <&usb_dmac1 0>, <&usb_dmac1 1>; + dma-names = "ch0", "ch1", "ch2", "ch3"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 704>; + renesas,buswait = <4>; + phys = <&usb0 1>; + phy-names = "usb"; + status = "disabled"; + }; - scifa1: serial@e6c50000 { - compatible = "renesas,scifa-r8a7791", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c50000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 203>; - clock-names = "fck"; - dmas = <&dmac0 0x25>, <&dmac0 0x26>, - <&dmac1 0x25>, <&dmac1 0x26>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 203>; - status = "disabled"; - }; + usbphy: usb-phy@e6590100 { + compatible = "renesas,usb-phy-r8a7791", + "renesas,rcar-gen2-usb-phy"; + reg = <0 0xe6590100 0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&cpg CPG_MOD 704>; + clock-names = "usbhs"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 704>; + status = "disabled"; - scifa2: serial@e6c60000 { - compatible = "renesas,scifa-r8a7791", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 202>; - clock-names = "fck"; - dmas = <&dmac0 0x27>, <&dmac0 0x28>, - <&dmac1 0x27>, <&dmac1 0x28>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 202>; - status = "disabled"; - }; + usb0: usb-channel@0 { + reg = <0>; + #phy-cells = <1>; + }; + usb2: usb-channel@2 { + reg = <2>; + #phy-cells = <1>; + }; + }; - scifa3: serial@e6c70000 { - compatible = "renesas,scifa-r8a7791", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c70000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1106>; - clock-names = "fck"; - dmas = <&dmac0 0x1b>, <&dmac0 0x1c>, - <&dmac1 0x1b>, <&dmac1 0x1c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 1106>; - status = "disabled"; - }; + usb_dmac0: dma-controller@e65a0000 { + compatible = "renesas,r8a7791-usb-dmac", + "renesas,usb-dmac"; + reg = <0 0xe65a0000 0 0x100>; + interrupts = ; + interrupt-names = "ch0", "ch1"; + clocks = <&cpg CPG_MOD 330>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 330>; + #dma-cells = <1>; + dma-channels = <2>; + }; - scifa4: serial@e6c78000 { - compatible = "renesas,scifa-r8a7791", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c78000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1107>; - clock-names = "fck"; - dmas = <&dmac0 0x1f>, <&dmac0 0x20>, - <&dmac1 0x1f>, <&dmac1 0x20>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 1107>; - status = "disabled"; - }; + usb_dmac1: dma-controller@e65b0000 { + compatible = "renesas,r8a7791-usb-dmac", + "renesas,usb-dmac"; + reg = <0 0xe65b0000 0 0x100>; + interrupts = ; + interrupt-names = "ch0", "ch1"; + clocks = <&cpg CPG_MOD 331>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 331>; + #dma-cells = <1>; + dma-channels = <2>; + }; - scifa5: serial@e6c80000 { - compatible = "renesas,scifa-r8a7791", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c80000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1108>; - clock-names = "fck"; - dmas = <&dmac0 0x23>, <&dmac0 0x24>, - <&dmac1 0x23>, <&dmac1 0x24>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 1108>; - status = "disabled"; - }; + dmac0: dma-controller@e6700000 { + compatible = "renesas,dmac-r8a7791", + "renesas,rcar-dmac"; + reg = <0 0xe6700000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 219>; + clock-names = "fck"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 219>; + #dma-cells = <1>; + dma-channels = <15>; + }; - scifb0: serial@e6c20000 { - compatible = "renesas,scifb-r8a7791", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c20000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 206>; - clock-names = "fck"; - dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, - <&dmac1 0x3d>, <&dmac1 0x3e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 206>; - status = "disabled"; - }; + dmac1: dma-controller@e6720000 { + compatible = "renesas,dmac-r8a7791", + "renesas,rcar-dmac"; + reg = <0 0xe6720000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 218>; + clock-names = "fck"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 218>; + #dma-cells = <1>; + dma-channels = <15>; + }; - scifb1: serial@e6c30000 { - compatible = "renesas,scifb-r8a7791", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c30000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 207>; - clock-names = "fck"; - dmas = <&dmac0 0x19>, <&dmac0 0x1a>, - <&dmac1 0x19>, <&dmac1 0x1a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 207>; - status = "disabled"; - }; + avb: ethernet@e6800000 { + compatible = "renesas,etheravb-r8a7791", + "renesas,etheravb-rcar-gen2"; + reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; + interrupts = ; + clocks = <&cpg CPG_MOD 812>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 812>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - scifb2: serial@e6ce0000 { - compatible = "renesas,scifb-r8a7791", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6ce0000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 216>; - clock-names = "fck"; - dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, - <&dmac1 0x1d>, <&dmac1 0x1e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 216>; - status = "disabled"; - }; + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7791", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 917>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - scif0: serial@e6e60000 { - compatible = "renesas,scif-r8a7791", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x29>, <&dmac0 0x2a>, - <&dmac1 0x29>, <&dmac1 0x2a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 721>; - status = "disabled"; - }; + scifa0: serial@e6c40000 { + compatible = "renesas,scifa-r8a7791", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c40000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 204>; + clock-names = "fck"; + dmas = <&dmac0 0x21>, <&dmac0 0x22>, + <&dmac1 0x21>, <&dmac1 0x22>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 204>; + status = "disabled"; + }; - scif1: serial@e6e68000 { - compatible = "renesas,scif-r8a7791", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e68000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 720>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, - <&dmac1 0x2d>, <&dmac1 0x2e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 720>; - status = "disabled"; - }; + scifa1: serial@e6c50000 { + compatible = "renesas,scifa-r8a7791", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c50000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 203>; + clock-names = "fck"; + dmas = <&dmac0 0x25>, <&dmac0 0x26>, + <&dmac1 0x25>, <&dmac1 0x26>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 203>; + status = "disabled"; + }; - adc: adc@e6e54000 { - compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc"; - reg = <0 0xe6e54000 0 64>; - clocks = <&cpg CPG_MOD 901>; - clock-names = "fck"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 901>; - status = "disabled"; - }; + scifa2: serial@e6c60000 { + compatible = "renesas,scifa-r8a7791", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 202>; + clock-names = "fck"; + dmas = <&dmac0 0x27>, <&dmac0 0x28>, + <&dmac1 0x27>, <&dmac1 0x28>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 202>; + status = "disabled"; + }; - scif2: serial@e6e58000 { - compatible = "renesas,scif-r8a7791", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e58000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 719>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, - <&dmac1 0x2b>, <&dmac1 0x2c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 719>; - status = "disabled"; - }; + scifa3: serial@e6c70000 { + compatible = "renesas,scifa-r8a7791", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c70000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1106>; + clock-names = "fck"; + dmas = <&dmac0 0x1b>, <&dmac0 0x1c>, + <&dmac1 0x1b>, <&dmac1 0x1c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 1106>; + status = "disabled"; + }; - scif3: serial@e6ea8000 { - compatible = "renesas,scif-r8a7791", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6ea8000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 718>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2f>, <&dmac0 0x30>, - <&dmac1 0x2f>, <&dmac1 0x30>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 718>; - status = "disabled"; - }; + scifa4: serial@e6c78000 { + compatible = "renesas,scifa-r8a7791", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c78000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1107>; + clock-names = "fck"; + dmas = <&dmac0 0x1f>, <&dmac0 0x20>, + <&dmac1 0x1f>, <&dmac1 0x20>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 1107>; + status = "disabled"; + }; - scif4: serial@e6ee0000 { - compatible = "renesas,scif-r8a7791", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6ee0000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 715>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0xfb>, <&dmac0 0xfc>, - <&dmac1 0xfb>, <&dmac1 0xfc>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 715>; - status = "disabled"; - }; + scifa5: serial@e6c80000 { + compatible = "renesas,scifa-r8a7791", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c80000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1108>; + clock-names = "fck"; + dmas = <&dmac0 0x23>, <&dmac0 0x24>, + <&dmac1 0x23>, <&dmac1 0x24>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 1108>; + status = "disabled"; + }; - scif5: serial@e6ee8000 { - compatible = "renesas,scif-r8a7791", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6ee8000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 714>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0xfd>, <&dmac0 0xfe>, - <&dmac1 0xfd>, <&dmac1 0xfe>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 714>; - status = "disabled"; - }; + scifb0: serial@e6c20000 { + compatible = "renesas,scifb-r8a7791", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c20000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 206>; + clock-names = "fck"; + dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, + <&dmac1 0x3d>, <&dmac1 0x3e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 206>; + status = "disabled"; + }; - hscif0: serial@e62c0000 { - compatible = "renesas,hscif-r8a7791", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c0000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 717>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x39>, <&dmac0 0x3a>, - <&dmac1 0x39>, <&dmac1 0x3a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 717>; - status = "disabled"; - }; + scifb1: serial@e6c30000 { + compatible = "renesas,scifb-r8a7791", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c30000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 207>; + clock-names = "fck"; + dmas = <&dmac0 0x19>, <&dmac0 0x1a>, + <&dmac1 0x19>, <&dmac1 0x1a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 207>; + status = "disabled"; + }; - hscif1: serial@e62c8000 { - compatible = "renesas,hscif-r8a7791", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c8000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 716>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, - <&dmac1 0x4d>, <&dmac1 0x4e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 716>; - status = "disabled"; - }; + scifb2: serial@e6ce0000 { + compatible = "renesas,scifb-r8a7791", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6ce0000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 216>; + clock-names = "fck"; + dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, + <&dmac1 0x1d>, <&dmac1 0x1e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 216>; + status = "disabled"; + }; - hscif2: serial@e62d0000 { - compatible = "renesas,hscif-r8a7791", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62d0000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 713>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, - <&dmac1 0x3b>, <&dmac1 0x3c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 713>; - status = "disabled"; - }; + scif0: serial@e6e60000 { + compatible = "renesas,scif-r8a7791", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6e60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x29>, <&dmac0 0x2a>, + <&dmac1 0x29>, <&dmac1 0x2a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 721>; + status = "disabled"; + }; - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; + scif1: serial@e6e68000 { + compatible = "renesas,scif-r8a7791", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6e68000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 720>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, + <&dmac1 0x2d>, <&dmac1 0x2e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 720>; + status = "disabled"; + }; - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; + scif2: serial@e6e58000 { + compatible = "renesas,scif-r8a7791", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6e58000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 719>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, + <&dmac1 0x2b>, <&dmac1 0x2c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 719>; + status = "disabled"; + }; - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; + scif3: serial@e6ea8000 { + compatible = "renesas,scif-r8a7791", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ea8000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 718>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2f>, <&dmac0 0x30>, + <&dmac1 0x2f>, <&dmac1 0x30>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 718>; + status = "disabled"; }; - }; - ether: ethernet@ee700000 { - compatible = "renesas,ether-r8a7791", - "renesas,rcar-gen2-ether"; - reg = <0 0xee700000 0 0x400>; - interrupts = ; - clocks = <&cpg CPG_MOD 813>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 813>; - phy-mode = "rmii"; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + scif4: serial@e6ee0000 { + compatible = "renesas,scif-r8a7791", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ee0000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 715>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0xfb>, <&dmac0 0xfc>, + <&dmac1 0xfb>, <&dmac1 0xfc>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 715>; + status = "disabled"; + }; - avb: ethernet@e6800000 { - compatible = "renesas,etheravb-r8a7791", - "renesas,etheravb-rcar-gen2"; - reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; - interrupts = ; - clocks = <&cpg CPG_MOD 812>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 812>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + scif5: serial@e6ee8000 { + compatible = "renesas,scif-r8a7791", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ee8000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 714>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0xfd>, <&dmac0 0xfe>, + <&dmac1 0xfd>, <&dmac1 0xfe>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 714>; + status = "disabled"; + }; - sata0: sata@ee300000 { - compatible = "renesas,sata-r8a7791", "renesas,rcar-gen2-sata"; - reg = <0 0xee300000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 815>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 815>; - status = "disabled"; - }; + hscif0: serial@e62c0000 { + compatible = "renesas,hscif-r8a7791", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c0000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 717>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x39>, <&dmac0 0x3a>, + <&dmac1 0x39>, <&dmac1 0x3a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 717>; + status = "disabled"; + }; - sata1: sata@ee500000 { - compatible = "renesas,sata-r8a7791", "renesas,rcar-gen2-sata"; - reg = <0 0xee500000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 814>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 814>; - status = "disabled"; - }; + hscif1: serial@e62c8000 { + compatible = "renesas,hscif-r8a7791", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c8000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 716>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, + <&dmac1 0x4d>, <&dmac1 0x4e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 716>; + status = "disabled"; + }; - hsusb: usb@e6590000 { - compatible = "renesas,usbhs-r8a7791", "renesas,rcar-gen2-usbhs"; - reg = <0 0xe6590000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 704>; - dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, - <&usb_dmac1 0>, <&usb_dmac1 1>; - dma-names = "ch0", "ch1", "ch2", "ch3"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 704>; - renesas,buswait = <4>; - phys = <&usb0 1>; - phy-names = "usb"; - status = "disabled"; - }; + hscif2: serial@e62d0000 { + compatible = "renesas,hscif-r8a7791", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62d0000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 713>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, + <&dmac1 0x3b>, <&dmac1 0x3c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 713>; + status = "disabled"; + }; - usbphy: usb-phy@e6590100 { - compatible = "renesas,usb-phy-r8a7791", - "renesas,rcar-gen2-usb-phy"; - reg = <0 0xe6590100 0 0x100>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&cpg CPG_MOD 704>; - clock-names = "usbhs"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 704>; - status = "disabled"; + msiof0: spi@e6e20000 { + compatible = "renesas,msiof-r8a7791", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6e20000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 000>; + dmas = <&dmac0 0x51>, <&dmac0 0x52>, + <&dmac1 0x51>, <&dmac1 0x52>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 0>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - usb0: usb-channel@0 { - reg = <0>; - #phy-cells = <1>; + msiof1: spi@e6e10000 { + compatible = "renesas,msiof-r8a7791", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6e10000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 208>; + dmas = <&dmac0 0x55>, <&dmac0 0x56>, + <&dmac1 0x55>, <&dmac1 0x56>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 208>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; - usb2: usb-channel@2 { - reg = <2>; - #phy-cells = <1>; + + msiof2: spi@e6e00000 { + compatible = "renesas,msiof-r8a7791", + "renesas,rcar-gen2-msiof"; + reg = <0 0xe6e00000 0 0x0064>; + interrupts = ; + clocks = <&cpg CPG_MOD 205>; + dmas = <&dmac0 0x41>, <&dmac0 0x42>, + <&dmac1 0x41>, <&dmac1 0x42>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 205>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; - }; - vin0: video@e6ef0000 { - compatible = "renesas,vin-r8a7791", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef0000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 811>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 811>; - status = "disabled"; - }; + adc: adc@e6e54000 { + compatible = "renesas,r8a7791-gyroadc", + "renesas,rcar-gyroadc"; + reg = <0 0xe6e54000 0 64>; + clocks = <&cpg CPG_MOD 901>; + clock-names = "fck"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 901>; + status = "disabled"; + }; - vin1: video@e6ef1000 { - compatible = "renesas,vin-r8a7791", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef1000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 810>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 810>; - status = "disabled"; - }; + can0: can@e6e80000 { + compatible = "renesas,can-r8a7791", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e80000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 916>, + <&cpg CPG_CORE R8A7791_CLK_RCAN>, <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 916>; + status = "disabled"; + }; - vin2: video@e6ef2000 { - compatible = "renesas,vin-r8a7791", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef2000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 809>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 809>; - status = "disabled"; - }; + can1: can@e6e88000 { + compatible = "renesas,can-r8a7791", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e88000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 915>, + <&cpg CPG_CORE R8A7791_CLK_RCAN>, <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 915>; + status = "disabled"; + }; - vsp@fe928000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe928000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 131>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 131>; - }; + vin0: video@e6ef0000 { + compatible = "renesas,vin-r8a7791", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef0000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 811>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 811>; + status = "disabled"; + }; - vsp@fe930000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe930000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 128>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 128>; - }; + vin1: video@e6ef1000 { + compatible = "renesas,vin-r8a7791", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef1000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 810>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 810>; + status = "disabled"; + }; - vsp@fe938000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe938000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 127>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 127>; - }; + vin2: video@e6ef2000 { + compatible = "renesas,vin-r8a7791", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef2000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 809>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 809>; + status = "disabled"; + }; + + rcar_sound: sound@ec500000 { + /* + * #sound-dai-cells is required + * + * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; + * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; + */ + compatible = "renesas,rcar_sound-r8a7791", + "renesas,rcar_sound-gen2"; + reg = <0 0xec500000 0 0x1000>, /* SCU */ + <0 0xec5a0000 0 0x100>, /* ADG */ + <0 0xec540000 0 0x1000>, /* SSIU */ + <0 0xec541000 0 0x280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ + reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; + + clocks = <&cpg CPG_MOD 1005>, + <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, + <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, + <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, + <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, + <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, + <&cpg CPG_MOD 1022>, <&cpg CPG_MOD 1023>, + <&cpg CPG_MOD 1024>, <&cpg CPG_MOD 1025>, + <&cpg CPG_MOD 1026>, <&cpg CPG_MOD 1027>, + <&cpg CPG_MOD 1028>, <&cpg CPG_MOD 1029>, + <&cpg CPG_MOD 1030>, <&cpg CPG_MOD 1031>, + <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, + <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, + <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, + <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, + <&cpg CPG_CORE R8A7791_CLK_M2>; + clock-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0", "src.9", "src.8", + "src.7", "src.6", "src.5", "src.4", + "src.3", "src.2", "src.1", "src.0", + "ctu.0", "ctu.1", + "mix.0", "mix.1", + "dvc.0", "dvc.1", + "clk_a", "clk_b", "clk_c", "clk_i"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 1005>, + <&cpg 1006>, <&cpg 1007>, + <&cpg 1008>, <&cpg 1009>, + <&cpg 1010>, <&cpg 1011>, + <&cpg 1012>, <&cpg 1013>, + <&cpg 1014>, <&cpg 1015>; + reset-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0"; + + status = "disabled"; + + rcar_sound,dvc { + dvc0: dvc-0 { + dmas = <&audma1 0xbc>; + dma-names = "tx"; + }; + dvc1: dvc-1 { + dmas = <&audma1 0xbe>; + dma-names = "tx"; + }; + }; + + rcar_sound,mix { + mix0: mix-0 { }; + mix1: mix-1 { }; + }; + + rcar_sound,ctu { + ctu00: ctu-0 { }; + ctu01: ctu-1 { }; + ctu02: ctu-2 { }; + ctu03: ctu-3 { }; + ctu10: ctu-4 { }; + ctu11: ctu-5 { }; + ctu12: ctu-6 { }; + ctu13: ctu-7 { }; + }; + + rcar_sound,src { + src0: src-0 { + interrupts = ; + dmas = <&audma0 0x85>, <&audma1 0x9a>; + dma-names = "rx", "tx"; + }; + src1: src-1 { + interrupts = ; + dmas = <&audma0 0x87>, <&audma1 0x9c>; + dma-names = "rx", "tx"; + }; + src2: src-2 { + interrupts = ; + dmas = <&audma0 0x89>, <&audma1 0x9e>; + dma-names = "rx", "tx"; + }; + src3: src-3 { + interrupts = ; + dmas = <&audma0 0x8b>, <&audma1 0xa0>; + dma-names = "rx", "tx"; + }; + src4: src-4 { + interrupts = ; + dmas = <&audma0 0x8d>, <&audma1 0xb0>; + dma-names = "rx", "tx"; + }; + src5: src-5 { + interrupts = ; + dmas = <&audma0 0x8f>, <&audma1 0xb2>; + dma-names = "rx", "tx"; + }; + src6: src-6 { + interrupts = ; + dmas = <&audma0 0x91>, <&audma1 0xb4>; + dma-names = "rx", "tx"; + }; + src7: src-7 { + interrupts = ; + dmas = <&audma0 0x93>, <&audma1 0xb6>; + dma-names = "rx", "tx"; + }; + src8: src-8 { + interrupts = ; + dmas = <&audma0 0x95>, <&audma1 0xb8>; + dma-names = "rx", "tx"; + }; + src9: src-9 { + interrupts = ; + dmas = <&audma0 0x97>, <&audma1 0xba>; + dma-names = "rx", "tx"; + }; + }; + + rcar_sound,ssi { + ssi0: ssi-0 { + interrupts = ; + dmas = <&audma0 0x01>, <&audma1 0x02>, + <&audma0 0x15>, <&audma1 0x16>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi1: ssi-1 { + interrupts = ; + dmas = <&audma0 0x03>, <&audma1 0x04>, + <&audma0 0x49>, <&audma1 0x4a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi2: ssi-2 { + interrupts = ; + dmas = <&audma0 0x05>, <&audma1 0x06>, + <&audma0 0x63>, <&audma1 0x64>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi3: ssi-3 { + interrupts = ; + dmas = <&audma0 0x07>, <&audma1 0x08>, + <&audma0 0x6f>, <&audma1 0x70>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi4: ssi-4 { + interrupts = ; + dmas = <&audma0 0x09>, <&audma1 0x0a>, + <&audma0 0x71>, <&audma1 0x72>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi5: ssi-5 { + interrupts = ; + dmas = <&audma0 0x0b>, <&audma1 0x0c>, + <&audma0 0x73>, <&audma1 0x74>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi6: ssi-6 { + interrupts = ; + dmas = <&audma0 0x0d>, <&audma1 0x0e>, + <&audma0 0x75>, <&audma1 0x76>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi7: ssi-7 { + interrupts = ; + dmas = <&audma0 0x0f>, <&audma1 0x10>, + <&audma0 0x79>, <&audma1 0x7a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi8: ssi-8 { + interrupts = ; + dmas = <&audma0 0x11>, <&audma1 0x12>, + <&audma0 0x7b>, <&audma1 0x7c>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi9: ssi-9 { + interrupts = ; + dmas = <&audma0 0x13>, <&audma1 0x14>, + <&audma0 0x7d>, <&audma1 0x7e>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + }; + }; + + audma0: dma-controller@ec700000 { + compatible = "renesas,dmac-r8a7791", + "renesas,rcar-dmac"; + reg = <0 0xec700000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 502>; + clock-names = "fck"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 502>; + #dma-cells = <1>; + dma-channels = <13>; + }; + + audma1: dma-controller@ec720000 { + compatible = "renesas,dmac-r8a7791", + "renesas,rcar-dmac"; + reg = <0 0xec720000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 501>; + clock-names = "fck"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 501>; + #dma-cells = <1>; + dma-channels = <13>; + }; - du: display@feb00000 { - compatible = "renesas,du-r8a7791"; - reg = <0 0xfeb00000 0 0x40000>, - <0 0xfeb90000 0 0x1c>; - reg-names = "du", "lvds.0"; - interrupts = , - ; - clocks = <&cpg CPG_MOD 724>, - <&cpg CPG_MOD 723>, - <&cpg CPG_MOD 726>; - clock-names = "du.0", "du.1", "lvds.0"; - status = "disabled"; - - ports { + xhci: usb@ee000000 { + compatible = "renesas,xhci-r8a7791", + "renesas,rcar-gen2-xhci"; + reg = <0 0xee000000 0 0xc00>; + interrupts = ; + clocks = <&cpg CPG_MOD 328>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 328>; + phys = <&usb2 1>; + phy-names = "usb"; + status = "disabled"; + }; + + pci0: pci@ee090000 { + compatible = "renesas,pci-r8a7791", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee090000 0 0xc00>, + <0 0xee080000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x800 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x1000 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + }; + + pci1: pci@ee0d0000 { + compatible = "renesas,pci-r8a7791", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee0d0000 0 0xc00>, + <0 0xee0c0000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <1 1>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x10800 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x11000 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + }; + + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7791", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; + + sdhi1: sd@ee140000 { + compatible = "renesas,sdhi-r8a7791", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee140000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, + <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 312>; + status = "disabled"; + }; + + sdhi2: sd@ee160000 { + compatible = "renesas,sdhi-r8a7791", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee160000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, + <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 311>; + status = "disabled"; + }; + + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7791", + "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 315>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 315>; + reg-io-width = <4>; + status = "disabled"; + max-frequency = <97500000>; + }; + + sata0: sata@ee300000 { + compatible = "renesas,sata-r8a7791", + "renesas,rcar-gen2-sata"; + reg = <0 0xee300000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 815>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 815>; + status = "disabled"; + }; + + sata1: sata@ee500000 { + compatible = "renesas,sata-r8a7791", + "renesas,rcar-gen2-sata"; + reg = <0 0xee500000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 814>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 814>; + status = "disabled"; + }; + + ether: ethernet@ee700000 { + compatible = "renesas,ether-r8a7791", + "renesas,rcar-gen2-ether"; + reg = <0 0xee700000 0 0x400>; + interrupts = ; + clocks = <&cpg CPG_MOD 813>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 813>; + phy-mode = "rmii"; #address-cells = <1>; #size-cells = <0>; + status = "disabled"; + }; - port@0 { - reg = <0>; - du_out_rgb: endpoint { + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 408>; + }; + + pciec: pcie@fe000000 { + compatible = "renesas,pcie-r8a7791", + "renesas,pcie-rcar-gen2"; + reg = <0 0xfe000000 0 0x80000>; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; + device_type = "pci"; + ranges = <0x01000000 0 0x00000000 0 0xfe100000 0 0x00100000 + 0x02000000 0 0xfe200000 0 0xfe200000 0 0x00200000 + 0x02000000 0 0x30000000 0 0x30000000 0 0x08000000 + 0x42000000 0 0x38000000 0 0x38000000 0 0x08000000>; + /* Map all possible DDR as inbound ranges */ + dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 0 0x80000000 + 0x43000000 2 0x00000000 2 0x00000000 1 0x00000000>; + interrupts = , + , + ; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 319>, <&pcie_bus_clk>; + clock-names = "pcie", "pcie_bus"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 319>; + status = "disabled"; + }; + + vsp@fe928000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe928000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 131>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 131>; + }; + + vsp@fe930000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe930000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 128>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 128>; + }; + + vsp@fe938000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe938000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 127>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 127>; + }; + + jpu: jpeg-codec@fe980000 { + compatible = "renesas,jpu-r8a7791", + "renesas,rcar-gen2-jpu"; + reg = <0 0xfe980000 0 0x10300>; + interrupts = ; + clocks = <&cpg CPG_MOD 106>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 106>; + }; + + du: display@feb00000 { + compatible = "renesas,du-r8a7791"; + reg = <0 0xfeb00000 0 0x40000>, + <0 0xfeb90000 0 0x1c>; + reg-names = "du", "lvds.0"; + interrupts = , + ; + clocks = <&cpg CPG_MOD 724>, + <&cpg CPG_MOD 723>, + <&cpg CPG_MOD 726>; + clock-names = "du.0", "du.1", "lvds.0"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb: endpoint { + }; }; - }; - port@1 { - reg = <1>; - du_out_lvds0: endpoint { + port@1 { + reg = <1>; + du_out_lvds0: endpoint { + }; }; }; }; - }; - can0: can@e6e80000 { - compatible = "renesas,can-r8a7791", "renesas,rcar-gen2-can"; - reg = <0 0xe6e80000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 916>, <&cpg CPG_CORE R8A7791_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 916>; - status = "disabled"; - }; + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; + }; + + cmt0: timer@ffca0000 { + compatible = "renesas,r8a7791-cmt0", + "renesas,rcar-gen2-cmt0"; + reg = <0 0xffca0000 0 0x1004>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 124>; + clock-names = "fck"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 124>; + + status = "disabled"; + }; - can1: can@e6e88000 { - compatible = "renesas,can-r8a7791", "renesas,rcar-gen2-can"; - reg = <0 0xe6e88000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 915>, <&cpg CPG_CORE R8A7791_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 915>; - status = "disabled"; + cmt1: timer@e6130000 { + compatible = "renesas,r8a7791-cmt1", + "renesas,rcar-gen2-cmt1"; + reg = <0 0xe6130000 0 0x1004>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 329>; + clock-names = "fck"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 329>; + + status = "disabled"; + }; }; - jpu: jpeg-codec@fe980000 { - compatible = "renesas,jpu-r8a7791", "renesas,rcar-gen2-jpu"; - reg = <0 0xfe980000 0 0x10300>; - interrupts = ; - clocks = <&cpg CPG_MOD 106>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 106>; + timer { + compatible = "arm,armv7-timer"; + interrupts-extended = <&gic GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>; }; /* External root clock */ @@ -1222,447 +1734,4 @@ /* This value must be overridden by the board. */ clock-frequency = <0>; }; - - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7791-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>, <&usb_extal_clk>; - clock-names = "extal", "usb_extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; - }; - - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7791-rst"; - reg = <0 0xe6160000 0 0x0100>; - }; - - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; - }; - - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7791-sysc"; - reg = <0 0xe6180000 0 0x0200>; - #power-domain-cells = <1>; - }; - - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7791", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 917>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - msiof0: spi@e6e20000 { - compatible = "renesas,msiof-r8a7791", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6e20000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 000>; - dmas = <&dmac0 0x51>, <&dmac0 0x52>, - <&dmac1 0x51>, <&dmac1 0x52>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 0>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - msiof1: spi@e6e10000 { - compatible = "renesas,msiof-r8a7791", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6e10000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 208>; - dmas = <&dmac0 0x55>, <&dmac0 0x56>, - <&dmac1 0x55>, <&dmac1 0x56>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 208>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - msiof2: spi@e6e00000 { - compatible = "renesas,msiof-r8a7791", - "renesas,rcar-gen2-msiof"; - reg = <0 0xe6e00000 0 0x0064>; - interrupts = ; - clocks = <&cpg CPG_MOD 205>; - dmas = <&dmac0 0x41>, <&dmac0 0x42>, - <&dmac1 0x41>, <&dmac1 0x42>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 205>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - xhci: usb@ee000000 { - compatible = "renesas,xhci-r8a7791", "renesas,rcar-gen2-xhci"; - reg = <0 0xee000000 0 0xc00>; - interrupts = ; - clocks = <&cpg CPG_MOD 328>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 328>; - phys = <&usb2 1>; - phy-names = "usb"; - status = "disabled"; - }; - - pci0: pci@ee090000 { - compatible = "renesas,pci-r8a7791", "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee090000 0 0xc00>, - <0 0xee080000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <0 0>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x800 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x1000 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; - }; - - pci1: pci@ee0d0000 { - compatible = "renesas,pci-r8a7791", "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee0d0000 0 0xc00>, - <0 0xee0c0000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <1 1>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x10800 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x11000 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - }; - - pciec: pcie@fe000000 { - compatible = "renesas,pcie-r8a7791", "renesas,pcie-rcar-gen2"; - reg = <0 0xfe000000 0 0x80000>; - #address-cells = <3>; - #size-cells = <2>; - bus-range = <0x00 0xff>; - device_type = "pci"; - ranges = <0x01000000 0 0x00000000 0 0xfe100000 0 0x00100000 - 0x02000000 0 0xfe200000 0 0xfe200000 0 0x00200000 - 0x02000000 0 0x30000000 0 0x30000000 0 0x08000000 - 0x42000000 0 0x38000000 0 0x38000000 0 0x08000000>; - /* Map all possible DDR as inbound ranges */ - dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 0 0x80000000 - 0x43000000 2 0x00000000 2 0x00000000 1 0x00000000>; - interrupts = , - , - ; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&cpg CPG_MOD 319>, <&pcie_bus_clk>; - clock-names = "pcie", "pcie_bus"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 319>; - status = "disabled"; - }; - - ipmmu_sy0: mmu@e6280000 { - compatible = "renesas,ipmmu-r8a7791", "renesas,ipmmu-vmsa"; - reg = <0 0xe6280000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_sy1: mmu@e6290000 { - compatible = "renesas,ipmmu-r8a7791", "renesas,ipmmu-vmsa"; - reg = <0 0xe6290000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_ds: mmu@e6740000 { - compatible = "renesas,ipmmu-r8a7791", "renesas,ipmmu-vmsa"; - reg = <0 0xe6740000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mp: mmu@ec680000 { - compatible = "renesas,ipmmu-r8a7791", "renesas,ipmmu-vmsa"; - reg = <0 0xec680000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mx: mmu@fe951000 { - compatible = "renesas,ipmmu-r8a7791", "renesas,ipmmu-vmsa"; - reg = <0 0xfe951000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_rt: mmu@ffc80000 { - compatible = "renesas,ipmmu-r8a7791", "renesas,ipmmu-vmsa"; - reg = <0 0xffc80000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_gp: mmu@e62a0000 { - compatible = "renesas,ipmmu-r8a7791", "renesas,ipmmu-vmsa"; - reg = <0 0xe62a0000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - rcar_sound: sound@ec500000 { - /* - * #sound-dai-cells is required - * - * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; - * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; - */ - compatible = "renesas,rcar_sound-r8a7791", - "renesas,rcar_sound-gen2"; - reg = <0 0xec500000 0 0x1000>, /* SCU */ - <0 0xec5a0000 0 0x100>, /* ADG */ - <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x280>, /* SSI */ - <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ - reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; - - clocks = <&cpg CPG_MOD 1005>, - <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, - <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, - <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, - <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, - <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, - <&cpg CPG_MOD 1022>, <&cpg CPG_MOD 1023>, - <&cpg CPG_MOD 1024>, <&cpg CPG_MOD 1025>, - <&cpg CPG_MOD 1026>, <&cpg CPG_MOD 1027>, - <&cpg CPG_MOD 1028>, <&cpg CPG_MOD 1029>, - <&cpg CPG_MOD 1030>, <&cpg CPG_MOD 1031>, - <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, - <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, - <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, - <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, - <&cpg CPG_CORE R8A7791_CLK_M2>; - clock-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", - "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0", - "src.9", "src.8", "src.7", "src.6", "src.5", - "src.4", "src.3", "src.2", "src.1", "src.0", - "ctu.0", "ctu.1", - "mix.0", "mix.1", - "dvc.0", "dvc.1", - "clk_a", "clk_b", "clk_c", "clk_i"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 1005>, - <&cpg 1006>, <&cpg 1007>, <&cpg 1008>, <&cpg 1009>, - <&cpg 1010>, <&cpg 1011>, <&cpg 1012>, <&cpg 1013>, - <&cpg 1014>, <&cpg 1015>; - reset-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", - "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0"; - - status = "disabled"; - - rcar_sound,dvc { - dvc0: dvc-0 { - dmas = <&audma1 0xbc>; - dma-names = "tx"; - }; - dvc1: dvc-1 { - dmas = <&audma1 0xbe>; - dma-names = "tx"; - }; - }; - - rcar_sound,mix { - mix0: mix-0 { }; - mix1: mix-1 { }; - }; - - rcar_sound,ctu { - ctu00: ctu-0 { }; - ctu01: ctu-1 { }; - ctu02: ctu-2 { }; - ctu03: ctu-3 { }; - ctu10: ctu-4 { }; - ctu11: ctu-5 { }; - ctu12: ctu-6 { }; - ctu13: ctu-7 { }; - }; - - rcar_sound,src { - src0: src-0 { - interrupts = ; - dmas = <&audma0 0x85>, <&audma1 0x9a>; - dma-names = "rx", "tx"; - }; - src1: src-1 { - interrupts = ; - dmas = <&audma0 0x87>, <&audma1 0x9c>; - dma-names = "rx", "tx"; - }; - src2: src-2 { - interrupts = ; - dmas = <&audma0 0x89>, <&audma1 0x9e>; - dma-names = "rx", "tx"; - }; - src3: src-3 { - interrupts = ; - dmas = <&audma0 0x8b>, <&audma1 0xa0>; - dma-names = "rx", "tx"; - }; - src4: src-4 { - interrupts = ; - dmas = <&audma0 0x8d>, <&audma1 0xb0>; - dma-names = "rx", "tx"; - }; - src5: src-5 { - interrupts = ; - dmas = <&audma0 0x8f>, <&audma1 0xb2>; - dma-names = "rx", "tx"; - }; - src6: src-6 { - interrupts = ; - dmas = <&audma0 0x91>, <&audma1 0xb4>; - dma-names = "rx", "tx"; - }; - src7: src-7 { - interrupts = ; - dmas = <&audma0 0x93>, <&audma1 0xb6>; - dma-names = "rx", "tx"; - }; - src8: src-8 { - interrupts = ; - dmas = <&audma0 0x95>, <&audma1 0xb8>; - dma-names = "rx", "tx"; - }; - src9: src-9 { - interrupts = ; - dmas = <&audma0 0x97>, <&audma1 0xba>; - dma-names = "rx", "tx"; - }; - }; - - rcar_sound,ssi { - ssi0: ssi-0 { - interrupts = ; - dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi1: ssi-1 { - interrupts = ; - dmas = <&audma0 0x03>, <&audma1 0x04>, <&audma0 0x49>, <&audma1 0x4a>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi2: ssi-2 { - interrupts = ; - dmas = <&audma0 0x05>, <&audma1 0x06>, <&audma0 0x63>, <&audma1 0x64>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi3: ssi-3 { - interrupts = ; - dmas = <&audma0 0x07>, <&audma1 0x08>, <&audma0 0x6f>, <&audma1 0x70>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi4: ssi-4 { - interrupts = ; - dmas = <&audma0 0x09>, <&audma1 0x0a>, <&audma0 0x71>, <&audma1 0x72>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi5: ssi-5 { - interrupts = ; - dmas = <&audma0 0x0b>, <&audma1 0x0c>, <&audma0 0x73>, <&audma1 0x74>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi6: ssi-6 { - interrupts = ; - dmas = <&audma0 0x0d>, <&audma1 0x0e>, <&audma0 0x75>, <&audma1 0x76>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi7: ssi-7 { - interrupts = ; - dmas = <&audma0 0x0f>, <&audma1 0x10>, <&audma0 0x79>, <&audma1 0x7a>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi8: ssi-8 { - interrupts = ; - dmas = <&audma0 0x11>, <&audma1 0x12>, <&audma0 0x7b>, <&audma1 0x7c>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi9: ssi-9 { - interrupts = ; - dmas = <&audma0 0x13>, <&audma1 0x14>, <&audma0 0x7d>, <&audma1 0x7e>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - }; - }; }; -- cgit v1.3-14-g43fede From 21b05c52abe355e205eb133f2fe3dbc2520edeed Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:08 +0100 Subject: ARM: dts: r8a7791: sort subnodes of root node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort subnodes of root node to aid maintenance. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7791.dtsi | 134 ++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index de2af2199731..dc659351472f 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -39,6 +39,35 @@ vin2 = &vin2; }; + /* + * The external audio clocks are configured as 0 Hz fixed frequency + * clocks by default. + * Boards that provide audio clocks should override them. + */ + audio_clk_a: audio_clk_a { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + audio_clk_b: audio_clk_b { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + audio_clk_c: audio_clk_c { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + /* External CAN clock */ + can_clk: can { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + cpus { #address-cells = <1>; #size-cells = <0>; @@ -82,23 +111,27 @@ }; }; - thermal-zones { - cpu_thermal: cpu-thermal { - polling-delay-passive = <0>; - polling-delay = <0>; + /* External root clock */ + extal_clk: extal { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; - thermal-sensors = <&thermal>; + /* External PCIe clock - can be overridden by the board */ + pcie_bus_clk: pcie_bus { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; - trips { - cpu-crit { - temperature = <95000>; - hysteresis = <0>; - type = "critical"; - }; - }; - cooling-maps { - }; - }; + /* External SCIF clock */ + scif_clk: scif { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; }; soc { @@ -1668,6 +1701,25 @@ }; }; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + + thermal-sensors = <&thermal>; + + trips { + cpu-crit { + temperature = <95000>; + hysteresis = <0>; + type = "critical"; + }; + }; + cooling-maps { + }; + }; + }; + timer { compatible = "arm,armv7-timer"; interrupts-extended = <&gic GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, @@ -1676,62 +1728,10 @@ <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>; }; - /* External root clock */ - extal_clk: extal { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; - - /* - * The external audio clocks are configured as 0 Hz fixed frequency - * clocks by default. - * Boards that provide audio clocks should override them. - */ - audio_clk_a: audio_clk_a { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - audio_clk_b: audio_clk_b { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - audio_clk_c: audio_clk_c { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - - /* External PCIe clock - can be overridden by the board */ - pcie_bus_clk: pcie_bus { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - - /* External SCIF clock */ - scif_clk: scif { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; - /* External USB clock - can be overridden by the board */ usb_extal_clk: usb_extal { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <48000000>; }; - - /* External CAN clock */ - can_clk: can { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; }; -- cgit v1.3-14-g43fede From 3758e51b4b1e9cfd98be29b32b374582a00873c3 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:09 +0100 Subject: ARM: dts: r8a7792: sort subnodes of soc node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort the subnodes of the soc node to improve maintainability. The sort key is the address on the bus with instances of the same IP block grouped together. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7792.dtsi | 498 ++++++++++++++++++++--------------------- 1 file changed, 249 insertions(+), 249 deletions(-) diff --git a/arch/arm/boot/dts/r8a7792.dtsi b/arch/arm/boot/dts/r8a7792.dtsi index 3be15a158bad..268987ff0201 100644 --- a/arch/arm/boot/dts/r8a7792.dtsi +++ b/arch/arm/boot/dts/r8a7792.dtsi @@ -101,63 +101,6 @@ #size-cells = <2>; ranges; - apmu@e6152000 { - compatible = "renesas,r8a7792-apmu", "renesas,apmu"; - reg = <0 0xe6152000 0 0x188>; - cpus = <&cpu0 &cpu1>; - }; - - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, - <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, - <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; - - irqc: interrupt-controller@e61c0000 { - compatible = "renesas,irqc-r8a7792", "renesas,irqc"; - #interrupt-cells = <2>; - interrupt-controller; - reg = <0 0xe61c0000 0 0x200>; - interrupts = , - , - , - ; - clocks = <&cpg CPG_MOD 407>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 407>; - }; - - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7792-rst"; - reg = <0 0xe6160000 0 0x0100>; - }; - - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; - }; - - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7792-sysc"; - reg = <0 0xe6180000 0 0x0200>; - #power-domain-cells = <1>; - }; - - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7792"; - reg = <0 0xe6060000 0 0x144>; - }; - gpio0: gpio@e6050000 { compatible = "renesas,gpio-r8a7792", "renesas,rcar-gen2-gpio"; @@ -338,6 +281,155 @@ resets = <&cpg 913>; }; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7792"; + reg = <0 0xe6060000 0 0x144>; + }; + + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7792-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>; + clock-names = "extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; + + apmu@e6152000 { + compatible = "renesas,r8a7792-apmu", "renesas,apmu"; + reg = <0 0xe6152000 0 0x188>; + cpus = <&cpu0 &cpu1>; + }; + + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7792-rst"; + reg = <0 0xe6160000 0 0x0100>; + }; + + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7792-sysc"; + reg = <0 0xe6180000 0 0x0200>; + #power-domain-cells = <1>; + }; + + irqc: interrupt-controller@e61c0000 { + compatible = "renesas,irqc-r8a7792", "renesas,irqc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0 0xe61c0000 0 0x200>; + interrupts = , + , + , + ; + clocks = <&cpg CPG_MOD 407>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 407>; + }; + + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; + }; + + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; + + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; + }; + + /* I2C doesn't need pinmux */ + i2c0: i2c@e6508000 { + compatible = "renesas,i2c-r8a7792", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6508000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 931>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 931>; + i2c-scl-internal-delay-ns = <6>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1: i2c@e6518000 { + compatible = "renesas,i2c-r8a7792", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6518000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 930>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 930>; + i2c-scl-internal-delay-ns = <6>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c2: i2c@e6530000 { + compatible = "renesas,i2c-r8a7792", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6530000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 929>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 929>; + i2c-scl-internal-delay-ns = <6>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c3: i2c@e6540000 { + compatible = "renesas,i2c-r8a7792", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6540000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 928>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 928>; + i2c-scl-internal-delay-ns = <6>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c4: i2c@e6520000 { + compatible = "renesas,i2c-r8a7792", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6520000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 927>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 927>; + i2c-scl-internal-delay-ns = <6>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c5: i2c@e6528000 { + compatible = "renesas,i2c-r8a7792", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6528000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 925>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 925>; + i2c-scl-internal-delay-ns = <110>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + dmac0: dma-controller@e6700000 { compatible = "renesas,dmac-r8a7792", "renesas,rcar-dmac"; @@ -404,6 +496,35 @@ dma-channels = <15>; }; + avb: ethernet@e6800000 { + compatible = "renesas,etheravb-r8a7792", + "renesas,etheravb-rcar-gen2"; + reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; + interrupts = ; + clocks = <&cpg CPG_MOD 812>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 812>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7792", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 917>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + scif0: serial@e6e60000 { compatible = "renesas,scif-r8a7792", "renesas,rcar-gen2-scif", "renesas,scif"; @@ -500,162 +621,6 @@ status = "disabled"; }; - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; - - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; - - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; - }; - }; - - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7792", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = <0 165 IRQ_TYPE_LEVEL_HIGH>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - clocks = <&cpg CPG_MOD 314>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 314>; - status = "disabled"; - }; - - jpu: jpeg-codec@fe980000 { - compatible = "renesas,jpu-r8a7792", - "renesas,rcar-gen2-jpu"; - reg = <0 0xfe980000 0 0x10300>; - interrupts = ; - clocks = <&cpg CPG_MOD 106>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 106>; - }; - - avb: ethernet@e6800000 { - compatible = "renesas,etheravb-r8a7792", - "renesas,etheravb-rcar-gen2"; - reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; - interrupts = ; - clocks = <&cpg CPG_MOD 812>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 812>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - /* I2C doesn't need pinmux */ - i2c0: i2c@e6508000 { - compatible = "renesas,i2c-r8a7792", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6508000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 931>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 931>; - i2c-scl-internal-delay-ns = <6>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c1: i2c@e6518000 { - compatible = "renesas,i2c-r8a7792", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6518000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 930>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 930>; - i2c-scl-internal-delay-ns = <6>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c2: i2c@e6530000 { - compatible = "renesas,i2c-r8a7792", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6530000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 929>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 929>; - i2c-scl-internal-delay-ns = <6>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c3: i2c@e6540000 { - compatible = "renesas,i2c-r8a7792", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6540000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 928>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 928>; - i2c-scl-internal-delay-ns = <6>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c4: i2c@e6520000 { - compatible = "renesas,i2c-r8a7792", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6520000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 927>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 927>; - i2c-scl-internal-delay-ns = <6>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c5: i2c@e6528000 { - compatible = "renesas,i2c-r8a7792", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6528000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 925>; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 925>; - i2c-scl-internal-delay-ns = <110>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7792", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; - resets = <&cpg 917>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - msiof0: spi@e6e20000 { compatible = "renesas,msiof-r8a7792", "renesas,rcar-gen2-msiof"; @@ -688,34 +653,6 @@ status = "disabled"; }; - du: display@feb00000 { - compatible = "renesas,du-r8a7792"; - reg = <0 0xfeb00000 0 0x40000>; - reg-names = "du"; - interrupts = , - ; - clocks = <&cpg CPG_MOD 724>, - <&cpg CPG_MOD 723>; - clock-names = "du.0", "du.1"; - status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - du_out_rgb0: endpoint { - }; - }; - port@1 { - reg = <1>; - du_out_rgb1: endpoint { - }; - }; - }; - }; - can0: can@e6e80000 { compatible = "renesas,can-r8a7792", "renesas,rcar-gen2-can"; @@ -808,6 +745,36 @@ status = "disabled"; }; + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7792", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = <0 165 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + clocks = <&cpg CPG_MOD 314>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; + + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, + <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, + <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 408>; + }; + vsp@fe928000 { compatible = "renesas,vsp1"; reg = <0 0xfe928000 0 0x8000>; @@ -835,14 +802,47 @@ resets = <&cpg 127>; }; - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7792-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>; - clock-names = "extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; + jpu: jpeg-codec@fe980000 { + compatible = "renesas,jpu-r8a7792", + "renesas,rcar-gen2-jpu"; + reg = <0 0xfe980000 0 0x10300>; + interrupts = ; + clocks = <&cpg CPG_MOD 106>; + power-domains = <&sysc R8A7792_PD_ALWAYS_ON>; + resets = <&cpg 106>; + }; + + du: display@feb00000 { + compatible = "renesas,du-r8a7792"; + reg = <0 0xfeb00000 0 0x40000>; + reg-names = "du"; + interrupts = , + ; + clocks = <&cpg CPG_MOD 724>, + <&cpg CPG_MOD 723>; + clock-names = "du.0", "du.1"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb0: endpoint { + }; + }; + port@1 { + reg = <1>; + du_out_rgb1: endpoint { + }; + }; + }; + }; + + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; }; }; -- cgit v1.3-14-g43fede From 93a4a46fa1a6b2cf51cfbdd2e46572e5d4dad57d Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:10 +0100 Subject: ARM: dts: r8a7793: consistently use single space after = MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consistently use a single space after a =. This patch removes instances where a tab is used instead. It also avoids running over 80 columns in width in one of the lines where whitespace is updated. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7793.dtsi | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/arch/arm/boot/dts/r8a7793.dtsi b/arch/arm/boot/dts/r8a7793.dtsi index e95f4fb44dc4..ea4bc06c8e8a 100644 --- a/arch/arm/boot/dts/r8a7793.dtsi +++ b/arch/arm/boot/dts/r8a7793.dtsi @@ -228,9 +228,9 @@ }; thermal: thermal@e61f0000 { - compatible = "renesas,thermal-r8a7793", - "renesas,rcar-gen2-thermal", - "renesas,rcar-thermal"; + compatible = "renesas,thermal-r8a7793", + "renesas,rcar-gen2-thermal", + "renesas,rcar-thermal"; reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; interrupts = ; clocks = <&cpg CPG_MOD 522>; @@ -1174,12 +1174,13 @@ * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; */ - compatible = "renesas,rcar_sound-r8a7793", "renesas,rcar_sound-gen2"; - reg = <0 0xec500000 0 0x1000>, /* SCU */ - <0 0xec5a0000 0 0x100>, /* ADG */ - <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x280>, /* SSI */ - <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ + compatible = "renesas,rcar_sound-r8a7793", + "renesas,rcar_sound-gen2"; + reg = <0 0xec500000 0 0x1000>, /* SCU */ + <0 0xec5a0000 0 0x100>, /* ADG */ + <0 0xec540000 0 0x1000>, /* SSIU */ + <0 0xec541000 0 0x280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; clocks = <&cpg CPG_MOD 1005>, -- cgit v1.3-14-g43fede From bff8f8c2feb7898e5dcdf451678041a1477cc9a2 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:11 +0100 Subject: ARM: dts: r8a7793: add soc node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add soc node to represent the bus and move all nodes with a base address into this node. This is consistent with handling of R-Car Gen3, RZ/G1, and R-Car V2H (R8A77920) SoCs upstream. It is intended to migrate other R-Car Gen2 SoCs to this scheme. The ordering is derived from simply moving each node with an address up to before any nodes without a base address that occur before the soc node. To improve maintainability follow-up patches will sort subnodes of both the new soc node and the root node. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7793.dtsi | 2325 +++++++++++++++++++++------------------- 1 file changed, 1193 insertions(+), 1132 deletions(-) diff --git a/arch/arm/boot/dts/r8a7793.dtsi b/arch/arm/boot/dts/r8a7793.dtsi index ea4bc06c8e8a..d18a65c647bb 100644 --- a/arch/arm/boot/dts/r8a7793.dtsi +++ b/arch/arm/boot/dts/r8a7793.dtsi @@ -15,7 +15,6 @@ / { compatible = "renesas,r8a7793"; - interrupt-parent = <&gic>; #address-cells = <2>; #size-cells = <2>; @@ -74,958 +73,1273 @@ }; }; - apmu@e6152000 { - compatible = "renesas,r8a7793-apmu", "renesas,apmu"; - reg = <0 0xe6152000 0 0x188>; - cpus = <&cpu0 &cpu1>; - }; + soc { + compatible = "simple-bus"; + interrupt-parent = <&gic>; - thermal-zones { - cpu_thermal: cpu-thermal { - polling-delay-passive = <0>; - polling-delay = <0>; + #address-cells = <2>; + #size-cells = <2>; + ranges; - thermal-sensors = <&thermal>; + apmu@e6152000 { + compatible = "renesas,r8a7793-apmu", "renesas,apmu"; + reg = <0 0xe6152000 0 0x188>; + cpus = <&cpu0 &cpu1>; + }; - trips { - cpu-crit { - temperature = <95000>; - hysteresis = <0>; - type = "critical"; - }; - }; - cooling-maps { - }; + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, + <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, + <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 408>; }; - }; - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <0>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, - <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, - <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; + gpio0: gpio@e6050000 { + compatible = "renesas,gpio-r8a7793", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6050000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 0 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 912>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 912>; + }; - gpio0: gpio@e6050000 { - compatible = "renesas,gpio-r8a7793", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6050000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 0 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 912>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 912>; - }; + gpio1: gpio@e6051000 { + compatible = "renesas,gpio-r8a7793", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6051000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 32 26>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 911>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 911>; + }; - gpio1: gpio@e6051000 { - compatible = "renesas,gpio-r8a7793", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6051000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 32 26>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 911>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 911>; - }; + gpio2: gpio@e6052000 { + compatible = "renesas,gpio-r8a7793", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6052000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 64 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 910>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 910>; + }; - gpio2: gpio@e6052000 { - compatible = "renesas,gpio-r8a7793", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6052000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 64 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 910>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 910>; - }; + gpio3: gpio@e6053000 { + compatible = "renesas,gpio-r8a7793", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6053000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 96 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 909>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 909>; + }; - gpio3: gpio@e6053000 { - compatible = "renesas,gpio-r8a7793", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6053000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 96 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 909>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 909>; - }; + gpio4: gpio@e6054000 { + compatible = "renesas,gpio-r8a7793", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6054000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 128 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 908>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 908>; + }; - gpio4: gpio@e6054000 { - compatible = "renesas,gpio-r8a7793", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6054000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 128 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 908>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 908>; - }; + gpio5: gpio@e6055000 { + compatible = "renesas,gpio-r8a7793", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6055000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 160 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 907>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 907>; + }; - gpio5: gpio@e6055000 { - compatible = "renesas,gpio-r8a7793", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6055000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 160 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 907>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 907>; - }; + gpio6: gpio@e6055400 { + compatible = "renesas,gpio-r8a7793", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6055400 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 192 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 905>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 905>; + }; - gpio6: gpio@e6055400 { - compatible = "renesas,gpio-r8a7793", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6055400 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 192 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 905>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 905>; - }; + gpio7: gpio@e6055800 { + compatible = "renesas,gpio-r8a7793", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6055800 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 224 26>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 904>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 904>; + }; - gpio7: gpio@e6055800 { - compatible = "renesas,gpio-r8a7793", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6055800 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 224 26>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 904>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 904>; - }; + thermal: thermal@e61f0000 { + compatible = "renesas,thermal-r8a7793", + "renesas,rcar-gen2-thermal", + "renesas,rcar-thermal"; + reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; + interrupts = ; + clocks = <&cpg CPG_MOD 522>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 522>; + #thermal-sensor-cells = <0>; + }; - thermal: thermal@e61f0000 { - compatible = "renesas,thermal-r8a7793", - "renesas,rcar-gen2-thermal", - "renesas,rcar-thermal"; - reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; - interrupts = ; - clocks = <&cpg CPG_MOD 522>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 522>; - #thermal-sensor-cells = <0>; - }; + cmt0: timer@ffca0000 { + compatible = "renesas,r8a7793-cmt0", + "renesas,rcar-gen2-cmt0"; + reg = <0 0xffca0000 0 0x1004>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 124>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 124>; + + status = "disabled"; + }; - timer { - compatible = "arm,armv7-timer"; - interrupts = , - , - , - ; - }; + cmt1: timer@e6130000 { + compatible = "renesas,r8a7793-cmt1", + "renesas,rcar-gen2-cmt1"; + reg = <0 0xe6130000 0 0x1004>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 329>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 329>; + + status = "disabled"; + }; - cmt0: timer@ffca0000 { - compatible = "renesas,r8a7793-cmt0", "renesas,rcar-gen2-cmt0"; - reg = <0 0xffca0000 0 0x1004>; - interrupts = , - ; - clocks = <&cpg CPG_MOD 124>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 124>; - - status = "disabled"; - }; + irqc0: interrupt-controller@e61c0000 { + compatible = "renesas,irqc-r8a7793", "renesas,irqc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0 0xe61c0000 0 0x200>; + interrupts = , + , + , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 407>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 407>; + }; - cmt1: timer@e6130000 { - compatible = "renesas,r8a7793-cmt1", "renesas,rcar-gen2-cmt1"; - reg = <0 0xe6130000 0 0x1004>; - interrupts = , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 329>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 329>; - - status = "disabled"; - }; + dmac0: dma-controller@e6700000 { + compatible = "renesas,dmac-r8a7793", + "renesas,rcar-dmac"; + reg = <0 0xe6700000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 219>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 219>; + #dma-cells = <1>; + dma-channels = <15>; + }; - irqc0: interrupt-controller@e61c0000 { - compatible = "renesas,irqc-r8a7793", "renesas,irqc"; - #interrupt-cells = <2>; - interrupt-controller; - reg = <0 0xe61c0000 0 0x200>; - interrupts = , - , - , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 407>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 407>; - }; + dmac1: dma-controller@e6720000 { + compatible = "renesas,dmac-r8a7793", + "renesas,rcar-dmac"; + reg = <0 0xe6720000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 218>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 218>; + #dma-cells = <1>; + dma-channels = <15>; + }; - dmac0: dma-controller@e6700000 { - compatible = "renesas,dmac-r8a7793", "renesas,rcar-dmac"; - reg = <0 0xe6700000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 219>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 219>; - #dma-cells = <1>; - dma-channels = <15>; - }; + audma0: dma-controller@ec700000 { + compatible = "renesas,dmac-r8a7793", + "renesas,rcar-dmac"; + reg = <0 0xec700000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 502>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 502>; + #dma-cells = <1>; + dma-channels = <13>; + }; - dmac1: dma-controller@e6720000 { - compatible = "renesas,dmac-r8a7793", "renesas,rcar-dmac"; - reg = <0 0xe6720000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 218>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 218>; - #dma-cells = <1>; - dma-channels = <15>; - }; + audma1: dma-controller@ec720000 { + compatible = "renesas,dmac-r8a7793", + "renesas,rcar-dmac"; + reg = <0 0xec720000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 501>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 501>; + #dma-cells = <1>; + dma-channels = <13>; + }; - audma0: dma-controller@ec700000 { - compatible = "renesas,dmac-r8a7793", "renesas,rcar-dmac"; - reg = <0 0xec700000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 502>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 502>; - #dma-cells = <1>; - dma-channels = <13>; - }; + /* The memory map in the User's Manual maps the cores to + * bus numbers + */ + i2c0: i2c@e6508000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7793", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6508000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 931>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 931>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - audma1: dma-controller@ec720000 { - compatible = "renesas,dmac-r8a7793", "renesas,rcar-dmac"; - reg = <0 0xec720000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 501>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 501>; - #dma-cells = <1>; - dma-channels = <13>; - }; + i2c1: i2c@e6518000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7793", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6518000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 930>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 930>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - /* The memory map in the User's Manual maps the cores to bus numbers */ - i2c0: i2c@e6508000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7793", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6508000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 931>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 931>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c2: i2c@e6530000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7793", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6530000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 929>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 929>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c1: i2c@e6518000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7793", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6518000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 930>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 930>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c3: i2c@e6540000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7793", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6540000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 928>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 928>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c2: i2c@e6530000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7793", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6530000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 929>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 929>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c4: i2c@e6520000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7793", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6520000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 927>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 927>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c3: i2c@e6540000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7793", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6540000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 928>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 928>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c5: i2c@e6528000 { + /* doesn't need pinmux */ + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7793", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6528000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 925>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 925>; + i2c-scl-internal-delay-ns = <110>; + status = "disabled"; + }; - i2c4: i2c@e6520000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7793", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6520000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 927>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 927>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c6: i2c@e60b0000 { + /* doesn't need pinmux */ + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7793", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe60b0000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 926>; + dmas = <&dmac0 0x77>, <&dmac0 0x78>, + <&dmac1 0x77>, <&dmac1 0x78>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 926>; + status = "disabled"; + }; - i2c5: i2c@e6528000 { - /* doesn't need pinmux */ - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7793", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6528000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 925>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 925>; - i2c-scl-internal-delay-ns = <110>; - status = "disabled"; - }; + i2c7: i2c@e6500000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7793", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6500000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 318>; + dmas = <&dmac0 0x61>, <&dmac0 0x62>, + <&dmac1 0x61>, <&dmac1 0x62>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 318>; + status = "disabled"; + }; - i2c6: i2c@e60b0000 { - /* doesn't need pinmux */ - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7793", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe60b0000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 926>; - dmas = <&dmac0 0x77>, <&dmac0 0x78>, - <&dmac1 0x77>, <&dmac1 0x78>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 926>; - status = "disabled"; - }; + i2c8: i2c@e6510000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7793", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6510000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 323>; + dmas = <&dmac0 0x65>, <&dmac0 0x66>, + <&dmac1 0x65>, <&dmac1 0x66>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 323>; + status = "disabled"; + }; - i2c7: i2c@e6500000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7793", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6500000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 318>; - dmas = <&dmac0 0x61>, <&dmac0 0x62>, - <&dmac1 0x61>, <&dmac1 0x62>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 318>; - status = "disabled"; - }; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7793"; + reg = <0 0xe6060000 0 0x250>; + }; - i2c8: i2c@e6510000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7793", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6510000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 323>; - dmas = <&dmac0 0x65>, <&dmac0 0x66>, - <&dmac1 0x65>, <&dmac1 0x66>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 323>; - status = "disabled"; - }; + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7793", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7793"; - reg = <0 0xe6060000 0 0x250>; - }; + sdhi1: sd@ee140000 { + compatible = "renesas,sdhi-r8a7793", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee140000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, + <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 312>; + status = "disabled"; + }; - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7793", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 314>; - status = "disabled"; - }; + sdhi2: sd@ee160000 { + compatible = "renesas,sdhi-r8a7793", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee160000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, + <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 311>; + status = "disabled"; + }; - sdhi1: sd@ee140000 { - compatible = "renesas,sdhi-r8a7793", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee140000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, - <&dmac1 0xc1>, <&dmac1 0xc2>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 312>; - status = "disabled"; - }; + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7793", + "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 315>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 315>; + reg-io-width = <4>; + status = "disabled"; + max-frequency = <97500000>; + }; - sdhi2: sd@ee160000 { - compatible = "renesas,sdhi-r8a7793", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee160000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, - <&dmac1 0xd3>, <&dmac1 0xd4>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 311>; - status = "disabled"; - }; + scifa0: serial@e6c40000 { + compatible = "renesas,scifa-r8a7793", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c40000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 204>; + clock-names = "fck"; + dmas = <&dmac0 0x21>, <&dmac0 0x22>, + <&dmac1 0x21>, <&dmac1 0x22>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 204>; + status = "disabled"; + }; - mmcif0: mmc@ee200000 { - compatible = "renesas,mmcif-r8a7793", "renesas,sh-mmcif"; - reg = <0 0xee200000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 315>; - dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, - <&dmac1 0xd1>, <&dmac1 0xd2>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 315>; - reg-io-width = <4>; - status = "disabled"; - max-frequency = <97500000>; - }; + scifa1: serial@e6c50000 { + compatible = "renesas,scifa-r8a7793", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c50000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 203>; + clock-names = "fck"; + dmas = <&dmac0 0x25>, <&dmac0 0x26>, + <&dmac1 0x25>, <&dmac1 0x26>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 203>; + status = "disabled"; + }; - scifa0: serial@e6c40000 { - compatible = "renesas,scifa-r8a7793", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c40000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 204>; - clock-names = "fck"; - dmas = <&dmac0 0x21>, <&dmac0 0x22>, - <&dmac1 0x21>, <&dmac1 0x22>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 204>; - status = "disabled"; - }; + scifa2: serial@e6c60000 { + compatible = "renesas,scifa-r8a7793", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 202>; + clock-names = "fck"; + dmas = <&dmac0 0x27>, <&dmac0 0x28>, + <&dmac1 0x27>, <&dmac1 0x28>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 202>; + status = "disabled"; + }; - scifa1: serial@e6c50000 { - compatible = "renesas,scifa-r8a7793", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c50000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 203>; - clock-names = "fck"; - dmas = <&dmac0 0x25>, <&dmac0 0x26>, - <&dmac1 0x25>, <&dmac1 0x26>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 203>; - status = "disabled"; - }; + scifa3: serial@e6c70000 { + compatible = "renesas,scifa-r8a7793", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c70000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1106>; + clock-names = "fck"; + dmas = <&dmac0 0x1b>, <&dmac0 0x1c>, + <&dmac1 0x1b>, <&dmac1 0x1c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 1106>; + status = "disabled"; + }; - scifa2: serial@e6c60000 { - compatible = "renesas,scifa-r8a7793", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 202>; - clock-names = "fck"; - dmas = <&dmac0 0x27>, <&dmac0 0x28>, - <&dmac1 0x27>, <&dmac1 0x28>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 202>; - status = "disabled"; - }; + scifa4: serial@e6c78000 { + compatible = "renesas,scifa-r8a7793", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c78000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1107>; + clock-names = "fck"; + dmas = <&dmac0 0x1f>, <&dmac0 0x20>, + <&dmac1 0x1f>, <&dmac1 0x20>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 1107>; + status = "disabled"; + }; - scifa3: serial@e6c70000 { - compatible = "renesas,scifa-r8a7793", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c70000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1106>; - clock-names = "fck"; - dmas = <&dmac0 0x1b>, <&dmac0 0x1c>, - <&dmac1 0x1b>, <&dmac1 0x1c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 1106>; - status = "disabled"; - }; + scifa5: serial@e6c80000 { + compatible = "renesas,scifa-r8a7793", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c80000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1108>; + clock-names = "fck"; + dmas = <&dmac0 0x23>, <&dmac0 0x24>, + <&dmac1 0x23>, <&dmac1 0x24>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 1108>; + status = "disabled"; + }; - scifa4: serial@e6c78000 { - compatible = "renesas,scifa-r8a7793", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c78000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1107>; - clock-names = "fck"; - dmas = <&dmac0 0x1f>, <&dmac0 0x20>, - <&dmac1 0x1f>, <&dmac1 0x20>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 1107>; - status = "disabled"; - }; + scifb0: serial@e6c20000 { + compatible = "renesas,scifb-r8a7793", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c20000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 206>; + clock-names = "fck"; + dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, + <&dmac1 0x3d>, <&dmac1 0x3e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 206>; + status = "disabled"; + }; - scifa5: serial@e6c80000 { - compatible = "renesas,scifa-r8a7793", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c80000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1108>; - clock-names = "fck"; - dmas = <&dmac0 0x23>, <&dmac0 0x24>, - <&dmac1 0x23>, <&dmac1 0x24>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 1108>; - status = "disabled"; - }; + scifb1: serial@e6c30000 { + compatible = "renesas,scifb-r8a7793", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c30000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 207>; + clock-names = "fck"; + dmas = <&dmac0 0x19>, <&dmac0 0x1a>, + <&dmac1 0x19>, <&dmac1 0x1a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 207>; + status = "disabled"; + }; - scifb0: serial@e6c20000 { - compatible = "renesas,scifb-r8a7793", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c20000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 206>; - clock-names = "fck"; - dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, - <&dmac1 0x3d>, <&dmac1 0x3e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 206>; - status = "disabled"; - }; + scifb2: serial@e6ce0000 { + compatible = "renesas,scifb-r8a7793", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6ce0000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 216>; + clock-names = "fck"; + dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, + <&dmac1 0x1d>, <&dmac1 0x1e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 216>; + status = "disabled"; + }; - scifb1: serial@e6c30000 { - compatible = "renesas,scifb-r8a7793", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c30000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 207>; - clock-names = "fck"; - dmas = <&dmac0 0x19>, <&dmac0 0x1a>, - <&dmac1 0x19>, <&dmac1 0x1a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 207>; - status = "disabled"; - }; + scif0: serial@e6e60000 { + compatible = "renesas,scif-r8a7793", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6e60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7793_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x29>, <&dmac0 0x2a>, + <&dmac1 0x29>, <&dmac1 0x2a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 721>; + status = "disabled"; + }; - scifb2: serial@e6ce0000 { - compatible = "renesas,scifb-r8a7793", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6ce0000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 216>; - clock-names = "fck"; - dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, - <&dmac1 0x1d>, <&dmac1 0x1e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 216>; - status = "disabled"; - }; + scif1: serial@e6e68000 { + compatible = "renesas,scif-r8a7793", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6e68000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 720>, <&cpg CPG_CORE R8A7793_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, + <&dmac1 0x2d>, <&dmac1 0x2e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 720>; + status = "disabled"; + }; - scif0: serial@e6e60000 { - compatible = "renesas,scif-r8a7793", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7793_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x29>, <&dmac0 0x2a>, - <&dmac1 0x29>, <&dmac1 0x2a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 721>; - status = "disabled"; - }; + scif2: serial@e6e58000 { + compatible = "renesas,scif-r8a7793", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6e58000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 719>, <&cpg CPG_CORE R8A7793_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, + <&dmac1 0x2b>, <&dmac1 0x2c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 719>; + status = "disabled"; + }; - scif1: serial@e6e68000 { - compatible = "renesas,scif-r8a7793", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e68000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 720>, <&cpg CPG_CORE R8A7793_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, - <&dmac1 0x2d>, <&dmac1 0x2e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 720>; - status = "disabled"; - }; + scif3: serial@e6ea8000 { + compatible = "renesas,scif-r8a7793", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ea8000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 718>, <&cpg CPG_CORE R8A7793_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2f>, <&dmac0 0x30>, + <&dmac1 0x2f>, <&dmac1 0x30>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 718>; + status = "disabled"; + }; - scif2: serial@e6e58000 { - compatible = "renesas,scif-r8a7793", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e58000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 719>, <&cpg CPG_CORE R8A7793_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, - <&dmac1 0x2b>, <&dmac1 0x2c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 719>; - status = "disabled"; - }; + scif4: serial@e6ee0000 { + compatible = "renesas,scif-r8a7793", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ee0000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 715>, <&cpg CPG_CORE R8A7793_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0xfb>, <&dmac0 0xfc>, + <&dmac1 0xfb>, <&dmac1 0xfc>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 715>; + status = "disabled"; + }; - scif3: serial@e6ea8000 { - compatible = "renesas,scif-r8a7793", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6ea8000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 718>, <&cpg CPG_CORE R8A7793_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2f>, <&dmac0 0x30>, - <&dmac1 0x2f>, <&dmac1 0x30>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 718>; - status = "disabled"; - }; + scif5: serial@e6ee8000 { + compatible = "renesas,scif-r8a7793", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ee8000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 714>, <&cpg CPG_CORE R8A7793_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0xfd>, <&dmac0 0xfe>, + <&dmac1 0xfd>, <&dmac1 0xfe>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 714>; + status = "disabled"; + }; - scif4: serial@e6ee0000 { - compatible = "renesas,scif-r8a7793", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6ee0000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 715>, <&cpg CPG_CORE R8A7793_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0xfb>, <&dmac0 0xfc>, - <&dmac1 0xfb>, <&dmac1 0xfc>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 715>; - status = "disabled"; - }; + hscif0: serial@e62c0000 { + compatible = "renesas,hscif-r8a7793", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c0000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 717>, <&cpg CPG_CORE R8A7793_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x39>, <&dmac0 0x3a>, + <&dmac1 0x39>, <&dmac1 0x3a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 717>; + status = "disabled"; + }; - scif5: serial@e6ee8000 { - compatible = "renesas,scif-r8a7793", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6ee8000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 714>, <&cpg CPG_CORE R8A7793_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0xfd>, <&dmac0 0xfe>, - <&dmac1 0xfd>, <&dmac1 0xfe>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 714>; - status = "disabled"; - }; + hscif1: serial@e62c8000 { + compatible = "renesas,hscif-r8a7793", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c8000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 716>, <&cpg CPG_CORE R8A7793_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, + <&dmac1 0x4d>, <&dmac1 0x4e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 716>; + status = "disabled"; + }; - hscif0: serial@e62c0000 { - compatible = "renesas,hscif-r8a7793", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c0000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 717>, <&cpg CPG_CORE R8A7793_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x39>, <&dmac0 0x3a>, - <&dmac1 0x39>, <&dmac1 0x3a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 717>; - status = "disabled"; - }; + hscif2: serial@e62d0000 { + compatible = "renesas,hscif-r8a7793", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62d0000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 713>, <&cpg CPG_CORE R8A7793_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, + <&dmac1 0x3b>, <&dmac1 0x3c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 713>; + status = "disabled"; + }; - hscif1: serial@e62c8000 { - compatible = "renesas,hscif-r8a7793", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c8000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 716>, <&cpg CPG_CORE R8A7793_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, - <&dmac1 0x4d>, <&dmac1 0x4e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 716>; - status = "disabled"; - }; + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; + }; - hscif2: serial@e62d0000 { - compatible = "renesas,hscif-r8a7793", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62d0000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 713>, <&cpg CPG_CORE R8A7793_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, - <&dmac1 0x3b>, <&dmac1 0x3c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 713>; - status = "disabled"; - }; + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; + }; - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; + ether: ethernet@ee700000 { + compatible = "renesas,ether-r8a7793", + "renesas,rcar-gen2-ether"; + reg = <0 0xee700000 0 0x400>; + interrupts = ; + clocks = <&cpg CPG_MOD 813>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 813>; + phy-mode = "rmii"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; + vin0: video@e6ef0000 { + compatible = "renesas,vin-r8a7793", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef0000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 811>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 811>; + status = "disabled"; }; - }; - ether: ethernet@ee700000 { - compatible = "renesas,ether-r8a7793", - "renesas,rcar-gen2-ether"; - reg = <0 0xee700000 0 0x400>; - interrupts = ; - clocks = <&cpg CPG_MOD 813>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 813>; - phy-mode = "rmii"; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + vin1: video@e6ef1000 { + compatible = "renesas,vin-r8a7793", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef1000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 810>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 810>; + status = "disabled"; + }; - vin0: video@e6ef0000 { - compatible = "renesas,vin-r8a7793", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef0000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 811>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 811>; - status = "disabled"; - }; + vin2: video@e6ef2000 { + compatible = "renesas,vin-r8a7793", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef2000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 809>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 809>; + status = "disabled"; + }; - vin1: video@e6ef1000 { - compatible = "renesas,vin-r8a7793", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef1000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 810>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 810>; - status = "disabled"; - }; + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7793", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 917>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - vin2: video@e6ef2000 { - compatible = "renesas,vin-r8a7793", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef2000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 809>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 809>; - status = "disabled"; - }; + du: display@feb00000 { + compatible = "renesas,du-r8a7793"; + reg = <0 0xfeb00000 0 0x40000>, + <0 0xfeb90000 0 0x1c>; + reg-names = "du", "lvds.0"; + interrupts = , + ; + clocks = <&cpg CPG_MOD 724>, + <&cpg CPG_MOD 723>, + <&cpg CPG_MOD 726>; + clock-names = "du.0", "du.1", "lvds.0"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb: endpoint { + }; + }; + port@1 { + reg = <1>; + du_out_lvds0: endpoint { + }; + }; + }; + }; - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7793", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 917>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + can0: can@e6e80000 { + compatible = "renesas,can-r8a7793", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e80000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 916>, <&cpg CPG_CORE R8A7793_CLK_RCAN>, + <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 916>; + status = "disabled"; + }; - du: display@feb00000 { - compatible = "renesas,du-r8a7793"; - reg = <0 0xfeb00000 0 0x40000>, - <0 0xfeb90000 0 0x1c>; - reg-names = "du", "lvds.0"; - interrupts = , - ; - clocks = <&cpg CPG_MOD 724>, - <&cpg CPG_MOD 723>, - <&cpg CPG_MOD 726>; - clock-names = "du.0", "du.1", "lvds.0"; - status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; + can1: can@e6e88000 { + compatible = "renesas,can-r8a7793", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e88000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 915>, <&cpg CPG_CORE R8A7793_CLK_RCAN>, + <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 915>; + status = "disabled"; + }; + + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7793-rst"; + reg = <0 0xe6160000 0 0x0100>; + }; + + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; + }; + + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7793-sysc"; + reg = <0 0xe6180000 0 0x0200>; + #power-domain-cells = <1>; + }; + + ipmmu_sy0: mmu@e6280000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6280000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; - port@0 { - reg = <0>; - du_out_rgb: endpoint { + ipmmu_sy1: mmu@e6290000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6290000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_ds: mmu@e6740000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6740000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mp: mmu@ec680000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xec680000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mx: mmu@fe951000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xfe951000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_rt: mmu@ffc80000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xffc80000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_gp: mmu@e62a0000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xe62a0000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + rcar_sound: sound@ec500000 { + /* + * #sound-dai-cells is required + * + * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; + * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; + */ + compatible = "renesas,rcar_sound-r8a7793", + "renesas,rcar_sound-gen2"; + reg = <0 0xec500000 0 0x1000>, /* SCU */ + <0 0xec5a0000 0 0x100>, /* ADG */ + <0 0xec540000 0 0x1000>, /* SSIU */ + <0 0xec541000 0 0x280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ + reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; + + clocks = <&cpg CPG_MOD 1005>, + <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, + <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, + <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, + <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, + <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, + <&cpg CPG_MOD 1022>, <&cpg CPG_MOD 1023>, + <&cpg CPG_MOD 1024>, <&cpg CPG_MOD 1025>, + <&cpg CPG_MOD 1026>, <&cpg CPG_MOD 1027>, + <&cpg CPG_MOD 1028>, <&cpg CPG_MOD 1029>, + <&cpg CPG_MOD 1030>, <&cpg CPG_MOD 1031>, + <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, + <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, + <&cpg CPG_CORE R8A7793_CLK_M2>; + clock-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0", + "src.9", "src.8", "src.7", "src.6", + "src.5", "src.4", "src.3", "src.2", + "src.1", "src.0", + "dvc.0", "dvc.1", + "clk_a", "clk_b", "clk_c", "clk_i"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 1005>, + <&cpg 1006>, <&cpg 1007>, + <&cpg 1008>, <&cpg 1009>, + <&cpg 1010>, <&cpg 1011>, + <&cpg 1012>, <&cpg 1013>, + <&cpg 1014>, <&cpg 1015>; + reset-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0"; + + status = "disabled"; + + rcar_sound,dvc { + dvc0: dvc-0 { + dmas = <&audma1 0xbc>; + dma-names = "tx"; + }; + dvc1: dvc-1 { + dmas = <&audma1 0xbe>; + dma-names = "tx"; }; }; - port@1 { - reg = <1>; - du_out_lvds0: endpoint { + + rcar_sound,src { + src0: src-0 { + interrupts = ; + dmas = <&audma0 0x85>, <&audma1 0x9a>; + dma-names = "rx", "tx"; + }; + src1: src-1 { + interrupts = ; + dmas = <&audma0 0x87>, <&audma1 0x9c>; + dma-names = "rx", "tx"; + }; + src2: src-2 { + interrupts = ; + dmas = <&audma0 0x89>, <&audma1 0x9e>; + dma-names = "rx", "tx"; + }; + src3: src-3 { + interrupts = ; + dmas = <&audma0 0x8b>, <&audma1 0xa0>; + dma-names = "rx", "tx"; + }; + src4: src-4 { + interrupts = ; + dmas = <&audma0 0x8d>, <&audma1 0xb0>; + dma-names = "rx", "tx"; + }; + src5: src-5 { + interrupts = ; + dmas = <&audma0 0x8f>, <&audma1 0xb2>; + dma-names = "rx", "tx"; + }; + src6: src-6 { + interrupts = ; + dmas = <&audma0 0x91>, <&audma1 0xb4>; + dma-names = "rx", "tx"; + }; + src7: src-7 { + interrupts = ; + dmas = <&audma0 0x93>, <&audma1 0xb6>; + dma-names = "rx", "tx"; + }; + src8: src-8 { + interrupts = ; + dmas = <&audma0 0x95>, <&audma1 0xb8>; + dma-names = "rx", "tx"; + }; + src9: src-9 { + interrupts = ; + dmas = <&audma0 0x97>, <&audma1 0xba>; + dma-names = "rx", "tx"; + }; + }; + + rcar_sound,ssi { + ssi0: ssi-0 { + interrupts = ; + dmas = <&audma0 0x01>, <&audma1 0x02>, + <&audma0 0x15>, <&audma1 0x16>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi1: ssi-1 { + interrupts = ; + dmas = <&audma0 0x03>, <&audma1 0x04>, + <&audma0 0x49>, <&audma1 0x4a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi2: ssi-2 { + interrupts = ; + dmas = <&audma0 0x05>, <&audma1 0x06>, + <&audma0 0x63>, <&audma1 0x64>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi3: ssi-3 { + interrupts = ; + dmas = <&audma0 0x07>, <&audma1 0x08>, + <&audma0 0x6f>, <&audma1 0x70>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi4: ssi-4 { + interrupts = ; + dmas = <&audma0 0x09>, <&audma1 0x0a>, + <&audma0 0x71>, <&audma1 0x72>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi5: ssi-5 { + interrupts = ; + dmas = <&audma0 0x0b>, <&audma1 0x0c>, + <&audma0 0x73>, <&audma1 0x74>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi6: ssi-6 { + interrupts = ; + dmas = <&audma0 0x0d>, <&audma1 0x0e>, + <&audma0 0x75>, <&audma1 0x76>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi7: ssi-7 { + interrupts = ; + dmas = <&audma0 0x0f>, <&audma1 0x10>, + <&audma0 0x79>, <&audma1 0x7a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi8: ssi-8 { + interrupts = ; + dmas = <&audma0 0x11>, <&audma1 0x12>, + <&audma0 0x7b>, <&audma1 0x7c>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi9: ssi-9 { + interrupts = ; + dmas = <&audma0 0x13>, <&audma1 0x14>, + <&audma0 0x7d>, <&audma1 0x7e>; + dma-names = "rx", "tx", "rxu", "txu"; }; }; }; + + /* Special CPG clocks */ + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7793-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&usb_extal_clk>; + clock-names = "extal", "usb_extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; }; - can0: can@e6e80000 { - compatible = "renesas,can-r8a7793", "renesas,rcar-gen2-can"; - reg = <0 0xe6e80000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 916>, <&cpg CPG_CORE R8A7793_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 916>; - status = "disabled"; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + + thermal-sensors = <&thermal>; + + trips { + cpu-crit { + temperature = <95000>; + hysteresis = <0>; + type = "critical"; + }; + }; + cooling-maps { + }; + }; }; - can1: can@e6e88000 { - compatible = "renesas,can-r8a7793", "renesas,rcar-gen2-can"; - reg = <0 0xe6e88000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 915>, <&cpg CPG_CORE R8A7793_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 915>; - status = "disabled"; + timer { + compatible = "arm,armv7-timer"; + interrupts-extended = <&gic GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>; }; /* External root clock */ @@ -1079,257 +1393,4 @@ /* This value must be overridden by the board. */ clock-frequency = <0>; }; - - /* Special CPG clocks */ - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7793-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>, <&usb_extal_clk>; - clock-names = "extal", "usb_extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; - }; - - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7793-rst"; - reg = <0 0xe6160000 0 0x0100>; - }; - - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; - }; - - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7793-sysc"; - reg = <0 0xe6180000 0 0x0200>; - #power-domain-cells = <1>; - }; - - ipmmu_sy0: mmu@e6280000 { - compatible = "renesas,ipmmu-r8a7793", "renesas,ipmmu-vmsa"; - reg = <0 0xe6280000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_sy1: mmu@e6290000 { - compatible = "renesas,ipmmu-r8a7793", "renesas,ipmmu-vmsa"; - reg = <0 0xe6290000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_ds: mmu@e6740000 { - compatible = "renesas,ipmmu-r8a7793", "renesas,ipmmu-vmsa"; - reg = <0 0xe6740000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mp: mmu@ec680000 { - compatible = "renesas,ipmmu-r8a7793", "renesas,ipmmu-vmsa"; - reg = <0 0xec680000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mx: mmu@fe951000 { - compatible = "renesas,ipmmu-r8a7793", "renesas,ipmmu-vmsa"; - reg = <0 0xfe951000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_rt: mmu@ffc80000 { - compatible = "renesas,ipmmu-r8a7793", "renesas,ipmmu-vmsa"; - reg = <0 0xffc80000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_gp: mmu@e62a0000 { - compatible = "renesas,ipmmu-r8a7793", "renesas,ipmmu-vmsa"; - reg = <0 0xe62a0000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - rcar_sound: sound@ec500000 { - /* - * #sound-dai-cells is required - * - * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; - * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; - */ - compatible = "renesas,rcar_sound-r8a7793", - "renesas,rcar_sound-gen2"; - reg = <0 0xec500000 0 0x1000>, /* SCU */ - <0 0xec5a0000 0 0x100>, /* ADG */ - <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x280>, /* SSI */ - <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ - reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; - - clocks = <&cpg CPG_MOD 1005>, - <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, - <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, - <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, - <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, - <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, - <&cpg CPG_MOD 1022>, <&cpg CPG_MOD 1023>, - <&cpg CPG_MOD 1024>, <&cpg CPG_MOD 1025>, - <&cpg CPG_MOD 1026>, <&cpg CPG_MOD 1027>, - <&cpg CPG_MOD 1028>, <&cpg CPG_MOD 1029>, - <&cpg CPG_MOD 1030>, <&cpg CPG_MOD 1031>, - <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, - <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, - <&cpg CPG_CORE R8A7793_CLK_M2>; - clock-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", - "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0", - "src.9", "src.8", "src.7", "src.6", "src.5", - "src.4", "src.3", "src.2", "src.1", "src.0", - "dvc.0", "dvc.1", - "clk_a", "clk_b", "clk_c", "clk_i"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 1005>, - <&cpg 1006>, <&cpg 1007>, <&cpg 1008>, <&cpg 1009>, - <&cpg 1010>, <&cpg 1011>, <&cpg 1012>, <&cpg 1013>, - <&cpg 1014>, <&cpg 1015>; - reset-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", - "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0"; - - status = "disabled"; - - rcar_sound,dvc { - dvc0: dvc-0 { - dmas = <&audma1 0xbc>; - dma-names = "tx"; - }; - dvc1: dvc-1 { - dmas = <&audma1 0xbe>; - dma-names = "tx"; - }; - }; - - rcar_sound,src { - src0: src-0 { - interrupts = ; - dmas = <&audma0 0x85>, <&audma1 0x9a>; - dma-names = "rx", "tx"; - }; - src1: src-1 { - interrupts = ; - dmas = <&audma0 0x87>, <&audma1 0x9c>; - dma-names = "rx", "tx"; - }; - src2: src-2 { - interrupts = ; - dmas = <&audma0 0x89>, <&audma1 0x9e>; - dma-names = "rx", "tx"; - }; - src3: src-3 { - interrupts = ; - dmas = <&audma0 0x8b>, <&audma1 0xa0>; - dma-names = "rx", "tx"; - }; - src4: src-4 { - interrupts = ; - dmas = <&audma0 0x8d>, <&audma1 0xb0>; - dma-names = "rx", "tx"; - }; - src5: src-5 { - interrupts = ; - dmas = <&audma0 0x8f>, <&audma1 0xb2>; - dma-names = "rx", "tx"; - }; - src6: src-6 { - interrupts = ; - dmas = <&audma0 0x91>, <&audma1 0xb4>; - dma-names = "rx", "tx"; - }; - src7: src-7 { - interrupts = ; - dmas = <&audma0 0x93>, <&audma1 0xb6>; - dma-names = "rx", "tx"; - }; - src8: src-8 { - interrupts = ; - dmas = <&audma0 0x95>, <&audma1 0xb8>; - dma-names = "rx", "tx"; - }; - src9: src-9 { - interrupts = ; - dmas = <&audma0 0x97>, <&audma1 0xba>; - dma-names = "rx", "tx"; - }; - }; - - rcar_sound,ssi { - ssi0: ssi-0 { - interrupts = ; - dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi1: ssi-1 { - interrupts = ; - dmas = <&audma0 0x03>, <&audma1 0x04>, <&audma0 0x49>, <&audma1 0x4a>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi2: ssi-2 { - interrupts = ; - dmas = <&audma0 0x05>, <&audma1 0x06>, <&audma0 0x63>, <&audma1 0x64>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi3: ssi-3 { - interrupts = ; - dmas = <&audma0 0x07>, <&audma1 0x08>, <&audma0 0x6f>, <&audma1 0x70>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi4: ssi-4 { - interrupts = ; - dmas = <&audma0 0x09>, <&audma1 0x0a>, <&audma0 0x71>, <&audma1 0x72>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi5: ssi-5 { - interrupts = ; - dmas = <&audma0 0x0b>, <&audma1 0x0c>, <&audma0 0x73>, <&audma1 0x74>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi6: ssi-6 { - interrupts = ; - dmas = <&audma0 0x0d>, <&audma1 0x0e>, <&audma0 0x75>, <&audma1 0x76>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi7: ssi-7 { - interrupts = ; - dmas = <&audma0 0x0f>, <&audma1 0x10>, <&audma0 0x79>, <&audma1 0x7a>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi8: ssi-8 { - interrupts = ; - dmas = <&audma0 0x11>, <&audma1 0x12>, <&audma0 0x7b>, <&audma1 0x7c>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi9: ssi-9 { - interrupts = ; - dmas = <&audma0 0x13>, <&audma1 0x14>, <&audma0 0x7d>, <&audma1 0x7e>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - }; - }; }; -- cgit v1.3-14-g43fede From 40ed6d16cf06fd8b1320c74cb6669356caf8daa9 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:12 +0100 Subject: ARM: dts: r8a7793: sort subnodes of soc node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort the subnodes of the soc node to improve maintainability. The sort key is the address on the bus with instances of the same IP block grouped together. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7793.dtsi | 872 ++++++++++++++++++++--------------------- 1 file changed, 436 insertions(+), 436 deletions(-) diff --git a/arch/arm/boot/dts/r8a7793.dtsi b/arch/arm/boot/dts/r8a7793.dtsi index d18a65c647bb..f2b58a28cee9 100644 --- a/arch/arm/boot/dts/r8a7793.dtsi +++ b/arch/arm/boot/dts/r8a7793.dtsi @@ -81,28 +81,6 @@ #size-cells = <2>; ranges; - apmu@e6152000 { - compatible = "renesas,r8a7793-apmu", "renesas,apmu"; - reg = <0 0xe6152000 0 0x188>; - cpus = <&cpu0 &cpu1>; - }; - - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <0>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, - <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, - <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; - gpio0: gpio@e6050000 { compatible = "renesas,gpio-r8a7793", "renesas,rcar-gen2-gpio"; @@ -223,50 +201,37 @@ resets = <&cpg 904>; }; - thermal: thermal@e61f0000 { - compatible = "renesas,thermal-r8a7793", - "renesas,rcar-gen2-thermal", - "renesas,rcar-thermal"; - reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; - interrupts = ; - clocks = <&cpg CPG_MOD 522>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 522>; - #thermal-sensor-cells = <0>; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7793"; + reg = <0 0xe6060000 0 0x250>; }; - cmt0: timer@ffca0000 { - compatible = "renesas,r8a7793-cmt0", - "renesas,rcar-gen2-cmt0"; - reg = <0 0xffca0000 0 0x1004>; - interrupts = , - ; - clocks = <&cpg CPG_MOD 124>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 124>; + /* Special CPG clocks */ + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7793-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&usb_extal_clk>; + clock-names = "extal", "usb_extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; - status = "disabled"; + apmu@e6152000 { + compatible = "renesas,r8a7793-apmu", "renesas,apmu"; + reg = <0 0xe6152000 0 0x188>; + cpus = <&cpu0 &cpu1>; }; - cmt1: timer@e6130000 { - compatible = "renesas,r8a7793-cmt1", - "renesas,rcar-gen2-cmt1"; - reg = <0 0xe6130000 0 0x1004>; - interrupts = , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 329>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 329>; + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7793-rst"; + reg = <0 0xe6160000 0 0x0100>; + }; - status = "disabled"; + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7793-sysc"; + reg = <0 0xe6180000 0 0x0200>; + #power-domain-cells = <1>; }; irqc0: interrupt-controller@e61c0000 { @@ -289,132 +254,101 @@ resets = <&cpg 407>; }; - dmac0: dma-controller@e6700000 { - compatible = "renesas,dmac-r8a7793", - "renesas,rcar-dmac"; - reg = <0 0xe6700000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 219>; - clock-names = "fck"; + thermal: thermal@e61f0000 { + compatible = "renesas,thermal-r8a7793", + "renesas,rcar-gen2-thermal", + "renesas,rcar-thermal"; + reg = <0 0xe61f0000 0 0x10>, <0 0xe61f0100 0 0x38>; + interrupts = ; + clocks = <&cpg CPG_MOD 522>; power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 219>; - #dma-cells = <1>; - dma-channels = <15>; + resets = <&cpg 522>; + #thermal-sensor-cells = <0>; }; - dmac1: dma-controller@e6720000 { - compatible = "renesas,dmac-r8a7793", - "renesas,rcar-dmac"; - reg = <0 0xe6720000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 218>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 218>; - #dma-cells = <1>; - dma-channels = <15>; + ipmmu_sy0: mmu@e6280000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6280000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; }; - audma0: dma-controller@ec700000 { - compatible = "renesas,dmac-r8a7793", - "renesas,rcar-dmac"; - reg = <0 0xec700000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 502>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 502>; - #dma-cells = <1>; - dma-channels = <13>; + ipmmu_sy1: mmu@e6290000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6290000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; }; - audma1: dma-controller@ec720000 { - compatible = "renesas,dmac-r8a7793", - "renesas,rcar-dmac"; - reg = <0 0xec720000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 501>; - clock-names = "fck"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 501>; - #dma-cells = <1>; - dma-channels = <13>; + ipmmu_ds: mmu@e6740000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6740000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mp: mmu@ec680000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xec680000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mx: mmu@fe951000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xfe951000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_rt: mmu@ffc80000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xffc80000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_gp: mmu@e62a0000 { + compatible = "renesas,ipmmu-r8a7793", + "renesas,ipmmu-vmsa"; + reg = <0 0xe62a0000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; + }; + + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; + + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; }; /* The memory map in the User's Manual maps the cores to @@ -557,70 +491,86 @@ status = "disabled"; }; - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7793"; - reg = <0 0xe6060000 0 0x250>; - }; - - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7793", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 314>; - status = "disabled"; - }; - - sdhi1: sd@ee140000 { - compatible = "renesas,sdhi-r8a7793", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee140000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, - <&dmac1 0xc1>, <&dmac1 0xc2>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; + dmac0: dma-controller@e6700000 { + compatible = "renesas,dmac-r8a7793", + "renesas,rcar-dmac"; + reg = <0 0xe6700000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 219>; + clock-names = "fck"; power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 312>; - status = "disabled"; + resets = <&cpg 219>; + #dma-cells = <1>; + dma-channels = <15>; }; - sdhi2: sd@ee160000 { - compatible = "renesas,sdhi-r8a7793", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee160000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, - <&dmac1 0xd3>, <&dmac1 0xd4>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; + dmac1: dma-controller@e6720000 { + compatible = "renesas,dmac-r8a7793", + "renesas,rcar-dmac"; + reg = <0 0xe6720000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 218>; + clock-names = "fck"; power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 311>; - status = "disabled"; + resets = <&cpg 218>; + #dma-cells = <1>; + dma-channels = <15>; }; - mmcif0: mmc@ee200000 { - compatible = "renesas,mmcif-r8a7793", - "renesas,sh-mmcif"; - reg = <0 0xee200000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 315>; - dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, - <&dmac1 0xd1>, <&dmac1 0xd2>; + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7793", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 315>; - reg-io-width = <4>; + resets = <&cpg 917>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; status = "disabled"; - max-frequency = <97500000>; }; scifa0: serial@e6c40000 { @@ -902,117 +852,6 @@ status = "disabled"; }; - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; - - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; - - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; - }; - }; - - ether: ethernet@ee700000 { - compatible = "renesas,ether-r8a7793", - "renesas,rcar-gen2-ether"; - reg = <0 0xee700000 0 0x400>; - interrupts = ; - clocks = <&cpg CPG_MOD 813>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 813>; - phy-mode = "rmii"; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - vin0: video@e6ef0000 { - compatible = "renesas,vin-r8a7793", - "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef0000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 811>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 811>; - status = "disabled"; - }; - - vin1: video@e6ef1000 { - compatible = "renesas,vin-r8a7793", - "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef1000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 810>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 810>; - status = "disabled"; - }; - - vin2: video@e6ef2000 { - compatible = "renesas,vin-r8a7793", - "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef2000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 809>; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 809>; - status = "disabled"; - }; - - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7793", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; - resets = <&cpg 917>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - du: display@feb00000 { - compatible = "renesas,du-r8a7793"; - reg = <0 0xfeb00000 0 0x40000>, - <0 0xfeb90000 0 0x1c>; - reg-names = "du", "lvds.0"; - interrupts = , - ; - clocks = <&cpg CPG_MOD 724>, - <&cpg CPG_MOD 723>, - <&cpg CPG_MOD 726>; - clock-names = "du.0", "du.1", "lvds.0"; - status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - du_out_rgb: endpoint { - }; - }; - port@1 { - reg = <1>; - du_out_lvds0: endpoint { - }; - }; - }; - }; - can0: can@e6e80000 { compatible = "renesas,can-r8a7793", "renesas,rcar-gen2-can"; @@ -1039,86 +878,36 @@ status = "disabled"; }; - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7793-rst"; - reg = <0 0xe6160000 0 0x0100>; - }; - - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; - }; - - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7793-sysc"; - reg = <0 0xe6180000 0 0x0200>; - #power-domain-cells = <1>; - }; - - ipmmu_sy0: mmu@e6280000 { - compatible = "renesas,ipmmu-r8a7793", - "renesas,ipmmu-vmsa"; - reg = <0 0xe6280000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_sy1: mmu@e6290000 { - compatible = "renesas,ipmmu-r8a7793", - "renesas,ipmmu-vmsa"; - reg = <0 0xe6290000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_ds: mmu@e6740000 { - compatible = "renesas,ipmmu-r8a7793", - "renesas,ipmmu-vmsa"; - reg = <0 0xe6740000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mp: mmu@ec680000 { - compatible = "renesas,ipmmu-r8a7793", - "renesas,ipmmu-vmsa"; - reg = <0 0xec680000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mx: mmu@fe951000 { - compatible = "renesas,ipmmu-r8a7793", - "renesas,ipmmu-vmsa"; - reg = <0 0xfe951000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; + vin0: video@e6ef0000 { + compatible = "renesas,vin-r8a7793", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef0000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 811>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 811>; status = "disabled"; }; - ipmmu_rt: mmu@ffc80000 { - compatible = "renesas,ipmmu-r8a7793", - "renesas,ipmmu-vmsa"; - reg = <0 0xffc80000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; + vin1: video@e6ef1000 { + compatible = "renesas,vin-r8a7793", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef1000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 810>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 810>; status = "disabled"; }; - ipmmu_gp: mmu@e62a0000 { - compatible = "renesas,ipmmu-r8a7793", - "renesas,ipmmu-vmsa"; - reg = <0 0xe62a0000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; + vin2: video@e6ef2000 { + compatible = "renesas,vin-r8a7793", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef2000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 809>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 809>; status = "disabled"; }; @@ -1153,14 +942,14 @@ <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, <&cpg CPG_CORE R8A7793_CLK_M2>; clock-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", - "ssi.5", "ssi.4", "ssi.3", "ssi.2", - "ssi.1", "ssi.0", - "src.9", "src.8", "src.7", "src.6", - "src.5", "src.4", "src.3", "src.2", - "src.1", "src.0", - "dvc.0", "dvc.1", - "clk_a", "clk_b", "clk_c", "clk_i"; + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0", + "src.9", "src.8", "src.7", "src.6", + "src.5", "src.4", "src.3", "src.2", + "src.1", "src.0", + "dvc.0", "dvc.1", + "clk_a", "clk_b", "clk_c", "clk_i"; power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; resets = <&cpg 1005>, <&cpg 1006>, <&cpg 1007>, @@ -1303,15 +1092,226 @@ }; }; - /* Special CPG clocks */ - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7793-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>, <&usb_extal_clk>; - clock-names = "extal", "usb_extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; + audma0: dma-controller@ec700000 { + compatible = "renesas,dmac-r8a7793", + "renesas,rcar-dmac"; + reg = <0 0xec700000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 502>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 502>; + #dma-cells = <1>; + dma-channels = <13>; + }; + + audma1: dma-controller@ec720000 { + compatible = "renesas,dmac-r8a7793", + "renesas,rcar-dmac"; + reg = <0 0xec720000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 501>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 501>; + #dma-cells = <1>; + dma-channels = <13>; + }; + + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7793", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; + + sdhi1: sd@ee140000 { + compatible = "renesas,sdhi-r8a7793", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee140000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, + <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 312>; + status = "disabled"; + }; + + sdhi2: sd@ee160000 { + compatible = "renesas,sdhi-r8a7793", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee160000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, + <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 311>; + status = "disabled"; + }; + + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7793", + "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 315>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 315>; + reg-io-width = <4>; + status = "disabled"; + max-frequency = <97500000>; + }; + + ether: ethernet@ee700000 { + compatible = "renesas,ether-r8a7793", + "renesas,rcar-gen2-ether"; + reg = <0 0xee700000 0 0x400>; + interrupts = ; + clocks = <&cpg CPG_MOD 813>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 813>; + phy-mode = "rmii"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, + <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, + <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 408>; + }; + + du: display@feb00000 { + compatible = "renesas,du-r8a7793"; + reg = <0 0xfeb00000 0 0x40000>, + <0 0xfeb90000 0 0x1c>; + reg-names = "du", "lvds.0"; + interrupts = , + ; + clocks = <&cpg CPG_MOD 724>, + <&cpg CPG_MOD 723>, + <&cpg CPG_MOD 726>; + clock-names = "du.0", "du.1", "lvds.0"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb: endpoint { + }; + }; + port@1 { + reg = <1>; + du_out_lvds0: endpoint { + }; + }; + }; + }; + + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; + }; + + cmt0: timer@ffca0000 { + compatible = "renesas,r8a7793-cmt0", + "renesas,rcar-gen2-cmt0"; + reg = <0 0xffca0000 0 0x1004>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 124>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 124>; + + status = "disabled"; + }; + + cmt1: timer@e6130000 { + compatible = "renesas,r8a7793-cmt1", + "renesas,rcar-gen2-cmt1"; + reg = <0 0xe6130000 0 0x1004>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 329>; + clock-names = "fck"; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 329>; + + status = "disabled"; }; }; -- cgit v1.3-14-g43fede From ac24c5e52285154cca1c34488348e62f5288d6dc Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:13 +0100 Subject: ARM: dts: r8a7793: sort subnodes of root node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort the subnodes of the soc node to improve maintainability. The sort key is the address on the bus with instances of the same IP block grouped together. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7793.dtsi | 90 +++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/arch/arm/boot/dts/r8a7793.dtsi b/arch/arm/boot/dts/r8a7793.dtsi index f2b58a28cee9..aa7d7792fb13 100644 --- a/arch/arm/boot/dts/r8a7793.dtsi +++ b/arch/arm/boot/dts/r8a7793.dtsi @@ -31,6 +31,35 @@ spi0 = &qspi; }; + /* + * The external audio clocks are configured as 0 Hz fixed frequency + * clocks by default. + * Boards that provide audio clocks should override them. + */ + audio_clk_a: audio_clk_a { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + audio_clk_b: audio_clk_b { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + audio_clk_c: audio_clk_c { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + /* External CAN clock */ + can_clk: can { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + cpus { #address-cells = <1>; #size-cells = <0>; @@ -73,6 +102,22 @@ }; }; + /* External root clock */ + extal_clk: extal { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + + /* External SCIF clock */ + scif_clk: scif { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + soc { compatible = "simple-bus"; interrupt-parent = <&gic>; @@ -1342,55 +1387,10 @@ <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>; }; - /* External root clock */ - extal_clk: extal { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; - - /* - * The external audio clocks are configured as 0 Hz fixed frequency - * clocks by default. - * Boards that provide audio clocks should override them. - */ - audio_clk_a: audio_clk_a { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - audio_clk_b: audio_clk_b { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - audio_clk_c: audio_clk_c { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - /* External USB clock - can be overridden by the board */ usb_extal_clk: usb_extal { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <48000000>; }; - - /* External CAN clock */ - can_clk: can { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; - - /* External SCIF clock */ - scif_clk: scif { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; }; -- cgit v1.3-14-g43fede From b190cce35ff90e392f89a3d45e0814cdbfd7434c Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:14 +0100 Subject: ARM: dts: r8a7794: consistently use single space after = MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consistently use a single space after a =. This patch removes instances where a tab is used instead. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7794.dtsi | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi index 106b4e1649ff..79263377ded3 100644 --- a/arch/arm/boot/dts/r8a7794.dtsi +++ b/arch/arm/boot/dts/r8a7794.dtsi @@ -319,20 +319,20 @@ audma0: dma-controller@ec700000 { compatible = "renesas,dmac-r8a7794", "renesas,rcar-dmac"; reg = <0 0xec700000 0 0x10000>; - interrupts = ; + interrupts = ; interrupt-names = "error", "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7", "ch8", "ch9", "ch10", "ch11", @@ -1186,11 +1186,11 @@ */ compatible = "renesas,rcar_sound-r8a7794", "renesas,rcar_sound-gen2"; - reg = <0 0xec500000 0 0x1000>, /* SCU */ - <0 0xec5a0000 0 0x100>, /* ADG */ - <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x280>, /* SSI */ - <0 0xec740000 0 0x200>; /* Audio DMAC peri peri */ + reg = <0 0xec500000 0 0x1000>, /* SCU */ + <0 0xec5a0000 0 0x100>, /* ADG */ + <0 0xec540000 0 0x1000>, /* SSIU */ + <0 0xec541000 0 0x280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri */ reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; clocks = <&cpg CPG_MOD 1005>, -- cgit v1.3-14-g43fede From 0f2901d74a99e2e6c0aea2ef9b1820c7d0ed88e1 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:15 +0100 Subject: ARM: dts: r8a7794: add soc node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add soc node to represent the bus and move all nodes with a base address into this node. This is consistent with handling of R-Car Gen3, RZ/G1, and R-Car V2H (R8A77920) SoCs upstream. It is intended to migrate other R-Car Gen2 SoCs to this scheme. The ordering is derived from simply moving each node with an address up to before any nodes without a base address that occur before the soc node. To improve maintainability follow-up patches will sort subnodes of both the new soc node and the root node. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7794.dtsi | 2358 ++++++++++++++++++++-------------------- 1 file changed, 1205 insertions(+), 1153 deletions(-) diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi index 79263377ded3..0d65069b1a89 100644 --- a/arch/arm/boot/dts/r8a7794.dtsi +++ b/arch/arm/boot/dts/r8a7794.dtsi @@ -16,7 +16,6 @@ / { compatible = "renesas,r8a7794"; - interrupt-parent = <&gic>; #address-cells = <2>; #size-cells = <2>; @@ -67,985 +66,1293 @@ }; }; - apmu@e6151000 { - compatible = "renesas,r8a7794-apmu", "renesas,apmu"; - reg = <0 0xe6151000 0 0x188>; - cpus = <&cpu0 &cpu1>; - }; + soc { + compatible = "simple-bus"; + interrupt-parent = <&gic>; - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <0>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, - <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, - <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; + #address-cells = <2>; + #size-cells = <2>; + ranges; - gpio0: gpio@e6050000 { - compatible = "renesas,gpio-r8a7794", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6050000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 0 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 912>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 912>; - }; + apmu@e6151000 { + compatible = "renesas,r8a7794-apmu", "renesas,apmu"; + reg = <0 0xe6151000 0 0x188>; + cpus = <&cpu0 &cpu1>; + }; - gpio1: gpio@e6051000 { - compatible = "renesas,gpio-r8a7794", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6051000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 32 26>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 911>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 911>; - }; + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, + <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, + <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 408>; + }; - gpio2: gpio@e6052000 { - compatible = "renesas,gpio-r8a7794", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6052000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 64 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 910>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 910>; - }; + gpio0: gpio@e6050000 { + compatible = "renesas,gpio-r8a7794", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6050000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 0 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 912>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 912>; + }; - gpio3: gpio@e6053000 { - compatible = "renesas,gpio-r8a7794", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6053000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 96 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 909>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 909>; - }; + gpio1: gpio@e6051000 { + compatible = "renesas,gpio-r8a7794", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6051000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 32 26>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 911>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 911>; + }; - gpio4: gpio@e6054000 { - compatible = "renesas,gpio-r8a7794", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6054000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 128 32>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 908>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 908>; - }; + gpio2: gpio@e6052000 { + compatible = "renesas,gpio-r8a7794", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6052000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 64 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 910>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 910>; + }; - gpio5: gpio@e6055000 { - compatible = "renesas,gpio-r8a7794", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6055000 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 160 28>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 907>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 907>; - }; + gpio3: gpio@e6053000 { + compatible = "renesas,gpio-r8a7794", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6053000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 96 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 909>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 909>; + }; - gpio6: gpio@e6055400 { - compatible = "renesas,gpio-r8a7794", "renesas,rcar-gen2-gpio"; - reg = <0 0xe6055400 0 0x50>; - interrupts = ; - #gpio-cells = <2>; - gpio-controller; - gpio-ranges = <&pfc 0 192 26>; - #interrupt-cells = <2>; - interrupt-controller; - clocks = <&cpg CPG_MOD 905>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 905>; - }; + gpio4: gpio@e6054000 { + compatible = "renesas,gpio-r8a7794", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6054000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 128 32>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 908>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 908>; + }; - cmt0: timer@ffca0000 { - compatible = "renesas,r8a7794-cmt0", "renesas,rcar-gen2-cmt0"; - reg = <0 0xffca0000 0 0x1004>; - interrupts = , - ; - clocks = <&cpg CPG_MOD 124>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 124>; - - status = "disabled"; - }; + gpio5: gpio@e6055000 { + compatible = "renesas,gpio-r8a7794", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6055000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 160 28>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 907>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 907>; + }; - cmt1: timer@e6130000 { - compatible = "renesas,r8a7794-cmt1", "renesas,rcar-gen2-cmt1"; - reg = <0 0xe6130000 0 0x1004>; - interrupts = , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 329>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 329>; - - status = "disabled"; - }; + gpio6: gpio@e6055400 { + compatible = "renesas,gpio-r8a7794", + "renesas,rcar-gen2-gpio"; + reg = <0 0xe6055400 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 192 26>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 905>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 905>; + }; - timer { - compatible = "arm,armv7-timer"; - interrupts = , - , - , - ; - }; + cmt0: timer@ffca0000 { + compatible = "renesas,r8a7794-cmt0", + "renesas,rcar-gen2-cmt0"; + reg = <0 0xffca0000 0 0x1004>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 124>; + clock-names = "fck"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 124>; + + status = "disabled"; + }; - irqc0: interrupt-controller@e61c0000 { - compatible = "renesas,irqc-r8a7794", "renesas,irqc"; - #interrupt-cells = <2>; - interrupt-controller; - reg = <0 0xe61c0000 0 0x200>; - interrupts = , - , - , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 407>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 407>; - }; + cmt1: timer@e6130000 { + compatible = "renesas,r8a7794-cmt1", + "renesas,rcar-gen2-cmt1"; + reg = <0 0xe6130000 0 0x1004>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 329>; + clock-names = "fck"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 329>; + + status = "disabled"; + }; - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7794"; - reg = <0 0xe6060000 0 0x11c>; - }; + irqc0: interrupt-controller@e61c0000 { + compatible = "renesas,irqc-r8a7794", "renesas,irqc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0 0xe61c0000 0 0x200>; + interrupts = , + , + , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 407>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 407>; + }; - dmac0: dma-controller@e6700000 { - compatible = "renesas,dmac-r8a7794", "renesas,rcar-dmac"; - reg = <0 0xe6700000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 219>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 219>; - #dma-cells = <1>; - dma-channels = <15>; - }; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7794"; + reg = <0 0xe6060000 0 0x11c>; + }; - dmac1: dma-controller@e6720000 { - compatible = "renesas,dmac-r8a7794", "renesas,rcar-dmac"; - reg = <0 0xe6720000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 218>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 218>; - #dma-cells = <1>; - dma-channels = <15>; - }; + dmac0: dma-controller@e6700000 { + compatible = "renesas,dmac-r8a7794", + "renesas,rcar-dmac"; + reg = <0 0xe6700000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 219>; + clock-names = "fck"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 219>; + #dma-cells = <1>; + dma-channels = <15>; + }; - audma0: dma-controller@ec700000 { - compatible = "renesas,dmac-r8a7794", "renesas,rcar-dmac"; - reg = <0 0xec700000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", - "ch6", "ch7", "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 502>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 502>; - #dma-cells = <1>; - dma-channels = <13>; - }; + dmac1: dma-controller@e6720000 { + compatible = "renesas,dmac-r8a7794", + "renesas,rcar-dmac"; + reg = <0 0xe6720000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 218>; + clock-names = "fck"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 218>; + #dma-cells = <1>; + dma-channels = <15>; + }; - scifa0: serial@e6c40000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c40000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 204>; - clock-names = "fck"; - dmas = <&dmac0 0x21>, <&dmac0 0x22>, - <&dmac1 0x21>, <&dmac1 0x22>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 204>; - status = "disabled"; - }; + audma0: dma-controller@ec700000 { + compatible = "renesas,dmac-r8a7794", + "renesas,rcar-dmac"; + reg = <0 0xec700000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", "ch4", + "ch5", "ch6", "ch7", "ch8", "ch9", + "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 502>; + clock-names = "fck"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 502>; + #dma-cells = <1>; + dma-channels = <13>; + }; - scifa1: serial@e6c50000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c50000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 203>; - clock-names = "fck"; - dmas = <&dmac0 0x25>, <&dmac0 0x26>, - <&dmac1 0x25>, <&dmac1 0x26>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 203>; - status = "disabled"; - }; + scifa0: serial@e6c40000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c40000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 204>; + clock-names = "fck"; + dmas = <&dmac0 0x21>, <&dmac0 0x22>, + <&dmac1 0x21>, <&dmac1 0x22>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 204>; + status = "disabled"; + }; - scifa2: serial@e6c60000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 202>; - clock-names = "fck"; - dmas = <&dmac0 0x27>, <&dmac0 0x28>, - <&dmac1 0x27>, <&dmac1 0x28>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 202>; - status = "disabled"; - }; + scifa1: serial@e6c50000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c50000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 203>; + clock-names = "fck"; + dmas = <&dmac0 0x25>, <&dmac0 0x26>, + <&dmac1 0x25>, <&dmac1 0x26>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 203>; + status = "disabled"; + }; - scifa3: serial@e6c70000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c70000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1106>; - clock-names = "fck"; - dmas = <&dmac0 0x1b>, <&dmac0 0x1c>, - <&dmac1 0x1b>, <&dmac1 0x1c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 1106>; - status = "disabled"; - }; + scifa2: serial@e6c60000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 202>; + clock-names = "fck"; + dmas = <&dmac0 0x27>, <&dmac0 0x28>, + <&dmac1 0x27>, <&dmac1 0x28>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 202>; + status = "disabled"; + }; - scifa4: serial@e6c78000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c78000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1107>; - clock-names = "fck"; - dmas = <&dmac0 0x1f>, <&dmac0 0x20>, - <&dmac1 0x1f>, <&dmac1 0x20>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 1107>; - status = "disabled"; - }; + scifa3: serial@e6c70000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c70000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1106>; + clock-names = "fck"; + dmas = <&dmac0 0x1b>, <&dmac0 0x1c>, + <&dmac1 0x1b>, <&dmac1 0x1c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 1106>; + status = "disabled"; + }; - scifa5: serial@e6c80000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c80000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1108>; - clock-names = "fck"; - dmas = <&dmac0 0x23>, <&dmac0 0x24>, - <&dmac1 0x23>, <&dmac1 0x24>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 1108>; - status = "disabled"; - }; + scifa4: serial@e6c78000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c78000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1107>; + clock-names = "fck"; + dmas = <&dmac0 0x1f>, <&dmac0 0x20>, + <&dmac1 0x1f>, <&dmac1 0x20>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 1107>; + status = "disabled"; + }; - scifb0: serial@e6c20000 { - compatible = "renesas,scifb-r8a7794", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c20000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 206>; - clock-names = "fck"; - dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, - <&dmac1 0x3d>, <&dmac1 0x3e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 206>; - status = "disabled"; - }; + scifa5: serial@e6c80000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c80000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1108>; + clock-names = "fck"; + dmas = <&dmac0 0x23>, <&dmac0 0x24>, + <&dmac1 0x23>, <&dmac1 0x24>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 1108>; + status = "disabled"; + }; - scifb1: serial@e6c30000 { - compatible = "renesas,scifb-r8a7794", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c30000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 207>; - clock-names = "fck"; - dmas = <&dmac0 0x19>, <&dmac0 0x1a>, - <&dmac1 0x19>, <&dmac1 0x1a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 207>; - status = "disabled"; - }; + scifb0: serial@e6c20000 { + compatible = "renesas,scifb-r8a7794", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c20000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 206>; + clock-names = "fck"; + dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, + <&dmac1 0x3d>, <&dmac1 0x3e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 206>; + status = "disabled"; + }; - scifb2: serial@e6ce0000 { - compatible = "renesas,scifb-r8a7794", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6ce0000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 216>; - clock-names = "fck"; - dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, - <&dmac1 0x1d>, <&dmac1 0x1e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 216>; - status = "disabled"; - }; + scifb1: serial@e6c30000 { + compatible = "renesas,scifb-r8a7794", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c30000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 207>; + clock-names = "fck"; + dmas = <&dmac0 0x19>, <&dmac0 0x1a>, + <&dmac1 0x19>, <&dmac1 0x1a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 207>; + status = "disabled"; + }; - scif0: serial@e6e60000 { - compatible = "renesas,scif-r8a7794", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x29>, <&dmac0 0x2a>, - <&dmac1 0x29>, <&dmac1 0x2a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 721>; - status = "disabled"; - }; + scifb2: serial@e6ce0000 { + compatible = "renesas,scifb-r8a7794", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6ce0000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 216>; + clock-names = "fck"; + dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, + <&dmac1 0x1d>, <&dmac1 0x1e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 216>; + status = "disabled"; + }; - scif1: serial@e6e68000 { - compatible = "renesas,scif-r8a7794", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e68000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 720>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, - <&dmac1 0x2d>, <&dmac1 0x2e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 720>; - status = "disabled"; - }; + scif0: serial@e6e60000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", + "renesas,scif"; + reg = <0 0xe6e60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x29>, <&dmac0 0x2a>, + <&dmac1 0x29>, <&dmac1 0x2a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 721>; + status = "disabled"; + }; - scif2: serial@e6e58000 { - compatible = "renesas,scif-r8a7794", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e58000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 719>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, - <&dmac1 0x2b>, <&dmac1 0x2c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 719>; - status = "disabled"; - }; + scif1: serial@e6e68000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", + "renesas,scif"; + reg = <0 0xe6e68000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 720>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, + <&dmac1 0x2d>, <&dmac1 0x2e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 720>; + status = "disabled"; + }; - scif3: serial@e6ea8000 { - compatible = "renesas,scif-r8a7794", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6ea8000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 718>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2f>, <&dmac0 0x30>, - <&dmac1 0x2f>, <&dmac1 0x30>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 718>; - status = "disabled"; - }; + scif2: serial@e6e58000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6e58000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 719>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, + <&dmac1 0x2b>, <&dmac1 0x2c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 719>; + status = "disabled"; + }; - scif4: serial@e6ee0000 { - compatible = "renesas,scif-r8a7794", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6ee0000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 715>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0xfb>, <&dmac0 0xfc>, - <&dmac1 0xfb>, <&dmac1 0xfc>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 715>; - status = "disabled"; - }; + scif3: serial@e6ea8000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ea8000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 718>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2f>, <&dmac0 0x30>, + <&dmac1 0x2f>, <&dmac1 0x30>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 718>; + status = "disabled"; + }; - scif5: serial@e6ee8000 { - compatible = "renesas,scif-r8a7794", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6ee8000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 714>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0xfd>, <&dmac0 0xfe>, - <&dmac1 0xfd>, <&dmac1 0xfe>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 714>; - status = "disabled"; - }; + scif4: serial@e6ee0000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ee0000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 715>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0xfb>, <&dmac0 0xfc>, + <&dmac1 0xfb>, <&dmac1 0xfc>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 715>; + status = "disabled"; + }; - hscif0: serial@e62c0000 { - compatible = "renesas,hscif-r8a7794", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c0000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 717>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x39>, <&dmac0 0x3a>, - <&dmac1 0x39>, <&dmac1 0x3a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 717>; - status = "disabled"; - }; + scif5: serial@e6ee8000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ee8000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 714>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0xfd>, <&dmac0 0xfe>, + <&dmac1 0xfd>, <&dmac1 0xfe>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 714>; + status = "disabled"; + }; - hscif1: serial@e62c8000 { - compatible = "renesas,hscif-r8a7794", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c8000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 716>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, - <&dmac1 0x4d>, <&dmac1 0x4e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 716>; - status = "disabled"; - }; + hscif0: serial@e62c0000 { + compatible = "renesas,hscif-r8a7794", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c0000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 717>, + <&cpg CPG_CORE R8A7794_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x39>, <&dmac0 0x3a>, + <&dmac1 0x39>, <&dmac1 0x3a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 717>; + status = "disabled"; + }; - hscif2: serial@e62d0000 { - compatible = "renesas,hscif-r8a7794", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62d0000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 713>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, - <&dmac1 0x3b>, <&dmac1 0x3c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 713>; - status = "disabled"; - }; + hscif1: serial@e62c8000 { + compatible = "renesas,hscif-r8a7794", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c8000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 716>, + <&cpg CPG_CORE R8A7794_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, + <&dmac1 0x4d>, <&dmac1 0x4e>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 716>; + status = "disabled"; + }; - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; + hscif2: serial@e62d0000 { + compatible = "renesas,hscif-r8a7794", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62d0000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 713>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, + <&dmac1 0x3b>, <&dmac1 0x3c>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 713>; + status = "disabled"; + }; - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; + }; - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; + + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; }; - }; - ether: ethernet@ee700000 { - compatible = "renesas,ether-r8a7794", - "renesas,rcar-gen2-ether"; - reg = <0 0xee700000 0 0x400>; - interrupts = ; - clocks = <&cpg CPG_MOD 813>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 813>; - phy-mode = "rmii"; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + ether: ethernet@ee700000 { + compatible = "renesas,ether-r8a7794", + "renesas,rcar-gen2-ether"; + reg = <0 0xee700000 0 0x400>; + interrupts = ; + clocks = <&cpg CPG_MOD 813>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 813>; + phy-mode = "rmii"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - avb: ethernet@e6800000 { - compatible = "renesas,etheravb-r8a7794", - "renesas,etheravb-rcar-gen2"; - reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; - interrupts = ; - clocks = <&cpg CPG_MOD 812>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 812>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + avb: ethernet@e6800000 { + compatible = "renesas,etheravb-r8a7794", + "renesas,etheravb-rcar-gen2"; + reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; + interrupts = ; + clocks = <&cpg CPG_MOD 812>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 812>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - /* The memory map in the User's Manual maps the cores to bus numbers */ - i2c0: i2c@e6508000 { - compatible = "renesas,i2c-r8a7794", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6508000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 931>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 931>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + /* The memory map in the User's Manual maps the cores to + * bus numbers + */ + i2c0: i2c@e6508000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6508000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 931>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 931>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c1: i2c@e6518000 { - compatible = "renesas,i2c-r8a7794", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6518000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 930>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 930>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c1: i2c@e6518000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6518000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 930>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 930>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c2: i2c@e6530000 { - compatible = "renesas,i2c-r8a7794", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6530000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 929>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 929>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c2: i2c@e6530000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6530000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 929>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 929>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c3: i2c@e6540000 { - compatible = "renesas,i2c-r8a7794", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6540000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 928>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 928>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c3: i2c@e6540000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6540000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 928>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 928>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c4: i2c@e6520000 { - compatible = "renesas,i2c-r8a7794", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6520000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 927>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 927>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c4: i2c@e6520000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6520000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 927>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 927>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c5: i2c@e6528000 { - compatible = "renesas,i2c-r8a7794", "renesas,rcar-gen2-i2c"; - reg = <0 0xe6528000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 925>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 925>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; + i2c5: i2c@e6528000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6528000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 925>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 925>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; - i2c6: i2c@e6500000 { - compatible = "renesas,iic-r8a7794", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6500000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 318>; - dmas = <&dmac0 0x61>, <&dmac0 0x62>, - <&dmac1 0x61>, <&dmac1 0x62>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 318>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + i2c6: i2c@e6500000 { + compatible = "renesas,iic-r8a7794", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6500000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 318>; + dmas = <&dmac0 0x61>, <&dmac0 0x62>, + <&dmac1 0x61>, <&dmac1 0x62>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 318>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - i2c7: i2c@e6510000 { - compatible = "renesas,iic-r8a7794", "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6510000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 323>; - dmas = <&dmac0 0x65>, <&dmac0 0x66>, - <&dmac1 0x65>, <&dmac1 0x66>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 323>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + i2c7: i2c@e6510000 { + compatible = "renesas,iic-r8a7794", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6510000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 323>; + dmas = <&dmac0 0x65>, <&dmac0 0x66>, + <&dmac1 0x65>, <&dmac1 0x66>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 323>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - mmcif0: mmc@ee200000 { - compatible = "renesas,mmcif-r8a7794", "renesas,sh-mmcif"; - reg = <0 0xee200000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 315>; - dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, - <&dmac1 0xd1>, <&dmac1 0xd2>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 315>; - reg-io-width = <4>; - status = "disabled"; - }; + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7794", + "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 315>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 315>; + reg-io-width = <4>; + status = "disabled"; + }; - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7794", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 314>; - status = "disabled"; - }; + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7794", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; - sdhi1: sd@ee140000 { - compatible = "renesas,sdhi-r8a7794", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee140000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, - <&dmac1 0xc1>, <&dmac1 0xc2>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 312>; - status = "disabled"; - }; + sdhi1: sd@ee140000 { + compatible = "renesas,sdhi-r8a7794", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee140000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, + <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 312>; + status = "disabled"; + }; - sdhi2: sd@ee160000 { - compatible = "renesas,sdhi-r8a7794", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee160000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, - <&dmac1 0xd3>, <&dmac1 0xd4>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 311>; - status = "disabled"; - }; + sdhi2: sd@ee160000 { + compatible = "renesas,sdhi-r8a7794", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee160000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, + <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 311>; + status = "disabled"; + }; - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7794", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 917>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7794", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 917>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; - vin0: video@e6ef0000 { - compatible = "renesas,vin-r8a7794", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef0000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 811>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 811>; - status = "disabled"; - }; + vin0: video@e6ef0000 { + compatible = "renesas,vin-r8a7794", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef0000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 811>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 811>; + status = "disabled"; + }; - vin1: video@e6ef1000 { - compatible = "renesas,vin-r8a7794", "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef1000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 810>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 810>; - status = "disabled"; - }; + vin1: video@e6ef1000 { + compatible = "renesas,vin-r8a7794", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef1000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 810>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 810>; + status = "disabled"; + }; - pci0: pci@ee090000 { - compatible = "renesas,pci-r8a7794", "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee090000 0 0xc00>, - <0 0xee080000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <0 0>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x800 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; + pci0: pci@ee090000 { + compatible = "renesas,pci-r8a7794", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee090000 0 0xc00>, + <0 0xee080000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x800 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x1000 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; }; - usb@2,0 { - reg = <0x1000 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; + pci1: pci@ee0d0000 { + compatible = "renesas,pci-r8a7794", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee0d0000 0 0xc00>, + <0 0xee0c0000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <1 1>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x10800 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x11000 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; }; - }; - pci1: pci@ee0d0000 { - compatible = "renesas,pci-r8a7794", "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee0d0000 0 0xc00>, - <0 0xee0c0000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <1 1>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x10800 0 0 0 0>; - phys = <&usb2 0>; + hsusb: usb@e6590000 { + compatible = "renesas,usbhs-r8a7794", + "renesas,rcar-gen2-usbhs"; + reg = <0 0xe6590000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 704>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 704>; + renesas,buswait = <4>; + phys = <&usb0 1>; phy-names = "usb"; + status = "disabled"; }; - usb@2,0 { - reg = <0x11000 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; + usbphy: usb-phy@e6590100 { + compatible = "renesas,usb-phy-r8a7794", + "renesas,rcar-gen2-usb-phy"; + reg = <0 0xe6590100 0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&cpg CPG_MOD 704>; + clock-names = "usbhs"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 704>; + status = "disabled"; + + usb0: usb-channel@0 { + reg = <0>; + #phy-cells = <1>; + }; + usb2: usb-channel@2 { + reg = <2>; + #phy-cells = <1>; + }; }; - }; - hsusb: usb@e6590000 { - compatible = "renesas,usbhs-r8a7794", "renesas,rcar-gen2-usbhs"; - reg = <0 0xe6590000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 704>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 704>; - renesas,buswait = <4>; - phys = <&usb0 1>; - phy-names = "usb"; - status = "disabled"; - }; + vsp@fe928000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe928000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 131>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 131>; + }; - usbphy: usb-phy@e6590100 { - compatible = "renesas,usb-phy-r8a7794", - "renesas,rcar-gen2-usb-phy"; - reg = <0 0xe6590100 0 0x100>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&cpg CPG_MOD 704>; - clock-names = "usbhs"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 704>; - status = "disabled"; + vsp@fe930000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe930000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 128>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 128>; + }; - usb0: usb-channel@0 { - reg = <0>; - #phy-cells = <1>; + du: display@feb00000 { + compatible = "renesas,du-r8a7794"; + reg = <0 0xfeb00000 0 0x40000>; + reg-names = "du"; + interrupts = , + ; + clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>; + clock-names = "du.0", "du.1"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb0: endpoint { + }; + }; + port@1 { + reg = <1>; + du_out_rgb1: endpoint { + }; + }; + }; }; - usb2: usb-channel@2 { - reg = <2>; - #phy-cells = <1>; + + can0: can@e6e80000 { + compatible = "renesas,can-r8a7794", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e80000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 916>, <&cpg CPG_CORE R8A7794_CLK_RCAN>, + <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 916>; + status = "disabled"; }; - }; - vsp@fe928000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe928000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 131>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 131>; - }; + can1: can@e6e88000 { + compatible = "renesas,can-r8a7794", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e88000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 915>, <&cpg CPG_CORE R8A7794_CLK_RCAN>, + <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 915>; + status = "disabled"; + }; - vsp@fe930000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe930000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 128>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 128>; - }; + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7794-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&usb_extal_clk>; + clock-names = "extal", "usb_extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; - du: display@feb00000 { - compatible = "renesas,du-r8a7794"; - reg = <0 0xfeb00000 0 0x40000>; - reg-names = "du"; - interrupts = , - ; - clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>; - clock-names = "du.0", "du.1"; - status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7794-rst"; + reg = <0 0xe6160000 0 0x0100>; + }; - port@0 { - reg = <0>; - du_out_rgb0: endpoint { + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; + }; + + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7794-sysc"; + reg = <0 0xe6180000 0 0x0200>; + #power-domain-cells = <1>; + }; + + ipmmu_sy0: mmu@e6280000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6280000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_sy1: mmu@e6290000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6290000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_ds: mmu@e6740000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6740000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mp: mmu@ec680000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xec680000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mx: mmu@fe951000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xfe951000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_gp: mmu@e62a0000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xe62a0000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + rcar_sound: sound@ec500000 { + /* + * #sound-dai-cells is required + * + * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; + * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; + */ + compatible = "renesas,rcar_sound-r8a7794", + "renesas,rcar_sound-gen2"; + reg = <0 0xec500000 0 0x1000>, /* SCU */ + <0 0xec5a0000 0 0x100>, /* ADG */ + <0 0xec540000 0 0x1000>, /* SSIU */ + <0 0xec541000 0 0x280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri */ + reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; + + clocks = <&cpg CPG_MOD 1005>, + <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, + <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, + <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, + <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, + <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, + <&cpg CPG_MOD 1025>, <&cpg CPG_MOD 1026>, + <&cpg CPG_MOD 1027>, <&cpg CPG_MOD 1028>, + <&cpg CPG_MOD 1029>, <&cpg CPG_MOD 1030>, + <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, + <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, + <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, + <&audio_clka>, <&audio_clkb>, <&audio_clkc>, + <&cpg CPG_CORE R8A7794_CLK_M2>; + clock-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0", + "src.6", "src.5", "src.4", "src.3", + "src.2", "src.1", + "ctu.0", "ctu.1", + "mix.0", "mix.1", + "dvc.0", "dvc.1", + "clk_a", "clk_b", "clk_c", "clk_i"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 1005>, + <&cpg 1006>, <&cpg 1007>, + <&cpg 1008>, <&cpg 1009>, + <&cpg 1010>, <&cpg 1011>, + <&cpg 1012>, <&cpg 1013>, + <&cpg 1014>, <&cpg 1015>; + reset-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", + "ssi.5", "ssi.4", "ssi.3", "ssi.2", + "ssi.1", "ssi.0"; + + status = "disabled"; + + rcar_sound,dvc { + dvc0: dvc-0 { + dmas = <&audma0 0xbc>; + dma-names = "tx"; + }; + dvc1: dvc-1 { + dmas = <&audma0 0xbe>; + dma-names = "tx"; }; }; - port@1 { - reg = <1>; - du_out_rgb1: endpoint { + + rcar_sound,mix { + mix0: mix-0 { }; + mix1: mix-1 { }; + }; + + rcar_sound,ctu { + ctu00: ctu-0 { }; + ctu01: ctu-1 { }; + ctu02: ctu-2 { }; + ctu03: ctu-3 { }; + ctu10: ctu-4 { }; + ctu11: ctu-5 { }; + ctu12: ctu-6 { }; + ctu13: ctu-7 { }; + }; + + rcar_sound,src { + src-0 { + status = "disabled"; + }; + src1: src-1 { + interrupts = ; + dmas = <&audma0 0x87>, <&audma0 0x9c>; + dma-names = "rx", "tx"; + }; + src2: src-2 { + interrupts = ; + dmas = <&audma0 0x89>, <&audma0 0x9e>; + dma-names = "rx", "tx"; + }; + src3: src-3 { + interrupts = ; + dmas = <&audma0 0x8b>, <&audma0 0xa0>; + dma-names = "rx", "tx"; + }; + src4: src-4 { + interrupts = ; + dmas = <&audma0 0x8d>, <&audma0 0xb0>; + dma-names = "rx", "tx"; + }; + src5: src-5 { + interrupts = ; + dmas = <&audma0 0x8f>, <&audma0 0xb2>; + dma-names = "rx", "tx"; + }; + src6: src-6 { + interrupts = ; + dmas = <&audma0 0x91>, <&audma0 0xb4>; + dma-names = "rx", "tx"; + }; + }; + + rcar_sound,ssi { + ssi0: ssi-0 { + interrupts = ; + dmas = <&audma0 0x01>, <&audma0 0x02>, + <&audma0 0x15>, <&audma0 0x16>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi1: ssi-1 { + interrupts = ; + dmas = <&audma0 0x03>, <&audma0 0x04>, + <&audma0 0x49>, <&audma0 0x4a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi2: ssi-2 { + interrupts = ; + dmas = <&audma0 0x05>, <&audma0 0x06>, + <&audma0 0x63>, <&audma0 0x64>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi3: ssi-3 { + interrupts = ; + dmas = <&audma0 0x07>, <&audma0 0x08>, + <&audma0 0x6f>, <&audma0 0x70>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi4: ssi-4 { + interrupts = ; + dmas = <&audma0 0x09>, <&audma0 0x0a>, + <&audma0 0x71>, <&audma0 0x72>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi5: ssi-5 { + interrupts = ; + dmas = <&audma0 0x0b>, <&audma0 0x0c>, + <&audma0 0x73>, <&audma0 0x74>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi6: ssi-6 { + interrupts = ; + dmas = <&audma0 0x0d>, <&audma0 0x0e>, + <&audma0 0x75>, <&audma0 0x76>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi7: ssi-7 { + interrupts = ; + dmas = <&audma0 0x0f>, <&audma0 0x10>, + <&audma0 0x79>, <&audma0 0x7a>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi8: ssi-8 { + interrupts = ; + dmas = <&audma0 0x11>, <&audma0 0x12>, + <&audma0 0x7b>, <&audma0 0x7c>; + dma-names = "rx", "tx", "rxu", "txu"; + }; + ssi9: ssi-9 { + interrupts = ; + dmas = <&audma0 0x13>, <&audma0 0x14>, + <&audma0 0x7d>, <&audma0 0x7e>; + dma-names = "rx", "tx", "rxu", "txu"; }; }; }; }; - can0: can@e6e80000 { - compatible = "renesas,can-r8a7794", "renesas,rcar-gen2-can"; - reg = <0 0xe6e80000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 916>, <&cpg CPG_CORE R8A7794_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 916>; - status = "disabled"; + timer { + compatible = "arm,armv7-timer"; + interrupts-extended = <&gic GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>; }; - can1: can@e6e88000 { - compatible = "renesas,can-r8a7794", "renesas,rcar-gen2-can"; - reg = <0 0xe6e88000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 915>, <&cpg CPG_CORE R8A7794_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 915>; - status = "disabled"; - }; /* External root clock */ extal_clk: extal { @@ -1098,259 +1405,4 @@ #clock-cells = <0>; clock-frequency = <0>; }; - - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7794-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>, <&usb_extal_clk>; - clock-names = "extal", "usb_extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; - }; - - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7794-rst"; - reg = <0 0xe6160000 0 0x0100>; - }; - - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; - }; - - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7794-sysc"; - reg = <0 0xe6180000 0 0x0200>; - #power-domain-cells = <1>; - }; - - ipmmu_sy0: mmu@e6280000 { - compatible = "renesas,ipmmu-r8a7794", "renesas,ipmmu-vmsa"; - reg = <0 0xe6280000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_sy1: mmu@e6290000 { - compatible = "renesas,ipmmu-r8a7794", "renesas,ipmmu-vmsa"; - reg = <0 0xe6290000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_ds: mmu@e6740000 { - compatible = "renesas,ipmmu-r8a7794", "renesas,ipmmu-vmsa"; - reg = <0 0xe6740000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mp: mmu@ec680000 { - compatible = "renesas,ipmmu-r8a7794", "renesas,ipmmu-vmsa"; - reg = <0 0xec680000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mx: mmu@fe951000 { - compatible = "renesas,ipmmu-r8a7794", "renesas,ipmmu-vmsa"; - reg = <0 0xfe951000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_gp: mmu@e62a0000 { - compatible = "renesas,ipmmu-r8a7794", "renesas,ipmmu-vmsa"; - reg = <0 0xe62a0000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - rcar_sound: sound@ec500000 { - /* - * #sound-dai-cells is required - * - * Single DAI : #sound-dai-cells = <0>; <&rcar_sound>; - * Multi DAI : #sound-dai-cells = <1>; <&rcar_sound N>; - */ - compatible = "renesas,rcar_sound-r8a7794", - "renesas,rcar_sound-gen2"; - reg = <0 0xec500000 0 0x1000>, /* SCU */ - <0 0xec5a0000 0 0x100>, /* ADG */ - <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x280>, /* SSI */ - <0 0xec740000 0 0x200>; /* Audio DMAC peri peri */ - reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; - - clocks = <&cpg CPG_MOD 1005>, - <&cpg CPG_MOD 1006>, <&cpg CPG_MOD 1007>, - <&cpg CPG_MOD 1008>, <&cpg CPG_MOD 1009>, - <&cpg CPG_MOD 1010>, <&cpg CPG_MOD 1011>, - <&cpg CPG_MOD 1012>, <&cpg CPG_MOD 1013>, - <&cpg CPG_MOD 1014>, <&cpg CPG_MOD 1015>, - <&cpg CPG_MOD 1025>, <&cpg CPG_MOD 1026>, - <&cpg CPG_MOD 1027>, <&cpg CPG_MOD 1028>, - <&cpg CPG_MOD 1029>, <&cpg CPG_MOD 1030>, - <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, - <&cpg CPG_MOD 1021>, <&cpg CPG_MOD 1020>, - <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>, - <&audio_clka>, <&audio_clkb>, <&audio_clkc>, - <&cpg CPG_CORE R8A7794_CLK_M2>; - clock-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", - "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0", - "src.6", "src.5", "src.4", "src.3", "src.2", - "src.1", - "ctu.0", "ctu.1", - "mix.0", "mix.1", - "dvc.0", "dvc.1", - "clk_a", "clk_b", "clk_c", "clk_i"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 1005>, - <&cpg 1006>, <&cpg 1007>, <&cpg 1008>, <&cpg 1009>, - <&cpg 1010>, <&cpg 1011>, <&cpg 1012>, <&cpg 1013>, - <&cpg 1014>, <&cpg 1015>; - reset-names = "ssi-all", - "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", - "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0"; - - status = "disabled"; - - rcar_sound,dvc { - dvc0: dvc-0 { - dmas = <&audma0 0xbc>; - dma-names = "tx"; - }; - dvc1: dvc-1 { - dmas = <&audma0 0xbe>; - dma-names = "tx"; - }; - }; - - rcar_sound,mix { - mix0: mix-0 { }; - mix1: mix-1 { }; - }; - - rcar_sound,ctu { - ctu00: ctu-0 { }; - ctu01: ctu-1 { }; - ctu02: ctu-2 { }; - ctu03: ctu-3 { }; - ctu10: ctu-4 { }; - ctu11: ctu-5 { }; - ctu12: ctu-6 { }; - ctu13: ctu-7 { }; - }; - - rcar_sound,src { - src-0 { - status = "disabled"; - }; - src1: src-1 { - interrupts = ; - dmas = <&audma0 0x87>, <&audma0 0x9c>; - dma-names = "rx", "tx"; - }; - src2: src-2 { - interrupts = ; - dmas = <&audma0 0x89>, <&audma0 0x9e>; - dma-names = "rx", "tx"; - }; - src3: src-3 { - interrupts = ; - dmas = <&audma0 0x8b>, <&audma0 0xa0>; - dma-names = "rx", "tx"; - }; - src4: src-4 { - interrupts = ; - dmas = <&audma0 0x8d>, <&audma0 0xb0>; - dma-names = "rx", "tx"; - }; - src5: src-5 { - interrupts = ; - dmas = <&audma0 0x8f>, <&audma0 0xb2>; - dma-names = "rx", "tx"; - }; - src6: src-6 { - interrupts = ; - dmas = <&audma0 0x91>, <&audma0 0xb4>; - dma-names = "rx", "tx"; - }; - }; - - rcar_sound,ssi { - ssi0: ssi-0 { - interrupts = ; - dmas = <&audma0 0x01>, <&audma0 0x02>, - <&audma0 0x15>, <&audma0 0x16>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi1: ssi-1 { - interrupts = ; - dmas = <&audma0 0x03>, <&audma0 0x04>, - <&audma0 0x49>, <&audma0 0x4a>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi2: ssi-2 { - interrupts = ; - dmas = <&audma0 0x05>, <&audma0 0x06>, - <&audma0 0x63>, <&audma0 0x64>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi3: ssi-3 { - interrupts = ; - dmas = <&audma0 0x07>, <&audma0 0x08>, - <&audma0 0x6f>, <&audma0 0x70>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi4: ssi-4 { - interrupts = ; - dmas = <&audma0 0x09>, <&audma0 0x0a>, - <&audma0 0x71>, <&audma0 0x72>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi5: ssi-5 { - interrupts = ; - dmas = <&audma0 0x0b>, <&audma0 0x0c>, - <&audma0 0x73>, <&audma0 0x74>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi6: ssi-6 { - interrupts = ; - dmas = <&audma0 0x0d>, <&audma0 0x0e>, - <&audma0 0x75>, <&audma0 0x76>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi7: ssi-7 { - interrupts = ; - dmas = <&audma0 0x0f>, <&audma0 0x10>, - <&audma0 0x79>, <&audma0 0x7a>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi8: ssi-8 { - interrupts = ; - dmas = <&audma0 0x11>, <&audma0 0x12>, - <&audma0 0x7b>, <&audma0 0x7c>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - ssi9: ssi-9 { - interrupts = ; - dmas = <&audma0 0x13>, <&audma0 0x14>, - <&audma0 0x7d>, <&audma0 0x7e>; - dma-names = "rx", "tx", "rxu", "txu"; - }; - }; - }; }; -- cgit v1.3-14-g43fede From f217f7bbb0fe5b446d5ad44ae6792e35eab4362e Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:16 +0100 Subject: ARM: dts: r8a7794: sort subnodes of soc node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort the subnodes of the soc node to improve maintainability. The sort key is the address on the bus with instances of the same IP block grouped together. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7794.dtsi | 1525 ++++++++++++++++++++-------------------- 1 file changed, 762 insertions(+), 763 deletions(-) diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi index 0d65069b1a89..9e5f886f53c5 100644 --- a/arch/arm/boot/dts/r8a7794.dtsi +++ b/arch/arm/boot/dts/r8a7794.dtsi @@ -74,28 +74,6 @@ #size-cells = <2>; ranges; - apmu@e6151000 { - compatible = "renesas,r8a7794-apmu", "renesas,apmu"; - reg = <0 0xe6151000 0 0x188>; - cpus = <&cpu0 &cpu1>; - }; - - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <0>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, - <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, - <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; - gpio0: gpio@e6050000 { compatible = "renesas,gpio-r8a7794", "renesas,rcar-gen2-gpio"; @@ -201,38 +179,36 @@ resets = <&cpg 905>; }; - cmt0: timer@ffca0000 { - compatible = "renesas,r8a7794-cmt0", - "renesas,rcar-gen2-cmt0"; - reg = <0 0xffca0000 0 0x1004>; - interrupts = , - ; - clocks = <&cpg CPG_MOD 124>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 124>; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7794"; + reg = <0 0xe6060000 0 0x11c>; + }; - status = "disabled"; + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7794-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&usb_extal_clk>; + clock-names = "extal", "usb_extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; }; - cmt1: timer@e6130000 { - compatible = "renesas,r8a7794-cmt1", - "renesas,rcar-gen2-cmt1"; - reg = <0 0xe6130000 0 0x1004>; - interrupts = , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 329>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 329>; + apmu@e6151000 { + compatible = "renesas,r8a7794-apmu", "renesas,apmu"; + reg = <0 0xe6151000 0 0x188>; + cpus = <&cpu0 &cpu1>; + }; - status = "disabled"; + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7794-rst"; + reg = <0 0xe6160000 0 0x0100>; + }; + + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7794-sysc"; + reg = <0 0xe6180000 0 0x0200>; + #power-domain-cells = <1>; }; irqc0: interrupt-controller@e61c0000 { @@ -255,419 +231,303 @@ resets = <&cpg 407>; }; - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7794"; - reg = <0 0xe6060000 0 0x11c>; + ipmmu_sy0: mmu@e6280000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6280000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; }; - dmac0: dma-controller@e6700000 { - compatible = "renesas,dmac-r8a7794", - "renesas,rcar-dmac"; - reg = <0 0xe6700000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 219>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 219>; - #dma-cells = <1>; - dma-channels = <15>; + ipmmu_sy1: mmu@e6290000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6290000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; }; - dmac1: dma-controller@e6720000 { - compatible = "renesas,dmac-r8a7794", - "renesas,rcar-dmac"; - reg = <0 0xe6720000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 218>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 218>; - #dma-cells = <1>; - dma-channels = <15>; + ipmmu_ds: mmu@e6740000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6740000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; }; - audma0: dma-controller@ec700000 { - compatible = "renesas,dmac-r8a7794", - "renesas,rcar-dmac"; - reg = <0 0xec700000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", "ch4", - "ch5", "ch6", "ch7", "ch8", "ch9", - "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 502>; - clock-names = "fck"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 502>; - #dma-cells = <1>; - dma-channels = <13>; + ipmmu_mp: mmu@ec680000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xec680000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; }; - scifa0: serial@e6c40000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c40000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 204>; - clock-names = "fck"; - dmas = <&dmac0 0x21>, <&dmac0 0x22>, - <&dmac1 0x21>, <&dmac1 0x22>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 204>; + ipmmu_mx: mmu@fe951000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xfe951000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; status = "disabled"; }; - scifa1: serial@e6c50000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c50000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 203>; - clock-names = "fck"; - dmas = <&dmac0 0x25>, <&dmac0 0x26>, - <&dmac1 0x25>, <&dmac1 0x26>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 203>; + ipmmu_gp: mmu@e62a0000 { + compatible = "renesas,ipmmu-r8a7794", + "renesas,ipmmu-vmsa"; + reg = <0 0xe62a0000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; status = "disabled"; }; - scifa2: serial@e6c60000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 202>; - clock-names = "fck"; - dmas = <&dmac0 0x27>, <&dmac0 0x28>, - <&dmac1 0x27>, <&dmac1 0x28>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 202>; - status = "disabled"; + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; }; - scifa3: serial@e6c70000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c70000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1106>; - clock-names = "fck"; - dmas = <&dmac0 0x1b>, <&dmac0 0x1c>, - <&dmac1 0x1b>, <&dmac1 0x1c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 1106>; - status = "disabled"; + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; + + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; }; - scifa4: serial@e6c78000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c78000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1107>; - clock-names = "fck"; - dmas = <&dmac0 0x1f>, <&dmac0 0x20>, - <&dmac1 0x1f>, <&dmac1 0x20>; - dma-names = "tx", "rx", "tx", "rx"; + /* The memory map in the User's Manual maps the cores to + * bus numbers + */ + i2c0: i2c@e6508000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6508000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 931>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 1107>; + resets = <&cpg 931>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - scifa5: serial@e6c80000 { - compatible = "renesas,scifa-r8a7794", - "renesas,rcar-gen2-scifa", "renesas,scifa"; - reg = <0 0xe6c80000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 1108>; - clock-names = "fck"; - dmas = <&dmac0 0x23>, <&dmac0 0x24>, - <&dmac1 0x23>, <&dmac1 0x24>; - dma-names = "tx", "rx", "tx", "rx"; + i2c1: i2c@e6518000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6518000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 930>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 1108>; + resets = <&cpg 930>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - scifb0: serial@e6c20000 { - compatible = "renesas,scifb-r8a7794", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c20000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 206>; - clock-names = "fck"; - dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, - <&dmac1 0x3d>, <&dmac1 0x3e>; - dma-names = "tx", "rx", "tx", "rx"; + i2c2: i2c@e6530000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6530000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 929>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 206>; + resets = <&cpg 929>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - scifb1: serial@e6c30000 { - compatible = "renesas,scifb-r8a7794", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6c30000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 207>; - clock-names = "fck"; - dmas = <&dmac0 0x19>, <&dmac0 0x1a>, - <&dmac1 0x19>, <&dmac1 0x1a>; - dma-names = "tx", "rx", "tx", "rx"; + i2c3: i2c@e6540000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6540000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 928>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 207>; + resets = <&cpg 928>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - scifb2: serial@e6ce0000 { - compatible = "renesas,scifb-r8a7794", - "renesas,rcar-gen2-scifb", "renesas,scifb"; - reg = <0 0xe6ce0000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 216>; - clock-names = "fck"; - dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, - <&dmac1 0x1d>, <&dmac1 0x1e>; - dma-names = "tx", "rx", "tx", "rx"; + i2c4: i2c@e6520000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6520000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 927>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 216>; + resets = <&cpg 927>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - scif0: serial@e6e60000 { - compatible = "renesas,scif-r8a7794", - "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e60000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x29>, <&dmac0 0x2a>, - <&dmac1 0x29>, <&dmac1 0x2a>; - dma-names = "tx", "rx", "tx", "rx"; + i2c5: i2c@e6528000 { + compatible = "renesas,i2c-r8a7794", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6528000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 925>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 721>; + resets = <&cpg 925>; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - scif1: serial@e6e68000 { - compatible = "renesas,scif-r8a7794", - "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0 0xe6e68000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 720>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, - <&dmac1 0x2d>, <&dmac1 0x2e>; + i2c6: i2c@e6500000 { + compatible = "renesas,iic-r8a7794", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6500000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 318>; + dmas = <&dmac0 0x61>, <&dmac0 0x62>, + <&dmac1 0x61>, <&dmac1 0x62>; dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 720>; + resets = <&cpg 318>; + #address-cells = <1>; + #size-cells = <0>; status = "disabled"; }; - scif2: serial@e6e58000 { - compatible = "renesas,scif-r8a7794", - "renesas,rcar-gen2-scif", "renesas,scif"; - reg = <0 0xe6e58000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 719>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, - <&dmac1 0x2b>, <&dmac1 0x2c>; + i2c7: i2c@e6510000 { + compatible = "renesas,iic-r8a7794", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6510000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 323>; + dmas = <&dmac0 0x65>, <&dmac0 0x66>, + <&dmac1 0x65>, <&dmac1 0x66>; dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 719>; + resets = <&cpg 323>; + #address-cells = <1>; + #size-cells = <0>; status = "disabled"; }; - scif3: serial@e6ea8000 { - compatible = "renesas,scif-r8a7794", - "renesas,rcar-gen2-scif", "renesas,scif"; - reg = <0 0xe6ea8000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 718>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x2f>, <&dmac0 0x30>, - <&dmac1 0x2f>, <&dmac1 0x30>; - dma-names = "tx", "rx", "tx", "rx"; + hsusb: usb@e6590000 { + compatible = "renesas,usbhs-r8a7794", + "renesas,rcar-gen2-usbhs"; + reg = <0 0xe6590000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 704>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 718>; + resets = <&cpg 704>; + renesas,buswait = <4>; + phys = <&usb0 1>; + phy-names = "usb"; status = "disabled"; }; - scif4: serial@e6ee0000 { - compatible = "renesas,scif-r8a7794", - "renesas,rcar-gen2-scif", "renesas,scif"; - reg = <0 0xe6ee0000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 715>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0xfb>, <&dmac0 0xfc>, - <&dmac1 0xfb>, <&dmac1 0xfc>; - dma-names = "tx", "rx", "tx", "rx"; + usbphy: usb-phy@e6590100 { + compatible = "renesas,usb-phy-r8a7794", + "renesas,rcar-gen2-usb-phy"; + reg = <0 0xe6590100 0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&cpg CPG_MOD 704>; + clock-names = "usbhs"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 715>; + resets = <&cpg 704>; status = "disabled"; + + usb0: usb-channel@0 { + reg = <0>; + #phy-cells = <1>; + }; + usb2: usb-channel@2 { + reg = <2>; + #phy-cells = <1>; + }; }; - scif5: serial@e6ee8000 { - compatible = "renesas,scif-r8a7794", - "renesas,rcar-gen2-scif", "renesas,scif"; - reg = <0 0xe6ee8000 0 64>; - interrupts = ; - clocks = <&cpg CPG_MOD 714>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0xfd>, <&dmac0 0xfe>, - <&dmac1 0xfd>, <&dmac1 0xfe>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 714>; - status = "disabled"; - }; - - hscif0: serial@e62c0000 { - compatible = "renesas,hscif-r8a7794", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c0000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 717>, - <&cpg CPG_CORE R8A7794_CLK_ZS>, <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x39>, <&dmac0 0x3a>, - <&dmac1 0x39>, <&dmac1 0x3a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 717>; - status = "disabled"; - }; - - hscif1: serial@e62c8000 { - compatible = "renesas,hscif-r8a7794", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c8000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 716>, - <&cpg CPG_CORE R8A7794_CLK_ZS>, <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, - <&dmac1 0x4d>, <&dmac1 0x4e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 716>; - status = "disabled"; - }; - - hscif2: serial@e62d0000 { - compatible = "renesas,hscif-r8a7794", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62d0000 0 96>; - interrupts = ; - clocks = <&cpg CPG_MOD 713>, <&cpg CPG_CORE R8A7794_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, - <&dmac1 0x3b>, <&dmac1 0x3c>; - dma-names = "tx", "rx", "tx", "rx"; + dmac0: dma-controller@e6700000 { + compatible = "renesas,dmac-r8a7794", + "renesas,rcar-dmac"; + reg = <0 0xe6700000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 219>; + clock-names = "fck"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 713>; - status = "disabled"; - }; - - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; - - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; - - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; - }; + resets = <&cpg 219>; + #dma-cells = <1>; + dma-channels = <15>; }; - ether: ethernet@ee700000 { - compatible = "renesas,ether-r8a7794", - "renesas,rcar-gen2-ether"; - reg = <0 0xee700000 0 0x400>; - interrupts = ; - clocks = <&cpg CPG_MOD 813>; + dmac1: dma-controller@e6720000 { + compatible = "renesas,dmac-r8a7794", + "renesas,rcar-dmac"; + reg = <0 0xe6720000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 218>; + clock-names = "fck"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 813>; - phy-mode = "rmii"; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; + resets = <&cpg 218>; + #dma-cells = <1>; + dma-channels = <15>; }; avb: ethernet@e6800000 { @@ -683,374 +543,301 @@ status = "disabled"; }; - /* The memory map in the User's Manual maps the cores to - * bus numbers - */ - i2c0: i2c@e6508000 { - compatible = "renesas,i2c-r8a7794", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6508000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 931>; + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7794", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 931>; + resets = <&cpg 917>; + num-cs = <1>; #address-cells = <1>; #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - i2c1: i2c@e6518000 { - compatible = "renesas,i2c-r8a7794", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6518000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 930>; + scifa0: serial@e6c40000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c40000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 204>; + clock-names = "fck"; + dmas = <&dmac0 0x21>, <&dmac0 0x22>, + <&dmac1 0x21>, <&dmac1 0x22>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 930>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; + resets = <&cpg 204>; status = "disabled"; }; - i2c2: i2c@e6530000 { - compatible = "renesas,i2c-r8a7794", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6530000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 929>; + scifa1: serial@e6c50000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c50000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 203>; + clock-names = "fck"; + dmas = <&dmac0 0x25>, <&dmac0 0x26>, + <&dmac1 0x25>, <&dmac1 0x26>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 929>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; + resets = <&cpg 203>; status = "disabled"; }; - i2c3: i2c@e6540000 { - compatible = "renesas,i2c-r8a7794", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6540000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 928>; + scifa2: serial@e6c60000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 202>; + clock-names = "fck"; + dmas = <&dmac0 0x27>, <&dmac0 0x28>, + <&dmac1 0x27>, <&dmac1 0x28>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 928>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; + resets = <&cpg 202>; status = "disabled"; }; - i2c4: i2c@e6520000 { - compatible = "renesas,i2c-r8a7794", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6520000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 927>; + scifa3: serial@e6c70000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c70000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1106>; + clock-names = "fck"; + dmas = <&dmac0 0x1b>, <&dmac0 0x1c>, + <&dmac1 0x1b>, <&dmac1 0x1c>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 927>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; + resets = <&cpg 1106>; status = "disabled"; }; - i2c5: i2c@e6528000 { - compatible = "renesas,i2c-r8a7794", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6528000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 925>; + scifa4: serial@e6c78000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c78000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1107>; + clock-names = "fck"; + dmas = <&dmac0 0x1f>, <&dmac0 0x20>, + <&dmac1 0x1f>, <&dmac1 0x20>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 925>; - #address-cells = <1>; - #size-cells = <0>; - i2c-scl-internal-delay-ns = <6>; + resets = <&cpg 1107>; status = "disabled"; }; - i2c6: i2c@e6500000 { - compatible = "renesas,iic-r8a7794", - "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6500000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 318>; - dmas = <&dmac0 0x61>, <&dmac0 0x62>, - <&dmac1 0x61>, <&dmac1 0x62>; + scifa5: serial@e6c80000 { + compatible = "renesas,scifa-r8a7794", + "renesas,rcar-gen2-scifa", "renesas,scifa"; + reg = <0 0xe6c80000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 1108>; + clock-names = "fck"; + dmas = <&dmac0 0x23>, <&dmac0 0x24>, + <&dmac1 0x23>, <&dmac1 0x24>; dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 318>; - #address-cells = <1>; - #size-cells = <0>; + resets = <&cpg 1108>; status = "disabled"; }; - i2c7: i2c@e6510000 { - compatible = "renesas,iic-r8a7794", - "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6510000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 323>; - dmas = <&dmac0 0x65>, <&dmac0 0x66>, - <&dmac1 0x65>, <&dmac1 0x66>; + scifb0: serial@e6c20000 { + compatible = "renesas,scifb-r8a7794", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c20000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 206>; + clock-names = "fck"; + dmas = <&dmac0 0x3d>, <&dmac0 0x3e>, + <&dmac1 0x3d>, <&dmac1 0x3e>; dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 323>; - #address-cells = <1>; - #size-cells = <0>; + resets = <&cpg 206>; status = "disabled"; }; - mmcif0: mmc@ee200000 { - compatible = "renesas,mmcif-r8a7794", - "renesas,sh-mmcif"; - reg = <0 0xee200000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 315>; - dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, - <&dmac1 0xd1>, <&dmac1 0xd2>; + scifb1: serial@e6c30000 { + compatible = "renesas,scifb-r8a7794", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6c30000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 207>; + clock-names = "fck"; + dmas = <&dmac0 0x19>, <&dmac0 0x1a>, + <&dmac1 0x19>, <&dmac1 0x1a>; dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 315>; - reg-io-width = <4>; + resets = <&cpg 207>; status = "disabled"; }; - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7794", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; + scifb2: serial@e6ce0000 { + compatible = "renesas,scifb-r8a7794", + "renesas,rcar-gen2-scifb", "renesas,scifb"; + reg = <0 0xe6ce0000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 216>; + clock-names = "fck"; + dmas = <&dmac0 0x1d>, <&dmac0 0x1e>, + <&dmac1 0x1d>, <&dmac1 0x1e>; dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 314>; + resets = <&cpg 216>; status = "disabled"; }; - sdhi1: sd@ee140000 { - compatible = "renesas,sdhi-r8a7794", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee140000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, - <&dmac1 0xc1>, <&dmac1 0xc2>; + scif0: serial@e6e60000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", + "renesas,scif"; + reg = <0 0xe6e60000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x29>, <&dmac0 0x2a>, + <&dmac1 0x29>, <&dmac1 0x2a>; dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 312>; + resets = <&cpg 721>; status = "disabled"; }; - sdhi2: sd@ee160000 { - compatible = "renesas,sdhi-r8a7794", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee160000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, - <&dmac1 0xd3>, <&dmac1 0xd4>; + scif1: serial@e6e68000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", + "renesas,scif"; + reg = <0 0xe6e68000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 720>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2d>, <&dmac0 0x2e>, + <&dmac1 0x2d>, <&dmac1 0x2e>; dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 311>; + resets = <&cpg 720>; status = "disabled"; }; - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7794", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; + scif2: serial@e6e58000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6e58000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 719>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2b>, <&dmac0 0x2c>, + <&dmac1 0x2b>, <&dmac1 0x2c>; dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 917>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; + resets = <&cpg 719>; status = "disabled"; }; - vin0: video@e6ef0000 { - compatible = "renesas,vin-r8a7794", - "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef0000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 811>; + scif3: serial@e6ea8000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ea8000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 718>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x2f>, <&dmac0 0x30>, + <&dmac1 0x2f>, <&dmac1 0x30>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 811>; + resets = <&cpg 718>; status = "disabled"; }; - vin1: video@e6ef1000 { - compatible = "renesas,vin-r8a7794", - "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef1000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 810>; + scif4: serial@e6ee0000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ee0000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 715>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0xfb>, <&dmac0 0xfc>, + <&dmac1 0xfb>, <&dmac1 0xfc>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 810>; + resets = <&cpg 715>; status = "disabled"; }; - pci0: pci@ee090000 { - compatible = "renesas,pci-r8a7794", - "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee090000 0 0xc00>, - <0 0xee080000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; + scif5: serial@e6ee8000 { + compatible = "renesas,scif-r8a7794", + "renesas,rcar-gen2-scif", "renesas,scif"; + reg = <0 0xe6ee8000 0 64>; + interrupts = ; + clocks = <&cpg CPG_MOD 714>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0xfd>, <&dmac0 0xfe>, + <&dmac1 0xfd>, <&dmac1 0xfe>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 703>; + resets = <&cpg 714>; status = "disabled"; - - bus-range = <0 0>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x800 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x1000 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; }; - pci1: pci@ee0d0000 { - compatible = "renesas,pci-r8a7794", - "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee0d0000 0 0xc00>, - <0 0xee0c0000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <1 1>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x10800 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x11000 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - }; - - hsusb: usb@e6590000 { - compatible = "renesas,usbhs-r8a7794", - "renesas,rcar-gen2-usbhs"; - reg = <0 0xe6590000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 704>; + hscif0: serial@e62c0000 { + compatible = "renesas,hscif-r8a7794", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c0000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 717>, + <&cpg CPG_CORE R8A7794_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x39>, <&dmac0 0x3a>, + <&dmac1 0x39>, <&dmac1 0x3a>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 704>; - renesas,buswait = <4>; - phys = <&usb0 1>; - phy-names = "usb"; + resets = <&cpg 717>; status = "disabled"; }; - usbphy: usb-phy@e6590100 { - compatible = "renesas,usb-phy-r8a7794", - "renesas,rcar-gen2-usb-phy"; - reg = <0 0xe6590100 0 0x100>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&cpg CPG_MOD 704>; - clock-names = "usbhs"; + hscif1: serial@e62c8000 { + compatible = "renesas,hscif-r8a7794", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c8000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 716>, + <&cpg CPG_CORE R8A7794_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, + <&dmac1 0x4d>, <&dmac1 0x4e>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 704>; + resets = <&cpg 716>; status = "disabled"; - - usb0: usb-channel@0 { - reg = <0>; - #phy-cells = <1>; - }; - usb2: usb-channel@2 { - reg = <2>; - #phy-cells = <1>; - }; - }; - - vsp@fe928000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe928000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 131>; - power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 131>; }; - vsp@fe930000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe930000 0 0x8000>; - interrupts = ; - clocks = <&cpg CPG_MOD 128>; + hscif2: serial@e62d0000 { + compatible = "renesas,hscif-r8a7794", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62d0000 0 96>; + interrupts = ; + clocks = <&cpg CPG_MOD 713>, <&cpg CPG_CORE R8A7794_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, + <&dmac1 0x3b>, <&dmac1 0x3c>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; - resets = <&cpg 128>; - }; - - du: display@feb00000 { - compatible = "renesas,du-r8a7794"; - reg = <0 0xfeb00000 0 0x40000>; - reg-names = "du"; - interrupts = , - ; - clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>; - clock-names = "du.0", "du.1"; + resets = <&cpg 713>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - du_out_rgb0: endpoint { - }; - }; - port@1 { - reg = <1>; - du_out_rgb1: endpoint { - }; - }; - }; }; can0: can@e6e80000 { @@ -1079,87 +866,25 @@ status = "disabled"; }; - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7794-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>, <&usb_extal_clk>; - clock-names = "extal", "usb_extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; - }; - - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7794-rst"; - reg = <0 0xe6160000 0 0x0100>; - }; - - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; - }; - - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7794-sysc"; - reg = <0 0xe6180000 0 0x0200>; - #power-domain-cells = <1>; - }; - - ipmmu_sy0: mmu@e6280000 { - compatible = "renesas,ipmmu-r8a7794", - "renesas,ipmmu-vmsa"; - reg = <0 0xe6280000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_sy1: mmu@e6290000 { - compatible = "renesas,ipmmu-r8a7794", - "renesas,ipmmu-vmsa"; - reg = <0 0xe6290000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_ds: mmu@e6740000 { - compatible = "renesas,ipmmu-r8a7794", - "renesas,ipmmu-vmsa"; - reg = <0 0xe6740000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mp: mmu@ec680000 { - compatible = "renesas,ipmmu-r8a7794", - "renesas,ipmmu-vmsa"; - reg = <0 0xec680000 0 0x1000>; - interrupts = ; - #iommu-cells = <1>; - status = "disabled"; - }; - - ipmmu_mx: mmu@fe951000 { - compatible = "renesas,ipmmu-r8a7794", - "renesas,ipmmu-vmsa"; - reg = <0 0xfe951000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; + vin0: video@e6ef0000 { + compatible = "renesas,vin-r8a7794", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef0000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 811>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 811>; status = "disabled"; }; - ipmmu_gp: mmu@e62a0000 { - compatible = "renesas,ipmmu-r8a7794", - "renesas,ipmmu-vmsa"; - reg = <0 0xe62a0000 0 0x1000>; - interrupts = , - ; - #iommu-cells = <1>; + vin1: video@e6ef1000 { + compatible = "renesas,vin-r8a7794", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef1000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 810>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 810>; status = "disabled"; }; @@ -1343,6 +1068,281 @@ }; }; }; + + audma0: dma-controller@ec700000 { + compatible = "renesas,dmac-r8a7794", + "renesas,rcar-dmac"; + reg = <0 0xec700000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", "ch4", + "ch5", "ch6", "ch7", "ch8", "ch9", + "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 502>; + clock-names = "fck"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 502>; + #dma-cells = <1>; + dma-channels = <13>; + }; + + pci0: pci@ee090000 { + compatible = "renesas,pci-r8a7794", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee090000 0 0xc00>, + <0 0xee080000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x800 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x1000 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + }; + + pci1: pci@ee0d0000 { + compatible = "renesas,pci-r8a7794", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee0d0000 0 0xc00>, + <0 0xee0c0000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <1 1>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x10800 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x11000 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + }; + + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7794", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; + + sdhi1: sd@ee140000 { + compatible = "renesas,sdhi-r8a7794", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee140000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, + <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 312>; + status = "disabled"; + }; + + sdhi2: sd@ee160000 { + compatible = "renesas,sdhi-r8a7794", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee160000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, + <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 311>; + status = "disabled"; + }; + + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7794", + "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 315>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 315>; + reg-io-width = <4>; + status = "disabled"; + }; + + ether: ethernet@ee700000 { + compatible = "renesas,ether-r8a7794", + "renesas,rcar-gen2-ether"; + reg = <0 0xee700000 0 0x400>; + interrupts = ; + clocks = <&cpg CPG_MOD 813>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 813>; + phy-mode = "rmii"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, + <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, + <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 408>; + }; + + vsp@fe928000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe928000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 131>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 131>; + }; + + vsp@fe930000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe930000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 128>; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 128>; + }; + + du: display@feb00000 { + compatible = "renesas,du-r8a7794"; + reg = <0 0xfeb00000 0 0x40000>; + reg-names = "du"; + interrupts = , + ; + clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>; + clock-names = "du.0", "du.1"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb0: endpoint { + }; + }; + port@1 { + reg = <1>; + du_out_rgb1: endpoint { + }; + }; + }; + }; + + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; + }; + + cmt0: timer@ffca0000 { + compatible = "renesas,r8a7794-cmt0", + "renesas,rcar-gen2-cmt0"; + reg = <0 0xffca0000 0 0x1004>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 124>; + clock-names = "fck"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 124>; + + status = "disabled"; + }; + + cmt1: timer@e6130000 { + compatible = "renesas,r8a7794-cmt1", + "renesas,rcar-gen2-cmt1"; + reg = <0 0xe6130000 0 0x1004>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 329>; + clock-names = "fck"; + power-domains = <&sysc R8A7794_PD_ALWAYS_ON>; + resets = <&cpg 329>; + + status = "disabled"; + }; }; timer { @@ -1353,7 +1353,6 @@ <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>; }; - /* External root clock */ extal_clk: extal { compatible = "fixed-clock"; -- cgit v1.3-14-g43fede From 807eeaf95e2647277f89284d91d1785c2280c2c6 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 17 Jan 2018 17:17:17 +0100 Subject: ARM: dts: r8a7794: sort subnodes of root node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort the subnodes of the soc node to improve maintainability. The sort key is the address on the bus with instances of the same IP block grouped together. Also re-align comment of audio_clka to match other R-Car SoCs. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7794.dtsi | 90 +++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi index 9e5f886f53c5..d588efa6aeaa 100644 --- a/arch/arm/boot/dts/r8a7794.dtsi +++ b/arch/arm/boot/dts/r8a7794.dtsi @@ -33,6 +33,35 @@ vin1 = &vin1; }; + /* + * The external audio clocks are configured as 0 Hz fixed frequency + * clocks by default. + * Boards that provide audio clocks should override them. + */ + audio_clka: audio_clka { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + audio_clkb: audio_clkb { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + audio_clkc: audio_clkc { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + /* External CAN clock */ + can_clk: can { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + cpus { #address-cells = <1>; #size-cells = <0>; @@ -66,6 +95,22 @@ }; }; + /* External root clock */ + extal_clk: extal { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + + /* External SCIF clock */ + scif_clk: scif { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + soc { compatible = "simple-bus"; interrupt-parent = <&gic>; @@ -1353,55 +1398,10 @@ <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>; }; - /* External root clock */ - extal_clk: extal { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; - /* External USB clock - can be overridden by the board */ usb_extal_clk: usb_extal { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <48000000>; }; - - /* External CAN clock */ - can_clk: can { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; - - /* External SCIF clock */ - scif_clk: scif { - compatible = "fixed-clock"; - #clock-cells = <0>; - /* This value must be overridden by the board. */ - clock-frequency = <0>; - }; - - /* - * The external audio clocks are configured as 0 Hz fixed - * frequency clocks by default. Boards that provide audio - * clocks should override them. - */ - audio_clka: audio_clka { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - audio_clkb: audio_clkb { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - audio_clkc: audio_clkc { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; }; -- cgit v1.3-14-g43fede From 5a63226206ce2313badee0b6ffec47f7a305c8ae Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 26 Jan 2018 10:36:33 +0100 Subject: ARM: dts: r8a7790: consistently use single space before = Consistently use a single space before a =. This patch fixes instances where a tab is used instead. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7790.dtsi | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index 3bbcc0b93f1c..e4367cecad18 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -1705,16 +1705,16 @@ thermal-zones { cpu_thermal: cpu-thermal { - polling-delay-passive = <0>; - polling-delay = <0>; + polling-delay-passive = <0>; + polling-delay = <0>; thermal-sensors = <&thermal>; trips { cpu-crit { - temperature = <95000>; - hysteresis = <0>; - type = "critical"; + temperature = <95000>; + hysteresis = <0>; + type = "critical"; }; }; cooling-maps { -- cgit v1.3-14-g43fede From c57c1b7ce8c6743ca745a577e7ac6b81a59a2479 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 26 Jan 2018 10:36:34 +0100 Subject: ARM: dts: r8a7791: consistently use single space before = Consistently use a single space before a =. This patch fixes instances where a tab is used instead. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7791.dtsi | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index dc659351472f..f11dab71b03a 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -1703,16 +1703,16 @@ thermal-zones { cpu_thermal: cpu-thermal { - polling-delay-passive = <0>; - polling-delay = <0>; + polling-delay-passive = <0>; + polling-delay = <0>; thermal-sensors = <&thermal>; trips { cpu-crit { - temperature = <95000>; - hysteresis = <0>; - type = "critical"; + temperature = <95000>; + hysteresis = <0>; + type = "critical"; }; }; cooling-maps { -- cgit v1.3-14-g43fede From 77b1e3d230e5e42bbde3f11923b34382832a8415 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 26 Jan 2018 10:36:35 +0100 Subject: ARM: dts: r8a7793: consistently use single space before = Consistently use a single space before a =. This patch fixes instances where a tab is used instead. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7793.dtsi | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/r8a7793.dtsi b/arch/arm/boot/dts/r8a7793.dtsi index aa7d7792fb13..f9c5a557107d 100644 --- a/arch/arm/boot/dts/r8a7793.dtsi +++ b/arch/arm/boot/dts/r8a7793.dtsi @@ -1362,16 +1362,16 @@ thermal-zones { cpu_thermal: cpu-thermal { - polling-delay-passive = <0>; - polling-delay = <0>; + polling-delay-passive = <0>; + polling-delay = <0>; thermal-sensors = <&thermal>; trips { cpu-crit { - temperature = <95000>; - hysteresis = <0>; - type = "critical"; + temperature = <95000>; + hysteresis = <0>; + type = "critical"; }; }; cooling-maps { -- cgit v1.3-14-g43fede From 8eccafe92d100d836dfee541c3337b42d818533c Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 26 Jan 2018 10:40:51 +0100 Subject: ARM: dts: r8a7743: sort subnodes of soc node Sort the subnodes of the soc node to improve maintainability. The sort key is the address on the bus with instances of the same IP block grouped together and sorted alphabetically. Minor whitespace and line-wrapping changes are also made to match the formatting of R-Car Gen2 SoCs. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7743.dtsi | 1327 ++++++++++++++++++++-------------------- 1 file changed, 662 insertions(+), 665 deletions(-) diff --git a/arch/arm/boot/dts/r8a7743.dtsi b/arch/arm/boot/dts/r8a7743.dtsi index 0b74c6c7d21d..1933aaccb874 100644 --- a/arch/arm/boot/dts/r8a7743.dtsi +++ b/arch/arm/boot/dts/r8a7743.dtsi @@ -141,29 +141,6 @@ #size-cells = <2>; ranges; - apmu@e6152000 { - compatible = "renesas,r8a7743-apmu", "renesas,apmu"; - reg = <0 0xe6152000 0 0x188>; - cpus = <&cpu0 &cpu1>; - }; - - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <0>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, - <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, - <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; - gpio0: gpio@e6050000 { compatible = "renesas,gpio-r8a7743", "renesas,rcar-gen2-gpio"; @@ -284,6 +261,48 @@ resets = <&cpg 904>; }; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7743"; + reg = <0 0xe6060000 0 0x250>; + }; + + tpu: pwm@e60f0000 { + compatible = "renesas,tpu-r8a7743", "renesas,tpu"; + reg = <0 0xe60f0000 0 0x148>; + clocks = <&cpg CPG_MOD 304>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 304>; + #pwm-cells = <3>; + status = "disabled"; + }; + + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7743-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&usb_extal_clk>; + clock-names = "extal", "usb_extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; + + apmu@e6152000 { + compatible = "renesas,r8a7743-apmu", "renesas,apmu"; + reg = <0 0xe6152000 0 0x188>; + cpus = <&cpu0 &cpu1>; + }; + + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7743-rst"; + reg = <0 0xe6160000 0 0x100>; + }; + + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7743-sysc"; + reg = <0 0xe6180000 0 0x200>; + #power-domain-cells = <1>; + }; + irqc: interrupt-controller@e61c0000 { compatible = "renesas,irqc-r8a7743", "renesas,irqc"; #interrupt-cells = <2>; @@ -316,195 +335,206 @@ #thermal-sensor-cells = <0>; }; - cmt0: timer@ffca0000 { - compatible = "renesas,r8a7743-cmt0", - "renesas,rcar-gen2-cmt0"; - reg = <0 0xffca0000 0 0x1004>; - interrupts = , - ; - clocks = <&cpg CPG_MOD 124>; - clock-names = "fck"; + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; + }; + + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; + + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; + }; + + icram2: sram@e6300000 { + compatible = "mmio-sram"; + reg = <0 0xe6300000 0 0x40000>; + }; + + /* The memory map in the User's Manual maps the cores to + * bus numbers + */ + i2c0: i2c@e6508000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7743", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6508000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 931>; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 124>; + resets = <&cpg 931>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - cmt1: timer@e6130000 { - compatible = "renesas,r8a7743-cmt1", - "renesas,rcar-gen2-cmt1"; - reg = <0 0xe6130000 0 0x1004>; - interrupts = , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 329>; - clock-names = "fck"; + i2c1: i2c@e6518000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7743", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6518000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 930>; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 329>; + resets = <&cpg 930>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7743-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>, <&usb_extal_clk>; - clock-names = "extal", "usb_extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; + i2c2: i2c@e6530000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7743", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6530000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 929>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 929>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; }; - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; + i2c3: i2c@e6540000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7743", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6540000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 928>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 928>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; }; - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7743-rst"; - reg = <0 0xe6160000 0 0x100>; + i2c4: i2c@e6520000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7743", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6520000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 927>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 927>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; }; - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7743-sysc"; - reg = <0 0xe6180000 0 0x200>; - #power-domain-cells = <1>; + i2c5: i2c@e6528000 { + /* doesn't need pinmux */ + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7743", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6528000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 925>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 925>; + i2c-scl-internal-delay-ns = <110>; + status = "disabled"; }; - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7743"; - reg = <0 0xe6060000 0 0x250>; + iic0: i2c@e6500000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7743", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6500000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 318>; + dmas = <&dmac0 0x61>, <&dmac0 0x62>, + <&dmac1 0x61>, <&dmac1 0x62>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 318>; + status = "disabled"; }; - dmac0: dma-controller@e6700000 { - compatible = "renesas,dmac-r8a7743", - "renesas,rcar-dmac"; - reg = <0 0xe6700000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 219>; - clock-names = "fck"; + iic1: i2c@e6510000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7743", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6510000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 323>; + dmas = <&dmac0 0x65>, <&dmac0 0x66>, + <&dmac1 0x65>, <&dmac1 0x66>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 219>; - #dma-cells = <1>; - dma-channels = <15>; + resets = <&cpg 323>; + status = "disabled"; }; - dmac1: dma-controller@e6720000 { - compatible = "renesas,dmac-r8a7743", - "renesas,rcar-dmac"; - reg = <0 0xe6720000 0 0x20000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 218>; - clock-names = "fck"; + iic3: i2c@e60b0000 { + /* doesn't need pinmux */ + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7743", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe60b0000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 926>; + dmas = <&dmac0 0x77>, <&dmac0 0x78>, + <&dmac1 0x77>, <&dmac1 0x78>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 218>; - #dma-cells = <1>; - dma-channels = <15>; + resets = <&cpg 926>; + status = "disabled"; }; - audma0: dma-controller@ec700000 { - compatible = "renesas,dmac-r8a7743", - "renesas,rcar-dmac"; - reg = <0 0xec700000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 502>; - clock-names = "fck"; + hsusb: usb@e6590000 { + compatible = "renesas,usbhs-r8a7743", + "renesas,rcar-gen2-usbhs"; + reg = <0 0xe6590000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 704>; + dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, + <&usb_dmac1 0>, <&usb_dmac1 1>; + dma-names = "ch0", "ch1", "ch2", "ch3"; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 502>; - #dma-cells = <1>; - dma-channels = <13>; + resets = <&cpg 704>; + renesas,buswait = <4>; + phys = <&usb0 1>; + phy-names = "usb"; + status = "disabled"; }; - audma1: dma-controller@ec720000 { - compatible = "renesas,dmac-r8a7743", - "renesas,rcar-dmac"; - reg = <0 0xec720000 0 0x10000>; - interrupts = ; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 501>; - clock-names = "fck"; + usbphy: usb-phy@e6590100 { + compatible = "renesas,usb-phy-r8a7743", + "renesas,rcar-gen2-usb-phy"; + reg = <0 0xe6590100 0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&cpg CPG_MOD 704>; + clock-names = "usbhs"; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 501>; - #dma-cells = <1>; - dma-channels = <13>; + resets = <&cpg 704>; + status = "disabled"; + + usb0: usb-channel@0 { + reg = <0>; + #phy-cells = <1>; + }; + usb2: usb-channel@2 { + reg = <2>; + #phy-cells = <1>; + }; }; usb_dmac0: dma-controller@e65a0000 { @@ -535,143 +565,98 @@ dma-channels = <2>; }; - /* The memory map in the User's Manual maps the cores to bus - * numbers - */ - i2c0: i2c@e6508000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7743", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6508000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 931>; + dmac0: dma-controller@e6700000 { + compatible = "renesas,dmac-r8a7743", + "renesas,rcar-dmac"; + reg = <0 0xe6700000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 219>; + clock-names = "fck"; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 931>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; + resets = <&cpg 219>; + #dma-cells = <1>; + dma-channels = <15>; }; - i2c1: i2c@e6518000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7743", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6518000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 930>; + dmac1: dma-controller@e6720000 { + compatible = "renesas,dmac-r8a7743", + "renesas,rcar-dmac"; + reg = <0 0xe6720000 0 0x20000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 218>; + clock-names = "fck"; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 930>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; + resets = <&cpg 218>; + #dma-cells = <1>; + dma-channels = <15>; }; - i2c2: i2c@e6530000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7743", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6530000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 929>; + avb: ethernet@e6800000 { + compatible = "renesas,etheravb-r8a7743", + "renesas,etheravb-rcar-gen2"; + reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; + interrupts = ; + clocks = <&cpg CPG_MOD 812>; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 929>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; - - i2c3: i2c@e6540000 { + resets = <&cpg 812>; #address-cells = <1>; #size-cells = <0>; - compatible = "renesas,i2c-r8a7743", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6540000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 928>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 928>; - i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - i2c4: i2c@e6520000 { + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7743", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + num-cs = <1>; #address-cells = <1>; #size-cells = <0>; - compatible = "renesas,i2c-r8a7743", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6520000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 927>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 927>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; - - i2c5: i2c@e6528000 { - /* doesn't need pinmux */ - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7743", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6528000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 925>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 925>; - i2c-scl-internal-delay-ns = <110>; - status = "disabled"; - }; - - iic0: i2c@e6500000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7743", - "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6500000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 318>; - dmas = <&dmac0 0x61>, <&dmac0 0x62>, - <&dmac1 0x61>, <&dmac1 0x62>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 318>; - status = "disabled"; - }; - - iic1: i2c@e6510000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7743", - "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6510000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 323>; - dmas = <&dmac0 0x65>, <&dmac0 0x66>, - <&dmac1 0x65>, <&dmac1 0x66>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 323>; - status = "disabled"; - }; - - iic3: i2c@e60b0000 { - /* doesn't need pinmux */ - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7743", - "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe60b0000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 926>; - dmas = <&dmac0 0x77>, <&dmac0 0x78>, - <&dmac1 0x77>, <&dmac1 0x78>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 926>; + resets = <&cpg 917>; status = "disabled"; }; @@ -954,88 +939,6 @@ status = "disabled"; }; - icram2: sram@e6300000 { - compatible = "mmio-sram"; - reg = <0 0xe6300000 0 0x40000>; - }; - - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; - - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; - - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; - }; - }; - - ether: ethernet@ee700000 { - compatible = "renesas,ether-r8a7743", - "renesas,rcar-gen2-ether"; - reg = <0 0xee700000 0 0x400>; - interrupts = ; - clocks = <&cpg CPG_MOD 813>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 813>; - phy-mode = "rmii"; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - avb: ethernet@e6800000 { - compatible = "renesas,etheravb-r8a7743", - "renesas,etheravb-rcar-gen2"; - reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; - interrupts = ; - clocks = <&cpg CPG_MOD 812>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 812>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - mmcif0: mmc@ee200000 { - compatible = "renesas,mmcif-r8a7743", - "renesas,sh-mmcif"; - reg = <0 0xee200000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 315>; - dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, - <&dmac1 0xd1>, <&dmac1 0xd2>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 315>; - reg-io-width = <4>; - max-frequency = <97500000>; - status = "disabled"; - }; - - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7743", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - resets = <&cpg 917>; - status = "disabled"; - }; - msiof0: spi@e6e20000 { compatible = "renesas,msiof-r8a7743", "renesas,rcar-gen2-msiof"; @@ -1084,26 +987,6 @@ status = "disabled"; }; - /* - * pci1 and xhci share the same phy, therefore only one of them - * can be active at any one time. If both of them are enabled, - * a race condition will determine who'll control the phy. - * A firmware file is needed by the xhci driver in order for - * USB 3.0 to work properly. - */ - xhci: usb@ee000000 { - compatible = "renesas,xhci-r8a7743", - "renesas,rcar-gen2-xhci"; - reg = <0 0xee000000 0 0xc00>; - interrupts = ; - clocks = <&cpg CPG_MOD 328>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 328>; - phys = <&usb2 1>; - phy-names = "usb"; - status = "disabled"; - }; - pwm0: pwm@e6e30000 { compatible = "renesas,pwm-r8a7743", "renesas,pwm-rcar"; reg = <0 0xe6e30000 0 0x8>; @@ -1174,119 +1057,53 @@ status = "disabled"; }; - tpu: pwm@e60f0000 { - compatible = "renesas,tpu-r8a7743", "renesas,tpu"; - reg = <0 0xe60f0000 0 0x148>; - clocks = <&cpg CPG_MOD 304>; + can0: can@e6e80000 { + compatible = "renesas,can-r8a7743", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e80000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 916>, + <&cpg CPG_CORE R8A7743_CLK_RCAN>, + <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 304>; - #pwm-cells = <3>; + resets = <&cpg 916>; status = "disabled"; }; - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7743", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; + can1: can@e6e88000 { + compatible = "renesas,can-r8a7743", + "renesas,rcar-gen2-can"; + reg = <0 0xe6e88000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 915>, + <&cpg CPG_CORE R8A7743_CLK_RCAN>, + <&can_clk>; + clock-names = "clkp1", "clkp2", "can_clk"; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 314>; + resets = <&cpg 915>; status = "disabled"; }; - sdhi1: sd@ee140000 { - compatible = "renesas,sdhi-r8a7743", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee140000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, - <&dmac1 0xc1>, <&dmac1 0xc2>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; + vin0: video@e6ef0000 { + compatible = "renesas,vin-r8a7743", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef0000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 811>; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 312>; + resets = <&cpg 811>; status = "disabled"; }; - sdhi2: sd@ee160000 { - compatible = "renesas,sdhi-r8a7743", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee160000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, - <&dmac1 0xd3>, <&dmac1 0xd4>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; + vin1: video@e6ef1000 { + compatible = "renesas,vin-r8a7743", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef1000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 810>; power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 311>; - status = "disabled"; - }; - - hsusb: usb@e6590000 { - compatible = "renesas,usbhs-r8a7743", - "renesas,rcar-gen2-usbhs"; - reg = <0 0xe6590000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 704>; - dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, - <&usb_dmac1 0>, <&usb_dmac1 1>; - dma-names = "ch0", "ch1", "ch2", "ch3"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 704>; - renesas,buswait = <4>; - phys = <&usb0 1>; - phy-names = "usb"; - status = "disabled"; - }; - - usbphy: usb-phy@e6590100 { - compatible = "renesas,usb-phy-r8a7743", - "renesas,rcar-gen2-usb-phy"; - reg = <0 0xe6590100 0 0x100>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&cpg CPG_MOD 704>; - clock-names = "usbhs"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 704>; - status = "disabled"; - - usb0: usb-channel@0 { - reg = <0>; - #phy-cells = <1>; - }; - usb2: usb-channel@2 { - reg = <2>; - #phy-cells = <1>; - }; - }; - - vin0: video@e6ef0000 { - compatible = "renesas,vin-r8a7743", - "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef0000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 811>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 811>; - status = "disabled"; - }; - - vin1: video@e6ef1000 { - compatible = "renesas,vin-r8a7743", - "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef1000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 810>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 810>; + resets = <&cpg 810>; status = "disabled"; }; @@ -1301,162 +1118,6 @@ status = "disabled"; }; - du: display@feb00000 { - compatible = "renesas,du-r8a7743"; - reg = <0 0xfeb00000 0 0x40000>, - <0 0xfeb90000 0 0x1c>; - reg-names = "du", "lvds.0"; - interrupts = , - ; - clocks = <&cpg CPG_MOD 724>, - <&cpg CPG_MOD 723>, - <&cpg CPG_MOD 726>; - clock-names = "du.0", "du.1", "lvds.0"; - status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - du_out_rgb: endpoint { - }; - }; - port@1 { - reg = <1>; - du_out_lvds0: endpoint { - }; - }; - }; - }; - - can0: can@e6e80000 { - compatible = "renesas,can-r8a7743", - "renesas,rcar-gen2-can"; - reg = <0 0xe6e80000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 916>, - <&cpg CPG_CORE R8A7743_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 916>; - status = "disabled"; - }; - - can1: can@e6e88000 { - compatible = "renesas,can-r8a7743", - "renesas,rcar-gen2-can"; - reg = <0 0xe6e88000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 915>, - <&cpg CPG_CORE R8A7743_CLK_RCAN>, - <&can_clk>; - clock-names = "clkp1", "clkp2", "can_clk"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 915>; - status = "disabled"; - }; - - pci0: pci@ee090000 { - compatible = "renesas,pci-r8a7743", - "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee090000 0 0xc00>, - <0 0xee080000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <0 0>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x800 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x1000 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; - }; - - pci1: pci@ee0d0000 { - compatible = "renesas,pci-r8a7743", - "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee0d0000 0 0xc00>, - <0 0xee0c0000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <1 1>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x10800 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x11000 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - }; - - pciec: pcie@fe000000 { - compatible = "renesas,pcie-r8a7743", - "renesas,pcie-rcar-gen2"; - reg = <0 0xfe000000 0 0x80000>; - #address-cells = <3>; - #size-cells = <2>; - bus-range = <0x00 0xff>; - device_type = "pci"; - ranges = <0x01000000 0 0x00000000 0 0xfe100000 0 0x00100000 - 0x02000000 0 0xfe200000 0 0xfe200000 0 0x00200000 - 0x02000000 0 0x30000000 0 0x30000000 0 0x08000000 - 0x42000000 0 0x38000000 0 0x38000000 0 0x08000000>; - /* Map all possible DDR as inbound ranges */ - dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 0 0x80000000 - 0x43000000 2 0x00000000 2 0x00000000 1 0x00000000>; - interrupts = , - , - ; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&cpg CPG_MOD 319>, <&pcie_bus_clk>; - clock-names = "pcie", "pcie_bus"; - power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; - resets = <&cpg 319>; - status = "disabled"; - }; - rcar_sound: sound@ec500000 { /* * #sound-dai-cells is required @@ -1641,6 +1302,342 @@ }; }; }; + + audma0: dma-controller@ec700000 { + compatible = "renesas,dmac-r8a7743", + "renesas,rcar-dmac"; + reg = <0 0xec700000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 502>; + clock-names = "fck"; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 502>; + #dma-cells = <1>; + dma-channels = <13>; + }; + + audma1: dma-controller@ec720000 { + compatible = "renesas,dmac-r8a7743", + "renesas,rcar-dmac"; + reg = <0 0xec720000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 501>; + clock-names = "fck"; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 501>; + #dma-cells = <1>; + dma-channels = <13>; + }; + + /* + * pci1 and xhci share the same phy, therefore only one of them + * can be active at any one time. If both of them are enabled, + * a race condition will determine who'll control the phy. + * A firmware file is needed by the xhci driver in order for + * USB 3.0 to work properly. + */ + xhci: usb@ee000000 { + compatible = "renesas,xhci-r8a7743", + "renesas,rcar-gen2-xhci"; + reg = <0 0xee000000 0 0xc00>; + interrupts = ; + clocks = <&cpg CPG_MOD 328>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 328>; + phys = <&usb2 1>; + phy-names = "usb"; + status = "disabled"; + }; + + pci0: pci@ee090000 { + compatible = "renesas,pci-r8a7743", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee090000 0 0xc00>, + <0 0xee080000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x800 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x1000 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + }; + + pci1: pci@ee0d0000 { + compatible = "renesas,pci-r8a7743", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee0d0000 0 0xc00>, + <0 0xee0c0000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <1 1>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x10800 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x11000 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + }; + + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7743", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; + + sdhi1: sd@ee140000 { + compatible = "renesas,sdhi-r8a7743", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee140000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, + <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 312>; + status = "disabled"; + }; + + sdhi2: sd@ee160000 { + compatible = "renesas,sdhi-r8a7743", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee160000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, + <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 311>; + status = "disabled"; + }; + + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7743", + "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 315>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 315>; + reg-io-width = <4>; + max-frequency = <97500000>; + status = "disabled"; + }; + + ether: ethernet@ee700000 { + compatible = "renesas,ether-r8a7743", + "renesas,rcar-gen2-ether"; + reg = <0 0xee700000 0 0x400>; + interrupts = ; + clocks = <&cpg CPG_MOD 813>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 813>; + phy-mode = "rmii"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 408>; + }; + + pciec: pcie@fe000000 { + compatible = "renesas,pcie-r8a7743", + "renesas,pcie-rcar-gen2"; + reg = <0 0xfe000000 0 0x80000>; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; + device_type = "pci"; + ranges = <0x01000000 0 0x00000000 0 0xfe100000 0 0x00100000 + 0x02000000 0 0xfe200000 0 0xfe200000 0 0x00200000 + 0x02000000 0 0x30000000 0 0x30000000 0 0x08000000 + 0x42000000 0 0x38000000 0 0x38000000 0 0x08000000>; + /* Map all possible DDR as inbound ranges */ + dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 0 0x80000000 + 0x43000000 2 0x00000000 2 0x00000000 1 0x00000000>; + interrupts = , + , + ; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 319>, <&pcie_bus_clk>; + clock-names = "pcie", "pcie_bus"; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 319>; + status = "disabled"; + }; + + du: display@feb00000 { + compatible = "renesas,du-r8a7743"; + reg = <0 0xfeb00000 0 0x40000>, + <0 0xfeb90000 0 0x1c>; + reg-names = "du", "lvds.0"; + interrupts = , + ; + clocks = <&cpg CPG_MOD 724>, + <&cpg CPG_MOD 723>, + <&cpg CPG_MOD 726>; + clock-names = "du.0", "du.1", "lvds.0"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb: endpoint { + }; + }; + port@1 { + reg = <1>; + du_out_lvds0: endpoint { + }; + }; + }; + }; + + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; + }; + + cmt0: timer@ffca0000 { + compatible = "renesas,r8a7743-cmt0", + "renesas,rcar-gen2-cmt0"; + reg = <0 0xffca0000 0 0x1004>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 124>; + clock-names = "fck"; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 124>; + status = "disabled"; + }; + + cmt1: timer@e6130000 { + compatible = "renesas,r8a7743-cmt1", + "renesas,rcar-gen2-cmt1"; + reg = <0 0xe6130000 0 0x1004>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 329>; + clock-names = "fck"; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 329>; + status = "disabled"; + }; }; thermal-zones { -- cgit v1.3-14-g43fede From 28c07001db756d5542a80be4441c0d2be0f114c8 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 26 Jan 2018 10:40:52 +0100 Subject: ARM: dts: r8a7745: sort subnodes of soc node Sort the subnodes of the soc node to improve maintainability. The sort key is the address on the bus with instances of the same IP block grouped together and sorted alphabetically. Minor whitespace and line-wrapping changes are also made to match the formatting of R-Car Gen2 SoCs. This patch should not introduce any functional change. Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven --- arch/arm/boot/dts/r8a7745.dtsi | 1138 ++++++++++++++++++++-------------------- 1 file changed, 567 insertions(+), 571 deletions(-) diff --git a/arch/arm/boot/dts/r8a7745.dtsi b/arch/arm/boot/dts/r8a7745.dtsi index ae918e9cce21..9c8d72c83bdc 100644 --- a/arch/arm/boot/dts/r8a7745.dtsi +++ b/arch/arm/boot/dts/r8a7745.dtsi @@ -121,29 +121,6 @@ #size-cells = <2>; ranges; - apmu@e6151000 { - compatible = "renesas,r8a7745-apmu", "renesas,apmu"; - reg = <0 0xe6151000 0 0x188>; - cpus = <&cpu0 &cpu1>; - }; - - gic: interrupt-controller@f1001000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <0>; - interrupt-controller; - reg = <0 0xf1001000 0 0x1000>, - <0 0xf1002000 0 0x2000>, - <0 0xf1004000 0 0x2000>, - <0 0xf1006000 0 0x2000>; - interrupts = ; - clocks = <&cpg CPG_MOD 408>; - clock-names = "clk"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 408>; - }; - gpio0: gpio@e6050000 { compatible = "renesas,gpio-r8a7745", "renesas,rcar-gen2-gpio"; @@ -249,6 +226,48 @@ resets = <&cpg 905>; }; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a7745"; + reg = <0 0xe6060000 0 0x11c>; + }; + + tpu: pwm@e60f0000 { + compatible = "renesas,tpu-r8a7745", "renesas,tpu"; + reg = <0 0xe60f0000 0 0x148>; + clocks = <&cpg CPG_MOD 304>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 304>; + #pwm-cells = <3>; + status = "disabled"; + }; + + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a7745-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&usb_extal_clk>; + clock-names = "extal", "usb_extal"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; + + apmu@e6151000 { + compatible = "renesas,r8a7745-apmu", "renesas,apmu"; + reg = <0 0xe6151000 0 0x188>; + cpus = <&cpu0 &cpu1>; + }; + + rst: reset-controller@e6160000 { + compatible = "renesas,r8a7745-rst"; + reg = <0 0xe6160000 0 0x100>; + }; + + sysc: system-controller@e6180000 { + compatible = "renesas,r8a7745-sysc"; + reg = <0 0xe6180000 0 0x200>; + #power-domain-cells = <1>; + }; + irqc: interrupt-controller@e61c0000 { compatible = "renesas,irqc-r8a7745", "renesas,irqc"; #interrupt-cells = <2>; @@ -269,67 +288,211 @@ resets = <&cpg 407>; }; - cmt0: timer@ffca0000 { - compatible = "renesas,r8a7745-cmt0", - "renesas,rcar-gen2-cmt0"; - reg = <0 0xffca0000 0 0x1004>; - interrupts = , - ; - clocks = <&cpg CPG_MOD 124>; - clock-names = "fck"; + icram0: sram@e63a0000 { + compatible = "mmio-sram"; + reg = <0 0xe63a0000 0 0x12000>; + }; + + icram1: sram@e63c0000 { + compatible = "mmio-sram"; + reg = <0 0xe63c0000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0xe63c0000 0x1000>; + + smp-sram@0 { + compatible = "renesas,smp-sram"; + reg = <0 0x10>; + }; + }; + + icram2: sram@e6300000 { + compatible = "mmio-sram"; + reg = <0 0xe6300000 0 0x40000>; + }; + i2c0: i2c@e6508000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7745", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6508000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 931>; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 124>; + resets = <&cpg 931>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - cmt1: timer@e6130000 { - compatible = "renesas,r8a7745-cmt1", - "renesas,rcar-gen2-cmt1"; - reg = <0 0xe6130000 0 0x1004>; - interrupts = , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD 329>; - clock-names = "fck"; + i2c1: i2c@e6518000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7745", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6518000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 930>; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 329>; + resets = <&cpg 930>; + i2c-scl-internal-delay-ns = <6>; status = "disabled"; }; - cpg: clock-controller@e6150000 { - compatible = "renesas,r8a7745-cpg-mssr"; - reg = <0 0xe6150000 0 0x1000>; - clocks = <&extal_clk>, <&usb_extal_clk>; - clock-names = "extal", "usb_extal"; - #clock-cells = <2>; - #power-domain-cells = <0>; - #reset-cells = <1>; + i2c2: i2c@e6530000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7745", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6530000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 929>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 929>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; }; - prr: chipid@ff000044 { - compatible = "renesas,prr"; - reg = <0 0xff000044 0 4>; + i2c3: i2c@e6540000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7745", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6540000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 928>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 928>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; }; - rst: reset-controller@e6160000 { - compatible = "renesas,r8a7745-rst"; - reg = <0 0xe6160000 0 0x100>; + i2c4: i2c@e6520000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7745", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6520000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 927>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 927>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; }; - sysc: system-controller@e6180000 { - compatible = "renesas,r8a7745-sysc"; - reg = <0 0xe6180000 0 0x200>; - #power-domain-cells = <1>; + i2c5: i2c@e6528000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a7745", + "renesas,rcar-gen2-i2c"; + reg = <0 0xe6528000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 925>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 925>; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; }; - pfc: pin-controller@e6060000 { - compatible = "renesas,pfc-r8a7745"; - reg = <0 0xe6060000 0 0x11c>; + iic0: i2c@e6500000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7745", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6500000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 318>; + dmas = <&dmac0 0x61>, <&dmac0 0x62>, + <&dmac1 0x61>, <&dmac1 0x62>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 318>; + status = "disabled"; + }; + + iic1: i2c@e6510000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,iic-r8a7745", + "renesas,rcar-gen2-iic", + "renesas,rmobile-iic"; + reg = <0 0xe6510000 0 0x425>; + interrupts = ; + clocks = <&cpg CPG_MOD 323>; + dmas = <&dmac0 0x65>, <&dmac0 0x66>, + <&dmac1 0x65>, <&dmac1 0x66>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 323>; + status = "disabled"; + }; + + hsusb: usb@e6590000 { + compatible = "renesas,usbhs-r8a7745", + "renesas,rcar-gen2-usbhs"; + reg = <0 0xe6590000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 704>; + dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, + <&usb_dmac1 0>, <&usb_dmac1 1>; + dma-names = "ch0", "ch1", "ch2", "ch3"; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 704>; + renesas,buswait = <4>; + phys = <&usb0 1>; + phy-names = "usb"; + status = "disabled"; + }; + + usbphy: usb-phy@e6590100 { + compatible = "renesas,usb-phy-r8a7745", + "renesas,rcar-gen2-usb-phy"; + reg = <0 0xe6590100 0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&cpg CPG_MOD 704>; + clock-names = "usbhs"; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 704>; + status = "disabled"; + + usb0: usb-channel@0 { + reg = <0>; + #phy-cells = <1>; + }; + usb2: usb-channel@2 { + reg = <2>; + #phy-cells = <1>; + }; + }; + + usb_dmac0: dma-controller@e65a0000 { + compatible = "renesas,r8a7745-usb-dmac", + "renesas,usb-dmac"; + reg = <0 0xe65a0000 0 0x100>; + interrupts = ; + interrupt-names = "ch0", "ch1"; + clocks = <&cpg CPG_MOD 330>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 330>; + #dma-cells = <1>; + dma-channels = <2>; + }; + + usb_dmac1: dma-controller@e65b0000 { + compatible = "renesas,r8a7745-usb-dmac", + "renesas,usb-dmac"; + reg = <0 0xe65b0000 0 0x100>; + interrupts = ; + interrupt-names = "ch0", "ch1"; + clocks = <&cpg CPG_MOD 331>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 331>; + #dma-cells = <1>; + dma-channels = <2>; }; dmac0: dma-controller@e6700000 { @@ -353,10 +516,10 @@ GIC_SPI 213 IRQ_TYPE_LEVEL_HIGH GIC_SPI 214 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14"; clocks = <&cpg CPG_MOD 219>; clock-names = "fck"; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; @@ -385,76 +548,46 @@ GIC_SPI 316 IRQ_TYPE_LEVEL_HIGH GIC_SPI 317 IRQ_TYPE_LEVEL_HIGH GIC_SPI 318 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "error", - "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5", "ch6", "ch7", - "ch8", "ch9", "ch10", "ch11", - "ch12", "ch13", "ch14"; - clocks = <&cpg CPG_MOD 218>; - clock-names = "fck"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 218>; - #dma-cells = <1>; - dma-channels = <15>; - }; - - audma0: dma-controller@ec700000 { - compatible = "renesas,dmac-r8a7745", - "renesas,rcar-dmac"; - reg = <0 0xec700000 0 0x10000>; - interrupts = ; interrupt-names = "error", "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7", "ch8", "ch9", "ch10", "ch11", - "ch12"; - clocks = <&cpg CPG_MOD 502>; + "ch12", "ch13", "ch14"; + clocks = <&cpg CPG_MOD 218>; clock-names = "fck"; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 502>; + resets = <&cpg 218>; #dma-cells = <1>; - dma-channels = <13>; + dma-channels = <15>; }; - usb_dmac0: dma-controller@e65a0000 { - compatible = "renesas,r8a7745-usb-dmac", - "renesas,usb-dmac"; - reg = <0 0xe65a0000 0 0x100>; - interrupts = ; - interrupt-names = "ch0", "ch1"; - clocks = <&cpg CPG_MOD 330>; + avb: ethernet@e6800000 { + compatible = "renesas,etheravb-r8a7745", + "renesas,etheravb-rcar-gen2"; + reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; + interrupts = ; + clocks = <&cpg CPG_MOD 812>; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 330>; - #dma-cells = <1>; - dma-channels = <2>; + resets = <&cpg 812>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; - usb_dmac1: dma-controller@e65b0000 { - compatible = "renesas,r8a7745-usb-dmac", - "renesas,usb-dmac"; - reg = <0 0xe65b0000 0 0x100>; - interrupts = ; - interrupt-names = "ch0", "ch1"; - clocks = <&cpg CPG_MOD 331>; + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7745", "renesas,qspi"; + reg = <0 0xe6b10000 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 331>; - #dma-cells = <1>; - dma-channels = <2>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&cpg 917>; + status = "disabled"; }; scifa0: serial@e6c40000 { @@ -695,294 +828,45 @@ interrupts = ; clocks = <&cpg CPG_MOD 717>, <&cpg CPG_CORE R8A7745_CLK_ZS>, <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x39>, <&dmac0 0x3a>, - <&dmac1 0x39>, <&dmac1 0x3a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 717>; - status = "disabled"; - }; - - hscif1: serial@e62c8000 { - compatible = "renesas,hscif-r8a7745", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62c8000 0 0x60>; - interrupts = ; - clocks = <&cpg CPG_MOD 716>, - <&cpg CPG_CORE R8A7745_CLK_ZS>, <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, - <&dmac1 0x4d>, <&dmac1 0x4e>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 716>; - status = "disabled"; - }; - - hscif2: serial@e62d0000 { - compatible = "renesas,hscif-r8a7745", - "renesas,rcar-gen2-hscif", "renesas,hscif"; - reg = <0 0xe62d0000 0 0x60>; - interrupts = ; - clocks = <&cpg CPG_MOD 713>, - <&cpg CPG_CORE R8A7745_CLK_ZS>, <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, - <&dmac1 0x3b>, <&dmac1 0x3c>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 713>; - status = "disabled"; - }; - - icram2: sram@e6300000 { - compatible = "mmio-sram"; - reg = <0 0xe6300000 0 0x40000>; - }; - - icram0: sram@e63a0000 { - compatible = "mmio-sram"; - reg = <0 0xe63a0000 0 0x12000>; - }; - - icram1: sram@e63c0000 { - compatible = "mmio-sram"; - reg = <0 0xe63c0000 0 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0 0xe63c0000 0x1000>; - - smp-sram@0 { - compatible = "renesas,smp-sram"; - reg = <0 0x10>; - }; - }; - - ether: ethernet@ee700000 { - compatible = "renesas,ether-r8a7745", - "renesas,rcar-gen2-ether"; - reg = <0 0xee700000 0 0x400>; - interrupts = ; - clocks = <&cpg CPG_MOD 813>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 813>; - phy-mode = "rmii"; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - avb: ethernet@e6800000 { - compatible = "renesas,etheravb-r8a7745", - "renesas,etheravb-rcar-gen2"; - reg = <0 0xe6800000 0 0x800>, <0 0xee0e8000 0 0x4000>; - interrupts = ; - clocks = <&cpg CPG_MOD 812>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 812>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c0: i2c@e6508000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7745", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6508000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 931>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 931>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; - - i2c1: i2c@e6518000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7745", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6518000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 930>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 930>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; - - i2c2: i2c@e6530000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7745", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6530000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 929>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 929>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; - - i2c3: i2c@e6540000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7745", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6540000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 928>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 928>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; - - i2c4: i2c@e6520000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7745", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6520000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 927>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 927>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; - - i2c5: i2c@e6528000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,i2c-r8a7745", - "renesas,rcar-gen2-i2c"; - reg = <0 0xe6528000 0 0x40>; - interrupts = ; - clocks = <&cpg CPG_MOD 925>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 925>; - i2c-scl-internal-delay-ns = <6>; - status = "disabled"; - }; - - iic0: i2c@e6500000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7745", - "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6500000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 318>; - dmas = <&dmac0 0x61>, <&dmac0 0x62>, - <&dmac1 0x61>, <&dmac1 0x62>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 318>; - status = "disabled"; - }; - - iic1: i2c@e6510000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "renesas,iic-r8a7745", - "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; - reg = <0 0xe6510000 0 0x425>; - interrupts = ; - clocks = <&cpg CPG_MOD 323>; - dmas = <&dmac0 0x65>, <&dmac0 0x66>, - <&dmac1 0x65>, <&dmac1 0x66>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 323>; - status = "disabled"; - }; - - mmcif0: mmc@ee200000 { - compatible = "renesas,mmcif-r8a7745", - "renesas,sh-mmcif"; - reg = <0 0xee200000 0 0x80>; - interrupts = ; - clocks = <&cpg CPG_MOD 315>; - dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, - <&dmac1 0xd1>, <&dmac1 0xd2>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 315>; - reg-io-width = <4>; - max-frequency = <97500000>; - status = "disabled"; - }; - - qspi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7745", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupts = ; - clocks = <&cpg CPG_MOD 917>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>, - <&dmac1 0x17>, <&dmac1 0x18>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x39>, <&dmac0 0x3a>, + <&dmac1 0x39>, <&dmac1 0x3a>; dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - resets = <&cpg 917>; + resets = <&cpg 717>; status = "disabled"; }; - vin0: video@e6ef0000 { - compatible = "renesas,vin-r8a7745", - "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef0000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 811>; + hscif1: serial@e62c8000 { + compatible = "renesas,hscif-r8a7745", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62c8000 0 0x60>; + interrupts = ; + clocks = <&cpg CPG_MOD 716>, + <&cpg CPG_CORE R8A7745_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x4d>, <&dmac0 0x4e>, + <&dmac1 0x4d>, <&dmac1 0x4e>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 811>; + resets = <&cpg 716>; status = "disabled"; }; - vin1: video@e6ef1000 { - compatible = "renesas,vin-r8a7745", - "renesas,rcar-gen2-vin"; - reg = <0 0xe6ef1000 0 0x1000>; - interrupts = ; - clocks = <&cpg CPG_MOD 810>; + hscif2: serial@e62d0000 { + compatible = "renesas,hscif-r8a7745", + "renesas,rcar-gen2-hscif", "renesas,hscif"; + reg = <0 0xe62d0000 0 0x60>; + interrupts = ; + clocks = <&cpg CPG_MOD 713>, + <&cpg CPG_CORE R8A7745_CLK_ZS>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x3b>, <&dmac0 0x3c>, + <&dmac1 0x3b>, <&dmac1 0x3c>; + dma-names = "tx", "rx", "tx", "rx"; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 810>; - status = "disabled"; - }; - - du: display@feb00000 { - compatible = "renesas,du-r8a7745"; - reg = <0 0xfeb00000 0 0x40000>; - reg-names = "du"; - interrupts = , - ; - clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>; - clock-names = "du.0", "du.1"; + resets = <&cpg 713>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - du_out_rgb0: endpoint { - }; - }; - port@1 { - reg = <1>; - du_out_rgb1: endpoint { - }; - }; - }; }; msiof0: spi@e6e20000 { @@ -1103,170 +987,6 @@ status = "disabled"; }; - tpu: pwm@e60f0000 { - compatible = "renesas,tpu-r8a7745", "renesas,tpu"; - reg = <0 0xe60f0000 0 0x148>; - clocks = <&cpg CPG_MOD 304>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 304>; - #pwm-cells = <3>; - status = "disabled"; - }; - - sdhi0: sd@ee100000 { - compatible = "renesas,sdhi-r8a7745", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee100000 0 0x328>; - interrupts = ; - clocks = <&cpg CPG_MOD 314>; - dmas = <&dmac0 0xcd>, <&dmac0 0xce>, - <&dmac1 0xcd>, <&dmac1 0xce>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <195000000>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 314>; - status = "disabled"; - }; - - sdhi1: sd@ee140000 { - compatible = "renesas,sdhi-r8a7745", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee140000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 312>; - dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, - <&dmac1 0xc1>, <&dmac1 0xc2>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 312>; - status = "disabled"; - }; - - sdhi2: sd@ee160000 { - compatible = "renesas,sdhi-r8a7745", - "renesas,rcar-gen2-sdhi"; - reg = <0 0xee160000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 311>; - dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, - <&dmac1 0xd3>, <&dmac1 0xd4>; - dma-names = "tx", "rx", "tx", "rx"; - max-frequency = <97500000>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 311>; - status = "disabled"; - }; - - pci0: pci@ee090000 { - compatible = "renesas,pci-r8a7745", - "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee090000 0 0xc00>, - <0 0xee080000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <0 0>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x800 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x1000 0 0 0 0>; - phys = <&usb0 0>; - phy-names = "usb"; - }; - }; - - pci1: pci@ee0d0000 { - compatible = "renesas,pci-r8a7745", - "renesas,pci-rcar-gen2"; - device_type = "pci"; - reg = <0 0xee0d0000 0 0xc00>, - <0 0xee0c0000 0 0x1100>; - interrupts = ; - clocks = <&cpg CPG_MOD 703>; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 703>; - status = "disabled"; - - bus-range = <1 1>; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; - interrupt-map-mask = <0xff00 0 0 0x7>; - interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH - 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; - - usb@1,0 { - reg = <0x10800 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - - usb@2,0 { - reg = <0x11000 0 0 0 0>; - phys = <&usb2 0>; - phy-names = "usb"; - }; - }; - - hsusb: usb@e6590000 { - compatible = "renesas,usbhs-r8a7745", - "renesas,rcar-gen2-usbhs"; - reg = <0 0xe6590000 0 0x100>; - interrupts = ; - clocks = <&cpg CPG_MOD 704>; - dmas = <&usb_dmac0 0>, <&usb_dmac0 1>, - <&usb_dmac1 0>, <&usb_dmac1 1>; - dma-names = "ch0", "ch1", "ch2", "ch3"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 704>; - renesas,buswait = <4>; - phys = <&usb0 1>; - phy-names = "usb"; - status = "disabled"; - }; - - usbphy: usb-phy@e6590100 { - compatible = "renesas,usb-phy-r8a7745", - "renesas,rcar-gen2-usb-phy"; - reg = <0 0xe6590100 0 0x100>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&cpg CPG_MOD 704>; - clock-names = "usbhs"; - power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 704>; - status = "disabled"; - - usb0: usb-channel@0 { - reg = <0>; - #phy-cells = <1>; - }; - usb2: usb-channel@2 { - reg = <2>; - #phy-cells = <1>; - }; - }; - can0: can@e6e80000 { compatible = "renesas,can-r8a7745", "renesas,rcar-gen2-can"; @@ -1291,7 +1011,29 @@ <&can_clk>; clock-names = "clkp1", "clkp2", "can_clk"; power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; - resets = <&cpg 915>; + resets = <&cpg 915>; + status = "disabled"; + }; + + vin0: video@e6ef0000 { + compatible = "renesas,vin-r8a7745", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef0000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 811>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 811>; + status = "disabled"; + }; + + vin1: video@e6ef1000 { + compatible = "renesas,vin-r8a7745", + "renesas,rcar-gen2-vin"; + reg = <0 0xe6ef1000 0 0x1000>; + interrupts = ; + clocks = <&cpg CPG_MOD 810>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 810>; status = "disabled"; }; @@ -1474,6 +1216,260 @@ }; }; }; + + audma0: dma-controller@ec700000 { + compatible = "renesas,dmac-r8a7745", + "renesas,rcar-dmac"; + reg = <0 0xec700000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12"; + clocks = <&cpg CPG_MOD 502>; + clock-names = "fck"; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 502>; + #dma-cells = <1>; + dma-channels = <13>; + }; + + pci0: pci@ee090000 { + compatible = "renesas,pci-r8a7745", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee090000 0 0xc00>, + <0 0xee080000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee080000 0 0xee080000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x800 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x1000 0 0 0 0>; + phys = <&usb0 0>; + phy-names = "usb"; + }; + }; + + pci1: pci@ee0d0000 { + compatible = "renesas,pci-r8a7745", + "renesas,pci-rcar-gen2"; + device_type = "pci"; + reg = <0 0xee0d0000 0 0xc00>, + <0 0xee0c0000 0 0x1100>; + interrupts = ; + clocks = <&cpg CPG_MOD 703>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 703>; + status = "disabled"; + + bus-range = <1 1>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x02000000 0 0xee0c0000 0 0xee0c0000 0 0x00010000>; + interrupt-map-mask = <0xff00 0 0 0x7>; + interrupt-map = <0x0000 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x0800 0 0 1 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH + 0x1000 0 0 2 &gic GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; + + usb@1,0 { + reg = <0x10800 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + + usb@2,0 { + reg = <0x11000 0 0 0 0>; + phys = <&usb2 0>; + phy-names = "usb"; + }; + }; + + sdhi0: sd@ee100000 { + compatible = "renesas,sdhi-r8a7745", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee100000 0 0x328>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>; + dmas = <&dmac0 0xcd>, <&dmac0 0xce>, + <&dmac1 0xcd>, <&dmac1 0xce>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <195000000>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 314>; + status = "disabled"; + }; + + sdhi1: sd@ee140000 { + compatible = "renesas,sdhi-r8a7745", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee140000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 312>; + dmas = <&dmac0 0xc1>, <&dmac0 0xc2>, + <&dmac1 0xc1>, <&dmac1 0xc2>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 312>; + status = "disabled"; + }; + + sdhi2: sd@ee160000 { + compatible = "renesas,sdhi-r8a7745", + "renesas,rcar-gen2-sdhi"; + reg = <0 0xee160000 0 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 311>; + dmas = <&dmac0 0xd3>, <&dmac0 0xd4>, + <&dmac1 0xd3>, <&dmac1 0xd4>; + dma-names = "tx", "rx", "tx", "rx"; + max-frequency = <97500000>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 311>; + status = "disabled"; + }; + + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7745", + "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = ; + clocks = <&cpg CPG_MOD 315>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 315>; + reg-io-width = <4>; + max-frequency = <97500000>; + status = "disabled"; + }; + + ether: ethernet@ee700000 { + compatible = "renesas,ether-r8a7745", + "renesas,rcar-gen2-ether"; + reg = <0 0xee700000 0 0x400>; + interrupts = ; + clocks = <&cpg CPG_MOD 813>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 813>; + phy-mode = "rmii"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + gic: interrupt-controller@f1001000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0xf1001000 0 0x1000>, <0 0xf1002000 0 0x2000>, + <0 0xf1004000 0 0x2000>, <0 0xf1006000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 408>; + }; + + du: display@feb00000 { + compatible = "renesas,du-r8a7745"; + reg = <0 0xfeb00000 0 0x40000>; + reg-names = "du"; + interrupts = , + ; + clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>; + clock-names = "du.0", "du.1"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb0: endpoint { + }; + }; + port@1 { + reg = <1>; + du_out_rgb1: endpoint { + }; + }; + }; + }; + + prr: chipid@ff000044 { + compatible = "renesas,prr"; + reg = <0 0xff000044 0 4>; + }; + + cmt0: timer@ffca0000 { + compatible = "renesas,r8a7745-cmt0", + "renesas,rcar-gen2-cmt0"; + reg = <0 0xffca0000 0 0x1004>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 124>; + clock-names = "fck"; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 124>; + status = "disabled"; + }; + + cmt1: timer@e6130000 { + compatible = "renesas,r8a7745-cmt1", + "renesas,rcar-gen2-cmt1"; + reg = <0 0xe6130000 0 0x1004>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&cpg CPG_MOD 329>; + clock-names = "fck"; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 329>; + status = "disabled"; + }; }; timer { -- cgit v1.3-14-g43fede From bbb44da0b5958e05cba96b36a354d5e9be46b1a8 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 24 Jan 2018 15:42:01 +0000 Subject: ARM: dts: r8a7743: Add IPMMU DT nodes Add the six IPMMU instances found in the r8a7743 to DT with a disabled status. Signed-off-by: Biju Das Reviewed-by: Chris Paterson Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7743.dtsi | 58 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/arch/arm/boot/dts/r8a7743.dtsi b/arch/arm/boot/dts/r8a7743.dtsi index 1933aaccb874..11a1263211eb 100644 --- a/arch/arm/boot/dts/r8a7743.dtsi +++ b/arch/arm/boot/dts/r8a7743.dtsi @@ -335,6 +335,64 @@ #thermal-sensor-cells = <0>; }; + ipmmu_sy0: mmu@e6280000 { + compatible = "renesas,ipmmu-r8a7743", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6280000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_sy1: mmu@e6290000 { + compatible = "renesas,ipmmu-r8a7743", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6290000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_ds: mmu@e6740000 { + compatible = "renesas,ipmmu-r8a7743", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6740000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mp: mmu@ec680000 { + compatible = "renesas,ipmmu-r8a7743", + "renesas,ipmmu-vmsa"; + reg = <0 0xec680000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mx: mmu@fe951000 { + compatible = "renesas,ipmmu-r8a7743", + "renesas,ipmmu-vmsa"; + reg = <0 0xfe951000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_gp: mmu@e62a0000 { + compatible = "renesas,ipmmu-r8a7743", + "renesas,ipmmu-vmsa"; + reg = <0 0xe62a0000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + icram0: sram@e63a0000 { compatible = "mmio-sram"; reg = <0 0xe63a0000 0 0x12000>; -- cgit v1.3-14-g43fede From 0dcba3de58354e35c91a31dafe597e81e7c22294 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 24 Jan 2018 15:42:02 +0000 Subject: ARM: dts: r8a7745: Add IPMMU DT nodes Add the six IPMMU instances found in the r8a7745 to DT with a disabled status. Signed-off-by: Biju Das Reviewed-by: Chris Paterson Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7745.dtsi | 58 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/arch/arm/boot/dts/r8a7745.dtsi b/arch/arm/boot/dts/r8a7745.dtsi index 9c8d72c83bdc..413288b05515 100644 --- a/arch/arm/boot/dts/r8a7745.dtsi +++ b/arch/arm/boot/dts/r8a7745.dtsi @@ -288,6 +288,64 @@ resets = <&cpg 407>; }; + ipmmu_sy0: mmu@e6280000 { + compatible = "renesas,ipmmu-r8a7745", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6280000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_sy1: mmu@e6290000 { + compatible = "renesas,ipmmu-r8a7745", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6290000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_ds: mmu@e6740000 { + compatible = "renesas,ipmmu-r8a7745", + "renesas,ipmmu-vmsa"; + reg = <0 0xe6740000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mp: mmu@ec680000 { + compatible = "renesas,ipmmu-r8a7745", + "renesas,ipmmu-vmsa"; + reg = <0 0xec680000 0 0x1000>; + interrupts = ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_mx: mmu@fe951000 { + compatible = "renesas,ipmmu-r8a7745", + "renesas,ipmmu-vmsa"; + reg = <0 0xfe951000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + + ipmmu_gp: mmu@e62a0000 { + compatible = "renesas,ipmmu-r8a7745", + "renesas,ipmmu-vmsa"; + reg = <0 0xe62a0000 0 0x1000>; + interrupts = , + ; + #iommu-cells = <1>; + status = "disabled"; + }; + icram0: sram@e63a0000 { compatible = "mmio-sram"; reg = <0 0xe63a0000 0 0x12000>; -- cgit v1.3-14-g43fede From 0565605aa9887a3d6423fb08c0cbb0de3a22f838 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 24 Jan 2018 16:11:51 +0000 Subject: ARM: dts: r8a7743: Add VSP support Add VSP support to SoC DT. Signed-off-by: Biju Das Reviewed-by: Chris Paterson Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7743.dtsi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/arm/boot/dts/r8a7743.dtsi b/arch/arm/boot/dts/r8a7743.dtsi index 11a1263211eb..1d9073ba0ce0 100644 --- a/arch/arm/boot/dts/r8a7743.dtsi +++ b/arch/arm/boot/dts/r8a7743.dtsi @@ -1630,6 +1630,33 @@ status = "disabled"; }; + vsp@fe928000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe928000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 131>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 131>; + }; + + vsp@fe930000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe930000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 128>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 128>; + }; + + vsp@fe938000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe938000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 127>; + power-domains = <&sysc R8A7743_PD_ALWAYS_ON>; + resets = <&cpg 127>; + }; + du: display@feb00000 { compatible = "renesas,du-r8a7743"; reg = <0 0xfeb00000 0 0x40000>, -- cgit v1.3-14-g43fede From 76a2577d97f0b221245e56a17a70bb10a3a97419 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 24 Jan 2018 16:11:52 +0000 Subject: ARM: dts: r8a7745: Add VSP support Add VSP support to SoC DT. Signed-off-by: Biju Das Reviewed-by: Chris Paterson Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7745.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm/boot/dts/r8a7745.dtsi b/arch/arm/boot/dts/r8a7745.dtsi index 413288b05515..dd49a8b48f3e 100644 --- a/arch/arm/boot/dts/r8a7745.dtsi +++ b/arch/arm/boot/dts/r8a7745.dtsi @@ -1465,6 +1465,24 @@ resets = <&cpg 408>; }; + vsp@fe928000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe928000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 131>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 131>; + }; + + vsp@fe930000 { + compatible = "renesas,vsp1"; + reg = <0 0xfe930000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 128>; + power-domains = <&sysc R8A7745_PD_ALWAYS_ON>; + resets = <&cpg 128>; + }; + du: display@feb00000 { compatible = "renesas,du-r8a7745"; reg = <0 0xfeb00000 0 0x40000>; -- cgit v1.3-14-g43fede From 4e65e1b6721aafca073a308ae7f32e1cdddfdcd8 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 6 Feb 2018 23:29:50 +0100 Subject: ARM: dts: lager: use demuxer for IIC2/I2C2 Create a separate bus for HDMI related I2C slaves. Based on work by Wolfram Sang. Signed-off-by: Simon Horman [wsa: rebased, removed typo in comment, fixed aliases, switched to named GPIOS, sort SCL pins first] Signed-off-by: Wolfram Sang --- arch/arm/boot/dts/r8a7790-lager.dts | 213 +++++++++++++++++++++--------------- 1 file changed, 124 insertions(+), 89 deletions(-) diff --git a/arch/arm/boot/dts/r8a7790-lager.dts b/arch/arm/boot/dts/r8a7790-lager.dts index 7892b113ecaa..9f511d4fac10 100644 --- a/arch/arm/boot/dts/r8a7790-lager.dts +++ b/arch/arm/boot/dts/r8a7790-lager.dts @@ -51,8 +51,10 @@ serial0 = &scif0; serial1 = &scifa1; i2c8 = &gpioi2c1; + i2c9 = &gpioi2c2; i2c10 = &i2cexio0; i2c11 = &i2cexio1; + i2c12 = &i2chdmi; }; chosen { @@ -272,8 +274,18 @@ #size-cells = <0>; compatible = "i2c-gpio"; status = "disabled"; - sda-gpios = <&gpio1 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpio1 16 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio1 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + }; + + gpioi2c2: i2c-9 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "i2c-gpio"; + status = "disabled"; + scl-gpios = <&gpio5 5 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio5 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; i2c-gpio,delay-us = <5>; }; @@ -308,6 +320,104 @@ #address-cells = <1>; #size-cells = <0>; }; + + /* + * IIC2 and I2C2 may be switched using pinmux. + * A fallback to GPIO is also provided. + */ + i2chdmi: i2c-12 { + compatible = "i2c-demux-pinctrl"; + i2c-parent = <&iic2>, <&i2c2>, <&gpioi2c2>; + i2c-bus-name = "i2c-hdmi"; + #address-cells = <1>; + #size-cells = <0>; + + ak4643: codec@12 { + compatible = "asahi-kasei,ak4643"; + #sound-dai-cells = <0>; + reg = <0x12>; + }; + + composite-in@20 { + compatible = "adi,adv7180"; + reg = <0x20>; + remote = <&vin1>; + + port { + adv7180: endpoint { + bus-width = <8>; + remote-endpoint = <&vin1ep0>; + }; + }; + }; + + cec_clock: cec-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <12000000>; + }; + + hdmi@39 { + compatible = "adi,adv7511w"; + reg = <0x39>; + interrupt-parent = <&gpio1>; + interrupts = <15 IRQ_TYPE_LEVEL_LOW>; + clocks = <&cec_clock>; + clock-names = "cec"; + + adi,input-depth = <8>; + adi,input-colorspace = "rgb"; + adi,input-clock = "1x"; + adi,input-style = <1>; + adi,input-justification = "evenly"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7511_in: endpoint { + remote-endpoint = <&du_out_lvds0>; + }; + }; + + port@1 { + reg = <1>; + adv7511_out: endpoint { + remote-endpoint = <&hdmi_con_out>; + }; + }; + }; + }; + + hdmi-in@4c { + compatible = "adi,adv7612"; + reg = <0x4c>; + interrupt-parent = <&gpio1>; + interrupts = <20 IRQ_TYPE_LEVEL_LOW>; + default-input = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7612_in: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; + }; + + port@2 { + reg = <2>; + adv7612_out: endpoint { + remote-endpoint = <&vin0ep2>; + }; + }; + }; + }; + }; }; &du { @@ -437,6 +547,11 @@ function = "iic1"; }; + i2c2_pins: i2c2 { + groups = "i2c2"; + function = "i2c2"; + }; + iic2_pins: iic2 { groups = "iic2"; function = "iic2"; @@ -643,98 +758,18 @@ pinctrl-names = "i2c-exio1"; }; -&iic2 { - status = "okay"; - pinctrl-0 = <&iic2_pins>; - pinctrl-names = "default"; +&i2c2 { + pinctrl-0 = <&i2c2_pins>; + pinctrl-names = "i2c-hdmi"; clock-frequency = <100000>; +}; - ak4643: codec@12 { - compatible = "asahi-kasei,ak4643"; - #sound-dai-cells = <0>; - reg = <0x12>; - }; - - composite-in@20 { - compatible = "adi,adv7180"; - reg = <0x20>; - remote = <&vin1>; - - port { - adv7180: endpoint { - bus-width = <8>; - remote-endpoint = <&vin1ep0>; - }; - }; - }; - - cec_clock: cec-clock { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <12000000>; - }; - - hdmi@39 { - compatible = "adi,adv7511w"; - reg = <0x39>; - interrupt-parent = <&gpio1>; - interrupts = <15 IRQ_TYPE_LEVEL_LOW>; - clocks = <&cec_clock>; - clock-names = "cec"; - - adi,input-depth = <8>; - adi,input-colorspace = "rgb"; - adi,input-clock = "1x"; - adi,input-style = <1>; - adi,input-justification = "evenly"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7511_in: endpoint { - remote-endpoint = <&du_out_lvds0>; - }; - }; - - port@1 { - reg = <1>; - adv7511_out: endpoint { - remote-endpoint = <&hdmi_con_out>; - }; - }; - }; - }; - - hdmi-in@4c { - compatible = "adi,adv7612"; - reg = <0x4c>; - interrupt-parent = <&gpio1>; - interrupts = <20 IRQ_TYPE_LEVEL_LOW>; - default-input = <0>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7612_in: endpoint { - remote-endpoint = <&hdmi_con_in>; - }; - }; +&iic2 { + pinctrl-0 = <&iic2_pins>; + pinctrl-names = "i2c-hdmi"; - port@2 { - reg = <2>; - adv7612_out: endpoint { - remote-endpoint = <&vin0ep2>; - }; - }; - }; - }; + clock-frequency = <100000>; }; &iic3 { -- cgit v1.3-14-g43fede From e6081b21a19156c982f757cb1b6753cfb7e93ebc Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 6 Feb 2018 23:29:51 +0100 Subject: ARM: dts: lager: use demuxer for IIC3/I2C3 Create a separate bus for da9063 PMIC and da9210 regulator related I2C slaves. Based on similar work for HDMI by Wolfram Sang. Signed-off-by: Simon Horman [wsa: rebased, corrected chip name in commit msg, updated aliases] Signed-off-by: Wolfram Sang --- arch/arm/boot/dts/r8a7790-lager.dts | 84 +++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/arch/arm/boot/dts/r8a7790-lager.dts b/arch/arm/boot/dts/r8a7790-lager.dts index 9f511d4fac10..20c1b4f224b8 100644 --- a/arch/arm/boot/dts/r8a7790-lager.dts +++ b/arch/arm/boot/dts/r8a7790-lager.dts @@ -55,6 +55,7 @@ i2c10 = &i2cexio0; i2c11 = &i2cexio1; i2c12 = &i2chdmi; + i2c13 = &i2cpwr; }; chosen { @@ -418,6 +419,46 @@ }; }; }; + + /* + * IIC3 and I2C3 may be switched using pinmux. + * IIC3/I2C3 does not appear to support fallback to GPIO. + */ + i2cpwr: i2c-13 { + compatible = "i2c-demux-pinctrl"; + i2c-parent = <&iic3>, <&i2c3>; + i2c-bus-name = "i2c-pwr"; + #address-cells = <1>; + #size-cells = <0>; + + pmic@58 { + compatible = "dlg,da9063"; + reg = <0x58>; + interrupt-parent = <&irqc0>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + + rtc { + compatible = "dlg,da9063-rtc"; + }; + + wdt { + compatible = "dlg,da9063-watchdog"; + }; + }; + + vdd_dvfs: regulator@68 { + compatible = "dlg,da9210"; + reg = <0x68>; + interrupt-parent = <&irqc0>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-boot-on; + regulator-always-on; + }; + }; }; &du { @@ -557,6 +598,11 @@ function = "iic2"; }; + i2c3_pins: i2c3 { + groups = "i2c3"; + function = "i2c3"; + }; + iic3_pins: iic3 { groups = "iic3"; function = "iic3"; @@ -772,38 +818,14 @@ clock-frequency = <100000>; }; -&iic3 { - pinctrl-names = "default"; - pinctrl-0 = <&iic3_pins>; - status = "okay"; - - pmic@58 { - compatible = "dlg,da9063"; - reg = <0x58>; - interrupt-parent = <&irqc0>; - interrupts = <2 IRQ_TYPE_LEVEL_LOW>; - interrupt-controller; - - rtc { - compatible = "dlg,da9063-rtc"; - }; - - wdt { - compatible = "dlg,da9063-watchdog"; - }; - }; - - vdd_dvfs: regulator@68 { - compatible = "dlg,da9210"; - reg = <0x68>; - interrupt-parent = <&irqc0>; - interrupts = <2 IRQ_TYPE_LEVEL_LOW>; +&i2c3 { + pinctrl-0 = <&i2c3_pins>; + pinctrl-names = "i2c-pwr"; +}; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - regulator-boot-on; - regulator-always-on; - }; +&iic3 { + pinctrl-0 = <&iic3_pins>; + pinctrl-names = "i2c-pwr"; }; &pci0 { -- cgit v1.3-14-g43fede From 168a70999401e516fdf63dca742218bab04996a4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 6 Feb 2018 23:29:52 +0100 Subject: ARM: dts: koelsch: use demuxer for I2C2 Create a separate bus for HDMI related I2C2 and provide fallback to GPIO. Based on work for the r8a7790/lager by Wolfram Sang. Signed-off-by: Simon Horman [wsa: rebased, fixed aliases, switched to named GPIOs, sorted by SCL pins] Signed-off-by: Wolfram Sang --- arch/arm/boot/dts/r8a7791-koelsch.dts | 200 +++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 89 deletions(-) diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts index a50924d12b6f..4b20db197de6 100644 --- a/arch/arm/boot/dts/r8a7791-koelsch.dts +++ b/arch/arm/boot/dts/r8a7791-koelsch.dts @@ -51,7 +51,9 @@ serial0 = &scif0; serial1 = &scif1; i2c9 = &gpioi2c1; + i2c10 = &gpioi2c2; i2c12 = &i2cexio1; + i2c13 = &i2chdmi; }; chosen { @@ -312,8 +314,18 @@ #size-cells = <0>; compatible = "i2c-gpio"; status = "disabled"; - sda-gpios = <&gpio7 16 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpio7 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio7 16 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + }; + + gpioi2c2: i2c-10 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "i2c-gpio"; + status = "disabled"; + scl-gpios = <&gpio2 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; i2c-gpio,delay-us = <5>; }; @@ -328,6 +340,103 @@ #address-cells = <1>; #size-cells = <0>; }; + + /* + * A fallback to GPIO is provided for I2C2. + */ + i2chdmi: i2c-13 { + compatible = "i2c-demux-pinctrl"; + i2c-parent = <&i2c2>, <&gpioi2c2>; + i2c-bus-name = "i2c-hdmi"; + #address-cells = <1>; + #size-cells = <0>; + + ak4643: codec@12 { + compatible = "asahi-kasei,ak4643"; + #sound-dai-cells = <0>; + reg = <0x12>; + }; + + composite-in@20 { + compatible = "adi,adv7180"; + reg = <0x20>; + remote = <&vin1>; + + port { + adv7180: endpoint { + bus-width = <8>; + remote-endpoint = <&vin1ep>; + }; + }; + }; + + hdmi@39 { + compatible = "adi,adv7511w"; + reg = <0x39>; + interrupt-parent = <&gpio3>; + interrupts = <29 IRQ_TYPE_LEVEL_LOW>; + clocks = <&cec_clock>; + clock-names = "cec"; + + adi,input-depth = <8>; + adi,input-colorspace = "rgb"; + adi,input-clock = "1x"; + adi,input-style = <1>; + adi,input-justification = "evenly"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7511_in: endpoint { + remote-endpoint = <&du_out_rgb>; + }; + }; + + port@1 { + reg = <1>; + adv7511_out: endpoint { + remote-endpoint = <&hdmi_con_out>; + }; + }; + }; + }; + + hdmi-in@4c { + compatible = "adi,adv7612"; + reg = <0x4c>; + interrupt-parent = <&gpio4>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + default-input = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7612_in: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; + }; + + port@2 { + reg = <2>; + adv7612_out: endpoint { + remote-endpoint = <&vin0ep2>; + }; + }; + }; + }; + + eeprom@50 { + compatible = "renesas,r1ex24002", "atmel,24c02"; + reg = <0x50>; + pagesize = <16>; + }; + }; }; &du { @@ -621,96 +730,9 @@ &i2c2 { pinctrl-0 = <&i2c2_pins>; - pinctrl-names = "default"; + pinctrl-names = "i2c-hdmi"; - status = "okay"; clock-frequency = <100000>; - - ak4643: codec@12 { - compatible = "asahi-kasei,ak4643"; - #sound-dai-cells = <0>; - reg = <0x12>; - }; - - composite-in@20 { - compatible = "adi,adv7180"; - reg = <0x20>; - remote = <&vin1>; - - port { - adv7180: endpoint { - bus-width = <8>; - remote-endpoint = <&vin1ep>; - }; - }; - }; - - hdmi@39 { - compatible = "adi,adv7511w"; - reg = <0x39>; - interrupt-parent = <&gpio3>; - interrupts = <29 IRQ_TYPE_LEVEL_LOW>; - clocks = <&cec_clock>; - clock-names = "cec"; - - adi,input-depth = <8>; - adi,input-colorspace = "rgb"; - adi,input-clock = "1x"; - adi,input-style = <1>; - adi,input-justification = "evenly"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7511_in: endpoint { - remote-endpoint = <&du_out_rgb>; - }; - }; - - port@1 { - reg = <1>; - adv7511_out: endpoint { - remote-endpoint = <&hdmi_con_out>; - }; - }; - }; - }; - - hdmi-in@4c { - compatible = "adi,adv7612"; - reg = <0x4c>; - interrupt-parent = <&gpio4>; - interrupts = <2 IRQ_TYPE_LEVEL_LOW>; - default-input = <0>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7612_in: endpoint { - remote-endpoint = <&hdmi_con_in>; - }; - }; - - port@2 { - reg = <2>; - adv7612_out: endpoint { - remote-endpoint = <&vin0ep2>; - }; - }; - }; - }; - - eeprom@50 { - compatible = "renesas,r1ex24002", "atmel,24c02"; - reg = <0x50>; - pagesize = <16>; - }; }; &i2c6 { -- cgit v1.3-14-g43fede From be93275c4dade2d089d495f38a09e62712ea4e39 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 6 Feb 2018 23:29:53 +0100 Subject: ARM: dts: koelsch: use demuxer for I2C4 Make it possible to fallback to GPIO for I2C4 on the EXIO-E connector. This is based on reference work for the I2C0 core of the lager/r8a7790 by Wolfram Sang. Signed-off-by: Simon Horman [wsa: rebased, fixed aliases, switched to named GPIOS, fixed pinmux for I2C4] Signed-off-by: Wolfram Sang --- arch/arm/boot/dts/r8a7791-koelsch.dts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts index 4b20db197de6..f40321a1c917 100644 --- a/arch/arm/boot/dts/r8a7791-koelsch.dts +++ b/arch/arm/boot/dts/r8a7791-koelsch.dts @@ -52,8 +52,10 @@ serial1 = &scif1; i2c9 = &gpioi2c1; i2c10 = &gpioi2c2; + i2c11 = &gpioi2c4; i2c12 = &i2cexio1; i2c13 = &i2chdmi; + i2c14 = &i2cexio4; }; chosen { @@ -329,6 +331,16 @@ i2c-gpio,delay-us = <5>; }; + gpioi2c4: i2c-11 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "i2c-gpio"; + status = "disabled"; + scl-gpios = <&gpio7 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio7 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + }; + /* * I2C1 is routed to EXIO connector B, pins 64 (SCL) + 66 (SDA). * A fallback to GPIO is provided. @@ -437,6 +449,18 @@ pagesize = <16>; }; }; + + /* + * I2C4 is routed to EXIO connector E, pins 37 (SCL) + 39 (SDA). + * A fallback to GPIO is provided. + */ + i2cexio4: i2c-14 { + compatible = "i2c-demux-pinctrl"; + i2c-parent = <&i2c4>, <&gpioi2c4>; + i2c-bus-name = "i2c-exio4"; + #address-cells = <1>; + #size-cells = <0>; + }; }; &du { @@ -480,6 +504,11 @@ function = "i2c2"; }; + i2c4_pins: i2c4 { + groups = "i2c4_c"; + function = "i2c4"; + }; + du_pins: du { groups = "du_rgb888", "du_sync", "du_disp", "du_clk_out_0"; function = "du"; @@ -735,6 +764,11 @@ clock-frequency = <100000>; }; +&i2c4 { + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "i2c-exio4"; +}; + &i2c6 { status = "okay"; clock-frequency = <100000>; -- cgit v1.3-14-g43fede From 12937bffd64f5b9970589d0b946305d14cc45e6a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 6 Feb 2018 23:29:54 +0100 Subject: ARM: dts: porter: use demuxer for I2C2 Create a separate bus for HDMI related I2C2 and provide fallback to GPIO. Based on work for the r8a7790/lager by Wolfram Sang. Signed-off-by: Simon Horman [wsa: rebased, fixed aliases, switched to named GPIOs] Signed-off-by: Wolfram Sang --- arch/arm/boot/dts/r8a7791-porter.dts | 128 ++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 53 deletions(-) diff --git a/arch/arm/boot/dts/r8a7791-porter.dts b/arch/arm/boot/dts/r8a7791-porter.dts index 9a02d03b23c2..c14e6fe9e4f6 100644 --- a/arch/arm/boot/dts/r8a7791-porter.dts +++ b/arch/arm/boot/dts/r8a7791-porter.dts @@ -29,6 +29,8 @@ aliases { serial0 = &scif0; + i2c9 = &gpioi2c2; + i2c10 = &i2chdmi; }; chosen { @@ -135,6 +137,78 @@ clocks = <&x14_clk>; }; }; + + gpioi2c2: i2c-9 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "i2c-gpio"; + status = "disabled"; + scl-gpios = <&gpio2 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + }; + + /* + * A fallback to GPIO is provided for I2C2. + */ + i2chdmi: i2c-10 { + compatible = "i2c-demux-pinctrl"; + i2c-parent = <&i2c2>, <&gpioi2c2>; + i2c-bus-name = "i2c-hdmi"; + #address-cells = <1>; + #size-cells = <0>; + + ak4642: codec@12 { + compatible = "asahi-kasei,ak4642"; + #sound-dai-cells = <0>; + reg = <0x12>; + }; + + composite-in@20 { + compatible = "adi,adv7180"; + reg = <0x20>; + remote = <&vin0>; + + port { + adv7180: endpoint { + bus-width = <8>; + remote-endpoint = <&vin0ep>; + }; + }; + }; + + hdmi@39 { + compatible = "adi,adv7511w"; + reg = <0x39>; + interrupt-parent = <&gpio3>; + interrupts = <29 IRQ_TYPE_LEVEL_LOW>; + + adi,input-depth = <8>; + adi,input-colorspace = "rgb"; + adi,input-clock = "1x"; + adi,input-style = <1>; + adi,input-justification = "evenly"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7511_in: endpoint { + remote-endpoint = <&du_out_rgb>; + }; + }; + + port@1 { + reg = <1>; + adv7511_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; + }; }; &extal_clk { @@ -296,61 +370,9 @@ &i2c2 { pinctrl-0 = <&i2c2_pins>; - pinctrl-names = "default"; + pinctrl-names = "i2c-hdmi"; - status = "okay"; clock-frequency = <400000>; - - ak4642: codec@12 { - compatible = "asahi-kasei,ak4642"; - #sound-dai-cells = <0>; - reg = <0x12>; - }; - - composite-in@20 { - compatible = "adi,adv7180"; - reg = <0x20>; - remote = <&vin0>; - - port { - adv7180: endpoint { - bus-width = <8>; - remote-endpoint = <&vin0ep>; - }; - }; - }; - - hdmi@39 { - compatible = "adi,adv7511w"; - reg = <0x39>; - interrupt-parent = <&gpio3>; - interrupts = <29 IRQ_TYPE_LEVEL_LOW>; - - adi,input-depth = <8>; - adi,input-colorspace = "rgb"; - adi,input-clock = "1x"; - adi,input-style = <1>; - adi,input-justification = "evenly"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7511_in: endpoint { - remote-endpoint = <&du_out_rgb>; - }; - }; - - port@1 { - reg = <1>; - adv7511_out: endpoint { - remote-endpoint = <&hdmi_con>; - }; - }; - }; - }; }; &sata0 { -- cgit v1.3-14-g43fede From 61866d7fdc7007acfca2107f7d9fcb05ed0c6203 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 6 Feb 2018 23:29:55 +0100 Subject: ARM: dts: alt: use demuxer for I2C1 Create a separate bus for HDMI related I2C1 and provide fallback to GPIO. Based on work for the r8a7790/lager by Wolfram Sang. Signed-off-by: Simon Horman [wsa: rebased, fixed aliases, switched to named GPIOs] Signed-off-by: Wolfram Sang --- arch/arm/boot/dts/r8a7794-alt.dts | 53 +++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/arch/arm/boot/dts/r8a7794-alt.dts b/arch/arm/boot/dts/r8a7794-alt.dts index 60c6515c4996..26a883484ea8 100644 --- a/arch/arm/boot/dts/r8a7794-alt.dts +++ b/arch/arm/boot/dts/r8a7794-alt.dts @@ -18,7 +18,9 @@ aliases { serial0 = &scif2; + i2c9 = &gpioi2c1; i2c10 = &gpioi2c4; + i2c11 = &i2chdmi; i2c12 = &i2cexio4; }; @@ -138,16 +140,49 @@ clock-frequency = <148500000>; }; + gpioi2c1: i2c-9 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "i2c-gpio"; + status = "disabled"; + scl-gpios = <&gpio4 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio4 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + }; + gpioi2c4: i2c-10 { #address-cells = <1>; #size-cells = <0>; compatible = "i2c-gpio"; status = "disabled"; - sda-gpios = <&gpio4 9 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpio4 8 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio4 9 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; i2c-gpio,delay-us = <5>; }; + /* + * A fallback to GPIO is provided for I2C1. + */ + i2chdmi: i2c-11 { + compatible = "i2c-demux-pinctrl"; + i2c-parent = <&i2c1>, <&gpioi2c1>; + i2c-bus-name = "i2c-hdmi"; + #address-cells = <1>; + #size-cells = <0>; + + composite-in@20 { + compatible = "adi,adv7180"; + reg = <0x20>; + remote = <&vin0>; + + port { + adv7180: endpoint { + bus-width = <8>; + remote-endpoint = <&vin0ep>; + }; + }; + }; + }; + /* * I2C4 is routed to EXIO connector B, pins 73 (SCL) + 74 (SDA). * A fallback to GPIO is provided. @@ -324,23 +359,9 @@ &i2c1 { pinctrl-0 = <&i2c1_pins>; - pinctrl-names = "default"; + pinctrl-names = "i2c-hdmi"; - status = "okay"; clock-frequency = <400000>; - - composite-in@20 { - compatible = "adi,adv7180"; - reg = <0x20>; - remote = <&vin0>; - - port { - adv7180: endpoint { - bus-width = <8>; - remote-endpoint = <&vin0ep>; - }; - }; - }; }; &i2c4 { -- cgit v1.3-14-g43fede From d4c1ce2813942407cead7adb86d1dd9dfc46f74c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 6 Feb 2018 23:29:56 +0100 Subject: ARM: dts: silk: use demuxer for I2C1 Create a separate bus for HDMI related I2C1 and provide fallback to GPIO. Based on work for the r8a7790/lager by Wolfram Sang. Signed-off-by: Simon Horman [wsa: rebased, fixed aliases, switched to named GPIOs] Signed-off-by: Wolfram Sang --- arch/arm/boot/dts/r8a7794-silk.dts | 128 ++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 53 deletions(-) diff --git a/arch/arm/boot/dts/r8a7794-silk.dts b/arch/arm/boot/dts/r8a7794-silk.dts index edfad0e5ac53..f1afe2dda13a 100644 --- a/arch/arm/boot/dts/r8a7794-silk.dts +++ b/arch/arm/boot/dts/r8a7794-silk.dts @@ -31,6 +31,8 @@ aliases { serial0 = &scif2; + i2c9 = &gpioi2c1; + i2c10 = &i2chdmi; }; chosen { @@ -153,6 +155,78 @@ clocks = <&x9_clk>; }; }; + + gpioi2c1: i2c-9 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "i2c-gpio"; + status = "disabled"; + scl-gpios = <&gpio4 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio4 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + }; + + /* + * A fallback to GPIO is provided for I2C1. + */ + i2chdmi: i2c-10 { + compatible = "i2c-demux-pinctrl"; + i2c-parent = <&i2c1>, <&gpioi2c1>; + i2c-bus-name = "i2c-hdmi"; + #address-cells = <1>; + #size-cells = <0>; + + ak4643: codec@12 { + compatible = "asahi-kasei,ak4643"; + #sound-dai-cells = <0>; + reg = <0x12>; + }; + + composite-in@20 { + compatible = "adi,adv7180"; + reg = <0x20>; + remote = <&vin0>; + + port { + adv7180: endpoint { + bus-width = <8>; + remote-endpoint = <&vin0ep>; + }; + }; + }; + + hdmi@39 { + compatible = "adi,adv7511w"; + reg = <0x39>; + interrupt-parent = <&gpio5>; + interrupts = <23 IRQ_TYPE_LEVEL_LOW>; + + adi,input-depth = <8>; + adi,input-colorspace = "rgb"; + adi,input-clock = "1x"; + adi,input-style = <1>; + adi,input-justification = "evenly"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7511_in: endpoint { + remote-endpoint = <&du_out_rgb0>; + }; + }; + + port@1 { + reg = <1>; + adv7511_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; + }; }; &extal_clk { @@ -268,61 +342,9 @@ &i2c1 { pinctrl-0 = <&i2c1_pins>; - pinctrl-names = "default"; + pinctrl-names = "i2c-hdmi"; - status = "okay"; clock-frequency = <400000>; - - ak4643: codec@12 { - compatible = "asahi-kasei,ak4643"; - #sound-dai-cells = <0>; - reg = <0x12>; - }; - - composite-in@20 { - compatible = "adi,adv7180"; - reg = <0x20>; - remote = <&vin0>; - - port { - adv7180: endpoint { - bus-width = <8>; - remote-endpoint = <&vin0ep>; - }; - }; - }; - - hdmi@39 { - compatible = "adi,adv7511w"; - reg = <0x39>; - interrupt-parent = <&gpio5>; - interrupts = <23 IRQ_TYPE_LEVEL_LOW>; - - adi,input-depth = <8>; - adi,input-colorspace = "rgb"; - adi,input-clock = "1x"; - adi,input-style = <1>; - adi,input-justification = "evenly"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7511_in: endpoint { - remote-endpoint = <&du_out_rgb0>; - }; - }; - - port@1 { - reg = <1>; - adv7511_out: endpoint { - remote-endpoint = <&hdmi_con>; - }; - }; - }; - }; }; &mmcif0 { -- cgit v1.3-14-g43fede From 786ef2eeb04cca41d6382556f1409f37f64c4a0c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 6 Feb 2018 23:29:57 +0100 Subject: ARM: dts: gose: use demuxer for I2C2 Create a separate bus for HDMI related I2C2 and provide fallback to GPIO. Based on work for the r8a7790/lager by Wolfram Sang. Signed-off-by: Simon Horman [wsa: rebased, corrected board name in subject, fixed aliases, switched to named GPIOs] Signed-off-by: Wolfram Sang --- arch/arm/boot/dts/r8a7793-gose.dts | 218 ++++++++++++++++++++----------------- 1 file changed, 121 insertions(+), 97 deletions(-) diff --git a/arch/arm/boot/dts/r8a7793-gose.dts b/arch/arm/boot/dts/r8a7793-gose.dts index 51b3ffac8efa..2b330ef71f4c 100644 --- a/arch/arm/boot/dts/r8a7793-gose.dts +++ b/arch/arm/boot/dts/r8a7793-gose.dts @@ -48,6 +48,8 @@ aliases { serial0 = &scif0; serial1 = &scif1; + i2c9 = &gpioi2c2; + i2c11 = &i2chdmi; }; chosen { @@ -296,6 +298,124 @@ #clock-cells = <0>; clock-frequency = <148500000>; }; + + gpioi2c2: i2c-9 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "i2c-gpio"; + status = "disabled"; + scl-gpios = <&gpio2 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + }; + + /* + * A fallback to GPIO is provided for I2C2. + */ + i2chdmi: i2c-11 { + compatible = "i2c-demux-pinctrl"; + i2c-parent = <&i2c2>, <&gpioi2c2>; + i2c-bus-name = "i2c-hdmi"; + #address-cells = <1>; + #size-cells = <0>; + + ak4643: codec@12 { + compatible = "asahi-kasei,ak4643"; + #sound-dai-cells = <0>; + reg = <0x12>; + }; + + composite-in@20 { + compatible = "adi,adv7180cp"; + reg = <0x20>; + remote = <&vin1>; + + port { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7180_in: endpoint { + remote-endpoint = <&composite_con_in>; + }; + }; + + port@3 { + reg = <3>; + adv7180_out: endpoint { + bus-width = <8>; + remote-endpoint = <&vin1ep>; + }; + }; + }; + }; + + hdmi@39 { + compatible = "adi,adv7511w"; + reg = <0x39>; + interrupt-parent = <&gpio3>; + interrupts = <29 IRQ_TYPE_LEVEL_LOW>; + + adi,input-depth = <8>; + adi,input-colorspace = "rgb"; + adi,input-clock = "1x"; + adi,input-style = <1>; + adi,input-justification = "evenly"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7511_in: endpoint { + remote-endpoint = <&du_out_rgb>; + }; + }; + + port@1 { + reg = <1>; + adv7511_out: endpoint { + remote-endpoint = <&hdmi_con_out>; + }; + }; + }; + }; + + hdmi-in@4c { + compatible = "adi,adv7612"; + reg = <0x4c>; + interrupt-parent = <&gpio4>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + default-input = <0>; + + port { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7612_in: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; + }; + + port@2 { + reg = <2>; + adv7612_out: endpoint { + remote-endpoint = <&vin0ep2>; + }; + }; + }; + }; + + eeprom@50 { + compatible = "renesas,r1ex24002", "atmel,24c02"; + reg = <0x50>; + pagesize = <16>; + }; + }; }; &du { @@ -544,107 +664,11 @@ &i2c2 { pinctrl-0 = <&i2c2_pins>; - pinctrl-names = "default"; + pinctrl-names = "i2c-hdmi"; status = "okay"; clock-frequency = <100000>; - ak4643: codec@12 { - compatible = "asahi-kasei,ak4643"; - #sound-dai-cells = <0>; - reg = <0x12>; - }; - - composite-in@20 { - compatible = "adi,adv7180cp"; - reg = <0x20>; - remote = <&vin1>; - - port { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7180_in: endpoint { - remote-endpoint = <&composite_con_in>; - }; - }; - - port@3 { - reg = <3>; - adv7180_out: endpoint { - bus-width = <8>; - remote-endpoint = <&vin1ep>; - }; - }; - }; - }; - - hdmi@39 { - compatible = "adi,adv7511w"; - reg = <0x39>; - interrupt-parent = <&gpio3>; - interrupts = <29 IRQ_TYPE_LEVEL_LOW>; - - adi,input-depth = <8>; - adi,input-colorspace = "rgb"; - adi,input-clock = "1x"; - adi,input-style = <1>; - adi,input-justification = "evenly"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7511_in: endpoint { - remote-endpoint = <&du_out_rgb>; - }; - }; - - port@1 { - reg = <1>; - adv7511_out: endpoint { - remote-endpoint = <&hdmi_con_out>; - }; - }; - }; - }; - - hdmi-in@4c { - compatible = "adi,adv7612"; - reg = <0x4c>; - interrupt-parent = <&gpio4>; - interrupts = <2 IRQ_TYPE_LEVEL_LOW>; - default-input = <0>; - - port { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7612_in: endpoint { - remote-endpoint = <&hdmi_con_in>; - }; - }; - - port@2 { - reg = <2>; - adv7612_out: endpoint { - remote-endpoint = <&vin0ep2>; - }; - }; - }; - }; - - eeprom@50 { - compatible = "renesas,r1ex24002", "atmel,24c02"; - reg = <0x50>; - pagesize = <16>; - }; }; &i2c6 { -- cgit v1.3-14-g43fede From 931bc7ec2d43f85c0b05aacb31bd1bd187ed7f65 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 6 Feb 2018 23:29:58 +0100 Subject: ARM: dts: gose: use demuxer for I2C4 Make it possible to fallback to GPIO for I2C4 on the EXIO-E connector. This is based on reference work for the I2C0 core of the lager/r8a7790 by Wolfram Sang. Signed-off-by: Simon Horman [wsa: rebased, corrected board name in subject, fixed aliases, switched to named GPIOs, fixed pinmux for I2C4] Signed-off-by: Wolfram Sang --- arch/arm/boot/dts/r8a7793-gose.dts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/arch/arm/boot/dts/r8a7793-gose.dts b/arch/arm/boot/dts/r8a7793-gose.dts index 2b330ef71f4c..9ed6961f2d9a 100644 --- a/arch/arm/boot/dts/r8a7793-gose.dts +++ b/arch/arm/boot/dts/r8a7793-gose.dts @@ -49,7 +49,9 @@ serial0 = &scif0; serial1 = &scif1; i2c9 = &gpioi2c2; + i2c10 = &gpioi2c4; i2c11 = &i2chdmi; + i2c12 = &i2cexio4; }; chosen { @@ -309,6 +311,16 @@ i2c-gpio,delay-us = <5>; }; + gpioi2c4: i2c-10 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "i2c-gpio"; + status = "disabled"; + scl-gpios = <&gpio7 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio7 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + }; + /* * A fallback to GPIO is provided for I2C2. */ @@ -416,6 +428,18 @@ pagesize = <16>; }; }; + + /* + * I2C4 is routed to EXIO connector E, pins 37 (SCL) + 39 (SDA). + * A fallback to GPIO is provided. + */ + i2cexio4: i2c-12 { + compatible = "i2c-demux-pinctrl"; + i2c-parent = <&i2c4>, <&gpioi2c4>; + i2c-bus-name = "i2c-exio4"; + #address-cells = <1>; + #size-cells = <0>; + }; }; &du { @@ -454,6 +478,11 @@ function = "i2c2"; }; + i2c4_pins: i2c4 { + groups = "i2c4_c"; + function = "i2c4"; + }; + du_pins: du { groups = "du_rgb888", "du_sync", "du_disp", "du_clk_out_0"; function = "du"; @@ -692,6 +721,11 @@ }; }; +&i2c4 { + pinctrl-0 = <&i2c4_pins>; + pinctrl-names = "i2c-exio4"; +}; + &rcar_sound { pinctrl-0 = <&sound_pins &sound_clk_pins>; pinctrl-names = "default"; -- cgit v1.3-14-g43fede From f15092f8482b63b91d7ca7f4bd7090da7f9f2f24 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 31 Jan 2018 22:56:47 +0300 Subject: dt-bindings: arm: document R8A77980 SoC bindings Document the R-Car V3H (R8A77980) SoC device tree bindings. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Reviewed-by: Rob Herring Signed-off-by: Simon Horman --- Documentation/devicetree/bindings/arm/shmobile.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/shmobile.txt b/Documentation/devicetree/bindings/arm/shmobile.txt index 5c3af7ef0761..3ccad353acfa 100644 --- a/Documentation/devicetree/bindings/arm/shmobile.txt +++ b/Documentation/devicetree/bindings/arm/shmobile.txt @@ -41,6 +41,8 @@ SoCs: compatible = "renesas,r8a7796" - R-Car V3M (R8A77970) compatible = "renesas,r8a77970" + - R-Car V3H (R8A77980) + compatible = "renesas,r8a77980" - R-Car D3 (R8A77995) compatible = "renesas,r8a77995" -- cgit v1.3-14-g43fede From 9ab5977d472b3c63f99c23c2c4ec3a9820c50faf Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 6 Feb 2018 14:20:45 +0100 Subject: arm64: defconfig: enable R8A77980 SoC Enable the Renesas R-Car V3H (R8A77980) SoC in the ARM64 defconfig. Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 634b373785c4..a850bc5e1af3 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -52,6 +52,7 @@ CONFIG_ARCH_RENESAS=y CONFIG_ARCH_R8A7795=y CONFIG_ARCH_R8A7796=y CONFIG_ARCH_R8A77970=y +CONFIG_ARCH_R8A77980=y CONFIG_ARCH_R8A77995=y CONFIG_ARCH_STRATIX10=y CONFIG_ARCH_TEGRA=y -- cgit v1.3-14-g43fede From 1c6c924a49e0cd8f908409fb2a68570d9a4b2998 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 3 Jan 2018 13:41:03 +0100 Subject: arm64: dts: renesas: r8a7795: move scif node into alphabetical order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move scif node so that sub-nodes of the root node are in alphabetical order. Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven Tested-by: Niklas Söderlund --- arch/arm64/boot/dts/renesas/r8a7795.dtsi | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi index d12df6f2ff09..24e9209ea54e 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi @@ -165,13 +165,6 @@ clock-frequency = <0>; }; - /* External SCIF clock - to be overridden by boards that provide it */ - scif_clk: scif { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - /* External PCIe clock - can be overridden by the board */ pcie_bus_clk: pcie_bus { compatible = "fixed-clock"; @@ -208,6 +201,13 @@ method = "smc"; }; + /* External SCIF clock - to be overridden by boards that provide it */ + scif_clk: scif { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + soc: soc { compatible = "simple-bus"; interrupt-parent = <&gic>; -- cgit v1.3-14-g43fede From dd149e851ace00a7832846e46ddefc6b181522c2 Mon Sep 17 00:00:00 2001 From: Dien Pham Date: Wed, 3 Jan 2018 13:41:04 +0100 Subject: arm64: dts: renesas: r8a7795: Add OPPs table for cpu devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define OOP tables for all CPUs. This allows CPUFreq to function. Based in part on work by Hien Dang. Signed-off-by: Dien Pham Signed-off-by: Takeshi Kihara Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven Tested-by: Niklas Söderlund --- arch/arm64/boot/dts/renesas/r8a7795.dtsi | 61 ++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi index 24e9209ea54e..1485e6a8e112 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi @@ -41,6 +41,8 @@ power-domains = <&sysc R8A7795_PD_CA57_CPU0>; next-level-cache = <&L2_CA57>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7795_CLK_Z>; + operating-points-v2 = <&cluster0_opp>; }; a57_1: cpu@1 { @@ -50,6 +52,8 @@ power-domains = <&sysc R8A7795_PD_CA57_CPU1>; next-level-cache = <&L2_CA57>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7795_CLK_Z>; + operating-points-v2 = <&cluster0_opp>; }; a57_2: cpu@2 { @@ -59,6 +63,8 @@ power-domains = <&sysc R8A7795_PD_CA57_CPU2>; next-level-cache = <&L2_CA57>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7795_CLK_Z>; + operating-points-v2 = <&cluster0_opp>; }; a57_3: cpu@3 { @@ -68,6 +74,8 @@ power-domains = <&sysc R8A7795_PD_CA57_CPU3>; next-level-cache = <&L2_CA57>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7795_CLK_Z>; + operating-points-v2 = <&cluster0_opp>; }; a53_0: cpu@100 { @@ -77,6 +85,8 @@ power-domains = <&sysc R8A7795_PD_CA53_CPU0>; next-level-cache = <&L2_CA53>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7795_CLK_Z2>; + operating-points-v2 = <&cluster1_opp>; }; a53_1: cpu@101 { @@ -86,6 +96,8 @@ power-domains = <&sysc R8A7795_PD_CA53_CPU1>; next-level-cache = <&L2_CA53>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7795_CLK_Z2>; + operating-points-v2 = <&cluster1_opp>; }; a53_2: cpu@102 { @@ -95,6 +107,8 @@ power-domains = <&sysc R8A7795_PD_CA53_CPU2>; next-level-cache = <&L2_CA53>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7795_CLK_Z2>; + operating-points-v2 = <&cluster1_opp>; }; a53_3: cpu@103 { @@ -104,6 +118,8 @@ power-domains = <&sysc R8A7795_PD_CA53_CPU3>; next-level-cache = <&L2_CA53>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7795_CLK_Z2>; + operating-points-v2 = <&cluster1_opp>; }; L2_CA57: cache-controller-0 { @@ -165,6 +181,51 @@ clock-frequency = <0>; }; + cluster0_opp: opp_table0 { + compatible = "operating-points-v2"; + opp-shared; + + opp-500000000 { + opp-hz = /bits/ 64 <500000000>; + opp-microvolt = <830000>; + clock-latency-ns = <300000>; + }; + opp-1000000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <830000>; + clock-latency-ns = <300000>; + }; + opp-1500000000 { + opp-hz = /bits/ 64 <1500000000>; + opp-microvolt = <830000>; + clock-latency-ns = <300000>; + opp-suspend; + }; + opp-1600000000 { + opp-hz = /bits/ 64 <1600000000>; + opp-microvolt = <900000>; + clock-latency-ns = <300000>; + turbo-mode; + }; + opp-1700000000 { + opp-hz = /bits/ 64 <1700000000>; + opp-microvolt = <960000>; + clock-latency-ns = <300000>; + turbo-mode; + }; + }; + + cluster1_opp: opp_table1 { + compatible = "operating-points-v2"; + opp-shared; + + opp-1200000000 { + opp-hz = /bits/ 64 <1200000000>; + opp-microvolt = <820000>; + clock-latency-ns = <300000>; + }; + }; + /* External PCIe clock - can be overridden by the board */ pcie_bus_clk: pcie_bus { compatible = "fixed-clock"; -- cgit v1.3-14-g43fede From da7e3113344fda50b10f1ad2c633abaaaf25d21b Mon Sep 17 00:00:00 2001 From: Dien Pham Date: Wed, 3 Jan 2018 13:41:05 +0100 Subject: arm64: dts: renesas: r8a7796: Add OPPs table for cpu devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define OOP tables for all CPUs. This allows CPUFreq to function. Based in part on work by Hien Dang. Signed-off-by: Dien Pham Signed-off-by: Takeshi Kihara Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven Tested-by: Niklas Söderlund --- arch/arm64/boot/dts/renesas/r8a7796.dtsi | 62 ++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi index c5192d513d7d..e06bde6e2853 100644 --- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi @@ -71,6 +71,8 @@ power-domains = <&sysc R8A7796_PD_CA57_CPU0>; next-level-cache = <&L2_CA57>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7796_CLK_Z>; + operating-points-v2 = <&cluster0_opp>; }; a57_1: cpu@1 { @@ -80,6 +82,8 @@ power-domains = <&sysc R8A7796_PD_CA57_CPU1>; next-level-cache = <&L2_CA57>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7796_CLK_Z>; + operating-points-v2 = <&cluster0_opp>; }; a53_0: cpu@100 { @@ -89,6 +93,8 @@ power-domains = <&sysc R8A7796_PD_CA53_CPU0>; next-level-cache = <&L2_CA53>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7796_CLK_Z2>; + operating-points-v2 = <&cluster1_opp>; }; a53_1: cpu@101 { @@ -98,6 +104,8 @@ power-domains = <&sysc R8A7796_PD_CA53_CPU1>; next-level-cache = <&L2_CA53>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7796_CLK_Z2>; + operating-points-v2 = <&cluster1_opp>; }; a53_2: cpu@102 { @@ -107,6 +115,8 @@ power-domains = <&sysc R8A7796_PD_CA53_CPU2>; next-level-cache = <&L2_CA53>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7796_CLK_Z2>; + operating-points-v2 = <&cluster1_opp>; }; a53_3: cpu@103 { @@ -116,6 +126,8 @@ power-domains = <&sysc R8A7796_PD_CA53_CPU3>; next-level-cache = <&L2_CA53>; enable-method = "psci"; + clocks =<&cpg CPG_CORE R8A7796_CLK_Z2>; + operating-points-v2 = <&cluster1_opp>; }; L2_CA57: cache-controller-0 { @@ -147,6 +159,56 @@ clock-frequency = <0>; }; + cluster0_opp: opp_table0 { + compatible = "operating-points-v2"; + opp-shared; + + opp-500000000 { + opp-hz = /bits/ 64 <500000000>; + opp-microvolt = <820000>; + clock-latency-ns = <300000>; + }; + opp-1000000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <820000>; + clock-latency-ns = <300000>; + }; + opp-1500000000 { + opp-hz = /bits/ 64 <1500000000>; + opp-microvolt = <820000>; + clock-latency-ns = <300000>; + }; + opp-1600000000 { + opp-hz = /bits/ 64 <1600000000>; + opp-microvolt = <900000>; + clock-latency-ns = <300000>; + turbo-mode; + }; + opp-1700000000 { + opp-hz = /bits/ 64 <1700000000>; + opp-microvolt = <900000>; + clock-latency-ns = <300000>; + turbo-mode; + }; + opp-1800000000 { + opp-hz = /bits/ 64 <1800000000>; + opp-microvolt = <960000>; + clock-latency-ns = <300000>; + turbo-mode; + }; + }; + + cluster1_opp: opp_table1 { + compatible = "operating-points-v2"; + opp-shared; + + opp-1200000000 { + opp-hz = /bits/ 64 <1200000000>; + opp-microvolt = <820000>; + clock-latency-ns = <300000>; + }; + }; + /* External PCIe clock - can be overridden by the board */ pcie_bus_clk: pcie_bus { compatible = "fixed-clock"; -- cgit v1.3-14-g43fede From 479e5d70a9cee635d0feda260eed3719fb565ca8 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 4 Jan 2018 17:03:20 +0100 Subject: arm64: dts: renesas: r8a7796: add thermal cooling management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add nodes and properties for thermal cooling management support. Signed-off-by: Niklas Söderlund Acked-by: Eduardo Valentin Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a7796.dtsi | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi index e06bde6e2853..ef10fb548681 100644 --- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi @@ -73,6 +73,7 @@ enable-method = "psci"; clocks =<&cpg CPG_CORE R8A7796_CLK_Z>; operating-points-v2 = <&cluster0_opp>; + #cooling-cells = <2>; }; a57_1: cpu@1 { @@ -84,6 +85,7 @@ enable-method = "psci"; clocks =<&cpg CPG_CORE R8A7796_CLK_Z>; operating-points-v2 = <&cluster0_opp>; + #cooling-cells = <2>; }; a53_0: cpu@100 { @@ -2060,12 +2062,24 @@ thermal-sensors = <&tsc 0>; trips { + sensor1_passive: sensor1-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; sensor1_crit: sensor1-crit { temperature = <120000>; hysteresis = <2000>; type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&sensor1_passive>; + cooling-device = <&a57_0 5 5>; + }; + }; }; sensor_thermal2: sensor-thermal2 { @@ -2074,12 +2088,24 @@ thermal-sensors = <&tsc 1>; trips { + sensor2_passive: sensor2-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; sensor2_crit: sensor2-crit { temperature = <120000>; hysteresis = <2000>; type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&sensor2_passive>; + cooling-device = <&a57_0 5 5>; + }; + }; }; sensor_thermal3: sensor-thermal3 { @@ -2088,12 +2114,24 @@ thermal-sensors = <&tsc 2>; trips { + sensor3_passive: sensor3-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; sensor3_crit: sensor3-crit { temperature = <120000>; hysteresis = <2000>; type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&sensor3_passive>; + cooling-device = <&a57_0 5 5>; + }; + }; }; }; -- cgit v1.3-14-g43fede From 0c38c54ef9bc2a1062900adb01833e0b824f8462 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 4 Jan 2018 17:03:19 +0100 Subject: arm64: dts: renesas: r8a7795: add thermal cooling management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add nodes and properties for thermal cooling management support. Signed-off-by: Niklas Söderlund Acked-by: Eduardo Valentin Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a7795.dtsi | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi index 1485e6a8e112..19b7de57704c 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi @@ -43,6 +43,7 @@ enable-method = "psci"; clocks =<&cpg CPG_CORE R8A7795_CLK_Z>; operating-points-v2 = <&cluster0_opp>; + #cooling-cells = <2>; }; a57_1: cpu@1 { @@ -54,6 +55,7 @@ enable-method = "psci"; clocks =<&cpg CPG_CORE R8A7795_CLK_Z>; operating-points-v2 = <&cluster0_opp>; + #cooling-cells = <2>; }; a57_2: cpu@2 { @@ -65,6 +67,7 @@ enable-method = "psci"; clocks =<&cpg CPG_CORE R8A7795_CLK_Z>; operating-points-v2 = <&cluster0_opp>; + #cooling-cells = <2>; }; a57_3: cpu@3 { @@ -76,6 +79,7 @@ enable-method = "psci"; clocks =<&cpg CPG_CORE R8A7795_CLK_Z>; operating-points-v2 = <&cluster0_opp>; + #cooling-cells = <2>; }; a53_0: cpu@100 { @@ -2418,12 +2422,24 @@ thermal-sensors = <&tsc 0>; trips { + sensor1_passive: sensor1-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; sensor1_crit: sensor1-crit { temperature = <120000>; hysteresis = <2000>; type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&sensor1_passive>; + cooling-device = <&a57_0 4 4>; + }; + }; }; sensor_thermal2: sensor-thermal2 { @@ -2432,12 +2448,24 @@ thermal-sensors = <&tsc 1>; trips { + sensor2_passive: sensor2-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; sensor2_crit: sensor2-crit { temperature = <120000>; hysteresis = <2000>; type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&sensor2_passive>; + cooling-device = <&a57_0 4 4>; + }; + }; }; sensor_thermal3: sensor-thermal3 { @@ -2446,12 +2474,24 @@ thermal-sensors = <&tsc 2>; trips { + sensor3_passive: sensor3-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; sensor3_crit: sensor3-crit { temperature = <120000>; hysteresis = <2000>; type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&sensor3_passive>; + cooling-device = <&a57_0 4 4>; + }; + }; }; }; -- cgit v1.3-14-g43fede From ece30287af4e08e681f27c3d58853b68cdde658b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 22 Dec 2017 21:11:46 +0100 Subject: arm64: dts: renesas: salvator-common: add GPIO extender We need to configure its GPIOs later. Signed-off-by: Wolfram Sang Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/salvator-common.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/salvator-common.dtsi b/arch/arm64/boot/dts/renesas/salvator-common.dtsi index c3fafb6025b3..89cbb3b95acd 100644 --- a/arch/arm64/boot/dts/renesas/salvator-common.dtsi +++ b/arch/arm64/boot/dts/renesas/salvator-common.dtsi @@ -338,6 +338,13 @@ &i2c4 { status = "okay"; + pca9654: gpio@20 { + compatible = "onnn,pca9654"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + }; + csa_vdd: adc@7c { compatible = "maxim,max9611"; reg = <0x7c>; -- cgit v1.3-14-g43fede From 7569d1ee01c75b8581521bc67e595a575a7eafce Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 20 Dec 2017 13:25:49 +0100 Subject: arm64: dts: renesas: r8a77970: move node which has no reg property out of bus Move timer node from soc node to root node. The node that have been moved do not have any register properties and thus shouldn't be placed on the bus. This problem is flagged by the compiler as follows: $ make W=1 ... DTC arch/arm64/boot/dts/renesas/r8a77970-eagle.dtb arch/arm64/boot/dts/renesas/r8a77970-eagle.dtb: Warning (simple_bus_reg): Node /soc/timer missing or empty reg/ranges property DTC arch/arm64/boot/dts/renesas/r8a77970-v3msk.dtb arch/arm64/boot/dts/renesas/r8a77970-v3msk.dtb: Warning (simple_bus_reg): Node /soc/timer missing or empty reg/ranges property Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77970.dtsi | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/r8a77970.dtsi b/arch/arm64/boot/dts/renesas/r8a77970.dtsi index c35a117fc447..566a7f704830 100644 --- a/arch/arm64/boot/dts/renesas/r8a77970.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77970.dtsi @@ -92,18 +92,6 @@ resets = <&cpg 408>; }; - timer { - compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; - }; - rwdt: watchdog@e6020000 { compatible = "renesas,r8a77970-wdt", "renesas,rcar-gen3-wdt"; @@ -442,4 +430,12 @@ #size-cells = <0>; }; }; + + timer { + compatible = "arm,armv8-timer"; + interrupts-extended = <&gic GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(1) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(1) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(1) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(1) | IRQ_TYPE_LEVEL_LOW)>; + }; }; -- cgit v1.3-14-g43fede From f320eead5dad37f296201bbcc00a88188a63a7da Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 20 Dec 2017 13:24:42 +0100 Subject: arm64: dts: renesas: r8a77995: move nodes which have no reg property out of bus Move pmu_a53 and timer nodes from soc node to root node. The nodes that have been moved do not have any register properties and thus shouldn't be placed on the bus. This problem is flagged by the compiler as follows: $ make W=1 ... arch/arm64/boot/dts/renesas/r8a77995-draak.dtb: Warning (simple_bus_reg): Node /soc/timer missing or empty reg/ranges property arch/arm64/boot/dts/renesas/r8a77995-draak.dtb: Warning (simple_bus_reg): Node /soc/pmu_a53 missing or empty reg/ranges property Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995.dtsi | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi index cff42cd1a6c8..23f763beab46 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi @@ -58,6 +58,11 @@ clock-frequency = <0>; }; + pmu_a53 { + compatible = "arm,cortex-a53-pmu"; + interrupts-extended = <&gic GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; + }; + scif_clk: scif { compatible = "fixed-clock"; #clock-cells = <0>; @@ -88,18 +93,6 @@ resets = <&cpg 408>; }; - timer { - compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; - }; - rwdt: watchdog@e6020000 { compatible = "renesas,r8a77995-wdt", "renesas,rcar-gen3-wdt"; @@ -110,11 +103,6 @@ status = "disabled"; }; - pmu_a53 { - compatible = "arm,cortex-a53-pmu"; - interrupts = ; - }; - ipmmu_vi0: mmu@febd0000 { compatible = "renesas,ipmmu-r8a77995"; reg = <0 0xfebd0000 0 0x1000>; @@ -637,4 +625,12 @@ status = "disabled"; }; }; + + timer { + compatible = "arm,armv8-timer"; + interrupts-extended = <&gic GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(1) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(1) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(1) | IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(1) | IRQ_TYPE_LEVEL_LOW)>; + }; }; -- cgit v1.3-14-g43fede From cd8325dc194d6fa0707c9d5b427ca1fed6893b53 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Fri, 5 Jan 2018 16:54:46 +0100 Subject: arm64: dts: renesas: r8a7795: update register size for thermal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be able to read fused calibration values from hardware the size of the register resource of TSC1 needs to be incremented to cover one more register which holds the information if the calibration values have been fused or not. Instead of increasing TSC1 size to the value from the datasheet update all TSC's size to the smallest granularity of the address decoder circuitry Signed-off-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a7795.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi index 19b7de57704c..ce85704976f0 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi @@ -2385,9 +2385,9 @@ tsc: thermal@e6198000 { compatible = "renesas,r8a7795-thermal"; - reg = <0 0xe6198000 0 0x68>, - <0 0xe61a0000 0 0x5c>, - <0 0xe61a8000 0 0x5c>; + reg = <0 0xe6198000 0 0x100>, + <0 0xe61a0000 0 0x100>, + <0 0xe61a8000 0 0x100>; interrupts = , , ; -- cgit v1.3-14-g43fede From a052d934a4e03a81e1f6cf1c63ac693b04e9a441 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Fri, 5 Jan 2018 16:54:47 +0100 Subject: arm64: dts: renesas: r8a7796: update register size for thermal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be able to read fused calibration values from hardware the size of the register resource of TSC1 needs to be incremented to cover one more register which holds the information if the calibration values have been fused or not. Instead of increasing TSC1 size to the value from the datasheet update all TSC's size to the smallest granularity of the address decoder circuitry. Signed-off-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a7796.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi index ef10fb548681..f8e9313f9405 100644 --- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi @@ -1625,9 +1625,9 @@ tsc: thermal@e6198000 { compatible = "renesas,r8a7796-thermal"; - reg = <0 0xe6198000 0 0x68>, - <0 0xe61a0000 0 0x5c>, - <0 0xe61a8000 0 0x5c>; + reg = <0 0xe6198000 0 0x100>, + <0 0xe61a0000 0 0x100>, + <0 0xe61a8000 0 0x100>; interrupts = , , ; -- cgit v1.3-14-g43fede From ffcd060fd51682d2695da7334aa607a807e3838e Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Mon, 29 Jan 2018 16:45:44 +0100 Subject: arm64: dts: renesas: r8a77995: add I2C support Defines R-Car D3 I2C controllers 0-3. Signed-off-by: Ulrich Hecht Reviewed-by: Geert Uytterhoeven Acked-by: Wolfram Sang Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995.dtsi | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi index 23f763beab46..22e633c87d4c 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi @@ -536,6 +536,73 @@ status = "disabled"; }; + i2c0: i2c@e6500000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a77995", + "renesas,rcar-gen3-i2c"; + reg = <0 0xe6500000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 931>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 931>; + dmas = <&dmac1 0x91>, <&dmac1 0x90>, + <&dmac2 0x91>, <&dmac2 0x90>; + dma-names = "tx", "rx", "tx", "rx"; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; + + i2c1: i2c@e6508000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a77995", + "renesas,rcar-gen3-i2c"; + reg = <0 0xe6508000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 930>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 930>; + dmas = <&dmac1 0x93>, <&dmac1 0x92>, + <&dmac2 0x93>, <&dmac2 0x92>; + dma-names = "tx", "rx", "tx", "rx"; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; + + i2c2: i2c@e6510000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a77995", + "renesas,rcar-gen3-i2c"; + reg = <0 0xe6510000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 929>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 929>; + dmas = <&dmac1 0x95>, <&dmac1 0x94>, + <&dmac2 0x95>, <&dmac2 0x94>; + dma-names = "tx", "rx", "tx", "rx"; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; + + i2c3: i2c@e66d0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "renesas,i2c-r8a77995", + "renesas,rcar-gen3-i2c"; + reg = <0 0xe66d0000 0 0x40>; + interrupts = ; + clocks = <&cpg CPG_MOD 928>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 928>; + dmas = <&dmac0 0x97>, <&dmac0 0x96>; + dma-names = "tx", "rx"; + i2c-scl-internal-delay-ns = <6>; + status = "disabled"; + }; + pwm0: pwm@e6e30000 { compatible = "renesas,pwm-r8a77995", "renesas,pwm-rcar"; reg = <0 0xe6e30000 0 0x8>; -- cgit v1.3-14-g43fede From 86e7a972adec5093e24e888ae3a949ee185dc533 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Mon, 29 Jan 2018 16:45:45 +0100 Subject: arm64: dts: renesas: draak: enable I2C controller 0 and EEPROM Enables EEPROM on I2C0 on the Draak board. Signed-off-by: Ulrich Hecht Reviewed-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995-draak.dts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts index 09de73b11db8..6ff9d3eb7540 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts +++ b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts @@ -46,6 +46,11 @@ }; }; + i2c0_pins: i2c0 { + groups = "i2c0"; + function = "i2c0"; + }; + pwm0_pins: pwm0 { groups = "pwm0_c"; function = "pwm0"; @@ -67,6 +72,18 @@ }; }; +&i2c0 { + pinctrl-0 = <&i2c0_pins>; + pinctrl-names = "default"; + status = "okay"; + + eeprom@50 { + compatible = "rohm,br24t01", "atmel,24c01"; + reg = <0x50>; + pagesize = <8>; + }; +}; + &ehci0 { status = "okay"; }; -- cgit v1.3-14-g43fede From 41337aa1556a38a0b6861447ba7861c3a34bc204 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Mon, 29 Jan 2018 16:45:46 +0100 Subject: arm64: dts: renesas: draak: enable I2C controller 1 No devices to add, I2C1 has an external connector only. Signed-off-by: Ulrich Hecht Reviewed-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995-draak.dts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts index 6ff9d3eb7540..af07da240be0 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts +++ b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts @@ -51,6 +51,11 @@ function = "i2c0"; }; + i2c1_pins: i2c1 { + groups = "i2c1"; + function = "i2c1"; + }; + pwm0_pins: pwm0 { groups = "pwm0_c"; function = "pwm0"; @@ -84,6 +89,12 @@ }; }; +&i2c1 { + pinctrl-0 = <&i2c1_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + &ehci0 { status = "okay"; }; -- cgit v1.3-14-g43fede From b85e1f77d9e285e61f8aa5a3a7e2bc46431d07ee Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 31 Jan 2018 22:59:03 +0300 Subject: arm64: add Renesas R8A77980 support Add a configuration option for the R-Car V3H (R8A77980) SoC. Based on the original (and large) patch by Vladimir Barinov. Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/Kconfig.platforms | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index fbedbd8f619a..b51db26f6eaa 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -196,6 +196,12 @@ config ARCH_R8A77970 help This enables support for the Renesas R-Car V3M SoC. +config ARCH_R8A77980 + bool "Renesas R-Car V3H SoC Platform" + depends on ARCH_RENESAS + help + This enables support for the Renesas R-Car V3H SoC. + config ARCH_R8A77995 bool "Renesas R-Car D3 SoC Platform" depends on ARCH_RENESAS -- cgit v1.3-14-g43fede From f3a54d6c17f5ec826ff81e4f9f35a11e63211c53 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 2 Feb 2018 21:33:39 +0300 Subject: arm64: dts: renesas: initial R8A77980 SoC device tree The initial R8A77980 SoC device tree including Cortex-A53 CPU, GIC, timer, CPG, RST, and SYSC. Based on the original (and large) patch by Vladimir Barinov. Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77980.dtsi | 122 ++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 arch/arm64/boot/dts/renesas/r8a77980.dtsi diff --git a/arch/arm64/boot/dts/renesas/r8a77980.dtsi b/arch/arm64/boot/dts/renesas/r8a77980.dtsi new file mode 100644 index 000000000000..6a92bbf55013 --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r8a77980.dtsi @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Source for the r8a77980 SoC + * + * Copyright (C) 2018 Renesas Electronics Corp. + * Copyright (C) 2018 Cogent Embedded, Inc. + */ + +#include +#include +#include + +/ { + compatible = "renesas,r8a77980"; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + a53_0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53", "arm,armv8"; + reg = <0>; + clocks = <&cpg CPG_CORE 0>; + power-domains = <&sysc 5>; + next-level-cache = <&L2_CA53>; + enable-method = "psci"; + }; + + L2_CA53: cache-controller { + compatible = "cache"; + power-domains = <&sysc 21>; + cache-unified; + cache-level = <2>; + }; + }; + + extal_clk: extal { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board */ + clock-frequency = <0>; + }; + + extalr_clk: extalr { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board */ + clock-frequency = <0>; + }; + + psci { + compatible = "arm,psci-1.0", "arm,psci-0.2"; + method = "smc"; + }; + + soc { + compatible = "simple-bus"; + interrupt-parent = <&gic>; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cpg: clock-controller@e6150000 { + compatible = "renesas,r8a77980-cpg-mssr"; + reg = <0 0xe6150000 0 0x1000>; + clocks = <&extal_clk>, <&extalr_clk>; + clock-names = "extal", "extalr"; + #clock-cells = <2>; + #power-domain-cells = <0>; + #reset-cells = <1>; + }; + + rst: reset-controller@e6160000 { + compatible = "renesas,r8a77980-rst"; + reg = <0 0xe6160000 0 0x200>; + }; + + sysc: system-controller@e6180000 { + compatible = "renesas,r8a77980-sysc"; + reg = <0 0xe6180000 0 0x440>; + #power-domain-cells = <1>; + }; + + gic: interrupt-controller@f1010000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x0 0xf1010000 0 0x1000>, + <0x0 0xf1020000 0 0x20000>, + <0x0 0xf1040000 0 0x20000>, + <0x0 0xf1060000 0 0x20000>; + interrupts = ; + clocks = <&cpg CPG_MOD 408>; + clock-names = "clk"; + power-domains = <&sysc 32>; + resets = <&cpg 408>; + }; + + prr: chipid@fff00044 { + compatible = "renesas,prr"; + reg = <0 0xfff00044 0 4>; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts-extended = <&gic GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(1) | + IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(1) | + IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(1) | + IRQ_TYPE_LEVEL_LOW)>, + <&gic GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(1) | + IRQ_TYPE_LEVEL_LOW)>; + }; +}; -- cgit v1.3-14-g43fede From 00d3375f918d503326bc4e4550b023d1a71e8d29 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 2 Feb 2018 21:36:16 +0300 Subject: arm64: dts: renesas: r8a77980: add SYS-DMAC support Describe SYS-DMAC1/2 in the R8A77980 device tree. Based on the original (and large) patch by Vladimir Barinov. Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77980.dtsi | 68 +++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77980.dtsi b/arch/arm64/boot/dts/renesas/r8a77980.dtsi index 6a92bbf55013..e5c7cf391334 100644 --- a/arch/arm64/boot/dts/renesas/r8a77980.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77980.dtsi @@ -85,6 +85,74 @@ #power-domain-cells = <1>; }; + dmac1: dma-controller@e7300000 { + compatible = "renesas,dmac-r8a77980", + "renesas,rcar-dmac"; + reg = <0 0xe7300000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14", "ch15"; + clocks = <&cpg CPG_MOD 218>; + clock-names = "fck"; + power-domains = <&sysc 32>; + resets = <&cpg 218>; + #dma-cells = <1>; + dma-channels = <16>; + }; + + dmac2: dma-controller@e7310000 { + compatible = "renesas,dmac-r8a77980", + "renesas,rcar-dmac"; + reg = <0 0xe7310000 0 0x10000>; + interrupts = ; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14", "ch15"; + clocks = <&cpg CPG_MOD 217>; + clock-names = "fck"; + power-domains = <&sysc 32>; + resets = <&cpg 217>; + #dma-cells = <1>; + dma-channels = <16>; + }; + gic: interrupt-controller@f1010000 { compatible = "arm,gic-400"; #interrupt-cells = <3>; -- cgit v1.3-14-g43fede From d0d2ad1ff6e379a8ca6c8e814433e54637fb1516 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 6 Feb 2018 14:05:53 +0100 Subject: arm64: dts: renesas: r8a77970: Remove non-existing STBE region R-Car V3M does not have the Stream Buffer for EtherAVB-IF (STBE). Note that the RAVB driver does not use this region. Fixes: bea2ab136eaacec2 ("arm64: dts: renesas: r8a77970: add EtherAVB support") Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77970.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/renesas/r8a77970.dtsi b/arch/arm64/boot/dts/renesas/r8a77970.dtsi index 566a7f704830..fc397ccd06b6 100644 --- a/arch/arm64/boot/dts/renesas/r8a77970.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77970.dtsi @@ -388,7 +388,7 @@ avb: ethernet@e6800000 { compatible = "renesas,etheravb-r8a77970", "renesas,etheravb-rcar-gen3"; - reg = <0 0xe6800000 0 0x800>, <0 0xe6a00000 0 0x10000>; + reg = <0 0xe6800000 0 0x800>; interrupts = , , , -- cgit v1.3-14-g43fede From fa3d4c67dfe920515a3d840de983a2b3e77d3b94 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 6 Feb 2018 14:05:54 +0100 Subject: arm64: dts: renesas: r8a77995: Remove non-existing STBE region R-Car D3 does not have the Stream Buffer for EtherAVB-IF (STBE). Note that the RAVB driver does not use this region. Fixes: f9ba0c4cfe6169b7 ("arm64: dts: renesas: r8a77995: Add EthernetAVB device node") Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi index 22e633c87d4c..cd3c6a30fc47 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi @@ -476,7 +476,7 @@ avb: ethernet@e6800000 { compatible = "renesas,etheravb-r8a77995", "renesas,etheravb-rcar-gen3"; - reg = <0 0xe6800000 0 0x800>, <0 0xe6a00000 0 0x10000>; + reg = <0 0xe6800000 0 0x800>; interrupts = , , , -- cgit v1.3-14-g43fede From 35090321a297942a2817e1dc2eaf0c25009fb4b0 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 3 Jan 2018 16:45:45 +0100 Subject: ARM: dts: ls1021a: add nodes for on-chip ram Although the two nodes constitute one contiguous 128K region, still describe them separately: - That's how they are described in the reference manual: "Each OCRAM occupies a 64 KB of address region...", and the names ocram1 and ocram2 are also as used in the manual. - The two areas are treated differently by the boot ROM code: OCRAM2 is zero-initialized, while, again quoting the RM, "software must perform the zero initialization of OCRAM1." Signed-off-by: Rasmus Villemoes Signed-off-by: Shawn Guo --- arch/arm/boot/dts/ls1021a.dtsi | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index c31dad98f989..fbd2897566c3 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -788,5 +788,21 @@ clock-names = "ipg", "per"; big-endian; }; + + ocram1: sram@10000000 { + compatible = "mmio-sram"; + reg = <0x0 0x10000000 0x0 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x10000000 0x10000>; + }; + + ocram2: sram@10010000 { + compatible = "mmio-sram"; + reg = <0x0 0x10010000 0x0 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x10010000 0x10000>; + }; }; }; -- cgit v1.3-14-g43fede From 74aeda3557db99a2eb1af5cd1cdf6f5322a4a4f4 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 27 Dec 2017 12:04:34 -0200 Subject: ARM: dts: imx23: Pass unit address and reg to IOMUX node Pass unit address and reg to IOMUX node to fix the following build warning with W=1: arch/arm/boot/dts/imx23-evk.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/gpmi-pins-fixup missing or empty reg/ranges property arch/arm/boot/dts/imx23-evk.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/mmc0-pins-fixup missing or empty reg/ranges property Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx23.dtsi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/imx23.dtsi b/arch/arm/boot/dts/imx23.dtsi index 10d57f9cbb42..da935a4fe907 100644 --- a/arch/arm/boot/dts/imx23.dtsi +++ b/arch/arm/boot/dts/imx23.dtsi @@ -222,7 +222,8 @@ fsl,pull-up = ; }; - gpmi_pins_fixup: gpmi-pins-fixup { + gpmi_pins_fixup: gpmi-pins-fixup@0 { + reg = <0>; fsl,pinmux-ids = < MX23_PAD_GPMI_WPN__GPMI_WPN MX23_PAD_GPMI_WRN__GPMI_WRN @@ -266,7 +267,8 @@ fsl,pull-up = ; }; - mmc0_pins_fixup: mmc0-pins-fixup { + mmc0_pins_fixup: mmc0-pins-fixup@0 { + reg = <0>; fsl,pinmux-ids = < MX23_PAD_SSP1_DETECT__SSP1_DETECT MX23_PAD_SSP1_SCK__SSP1_SCK -- cgit v1.3-14-g43fede From 497b90db5e4643fc41dc5761e01198c50b7d9954 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 27 Dec 2017 12:04:35 -0200 Subject: ARM: dts: imx28: Pass unit address and reg to IOMUX node Pass unit address and reg to IOMUX node to fix the following build warning with W=1: arch/arm/boot/dts/imx28-apf28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/gpmi-status-cfg missing or empty reg/ranges property arch/arm/boot/dts/imx28-apf28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/mmc0-cd-cfg missing or empty reg/ranges property arch/arm/boot/dts/imx28-apf28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/mmc0-sck-cfg missing or empty reg/ranges property arch/arm/boot/dts/imx28-apf28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/mmc1-cd-cfg missing or empty reg/ranges property arch/arm/boot/dts/imx28-apf28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/mmc1-sck-cfg missing or empty reg/ranges property arch/arm/boot/dts/imx28-apf28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/mmc2-cd-cfg missing or empty reg/ranges property Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx28.dtsi | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi index e52e05c0fe56..ee3396d21c01 100644 --- a/arch/arm/boot/dts/imx28.dtsi +++ b/arch/arm/boot/dts/imx28.dtsi @@ -283,7 +283,8 @@ fsl,pull-up = ; }; - gpmi_status_cfg: gpmi-status-cfg { + gpmi_status_cfg: gpmi-status-cfg@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_GPMI_RDN__GPMI_RDN MX28_PAD_GPMI_WRN__GPMI_WRN @@ -527,14 +528,16 @@ fsl,pull-up = ; }; - mmc0_cd_cfg: mmc0-cd-cfg { + mmc0_cd_cfg: mmc0-cd-cfg@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_SSP0_DETECT__SSP0_CARD_DETECT >; fsl,pull-up = ; }; - mmc0_sck_cfg: mmc0-sck-cfg { + mmc0_sck_cfg: mmc0-sck-cfg@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_SSP0_SCK__SSP0_SCK >; @@ -558,14 +561,16 @@ fsl,pull-up = ; }; - mmc1_cd_cfg: mmc1-cd-cfg { + mmc1_cd_cfg: mmc1-cd-cfg@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_GPMI_RDY0__SSP1_CARD_DETECT >; fsl,pull-up = ; }; - mmc1_sck_cfg: mmc1-sck-cfg { + mmc1_sck_cfg: mmc1-sck-cfg@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_GPMI_WRN__SSP1_SCK >; @@ -606,7 +611,8 @@ fsl,pull-up = ; }; - mmc2_cd_cfg: mmc2-cd-cfg { + mmc2_cd_cfg: mmc2-cd-cfg@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_AUART1_RX__SSP2_CARD_DETECT >; -- cgit v1.3-14-g43fede From d039e25342cb7192320bf8849fe6be98e26ec995 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 27 Dec 2017 12:04:36 -0200 Subject: ARM: dts: imx28-tx28: Pass unit address and reg to IOMUX node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass unit address and reg to IOMUX node to fix the following build warning with W=1: arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/tx28-edt-ft5x06-pins missing or empty reg/ranges property arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/tx28-flexcan-xcvr-pins missing or empty reg/ranges property arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/tx28-lcdif-23bit missing or empty reg/ranges property arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/tx28-lcdif-ctrl missing or empty reg/ranges property arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/tx28-mac0-gpio-pins missing or empty reg/ranges property arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/tx28-pca9554-pins missing or empty reg/ranges property arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/spi-gpiogrp missing or empty reg/ranges property arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/tx28-tsc2007-pins missing or empty reg/ranges property arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/tx28-usbphy0-pins missing or empty reg/ranges property arch/arm/boot/dts/imx28-tx28.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/tx28-usbphy1-pins missing or empty reg/ranges property Signed-off-by: Fabio Estevam Acked-By: Lothar Waßmann Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx28-tx28.dts | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/arch/arm/boot/dts/imx28-tx28.dts b/arch/arm/boot/dts/imx28-tx28.dts index 152621ea37db..8a4f5bc19a57 100644 --- a/arch/arm/boot/dts/imx28-tx28.dts +++ b/arch/arm/boot/dts/imx28-tx28.dts @@ -531,7 +531,8 @@ fsl,pull-up = ; }; - tx28_edt_ft5x06_pins: tx28-edt-ft5x06-pins { + tx28_edt_ft5x06_pins: tx28-edt-ft5x06-pins@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_SSP0_DATA6__GPIO_2_6 /* RESET */ MX28_PAD_SSP0_DATA5__GPIO_2_5 /* IRQ */ @@ -542,7 +543,8 @@ fsl,pull-up = ; }; - tx28_flexcan_xcvr_pins: tx28-flexcan-xcvr-pins { + tx28_flexcan_xcvr_pins: tx28-flexcan-xcvr-pins@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_LCD_D00__GPIO_1_0 >; @@ -551,7 +553,8 @@ fsl,pull-up = ; }; - tx28_lcdif_23bit_pins: tx28-lcdif-23bit { + tx28_lcdif_23bit_pins: tx28-lcdif-23bit@0 { + reg = <0>; fsl,pinmux-ids = < /* LCD_D00 may be used as Flexcan Transceiver Enable on STK5-V5 */ MX28_PAD_LCD_D01__LCD_D1 @@ -583,7 +586,8 @@ fsl,pull-up = ; }; - tx28_lcdif_ctrl_pins: tx28-lcdif-ctrl { + tx28_lcdif_ctrl_pins: tx28-lcdif-ctrl@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_LCD_ENABLE__GPIO_1_31 /* Enable */ MX28_PAD_LCD_RESET__GPIO_3_30 /* Reset */ @@ -593,7 +597,8 @@ fsl,pull-up = ; }; - tx28_mac0_pins_gpio: tx28-mac0-gpio-pins { + tx28_mac0_pins_gpio: tx28-mac0-gpio-pins@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_ENET0_MDC__GPIO_4_0 MX28_PAD_ENET0_MDIO__GPIO_4_1 @@ -610,7 +615,8 @@ fsl,pull-up = ; }; - tx28_pca9554_pins: tx28-pca9554-pins { + tx28_pca9554_pins: tx28-pca9554-pins@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_PWM3__GPIO_3_28 >; @@ -619,7 +625,8 @@ fsl,pull-up = ; }; - tx28_spi_gpio_pins: spi-gpiogrp { + tx28_spi_gpio_pins: spi-gpiogrp@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_AUART2_RX__GPIO_3_8 MX28_PAD_AUART2_TX__GPIO_3_9 @@ -633,7 +640,8 @@ fsl,pull-up = ; }; - tx28_tsc2007_pins: tx28-tsc2007-pins { + tx28_tsc2007_pins: tx28-tsc2007-pins@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_SAIF0_MCLK__GPIO_3_20 /* TSC2007 IRQ */ >; @@ -643,7 +651,8 @@ }; - tx28_usbphy0_pins: tx28-usbphy0-pins { + tx28_usbphy0_pins: tx28-usbphy0-pins@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_GPMI_CE2N__GPIO_0_18 /* USBOTG_VBUSEN */ MX28_PAD_GPMI_CE3N__GPIO_0_19 /* USBOTH_OC */ @@ -653,7 +662,8 @@ fsl,pull-up = ; }; - tx28_usbphy1_pins: tx28-usbphy1-pins { + tx28_usbphy1_pins: tx28-usbphy1-pins@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_SPDIF__GPIO_3_27 /* USBH_VBUSEN */ MX28_PAD_JTAG_RTCK__GPIO_4_20 /* USBH_OC */ -- cgit v1.3-14-g43fede From 5022427870b9a9b682989d10a188c81b568a7c20 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 27 Dec 2017 12:04:37 -0200 Subject: ARM: dts: imx28-apx4devkit: Pass unit address and reg to IOMUX node Pass unit address and reg to IOMUX node to fix the following build warning with W=1: arch/arm/boot/dts/imx28-apx4devkit.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbh@80000000/pinctrl@80018000/mmc2-sck-cfg-apx4 missing or empty reg/ranges property Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx28-apx4devkit.dts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx28-apx4devkit.dts b/arch/arm/boot/dts/imx28-apx4devkit.dts index ae078341fb60..91456bc08d24 100644 --- a/arch/arm/boot/dts/imx28-apx4devkit.dts +++ b/arch/arm/boot/dts/imx28-apx4devkit.dts @@ -82,7 +82,8 @@ fsl,pull-up = ; }; - mmc2_sck_cfg_apx4: mmc2-sck-cfg-apx4 { + mmc2_sck_cfg_apx4: mmc2-sck-cfg-apx4@0 { + reg = <0>; fsl,pinmux-ids = < MX28_PAD_SSP0_DATA7__SSP2_SCK >; -- cgit v1.3-14-g43fede From 142ee3366940f186d8e5b7a09ee2932010c88565 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 27 Dec 2017 12:04:38 -0200 Subject: ARM: dts: imx28-cfa10049: Move i2cmux out of bus node Move i2cmux node from soc node to root node. i2cmux node does not have any register properties and thus shouldn't be placed on the bus. This fixes the following build warnings with W=1: arch/arm/boot/dts/imx28-cfa10049.dtb: Warning (simple_bus_reg): Node /apb@80000000/apbx@80040000/i2cmux missing or empty reg/ranges property Signed-off-by: Fabio Estevam Acked-by: Maxime Ripard Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx28-cfa10049.dts | 130 +++++++++++++++++------------------ 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/arch/arm/boot/dts/imx28-cfa10049.dts b/arch/arm/boot/dts/imx28-cfa10049.dts index 4cd52d53cf00..60e5c7fd5035 100644 --- a/arch/arm/boot/dts/imx28-cfa10049.dts +++ b/arch/arm/boot/dts/imx28-cfa10049.dts @@ -19,6 +19,71 @@ model = "Crystalfontz CFA-10049 Board"; compatible = "crystalfontz,cfa10049", "crystalfontz,cfa10036", "fsl,imx28"; + i2cmux { + compatible = "i2c-mux-gpio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&i2cmux_pins_cfa10049>; + mux-gpios = <&gpio1 22 0 &gpio1 23 0>; + i2c-parent = <&i2c1>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + adc0: nau7802@2a { + compatible = "nuvoton,nau7802"; + reg = <0x2a>; + nuvoton,vldo = <3000>; + }; + }; + + i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + adc1: nau7802@2a { + compatible = "nuvoton,nau7802"; + reg = <0x2a>; + nuvoton,vldo = <3000>; + }; + }; + + i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + + adc2: nau7802@2a { + compatible = "nuvoton,nau7802"; + reg = <0x2a>; + nuvoton,vldo = <3000>; + }; + }; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + pca9555: pca9555@20 { + compatible = "nxp,pca9555"; + pinctrl-names = "default"; + pinctrl-0 = <&pca_pins_cfa10049>; + interrupt-parent = <&gpio2>; + interrupts = <19 0x2>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x20>; + }; + }; + }; + apb@80000000 { apbh@80000000 { pinctrl@80018000 { @@ -219,71 +284,6 @@ status = "okay"; }; - i2cmux { - compatible = "i2c-mux-gpio"; - #address-cells = <1>; - #size-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&i2cmux_pins_cfa10049>; - mux-gpios = <&gpio1 22 0 &gpio1 23 0>; - i2c-parent = <&i2c1>; - - i2c@0 { - #address-cells = <1>; - #size-cells = <0>; - reg = <0>; - - adc0: nau7802@2a { - compatible = "nuvoton,nau7802"; - reg = <0x2a>; - nuvoton,vldo = <3000>; - }; - }; - - i2c@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - - adc1: nau7802@2a { - compatible = "nuvoton,nau7802"; - reg = <0x2a>; - nuvoton,vldo = <3000>; - }; - }; - - i2c@2 { - #address-cells = <1>; - #size-cells = <0>; - reg = <2>; - - adc2: nau7802@2a { - compatible = "nuvoton,nau7802"; - reg = <0x2a>; - nuvoton,vldo = <3000>; - }; - }; - - i2c@3 { - reg = <3>; - #address-cells = <1>; - #size-cells = <0>; - - pca9555: pca9555@20 { - compatible = "nxp,pca9555"; - pinctrl-names = "default"; - pinctrl-0 = <&pca_pins_cfa10049>; - interrupt-parent = <&gpio2>; - interrupts = <19 0x2>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x20>; - }; - }; - }; - usbphy1: usbphy@8007e000 { status = "okay"; }; -- cgit v1.3-14-g43fede From 081e0653ec05a362111017697517f3eea616c1d7 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 27 Dec 2017 14:23:23 -0200 Subject: ARM: dts: imx27-eukrea-mbimxsd27-baseboard: Pass missing unit address ads7846 is connected to SPI chip select 0, so pass the unit address accordingly to fix the following build warning with W=1: arch/arm/boot/dts/imx27-eukrea-mbimxsd27-baseboard.dtb: Warning (unit_address_vs_reg): Node /soc/aipi@10000000/cspi@1000e000/ads7846 has a reg or ranges property, but no unit name Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx27-eukrea-mbimxsd27-baseboard.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx27-eukrea-mbimxsd27-baseboard.dts b/arch/arm/boot/dts/imx27-eukrea-mbimxsd27-baseboard.dts index f56535768ee8..15145e7f9778 100644 --- a/arch/arm/boot/dts/imx27-eukrea-mbimxsd27-baseboard.dts +++ b/arch/arm/boot/dts/imx27-eukrea-mbimxsd27-baseboard.dts @@ -84,7 +84,7 @@ cs-gpios = <&gpio4 28 GPIO_ACTIVE_LOW>; status = "okay"; - ads7846 { + ads7846@0 { compatible = "ti,ads7846"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_touch>; -- cgit v1.3-14-g43fede From 4a7459bc5a3dd1f25f7a9d50083824f8dad8ebd7 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 3 Jan 2018 19:22:14 +0800 Subject: ARM: dts: imx6ul: remove unnecessary clocks for cpu-freq Remove unnecessary clocks for cpu-freq driver to avoid confusion. Signed-off-by: Anson Huang Reviewed-by: Fabio Estevam Acked-by: Dong Aisheng Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ul.dtsi | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi index 5d6c3ba36cd1..d88fc9a2248f 100644 --- a/arch/arm/boot/dts/imx6ul.dtsi +++ b/arch/arm/boot/dts/imx6ul.dtsi @@ -86,15 +86,10 @@ <&clks IMX6UL_CA7_SECONDARY_SEL>, <&clks IMX6UL_CLK_STEP>, <&clks IMX6UL_CLK_PLL1_SW>, - <&clks IMX6UL_CLK_PLL1_SYS>, - <&clks IMX6UL_PLL1_BYPASS>, - <&clks IMX6UL_CLK_PLL1>, - <&clks IMX6UL_PLL1_BYPASS_SRC>, - <&clks IMX6UL_CLK_OSC>; + <&clks IMX6UL_CLK_PLL1_SYS>; clock-names = "arm", "pll2_bus", "pll2_pfd2_396m", "secondary_sel", "step", "pll1_sw", - "pll1_sys", "pll1_bypass", "pll1", - "pll1_bypass_src", "osc"; + "pll1_sys"; arm-supply = <®_arm>; soc-supply = <®_soc>; }; -- cgit v1.3-14-g43fede From 7055f714032c03a7747fae9526802aab9ace1110 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 3 Jan 2018 11:32:37 -0800 Subject: ARM: dts: imx6: RDU2: disable internal watchdog The system has an external watchdog in the environment processor so the internal watchdog is of no use. Cc: Sascha Hauer Cc: Rob Herring Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org Cc: devicetree@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: cphealy@gmail.com Reviewed-by: Fabio Estevam Signed-off-by: Lucas Stach Signed-off-by: Andrey Smirnov Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi b/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi index 72f52fcecee1..928f35800da5 100644 --- a/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi +++ b/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi @@ -805,6 +805,10 @@ }; }; +&wdog1 { + status = "disabled"; +}; + &iomuxc { pinctrl_accel: accelgrp { fsl,pins = < -- cgit v1.3-14-g43fede From 256909a47871a5ff2456ce3d2ec30a75e1236090 Mon Sep 17 00:00:00 2001 From: Marco Franchi Date: Tue, 9 Jan 2018 09:46:23 -0200 Subject: ARM: dts: imx6ul-evk: Add support for mag3110 sensor The i.MX 6UL EVK has a MAG3110 Magnetometer sensor in its base board. Add support for this sensor, which is included in the trivial i2c devices and according to the bindings documentation, just need a compatible field and an address. Signed-off-by: Marco Franchi Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ul-14x14-evk.dts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dts b/arch/arm/boot/dts/imx6ul-14x14-evk.dts index 18fdb088ba1e..cb33baa28282 100644 --- a/arch/arm/boot/dts/imx6ul-14x14-evk.dts +++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dts @@ -140,6 +140,17 @@ }; }; +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + mag3110@e { + compatible = "fsl,mag3110"; + reg = <0x0e>; + }; +}; &lcdif { assigned-clocks = <&clks IMX6UL_CLK_LCDIF_PRE_SEL>; -- cgit v1.3-14-g43fede From dc5c632596f47bc528f38ea84621d3908637a71f Mon Sep 17 00:00:00 2001 From: Marco Franchi Date: Tue, 9 Jan 2018 09:46:25 -0200 Subject: ARM: dts: imx7d-sdb: Add support for mpl3115 sensor The i.MX 7D SDB has a MPL3115 Pressure sensor. Add support for this sensor, which is included in the trivial i2c devices and according to the bindings documentation, just need a compatible field and an address. Signed-off-by: Marco Franchi Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx7d-sdb.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts index a7a5dc7b2700..a20c942c46b8 100644 --- a/arch/arm/boot/dts/imx7d-sdb.dts +++ b/arch/arm/boot/dts/imx7d-sdb.dts @@ -336,6 +336,11 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c2>; status = "okay"; + + mpl3115@60 { + compatible = "fsl,mpl3115"; + reg = <0x60>; + }; }; &i2c3 { -- cgit v1.3-14-g43fede From ddec5d1c00475630457c59c81392846d27d10455 Mon Sep 17 00:00:00 2001 From: Stefan Riedmueller Date: Tue, 9 Jan 2018 17:09:55 +0100 Subject: ARM: dts: imx6: Add initial support for phyCORE-i.MX 6 SOM This patch adds basic support for PHYTEC phyCORE-i.MX 6 SOM with i.MX 6Quad/Dual or i.MX 6DualLight/Solo. Signed-off-by: Christian Hemp Signed-off-by: Stefan Christ Signed-off-by: Christoph Fritz Signed-off-by: Stefan Riedmueller Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6qdl-phytec-phycore-som.dtsi | 279 ++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 arch/arm/boot/dts/imx6qdl-phytec-phycore-som.dtsi diff --git a/arch/arm/boot/dts/imx6qdl-phytec-phycore-som.dtsi b/arch/arm/boot/dts/imx6qdl-phytec-phycore-som.dtsi new file mode 100644 index 000000000000..6486df3e2942 --- /dev/null +++ b/arch/arm/boot/dts/imx6qdl-phytec-phycore-som.dtsi @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2018 PHYTEC Messtechnik GmbH + * Author: Christian Hemp + */ + +#include + +/ { + aliases { + rtc1 = &da9062_rtc; + rtc2 = &snvs_rtc; + }; + + /* + * Set the minimum memory size here and + * let the bootloader set the real size. + */ + memory@10000000 { + device_type = "memory"; + reg = <0x10000000 0x8000000>; + }; + + gpio_leds_som: somleds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpioleds_som>; + + som-led-green { + label = "phycore:green"; + gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; +}; + +&ecspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1>; + cs-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>; + status = "okay"; + + m25p80: flash@0 { + compatible = "jedec,spi-nor"; + spi-max-frequency = <20000000>; + reg = <0>; + status = "disabled"; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-handle = <ðphy>; + phy-mode = "rgmii"; + phy-supply = <&vdd_eth_io>; + phy-reset-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; + status = "disabled"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethphy: ethernet-phy@3 { + reg = <3>; + txc-skew-ps = <1680>; + rxc-skew-ps = <1860>; + }; + }; +}; + +&gpmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; + status = "disabled"; +}; + +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + clock-frequency = <400000>; + status = "okay"; + + eeprom@50 { + compatible = "atmel,24c32"; + reg = <0x50>; + }; + + pmic@58 { + compatible = "dlg,da9062"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pmic>; + reg = <0x58>; + interrupt-parent = <&gpio1>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + + da9062_rtc: rtc { + compatible = "dlg,da9062-rtc"; + }; + + watchdog { + compatible = "dlg,da9062-watchdog"; + }; + + regulators { + vdd_arm: buck1 { + regulator-name = "vdd_arm"; + regulator-min-microvolt = <730000>; + regulator-max-microvolt = <1380000>; + regulator-always-on; + }; + + vdd_soc: buck2 { + regulator-name = "vdd_soc"; + regulator-min-microvolt = <730000>; + regulator-max-microvolt = <1380000>; + regulator-always-on; + }; + + vdd_ddr3_1p5: buck3 { + regulator-name = "vdd_ddr3"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + }; + + vdd_eth_1p2: buck4 { + regulator-name = "vdd_eth"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + vdd_snvs: ldo1 { + regulator-name = "vdd_snvs"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + }; + + vdd_high: ldo2 { + regulator-name = "vdd_high"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + }; + + vdd_eth_io: ldo3 { + regulator-name = "vdd_eth_io"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + }; + + vdd_emmc_1p8: ldo4 { + regulator-name = "vdd_emmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + }; + }; +}; + +®_arm { + vin-supply = <&vdd_arm>; +}; + +®_pu { + vin-supply = <&vdd_soc>; +}; + +®_soc { + vin-supply = <&vdd_soc>; +}; + +&snvs_poweroff { + status = "okay"; +}; + +&usdhc4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc4>; + bus-width = <8>; + non-removable; + vmmc-supply = <&vdd_emmc_1p8>; + status = "disabled"; +}; + +&iomuxc { + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0 + MX6QDL_PAD_SD2_DAT1__GPIO1_IO14 0x1b0b0 + >; + }; + + pinctrl_gpioleds_som: gpioledssomgrp { + fsl,pins = < + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1b0b0 + >; + }; + + pinctrl_gpmi_nand: gpminandgrp { + fsl,pins = < + MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 + MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 + MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 + MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 + MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 + MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 + MX6QDL_PAD_NANDF_CS2__NAND_CE2_B 0xb0b1 + MX6QDL_PAD_NANDF_CS3__NAND_CE3_B 0xb0b1 + MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 + MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 + MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 + MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 + MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 + MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 + MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 + MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 + MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 + MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 + MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 + >; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1 + >; + }; + + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 + MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 + MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x1b0b0 + >; + }; + + pinctrl_pmic: pmicgrp { + fsl,pins = < + MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x1b0b0 + >; + }; + + pinctrl_usdhc4: usdhc4grp { + fsl,pins = < + MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 + MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 + MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 + MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 + MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 + MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 + MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 + MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 + MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 + MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 + >; + }; +}; -- cgit v1.3-14-g43fede From 800d595151bbd8b42667c69d62af793290454817 Mon Sep 17 00:00:00 2001 From: Stefan Riedmueller Date: Tue, 9 Jan 2018 17:09:56 +0100 Subject: ARM: dts: imx6: Add initial support for phyBOARD-Mira This patch adds basic support for PHYTEC phyBOARD-Mira as carrier board for PHYTEC phyCORE-i.MX 6. Signed-off-by: Christian Hemp Signed-off-by: Stefan Christ Signed-off-by: Christoph Fritz Signed-off-by: Stefan Riedmueller Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6qdl-phytec-mira.dtsi | 390 +++++++++++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 arch/arm/boot/dts/imx6qdl-phytec-mira.dtsi diff --git a/arch/arm/boot/dts/imx6qdl-phytec-mira.dtsi b/arch/arm/boot/dts/imx6qdl-phytec-mira.dtsi new file mode 100644 index 000000000000..9ebd438dce7d --- /dev/null +++ b/arch/arm/boot/dts/imx6qdl-phytec-mira.dtsi @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2018 PHYTEC Messtechnik GmbH + * Author: Christian Hemp + */ + + +/ { + aliases { + rtc0 = &i2c_rtc; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + power-supply = <®_backlight>; + pwms = <&pwm1 0 5000000>; + status = "okay"; + }; + + gpio_leds: leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpioleds>; + status = "disabled"; + + red { + label = "phyboard-mira:red"; + gpios = <&gpio5 22 GPIO_ACTIVE_HIGH>; + }; + + green { + label = "phyboard-mira:green"; + gpios = <&gpio5 23 GPIO_ACTIVE_HIGH>; + }; + + blue { + label = "phyboard-mira:blue"; + gpios = <&gpio5 24 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + }; + }; + + reg_backlight: regulator-backlight { + compatible = "regulator-fixed"; + regulator-name = "backlight_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + reg_en_switch: regulator-en-switch { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_en_switch>; + regulator-name = "Enable Switch"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + gpio = <&gpio3 4 GPIO_ACTIVE_HIGH>; + regulator-always-on; + }; + + reg_flexcan1: regulator-flexcan1 { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1_en>; + regulator-name = "flexcan1-reg"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + gpio = <&gpio2 20 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_panel: regulator-panel { + compatible = "regulator-fixed"; + regulator-name = "panel-power-supply"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-always-on; + }; + + reg_pcie: regulator-pcie { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie_reg>; + regulator-name = "mPCIe_1V5"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + gpio = <&gpio3 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usb_h1_vbus: usb-h1-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbh1_vbus>; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio2 18 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usbotg_vbus: usbotg-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg_vbus>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio2 19 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + panel { + compatible = "auo,g104sn02"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_panel_en>; + power-supply = <®_panel>; + enable-gpios = <&gpio2 28 GPIO_ACTIVE_LOW>; + backlight = <&backlight>; + + port { + panel_in: endpoint { + remote-endpoint = <&lvds0_out>; + }; + }; + }; +}; + +&can1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1>; + xceiver-supply = <®_flexcan1>; + status = "disabled"; +}; + +&hdmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmicec>; + ddc-i2c-bus = <&i2c2>; + status = "disabled"; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + clock-frequency = <400000>; + status = "disabled"; + + stmpe: touchctrl@44 { + compatible = "st,stmpe811"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_stmpe>; + reg = <0x44>; + interrupt-parent = <&gpio7>; + interrupts = <12 IRQ_TYPE_NONE>; + status = "disabled"; + + stmpe_touchscreen { + compatible = "st,stmpe-ts"; + st,sample-time = <4>; + st,mod-12b = <1>; + st,ref-sel = <0>; + st,adc-freq = <1>; + st,ave-ctrl = <1>; + st,touch-det-delay = <2>; + st,settling = <2>; + st,fraction-z = <7>; + st,i-drive = <1>; + }; + }; + + i2c_rtc: rtc@68 { + compatible = "microcrystal,rv4162"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rtc_int>; + reg = <0x68>; + interrupt-parent = <&gpio7>; + interrupts = <8 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + clock-frequency = <100000>; + status = "disabled"; +}; + +&ldb { + status = "okay"; + + lvds-channel@0 { + fsl,data-mapping = "spwg"; + fsl,data-width = <24>; + status = "disabled"; + + port@4 { + reg = <4>; + + lvds0_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie>; + reset-gpio = <&gpio2 25 GPIO_ACTIVE_LOW>; + vpcie-supply = <®_pcie>; + status = "disabled"; +}; + +&pwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm1>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3>; + uart-has-rtscts; + status = "disabled"; +}; + +&usbh1 { + vbus-supply = <®_usb_h1_vbus>; + disable-over-current; + status = "disabled"; +}; + +&usbotg { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg>; + vbus-supply = <®_usbotg_vbus>; + disable-over-current; + status = "disabled"; +}; + +&usdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc1>; + cd-gpios = <&gpio6 31 GPIO_ACTIVE_LOW>; + no-1-8-v; + status = "disabled"; +}; + +&iomuxc { + pinctrl_panel_en: panelen1grp { + fsl,pins = < + MX6QDL_PAD_EIM_EB0__GPIO2_IO28 0xb0b1 + >; + }; + + pinctrl_en_switch: enswitchgrp { + fsl,pins = < + MX6QDL_PAD_EIM_DA4__GPIO3_IO04 0xb0b1 + >; + }; + + pinctrl_flexcan1: flexcan1grp { + fsl,pins = < + MX6QDL_PAD_GPIO_7__FLEXCAN1_TX 0x1b0b0 + MX6QDL_PAD_GPIO_8__FLEXCAN1_RX 0x1b0b0 + >; + }; + + pinctrl_flexcan1_en: flexcan1engrp { + fsl,pins = < + MX6QDL_PAD_EIM_A18__GPIO2_IO20 0xb0b1 + >; + }; + + pinctrl_gpioleds: gpioledsgrp { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT4__GPIO5_IO22 0x1b0b0 + MX6QDL_PAD_CSI0_DAT5__GPIO5_IO23 0x1b0b0 + MX6QDL_PAD_CSI0_DAT6__GPIO5_IO24 0x1b0b0 + >; + }; + + pinctrl_hdmicec: hdmicecgrp { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 + >; + }; + + pinctrl_pcie: pciegrp { + fsl,pins = < + MX6QDL_PAD_EIM_OE__GPIO2_IO25 0xb0b1 + >; + }; + + pinctrl_pcie_reg: pciereggrp { + fsl,pins = < + MX6QDL_PAD_EIM_DA0__GPIO3_IO00 0xb0b1 + >; + }; + + pinctrl_pwm1: pwm1grp { + fsl,pins = < + MX6QDL_PAD_GPIO_9__PWM1_OUT 0x1b0b1 + >; + }; + + pinctrl_rtc_int: rtcintgrp { + fsl,pins = < + MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x1b0b0 + >; + }; + + pinctrl_stmpe: stmpegrp { + fsl,pins = < + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0 + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 + >; + }; + + pinctrl_uart3: uart3grp { + fsl,pins = < + MX6QDL_PAD_EIM_EB3__UART3_CTS_B 0x1b0b1 + MX6QDL_PAD_EIM_D23__UART3_RTS_B 0x1b0b1 + MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1 + >; + }; + + pinctrl_usbh1_vbus: usbh1vbusgrp { + fsl,pins = < + MX6QDL_PAD_EIM_A20__GPIO2_IO18 0xb0b1 + >; + }; + + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 + >; + }; + + pinctrl_usbotg_vbus: usbotgvbusgrp { + fsl,pins = < + MX6QDL_PAD_EIM_A19__GPIO2_IO19 0xb0b1 + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6QDL_PAD_SD1_CMD__SD1_CMD 0x170f9 + MX6QDL_PAD_SD1_CLK__SD1_CLK 0x100f9 + MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x170f9 + MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x170f9 + MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x170f9 + MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x170f9 + MX6QDL_PAD_EIM_BCLK__GPIO6_IO31 0xb0b1 /* CD */ + >; + }; +}; -- cgit v1.3-14-g43fede From f5b65bd5c45b94282a5d9929bebcb09f6e3b812a Mon Sep 17 00:00:00 2001 From: Christian Hemp Date: Tue, 9 Jan 2018 17:09:57 +0100 Subject: ARM: dts: imx6: Add support for phyBOARD-Mira i.MX 6Quad/Dual RDK Add support for the PHYTEC phyBOARD-Mira Rapid Development Kit with i.MX 6Quad/Dual with eMMC or NAND. Following interfaces are supported: - Gigabit Ethernet - USB Host - CAN - RS232 - PCIe - LVDS - HDMI Signed-off-by: Christian Hemp Signed-off-by: Stefan Christ Signed-off-by: Stefan Riedmueller Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/Makefile | 2 + arch/arm/boot/dts/imx6q-phytec-mira-rdk-emmc.dts | 72 ++++++++++++++++++++++++ arch/arm/boot/dts/imx6q-phytec-mira-rdk-nand.dts | 72 ++++++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 arch/arm/boot/dts/imx6q-phytec-mira-rdk-emmc.dts create mode 100644 arch/arm/boot/dts/imx6q-phytec-mira-rdk-nand.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index ade7a38543dc..6182c52b7d19 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -465,6 +465,8 @@ dtb-$(CONFIG_SOC_IMX6Q) += \ imx6q-nitrogen6_max.dtb \ imx6q-nitrogen6_som2.dtb \ imx6q-novena.dtb \ + imx6q-phytec-mira-rdk-emmc.dtb \ + imx6q-phytec-mira-rdk-nand.dtb \ imx6q-phytec-pbab01.dtb \ imx6q-pistachio.dtb \ imx6q-rex-pro.dtb \ diff --git a/arch/arm/boot/dts/imx6q-phytec-mira-rdk-emmc.dts b/arch/arm/boot/dts/imx6q-phytec-mira-rdk-emmc.dts new file mode 100644 index 000000000000..8fdce3c8e5fa --- /dev/null +++ b/arch/arm/boot/dts/imx6q-phytec-mira-rdk-emmc.dts @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2018 PHYTEC Messtechnik GmbH + * Author: Christian Hemp + */ + +/dts-v1/; +#include "imx6q.dtsi" +#include "imx6qdl-phytec-phycore-som.dtsi" +#include "imx6qdl-phytec-mira.dtsi" + +/ { + model = "PHYTEC phyBOARD-Mira Quad Carrier-Board with eMMC"; + compatible = "phytec,imx6q-pbac06-emmc", "phytec,imx6q-pbac06", + "phytec,imx6qdl-pcm058", "fsl,imx6q"; + + chosen { + linux,stdout-path = &uart2; + }; +}; + +&can1 { + status = "okay"; +}; + +&fec { + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&i2c_rtc { + status = "okay"; +}; + +&m25p80 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&usbh1 { + status = "okay"; +}; + +&usbotg { + status = "okay"; +}; + +&usdhc1 { + status = "okay"; +}; + +&usdhc4 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6q-phytec-mira-rdk-nand.dts b/arch/arm/boot/dts/imx6q-phytec-mira-rdk-nand.dts new file mode 100644 index 000000000000..8afa5ceb7d7c --- /dev/null +++ b/arch/arm/boot/dts/imx6q-phytec-mira-rdk-nand.dts @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2018 PHYTEC Messtechnik GmbH + * Author: Christian Hemp + */ + +/dts-v1/; +#include "imx6q.dtsi" +#include "imx6qdl-phytec-phycore-som.dtsi" +#include "imx6qdl-phytec-mira.dtsi" + +/ { + model = "PHYTEC phyBOARD-Mira Quad Carrier-Board with NAND"; + compatible = "phytec,imx6q-pbac06-nand", "phytec,imx6q-pbac06", + "phytec,imx6qdl-pcm058", "fsl,imx6q"; + + chosen { + linux,stdout-path = &uart2; + }; +}; + +&can1 { + status = "okay"; +}; + +&fec { + status = "okay"; +}; + +&gpmi { + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&i2c_rtc { + status = "okay"; +}; + +&m25p80 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&usbh1 { + status = "okay"; +}; + +&usbotg { + status = "okay"; +}; + +&usdhc1 { + status = "okay"; +}; -- cgit v1.3-14-g43fede From 685991a27db24c83eca51b2038c13d768191cb5a Mon Sep 17 00:00:00 2001 From: Christian Hemp Date: Tue, 9 Jan 2018 17:09:58 +0100 Subject: ARM: dts: imx6: Add support for phyBOARD-Mira i.MX 6 DualLight/Solo RDK Add support for the PHYTEC phyBOARD-Mira Low-Cost Rapid Development Kit with i.MX 6DualLight/Solo with NAND. Following interfaces are supported: - 100 MBit Ethernet - USB Host - RS232 - HDMI Signed-off-by: Christian Hemp Signed-off-by: Stefan Christ Signed-off-by: Stefan Riedmueller Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/imx6dl-phytec-mira-rdk-nand.dts | 64 +++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 arch/arm/boot/dts/imx6dl-phytec-mira-rdk-nand.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 6182c52b7d19..b21ec06a2357 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -396,6 +396,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \ imx6dl-icore-rqs.dtb \ imx6dl-nit6xlite.dtb \ imx6dl-nitrogen6x.dtb \ + imx6dl-phytec-mira-rdk-nand.dtb \ imx6dl-phytec-pbab01.dtb \ imx6dl-rex-basic.dtb \ imx6dl-riotboard.dtb \ diff --git a/arch/arm/boot/dts/imx6dl-phytec-mira-rdk-nand.dts b/arch/arm/boot/dts/imx6dl-phytec-mira-rdk-nand.dts new file mode 100644 index 000000000000..a8adcb2ec3fd --- /dev/null +++ b/arch/arm/boot/dts/imx6dl-phytec-mira-rdk-nand.dts @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2018 PHYTEC Messtechnik GmbH + * Author: Christian Hemp + */ + +/dts-v1/; +#include "imx6dl.dtsi" +#include "imx6qdl-phytec-phycore-som.dtsi" +#include "imx6qdl-phytec-mira.dtsi" + +/ { + model = "PHYTEC phyBOARD-Mira DualLite/Solo Carrier-Board with NAND"; + compatible = "phytec,imx6dl-pbac06-nand", "phytec,imx6dl-pbac06", + "phytec,imx6qdl-pcm058", "fsl,imx6dl"; + + chosen { + linux,stdout-path = &uart2; + }; +}; + +ðphy { + max-speed = <100>; +}; + +&fec { + status = "okay"; +}; + +&gpmi { + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&i2c_rtc { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&usbh1 { + status = "okay"; +}; + +&usbotg { + status = "okay"; +}; + +&usdhc1 { + status = "okay"; +}; -- cgit v1.3-14-g43fede From e56d8d729df7abb47449fedbafa657bf77c87f47 Mon Sep 17 00:00:00 2001 From: Enrico Scholz Date: Tue, 9 Jan 2018 17:09:59 +0100 Subject: ARM: dts: imx6: Add support for phyBOARD-Mira with i.MX 6QuadPlus Add support for the PHYTEC phyBOARD-Mira with i.MX 6QuadPlus with NAND. It is based on the phyBOARD-Mira with i.MX 6Quad/Dual and supports the same interfaces. Signed-off-by: Enrico Scholz Signed-off-by: Stefan Lengfeld Signed-off-by: Christian Hemp Signed-off-by: Stefan Riedmueller Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/imx6qp-phytec-mira-rdk-nand.dts | 72 +++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 arch/arm/boot/dts/imx6qp-phytec-mira-rdk-nand.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index b21ec06a2357..e1c8954e9849 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -497,6 +497,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \ imx6q-zii-rdu2.dtb \ imx6qp-nitrogen6_max.dtb \ imx6qp-nitrogen6_som2.dtb \ + imx6qp-phytec-mira-rdk-nand.dtb \ imx6qp-sabreauto.dtb \ imx6qp-sabresd.dtb \ imx6qp-tx6qp-8037.dtb \ diff --git a/arch/arm/boot/dts/imx6qp-phytec-mira-rdk-nand.dts b/arch/arm/boot/dts/imx6qp-phytec-mira-rdk-nand.dts new file mode 100644 index 000000000000..3618e5316bf4 --- /dev/null +++ b/arch/arm/boot/dts/imx6qp-phytec-mira-rdk-nand.dts @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2018 PHYTEC Messtechnik GmbH + * Author: Enrico Scholz + */ + +/dts-v1/; +#include "imx6qp.dtsi" +#include "imx6qdl-phytec-phycore-som.dtsi" +#include "imx6qdl-phytec-mira.dtsi" + +/ { + model = "PHYTEC phyBOARD-Mira QuadPlus Carrier-Board with NAND"; + compatible = "phytec,imx6qp-pbac06-nand", "phytec,imx6qp-pbac06", + "phytec,imx6qdl-pcm058", "fsl,imx6qp"; + + chosen { + linux,stdout-path = &uart2; + }; +}; + +&can1 { + status = "okay"; +}; + +&fec { + status = "okay"; +}; + +&gpmi { + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&i2c_rtc { + status = "okay"; +}; + +&m25p80 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&usbh1 { + status = "okay"; +}; + +&usbotg { + status = "okay"; +}; + +&usdhc1 { + status = "okay"; +}; -- cgit v1.3-14-g43fede From 64b8cd3cfa6b1bd8831bb652712e48be3031a181 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Wed, 10 Jan 2018 22:04:48 +0100 Subject: ARM: dts: imx6ul: update i.MX 6UltraLite iomux headers Add previously missing daisy chain configurations and several additional pinmux options. Synchronized with NXP Linux 4.9.11_1.0.0 release. Signed-off-by: Fugang Duan Signed-off-by: Stefan Agner Reviewed-by: Rob Herring Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ul-pinfunc.h | 169 +++++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 72 deletions(-) diff --git a/arch/arm/boot/dts/imx6ul-pinfunc.h b/arch/arm/boot/dts/imx6ul-pinfunc.h index 0034eeb84542..9538b0ed5c11 100644 --- a/arch/arm/boot/dts/imx6ul-pinfunc.h +++ b/arch/arm/boot/dts/imx6ul-pinfunc.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Freescale Semiconductor, Inc. + * Copyright 2014 - 2015 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -34,14 +34,14 @@ #define MX6UL_PAD_JTAG_MOD__ENET1_REF_CLK_25M 0x0044 0x02d0 0x0000 3 0 #define MX6UL_PAD_JTAG_MOD__CCM_PMIC_RDY 0x0044 0x02d0 0x04c0 4 0 #define MX6UL_PAD_JTAG_MOD__GPIO1_IO10 0x0044 0x02d0 0x0000 5 0 -#define MX6UL_PAD_JTAG_MOD__SDMA_EXT_EVENT00 0x0044 0x02d0 0x0000 6 0 +#define MX6UL_PAD_JTAG_MOD__SDMA_EXT_EVENT00 0x0044 0x02d0 0x0610 6 0 #define MX6UL_PAD_JTAG_TMS__SJC_TMS 0x0048 0x02d4 0x0000 0 0 #define MX6UL_PAD_JTAG_TMS__GPT2_CAPTURE1 0x0048 0x02d4 0x0598 1 0 -#define MX6UL_PAD_JTAG_TMS__SAI2_MCLK 0x0048 0x02d4 0x0000 2 0 +#define MX6UL_PAD_JTAG_TMS__SAI2_MCLK 0x0048 0x02d4 0x05f0 2 0 #define MX6UL_PAD_JTAG_TMS__CCM_CLKO1 0x0048 0x02d4 0x0000 3 0 #define MX6UL_PAD_JTAG_TMS__CCM_WAIT 0x0048 0x02d4 0x0000 4 0 #define MX6UL_PAD_JTAG_TMS__GPIO1_IO11 0x0048 0x02d4 0x0000 5 0 -#define MX6UL_PAD_JTAG_TMS__SDMA_EXT_EVENT01 0x0048 0x02d4 0x0000 6 0 +#define MX6UL_PAD_JTAG_TMS__SDMA_EXT_EVENT01 0x0048 0x02d4 0x0614 6 0 #define MX6UL_PAD_JTAG_TMS__EPIT1_OUT 0x0048 0x02d4 0x0000 8 0 #define MX6UL_PAD_JTAG_TDO__SJC_TDO 0x004c 0x02d8 0x0000 0 0 #define MX6UL_PAD_JTAG_TDO__GPT2_CAPTURE2 0x004c 0x02d8 0x059c 1 0 @@ -63,12 +63,14 @@ #define MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x0054 0x02e0 0x05f4 2 0 #define MX6UL_PAD_JTAG_TCK__PWM7_OUT 0x0054 0x02e0 0x0000 4 0 #define MX6UL_PAD_JTAG_TCK__GPIO1_IO14 0x0054 0x02e0 0x0000 5 0 +#define MX6UL_PAD_JTAG_TCK__REF_CLK_32K 0x0054 0x02e0 0x0000 6 0 #define MX6UL_PAD_JTAG_TCK__SIM2_POWER_FAIL 0x0054 0x02e0 0x0000 8 0 #define MX6UL_PAD_JTAG_TRST_B__SJC_TRSTB 0x0058 0x02e4 0x0000 0 0 #define MX6UL_PAD_JTAG_TRST_B__GPT2_COMPARE3 0x0058 0x02e4 0x0000 1 0 #define MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x0058 0x02e4 0x0000 2 0 #define MX6UL_PAD_JTAG_TRST_B__PWM8_OUT 0x0058 0x02e4 0x0000 4 0 #define MX6UL_PAD_JTAG_TRST_B__GPIO1_IO15 0x0058 0x02e4 0x0000 5 0 +#define MX6UL_PAD_JTAG_TRST_B__REF_CLK_24M 0x0058 0x02e4 0x0000 6 0 #define MX6UL_PAD_JTAG_TRST_B__CAAM_RNG_OSC_OBS 0x0058 0x02e4 0x0000 8 0 #define MX6UL_PAD_GPIO1_IO00__I2C2_SCL 0x005c 0x02e8 0x05ac 0 1 #define MX6UL_PAD_GPIO1_IO00__GPT1_CAPTURE1 0x005c 0x02e8 0x058c 1 0 @@ -94,22 +96,24 @@ #define MX6UL_PAD_GPIO1_IO02__ENET1_REF_CLK_25M 0x0064 0x02f0 0x0000 3 0 #define MX6UL_PAD_GPIO1_IO02__USDHC1_WP 0x0064 0x02f0 0x066c 4 0 #define MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0x0064 0x02f0 0x0000 5 0 -#define MX6UL_PAD_GPIO1_IO02__SDMA_EXT_EVENT00 0x0064 0x02f0 0x0000 6 0 +#define MX6UL_PAD_GPIO1_IO02__SDMA_EXT_EVENT00 0x0064 0x02f0 0x0610 6 1 #define MX6UL_PAD_GPIO1_IO02__SRC_ANY_PU_RESET 0x0064 0x02f0 0x0000 7 0 #define MX6UL_PAD_GPIO1_IO02__UART1_DCE_TX 0x0064 0x02f0 0x0000 8 0 #define MX6UL_PAD_GPIO1_IO02__UART1_DTE_RX 0x0064 0x02f0 0x0624 8 0 #define MX6UL_PAD_GPIO1_IO03__I2C1_SDA 0x0068 0x02f4 0x05a8 0 1 #define MX6UL_PAD_GPIO1_IO03__GPT1_COMPARE3 0x0068 0x02f4 0x0000 1 0 #define MX6UL_PAD_GPIO1_IO03__USB_OTG2_OC 0x0068 0x02f4 0x0660 2 0 +#define MX6UL_PAD_GPIO1_IO03__REF_CLK_32K 0x0068 0x02f4 0x0000 3 0 #define MX6UL_PAD_GPIO1_IO03__USDHC1_CD_B 0x0068 0x02f4 0x0668 4 0 #define MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x0068 0x02f4 0x0000 5 0 -#define MX6UL_PAD_GPIO1_IO03__CCM_DI0_eXT_CLK 0x0068 0x02f4 0x0000 6 0 +#define MX6UL_PAD_GPIO1_IO03__CCM_DI0_EXT_CLK 0x0068 0x02f4 0x0000 6 0 #define MX6UL_PAD_GPIO1_IO03__SRC_TESTER_ACK 0x0068 0x02f4 0x0000 7 0 -#define MX6UL_PAD_GPIO1_IO03__UART1_DTE_TX 0x0068 0x02f4 0x0000 8 0 #define MX6UL_PAD_GPIO1_IO03__UART1_DCE_RX 0x0068 0x02f4 0x0624 8 1 +#define MX6UL_PAD_GPIO1_IO03__UART1_DTE_TX 0x0068 0x02f4 0x0000 8 0 #define MX6UL_PAD_GPIO1_IO04__ENET1_REF_CLK1 0x006c 0x02f8 0x0574 0 1 #define MX6UL_PAD_GPIO1_IO04__PWM3_OUT 0x006c 0x02f8 0x0000 1 0 #define MX6UL_PAD_GPIO1_IO04__USB_OTG1_PWR 0x006c 0x02f8 0x0000 2 0 +#define MX6UL_PAD_GPIO1_IO04__REF_CLK_24M 0x006c 0x02f8 0x0000 3 0 #define MX6UL_PAD_GPIO1_IO04__USDHC1_RESET_B 0x006c 0x02f8 0x0000 4 0 #define MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x006c 0x02f8 0x0000 5 0 #define MX6UL_PAD_GPIO1_IO04__ENET2_1588_EVENT0_IN 0x006c 0x02f8 0x0000 6 0 @@ -200,7 +204,7 @@ #define MX6UL_PAD_UART2_TX_DATA__CSI_DATA06 0x0094 0x0320 0x04dc 3 0 #define MX6UL_PAD_UART2_TX_DATA__GPT1_CAPTURE1 0x0094 0x0320 0x058c 4 1 #define MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x0094 0x0320 0x0000 5 0 -#define MX6UL_PAD_UART2_TX_DATA__ECSPI3_SS0 0x0094 0x0320 0x0000 8 0 +#define MX6UL_PAD_UART2_TX_DATA__ECSPI3_SS0 0x0094 0x0320 0x0560 8 0 #define MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX 0x0098 0x0324 0x062c 0 1 #define MX6UL_PAD_UART2_RX_DATA__UART2_DTE_TX 0x0098 0x0324 0x0000 0 0 #define MX6UL_PAD_UART2_RX_DATA__ENET1_TDATA03 0x0098 0x0324 0x0000 1 0 @@ -232,7 +236,7 @@ #define MX6UL_PAD_UART3_TX_DATA__UART3_DTE_RX 0x00a4 0x0330 0x0634 0 0 #define MX6UL_PAD_UART3_TX_DATA__ENET2_RDATA02 0x00a4 0x0330 0x0000 1 0 #define MX6UL_PAD_UART3_TX_DATA__SIM1_PORT0_PD 0x00a4 0x0330 0x0000 2 0 -#define MX6UL_PAD_UART3_TX_DATA__CSI_DATA01 0x00a4 0x0330 0x0000 3 0 +#define MX6UL_PAD_UART3_TX_DATA__CSI_DATA01 0x00a4 0x0330 0x04d4 3 0 #define MX6UL_PAD_UART3_TX_DATA__UART2_DCE_CTS 0x00a4 0x0330 0x0000 4 0 #define MX6UL_PAD_UART3_TX_DATA__UART2_DTE_RTS 0x00a4 0x0330 0x0628 4 2 #define MX6UL_PAD_UART3_TX_DATA__GPIO1_IO24 0x00a4 0x0330 0x0000 5 0 @@ -242,7 +246,7 @@ #define MX6UL_PAD_UART3_RX_DATA__UART3_DTE_TX 0x00a8 0x0334 0x0000 0 0 #define MX6UL_PAD_UART3_RX_DATA__ENET2_RDATA03 0x00a8 0x0334 0x0000 1 0 #define MX6UL_PAD_UART3_RX_DATA__SIM2_PORT0_PD 0x00a8 0x0334 0x0000 2 0 -#define MX6UL_PAD_UART3_RX_DATA__CSI_DATA00 0x00a8 0x0334 0x0000 3 0 +#define MX6UL_PAD_UART3_RX_DATA__CSI_DATA00 0x00a8 0x0334 0x04d0 3 0 #define MX6UL_PAD_UART3_RX_DATA__UART2_DCE_RTS 0x00a8 0x0334 0x0628 4 3 #define MX6UL_PAD_UART3_RX_DATA__UART2_DTE_CTS 0x00a8 0x0334 0x0000 4 0 #define MX6UL_PAD_UART3_RX_DATA__GPIO1_IO25 0x00a8 0x0334 0x0000 5 0 @@ -251,7 +255,7 @@ #define MX6UL_PAD_UART3_CTS_B__UART3_DTE_RTS 0x00ac 0x0338 0x0630 0 0 #define MX6UL_PAD_UART3_CTS_B__ENET2_RX_CLK 0x00ac 0x0338 0x0000 1 0 #define MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x00ac 0x0338 0x0000 2 0 -#define MX6UL_PAD_UART3_CTS_B__CSI_DATA10 0x00ac 0x0338 0x0000 3 0 +#define MX6UL_PAD_UART3_CTS_B__CSI_DATA10 0x00ac 0x0338 0x04ec 3 0 #define MX6UL_PAD_UART3_CTS_B__ENET1_1588_EVENT1_IN 0x00ac 0x0338 0x0000 4 0 #define MX6UL_PAD_UART3_CTS_B__GPIO1_IO26 0x00ac 0x0338 0x0000 5 0 #define MX6UL_PAD_UART3_CTS_B__EPIT2_OUT 0x00ac 0x0338 0x0000 8 0 @@ -259,7 +263,7 @@ #define MX6UL_PAD_UART3_RTS_B__UART3_DTE_CTS 0x00b0 0x033c 0x0000 0 0 #define MX6UL_PAD_UART3_RTS_B__ENET2_TX_ER 0x00b0 0x033c 0x0000 1 0 #define MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x00b0 0x033c 0x0584 2 0 -#define MX6UL_PAD_UART3_RTS_B__CSI_DATA11 0x00b0 0x033c 0x0000 3 0 +#define MX6UL_PAD_UART3_RTS_B__CSI_DATA11 0x00b0 0x033c 0x04f0 3 0 #define MX6UL_PAD_UART3_RTS_B__ENET1_1588_EVENT1_OUT 0x00b0 0x033c 0x0000 4 0 #define MX6UL_PAD_UART3_RTS_B__GPIO1_IO27 0x00b0 0x033c 0x0000 5 0 #define MX6UL_PAD_UART3_RTS_B__WDOG1_WDOG_B 0x00b0 0x033c 0x0000 8 0 @@ -267,7 +271,7 @@ #define MX6UL_PAD_UART4_TX_DATA__UART4_DTE_RX 0x00b4 0x0340 0x063c 0 0 #define MX6UL_PAD_UART4_TX_DATA__ENET2_TDATA02 0x00b4 0x0340 0x0000 1 0 #define MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x00b4 0x0340 0x05a4 2 1 -#define MX6UL_PAD_UART4_TX_DATA__CSI_DATA12 0x00b4 0x0340 0x0000 3 0 +#define MX6UL_PAD_UART4_TX_DATA__CSI_DATA12 0x00b4 0x0340 0x04f4 3 0 #define MX6UL_PAD_UART4_TX_DATA__CSU_CSU_ALARM_AUT02 0x00b4 0x0340 0x0000 4 0 #define MX6UL_PAD_UART4_TX_DATA__GPIO1_IO28 0x00b4 0x0340 0x0000 5 0 #define MX6UL_PAD_UART4_TX_DATA__ECSPI2_SCLK 0x00b4 0x0340 0x0544 8 1 @@ -275,23 +279,23 @@ #define MX6UL_PAD_UART4_RX_DATA__UART4_DTE_TX 0x00b8 0x0344 0x0000 0 0 #define MX6UL_PAD_UART4_RX_DATA__ENET2_TDATA03 0x00b8 0x0344 0x0000 1 0 #define MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x00b8 0x0344 0x05a8 2 2 -#define MX6UL_PAD_UART4_RX_DATA__CSI_DATA13 0x00b8 0x0344 0x0000 3 0 +#define MX6UL_PAD_UART4_RX_DATA__CSI_DATA13 0x00b8 0x0344 0x04f8 3 0 #define MX6UL_PAD_UART4_RX_DATA__CSU_CSU_ALARM_AUT01 0x00b8 0x0344 0x0000 4 0 #define MX6UL_PAD_UART4_RX_DATA__GPIO1_IO29 0x00b8 0x0344 0x0000 5 0 -#define MX6UL_PAD_UART4_RX_DATA__ECSPI2_SS0 0x00b8 0x0344 0x0000 8 0 +#define MX6UL_PAD_UART4_RX_DATA__ECSPI2_SS0 0x00b8 0x0344 0x0550 8 1 #define MX6UL_PAD_UART5_TX_DATA__GPIO1_IO30 0x00bc 0x0348 0x0000 5 0 #define MX6UL_PAD_UART5_TX_DATA__ECSPI2_MOSI 0x00bc 0x0348 0x054c 8 0 #define MX6UL_PAD_UART5_TX_DATA__UART5_DCE_TX 0x00bc 0x0348 0x0000 0 0 #define MX6UL_PAD_UART5_TX_DATA__UART5_DTE_RX 0x00bc 0x0348 0x0644 0 4 #define MX6UL_PAD_UART5_TX_DATA__ENET2_CRS 0x00bc 0x0348 0x0000 1 0 #define MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x00bc 0x0348 0x05ac 2 2 -#define MX6UL_PAD_UART5_TX_DATA__CSI_DATA14 0x00bc 0x0348 0x0000 3 0 +#define MX6UL_PAD_UART5_TX_DATA__CSI_DATA14 0x00bc 0x0348 0x04fc 3 0 #define MX6UL_PAD_UART5_TX_DATA__CSU_CSU_ALARM_AUT00 0x00bc 0x0348 0x0000 4 0 #define MX6UL_PAD_UART5_RX_DATA__UART5_DCE_RX 0x00c0 0x034c 0x0644 0 5 #define MX6UL_PAD_UART5_RX_DATA__UART5_DTE_TX 0x00c0 0x034c 0x0000 0 0 #define MX6UL_PAD_UART5_RX_DATA__ENET2_COL 0x00c0 0x034c 0x0000 1 0 #define MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x00c0 0x034c 0x05b0 2 2 -#define MX6UL_PAD_UART5_RX_DATA__CSI_DATA15 0x00c0 0x034c 0x0000 3 0 +#define MX6UL_PAD_UART5_RX_DATA__CSI_DATA15 0x00c0 0x034c 0x0500 3 0 #define MX6UL_PAD_UART5_RX_DATA__CSU_CSU_INT_DEB 0x00c0 0x034c 0x0000 4 0 #define MX6UL_PAD_UART5_RX_DATA__GPIO1_IO31 0x00c0 0x034c 0x0000 5 0 #define MX6UL_PAD_UART5_RX_DATA__ECSPI2_MISO 0x00c0 0x034c 0x0548 8 1 @@ -299,59 +303,61 @@ #define MX6UL_PAD_ENET1_RX_DATA0__UART4_DCE_RTS 0x00c4 0x0350 0x0638 1 0 #define MX6UL_PAD_ENET1_RX_DATA0__UART4_DTE_CTS 0x00c4 0x0350 0x0000 1 0 #define MX6UL_PAD_ENET1_RX_DATA0__PWM1_OUT 0x00c4 0x0350 0x0000 2 0 -#define MX6UL_PAD_ENET1_RX_DATA0__CSI_DATA16 0x00c4 0x0350 0x0000 3 0 +#define MX6UL_PAD_ENET1_RX_DATA0__CSI_DATA16 0x00c4 0x0350 0x0504 3 0 #define MX6UL_PAD_ENET1_RX_DATA0__FLEXCAN1_TX 0x00c4 0x0350 0x0000 4 0 #define MX6UL_PAD_ENET1_RX_DATA0__GPIO2_IO00 0x00c4 0x0350 0x0000 5 0 -#define MX6UL_PAD_ENET1_RX_DATA0__KPP_ROW00 0x00c4 0x0350 0x0000 6 0 +#define MX6UL_PAD_ENET1_RX_DATA0__KPP_ROW00 0x00c4 0x0350 0x05d0 6 0 #define MX6UL_PAD_ENET1_RX_DATA0__USDHC1_LCTL 0x00c4 0x0350 0x0000 8 0 #define MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x00c8 0x0354 0x0000 0 0 #define MX6UL_PAD_ENET1_RX_DATA1__UART4_DCE_CTS 0x00c8 0x0354 0x0000 1 0 #define MX6UL_PAD_ENET1_RX_DATA1__UART4_DTE_RTS 0x00c8 0x0354 0x0638 1 1 #define MX6UL_PAD_ENET1_RX_DATA1__PWM2_OUT 0x00c8 0x0354 0x0000 2 0 -#define MX6UL_PAD_ENET1_RX_DATA1__CSI_DATA17 0x00c8 0x0354 0x0000 3 0 +#define MX6UL_PAD_ENET1_RX_DATA1__CSI_DATA17 0x00c8 0x0354 0x0508 3 0 #define MX6UL_PAD_ENET1_RX_DATA1__FLEXCAN1_RX 0x00c8 0x0354 0x0584 4 1 #define MX6UL_PAD_ENET1_RX_DATA1__GPIO2_IO01 0x00c8 0x0354 0x0000 5 0 -#define MX6UL_PAD_ENET1_RX_DATA1__KPP_COL00 0x00c8 0x0354 0x0000 6 0 +#define MX6UL_PAD_ENET1_RX_DATA1__KPP_COL00 0x00c8 0x0354 0x05c4 6 0 #define MX6UL_PAD_ENET1_RX_DATA1__USDHC2_LCTL 0x00c8 0x0354 0x0000 8 0 #define MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x00cc 0x0358 0x0000 0 0 #define MX6UL_PAD_ENET1_RX_EN__UART5_DCE_RTS 0x00cc 0x0358 0x0640 1 3 #define MX6UL_PAD_ENET1_RX_EN__UART5_DTE_CTS 0x00cc 0x0358 0x0000 1 0 -#define MX6UL_PAD_ENET1_RX_EN__CSI_DATA18 0x00cc 0x0358 0x0000 3 0 +#define MX6UL_PAD_ENET1_RX_EN__REF_CLK_32K 0x00cc 0x0358 0x0000 2 0 +#define MX6UL_PAD_ENET1_RX_EN__CSI_DATA18 0x00cc 0x0358 0x050c 3 0 #define MX6UL_PAD_ENET1_RX_EN__FLEXCAN2_TX 0x00cc 0x0358 0x0000 4 0 #define MX6UL_PAD_ENET1_RX_EN__GPIO2_IO02 0x00cc 0x0358 0x0000 5 0 -#define MX6UL_PAD_ENET1_RX_EN__KPP_ROW01 0x00cc 0x0358 0x0000 6 0 +#define MX6UL_PAD_ENET1_RX_EN__KPP_ROW01 0x00cc 0x0358 0x05d4 6 0 #define MX6UL_PAD_ENET1_RX_EN__USDHC1_VSELECT 0x00cc 0x0358 0x0000 8 0 #define MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x00d0 0x035c 0x0000 0 0 #define MX6UL_PAD_ENET1_TX_DATA0__UART5_DCE_CTS 0x00d0 0x035c 0x0000 1 0 #define MX6UL_PAD_ENET1_TX_DATA0__UART5_DTE_RTS 0x00d0 0x035c 0x0640 1 4 -#define MX6UL_PAD_ENET1_TX_DATA0__CSI_DATA19 0x00d0 0x035c 0x0000 3 0 +#define MX6UL_PAD_ENET1_TX_DATA0__REF_CLK_24M 0x00d0 0x035c 0x0000 2 0 +#define MX6UL_PAD_ENET1_TX_DATA0__CSI_DATA19 0x00d0 0x035c 0x0510 3 0 #define MX6UL_PAD_ENET1_TX_DATA0__FLEXCAN2_RX 0x00d0 0x035c 0x0588 4 1 #define MX6UL_PAD_ENET1_TX_DATA0__GPIO2_IO03 0x00d0 0x035c 0x0000 5 0 -#define MX6UL_PAD_ENET1_TX_DATA0__KPP_COL01 0x00d0 0x035c 0x0000 6 0 +#define MX6UL_PAD_ENET1_TX_DATA0__KPP_COL01 0x00d0 0x035c 0x05c8 6 0 #define MX6UL_PAD_ENET1_TX_DATA0__USDHC2_VSELECT 0x00d0 0x035c 0x0000 8 0 #define MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x00d4 0x0360 0x0000 0 0 #define MX6UL_PAD_ENET1_TX_DATA1__UART6_DCE_CTS 0x00d4 0x0360 0x0000 1 0 #define MX6UL_PAD_ENET1_TX_DATA1__UART6_DTE_RTS 0x00d4 0x0360 0x0648 1 2 #define MX6UL_PAD_ENET1_TX_DATA1__PWM5_OUT 0x00d4 0x0360 0x0000 2 0 -#define MX6UL_PAD_ENET1_TX_DATA1__CSI_DATA20 0x00d4 0x0360 0x0000 3 0 +#define MX6UL_PAD_ENET1_TX_DATA1__CSI_DATA20 0x00d4 0x0360 0x0514 3 0 #define MX6UL_PAD_ENET1_TX_DATA1__ENET2_MDIO 0x00d4 0x0360 0x0580 4 1 #define MX6UL_PAD_ENET1_TX_DATA1__GPIO2_IO04 0x00d4 0x0360 0x0000 5 0 -#define MX6UL_PAD_ENET1_TX_DATA1__KPP_ROW02 0x00d4 0x0360 0x0000 6 0 +#define MX6UL_PAD_ENET1_TX_DATA1__KPP_ROW02 0x00d4 0x0360 0x05d8 6 0 #define MX6UL_PAD_ENET1_TX_DATA1__WDOG1_WDOG_RST_B_DEB 0x00d4 0x0360 0x0000 8 0 #define MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x00d8 0x0364 0x0000 0 0 #define MX6UL_PAD_ENET1_TX_EN__UART6_DCE_RTS 0x00d8 0x0364 0x0648 1 3 #define MX6UL_PAD_ENET1_TX_EN__UART6_DTE_CTS 0x00d8 0x0364 0x0000 1 0 #define MX6UL_PAD_ENET1_TX_EN__PWM6_OUT 0x00d8 0x0364 0x0000 2 0 -#define MX6UL_PAD_ENET1_TX_EN__CSI_DATA21 0x00d8 0x0364 0x0000 3 0 +#define MX6UL_PAD_ENET1_TX_EN__CSI_DATA21 0x00d8 0x0364 0x0518 3 0 #define MX6UL_PAD_ENET1_TX_EN__ENET2_MDC 0x00d8 0x0364 0x0000 4 0 #define MX6UL_PAD_ENET1_TX_EN__GPIO2_IO05 0x00d8 0x0364 0x0000 5 0 -#define MX6UL_PAD_ENET1_TX_EN__KPP_COL02 0x00d8 0x0364 0x0000 6 0 +#define MX6UL_PAD_ENET1_TX_EN__KPP_COL02 0x00d8 0x0364 0x05cc 6 0 #define MX6UL_PAD_ENET1_TX_EN__WDOG2_WDOG_RST_B_DEB 0x00d8 0x0364 0x0000 8 0 #define MX6UL_PAD_ENET1_TX_CLK__ENET1_TX_CLK 0x00dc 0x0368 0x0000 0 0 #define MX6UL_PAD_ENET1_TX_CLK__UART7_DCE_CTS 0x00dc 0x0368 0x0000 1 0 #define MX6UL_PAD_ENET1_TX_CLK__UART7_DTE_RTS 0x00dc 0x0368 0x0650 1 0 #define MX6UL_PAD_ENET1_TX_CLK__PWM7_OUT 0x00dc 0x0368 0x0000 2 0 -#define MX6UL_PAD_ENET1_TX_CLK__CSI_DATA22 0x00dc 0x0368 0x0000 3 0 +#define MX6UL_PAD_ENET1_TX_CLK__CSI_DATA22 0x00dc 0x0368 0x051c 3 0 #define MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x00dc 0x0368 0x0574 4 2 #define MX6UL_PAD_ENET1_TX_CLK__GPIO2_IO06 0x00dc 0x0368 0x0000 5 0 #define MX6UL_PAD_ENET1_TX_CLK__KPP_ROW03 0x00dc 0x0368 0x0000 6 0 @@ -360,7 +366,7 @@ #define MX6UL_PAD_ENET1_RX_ER__UART7_DCE_RTS 0x00e0 0x036c 0x0650 1 1 #define MX6UL_PAD_ENET1_RX_ER__UART7_DTE_CTS 0x00e0 0x036c 0x0000 1 0 #define MX6UL_PAD_ENET1_RX_ER__PWM8_OUT 0x00e0 0x036c 0x0000 2 0 -#define MX6UL_PAD_ENET1_RX_ER__CSI_DATA23 0x00e0 0x036c 0x0000 3 0 +#define MX6UL_PAD_ENET1_RX_ER__CSI_DATA23 0x00e0 0x036c 0x0520 3 0 #define MX6UL_PAD_ENET1_RX_ER__EIM_CRE 0x00e0 0x036c 0x0000 4 0 #define MX6UL_PAD_ENET1_RX_ER__GPIO2_IO07 0x00e0 0x036c 0x0000 5 0 #define MX6UL_PAD_ENET1_RX_ER__KPP_COL03 0x00e0 0x036c 0x0000 6 0 @@ -377,7 +383,7 @@ #define MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x00e8 0x0374 0x0000 0 0 #define MX6UL_PAD_ENET2_RX_DATA1__UART6_DCE_RX 0x00e8 0x0374 0x064c 1 2 #define MX6UL_PAD_ENET2_RX_DATA1__UART6_DTE_TX 0x00e8 0x0374 0x0000 1 0 -#define MX6UL_PAD_ENET2_RX_DATA1__SIM1_PORT0_cLK 0x00e8 0x0374 0x0000 2 0 +#define MX6UL_PAD_ENET2_RX_DATA1__SIM1_PORT0_CLK 0x00e8 0x0374 0x0000 2 0 #define MX6UL_PAD_ENET2_RX_DATA1__I2C3_SDA 0x00e8 0x0374 0x05b8 3 1 #define MX6UL_PAD_ENET2_RX_DATA1__ENET1_MDC 0x00e8 0x0374 0x0000 4 0 #define MX6UL_PAD_ENET2_RX_DATA1__GPIO2_IO09 0x00e8 0x0374 0x0000 5 0 @@ -400,6 +406,7 @@ #define MX6UL_PAD_ENET2_TX_DATA0__EIM_EB_B02 0x00f0 0x037c 0x0000 4 0 #define MX6UL_PAD_ENET2_TX_DATA0__GPIO2_IO11 0x00f0 0x037c 0x0000 5 0 #define MX6UL_PAD_ENET2_TX_DATA0__KPP_COL05 0x00f0 0x037c 0x0000 6 0 +#define MX6UL_PAD_ENET2_TX_DATA0__REF_CLK_24M 0x00f0 0x037c 0x0000 8 0 #define MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x00f4 0x0380 0x0000 0 0 #define MX6UL_PAD_ENET2_TX_DATA1__UART8_DCE_TX 0x00f4 0x0380 0x0000 1 0 #define MX6UL_PAD_ENET2_TX_DATA1__UART8_DTE_RX 0x00f4 0x0380 0x065c 1 0 @@ -412,7 +419,7 @@ #define MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x00f8 0x0384 0x0000 0 0 #define MX6UL_PAD_ENET2_TX_EN__UART8_DCE_RX 0x00f8 0x0384 0x065c 1 1 #define MX6UL_PAD_ENET2_TX_EN__UART8_DTE_TX 0x00f8 0x0384 0x0000 1 0 -#define MX6UL_PAD_ENET2_TX_EN__SIM2_PORT0_cLK 0x00f8 0x0384 0x0000 2 0 +#define MX6UL_PAD_ENET2_TX_EN__SIM2_PORT0_CLK 0x00f8 0x0384 0x0000 2 0 #define MX6UL_PAD_ENET2_TX_EN__ECSPI4_MOSI 0x00f8 0x0384 0x056c 3 0 #define MX6UL_PAD_ENET2_TX_EN__EIM_ACLK_FREERUN 0x00f8 0x0384 0x0000 4 0 #define MX6UL_PAD_ENET2_TX_EN__GPIO2_IO13 0x00f8 0x0384 0x0000 5 0 @@ -431,7 +438,7 @@ #define MX6UL_PAD_ENET2_RX_ER__UART8_DCE_RTS 0x0100 0x038c 0x0658 1 1 #define MX6UL_PAD_ENET2_RX_ER__UART8_DTE_CTS 0x0100 0x038c 0x0000 1 0 #define MX6UL_PAD_ENET2_RX_ER__SIM2_PORT0_SVEN 0x0100 0x038c 0x0000 2 0 -#define MX6UL_PAD_ENET2_RX_ER__ECSPI4_SS0 0x0100 0x038c 0x0000 3 0 +#define MX6UL_PAD_ENET2_RX_ER__ECSPI4_SS0 0x0100 0x038c 0x0570 3 0 #define MX6UL_PAD_ENET2_RX_ER__EIM_ADDR25 0x0100 0x038c 0x0000 4 0 #define MX6UL_PAD_ENET2_RX_ER__GPIO2_IO15 0x0100 0x038c 0x0000 5 0 #define MX6UL_PAD_ENET2_RX_ER__KPP_COL07 0x0100 0x038c 0x0000 6 0 @@ -440,7 +447,7 @@ #define MX6UL_PAD_LCD_CLK__LCDIF_WR_RWN 0x0104 0x0390 0x0000 1 0 #define MX6UL_PAD_LCD_CLK__UART4_DCE_TX 0x0104 0x0390 0x0000 2 0 #define MX6UL_PAD_LCD_CLK__UART4_DTE_RX 0x0104 0x0390 0x063c 2 2 -#define MX6UL_PAD_LCD_CLK__SAI3_MCLK 0x0104 0x0390 0x0000 3 0 +#define MX6UL_PAD_LCD_CLK__SAI3_MCLK 0x0104 0x0390 0x0600 3 0 #define MX6UL_PAD_LCD_CLK__EIM_CS2_B 0x0104 0x0390 0x0000 4 0 #define MX6UL_PAD_LCD_CLK__GPIO3_IO00 0x0104 0x0390 0x0000 5 0 #define MX6UL_PAD_LCD_CLK__WDOG1_WDOG_RST_B_DEB 0x0104 0x0390 0x0000 8 0 @@ -464,7 +471,7 @@ #define MX6UL_PAD_LCD_VSYNC__LCDIF_BUSY 0x0110 0x039c 0x05dc 1 1 #define MX6UL_PAD_LCD_VSYNC__UART4_DCE_RTS 0x0110 0x039c 0x0638 2 3 #define MX6UL_PAD_LCD_VSYNC__UART4_DTE_CTS 0x0110 0x039c 0x0000 2 0 -#define MX6UL_PAD_LCD_VSYNC__SAI3_RX_DATA 0x0110 0x039c 0x0000 3 0 +#define MX6UL_PAD_LCD_VSYNC__SAI3_RX_DATA 0x0110 0x039c 0x0604 3 0 #define MX6UL_PAD_LCD_VSYNC__WDOG2_WDOG_B 0x0110 0x039c 0x0000 4 0 #define MX6UL_PAD_LCD_VSYNC__GPIO3_IO03 0x0110 0x039c 0x0000 5 0 #define MX6UL_PAD_LCD_VSYNC__ECSPI2_SS2 0x0110 0x039c 0x0000 8 0 @@ -477,13 +484,15 @@ #define MX6UL_PAD_LCD_RESET__ECSPI2_SS3 0x0114 0x03a0 0x0000 8 0 #define MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x0118 0x03a4 0x0000 0 0 #define MX6UL_PAD_LCD_DATA00__PWM1_OUT 0x0118 0x03a4 0x0000 1 0 +#define MX6UL_PAD_LCD_DATA00__CA7_MX6UL_TRACE0 0x0118 0x03a4 0x0000 2 0 #define MX6UL_PAD_LCD_DATA00__ENET1_1588_EVENT2_IN 0x0118 0x03a4 0x0000 3 0 #define MX6UL_PAD_LCD_DATA00__I2C3_SDA 0x0118 0x03a4 0x05b8 4 2 #define MX6UL_PAD_LCD_DATA00__GPIO3_IO05 0x0118 0x03a4 0x0000 5 0 #define MX6UL_PAD_LCD_DATA00__SRC_BT_CFG00 0x0118 0x03a4 0x0000 6 0 -#define MX6UL_PAD_LCD_DATA00__SAI1_MCLK 0x0118 0x03a4 0x0000 8 0 +#define MX6UL_PAD_LCD_DATA00__SAI1_MCLK 0x0118 0x03a4 0x05e0 8 1 #define MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x011c 0x03a8 0x0000 0 0 #define MX6UL_PAD_LCD_DATA01__PWM2_OUT 0x011c 0x03a8 0x0000 1 0 +#define MX6UL_PAD_LCD_DATA01__CA7_MX6UL_TRACE1 0x011c 0x03a8 0x0000 2 0 #define MX6UL_PAD_LCD_DATA01__ENET1_1588_EVENT2_OUT 0x011c 0x03a8 0x0000 3 0 #define MX6UL_PAD_LCD_DATA01__I2C3_SCL 0x011c 0x03a8 0x05b4 4 2 #define MX6UL_PAD_LCD_DATA01__GPIO3_IO06 0x011c 0x03a8 0x0000 5 0 @@ -491,6 +500,7 @@ #define MX6UL_PAD_LCD_DATA01__SAI1_TX_SYNC 0x011c 0x03a8 0x05ec 8 0 #define MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x0120 0x03ac 0x0000 0 0 #define MX6UL_PAD_LCD_DATA02__PWM3_OUT 0x0120 0x03ac 0x0000 1 0 +#define MX6UL_PAD_LCD_DATA02__CA7_MX6UL_TRACE2 0x0120 0x03ac 0x0000 2 0 #define MX6UL_PAD_LCD_DATA02__ENET1_1588_EVENT3_IN 0x0120 0x03ac 0x0000 3 0 #define MX6UL_PAD_LCD_DATA02__I2C4_SDA 0x0120 0x03ac 0x05c0 4 2 #define MX6UL_PAD_LCD_DATA02__GPIO3_IO07 0x0120 0x03ac 0x0000 5 0 @@ -498,14 +508,16 @@ #define MX6UL_PAD_LCD_DATA02__SAI1_TX_BCLK 0x0120 0x03ac 0x05e8 8 0 #define MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x0124 0x03b0 0x0000 0 0 #define MX6UL_PAD_LCD_DATA03__PWM4_OUT 0x0124 0x03b0 0x0000 1 0 +#define MX6UL_PAD_LCD_DATA03__CA7_MX6UL_TRACE3 0x0124 0x03b0 0x0000 2 0 #define MX6UL_PAD_LCD_DATA03__ENET1_1588_EVENT3_OUT 0x0124 0x03b0 0x0000 3 0 #define MX6UL_PAD_LCD_DATA03__I2C4_SCL 0x0124 0x03b0 0x05bc 4 2 #define MX6UL_PAD_LCD_DATA03__GPIO3_IO08 0x0124 0x03b0 0x0000 5 0 #define MX6UL_PAD_LCD_DATA03__SRC_BT_CFG03 0x0124 0x03b0 0x0000 6 0 -#define MX6UL_PAD_LCD_DATA03__SAI1_RX_DATA 0x0124 0x03b0 0x0000 8 0 +#define MX6UL_PAD_LCD_DATA03__SAI1_RX_DATA 0x0124 0x03b0 0x05e4 8 0 #define MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x0128 0x03b4 0x0000 0 0 #define MX6UL_PAD_LCD_DATA04__UART8_DCE_CTS 0x0128 0x03b4 0x0000 1 0 #define MX6UL_PAD_LCD_DATA04__UART8_DTE_RTS 0x0128 0x03b4 0x0658 1 2 +#define MX6UL_PAD_LCD_DATA04__CA7_MX6UL_TRACE4 0x0128 0x03b4 0x0000 2 0 #define MX6UL_PAD_LCD_DATA04__ENET2_1588_EVENT2_IN 0x0128 0x03b4 0x0000 3 0 #define MX6UL_PAD_LCD_DATA04__SPDIF_SR_CLK 0x0128 0x03b4 0x0000 4 0 #define MX6UL_PAD_LCD_DATA04__GPIO3_IO09 0x0128 0x03b4 0x0000 5 0 @@ -514,6 +526,7 @@ #define MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x012c 0x03b8 0x0000 0 0 #define MX6UL_PAD_LCD_DATA05__UART8_DCE_RTS 0x012c 0x03b8 0x0658 1 3 #define MX6UL_PAD_LCD_DATA05__UART8_DTE_CTS 0x012c 0x03b8 0x0000 1 0 +#define MX6UL_PAD_LCD_DATA05__CA7_MX6UL_TRACE5 0x012c 0x03b8 0x0000 2 0 #define MX6UL_PAD_LCD_DATA05__ENET2_1588_EVENT2_OUT 0x012c 0x03b8 0x0000 3 0 #define MX6UL_PAD_LCD_DATA05__SPDIF_OUT 0x012c 0x03b8 0x0000 4 0 #define MX6UL_PAD_LCD_DATA05__GPIO3_IO10 0x012c 0x03b8 0x0000 5 0 @@ -522,6 +535,7 @@ #define MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x0130 0x03bc 0x0000 0 0 #define MX6UL_PAD_LCD_DATA06__UART7_DCE_CTS 0x0130 0x03bc 0x0000 1 0 #define MX6UL_PAD_LCD_DATA06__UART7_DTE_RTS 0x0130 0x03bc 0x0650 1 2 +#define MX6UL_PAD_LCD_DATA06__CA7_MX6UL_TRACE6 0x0130 0x03bc 0x0000 2 0 #define MX6UL_PAD_LCD_DATA06__ENET2_1588_EVENT3_IN 0x0130 0x03bc 0x0000 3 0 #define MX6UL_PAD_LCD_DATA06__SPDIF_LOCK 0x0130 0x03bc 0x0000 4 0 #define MX6UL_PAD_LCD_DATA06__GPIO3_IO11 0x0130 0x03bc 0x0000 5 0 @@ -530,6 +544,7 @@ #define MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x0134 0x03c0 0x0000 0 0 #define MX6UL_PAD_LCD_DATA07__UART7_DCE_RTS 0x0134 0x03c0 0x0650 1 3 #define MX6UL_PAD_LCD_DATA07__UART7_DTE_CTS 0x0134 0x03c0 0x0000 1 0 +#define MX6UL_PAD_LCD_DATA07__CA7_MX6UL_TRACE7 0x0134 0x03c0 0x0000 2 0 #define MX6UL_PAD_LCD_DATA07__ENET2_1588_EVENT3_OUT 0x0134 0x03c0 0x0000 3 0 #define MX6UL_PAD_LCD_DATA07__SPDIF_EXT_CLK 0x0134 0x03c0 0x061c 4 0 #define MX6UL_PAD_LCD_DATA07__GPIO3_IO12 0x0134 0x03c0 0x0000 5 0 @@ -537,56 +552,64 @@ #define MX6UL_PAD_LCD_DATA07__ECSPI1_SS3 0x0134 0x03c0 0x0000 8 0 #define MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x0138 0x03c4 0x0000 0 0 #define MX6UL_PAD_LCD_DATA08__SPDIF_IN 0x0138 0x03c4 0x0618 1 2 -#define MX6UL_PAD_LCD_DATA08__CSI_DATA16 0x0138 0x03c4 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA08__CA7_MX6UL_TRACE8 0x0138 0x03c4 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA08__CSI_DATA16 0x0138 0x03c4 0x0504 3 1 #define MX6UL_PAD_LCD_DATA08__EIM_DATA00 0x0138 0x03c4 0x0000 4 0 #define MX6UL_PAD_LCD_DATA08__GPIO3_IO13 0x0138 0x03c4 0x0000 5 0 #define MX6UL_PAD_LCD_DATA08__SRC_BT_CFG08 0x0138 0x03c4 0x0000 6 0 #define MX6UL_PAD_LCD_DATA08__FLEXCAN1_TX 0x0138 0x03c4 0x0000 8 0 #define MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x013c 0x03c8 0x0000 0 0 -#define MX6UL_PAD_LCD_DATA09__SAI3_MCLK 0x013c 0x03c8 0x0000 1 0 -#define MX6UL_PAD_LCD_DATA09__CSI_DATA17 0x013c 0x03c8 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA09__SAI3_MCLK 0x013c 0x03c8 0x0600 1 1 +#define MX6UL_PAD_LCD_DATA09__CA7_MX6UL_TRACE9 0x013c 0x03c8 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA09__CSI_DATA17 0x013c 0x03c8 0x0508 3 1 #define MX6UL_PAD_LCD_DATA09__EIM_DATA01 0x013c 0x03c8 0x0000 4 0 #define MX6UL_PAD_LCD_DATA09__GPIO3_IO14 0x013c 0x03c8 0x0000 5 0 #define MX6UL_PAD_LCD_DATA09__SRC_BT_CFG09 0x013c 0x03c8 0x0000 6 0 #define MX6UL_PAD_LCD_DATA09__FLEXCAN1_RX 0x013c 0x03c8 0x0584 8 2 #define MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x0140 0x03cc 0x0000 0 0 #define MX6UL_PAD_LCD_DATA10__SAI3_RX_SYNC 0x0140 0x03cc 0x0000 1 0 -#define MX6UL_PAD_LCD_DATA10__CSI_DATA18 0x0140 0x03cc 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA10__CA7_MX6UL_TRACE10 0x0140 0x03cc 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA10__CSI_DATA18 0x0140 0x03cc 0x050c 3 1 #define MX6UL_PAD_LCD_DATA10__EIM_DATA02 0x0140 0x03cc 0x0000 4 0 #define MX6UL_PAD_LCD_DATA10__GPIO3_IO15 0x0140 0x03cc 0x0000 5 0 #define MX6UL_PAD_LCD_DATA10__SRC_BT_CFG10 0x0140 0x03cc 0x0000 6 0 #define MX6UL_PAD_LCD_DATA10__FLEXCAN2_TX 0x0140 0x03cc 0x0000 8 0 #define MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x0144 0x03d0 0x0000 0 0 #define MX6UL_PAD_LCD_DATA11__SAI3_RX_BCLK 0x0144 0x03d0 0x0000 1 0 -#define MX6UL_PAD_LCD_DATA11__CSI_DATA19 0x0144 0x03d0 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA11__CA7_MX6UL_TRACE11 0x0144 0x03d0 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA11__CSI_DATA19 0x0144 0x03d0 0x0510 3 1 #define MX6UL_PAD_LCD_DATA11__EIM_DATA03 0x0144 0x03d0 0x0000 4 0 #define MX6UL_PAD_LCD_DATA11__GPIO3_IO16 0x0144 0x03d0 0x0000 5 0 #define MX6UL_PAD_LCD_DATA11__SRC_BT_CFG11 0x0144 0x03d0 0x0000 6 0 #define MX6UL_PAD_LCD_DATA11__FLEXCAN2_RX 0x0144 0x03d0 0x0588 8 2 #define MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x0148 0x03d4 0x0000 0 0 #define MX6UL_PAD_LCD_DATA12__SAI3_TX_SYNC 0x0148 0x03d4 0x060c 1 1 -#define MX6UL_PAD_LCD_DATA12__CSI_DATA20 0x0148 0x03d4 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA12__CA7_MX6UL_TRACE12 0x0148 0x03d4 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA12__CSI_DATA20 0x0148 0x03d4 0x0514 3 1 #define MX6UL_PAD_LCD_DATA12__EIM_DATA04 0x0148 0x03d4 0x0000 4 0 #define MX6UL_PAD_LCD_DATA12__GPIO3_IO17 0x0148 0x03d4 0x0000 5 0 #define MX6UL_PAD_LCD_DATA12__SRC_BT_CFG12 0x0148 0x03d4 0x0000 6 0 #define MX6UL_PAD_LCD_DATA12__ECSPI1_RDY 0x0148 0x03d4 0x0000 8 0 #define MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x014c 0x03d8 0x0000 0 0 #define MX6UL_PAD_LCD_DATA13__SAI3_TX_BCLK 0x014c 0x03d8 0x0608 1 1 -#define MX6UL_PAD_LCD_DATA13__CSI_DATA21 0x014c 0x03d8 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA13__CA7_MX6UL_TRACE13 0x014c 0x03d8 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA13__CSI_DATA21 0x014c 0x03d8 0x0518 3 1 #define MX6UL_PAD_LCD_DATA13__EIM_DATA05 0x014c 0x03d8 0x0000 4 0 #define MX6UL_PAD_LCD_DATA13__GPIO3_IO18 0x014c 0x03d8 0x0000 5 0 #define MX6UL_PAD_LCD_DATA13__SRC_BT_CFG13 0x014c 0x03d8 0x0000 6 0 #define MX6UL_PAD_LCD_DATA13__USDHC2_RESET_B 0x014c 0x03d8 0x0000 8 0 #define MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x0150 0x03dc 0x0000 0 0 -#define MX6UL_PAD_LCD_DATA14__SAI3_RX_DATA 0x0150 0x03dc 0x0000 1 0 -#define MX6UL_PAD_LCD_DATA14__CSI_DATA22 0x0150 0x03dc 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA14__SAI3_RX_DATA 0x0150 0x03dc 0x0604 1 1 +#define MX6UL_PAD_LCD_DATA14__CA7_MX6UL_TRACE14 0x0150 0x03dc 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA14__CSI_DATA22 0x0150 0x03dc 0x051c 3 1 #define MX6UL_PAD_LCD_DATA14__EIM_DATA06 0x0150 0x03dc 0x0000 4 0 #define MX6UL_PAD_LCD_DATA14__GPIO3_IO19 0x0150 0x03dc 0x0000 5 0 #define MX6UL_PAD_LCD_DATA14__SRC_BT_CFG14 0x0150 0x03dc 0x0000 6 0 #define MX6UL_PAD_LCD_DATA14__USDHC2_DATA4 0x0150 0x03dc 0x068c 8 0 #define MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x0154 0x03e0 0x0000 0 0 #define MX6UL_PAD_LCD_DATA15__SAI3_TX_DATA 0x0154 0x03e0 0x0000 1 0 -#define MX6UL_PAD_LCD_DATA15__CSI_DATA23 0x0154 0x03e0 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA15__CA7_MX6UL_TRACE15 0x0154 0x03e0 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA15__CSI_DATA23 0x0154 0x03e0 0x0520 3 1 #define MX6UL_PAD_LCD_DATA15__EIM_DATA07 0x0154 0x03e0 0x0000 4 0 #define MX6UL_PAD_LCD_DATA15__GPIO3_IO20 0x0154 0x03e0 0x0000 5 0 #define MX6UL_PAD_LCD_DATA15__SRC_BT_CFG15 0x0154 0x03e0 0x0000 6 0 @@ -594,7 +617,8 @@ #define MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x0158 0x03e4 0x0000 0 0 #define MX6UL_PAD_LCD_DATA16__UART7_DCE_TX 0x0158 0x03e4 0x0000 1 0 #define MX6UL_PAD_LCD_DATA16__UART7_DTE_RX 0x0158 0x03e4 0x0654 1 2 -#define MX6UL_PAD_LCD_DATA16__CSI_DATA01 0x0158 0x03e4 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA16__CA7_MX6UL_TRACE_CLK 0x0158 0x03e4 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA16__CSI_DATA01 0x0158 0x03e4 0x04d4 3 1 #define MX6UL_PAD_LCD_DATA16__EIM_DATA08 0x0158 0x03e4 0x0000 4 0 #define MX6UL_PAD_LCD_DATA16__GPIO3_IO21 0x0158 0x03e4 0x0000 5 0 #define MX6UL_PAD_LCD_DATA16__SRC_BT_CFG24 0x0158 0x03e4 0x0000 6 0 @@ -602,7 +626,8 @@ #define MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x015c 0x03e8 0x0000 0 0 #define MX6UL_PAD_LCD_DATA17__UART7_DCE_RX 0x015c 0x03e8 0x0654 1 3 #define MX6UL_PAD_LCD_DATA17__UART7_DTE_TX 0x015c 0x03e8 0x0000 1 0 -#define MX6UL_PAD_LCD_DATA17__CSI_DATA00 0x015c 0x03e8 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA17__CA7_MX6UL_TRACE_CTL 0x015c 0x03e8 0x0000 2 0 +#define MX6UL_PAD_LCD_DATA17__CSI_DATA00 0x015c 0x03e8 0x04d0 3 1 #define MX6UL_PAD_LCD_DATA17__EIM_DATA09 0x015c 0x03e8 0x0000 4 0 #define MX6UL_PAD_LCD_DATA17__GPIO3_IO22 0x015c 0x03e8 0x0000 5 0 #define MX6UL_PAD_LCD_DATA17__SRC_BT_CFG25 0x015c 0x03e8 0x0000 6 0 @@ -610,7 +635,7 @@ #define MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x0160 0x03ec 0x0000 0 0 #define MX6UL_PAD_LCD_DATA18__PWM5_OUT 0x0160 0x03ec 0x0000 1 0 #define MX6UL_PAD_LCD_DATA18__CA7_MX6UL_EVENTO 0x0160 0x03ec 0x0000 2 0 -#define MX6UL_PAD_LCD_DATA18__CSI_DATA10 0x0160 0x03ec 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA18__CSI_DATA10 0x0160 0x03ec 0x04ec 3 1 #define MX6UL_PAD_LCD_DATA18__EIM_DATA10 0x0160 0x03ec 0x0000 4 0 #define MX6UL_PAD_LCD_DATA18__GPIO3_IO23 0x0160 0x03ec 0x0000 5 0 #define MX6UL_PAD_LCD_DATA18__SRC_BT_CFG26 0x0160 0x03ec 0x0000 6 0 @@ -622,7 +647,7 @@ #define MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x0164 0x03f0 0x0000 0 0 #define MX6UL_PAD_LCD_DATA19__PWM6_OUT 0x0164 0x03f0 0x0000 1 0 #define MX6UL_PAD_LCD_DATA19__WDOG1_WDOG_ANY 0x0164 0x03f0 0x0000 2 0 -#define MX6UL_PAD_LCD_DATA19__CSI_DATA11 0x0164 0x03f0 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA19__CSI_DATA11 0x0164 0x03f0 0x04f0 3 1 #define MX6UL_PAD_LCD_DATA20__EIM_DATA12 0x0168 0x03f4 0x0000 4 0 #define MX6UL_PAD_LCD_DATA20__GPIO3_IO25 0x0168 0x03f4 0x0000 5 0 #define MX6UL_PAD_LCD_DATA20__SRC_BT_CFG28 0x0168 0x03f4 0x0000 6 0 @@ -631,12 +656,12 @@ #define MX6UL_PAD_LCD_DATA20__UART8_DCE_TX 0x0168 0x03f4 0x0000 1 0 #define MX6UL_PAD_LCD_DATA20__UART8_DTE_RX 0x0168 0x03f4 0x065c 1 2 #define MX6UL_PAD_LCD_DATA20__ECSPI1_SCLK 0x0168 0x03f4 0x0534 2 0 -#define MX6UL_PAD_LCD_DATA20__CSI_DATA12 0x0168 0x03f4 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA20__CSI_DATA12 0x0168 0x03f4 0x04f4 3 1 #define MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x016c 0x03f8 0x0000 0 0 #define MX6UL_PAD_LCD_DATA21__UART8_DCE_RX 0x016c 0x03f8 0x065c 1 3 #define MX6UL_PAD_LCD_DATA21__UART8_DTE_TX 0x016c 0x03f8 0x0000 1 0 -#define MX6UL_PAD_LCD_DATA21__ECSPI1_SS0 0x016c 0x03f8 0x0000 2 0 -#define MX6UL_PAD_LCD_DATA21__CSI_DATA13 0x016c 0x03f8 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA21__ECSPI1_SS0 0x016c 0x03f8 0x0540 2 0 +#define MX6UL_PAD_LCD_DATA21__CSI_DATA13 0x016c 0x03f8 0x04f8 3 1 #define MX6UL_PAD_LCD_DATA21__EIM_DATA13 0x016c 0x03f8 0x0000 4 0 #define MX6UL_PAD_LCD_DATA21__GPIO3_IO26 0x016c 0x03f8 0x0000 5 0 #define MX6UL_PAD_LCD_DATA21__SRC_BT_CFG29 0x016c 0x03f8 0x0000 6 0 @@ -644,7 +669,7 @@ #define MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x0170 0x03fc 0x0000 0 0 #define MX6UL_PAD_LCD_DATA22__MQS_RIGHT 0x0170 0x03fc 0x0000 1 0 #define MX6UL_PAD_LCD_DATA22__ECSPI1_MOSI 0x0170 0x03fc 0x053c 2 0 -#define MX6UL_PAD_LCD_DATA22__CSI_DATA14 0x0170 0x03fc 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA22__CSI_DATA14 0x0170 0x03fc 0x04fc 3 1 #define MX6UL_PAD_LCD_DATA22__EIM_DATA14 0x0170 0x03fc 0x0000 4 0 #define MX6UL_PAD_LCD_DATA22__GPIO3_IO27 0x0170 0x03fc 0x0000 5 0 #define MX6UL_PAD_LCD_DATA22__SRC_BT_CFG30 0x0170 0x03fc 0x0000 6 0 @@ -652,7 +677,7 @@ #define MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x0174 0x0400 0x0000 0 0 #define MX6UL_PAD_LCD_DATA23__MQS_LEFT 0x0174 0x0400 0x0000 1 0 #define MX6UL_PAD_LCD_DATA23__ECSPI1_MISO 0x0174 0x0400 0x0538 2 0 -#define MX6UL_PAD_LCD_DATA23__CSI_DATA15 0x0174 0x0400 0x0000 3 0 +#define MX6UL_PAD_LCD_DATA23__CSI_DATA15 0x0174 0x0400 0x0500 3 1 #define MX6UL_PAD_LCD_DATA23__EIM_DATA15 0x0174 0x0400 0x0000 4 0 #define MX6UL_PAD_LCD_DATA23__GPIO3_IO28 0x0174 0x0400 0x0000 5 0 #define MX6UL_PAD_LCD_DATA23__SRC_BT_CFG31 0x0174 0x0400 0x0000 6 0 @@ -660,42 +685,42 @@ #define MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B 0x0178 0x0404 0x0000 0 0 #define MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x0178 0x0404 0x0670 1 2 #define MX6UL_PAD_NAND_RE_B__QSPI_B_SCLK 0x0178 0x0404 0x0000 2 0 -#define MX6UL_PAD_NAND_RE_B__KPP_ROW00 0x0178 0x0404 0x0000 3 0 +#define MX6UL_PAD_NAND_RE_B__KPP_ROW00 0x0178 0x0404 0x05d0 3 1 #define MX6UL_PAD_NAND_RE_B__EIM_EB_B00 0x0178 0x0404 0x0000 4 0 #define MX6UL_PAD_NAND_RE_B__GPIO4_IO00 0x0178 0x0404 0x0000 5 0 #define MX6UL_PAD_NAND_RE_B__ECSPI3_SS2 0x0178 0x0404 0x0000 8 0 #define MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B 0x017c 0x0408 0x0000 0 0 #define MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x017c 0x0408 0x0678 1 2 #define MX6UL_PAD_NAND_WE_B__QSPI_B_SS0_B 0x017c 0x0408 0x0000 2 0 -#define MX6UL_PAD_NAND_WE_B__KPP_COL00 0x017c 0x0408 0x0000 3 0 +#define MX6UL_PAD_NAND_WE_B__KPP_COL00 0x017c 0x0408 0x05c4 3 1 #define MX6UL_PAD_NAND_WE_B__EIM_EB_B01 0x017c 0x0408 0x0000 4 0 #define MX6UL_PAD_NAND_WE_B__GPIO4_IO01 0x017c 0x0408 0x0000 5 0 #define MX6UL_PAD_NAND_WE_B__ECSPI3_SS3 0x017c 0x0408 0x0000 8 0 #define MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00 0x0180 0x040c 0x0000 0 0 #define MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x0180 0x040c 0x067c 1 2 #define MX6UL_PAD_NAND_DATA00__QSPI_B_SS1_B 0x0180 0x040c 0x0000 2 0 -#define MX6UL_PAD_NAND_DATA00__KPP_ROW01 0x0180 0x040c 0x0000 3 0 +#define MX6UL_PAD_NAND_DATA00__KPP_ROW01 0x0180 0x040c 0x05d4 3 1 #define MX6UL_PAD_NAND_DATA00__EIM_AD08 0x0180 0x040c 0x0000 4 0 #define MX6UL_PAD_NAND_DATA00__GPIO4_IO02 0x0180 0x040c 0x0000 5 0 #define MX6UL_PAD_NAND_DATA00__ECSPI4_RDY 0x0180 0x040c 0x0000 8 0 #define MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01 0x0184 0x0410 0x0000 0 0 #define MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x0184 0x0410 0x0680 1 2 #define MX6UL_PAD_NAND_DATA01__QSPI_B_DQS 0x0184 0x0410 0x0000 2 0 -#define MX6UL_PAD_NAND_DATA01__KPP_COL01 0x0184 0x0410 0x0000 3 0 +#define MX6UL_PAD_NAND_DATA01__KPP_COL01 0x0184 0x0410 0x05c8 3 1 #define MX6UL_PAD_NAND_DATA01__EIM_AD09 0x0184 0x0410 0x0000 4 0 #define MX6UL_PAD_NAND_DATA01__GPIO4_IO03 0x0184 0x0410 0x0000 5 0 #define MX6UL_PAD_NAND_DATA01__ECSPI4_SS1 0x0184 0x0410 0x0000 8 0 #define MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02 0x0188 0x0414 0x0000 0 0 #define MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x0188 0x0414 0x0684 1 1 #define MX6UL_PAD_NAND_DATA02__QSPI_B_DATA00 0x0188 0x0414 0x0000 2 0 -#define MX6UL_PAD_NAND_DATA02__KPP_ROW02 0x0188 0x0414 0x0000 3 0 +#define MX6UL_PAD_NAND_DATA02__KPP_ROW02 0x0188 0x0414 0x05d8 3 1 #define MX6UL_PAD_NAND_DATA02__EIM_AD10 0x0188 0x0414 0x0000 4 0 #define MX6UL_PAD_NAND_DATA02__GPIO4_IO04 0x0188 0x0414 0x0000 5 0 #define MX6UL_PAD_NAND_DATA02__ECSPI4_SS2 0x0188 0x0414 0x0000 8 0 #define MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03 0x018c 0x0418 0x0000 0 0 #define MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x018c 0x0418 0x0688 1 2 #define MX6UL_PAD_NAND_DATA03__QSPI_B_DATA01 0x018c 0x0418 0x0000 2 0 -#define MX6UL_PAD_NAND_DATA03__KPP_COL02 0x018c 0x0418 0x0000 3 0 +#define MX6UL_PAD_NAND_DATA03__KPP_COL02 0x018c 0x0418 0x05cc 3 1 #define MX6UL_PAD_NAND_DATA03__EIM_AD11 0x018c 0x0418 0x0000 4 0 #define MX6UL_PAD_NAND_DATA03__GPIO4_IO05 0x018c 0x0418 0x0000 5 0 #define MX6UL_PAD_NAND_DATA03__ECSPI4_SS3 0x018c 0x0418 0x0000 8 0 @@ -726,7 +751,7 @@ #define MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07 0x019c 0x0428 0x0000 0 0 #define MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x019c 0x0428 0x0698 1 1 #define MX6UL_PAD_NAND_DATA07__QSPI_A_SS1_B 0x019c 0x0428 0x0000 2 0 -#define MX6UL_PAD_NAND_DATA07__ECSPI4_SS0 0x019c 0x0428 0x0000 3 0 +#define MX6UL_PAD_NAND_DATA07__ECSPI4_SS0 0x019c 0x0428 0x0570 3 1 #define MX6UL_PAD_NAND_DATA07__EIM_AD15 0x019c 0x0428 0x0000 4 0 #define MX6UL_PAD_NAND_DATA07__GPIO4_IO09 0x019c 0x0428 0x0000 5 0 #define MX6UL_PAD_NAND_DATA07__UART2_DCE_RTS 0x019c 0x0428 0x0628 8 5 @@ -748,7 +773,7 @@ #define MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0x01a8 0x0434 0x0000 0 0 #define MX6UL_PAD_NAND_READY_B__USDHC1_DATA4 0x01a8 0x0434 0x0000 1 0 #define MX6UL_PAD_NAND_READY_B__QSPI_A_DATA00 0x01a8 0x0434 0x0000 2 0 -#define MX6UL_PAD_NAND_READY_B__ECSPI3_SS0 0x01a8 0x0434 0x0000 3 0 +#define MX6UL_PAD_NAND_READY_B__ECSPI3_SS0 0x01a8 0x0434 0x0560 3 1 #define MX6UL_PAD_NAND_READY_B__EIM_CS1_B 0x01a8 0x0434 0x0000 4 0 #define MX6UL_PAD_NAND_READY_B__GPIO4_IO12 0x01a8 0x0434 0x0000 5 0 #define MX6UL_PAD_NAND_READY_B__UART3_DCE_TX 0x01a8 0x0434 0x0000 8 0 @@ -783,7 +808,7 @@ #define MX6UL_PAD_NAND_DQS__PWM5_OUT 0x01b8 0x0444 0x0000 3 0 #define MX6UL_PAD_NAND_DQS__EIM_WAIT 0x01b8 0x0444 0x0000 4 0 #define MX6UL_PAD_NAND_DQS__GPIO4_IO16 0x01b8 0x0444 0x0000 5 0 -#define MX6UL_PAD_NAND_DQS__SDMA_EXT_EVENT01 0x01b8 0x0444 0x0000 6 0 +#define MX6UL_PAD_NAND_DQS__SDMA_EXT_EVENT01 0x01b8 0x0444 0x0614 6 1 #define MX6UL_PAD_NAND_DQS__SPDIF_EXT_CLK 0x01b8 0x0444 0x061c 8 1 #define MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x01bc 0x0448 0x0000 0 0 #define MX6UL_PAD_SD1_CMD__GPT2_COMPARE1 0x01bc 0x0448 0x0000 1 0 @@ -791,11 +816,11 @@ #define MX6UL_PAD_SD1_CMD__SPDIF_OUT 0x01bc 0x0448 0x0000 3 0 #define MX6UL_PAD_SD1_CMD__EIM_ADDR19 0x01bc 0x0448 0x0000 4 0 #define MX6UL_PAD_SD1_CMD__GPIO2_IO16 0x01bc 0x0448 0x0000 5 0 -#define MX6UL_PAD_SD1_CMD__SDMA_EXT_EVENT00 0x01bc 0x0448 0x0000 6 0 +#define MX6UL_PAD_SD1_CMD__SDMA_EXT_EVENT00 0x01bc 0x0448 0x0610 6 2 #define MX6UL_PAD_SD1_CMD__USB_OTG1_PWR 0x01bc 0x0448 0x0000 8 0 #define MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x01c0 0x044c 0x0000 0 0 #define MX6UL_PAD_SD1_CLK__GPT2_COMPARE2 0x01c0 0x044c 0x0000 1 0 -#define MX6UL_PAD_SD1_CLK__SAI2_MCLK 0x01c0 0x044c 0x0000 2 0 +#define MX6UL_PAD_SD1_CLK__SAI2_MCLK 0x01c0 0x044c 0x05f0 2 1 #define MX6UL_PAD_SD1_CLK__SPDIF_IN 0x01c0 0x044c 0x0618 3 3 #define MX6UL_PAD_SD1_CLK__EIM_ADDR20 0x01c0 0x044c 0x0000 4 0 #define MX6UL_PAD_SD1_CLK__GPIO2_IO17 0x01c0 0x044c 0x0000 5 0 @@ -878,10 +903,10 @@ #define MX6UL_PAD_CSI_DATA01__CSI_DATA03 0x01e8 0x0474 0x04c8 0 0 #define MX6UL_PAD_CSI_DATA01__USDHC2_DATA1 0x01e8 0x0474 0x0680 1 0 #define MX6UL_PAD_CSI_DATA01__SIM1_PORT1_SVEN 0x01e8 0x0474 0x0000 2 0 -#define MX6UL_PAD_CSI_DATA01__ECSPI2_SS0 0x01e8 0x0474 0x0000 3 0 +#define MX6UL_PAD_CSI_DATA01__ECSPI2_SS0 0x01e8 0x0474 0x0550 3 0 #define MX6UL_PAD_CSI_DATA01__EIM_AD01 0x01e8 0x0474 0x0000 4 0 #define MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x01e8 0x0474 0x0000 5 0 -#define MX6UL_PAD_CSI_DATA01__SAI1_MCLK 0x01e8 0x0474 0x0000 6 0 +#define MX6UL_PAD_CSI_DATA01__SAI1_MCLK 0x01e8 0x0474 0x05e0 6 0 #define MX6UL_PAD_CSI_DATA01__UART5_DCE_RX 0x01e8 0x0474 0x0644 8 1 #define MX6UL_PAD_CSI_DATA01__UART5_DTE_TX 0x01e8 0x0474 0x0000 8 0 #define MX6UL_PAD_CSI_DATA02__CSI_DATA04 0x01ec 0x0478 0x04d8 0 1 @@ -913,7 +938,7 @@ #define MX6UL_PAD_CSI_DATA05__CSI_DATA07 0x01f8 0x0484 0x04e0 0 1 #define MX6UL_PAD_CSI_DATA05__USDHC2_DATA5 0x01f8 0x0484 0x0690 1 2 #define MX6UL_PAD_CSI_DATA05__SIM2_PORT1_RST_B 0x01f8 0x0484 0x0000 2 0 -#define MX6UL_PAD_CSI_DATA05__ECSPI1_SS0 0x01f8 0x0484 0x0000 3 0 +#define MX6UL_PAD_CSI_DATA05__ECSPI1_SS0 0x01f8 0x0484 0x0540 3 1 #define MX6UL_PAD_CSI_DATA05__EIM_AD05 0x01f8 0x0484 0x0000 4 0 #define MX6UL_PAD_CSI_DATA05__GPIO4_IO26 0x01f8 0x0484 0x0000 5 0 #define MX6UL_PAD_CSI_DATA05__SAI1_TX_BCLK 0x01f8 0x0484 0x05e8 6 1 @@ -924,7 +949,7 @@ #define MX6UL_PAD_CSI_DATA06__ECSPI1_MOSI 0x01fc 0x0488 0x053c 3 1 #define MX6UL_PAD_CSI_DATA06__EIM_AD06 0x01fc 0x0488 0x0000 4 0 #define MX6UL_PAD_CSI_DATA06__GPIO4_IO27 0x01fc 0x0488 0x0000 5 0 -#define MX6UL_PAD_CSI_DATA06__SAI1_RX_DATA 0x01fc 0x0488 0x0000 6 0 +#define MX6UL_PAD_CSI_DATA06__SAI1_RX_DATA 0x01fc 0x0488 0x05e4 6 1 #define MX6UL_PAD_CSI_DATA06__USDHC1_RESET_B 0x01fc 0x0488 0x0000 8 0 #define MX6UL_PAD_CSI_DATA07__CSI_DATA09 0x0200 0x048c 0x04e8 0 1 #define MX6UL_PAD_CSI_DATA07__USDHC2_DATA7 0x0200 0x048c 0x0698 1 2 -- cgit v1.3-14-g43fede From bc6805f20bd5982bc1bbc747268fdd567e6b9ee6 Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Wed, 10 Jan 2018 22:04:49 +0100 Subject: ARM: dts: imx6ull: add additional pinfunc defines for i.MX 6ULL On i.MX 6ULL, the pin MUX and CTRL register of BOOT_MODEx and TAMPERx pins are available through IOMUXC_SNVS. Add additional pinfunc defines. Signed-off-by: Bai Ping Signed-off-by: Stefan Agner Reviewed-by: Rob Herring Acked-by: Dong Aisheng Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ull-pinfunc-snvs.h | 26 ++++++++++++++++++++++++++ arch/arm/boot/dts/imx6ull.dtsi | 1 + 2 files changed, 27 insertions(+) create mode 100644 arch/arm/boot/dts/imx6ull-pinfunc-snvs.h diff --git a/arch/arm/boot/dts/imx6ull-pinfunc-snvs.h b/arch/arm/boot/dts/imx6ull-pinfunc-snvs.h new file mode 100644 index 000000000000..f6fb6783c193 --- /dev/null +++ b/arch/arm/boot/dts/imx6ull-pinfunc-snvs.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright (C) 2017 NXP + */ + +#ifndef __DTS_IMX6ULL_PINFUNC_SNVS_H +#define __DTS_IMX6ULL_PINFUNC_SNVS_H +/* + * The pin function ID is a tuple of + * + */ +#define MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x0000 0x0044 0x0000 0x5 0x0 +#define MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x0004 0x0048 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x0008 0x004C 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x000C 0x0050 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x0010 0x0054 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x0014 0x0058 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x0018 0x005C 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER5__GPIO5_IO05 0x001C 0x0060 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x0020 0x0064 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x0024 0x0068 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x0028 0x006C 0x0000 0x5 0x0 +#define MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x002C 0x0070 0x0000 0x5 0x0 + +#endif /* __DTS_IMX6ULL_PINFUNC_SNVS_H */ diff --git a/arch/arm/boot/dts/imx6ull.dtsi b/arch/arm/boot/dts/imx6ull.dtsi index 0c182917b863..a58c01dc15c3 100644 --- a/arch/arm/boot/dts/imx6ull.dtsi +++ b/arch/arm/boot/dts/imx6ull.dtsi @@ -41,3 +41,4 @@ #include "imx6ul.dtsi" #include "imx6ull-pinfunc.h" +#include "imx6ull-pinfunc-snvs.h" -- cgit v1.3-14-g43fede From 8dc72265c5f9f8583513a2c30c25eefee3458985 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 10 Jan 2018 22:04:50 +0100 Subject: ARM: dts: imx6ul: add interrupt of virt-capable GIC The Cortex-A7 and its GIC support virtualization extensions. To make use of them the CPU private interrupt needs to be specified. Signed-off-by: Stefan Agner Reviewed-by: Rob Herring Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ul.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi index d88fc9a2248f..9dc0a1279295 100644 --- a/arch/arm/boot/dts/imx6ul.dtsi +++ b/arch/arm/boot/dts/imx6ul.dtsi @@ -97,8 +97,10 @@ intc: interrupt-controller@a01000 { compatible = "arm,gic-400", "arm,cortex-a7-gic"; + interrupts = ; #interrupt-cells = <3>; interrupt-controller; + interrupt-parent = <&intc>; reg = <0x00a01000 0x1000>, <0x00a02000 0x2000>, <0x00a04000 0x2000>, -- cgit v1.3-14-g43fede From cff1ce712ff5a74317800596c5e7e524b9a1b5cf Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 10 Jan 2018 22:04:51 +0100 Subject: ARM: dts: imx6ul: add ARM architected timer Add per-core ARM architected timer. Unfortunately bootloaders (U-Boot) currently do not make the necessary initialization. Also specifing the clock manually using the clock-frequency property seems not to help. Therefor leave the timer disabled by default for now. Signed-off-by: Stefan Agner Reviewed-by: Rob Herring Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ul.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi index 9dc0a1279295..ba2d8a277439 100644 --- a/arch/arm/boot/dts/imx6ul.dtsi +++ b/arch/arm/boot/dts/imx6ul.dtsi @@ -107,6 +107,16 @@ <0x00a06000 0x2000>; }; + timer { + compatible = "arm,armv7-timer"; + interrupts = , + , + , + ; + interrupt-parent = <&intc>; + status = "disabled"; + }; + ckil: clock-cli { compatible = "fixed-clock"; #clock-cells = <0>; -- cgit v1.3-14-g43fede From c28bb160662604478d21b744c9525df62445eaf5 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 10 Jan 2018 22:04:52 +0100 Subject: ARM: dts: imx6ull: add IOMUXC SNVS instance The i.MX 6ULL features another IOMUX Controller called IOMUXC SNVS which allows to control BOOT_MODE and TAMPER pins. Add the controller to the i.MX 6ULL specific imx6ull.dtsi device tree. Signed-off-by: Stefan Agner Reviewed-by: Rob Herring Acked-by: Dong Aisheng Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ull.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm/boot/dts/imx6ull.dtsi b/arch/arm/boot/dts/imx6ull.dtsi index a58c01dc15c3..bc2cd4fb8b12 100644 --- a/arch/arm/boot/dts/imx6ull.dtsi +++ b/arch/arm/boot/dts/imx6ull.dtsi @@ -42,3 +42,20 @@ #include "imx6ul.dtsi" #include "imx6ull-pinfunc.h" #include "imx6ull-pinfunc-snvs.h" + +/ { + soc { + aips3: aips-bus@2200000 { + compatible = "fsl,aips-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x02200000 0x100000>; + ranges; + + iomuxc_snvs: iomuxc-snvs@2290000 { + compatible = "fsl,imx6ull-iomuxc-snvs"; + reg = <0x02290000 0x4000>; + }; + }; + }; +}; -- cgit v1.3-14-g43fede From 71a0febaa443fa5506ba00622047237949b9129d Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 10 Jan 2018 22:04:53 +0100 Subject: ARM: dts: imx6ull: add UART8 support In i.MX 6ULL UART8 is part of the AIPS-3 memory map instead of AIPS-1. Clocks and interrupts remain the same. Signed-off-by: Stefan Agner Reviewed-by: Rob Herring Acked-by: Dong Aisheng Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ull.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm/boot/dts/imx6ull.dtsi b/arch/arm/boot/dts/imx6ull.dtsi index bc2cd4fb8b12..571ddd71cdba 100644 --- a/arch/arm/boot/dts/imx6ull.dtsi +++ b/arch/arm/boot/dts/imx6ull.dtsi @@ -43,6 +43,9 @@ #include "imx6ull-pinfunc.h" #include "imx6ull-pinfunc-snvs.h" +/* Delete UART8 in AIPS-1 (i.MX6UL specific) */ +/delete-node/ &uart8; + / { soc { aips3: aips-bus@2200000 { @@ -56,6 +59,17 @@ compatible = "fsl,imx6ull-iomuxc-snvs"; reg = <0x02290000 0x4000>; }; + + uart8: serial@2288000 { + compatible = "fsl,imx6ul-uart", + "fsl,imx6q-uart"; + reg = <0x02288000 0x4000>; + interrupts = ; + clocks = <&clks IMX6UL_CLK_UART8_IPG>, + <&clks IMX6UL_CLK_UART8_SERIAL>; + clock-names = "ipg", "per"; + status = "disabled"; + }; }; }; }; -- cgit v1.3-14-g43fede From 68b09c864143d8c7d664d4369fd9710f5b119640 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Mon, 15 Jan 2018 15:24:52 +0100 Subject: ARM: dts: imx53-ppd: Add touchscreen reset line Utilise new support in Atmel MaxTouch driver to drive the touchscreen controllers reset line correctly. Signed-off-by: Martyn Welch Signed-off-by: Sebastian Reichel Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx53-ppd.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/imx53-ppd.dts b/arch/arm/boot/dts/imx53-ppd.dts index cce959438a79..123297da43a7 100644 --- a/arch/arm/boot/dts/imx53-ppd.dts +++ b/arch/arm/boot/dts/imx53-ppd.dts @@ -525,6 +525,7 @@ touchscreen@4b { compatible = "atmel,maxtouch"; + reset-gpio = <&gpio5 19 GPIO_ACTIVE_HIGH>; reg = <0x4b>; interrupt-parent = <&gpio5>; interrupts = <4 0x8>; -- cgit v1.3-14-g43fede From 1022c489a76eaad0b82a7166a1038a4506fb2a36 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 15 Jan 2018 15:28:20 +0100 Subject: ARM: dts: imx53-ppd: use PMIC's TSI pins in adc mode PPD uses the PMIC's TSI pins in general purpose ADC mode. Signed-off-by: Sebastian Reichel Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx53-ppd.dts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/imx53-ppd.dts b/arch/arm/boot/dts/imx53-ppd.dts index 123297da43a7..57730ddfe000 100644 --- a/arch/arm/boot/dts/imx53-ppd.dts +++ b/arch/arm/boot/dts/imx53-ppd.dts @@ -132,6 +132,14 @@ enable-active-high; }; + reg_tsiref: regulator-tsiref { + compatible = "regulator-fixed"; + regulator-name = "tsiref"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-always-on; + }; + pwm_bl: backlight { compatible = "pwm-backlight"; pwms = <&pwm2 0 50000>; @@ -294,6 +302,8 @@ interrupt-parent = <&gpio3>; interrupts = <12 0x8>; spi-max-frequency = <1000000>; + dlg,tsi-as-adc; + tsiref-supply = <®_tsiref>; regulators { buck1_reg: buck1 { -- cgit v1.3-14-g43fede From 9e5b2e9be15d6ac5210b14a787c6e9936e10ed77 Mon Sep 17 00:00:00 2001 From: Ian Ray Date: Mon, 8 Jan 2018 13:29:01 +0200 Subject: ARM: dts: imx6q-bx50v3: disable SD card Default boot medium must be internal eMMC, we don't want the SDIO to be used for these devices. This commit disables the SD card interface for GE Healthcare Bx50v3 devices. Signed-off-by: Ian Ray Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6q-bx50v3.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-bx50v3.dtsi b/arch/arm/boot/dts/imx6q-bx50v3.dtsi index 916ea94d75ca..e8ad6a5e9370 100644 --- a/arch/arm/boot/dts/imx6q-bx50v3.dtsi +++ b/arch/arm/boot/dts/imx6q-bx50v3.dtsi @@ -353,6 +353,10 @@ }; }; +&usdhc2 { + status = "disabled"; +}; + &usdhc4 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usdhc4>; -- cgit v1.3-14-g43fede From 9acb15e1dcd54ef32b94e7a03ad70fc0a8ad69f4 Mon Sep 17 00:00:00 2001 From: Ryan Harkin Date: Tue, 23 Jan 2018 12:37:30 +0000 Subject: ARM: dts: imx7s-warp: enable uart6 on mikroBus This patch configures the board to output UART6 to the mikroBus. The NXP WaRP7 board can route UART6 to the mikroBus, where an RS232 click board can be plugged in to get an additional serial port. Signed-off-by: Ryan Harkin Reviewed-by: Rui Miguel Silva Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx7s-warp.dts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/arm/boot/dts/imx7s-warp.dts b/arch/arm/boot/dts/imx7s-warp.dts index 9bdf121f7e43..1606713bf66c 100644 --- a/arch/arm/boot/dts/imx7s-warp.dts +++ b/arch/arm/boot/dts/imx7s-warp.dts @@ -271,6 +271,15 @@ status = "okay"; }; +&uart6 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart6>; + assigned-clocks = <&clks IMX7D_UART6_ROOT_SRC>; + assigned-clock-parents = <&clks IMX7D_PLL_SYS_MAIN_240M_CLK>; + fsl,dte-mode; + status = "okay"; +}; + &usbotg1 { dr_mode = "peripheral"; status = "okay"; @@ -379,6 +388,13 @@ >; }; + pinctrl_uart6: uart6grp { + fsl,pins = < + MX7D_PAD_ECSPI1_MOSI__UART6_DTE_RX 0x79 + MX7D_PAD_ECSPI1_SCLK__UART6_DTE_TX 0x79 + >; + }; + pinctrl_usdhc1: usdhc1grp { fsl,pins = < MX7D_PAD_SD1_CMD__SD1_CMD 0x59 -- cgit v1.3-14-g43fede From 7a485590960dff9f4cac42d9a197c7f9fbfae376 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 23 Jan 2018 23:12:23 +0800 Subject: ARM: dts: imx6sx: add pu power domain support Add PU power domain support, GPU is the only module inside PU power domain, and PU power is supplied by LDO_SOC. Signed-off-by: Anson Huang Acked-by: Dong Aisheng Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6sx.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi index fd7879342d0d..42ef4c6590f3 100644 --- a/arch/arm/boot/dts/imx6sx.dtsi +++ b/arch/arm/boot/dts/imx6sx.dtsi @@ -188,6 +188,7 @@ <&clks IMX6SX_CLK_GPU>, <&clks IMX6SX_CLK_GPU>; clock-names = "bus", "core", "shader"; + power-domains = <&pd_pu>; }; dma_apbh: dma-apbh@1804000 { @@ -767,6 +768,13 @@ #address-cells = <1>; #size-cells = <0>; + pd_pu: power-domain@1 { + reg = <1>; + #power-domain-cells = <0>; + power-supply = <®_soc>; + clocks = <&clks IMX6SX_CLK_GPU>; + }; + pd_pci: power-domain@3 { reg = <3>; #power-domain-cells = <0>; -- cgit v1.3-14-g43fede From bf4556703b0cd789a2e4f22ac09b0d54cc8a4f67 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 24 Jan 2018 00:42:14 +0800 Subject: ARM: dts: imx6sx: add ARM power domain support Add ARM power domain in PGC. Signed-off-by: Anson Huang Reviewed-by: Lucas Stach Acked-by: Dong Aisheng Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6sx.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi index 42ef4c6590f3..aa29ca6561ee 100644 --- a/arch/arm/boot/dts/imx6sx.dtsi +++ b/arch/arm/boot/dts/imx6sx.dtsi @@ -768,6 +768,11 @@ #address-cells = <1>; #size-cells = <0>; + power-domain@0 { + reg = <0>; + #power-domain-cells = <0>; + }; + pd_pu: power-domain@1 { reg = <1>; #power-domain-cells = <0>; -- cgit v1.3-14-g43fede From 6de57233aa24ccadb909f86ec6fd53ff25e6e2e0 Mon Sep 17 00:00:00 2001 From: Marco Franchi Date: Wed, 24 Jan 2018 11:22:12 -0200 Subject: ARM: dts: imx: Pass empty memory size on board dts In preparation for removing 'reg = <0 0>;' from the dtsi SoC files, pass 'reg = <0 0 >;' to the dts/dtsi board files that do not pass the memory size. Signed-off-by: Marco Franchi Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx51-zii-rdu1.dts | 5 +++++ arch/arm/boot/dts/imx53-tx53.dtsi | 5 +++++ arch/arm/boot/dts/imx6dl-colibri-eval-v3.dts | 5 +++++ arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts | 5 +++++ arch/arm/boot/dts/imx6dl-ts4900.dts | 5 +++++ arch/arm/boot/dts/imx6dl-ts7970.dts | 5 +++++ arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts | 5 +++++ arch/arm/boot/dts/imx6q-h100.dts | 5 +++++ arch/arm/boot/dts/imx6q-novena.dts | 5 +++++ arch/arm/boot/dts/imx6q-ts4900.dts | 5 +++++ arch/arm/boot/dts/imx6q-ts7970.dts | 5 +++++ arch/arm/boot/dts/imx6q-zii-rdu2.dts | 5 +++++ arch/arm/boot/dts/imx6qdl-apalis.dtsi | 5 +++++ arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 5 +++++ arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 5 +++++ arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi | 5 +++++ arch/arm/boot/dts/imx6qp-zii-rdu2.dts | 5 +++++ 17 files changed, 85 insertions(+) diff --git a/arch/arm/boot/dts/imx51-zii-rdu1.dts b/arch/arm/boot/dts/imx51-zii-rdu1.dts index 5306b78de0ca..aa36204f917f 100644 --- a/arch/arm/boot/dts/imx51-zii-rdu1.dts +++ b/arch/arm/boot/dts/imx51-zii-rdu1.dts @@ -51,6 +51,11 @@ stdout-path = &uart1; }; + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; + aliases { mdio-gpio0 = &mdio_gpio; rtc0 = &ds1341; diff --git a/arch/arm/boot/dts/imx53-tx53.dtsi b/arch/arm/boot/dts/imx53-tx53.dtsi index a22e461fc168..e77dd0a4e1b9 100644 --- a/arch/arm/boot/dts/imx53-tx53.dtsi +++ b/arch/arm/boot/dts/imx53-tx53.dtsi @@ -49,6 +49,11 @@ model = "Ka-Ro electronics TX53 module"; compatible = "karo,tx53", "fsl,imx53"; + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; + aliases { can0 = &can2; /* Make the can interface indices consistent with TX28/TX48 modules */ can1 = &can1; diff --git a/arch/arm/boot/dts/imx6dl-colibri-eval-v3.dts b/arch/arm/boot/dts/imx6dl-colibri-eval-v3.dts index dcf9206f3e0d..700e129d3865 100644 --- a/arch/arm/boot/dts/imx6dl-colibri-eval-v3.dts +++ b/arch/arm/boot/dts/imx6dl-colibri-eval-v3.dts @@ -53,6 +53,11 @@ compatible = "toradex,colibri_imx6dl-eval-v3", "toradex,colibri_imx6dl", "fsl,imx6dl"; + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; + aliases { i2c0 = &i2c2; i2c1 = &i2c3; diff --git a/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts b/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts index 994f96a3fb54..c195ad629024 100644 --- a/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts +++ b/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts @@ -20,4 +20,9 @@ / { model = "DFI FS700-M60-6DL i.MX6dl Q7 Board"; compatible = "dfi,fs700-m60-6dl", "dfi,fs700e-m60", "fsl,imx6dl"; + + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; }; diff --git a/arch/arm/boot/dts/imx6dl-ts4900.dts b/arch/arm/boot/dts/imx6dl-ts4900.dts index 6ea0b780677d..e23f7891cf15 100644 --- a/arch/arm/boot/dts/imx6dl-ts4900.dts +++ b/arch/arm/boot/dts/imx6dl-ts4900.dts @@ -46,4 +46,9 @@ / { model = "Technologic Systems i.MX6 Solo/DualLite TS-4900 (Default Device Tree)"; compatible = "technologic,imx6dl-ts4900", "fsl,imx6dl"; + + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; }; diff --git a/arch/arm/boot/dts/imx6dl-ts7970.dts b/arch/arm/boot/dts/imx6dl-ts7970.dts index d104daf305d9..cae3313201a1 100644 --- a/arch/arm/boot/dts/imx6dl-ts7970.dts +++ b/arch/arm/boot/dts/imx6dl-ts7970.dts @@ -47,4 +47,9 @@ / { model = "Technologic Systems i.MX6 Solo/DualLite TS-7970 (Default Device Tree)"; compatible = "technologic,imx6dl-ts7970", "fsl,imx6dl"; + + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; }; diff --git a/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts b/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts index fd0ad9a8866c..a266fe6adaa3 100644 --- a/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts +++ b/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts @@ -20,4 +20,9 @@ / { model = "DFI FS700-M60-6QD i.MX6qd Q7 Board"; compatible = "dfi,fs700-m60-6qd", "dfi,fs700e-m60", "fsl,imx6q"; + + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; }; diff --git a/arch/arm/boot/dts/imx6q-h100.dts b/arch/arm/boot/dts/imx6q-h100.dts index 8a2ea6c58902..db0e738b5391 100644 --- a/arch/arm/boot/dts/imx6q-h100.dts +++ b/arch/arm/boot/dts/imx6q-h100.dts @@ -49,6 +49,11 @@ model = "Auvidea H100"; compatible = "auvidea,h100", "fsl,imx6q"; + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; + aliases { rtc0 = &rtc; rtc1 = &snvs_rtc; diff --git a/arch/arm/boot/dts/imx6q-novena.dts b/arch/arm/boot/dts/imx6q-novena.dts index 7d7dc59507cf..43a0c32ea85f 100644 --- a/arch/arm/boot/dts/imx6q-novena.dts +++ b/arch/arm/boot/dts/imx6q-novena.dts @@ -55,6 +55,11 @@ model = "Kosagi Novena Dual/Quad"; compatible = "kosagi,imx6q-novena", "fsl,imx6q"; + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; + chosen { stdout-path = &uart2; }; diff --git a/arch/arm/boot/dts/imx6q-ts4900.dts b/arch/arm/boot/dts/imx6q-ts4900.dts index fab76f8cd076..9cf7b375588d 100644 --- a/arch/arm/boot/dts/imx6q-ts4900.dts +++ b/arch/arm/boot/dts/imx6q-ts4900.dts @@ -46,6 +46,11 @@ / { model = "Technologic Systems i.MX6 Quad TS-4900 (Default Device Tree)"; compatible = "technologic,imx6q-ts4900", "fsl,imx6q"; + + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; }; &sata { diff --git a/arch/arm/boot/dts/imx6q-ts7970.dts b/arch/arm/boot/dts/imx6q-ts7970.dts index f19e18995e68..fd84fc53f1ea 100644 --- a/arch/arm/boot/dts/imx6q-ts7970.dts +++ b/arch/arm/boot/dts/imx6q-ts7970.dts @@ -47,6 +47,11 @@ / { model = "Technologic Systems i.MX6 Quad TS-7970 (Default Device Tree)"; compatible = "technologic,imx6q-ts7970", "fsl,imx6q"; + + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; }; &sata { diff --git a/arch/arm/boot/dts/imx6q-zii-rdu2.dts b/arch/arm/boot/dts/imx6q-zii-rdu2.dts index 6be8a1eea895..fe8b12f31a4c 100644 --- a/arch/arm/boot/dts/imx6q-zii-rdu2.dts +++ b/arch/arm/boot/dts/imx6q-zii-rdu2.dts @@ -47,4 +47,9 @@ / { model = "ZII RDU2 Board"; compatible = "zii,imx6q-zii-rdu2", "fsl,imx6q"; + + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; }; diff --git a/arch/arm/boot/dts/imx6qdl-apalis.dtsi b/arch/arm/boot/dts/imx6qdl-apalis.dtsi index 4e776e036cbc..bd658db01290 100644 --- a/arch/arm/boot/dts/imx6qdl-apalis.dtsi +++ b/arch/arm/boot/dts/imx6qdl-apalis.dtsi @@ -47,6 +47,11 @@ model = "Toradex Apalis iMX6Q/D Module"; compatible = "toradex,apalis_imx6q", "fsl,imx6q"; + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; + backlight: backlight { compatible = "pwm-backlight"; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi index d1cfdc264126..4c5f5132345e 100644 --- a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi @@ -42,6 +42,11 @@ #include / { + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; + ir_recv: ir-receiver { compatible = "gpio-ir-receiver"; gpios = <&gpio3 9 1>; diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi index 92583238ca4a..52eeeed11eb1 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -40,6 +40,11 @@ */ / { + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; + chosen { stdout-path = &uart1; }; diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi index dffbc92e0023..e68e7db0f3bd 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi @@ -40,6 +40,11 @@ */ / { + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; + chosen { stdout-path = &uart1; }; diff --git a/arch/arm/boot/dts/imx6qp-zii-rdu2.dts b/arch/arm/boot/dts/imx6qp-zii-rdu2.dts index 547a76677ab3..96ab983918e4 100644 --- a/arch/arm/boot/dts/imx6qp-zii-rdu2.dts +++ b/arch/arm/boot/dts/imx6qp-zii-rdu2.dts @@ -47,4 +47,9 @@ / { model = "ZII RDU2+ Board"; compatible = "zii,imx6qp-zii-rdu2", "fsl,imx6qp"; + + /* Will be filled by the bootloader */ + memory { + reg = <0 0>; + }; }; -- cgit v1.3-14-g43fede From 7f08e6aabdb46c095f5f32d5b252c05bdd4e5fd6 Mon Sep 17 00:00:00 2001 From: Marco Franchi Date: Wed, 24 Jan 2018 11:22:13 -0200 Subject: ARM: dts: imx: Remove empty memory size nodes Remove the empty reg property from the SoC dtsi files in order to avoid duplicate memory nodes when the correct size is passed in board dts files. Signed-off-by: Marco Franchi Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx1.dtsi | 2 +- arch/arm/boot/dts/imx23.dtsi | 2 +- arch/arm/boot/dts/imx25.dtsi | 2 +- arch/arm/boot/dts/imx27.dtsi | 2 +- arch/arm/boot/dts/imx28.dtsi | 2 +- arch/arm/boot/dts/imx31.dtsi | 2 +- arch/arm/boot/dts/imx35.dtsi | 2 +- arch/arm/boot/dts/imx50.dtsi | 2 +- arch/arm/boot/dts/imx51.dtsi | 2 +- arch/arm/boot/dts/imx53.dtsi | 2 +- arch/arm/boot/dts/imx6qdl.dtsi | 2 +- arch/arm/boot/dts/imx6sl.dtsi | 2 +- arch/arm/boot/dts/imx6sx.dtsi | 2 +- arch/arm/boot/dts/imx6ul.dtsi | 2 +- arch/arm/boot/dts/imx7s.dtsi | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/arm/boot/dts/imx1.dtsi b/arch/arm/boot/dts/imx1.dtsi index 20f6565c337d..f7b9edf93f5e 100644 --- a/arch/arm/boot/dts/imx1.dtsi +++ b/arch/arm/boot/dts/imx1.dtsi @@ -25,7 +25,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { gpio0 = &gpio1; diff --git a/arch/arm/boot/dts/imx23.dtsi b/arch/arm/boot/dts/imx23.dtsi index da935a4fe907..cb0a3fe32718 100644 --- a/arch/arm/boot/dts/imx23.dtsi +++ b/arch/arm/boot/dts/imx23.dtsi @@ -23,7 +23,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { gpio0 = &gpio0; diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi index 9445f8e1473c..972570568c01 100644 --- a/arch/arm/boot/dts/imx25.dtsi +++ b/arch/arm/boot/dts/imx25.dtsi @@ -22,7 +22,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &fec; diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi index 15d85f1f85fd..6585b00c3917 100644 --- a/arch/arm/boot/dts/imx27.dtsi +++ b/arch/arm/boot/dts/imx27.dtsi @@ -26,7 +26,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &fec; diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi index ee3396d21c01..9ad8d3556859 100644 --- a/arch/arm/boot/dts/imx28.dtsi +++ b/arch/arm/boot/dts/imx28.dtsi @@ -24,7 +24,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &mac0; diff --git a/arch/arm/boot/dts/imx31.dtsi b/arch/arm/boot/dts/imx31.dtsi index a72031407ebd..ebc3f2dbb6fd 100644 --- a/arch/arm/boot/dts/imx31.dtsi +++ b/arch/arm/boot/dts/imx31.dtsi @@ -19,7 +19,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { serial0 = &uart1; diff --git a/arch/arm/boot/dts/imx35.dtsi b/arch/arm/boot/dts/imx35.dtsi index e08c0c193767..bf343195697e 100644 --- a/arch/arm/boot/dts/imx35.dtsi +++ b/arch/arm/boot/dts/imx35.dtsi @@ -20,7 +20,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &fec; diff --git a/arch/arm/boot/dts/imx50.dtsi b/arch/arm/boot/dts/imx50.dtsi index 35955e63d6c5..7954e79d0a16 100644 --- a/arch/arm/boot/dts/imx50.dtsi +++ b/arch/arm/boot/dts/imx50.dtsi @@ -25,7 +25,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &fec; diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi index 00d30bd70068..5d390a64e976 100644 --- a/arch/arm/boot/dts/imx51.dtsi +++ b/arch/arm/boot/dts/imx51.dtsi @@ -26,7 +26,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &fec; diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi index 1040251f2951..7d647d043f52 100644 --- a/arch/arm/boot/dts/imx53.dtsi +++ b/arch/arm/boot/dts/imx53.dtsi @@ -26,7 +26,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &fec; diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index 59ff86695a14..0c70ea888d32 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -23,7 +23,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &fec; diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi index ae8df3cf687e..ab6a7e2e7e8f 100644 --- a/arch/arm/boot/dts/imx6sl.dtsi +++ b/arch/arm/boot/dts/imx6sl.dtsi @@ -21,7 +21,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &fec; diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi index aa29ca6561ee..3d04201b599c 100644 --- a/arch/arm/boot/dts/imx6sx.dtsi +++ b/arch/arm/boot/dts/imx6sx.dtsi @@ -22,7 +22,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { can0 = &flexcan1; diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi index ba2d8a277439..81c426e1b664 100644 --- a/arch/arm/boot/dts/imx6ul.dtsi +++ b/arch/arm/boot/dts/imx6ul.dtsi @@ -22,7 +22,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { ethernet0 = &fec1; diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi index 9aa2bb998552..ec6292c5b2e4 100644 --- a/arch/arm/boot/dts/imx7s.dtsi +++ b/arch/arm/boot/dts/imx7s.dtsi @@ -58,7 +58,7 @@ * Also for U-Boot there must be a pre-existing /memory node. */ chosen {}; - memory { device_type = "memory"; reg = <0 0>; }; + memory { device_type = "memory"; }; aliases { gpio0 = &gpio1; -- cgit v1.3-14-g43fede From ad00e080eb756b7823c81789dd65f1b75c702d6d Mon Sep 17 00:00:00 2001 From: Marco Franchi Date: Wed, 24 Jan 2018 11:22:14 -0200 Subject: ARM: dts: imx: Add memory node unit name Fix the following warnings from dtc by adding the unit name to memory nodes: Warning (unit_address_vs_reg): Node /memory has a reg or ranges property, but no unit name Converted using the following command: perl -p0777i -e 's/memory \{\n\t\treg = \<0x+([0-9a-f])/memory\@$1$\0000000 \{\n\t\treg = <0x$1/m' `find ./arch/arm/boot/dts -name "imx*"` The files below were manually fixed: -imx1-ads.dts -imx1-apf9328.dts -imx6q-pistachio.dts Signed-off-by: Marco Franchi Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx1-ads.dts | 2 +- arch/arm/boot/dts/imx1-apf9328.dts | 2 +- arch/arm/boot/dts/imx23-evk.dts | 2 +- arch/arm/boot/dts/imx23-olinuxino.dts | 2 +- arch/arm/boot/dts/imx23-sansa.dts | 2 +- arch/arm/boot/dts/imx23-stmp378x_devb.dts | 2 +- arch/arm/boot/dts/imx23-xfi3.dts | 2 +- arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi | 2 +- arch/arm/boot/dts/imx25-karo-tx25.dts | 2 +- arch/arm/boot/dts/imx25-pdk.dts | 2 +- arch/arm/boot/dts/imx27-apf27.dts | 2 +- arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi | 2 +- arch/arm/boot/dts/imx27-pdk.dts | 2 +- arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi | 2 +- arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi | 2 +- arch/arm/boot/dts/imx28-apf28.dts | 2 +- arch/arm/boot/dts/imx28-apx4devkit.dts | 2 +- arch/arm/boot/dts/imx28-cfa10036.dts | 2 +- arch/arm/boot/dts/imx28-duckbill-2-485.dts | 2 +- arch/arm/boot/dts/imx28-duckbill-2-enocean.dts | 2 +- arch/arm/boot/dts/imx28-duckbill-2-spi.dts | 2 +- arch/arm/boot/dts/imx28-duckbill-2.dts | 2 +- arch/arm/boot/dts/imx28-duckbill.dts | 2 +- arch/arm/boot/dts/imx28-eukrea-mbmx283lc.dts | 2 +- arch/arm/boot/dts/imx28-eukrea-mbmx287lc.dts | 2 +- arch/arm/boot/dts/imx28-evk.dts | 2 +- arch/arm/boot/dts/imx28-m28.dtsi | 2 +- arch/arm/boot/dts/imx28-m28cu3.dts | 2 +- arch/arm/boot/dts/imx28-sps1.dts | 2 +- arch/arm/boot/dts/imx28-ts4600.dts | 2 +- arch/arm/boot/dts/imx31-bug.dts | 2 +- arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi | 2 +- arch/arm/boot/dts/imx35-pdk.dts | 2 +- arch/arm/boot/dts/imx50-evk.dts | 2 +- arch/arm/boot/dts/imx51-apf51.dts | 2 +- arch/arm/boot/dts/imx51-babbage.dts | 2 +- arch/arm/boot/dts/imx51-digi-connectcore-som.dtsi | 2 +- arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi | 2 +- arch/arm/boot/dts/imx51-ts4800.dts | 2 +- arch/arm/boot/dts/imx53-ard.dts | 2 +- arch/arm/boot/dts/imx53-cx9020.dts | 2 +- arch/arm/boot/dts/imx53-m53.dtsi | 2 +- arch/arm/boot/dts/imx53-qsb-common.dtsi | 2 +- arch/arm/boot/dts/imx53-smd.dts | 2 +- arch/arm/boot/dts/imx53-tqma53.dtsi | 2 +- arch/arm/boot/dts/imx53-usbarmory.dts | 2 +- arch/arm/boot/dts/imx6dl-apf6dev.dts | 2 +- arch/arm/boot/dts/imx6dl-aristainetos2_4.dts | 2 +- arch/arm/boot/dts/imx6dl-aristainetos2_7.dts | 2 +- arch/arm/boot/dts/imx6dl-aristainetos_4.dts | 2 +- arch/arm/boot/dts/imx6dl-aristainetos_7.dts | 2 +- arch/arm/boot/dts/imx6dl-phytec-pfla02.dtsi | 2 +- arch/arm/boot/dts/imx6dl-rex-basic.dts | 2 +- arch/arm/boot/dts/imx6dl-riotboard.dts | 2 +- arch/arm/boot/dts/imx6dl-wandboard-revb1.dts | 2 +- arch/arm/boot/dts/imx6dl-wandboard-revd1.dts | 2 +- arch/arm/boot/dts/imx6dl-wandboard.dts | 2 +- arch/arm/boot/dts/imx6q-apf6dev.dts | 2 +- arch/arm/boot/dts/imx6q-arm2.dts | 2 +- arch/arm/boot/dts/imx6q-ba16.dtsi | 2 +- arch/arm/boot/dts/imx6q-cm-fx6.dts | 2 +- arch/arm/boot/dts/imx6q-display5.dtsi | 2 +- arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts | 2 +- arch/arm/boot/dts/imx6q-evi.dts | 2 +- arch/arm/boot/dts/imx6q-gk802.dts | 2 +- arch/arm/boot/dts/imx6q-gw5400-a.dts | 2 +- arch/arm/boot/dts/imx6q-marsboard.dts | 2 +- arch/arm/boot/dts/imx6q-mccmon6.dts | 2 +- arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi | 2 +- arch/arm/boot/dts/imx6q-pistachio.dts | 2 +- arch/arm/boot/dts/imx6q-rex-pro.dts | 2 +- arch/arm/boot/dts/imx6q-sbc6x.dts | 2 +- arch/arm/boot/dts/imx6q-tbs2910.dts | 2 +- arch/arm/boot/dts/imx6q-wandboard-revb1.dts | 2 +- arch/arm/boot/dts/imx6q-wandboard-revd1.dts | 2 +- arch/arm/boot/dts/imx6q-wandboard.dts | 2 +- arch/arm/boot/dts/imx6qdl-gw51xx.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-gw52xx.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-gw53xx.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-gw54xx.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-gw551x.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-gw552x.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-gw553x.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-gw5903.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-gw5904.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-icore-rqs.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-icore.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-nit6xlite.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-nitrogen6_som2.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-sabrelite.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-sabresd.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-udoo.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-var-dart.dtsi | 2 +- arch/arm/boot/dts/imx6qp-wandboard-revd1.dts | 2 +- arch/arm/boot/dts/imx6sl-evk.dts | 2 +- arch/arm/boot/dts/imx6sl-warp.dts | 2 +- arch/arm/boot/dts/imx6sx-nitrogen6sx.dts | 2 +- arch/arm/boot/dts/imx6sx-sabreauto.dts | 2 +- arch/arm/boot/dts/imx6sx-sdb.dtsi | 2 +- arch/arm/boot/dts/imx6sx-softing-vining-2000.dts | 2 +- arch/arm/boot/dts/imx6sx-udoo-neo-basic.dts | 2 +- arch/arm/boot/dts/imx6sx-udoo-neo-extended.dts | 2 +- arch/arm/boot/dts/imx6sx-udoo-neo-full.dts | 2 +- arch/arm/boot/dts/imx6ul-14x14-evk.dts | 2 +- arch/arm/boot/dts/imx6ul-geam.dts | 2 +- arch/arm/boot/dts/imx6ul-isiot.dtsi | 2 +- arch/arm/boot/dts/imx6ul-litesom.dtsi | 2 +- arch/arm/boot/dts/imx6ul-opos6ul.dtsi | 2 +- arch/arm/boot/dts/imx6ul-pico-hobbit.dts | 2 +- arch/arm/boot/dts/imx7d-cl-som-imx7.dts | 2 +- arch/arm/boot/dts/imx7d-colibri-emmc.dtsi | 2 +- arch/arm/boot/dts/imx7d-colibri.dtsi | 2 +- arch/arm/boot/dts/imx7d-nitrogen7.dts | 2 +- arch/arm/boot/dts/imx7d-pico.dtsi | 2 +- arch/arm/boot/dts/imx7d-sdb.dts | 2 +- arch/arm/boot/dts/imx7s-colibri.dtsi | 2 +- arch/arm/boot/dts/imx7s-warp.dts | 2 +- 121 files changed, 121 insertions(+), 121 deletions(-) diff --git a/arch/arm/boot/dts/imx1-ads.dts b/arch/arm/boot/dts/imx1-ads.dts index 5ea28ee07cf4..6354e4c87313 100644 --- a/arch/arm/boot/dts/imx1-ads.dts +++ b/arch/arm/boot/dts/imx1-ads.dts @@ -20,7 +20,7 @@ stdout-path = &uart1; }; - memory { + memory@8000000 { reg = <0x08000000 0x04000000>; }; diff --git a/arch/arm/boot/dts/imx1-apf9328.dts b/arch/arm/boot/dts/imx1-apf9328.dts index e8b4b52c2418..11515c0cb195 100644 --- a/arch/arm/boot/dts/imx1-apf9328.dts +++ b/arch/arm/boot/dts/imx1-apf9328.dts @@ -20,7 +20,7 @@ stdout-path = &uart1; }; - memory { + memory@8000000 { reg = <0x08000000 0x00800000>; }; }; diff --git a/arch/arm/boot/dts/imx23-evk.dts b/arch/arm/boot/dts/imx23-evk.dts index 57e29977ba06..9d92ece82560 100644 --- a/arch/arm/boot/dts/imx23-evk.dts +++ b/arch/arm/boot/dts/imx23-evk.dts @@ -16,7 +16,7 @@ model = "Freescale i.MX23 Evaluation Kit"; compatible = "fsl,imx23-evk", "fsl,imx23"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx23-olinuxino.dts b/arch/arm/boot/dts/imx23-olinuxino.dts index a8b1c53ebe46..e9351774c619 100644 --- a/arch/arm/boot/dts/imx23-olinuxino.dts +++ b/arch/arm/boot/dts/imx23-olinuxino.dts @@ -19,7 +19,7 @@ model = "i.MX23 Olinuxino Low Cost Board"; compatible = "olimex,imx23-olinuxino", "fsl,imx23"; - memory { + memory@40000000 { reg = <0x40000000 0x04000000>; }; diff --git a/arch/arm/boot/dts/imx23-sansa.dts b/arch/arm/boot/dts/imx23-sansa.dts index 221fd55e967e..67de7863ad79 100644 --- a/arch/arm/boot/dts/imx23-sansa.dts +++ b/arch/arm/boot/dts/imx23-sansa.dts @@ -49,7 +49,7 @@ model = "SanDisk Sansa Fuze+"; compatible = "sandisk,sansa_fuze_plus", "fsl,imx23"; - memory { + memory@40000000 { reg = <0x40000000 0x04000000>; }; diff --git a/arch/arm/boot/dts/imx23-stmp378x_devb.dts b/arch/arm/boot/dts/imx23-stmp378x_devb.dts index 455169e99d49..95c7b918f6d6 100644 --- a/arch/arm/boot/dts/imx23-stmp378x_devb.dts +++ b/arch/arm/boot/dts/imx23-stmp378x_devb.dts @@ -16,7 +16,7 @@ model = "Freescale STMP378x Development Board"; compatible = "fsl,stmp378x-devb", "fsl,imx23"; - memory { + memory@40000000 { reg = <0x40000000 0x04000000>; }; diff --git a/arch/arm/boot/dts/imx23-xfi3.dts b/arch/arm/boot/dts/imx23-xfi3.dts index 025cf949662d..9616e500b996 100644 --- a/arch/arm/boot/dts/imx23-xfi3.dts +++ b/arch/arm/boot/dts/imx23-xfi3.dts @@ -48,7 +48,7 @@ model = "Creative ZEN X-Fi3"; compatible = "creative,x-fi3", "fsl,imx23"; - memory { + memory@40000000 { reg = <0x40000000 0x04000000>; }; diff --git a/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi b/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi index d6f27641c0ef..e316fe08837a 100644 --- a/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi +++ b/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi @@ -17,7 +17,7 @@ model = "Eukrea CPUIMX25"; compatible = "eukrea,cpuimx25", "fsl,imx25"; - memory { + memory@80000000 { reg = <0x80000000 0x4000000>; /* 64M */ }; }; diff --git a/arch/arm/boot/dts/imx25-karo-tx25.dts b/arch/arm/boot/dts/imx25-karo-tx25.dts index 30a62d4be8d9..5cb6967866c0 100644 --- a/arch/arm/boot/dts/imx25-karo-tx25.dts +++ b/arch/arm/boot/dts/imx25-karo-tx25.dts @@ -36,7 +36,7 @@ }; }; - memory { + memory@80000000 { reg = <0x80000000 0x02000000 0x90000000 0x02000000>; }; }; diff --git a/arch/arm/boot/dts/imx25-pdk.dts b/arch/arm/boot/dts/imx25-pdk.dts index 2d15ce72d006..c7ef3ff980c5 100644 --- a/arch/arm/boot/dts/imx25-pdk.dts +++ b/arch/arm/boot/dts/imx25-pdk.dts @@ -18,7 +18,7 @@ model = "Freescale i.MX25 Product Development Kit"; compatible = "fsl,imx25-pdk", "fsl,imx25"; - memory { + memory@80000000 { reg = <0x80000000 0x4000000>; }; diff --git a/arch/arm/boot/dts/imx27-apf27.dts b/arch/arm/boot/dts/imx27-apf27.dts index 73aae4f5e539..66941cdbf244 100644 --- a/arch/arm/boot/dts/imx27-apf27.dts +++ b/arch/arm/boot/dts/imx27-apf27.dts @@ -19,7 +19,7 @@ model = "Armadeus Systems APF27 module"; compatible = "armadeus,imx27-apf27", "fsl,imx27"; - memory { + memory@a0000000 { reg = <0xa0000000 0x04000000>; }; diff --git a/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi b/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi index 2cf896c505f9..b2b6f84527a1 100644 --- a/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi +++ b/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi @@ -16,7 +16,7 @@ model = "Eukrea CPUIMX27"; compatible = "eukrea,cpuimx27", "fsl,imx27"; - memory { + memory@a0000000 { reg = <0xa0000000 0x04000000>; }; diff --git a/arch/arm/boot/dts/imx27-pdk.dts b/arch/arm/boot/dts/imx27-pdk.dts index 2a140c8ae6d2..924b90c9985d 100644 --- a/arch/arm/boot/dts/imx27-pdk.dts +++ b/arch/arm/boot/dts/imx27-pdk.dts @@ -16,7 +16,7 @@ model = "Freescale i.MX27 Product Development Kit"; compatible = "fsl,imx27-pdk", "fsl,imx27"; - memory { + memory@a0000000 { reg = <0xa0000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi b/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi index 0b8490b21a38..cbad7c88c58c 100644 --- a/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi +++ b/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi @@ -17,7 +17,7 @@ model = "Phytec pca100"; compatible = "phytec,imx27-pca100", "fsl,imx27"; - memory { + memory@a0000000 { reg = <0xa0000000 0x08000000>; /* 128MB */ }; }; diff --git a/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi b/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi index c9095b7654c6..ec466b4bfd41 100644 --- a/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi +++ b/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi @@ -16,7 +16,7 @@ model = "Phytec pcm038"; compatible = "phytec,imx27-pcm038", "fsl,imx27"; - memory { + memory@a0000000 { reg = <0xa0000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-apf28.dts b/arch/arm/boot/dts/imx28-apf28.dts index 070e59cbdd8b..bab78346fa9f 100644 --- a/arch/arm/boot/dts/imx28-apf28.dts +++ b/arch/arm/boot/dts/imx28-apf28.dts @@ -16,7 +16,7 @@ model = "Armadeus Systems APF28 module"; compatible = "armadeus,imx28-apf28", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-apx4devkit.dts b/arch/arm/boot/dts/imx28-apx4devkit.dts index 91456bc08d24..1416a4b4f599 100644 --- a/arch/arm/boot/dts/imx28-apx4devkit.dts +++ b/arch/arm/boot/dts/imx28-apx4devkit.dts @@ -6,7 +6,7 @@ model = "Bluegiga APX4 Development Kit"; compatible = "bluegiga,apx4devkit", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x04000000>; }; diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts index 570aa339a05e..e54f5aba7091 100644 --- a/arch/arm/boot/dts/imx28-cfa10036.dts +++ b/arch/arm/boot/dts/imx28-cfa10036.dts @@ -16,7 +16,7 @@ model = "Crystalfontz CFA-10036 Board"; compatible = "crystalfontz,cfa10036", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-duckbill-2-485.dts b/arch/arm/boot/dts/imx28-duckbill-2-485.dts index bd3fd470f9c3..97084e463d7c 100644 --- a/arch/arm/boot/dts/imx28-duckbill-2-485.dts +++ b/arch/arm/boot/dts/imx28-duckbill-2-485.dts @@ -19,7 +19,7 @@ model = "I2SE Duckbill 2 485"; compatible = "i2se,duckbill-2-485", "i2se,duckbill-2", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-duckbill-2-enocean.dts b/arch/arm/boot/dts/imx28-duckbill-2-enocean.dts index 4450047885eb..7f8d40a9c67e 100644 --- a/arch/arm/boot/dts/imx28-duckbill-2-enocean.dts +++ b/arch/arm/boot/dts/imx28-duckbill-2-enocean.dts @@ -20,7 +20,7 @@ model = "I2SE Duckbill 2 EnOcean"; compatible = "i2se,duckbill-2-enocean", "i2se,duckbill-2", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-duckbill-2-spi.dts b/arch/arm/boot/dts/imx28-duckbill-2-spi.dts index 927732efca98..13e7b134da9e 100644 --- a/arch/arm/boot/dts/imx28-duckbill-2-spi.dts +++ b/arch/arm/boot/dts/imx28-duckbill-2-spi.dts @@ -23,7 +23,7 @@ ethernet1 = &qca7000; }; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-duckbill-2.dts b/arch/arm/boot/dts/imx28-duckbill-2.dts index 7fa3d759505c..88556c93b00f 100644 --- a/arch/arm/boot/dts/imx28-duckbill-2.dts +++ b/arch/arm/boot/dts/imx28-duckbill-2.dts @@ -19,7 +19,7 @@ model = "I2SE Duckbill 2"; compatible = "i2se,duckbill-2", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-duckbill.dts b/arch/arm/boot/dts/imx28-duckbill.dts index 3e4385d4ed78..f286bfe699be 100644 --- a/arch/arm/boot/dts/imx28-duckbill.dts +++ b/arch/arm/boot/dts/imx28-duckbill.dts @@ -18,7 +18,7 @@ model = "I2SE Duckbill"; compatible = "i2se,duckbill", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-eukrea-mbmx283lc.dts b/arch/arm/boot/dts/imx28-eukrea-mbmx283lc.dts index 7c1572c5a4fb..b70f3349c350 100644 --- a/arch/arm/boot/dts/imx28-eukrea-mbmx283lc.dts +++ b/arch/arm/boot/dts/imx28-eukrea-mbmx283lc.dts @@ -23,7 +23,7 @@ model = "Eukrea Electromatique MBMX283LC"; compatible = "eukrea,mbmx283lc", "eukrea,mbmx28lc", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x04000000>; }; }; diff --git a/arch/arm/boot/dts/imx28-eukrea-mbmx287lc.dts b/arch/arm/boot/dts/imx28-eukrea-mbmx287lc.dts index b61fd61eb1c7..65efb78ac040 100644 --- a/arch/arm/boot/dts/imx28-eukrea-mbmx287lc.dts +++ b/arch/arm/boot/dts/imx28-eukrea-mbmx287lc.dts @@ -22,7 +22,7 @@ model = "Eukrea Electromatique MBMX287LC"; compatible = "eukrea,mbmx287lc", "eukrea,mbmx283lc", "eukrea,mbmx28lc", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; }; diff --git a/arch/arm/boot/dts/imx28-evk.dts b/arch/arm/boot/dts/imx28-evk.dts index 7f5b80402c54..8ccbcc153c50 100644 --- a/arch/arm/boot/dts/imx28-evk.dts +++ b/arch/arm/boot/dts/imx28-evk.dts @@ -16,7 +16,7 @@ model = "Freescale i.MX28 Evaluation Kit"; compatible = "fsl,imx28-evk", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-m28.dtsi b/arch/arm/boot/dts/imx28-m28.dtsi index a69856e41ba4..0ec415e1ff58 100644 --- a/arch/arm/boot/dts/imx28-m28.dtsi +++ b/arch/arm/boot/dts/imx28-m28.dtsi @@ -15,7 +15,7 @@ model = "Aries/DENX M28"; compatible = "aries,m28", "denx,m28", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-m28cu3.dts b/arch/arm/boot/dts/imx28-m28cu3.dts index 9d6c8fe28d74..3bb5ffc644d6 100644 --- a/arch/arm/boot/dts/imx28-m28cu3.dts +++ b/arch/arm/boot/dts/imx28-m28cu3.dts @@ -16,7 +16,7 @@ model = "MSR M28CU3"; compatible = "msr,m28cu3", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-sps1.dts b/arch/arm/boot/dts/imx28-sps1.dts index 0ce3cb8e7914..2393e83979e0 100644 --- a/arch/arm/boot/dts/imx28-sps1.dts +++ b/arch/arm/boot/dts/imx28-sps1.dts @@ -16,7 +16,7 @@ model = "SchulerControl GmbH, SC SPS 1"; compatible = "schulercontrol,imx28-sps1", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx28-ts4600.dts b/arch/arm/boot/dts/imx28-ts4600.dts index 1e391c9f1b7a..f8a09a8c2c36 100644 --- a/arch/arm/boot/dts/imx28-ts4600.dts +++ b/arch/arm/boot/dts/imx28-ts4600.dts @@ -19,7 +19,7 @@ model = "Technologic Systems i.MX28 TS-4600"; compatible = "technologic,imx28-ts4600", "fsl,imx28"; - memory { + memory@40000000 { reg = <0x40000000 0x10000000>; /* 256MB */ }; diff --git a/arch/arm/boot/dts/imx31-bug.dts b/arch/arm/boot/dts/imx31-bug.dts index ae6cebbed84b..6ee4ff8e4e8f 100644 --- a/arch/arm/boot/dts/imx31-bug.dts +++ b/arch/arm/boot/dts/imx31-bug.dts @@ -16,7 +16,7 @@ model = "Buglabs i.MX31 Bug 1.x"; compatible = "buglabs,imx31-bug", "fsl,imx31"; - memory { + memory@80000000 { reg = <0x80000000 0x8000000>; /* 128M */ }; }; diff --git a/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi b/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi index 9c2b715ab8bf..ba39d938f289 100644 --- a/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi +++ b/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi @@ -17,7 +17,7 @@ model = "Eukrea CPUIMX35"; compatible = "eukrea,cpuimx35", "fsl,imx35"; - memory { + memory@80000000 { reg = <0x80000000 0x8000000>; /* 128M */ }; }; diff --git a/arch/arm/boot/dts/imx35-pdk.dts b/arch/arm/boot/dts/imx35-pdk.dts index 9bb628f22502..646b1257bba2 100644 --- a/arch/arm/boot/dts/imx35-pdk.dts +++ b/arch/arm/boot/dts/imx35-pdk.dts @@ -17,7 +17,7 @@ model = "Freescale i.MX35 Product Development Kit"; compatible = "fsl,imx35-pdk", "fsl,imx35"; - memory { + memory@80000000 { reg = <0x80000000 0x8000000>, <0x90000000 0x8000000>; }; diff --git a/arch/arm/boot/dts/imx50-evk.dts b/arch/arm/boot/dts/imx50-evk.dts index 98b5faa06e27..23f1833e23fa 100644 --- a/arch/arm/boot/dts/imx50-evk.dts +++ b/arch/arm/boot/dts/imx50-evk.dts @@ -18,7 +18,7 @@ model = "Freescale i.MX50 Evaluation Kit"; compatible = "fsl,imx50-evk", "fsl,imx50"; - memory { + memory@70000000 { reg = <0x70000000 0x80000000>; }; }; diff --git a/arch/arm/boot/dts/imx51-apf51.dts b/arch/arm/boot/dts/imx51-apf51.dts index c83ac1600322..79d80036f74d 100644 --- a/arch/arm/boot/dts/imx51-apf51.dts +++ b/arch/arm/boot/dts/imx51-apf51.dts @@ -21,7 +21,7 @@ model = "Armadeus Systems APF51 module"; compatible = "armadeus,imx51-apf51", "fsl,imx51"; - memory { + memory@90000000 { reg = <0x90000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts index 4ac5ab614a7f..51b12114b0fe 100644 --- a/arch/arm/boot/dts/imx51-babbage.dts +++ b/arch/arm/boot/dts/imx51-babbage.dts @@ -21,7 +21,7 @@ stdout-path = &uart1; }; - memory { + memory@90000000 { reg = <0x90000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx51-digi-connectcore-som.dtsi b/arch/arm/boot/dts/imx51-digi-connectcore-som.dtsi index b821066a0d2a..5761a66e8a0d 100644 --- a/arch/arm/boot/dts/imx51-digi-connectcore-som.dtsi +++ b/arch/arm/boot/dts/imx51-digi-connectcore-som.dtsi @@ -16,7 +16,7 @@ model = "Digi ConnectCore CC(W)-MX51"; compatible = "digi,connectcore-ccxmx51-som", "fsl,imx51"; - memory { + memory@90000000 { reg = <0x90000000 0x08000000>; }; }; diff --git a/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi b/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi index 63164266af83..f8902a338e49 100644 --- a/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi +++ b/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi @@ -22,7 +22,7 @@ model = "Eukrea CPUIMX51"; compatible = "eukrea,cpuimx51", "fsl,imx51"; - memory { + memory@90000000 { reg = <0x90000000 0x10000000>; /* 256M */ }; }; diff --git a/arch/arm/boot/dts/imx51-ts4800.dts b/arch/arm/boot/dts/imx51-ts4800.dts index f59b02bae68d..39eb067904c3 100644 --- a/arch/arm/boot/dts/imx51-ts4800.dts +++ b/arch/arm/boot/dts/imx51-ts4800.dts @@ -17,7 +17,7 @@ stdout-path = &uart1; }; - memory { + memory@90000000 { reg = <0x90000000 0x10000000>; }; diff --git a/arch/arm/boot/dts/imx53-ard.dts b/arch/arm/boot/dts/imx53-ard.dts index 4486bc47d140..80fc00705d92 100644 --- a/arch/arm/boot/dts/imx53-ard.dts +++ b/arch/arm/boot/dts/imx53-ard.dts @@ -17,7 +17,7 @@ model = "Freescale i.MX53 Automotive Reference Design Board"; compatible = "fsl,imx53-ard", "fsl,imx53"; - memory { + memory@70000000 { reg = <0x70000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx53-cx9020.dts b/arch/arm/boot/dts/imx53-cx9020.dts index 5e67e43004e7..cf70ebc4399a 100644 --- a/arch/arm/boot/dts/imx53-cx9020.dts +++ b/arch/arm/boot/dts/imx53-cx9020.dts @@ -21,7 +21,7 @@ stdout-path = &uart2; }; - memory { + memory@70000000 { reg = <0x70000000 0x20000000>, <0xb0000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx53-m53.dtsi b/arch/arm/boot/dts/imx53-m53.dtsi index 7ce69c63510c..3da6dd5edb79 100644 --- a/arch/arm/boot/dts/imx53-m53.dtsi +++ b/arch/arm/boot/dts/imx53-m53.dtsi @@ -15,7 +15,7 @@ model = "Aries/DENX M53"; compatible = "aries,imx53-m53", "denx,imx53-m53", "fsl,imx53"; - memory { + memory@70000000 { reg = <0x70000000 0x20000000>, <0xb0000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx53-qsb-common.dtsi b/arch/arm/boot/dts/imx53-qsb-common.dtsi index 41a2e2a2b079..08ac46e564c3 100644 --- a/arch/arm/boot/dts/imx53-qsb-common.dtsi +++ b/arch/arm/boot/dts/imx53-qsb-common.dtsi @@ -17,7 +17,7 @@ stdout-path = &uart1; }; - memory { + memory@70000000 { reg = <0x70000000 0x20000000>, <0xb0000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx53-smd.dts b/arch/arm/boot/dts/imx53-smd.dts index 51f4a42a55e2..fd030128666c 100644 --- a/arch/arm/boot/dts/imx53-smd.dts +++ b/arch/arm/boot/dts/imx53-smd.dts @@ -17,7 +17,7 @@ model = "Freescale i.MX53 Smart Mobile Reference Design Board"; compatible = "fsl,imx53-smd", "fsl,imx53"; - memory { + memory@70000000 { reg = <0x70000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx53-tqma53.dtsi b/arch/arm/boot/dts/imx53-tqma53.dtsi index eecdc1c55eef..a72b8981fc3b 100644 --- a/arch/arm/boot/dts/imx53-tqma53.dtsi +++ b/arch/arm/boot/dts/imx53-tqma53.dtsi @@ -16,7 +16,7 @@ model = "TQ TQMa53"; compatible = "tq,tqma53", "fsl,imx53"; - memory { + memory@70000000 { reg = <0x70000000 0x40000000>; /* Up to 1GiB */ }; diff --git a/arch/arm/boot/dts/imx53-usbarmory.dts b/arch/arm/boot/dts/imx53-usbarmory.dts index 6782d7fc5961..f6268d0ded29 100644 --- a/arch/arm/boot/dts/imx53-usbarmory.dts +++ b/arch/arm/boot/dts/imx53-usbarmory.dts @@ -57,7 +57,7 @@ stdout-path = &uart1; }; - memory { + memory@70000000 { reg = <0x70000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6dl-apf6dev.dts b/arch/arm/boot/dts/imx6dl-apf6dev.dts index df26e542ab3a..4a7f86de6c39 100644 --- a/arch/arm/boot/dts/imx6dl-apf6dev.dts +++ b/arch/arm/boot/dts/imx6dl-apf6dev.dts @@ -54,7 +54,7 @@ model = "Armadeus APF6 Solo Module on APF6Dev Board"; compatible = "armadeus,imx6dl-apf6dev", "armadeus,imx6dl-apf6", "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x20000000>; }; }; diff --git a/arch/arm/boot/dts/imx6dl-aristainetos2_4.dts b/arch/arm/boot/dts/imx6dl-aristainetos2_4.dts index 5f0d196495d0..7128c76d5721 100644 --- a/arch/arm/boot/dts/imx6dl-aristainetos2_4.dts +++ b/arch/arm/boot/dts/imx6dl-aristainetos2_4.dts @@ -48,7 +48,7 @@ model = "aristainetos2 i.MX6 Dual Lite Board 4"; compatible = "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6dl-aristainetos2_7.dts b/arch/arm/boot/dts/imx6dl-aristainetos2_7.dts index 805b1318b7f7..240f3661469f 100644 --- a/arch/arm/boot/dts/imx6dl-aristainetos2_7.dts +++ b/arch/arm/boot/dts/imx6dl-aristainetos2_7.dts @@ -48,7 +48,7 @@ model = "aristainetos2 i.MX6 Dual Lite Board 7"; compatible = "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6dl-aristainetos_4.dts b/arch/arm/boot/dts/imx6dl-aristainetos_4.dts index 3c9f4af9e9ff..ad7733662fe5 100644 --- a/arch/arm/boot/dts/imx6dl-aristainetos_4.dts +++ b/arch/arm/boot/dts/imx6dl-aristainetos_4.dts @@ -27,7 +27,7 @@ status = "okay"; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6dl-aristainetos_7.dts b/arch/arm/boot/dts/imx6dl-aristainetos_7.dts index 96cd835ccbf6..64ed84e3c512 100644 --- a/arch/arm/boot/dts/imx6dl-aristainetos_7.dts +++ b/arch/arm/boot/dts/imx6dl-aristainetos_7.dts @@ -16,7 +16,7 @@ model = "aristainetos i.MX6 Dual Lite Board 7"; compatible = "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6dl-phytec-pfla02.dtsi b/arch/arm/boot/dts/imx6dl-phytec-pfla02.dtsi index 964bc2ad3c5d..7d9888937f12 100644 --- a/arch/arm/boot/dts/imx6dl-phytec-pfla02.dtsi +++ b/arch/arm/boot/dts/imx6dl-phytec-pfla02.dtsi @@ -16,7 +16,7 @@ model = "Phytec phyFLEX-i.MX6 DualLite/Solo"; compatible = "phytec,imx6dl-pfla02", "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x20000000>; }; }; diff --git a/arch/arm/boot/dts/imx6dl-rex-basic.dts b/arch/arm/boot/dts/imx6dl-rex-basic.dts index c3a14a4330a2..3fb7f4ee2496 100644 --- a/arch/arm/boot/dts/imx6dl-rex-basic.dts +++ b/arch/arm/boot/dts/imx6dl-rex-basic.dts @@ -16,7 +16,7 @@ model = "Rex Basic i.MX6 Dual Lite Board"; compatible = "rex,imx6dl-rex-basic", "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x20000000>; }; }; diff --git a/arch/arm/boot/dts/imx6dl-riotboard.dts b/arch/arm/boot/dts/imx6dl-riotboard.dts index 23e108204e1e..2e98c92adff7 100644 --- a/arch/arm/boot/dts/imx6dl-riotboard.dts +++ b/arch/arm/boot/dts/imx6dl-riotboard.dts @@ -15,7 +15,7 @@ model = "RIoTboard i.MX6S"; compatible = "riot,imx6s-riotboard", "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6dl-wandboard-revb1.dts b/arch/arm/boot/dts/imx6dl-wandboard-revb1.dts index 8c314eee4fdd..5727fa48cfd5 100644 --- a/arch/arm/boot/dts/imx6dl-wandboard-revb1.dts +++ b/arch/arm/boot/dts/imx6dl-wandboard-revb1.dts @@ -16,7 +16,7 @@ model = "Wandboard i.MX6 Dual Lite Board rev B1"; compatible = "wand,imx6dl-wandboard", "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; }; diff --git a/arch/arm/boot/dts/imx6dl-wandboard-revd1.dts b/arch/arm/boot/dts/imx6dl-wandboard-revd1.dts index aa4d4faaaec4..a72c07db7dda 100644 --- a/arch/arm/boot/dts/imx6dl-wandboard-revd1.dts +++ b/arch/arm/boot/dts/imx6dl-wandboard-revd1.dts @@ -16,7 +16,7 @@ model = "Wandboard i.MX6 Dual Lite Board revD1"; compatible = "wand,imx6dl-wandboard", "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; }; diff --git a/arch/arm/boot/dts/imx6dl-wandboard.dts b/arch/arm/boot/dts/imx6dl-wandboard.dts index bbb616723097..a09f274cd1f4 100644 --- a/arch/arm/boot/dts/imx6dl-wandboard.dts +++ b/arch/arm/boot/dts/imx6dl-wandboard.dts @@ -16,7 +16,7 @@ model = "Wandboard i.MX6 Dual Lite Board"; compatible = "wand,imx6dl-wandboard", "fsl,imx6dl"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; }; diff --git a/arch/arm/boot/dts/imx6q-apf6dev.dts b/arch/arm/boot/dts/imx6q-apf6dev.dts index 4e4de821d9e5..5e72f81cdf8b 100644 --- a/arch/arm/boot/dts/imx6q-apf6dev.dts +++ b/arch/arm/boot/dts/imx6q-apf6dev.dts @@ -54,7 +54,7 @@ model = "Armadeus APF6 Quad / Dual Module on APF6Dev Board"; compatible = "armadeus,imx6q-apf6dev", "armadeus,imx6q-apf6", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; }; diff --git a/arch/arm/boot/dts/imx6q-arm2.dts b/arch/arm/boot/dts/imx6q-arm2.dts index 4989d0bff10f..953a5b5a8ea4 100644 --- a/arch/arm/boot/dts/imx6q-arm2.dts +++ b/arch/arm/boot/dts/imx6q-arm2.dts @@ -18,7 +18,7 @@ model = "Freescale i.MX6 Quad Armadillo2 Board"; compatible = "fsl,imx6q-arm2", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6q-ba16.dtsi b/arch/arm/boot/dts/imx6q-ba16.dtsi index 5fcb0372d58b..bf4bdb385de9 100644 --- a/arch/arm/boot/dts/imx6q-ba16.dtsi +++ b/arch/arm/boot/dts/imx6q-ba16.dtsi @@ -46,7 +46,7 @@ #include / { - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index bc7587c383f6..65ef4cacbc71 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -50,7 +50,7 @@ model = "CompuLab CM-FX6"; compatible = "compulab,cm-fx6", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6q-display5.dtsi b/arch/arm/boot/dts/imx6q-display5.dtsi index 09085fde3341..85232c7c36a0 100644 --- a/arch/arm/boot/dts/imx6q-display5.dtsi +++ b/arch/arm/boot/dts/imx6q-display5.dtsi @@ -47,7 +47,7 @@ model = "Liebherr (LWN) display5 i.MX6 Quad Board"; compatible = "lwn,display5", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts b/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts index f0316ea96898..b3c6a4a7897d 100644 --- a/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts +++ b/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts @@ -29,7 +29,7 @@ stmpe-i2c1 = &stmpe2; }; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6q-evi.dts b/arch/arm/boot/dts/imx6q-evi.dts index e0aea782c666..fcd257bc5ac3 100644 --- a/arch/arm/boot/dts/imx6q-evi.dts +++ b/arch/arm/boot/dts/imx6q-evi.dts @@ -50,7 +50,7 @@ model = "Uniwest Evi"; compatible = "uniwest,imx6q-evi", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6q-gk802.dts b/arch/arm/boot/dts/imx6q-gk802.dts index b715deb4ea46..0be375611382 100644 --- a/arch/arm/boot/dts/imx6q-gk802.dts +++ b/arch/arm/boot/dts/imx6q-gk802.dts @@ -18,7 +18,7 @@ stdout-path = &uart4; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6q-gw5400-a.dts b/arch/arm/boot/dts/imx6q-gw5400-a.dts index 29adaa7c72f8..a8f70b4266ef 100644 --- a/arch/arm/boot/dts/imx6q-gw5400-a.dts +++ b/arch/arm/boot/dts/imx6q-gw5400-a.dts @@ -60,7 +60,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6q-marsboard.dts b/arch/arm/boot/dts/imx6q-marsboard.dts index 432291bedcf1..dd763f205819 100644 --- a/arch/arm/boot/dts/imx6q-marsboard.dts +++ b/arch/arm/boot/dts/imx6q-marsboard.dts @@ -47,7 +47,7 @@ model = "Embest MarS Board i.MX6Dual"; compatible = "embest,imx6q-marsboard", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6q-mccmon6.dts b/arch/arm/boot/dts/imx6q-mccmon6.dts index cab36f48d5f1..b7e9f38cec72 100644 --- a/arch/arm/boot/dts/imx6q-mccmon6.dts +++ b/arch/arm/boot/dts/imx6q-mccmon6.dts @@ -19,7 +19,7 @@ model = "Liebherr (LWN) monitor6 i.MX6 Quad Board"; compatible = "lwn,mccmon6", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi b/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi index cd20d0a948de..fad858c30fe9 100644 --- a/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi +++ b/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi @@ -16,7 +16,7 @@ model = "Phytec phyFLEX-i.MX6 Quad"; compatible = "phytec,imx6q-pfla02", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; }; diff --git a/arch/arm/boot/dts/imx6q-pistachio.dts b/arch/arm/boot/dts/imx6q-pistachio.dts index 1effb58f304c..bd57b3b74db7 100644 --- a/arch/arm/boot/dts/imx6q-pistachio.dts +++ b/arch/arm/boot/dts/imx6q-pistachio.dts @@ -56,7 +56,7 @@ stdout-path = &uart4; }; - memory: memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6q-rex-pro.dts b/arch/arm/boot/dts/imx6q-rex-pro.dts index 90ea61ae04e9..d6cae73b1927 100644 --- a/arch/arm/boot/dts/imx6q-rex-pro.dts +++ b/arch/arm/boot/dts/imx6q-rex-pro.dts @@ -16,7 +16,7 @@ model = "Rex Pro i.MX6 Quad Board"; compatible = "rex,imx6q-rex-pro", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; }; diff --git a/arch/arm/boot/dts/imx6q-sbc6x.dts b/arch/arm/boot/dts/imx6q-sbc6x.dts index 255733063ea4..b7aa2f0b9f53 100644 --- a/arch/arm/boot/dts/imx6q-sbc6x.dts +++ b/arch/arm/boot/dts/imx6q-sbc6x.dts @@ -12,7 +12,7 @@ model = "MicroSys sbc6x board"; compatible = "microsys,sbc6x", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; }; diff --git a/arch/arm/boot/dts/imx6q-tbs2910.dts b/arch/arm/boot/dts/imx6q-tbs2910.dts index a3cd7afac20a..505cba776a2d 100644 --- a/arch/arm/boot/dts/imx6q-tbs2910.dts +++ b/arch/arm/boot/dts/imx6q-tbs2910.dts @@ -59,7 +59,7 @@ stdout-path = &uart1; }; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6q-wandboard-revb1.dts b/arch/arm/boot/dts/imx6q-wandboard-revb1.dts index 9207d80f9cfb..b763352cddae 100644 --- a/arch/arm/boot/dts/imx6q-wandboard-revb1.dts +++ b/arch/arm/boot/dts/imx6q-wandboard-revb1.dts @@ -16,7 +16,7 @@ model = "Wandboard i.MX6 Quad Board rev B1"; compatible = "wand,imx6q-wandboard", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; }; diff --git a/arch/arm/boot/dts/imx6q-wandboard-revd1.dts b/arch/arm/boot/dts/imx6q-wandboard-revd1.dts index e87ddb168669..8691fab21058 100644 --- a/arch/arm/boot/dts/imx6q-wandboard-revd1.dts +++ b/arch/arm/boot/dts/imx6q-wandboard-revd1.dts @@ -16,7 +16,7 @@ model = "Wandboard i.MX6 Quad Board revD1"; compatible = "wand,imx6q-wandboard", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; }; diff --git a/arch/arm/boot/dts/imx6q-wandboard.dts b/arch/arm/boot/dts/imx6q-wandboard.dts index 4a8a6ee13e9f..2a3d98c1489a 100644 --- a/arch/arm/boot/dts/imx6q-wandboard.dts +++ b/arch/arm/boot/dts/imx6q-wandboard.dts @@ -16,7 +16,7 @@ model = "Wandboard i.MX6 Quad Board"; compatible = "wand,imx6q-wandboard", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi index dea8fc43c692..17a7b9c083d0 100644 --- a/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi @@ -44,7 +44,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi index 363a44394dad..b8044681006c 100644 --- a/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi @@ -59,7 +59,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi index c75385c0cad0..629908fbaa32 100644 --- a/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi @@ -59,7 +59,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi index eab75f3dbaf3..a1a6fb5541e1 100644 --- a/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi @@ -59,7 +59,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw551x.dtsi b/arch/arm/boot/dts/imx6qdl-gw551x.dtsi index 30d4662d4480..4e21b3849394 100644 --- a/arch/arm/boot/dts/imx6qdl-gw551x.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw551x.dtsi @@ -74,7 +74,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw552x.dtsi b/arch/arm/boot/dts/imx6qdl-gw552x.dtsi index c67c10605070..81dae5b5bc87 100644 --- a/arch/arm/boot/dts/imx6qdl-gw552x.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw552x.dtsi @@ -51,7 +51,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw553x.dtsi b/arch/arm/boot/dts/imx6qdl-gw553x.dtsi index 1a0faa1a14c8..c5d95e8d2e09 100644 --- a/arch/arm/boot/dts/imx6qdl-gw553x.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw553x.dtsi @@ -80,7 +80,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw5903.dtsi b/arch/arm/boot/dts/imx6qdl-gw5903.dtsi index 444425153fc7..368132274a91 100644 --- a/arch/arm/boot/dts/imx6qdl-gw5903.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw5903.dtsi @@ -83,7 +83,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw5904.dtsi b/arch/arm/boot/dts/imx6qdl-gw5904.dtsi index fd4b68be9fe9..58124adfd65b 100644 --- a/arch/arm/boot/dts/imx6qdl-gw5904.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw5904.dtsi @@ -93,7 +93,7 @@ }; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-icore-rqs.dtsi b/arch/arm/boot/dts/imx6qdl-icore-rqs.dtsi index b6220d62f6de..49daa2b7892d 100644 --- a/arch/arm/boot/dts/imx6qdl-icore-rqs.dtsi +++ b/arch/arm/boot/dts/imx6qdl-icore-rqs.dtsi @@ -44,7 +44,7 @@ #include / { - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-icore.dtsi b/arch/arm/boot/dts/imx6qdl-icore.dtsi index a1b469c142f1..b3a463a5908b 100644 --- a/arch/arm/boot/dts/imx6qdl-icore.dtsi +++ b/arch/arm/boot/dts/imx6qdl-icore.dtsi @@ -45,7 +45,7 @@ #include / { - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-nit6xlite.dtsi b/arch/arm/boot/dts/imx6qdl-nit6xlite.dtsi index 4cc4e23cf99c..aab088f318e8 100644 --- a/arch/arm/boot/dts/imx6qdl-nit6xlite.dtsi +++ b/arch/arm/boot/dts/imx6qdl-nit6xlite.dtsi @@ -46,7 +46,7 @@ stdout-path = &uart2; }; - memory { + memory@10000000 { reg = <0x10000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi index fd05f7caa472..87ca6ead4098 100644 --- a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi +++ b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi @@ -46,7 +46,7 @@ stdout-path = &uart2; }; - memory { + memory@10000000 { reg = <0x10000000 0xF0000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-nitrogen6_som2.dtsi b/arch/arm/boot/dts/imx6qdl-nitrogen6_som2.dtsi index 40942d6b94b3..f5b763d39285 100644 --- a/arch/arm/boot/dts/imx6qdl-nitrogen6_som2.dtsi +++ b/arch/arm/boot/dts/imx6qdl-nitrogen6_som2.dtsi @@ -46,7 +46,7 @@ stdout-path = &uart2; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi b/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi index 919b6b7619a4..596866b0a0d2 100644 --- a/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi +++ b/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi @@ -48,7 +48,7 @@ stdout-path = &uart2; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi b/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi index d81b0078a100..c58f3443d55d 100644 --- a/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi +++ b/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi @@ -15,7 +15,7 @@ model = "Phytec phyFLEX-i.MX6 Quad"; compatible = "phytec,imx6q-pfla02", "fsl,imx6q"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi index 82d6ccb46982..54b0139e978d 100644 --- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi @@ -13,7 +13,7 @@ #include / { - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi index 35de7adc997b..18b65052553d 100644 --- a/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi @@ -49,7 +49,7 @@ stdout-path = &uart2; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi index 0a50705b9c18..f019f9900369 100644 --- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi @@ -19,7 +19,7 @@ stdout-path = &uart1; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-udoo.dtsi b/arch/arm/boot/dts/imx6qdl-udoo.dtsi index 4161b7d4323a..906387915dc5 100644 --- a/arch/arm/boot/dts/imx6qdl-udoo.dtsi +++ b/arch/arm/boot/dts/imx6qdl-udoo.dtsi @@ -35,7 +35,7 @@ pinctrl-names = "default"; }; - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qdl-var-dart.dtsi b/arch/arm/boot/dts/imx6qdl-var-dart.dtsi index 421d6f527609..38080c1dfaec 100644 --- a/arch/arm/boot/dts/imx6qdl-var-dart.dtsi +++ b/arch/arm/boot/dts/imx6qdl-var-dart.dtsi @@ -10,7 +10,7 @@ #include / { - memory { + memory@10000000 { reg = <0x10000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6qp-wandboard-revd1.dts b/arch/arm/boot/dts/imx6qp-wandboard-revd1.dts index f7badd82ce8a..907ba0c74ba6 100644 --- a/arch/arm/boot/dts/imx6qp-wandboard-revd1.dts +++ b/arch/arm/boot/dts/imx6qp-wandboard-revd1.dts @@ -16,7 +16,7 @@ model = "Wandboard i.MX6 QuadPlus Board revD1"; compatible = "wand,imx6qp-wandboard", "fsl,imx6qp"; - memory { + memory@10000000 { reg = <0x10000000 0x80000000>; }; }; diff --git a/arch/arm/boot/dts/imx6sl-evk.dts b/arch/arm/boot/dts/imx6sl-evk.dts index 2844ab541759..37e792fdc160 100644 --- a/arch/arm/boot/dts/imx6sl-evk.dts +++ b/arch/arm/boot/dts/imx6sl-evk.dts @@ -16,7 +16,7 @@ model = "Freescale i.MX6 SoloLite EVK Board"; compatible = "fsl,imx6sl-evk", "fsl,imx6sl"; - memory { + memory@80000000 { reg = <0x80000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6sl-warp.dts b/arch/arm/boot/dts/imx6sl-warp.dts index 72c7745f51d3..404e602e6781 100644 --- a/arch/arm/boot/dts/imx6sl-warp.dts +++ b/arch/arm/boot/dts/imx6sl-warp.dts @@ -54,7 +54,7 @@ model = "WaRP Board"; compatible = "warp,imx6sl-warp", "fsl,imx6sl"; - memory { + memory@80000000 { reg = <0x80000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts b/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts index f9d40ee14982..b58f770c40d9 100644 --- a/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts +++ b/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts @@ -52,7 +52,7 @@ t_lcd = &t_lcd; }; - memory { + memory@80000000 { reg = <0x80000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6sx-sabreauto.dts b/arch/arm/boot/dts/imx6sx-sabreauto.dts index 240a2864d044..72da5acf35a2 100644 --- a/arch/arm/boot/dts/imx6sx-sabreauto.dts +++ b/arch/arm/boot/dts/imx6sx-sabreauto.dts @@ -14,7 +14,7 @@ model = "Freescale i.MX6 SoloX Sabre Auto Board"; compatible = "fsl,imx6sx-sabreauto", "fsl,imx6sx"; - memory { + memory@80000000 { reg = <0x80000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx6sx-sdb.dtsi b/arch/arm/boot/dts/imx6sx-sdb.dtsi index d35aa858f9db..f8f31872fa14 100644 --- a/arch/arm/boot/dts/imx6sx-sdb.dtsi +++ b/arch/arm/boot/dts/imx6sx-sdb.dtsi @@ -20,7 +20,7 @@ stdout-path = &uart1; }; - memory { + memory@80000000 { reg = <0x80000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6sx-softing-vining-2000.dts b/arch/arm/boot/dts/imx6sx-softing-vining-2000.dts index 4d8c6521845f..252175b59247 100644 --- a/arch/arm/boot/dts/imx6sx-softing-vining-2000.dts +++ b/arch/arm/boot/dts/imx6sx-softing-vining-2000.dts @@ -20,7 +20,7 @@ stdout-path = &uart1; }; - memory { + memory@80000000 { reg = <0x80000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx6sx-udoo-neo-basic.dts b/arch/arm/boot/dts/imx6sx-udoo-neo-basic.dts index 0c1fc1a8f913..40ccdf43dffc 100644 --- a/arch/arm/boot/dts/imx6sx-udoo-neo-basic.dts +++ b/arch/arm/boot/dts/imx6sx-udoo-neo-basic.dts @@ -48,7 +48,7 @@ model = "UDOO Neo Basic"; compatible = "udoo,neobasic", "fsl,imx6sx"; - memory { + memory@80000000 { reg = <0x80000000 0x20000000>; }; }; diff --git a/arch/arm/boot/dts/imx6sx-udoo-neo-extended.dts b/arch/arm/boot/dts/imx6sx-udoo-neo-extended.dts index 5d6c2274ee2b..42bfc8f8f7f6 100644 --- a/arch/arm/boot/dts/imx6sx-udoo-neo-extended.dts +++ b/arch/arm/boot/dts/imx6sx-udoo-neo-extended.dts @@ -48,7 +48,7 @@ model = "UDOO Neo Extended"; compatible = "udoo,neoextended", "fsl,imx6sx"; - memory { + memory@80000000 { reg = <0x80000000 0x40000000>; }; }; diff --git a/arch/arm/boot/dts/imx6sx-udoo-neo-full.dts b/arch/arm/boot/dts/imx6sx-udoo-neo-full.dts index 653ceb29e28b..c84c877f09d4 100644 --- a/arch/arm/boot/dts/imx6sx-udoo-neo-full.dts +++ b/arch/arm/boot/dts/imx6sx-udoo-neo-full.dts @@ -48,7 +48,7 @@ model = "UDOO Neo Full"; compatible = "udoo,neofull", "fsl,imx6sx"; - memory { + memory@80000000 { reg = <0x80000000 0x40000000>; }; }; diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dts b/arch/arm/boot/dts/imx6ul-14x14-evk.dts index cb33baa28282..5db83297f9d6 100644 --- a/arch/arm/boot/dts/imx6ul-14x14-evk.dts +++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dts @@ -18,7 +18,7 @@ stdout-path = &uart1; }; - memory { + memory@80000000 { reg = <0x80000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6ul-geam.dts b/arch/arm/boot/dts/imx6ul-geam.dts index 571eea7f1c6b..0dbbd78c867f 100644 --- a/arch/arm/boot/dts/imx6ul-geam.dts +++ b/arch/arm/boot/dts/imx6ul-geam.dts @@ -50,7 +50,7 @@ model = "Engicam GEAM6UL Starter Kit"; compatible = "engicam,imx6ul-geam", "fsl,imx6ul"; - memory { + memory@80000000 { reg = <0x80000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/imx6ul-isiot.dtsi b/arch/arm/boot/dts/imx6ul-isiot.dtsi index 950fb28b630a..b0ecebb512b3 100644 --- a/arch/arm/boot/dts/imx6ul-isiot.dtsi +++ b/arch/arm/boot/dts/imx6ul-isiot.dtsi @@ -45,7 +45,7 @@ #include "imx6ul.dtsi" / { - memory { + memory@80000000 { reg = <0x80000000 0x20000000>; }; diff --git a/arch/arm/boot/dts/imx6ul-litesom.dtsi b/arch/arm/boot/dts/imx6ul-litesom.dtsi index 039721d3dcb4..8f775f6974d1 100644 --- a/arch/arm/boot/dts/imx6ul-litesom.dtsi +++ b/arch/arm/boot/dts/imx6ul-litesom.dtsi @@ -47,7 +47,7 @@ model = "Grinn i.MX6UL liteSOM"; compatible = "grinn,imx6ul-litesom", "fsl,imx6ul"; - memory { + memory@80000000 { reg = <0x80000000 0x20000000>; }; }; diff --git a/arch/arm/boot/dts/imx6ul-opos6ul.dtsi b/arch/arm/boot/dts/imx6ul-opos6ul.dtsi index aec5ccce0321..a031bee311df 100644 --- a/arch/arm/boot/dts/imx6ul-opos6ul.dtsi +++ b/arch/arm/boot/dts/imx6ul-opos6ul.dtsi @@ -48,7 +48,7 @@ #include "imx6ul.dtsi" / { - memory { + memory@80000000 { reg = <0x80000000 0>; /* will be filled by U-Boot */ }; diff --git a/arch/arm/boot/dts/imx6ul-pico-hobbit.dts b/arch/arm/boot/dts/imx6ul-pico-hobbit.dts index 3bf26ebd4df9..47682b8c023c 100644 --- a/arch/arm/boot/dts/imx6ul-pico-hobbit.dts +++ b/arch/arm/boot/dts/imx6ul-pico-hobbit.dts @@ -51,7 +51,7 @@ model = "Technexion Pico i.MX6UL Board"; compatible = "technexion,imx6ul-pico-hobbit", "fsl,imx6ul"; - memory { + memory@80000000 { reg = <0x80000000 0x10000000>; }; diff --git a/arch/arm/boot/dts/imx7d-cl-som-imx7.dts b/arch/arm/boot/dts/imx7d-cl-som-imx7.dts index ae45af1ad062..c1a3d623f67d 100644 --- a/arch/arm/boot/dts/imx7d-cl-som-imx7.dts +++ b/arch/arm/boot/dts/imx7d-cl-som-imx7.dts @@ -18,7 +18,7 @@ model = "CompuLab CL-SOM-iMX7"; compatible = "compulab,cl-som-imx7", "fsl,imx7d"; - memory { + memory@80000000 { reg = <0x80000000 0x10000000>; /* 256 MB - minimal configuration */ }; diff --git a/arch/arm/boot/dts/imx7d-colibri-emmc.dtsi b/arch/arm/boot/dts/imx7d-colibri-emmc.dtsi index 9b63b9c89e4b..04d24ee17b14 100644 --- a/arch/arm/boot/dts/imx7d-colibri-emmc.dtsi +++ b/arch/arm/boot/dts/imx7d-colibri-emmc.dtsi @@ -7,7 +7,7 @@ #include "imx7-colibri.dtsi" / { - memory { + memory@80000000 { reg = <0x80000000 0x40000000>; }; }; diff --git a/arch/arm/boot/dts/imx7d-colibri.dtsi b/arch/arm/boot/dts/imx7d-colibri.dtsi index 6f2bb70c1fbd..d9f8fb69511b 100644 --- a/arch/arm/boot/dts/imx7d-colibri.dtsi +++ b/arch/arm/boot/dts/imx7d-colibri.dtsi @@ -44,7 +44,7 @@ #include "imx7-colibri.dtsi" / { - memory { + memory@80000000 { reg = <0x80000000 0x20000000>; }; }; diff --git a/arch/arm/boot/dts/imx7d-nitrogen7.dts b/arch/arm/boot/dts/imx7d-nitrogen7.dts index 2b05898bb3f6..52167298984d 100644 --- a/arch/arm/boot/dts/imx7d-nitrogen7.dts +++ b/arch/arm/boot/dts/imx7d-nitrogen7.dts @@ -53,7 +53,7 @@ t_lcd = &t_lcd; }; - memory { + memory@80000000 { reg = <0x80000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/imx7d-pico.dtsi b/arch/arm/boot/dts/imx7d-pico.dtsi index e307462a48ec..21973eb55671 100644 --- a/arch/arm/boot/dts/imx7d-pico.dtsi +++ b/arch/arm/boot/dts/imx7d-pico.dtsi @@ -48,7 +48,7 @@ model = "Technexion Pico i.MX7D Board"; compatible = "technexion,imx7d-pico", "fsl,imx7d"; - memory { + memory@80000000 { reg = <0x80000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts index a20c942c46b8..7f241afc15ea 100644 --- a/arch/arm/boot/dts/imx7d-sdb.dts +++ b/arch/arm/boot/dts/imx7d-sdb.dts @@ -48,7 +48,7 @@ model = "Freescale i.MX7 SabreSD Board"; compatible = "fsl,imx7d-sdb", "fsl,imx7d"; - memory { + memory@80000000 { reg = <0x80000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/imx7s-colibri.dtsi b/arch/arm/boot/dts/imx7s-colibri.dtsi index b81013455b21..fe8344cee864 100644 --- a/arch/arm/boot/dts/imx7s-colibri.dtsi +++ b/arch/arm/boot/dts/imx7s-colibri.dtsi @@ -44,7 +44,7 @@ #include "imx7-colibri.dtsi" / { - memory { + memory@80000000 { reg = <0x80000000 0x10000000>; }; }; diff --git a/arch/arm/boot/dts/imx7s-warp.dts b/arch/arm/boot/dts/imx7s-warp.dts index 1606713bf66c..8a30b148534d 100644 --- a/arch/arm/boot/dts/imx7s-warp.dts +++ b/arch/arm/boot/dts/imx7s-warp.dts @@ -50,7 +50,7 @@ model = "Warp i.MX7 Board"; compatible = "warp,imx7s-warp", "fsl,imx7s"; - memory { + memory@80000000 { reg = <0x80000000 0x20000000>; }; -- cgit v1.3-14-g43fede From 55071b0aaa6540beb268cc507b087bf0d92b3fbc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2018 22:28:23 +0100 Subject: ARM: dts: use 'atmel' as at24 manufacturer for imx6qdl-zii-rdu2 Using 'at' as the part of the compatible string is now deprecated. Use a correct string: 'atmel,'. Signed-off-by: Bartosz Golaszewski Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi b/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi index 928f35800da5..f81ae0cfc6ac 100644 --- a/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi +++ b/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi @@ -498,7 +498,7 @@ }; eeprom@54 { - compatible = "at,24c128"; + compatible = "atmel,24c128"; reg = <0x54>; }; -- cgit v1.3-14-g43fede From c37c2089ff1a7500112853240dda81466798124b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2018 22:31:16 +0100 Subject: ARM: dts: use 'atmel' as at24 manufacturer for imx6qdl-rex Using 'at' as the part of the compatible string is now deprecated. Use a correct string: 'atmel,'. Signed-off-by: Bartosz Golaszewski Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6qdl-rex.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6qdl-rex.dtsi b/arch/arm/boot/dts/imx6qdl-rex.dtsi index 6e9549ff11da..039e3b8306c4 100644 --- a/arch/arm/boot/dts/imx6qdl-rex.dtsi +++ b/arch/arm/boot/dts/imx6qdl-rex.dtsi @@ -137,7 +137,7 @@ status = "okay"; eeprom@57 { - compatible = "at,24c02"; + compatible = "atmel,24c02"; reg = <0x57>; }; }; -- cgit v1.3-14-g43fede From 7e9c1a4fb4efbc855f0e1b272d39c59293f163aa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2018 22:31:48 +0100 Subject: ARM: dts: fix the at24 compatible string in imx6q-h100 Using 'at24' as fallback is now deprecated - use the full 'atmel,' string. Signed-off-by: Bartosz Golaszewski Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6q-h100.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-h100.dts b/arch/arm/boot/dts/imx6q-h100.dts index db0e738b5391..dd24b465a4e5 100644 --- a/arch/arm/boot/dts/imx6q-h100.dts +++ b/arch/arm/boot/dts/imx6q-h100.dts @@ -166,7 +166,7 @@ status = "okay"; eeprom: 24c02@51 { - compatible = "microchip,24c02", "at24"; + compatible = "microchip,24c02", "atmel,24c02"; reg = <0x51>; }; -- cgit v1.3-14-g43fede From d1bf7b4468bc88a190efc396c10d27981119b175 Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Wed, 31 Jan 2018 22:35:43 +0100 Subject: ARM: dts: imx25-pinfunc: Use consistent naming for eSDHC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This file had several naming inconsistencies for eSDHC: - the instances were named sometimes SDn, sometimes SDHCn, whereas they are named ESDHCn in the reference manual, e.g.: MX25_PAD_SD1_CMD__SD1_CMD MX25_PAD_D15__SDHC1_DAT7 - the data ports were named sometimes DATAn, sometimes DATn like in the reference manual, e.g.: MX25_PAD_SD1_DATA0__SD1_DATA0 MX25_PAD_D15__SDHC1_DAT7 - in one case, the clock port was named DAT_CLK instead of CLK: MX25_PAD_CSI_D7__SDHC2_DAT_CLK This change: - introduces new definitions using the naming from the reference manual, - keeps definitions using the legacy naming in order not to break compatibility for out-of-tree users (they can be removed later), - updates the in-tree files that were using the legacy naming. Signed-off-by: Benoît Thébaudeau Acked-by: Uwe Kleine-König Reviewed-by: Fabio Estevam Reviewed-by: Rob Herring Signed-off-by: Shawn Guo --- .../boot/dts/imx25-eukrea-mbimxsd25-baseboard.dts | 12 ++-- arch/arm/boot/dts/imx25-pdk.dts | 12 ++-- arch/arm/boot/dts/imx25-pinfunc.h | 72 ++++++++++++++-------- 3 files changed, 60 insertions(+), 36 deletions(-) diff --git a/arch/arm/boot/dts/imx25-eukrea-mbimxsd25-baseboard.dts b/arch/arm/boot/dts/imx25-eukrea-mbimxsd25-baseboard.dts index 0f053721d80f..6273a1f243ed 100644 --- a/arch/arm/boot/dts/imx25-eukrea-mbimxsd25-baseboard.dts +++ b/arch/arm/boot/dts/imx25-eukrea-mbimxsd25-baseboard.dts @@ -88,12 +88,12 @@ pinctrl_esdhc1: esdhc1grp { fsl,pins = < - MX25_PAD_SD1_CMD__SD1_CMD 0x400000c0 - MX25_PAD_SD1_CLK__SD1_CLK 0x400000c0 - MX25_PAD_SD1_DATA0__SD1_DATA0 0x400000c0 - MX25_PAD_SD1_DATA1__SD1_DATA1 0x400000c0 - MX25_PAD_SD1_DATA2__SD1_DATA2 0x400000c0 - MX25_PAD_SD1_DATA3__SD1_DATA3 0x400000c0 + MX25_PAD_SD1_CMD__ESDHC1_CMD 0x400000c0 + MX25_PAD_SD1_CLK__ESDHC1_CLK 0x400000c0 + MX25_PAD_SD1_DATA0__ESDHC1_DAT0 0x400000c0 + MX25_PAD_SD1_DATA1__ESDHC1_DAT1 0x400000c0 + MX25_PAD_SD1_DATA2__ESDHC1_DAT2 0x400000c0 + MX25_PAD_SD1_DATA3__ESDHC1_DAT3 0x400000c0 >; }; diff --git a/arch/arm/boot/dts/imx25-pdk.dts b/arch/arm/boot/dts/imx25-pdk.dts index c7ef3ff980c5..7f9bd052b84e 100644 --- a/arch/arm/boot/dts/imx25-pdk.dts +++ b/arch/arm/boot/dts/imx25-pdk.dts @@ -165,12 +165,12 @@ pinctrl_esdhc1: esdhc1grp { fsl,pins = < - MX25_PAD_SD1_CMD__SD1_CMD 0x80000000 - MX25_PAD_SD1_CLK__SD1_CLK 0x80000000 - MX25_PAD_SD1_DATA0__SD1_DATA0 0x80000000 - MX25_PAD_SD1_DATA1__SD1_DATA1 0x80000000 - MX25_PAD_SD1_DATA2__SD1_DATA2 0x80000000 - MX25_PAD_SD1_DATA3__SD1_DATA3 0x80000000 + MX25_PAD_SD1_CMD__ESDHC1_CMD 0x80000000 + MX25_PAD_SD1_CLK__ESDHC1_CLK 0x80000000 + MX25_PAD_SD1_DATA0__ESDHC1_DAT0 0x80000000 + MX25_PAD_SD1_DATA1__ESDHC1_DAT1 0x80000000 + MX25_PAD_SD1_DATA2__ESDHC1_DAT2 0x80000000 + MX25_PAD_SD1_DATA3__ESDHC1_DAT3 0x80000000 MX25_PAD_A14__GPIO_2_0 0x80000000 MX25_PAD_A15__GPIO_2_1 0x80000000 >; diff --git a/arch/arm/boot/dts/imx25-pinfunc.h b/arch/arm/boot/dts/imx25-pinfunc.h index 6c63dca1b9b8..2915c65a13c9 100644 --- a/arch/arm/boot/dts/imx25-pinfunc.h +++ b/arch/arm/boot/dts/imx25-pinfunc.h @@ -151,21 +151,21 @@ #define MX25_PAD_D15__D15 0x088 0x280 0x000 0x00 0x000 #define MX25_PAD_D15__LD16 0x088 0x280 0x000 0x01 0x000 #define MX25_PAD_D15__GPIO_4_5 0x088 0x280 0x000 0x05 0x000 -#define MX25_PAD_D15__SDHC1_DAT7 0x088 0x280 0x4d8 0x06 0x000 +#define MX25_PAD_D15__ESDHC1_DAT7 0x088 0x280 0x4d8 0x06 0x000 #define MX25_PAD_D14__D14 0x08c 0x284 0x000 0x00 0x000 #define MX25_PAD_D14__LD17 0x08c 0x284 0x000 0x01 0x000 #define MX25_PAD_D14__GPIO_4_6 0x08c 0x284 0x000 0x05 0x000 -#define MX25_PAD_D14__SDHC1_DAT6 0x08c 0x284 0x4d4 0x06 0x000 +#define MX25_PAD_D14__ESDHC1_DAT6 0x08c 0x284 0x4d4 0x06 0x000 #define MX25_PAD_D13__D13 0x090 0x288 0x000 0x00 0x000 #define MX25_PAD_D13__LD18 0x090 0x288 0x000 0x01 0x000 #define MX25_PAD_D13__GPIO_4_7 0x090 0x288 0x000 0x05 0x000 -#define MX25_PAD_D13__SDHC1_DAT5 0x090 0x288 0x4d0 0x06 0x000 +#define MX25_PAD_D13__ESDHC1_DAT5 0x090 0x288 0x4d0 0x06 0x000 #define MX25_PAD_D12__D12 0x094 0x28c 0x000 0x00 0x000 #define MX25_PAD_D12__GPIO_4_8 0x094 0x28c 0x000 0x05 0x000 -#define MX25_PAD_D12__SDHC1_DAT4 0x094 0x28c 0x4cc 0x06 0x000 +#define MX25_PAD_D12__ESDHC1_DAT4 0x094 0x28c 0x4cc 0x06 0x000 #define MX25_PAD_D11__D11 0x098 0x290 0x000 0x00 0x000 #define MX25_PAD_D11__GPIO_4_9 0x098 0x290 0x000 0x05 0x000 @@ -236,12 +236,12 @@ #define MX25_PAD_LD8__LD8 0x0e8 0x2e0 0x000 0x00 0x000 #define MX25_PAD_LD8__UART4_RXD 0x0e8 0x2e0 0x570 0x02 0x000 #define MX25_PAD_LD8__FEC_TX_ERR 0x0e8 0x2e0 0x000 0x05 0x000 -#define MX25_PAD_LD8__SDHC2_CMD 0x0e8 0x2e0 0x4e0 0x06 0x000 +#define MX25_PAD_LD8__ESDHC2_CMD 0x0e8 0x2e0 0x4e0 0x06 0x000 #define MX25_PAD_LD9__LD9 0x0ec 0x2e4 0x000 0x00 0x000 #define MX25_PAD_LD9__UART4_TXD 0x0ec 0x2e4 0x000 0x02 0x000 #define MX25_PAD_LD9__FEC_COL 0x0ec 0x2e4 0x504 0x05 0x001 -#define MX25_PAD_LD9__SDHC2_CLK 0x0ec 0x2e4 0x4dc 0x06 0x000 +#define MX25_PAD_LD9__ESDHC2_CLK 0x0ec 0x2e4 0x4dc 0x06 0x000 #define MX25_PAD_LD10__LD10 0x0f0 0x2e8 0x000 0x00 0x000 #define MX25_PAD_LD10__UART4_RTS 0x0f0 0x2e8 0x56c 0x02 0x000 @@ -250,7 +250,7 @@ #define MX25_PAD_LD11__LD11 0x0f4 0x2ec 0x000 0x00 0x000 #define MX25_PAD_LD11__UART4_CTS 0x0f4 0x2ec 0x000 0x02 0x000 #define MX25_PAD_LD11__FEC_RDATA2 0x0f4 0x2ec 0x50c 0x05 0x001 -#define MX25_PAD_LD11__SDHC2_DAT1 0x0f4 0x2ec 0x4e8 0x06 0x000 +#define MX25_PAD_LD11__ESDHC2_DAT1 0x0f4 0x2ec 0x4e8 0x06 0x000 #define MX25_PAD_LD12__LD12 0x0f8 0x2f0 0x000 0x00 0x000 #define MX25_PAD_LD12__CSPI2_MOSI 0x0f8 0x2f0 0x4a0 0x02 0x000 @@ -316,12 +316,12 @@ #define MX25_PAD_CSI_D5__CSPI3_RDY 0x12c 0x324 0x000 0x07 0x000 #define MX25_PAD_CSI_D6__CSI_D6 0x130 0x328 0x000 0x00 0x000 -#define MX25_PAD_CSI_D6__SDHC2_CMD 0x130 0x328 0x4e0 0x02 0x001 +#define MX25_PAD_CSI_D6__ESDHC2_CMD 0x130 0x328 0x4e0 0x02 0x001 #define MX25_PAD_CSI_D6__SIM1_PD0 0x130 0x328 0x000 0x04 0x000 #define MX25_PAD_CSI_D6__GPIO_1_31 0x130 0x328 0x000 0x05 0x000 #define MX25_PAD_CSI_D7__CSI_D7 0x134 0x32c 0x000 0x00 0x000 -#define MX25_PAD_CSI_D7__SDHC2_DAT_CLK 0x134 0x32C 0x4dc 0x02 0x001 +#define MX25_PAD_CSI_D7__ESDHC2_CLK 0x134 0x32C 0x4dc 0x02 0x001 #define MX25_PAD_CSI_D7__GPIO_1_6 0x134 0x32c 0x000 0x05 0x000 #define MX25_PAD_CSI_D8__CSI_D8 0x138 0x330 0x000 0x00 0x000 @@ -336,22 +336,22 @@ #define MX25_PAD_CSI_MCLK__CSI_MCLK 0x140 0x338 0x000 0x00 0x000 #define MX25_PAD_CSI_MCLK__AUD6_TXD 0x140 0x338 0x000 0x01 0x000 -#define MX25_PAD_CSI_MCLK__SDHC2_DAT0 0x140 0x338 0x4e4 0x02 0x001 +#define MX25_PAD_CSI_MCLK__ESDHC2_DAT0 0x140 0x338 0x4e4 0x02 0x001 #define MX25_PAD_CSI_MCLK__GPIO_1_8 0x140 0x338 0x000 0x05 0x000 #define MX25_PAD_CSI_VSYNC__CSI_VSYNC 0x144 0x33c 0x000 0x00 0x000 #define MX25_PAD_CSI_VSYNC__AUD6_RXD 0x144 0x33c 0x000 0x01 0x000 -#define MX25_PAD_CSI_VSYNC__SDHC2_DAT1 0x144 0x33c 0x4e8 0x02 0x001 +#define MX25_PAD_CSI_VSYNC__ESDHC2_DAT1 0x144 0x33c 0x4e8 0x02 0x001 #define MX25_PAD_CSI_VSYNC__GPIO_1_9 0x144 0x33c 0x000 0x05 0x000 #define MX25_PAD_CSI_HSYNC__CSI_HSYNC 0x148 0x340 0x000 0x00 0x000 #define MX25_PAD_CSI_HSYNC__AUD6_TXC 0x148 0x340 0x000 0x01 0x000 -#define MX25_PAD_CSI_HSYNC__SDHC2_DAT2 0x148 0x340 0x4ec 0x02 0x001 +#define MX25_PAD_CSI_HSYNC__ESDHC2_DAT2 0x148 0x340 0x4ec 0x02 0x001 #define MX25_PAD_CSI_HSYNC__GPIO_1_10 0x148 0x340 0x000 0x05 0x000 #define MX25_PAD_CSI_PIXCLK__CSI_PIXCLK 0x14c 0x344 0x000 0x00 0x000 #define MX25_PAD_CSI_PIXCLK__AUD6_TXFS 0x14c 0x344 0x000 0x01 0x000 -#define MX25_PAD_CSI_PIXCLK__SDHC2_DAT3 0x14c 0x344 0x4f0 0x02 0x001 +#define MX25_PAD_CSI_PIXCLK__ESDHC2_DAT3 0x14c 0x344 0x4f0 0x02 0x001 #define MX25_PAD_CSI_PIXCLK__GPIO_1_11 0x14c 0x344 0x000 0x05 0x000 #define MX25_PAD_I2C1_CLK__I2C1_CLK 0x150 0x348 0x000 0x00 0x000 @@ -419,37 +419,37 @@ #define MX25_PAD_UART2_CTS__GPIO_4_29 0x18c 0x384 0x000 0x05 0x000 /* - * Removing the SION bit from MX25_PAD_SD1_CMD__SD1_CMD breaks detecting an SD - * card. According to the i.MX25 reference manual (e.g. Figure 23-2 in IMX25RM - * Rev. 2 from 01/2011) this pin is bidirectional. So it seems to be a silicon - * bug that configuring the SD1_CMD function doesn't enable the input path for - * this pin. + * Removing the SION bit from MX25_PAD_SD1_CMD__ESDHC1_CMD breaks detecting an + * SD card. According to the i.MX25 reference manual (e.g. Figure 23-2 in + * IMX25RM Rev. 2 from 01/2011) this pin is bidirectional. So it seems to be a + * silicon bug that configuring the ESDHC1_CMD function doesn't enable the input + * path for this pin. * This might have side effects for other hardware units that are connected to * that pin and use the respective function as input. */ -#define MX25_PAD_SD1_CMD__SD1_CMD 0x190 0x388 0x000 0x10 0x000 +#define MX25_PAD_SD1_CMD__ESDHC1_CMD 0x190 0x388 0x000 0x10 0x000 #define MX25_PAD_SD1_CMD__CSPI2_MOSI 0x190 0x388 0x4a0 0x01 0x001 #define MX25_PAD_SD1_CMD__FEC_RDATA2 0x190 0x388 0x50c 0x02 0x002 #define MX25_PAD_SD1_CMD__GPIO_2_23 0x190 0x388 0x000 0x05 0x000 -#define MX25_PAD_SD1_CLK__SD1_CLK 0x194 0x38c 0x000 0x00 0x000 +#define MX25_PAD_SD1_CLK__ESDHC1_CLK 0x194 0x38c 0x000 0x00 0x000 #define MX25_PAD_SD1_CLK__CSPI2_MISO 0x194 0x38c 0x49c 0x01 0x001 #define MX25_PAD_SD1_CLK__FEC_RDATA3 0x194 0x38c 0x510 0x02 0x002 #define MX25_PAD_SD1_CLK__GPIO_2_24 0x194 0x38c 0x000 0x05 0x000 -#define MX25_PAD_SD1_DATA0__SD1_DATA0 0x198 0x390 0x000 0x00 0x000 +#define MX25_PAD_SD1_DATA0__ESDHC1_DAT0 0x198 0x390 0x000 0x00 0x000 #define MX25_PAD_SD1_DATA0__CSPI2_SCLK 0x198 0x390 0x494 0x01 0x001 #define MX25_PAD_SD1_DATA0__GPIO_2_25 0x198 0x390 0x000 0x05 0x000 -#define MX25_PAD_SD1_DATA1__SD1_DATA1 0x19c 0x394 0x000 0x00 0x000 +#define MX25_PAD_SD1_DATA1__ESDHC1_DAT1 0x19c 0x394 0x000 0x00 0x000 #define MX25_PAD_SD1_DATA1__AUD7_RXD 0x19c 0x394 0x478 0x03 0x000 #define MX25_PAD_SD1_DATA1__GPIO_2_26 0x19c 0x394 0x000 0x05 0x000 -#define MX25_PAD_SD1_DATA2__SD1_DATA2 0x1a0 0x398 0x000 0x00 0x000 +#define MX25_PAD_SD1_DATA2__ESDHC1_DAT2 0x1a0 0x398 0x000 0x00 0x000 #define MX25_PAD_SD1_DATA2__FEC_RX_CLK 0x1a0 0x398 0x514 0x02 0x002 #define MX25_PAD_SD1_DATA2__GPIO_2_27 0x1a0 0x398 0x000 0x05 0x000 -#define MX25_PAD_SD1_DATA3__SD1_DATA3 0x1a4 0x39c 0x000 0x00 0x000 +#define MX25_PAD_SD1_DATA3__ESDHC1_DAT3 0x1a4 0x39c 0x000 0x00 0x000 #define MX25_PAD_SD1_DATA3__FEC_CRS 0x1a4 0x39c 0x508 0x02 0x002 #define MX25_PAD_SD1_DATA3__GPIO_2_28 0x1a4 0x39c 0x000 0x05 0x000 @@ -601,4 +601,28 @@ #define MX25_PAD_BOOT_MODE1__BOOT_MODE1 0x228 0x000 0x000 0x00 0x000 #define MX25_PAD_BOOT_MODE1__GPIO_4_31 0x228 0x000 0x000 0x05 0x000 +/* + * Compatibility defines for out-of-tree users. You should update if you make + * use of one of them. + */ +#define MX25_PAD_D15__SDHC1_DAT7 MX25_PAD_D15__ESDHC1_DAT7 +#define MX25_PAD_D14__SDHC1_DAT6 MX25_PAD_D14__ESDHC1_DAT6 +#define MX25_PAD_D13__SDHC1_DAT5 MX25_PAD_D13__ESDHC1_DAT5 +#define MX25_PAD_D12__SDHC1_DAT4 MX25_PAD_D12__ESDHC1_DAT4 +#define MX25_PAD_LD8__SDHC2_CMD MX25_PAD_LD8__ESDHC2_CMD +#define MX25_PAD_LD9__SDHC2_CLK MX25_PAD_LD9__ESDHC2_CLK +#define MX25_PAD_LD11__SDHC2_DAT1 MX25_PAD_LD11__ESDHC2_DAT1 +#define MX25_PAD_CSI_D6__SDHC2_CMD MX25_PAD_CSI_D6__ESDHC2_CMD +#define MX25_PAD_CSI_D7__SDHC2_DAT_CLK MX25_PAD_CSI_D7__ESDHC2_CLK +#define MX25_PAD_CSI_MCLK__SDHC2_DAT0 MX25_PAD_CSI_MCLK__ESDHC2_DAT0 +#define MX25_PAD_CSI_VSYNC__SDHC2_DAT1 MX25_PAD_CSI_VSYNC__ESDHC2_DAT1 +#define MX25_PAD_CSI_HSYNC__SDHC2_DAT2 MX25_PAD_CSI_HSYNC__ESDHC2_DAT2 +#define MX25_PAD_CSI_PIXCLK__SDHC2_DAT3 MX25_PAD_CSI_PIXCLK__ESDHC2_DAT3 +#define MX25_PAD_SD1_CMD__SD1_CMD MX25_PAD_SD1_CMD__ESDHC1_CMD +#define MX25_PAD_SD1_CLK__SD1_CLK MX25_PAD_SD1_CLK__ESDHC1_CLK +#define MX25_PAD_SD1_DATA0__SD1_DATA0 MX25_PAD_SD1_DATA0__ESDHC1_DAT0 +#define MX25_PAD_SD1_DATA1__SD1_DATA1 MX25_PAD_SD1_DATA1__ESDHC1_DAT1 +#define MX25_PAD_SD1_DATA2__SD1_DATA2 MX25_PAD_SD1_DATA2__ESDHC1_DAT2 +#define MX25_PAD_SD1_DATA3__SD1_DATA3 MX25_PAD_SD1_DATA3__ESDHC1_DAT3 + #endif /* __DTS_IMX25_PINFUNC_H */ -- cgit v1.3-14-g43fede From ce3afb6aca353edc68f6aa331c823ec344a326c9 Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Wed, 31 Jan 2018 22:35:44 +0100 Subject: ARM: dts: imx25-pinfunc: Always set SION for eSDHC CMD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The eSDHC does not work properly if the SION bit is not set for the bidirectional CMD signal, whatever the eSDHC instance and the selected pad. Therefore, setting SION is mandatory for all eSDHC CMD ports. Do this for MX25_PAD_*__ESDHCn_CMD in imx25-pinfunc.h in order to enforce this behavior for all boards. This had already been done for eSDHC1, but not for eSDHC2. Also, define MX25_PAD_FEC_MDC__ESDHC2_CMD so that all the possible cases are covered from now on. Signed-off-by: Benoît Thébaudeau Reviewed-by: Fabio Estevam Acked-by: Uwe Kleine-König Reviewed-by: Rob Herring Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx25-pinfunc.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/arch/arm/boot/dts/imx25-pinfunc.h b/arch/arm/boot/dts/imx25-pinfunc.h index 2915c65a13c9..a4807062a90f 100644 --- a/arch/arm/boot/dts/imx25-pinfunc.h +++ b/arch/arm/boot/dts/imx25-pinfunc.h @@ -236,7 +236,8 @@ #define MX25_PAD_LD8__LD8 0x0e8 0x2e0 0x000 0x00 0x000 #define MX25_PAD_LD8__UART4_RXD 0x0e8 0x2e0 0x570 0x02 0x000 #define MX25_PAD_LD8__FEC_TX_ERR 0x0e8 0x2e0 0x000 0x05 0x000 -#define MX25_PAD_LD8__ESDHC2_CMD 0x0e8 0x2e0 0x4e0 0x06 0x000 +/* SION must be set; see the comment for MX25_PAD_SD1_CMD__ESDHC1_CMD. */ +#define MX25_PAD_LD8__ESDHC2_CMD 0x0e8 0x2e0 0x4e0 0x16 0x000 #define MX25_PAD_LD9__LD9 0x0ec 0x2e4 0x000 0x00 0x000 #define MX25_PAD_LD9__UART4_TXD 0x0ec 0x2e4 0x000 0x02 0x000 @@ -316,7 +317,8 @@ #define MX25_PAD_CSI_D5__CSPI3_RDY 0x12c 0x324 0x000 0x07 0x000 #define MX25_PAD_CSI_D6__CSI_D6 0x130 0x328 0x000 0x00 0x000 -#define MX25_PAD_CSI_D6__ESDHC2_CMD 0x130 0x328 0x4e0 0x02 0x001 +/* SION must be set; see the comment for MX25_PAD_SD1_CMD__ESDHC1_CMD. */ +#define MX25_PAD_CSI_D6__ESDHC2_CMD 0x130 0x328 0x4e0 0x12 0x001 #define MX25_PAD_CSI_D6__SIM1_PD0 0x130 0x328 0x000 0x04 0x000 #define MX25_PAD_CSI_D6__GPIO_1_31 0x130 0x328 0x000 0x05 0x000 @@ -419,11 +421,11 @@ #define MX25_PAD_UART2_CTS__GPIO_4_29 0x18c 0x384 0x000 0x05 0x000 /* - * Removing the SION bit from MX25_PAD_SD1_CMD__ESDHC1_CMD breaks detecting an - * SD card. According to the i.MX25 reference manual (e.g. Figure 23-2 in - * IMX25RM Rev. 2 from 01/2011) this pin is bidirectional. So it seems to be a - * silicon bug that configuring the ESDHC1_CMD function doesn't enable the input - * path for this pin. + * Removing the SION bit from MX25_PAD_*__ESDHCn_CMD breaks detecting an SD + * card. According to the i.MX25 reference manual (e.g. Figure 23-2 in IMX25RM + * Rev. 2 from 01/2011) this pin is bidirectional. So it seems to be a silicon + * bug that configuring the ESDHCn_CMD function doesn't enable the input path + * for this pin. * This might have side effects for other hardware units that are connected to * that pin and use the respective function as input. */ @@ -496,6 +498,8 @@ #define MX25_PAD_KPP_COL3__GPIO_3_4 0x1c4 0x3bc 0x000 0x05 0x000 #define MX25_PAD_FEC_MDC__FEC_MDC 0x1c8 0x3c0 0x000 0x00 0x000 +/* SION must be set; see the comment for MX25_PAD_SD1_CMD__ESDHC1_CMD. */ +#define MX25_PAD_FEC_MDC__ESDHC2_CMD 0x1c8 0x3c0 0x4e0 0x11 0x002 #define MX25_PAD_FEC_MDC__AUD4_TXD 0x1c8 0x3c0 0x464 0x02 0x001 #define MX25_PAD_FEC_MDC__GPIO_3_5 0x1c8 0x3c0 0x000 0x05 0x000 -- cgit v1.3-14-g43fede From 2bada7ac1fdcbf79a9689bd2ff65fa515ca7a31f Mon Sep 17 00:00:00 2001 From: Aapo Vienamo Date: Wed, 31 Jan 2018 14:34:07 +0000 Subject: ARM: dts: imx7d: cl-som-imx7: fix pinctrl_enet The missing last digit of the CONFIG values is added. Looks like a typo of some sort when comparing to the downstream dt. This fixes intermittent behavior behaviour of the ethernet controllers. Signed-off-by: Aapo Vienamo Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx7d-cl-som-imx7.dts | 52 ++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/arch/arm/boot/dts/imx7d-cl-som-imx7.dts b/arch/arm/boot/dts/imx7d-cl-som-imx7.dts index c1a3d623f67d..7f645683f53b 100644 --- a/arch/arm/boot/dts/imx7d-cl-som-imx7.dts +++ b/arch/arm/boot/dts/imx7d-cl-som-imx7.dts @@ -213,37 +213,37 @@ &iomuxc { pinctrl_enet1: enet1grp { fsl,pins = < - MX7D_PAD_SD2_CD_B__ENET1_MDIO 0x3 - MX7D_PAD_SD2_WP__ENET1_MDC 0x3 - MX7D_PAD_ENET1_RGMII_TXC__ENET1_RGMII_TXC 0x1 - MX7D_PAD_ENET1_RGMII_TD0__ENET1_RGMII_TD0 0x1 - MX7D_PAD_ENET1_RGMII_TD1__ENET1_RGMII_TD1 0x1 - MX7D_PAD_ENET1_RGMII_TD2__ENET1_RGMII_TD2 0x1 - MX7D_PAD_ENET1_RGMII_TD3__ENET1_RGMII_TD3 0x1 - MX7D_PAD_ENET1_RGMII_TX_CTL__ENET1_RGMII_TX_CTL 0x1 - MX7D_PAD_ENET1_RGMII_RXC__ENET1_RGMII_RXC 0x1 - MX7D_PAD_ENET1_RGMII_RD0__ENET1_RGMII_RD0 0x1 - MX7D_PAD_ENET1_RGMII_RD1__ENET1_RGMII_RD1 0x1 - MX7D_PAD_ENET1_RGMII_RD2__ENET1_RGMII_RD2 0x1 - MX7D_PAD_ENET1_RGMII_RD3__ENET1_RGMII_RD3 0x1 - MX7D_PAD_ENET1_RGMII_RX_CTL__ENET1_RGMII_RX_CTL 0x1 + MX7D_PAD_SD2_CD_B__ENET1_MDIO 0x30 + MX7D_PAD_SD2_WP__ENET1_MDC 0x30 + MX7D_PAD_ENET1_RGMII_TXC__ENET1_RGMII_TXC 0x11 + MX7D_PAD_ENET1_RGMII_TD0__ENET1_RGMII_TD0 0x11 + MX7D_PAD_ENET1_RGMII_TD1__ENET1_RGMII_TD1 0x11 + MX7D_PAD_ENET1_RGMII_TD2__ENET1_RGMII_TD2 0x11 + MX7D_PAD_ENET1_RGMII_TD3__ENET1_RGMII_TD3 0x11 + MX7D_PAD_ENET1_RGMII_TX_CTL__ENET1_RGMII_TX_CTL 0x11 + MX7D_PAD_ENET1_RGMII_RXC__ENET1_RGMII_RXC 0x11 + MX7D_PAD_ENET1_RGMII_RD0__ENET1_RGMII_RD0 0x11 + MX7D_PAD_ENET1_RGMII_RD1__ENET1_RGMII_RD1 0x11 + MX7D_PAD_ENET1_RGMII_RD2__ENET1_RGMII_RD2 0x11 + MX7D_PAD_ENET1_RGMII_RD3__ENET1_RGMII_RD3 0x11 + MX7D_PAD_ENET1_RGMII_RX_CTL__ENET1_RGMII_RX_CTL 0x11 >; }; pinctrl_enet2: enet2grp { fsl,pins = < - MX7D_PAD_EPDC_GDSP__ENET2_RGMII_TXC 0x1 - MX7D_PAD_EPDC_SDCE2__ENET2_RGMII_TD0 0x1 - MX7D_PAD_EPDC_SDCE3__ENET2_RGMII_TD1 0x1 - MX7D_PAD_EPDC_GDCLK__ENET2_RGMII_TD2 0x1 - MX7D_PAD_EPDC_GDOE__ENET2_RGMII_TD3 0x1 - MX7D_PAD_EPDC_GDRL__ENET2_RGMII_TX_CTL 0x1 - MX7D_PAD_EPDC_SDCE1__ENET2_RGMII_RXC 0x1 - MX7D_PAD_EPDC_SDCLK__ENET2_RGMII_RD0 0x1 - MX7D_PAD_EPDC_SDLE__ENET2_RGMII_RD1 0x1 - MX7D_PAD_EPDC_SDOE__ENET2_RGMII_RD2 0x1 - MX7D_PAD_EPDC_SDSHR__ENET2_RGMII_RD3 0x1 - MX7D_PAD_EPDC_SDCE0__ENET2_RGMII_RX_CTL 0x1 + MX7D_PAD_EPDC_GDSP__ENET2_RGMII_TXC 0x11 + MX7D_PAD_EPDC_SDCE2__ENET2_RGMII_TD0 0x11 + MX7D_PAD_EPDC_SDCE3__ENET2_RGMII_TD1 0x11 + MX7D_PAD_EPDC_GDCLK__ENET2_RGMII_TD2 0x11 + MX7D_PAD_EPDC_GDOE__ENET2_RGMII_TD3 0x11 + MX7D_PAD_EPDC_GDRL__ENET2_RGMII_TX_CTL 0x11 + MX7D_PAD_EPDC_SDCE1__ENET2_RGMII_RXC 0x11 + MX7D_PAD_EPDC_SDCLK__ENET2_RGMII_RD0 0x11 + MX7D_PAD_EPDC_SDLE__ENET2_RGMII_RD1 0x11 + MX7D_PAD_EPDC_SDOE__ENET2_RGMII_RD2 0x11 + MX7D_PAD_EPDC_SDSHR__ENET2_RGMII_RD3 0x11 + MX7D_PAD_EPDC_SDCE0__ENET2_RGMII_RX_CTL 0x11 >; }; -- cgit v1.3-14-g43fede From 7d926e118e0d937e9a78b3c7aa944bba10931df6 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 2 Feb 2018 21:26:39 -0200 Subject: ARM: dts: imx27-eukrea-cpuimx27: Put the clock node directly into root Put the UART clock node directly under root with a unique node name and drop unit-address in order to fix the following build warning with W=1: arch/arm/boot/dts/imx27-eukrea-mbimxsd27-baseboard.dtb: Warning (simple_bus_reg): Node /clocks/osc26m missing or empty reg/ranges property Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi b/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi index b2b6f84527a1..9c455dcbe6eb 100644 --- a/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi +++ b/arch/arm/boot/dts/imx27-eukrea-cpuimx27.dtsi @@ -20,17 +20,10 @@ reg = <0xa0000000 0x04000000>; }; - clocks { - #address-cells = <1>; - #size-cells = <0>; - compatible = "simple-bus"; - - clk14745600: clock@0 { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <14745600>; - reg = <0>; - }; + clk14745600: clk-uart { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <14745600>; }; }; -- cgit v1.3-14-g43fede From 10fff25906d84255b31027c8f4d3a31c8adbf6d9 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Mon, 5 Feb 2018 18:08:40 +0100 Subject: ARM: dts: imx6q-bx50v3: Enable secure-reg-access Enable secure debug enable register access for Bx50v3 devices to enable PMU and hardware counters for perf. Signed-off-by: Peter Senna Tschudin Signed-off-by: Sebastian Reichel Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6q-bx50v3.dtsi | 4 ++++ arch/arm/boot/dts/imx6qdl.dtsi | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-bx50v3.dtsi b/arch/arm/boot/dts/imx6q-bx50v3.dtsi index e8ad6a5e9370..990e411cbca0 100644 --- a/arch/arm/boot/dts/imx6q-bx50v3.dtsi +++ b/arch/arm/boot/dts/imx6q-bx50v3.dtsi @@ -353,6 +353,10 @@ }; }; +&pmu { + secure-reg-access; +}; + &usdhc2 { status = "disabled"; }; diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index 0c70ea888d32..c003e62bf290 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -143,7 +143,7 @@ }; }; - pmu { + pmu: pmu { compatible = "arm,cortex-a9-pmu"; interrupt-parent = <&gpc>; interrupts = <0 94 IRQ_TYPE_LEVEL_HIGH>; -- cgit v1.3-14-g43fede From 7d77b8505aa941f326fc2df519f15437f0b0a85b Mon Sep 17 00:00:00 2001 From: Lothar Waßmann Date: Thu, 8 Feb 2018 15:07:37 +0800 Subject: ARM: dts: imx6ull: fix the imx6ull-14x14-evk configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit imx6ull-14x14-evk.dts currently includes the imx6ul.dtsi file for an i.MX6ULL SoC which is plain wrong. Rename the current imx6ul-14x14-evk.dts to .dtsi and include it from imx6ul-14x14-evk.dts and imx6ull-14x14-evk.dts, so that both can include the appropriate SoC specific (imx6ul.dtsi/imx6ull.dtsi) file. Signed-off-by: Lothar Waßmann Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ul-14x14-evk.dts | 491 +------------------------------ arch/arm/boot/dts/imx6ul-14x14-evk.dtsi | 499 ++++++++++++++++++++++++++++++++ arch/arm/boot/dts/imx6ull-14x14-evk.dts | 5 +- 3 files changed, 504 insertions(+), 491 deletions(-) create mode 100644 arch/arm/boot/dts/imx6ul-14x14-evk.dtsi diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dts b/arch/arm/boot/dts/imx6ul-14x14-evk.dts index 5db83297f9d6..6d720b20e7ed 100644 --- a/arch/arm/boot/dts/imx6ul-14x14-evk.dts +++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dts @@ -9,498 +9,9 @@ /dts-v1/; #include "imx6ul.dtsi" +#include "imx6ul-14x14-evk.dtsi" / { model = "Freescale i.MX6 UltraLite 14x14 EVK Board"; compatible = "fsl,imx6ul-14x14-evk", "fsl,imx6ul"; - - chosen { - stdout-path = &uart1; - }; - - memory@80000000 { - reg = <0x80000000 0x20000000>; - }; - - backlight_display: backlight-display { - compatible = "pwm-backlight"; - pwms = <&pwm1 0 5000000>; - brightness-levels = <0 4 8 16 32 64 128 255>; - default-brightness-level = <6>; - status = "okay"; - }; - - - reg_sd1_vmmc: regulator-sd1-vmmc { - compatible = "regulator-fixed"; - regulator-name = "VSD_3V3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; - enable-active-high; - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "mx6ul-wm8960"; - simple-audio-card,format = "i2s"; - simple-audio-card,bitclock-master = <&dailink_master>; - simple-audio-card,frame-master = <&dailink_master>; - simple-audio-card,widgets = - "Microphone", "Mic Jack", - "Line", "Line In", - "Line", "Line Out", - "Speaker", "Speaker", - "Headphone", "Headphone Jack"; - simple-audio-card,routing = - "Headphone Jack", "HP_L", - "Headphone Jack", "HP_R", - "Speaker", "SPK_LP", - "Speaker", "SPK_LN", - "Speaker", "SPK_RP", - "Speaker", "SPK_RN", - "LINPUT1", "Mic Jack", - "LINPUT3", "Mic Jack", - "RINPUT1", "Mic Jack", - "RINPUT2", "Mic Jack"; - - simple-audio-card,cpu { - sound-dai = <&sai2>; - }; - - dailink_master: simple-audio-card,codec { - sound-dai = <&codec>; - clocks = <&clks IMX6UL_CLK_SAI2>; - }; - }; - - panel { - compatible = "innolux,at043tn24"; - backlight = <&backlight_display>; - - port { - panel_in: endpoint { - remote-endpoint = <&display_out>; - }; - }; - }; -}; - -&clks { - assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; - assigned-clock-rates = <786432000>; -}; - -&i2c2 { - clock_frequency = <100000>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c2>; - status = "okay"; - - codec: wm8960@1a { - #sound-dai-cells = <0>; - compatible = "wlf,wm8960"; - reg = <0x1a>; - wlf,shared-lrclk; - }; -}; - -&fec1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet1>; - phy-mode = "rmii"; - phy-handle = <ðphy0>; - status = "okay"; -}; - -&fec2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet2>; - phy-mode = "rmii"; - phy-handle = <ðphy1>; - status = "okay"; - - mdio { - #address-cells = <1>; - #size-cells = <0>; - - ethphy0: ethernet-phy@2 { - reg = <2>; - micrel,led-mode = <1>; - clocks = <&clks IMX6UL_CLK_ENET_REF>; - clock-names = "rmii-ref"; - }; - - ethphy1: ethernet-phy@1 { - reg = <1>; - micrel,led-mode = <1>; - clocks = <&clks IMX6UL_CLK_ENET2_REF>; - clock-names = "rmii-ref"; - }; - }; -}; - -&i2c1 { - clock-frequency = <100000>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c1>; - status = "okay"; - - mag3110@e { - compatible = "fsl,mag3110"; - reg = <0x0e>; - }; -}; - -&lcdif { - assigned-clocks = <&clks IMX6UL_CLK_LCDIF_PRE_SEL>; - assigned-clock-parents = <&clks IMX6UL_CLK_PLL5_VIDEO_DIV>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_lcdif_dat - &pinctrl_lcdif_ctrl>; - status = "okay"; - - port { - display_out: endpoint { - remote-endpoint = <&panel_in>; - }; - }; -}; - -&pwm1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pwm1>; - status = "okay"; -}; - -&qspi { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_qspi>; - status = "okay"; - - flash0: n25q256a@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "micron,n25q256a"; - spi-max-frequency = <29000000>; - reg = <0>; - }; -}; - -&sai2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_sai2>; - assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>, - <&clks IMX6UL_CLK_SAI2>; - assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; - assigned-clock-rates = <0>, <12288000>; - fsl,sai-mclk-direction-output; - status = "okay"; -}; - -&snvs_poweroff { - status = "okay"; -}; - -&tsc { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_tsc>; - xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; - measure-delay-time = <0xffff>; - pre-charge-time = <0xfff>; - status = "okay"; -}; - -&uart1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1>; - status = "okay"; -}; - -&uart2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2>; - uart-has-rtscts; - status = "okay"; -}; - -&usbotg1 { - dr_mode = "otg"; - status = "okay"; -}; - -&usbotg2 { - dr_mode = "host"; - disable-over-current; - status = "okay"; -}; - -&usbphy1 { - fsl,tx-d-cal = <106>; -}; - -&usbphy2 { - fsl,tx-d-cal = <106>; -}; - -&usdhc1 { - pinctrl-names = "default", "state_100mhz", "state_200mhz"; - pinctrl-0 = <&pinctrl_usdhc1>; - pinctrl-1 = <&pinctrl_usdhc1_100mhz>; - pinctrl-2 = <&pinctrl_usdhc1_200mhz>; - cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; - keep-power-in-suspend; - wakeup-source; - vmmc-supply = <®_sd1_vmmc>; - status = "okay"; -}; - -&usdhc2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc2>; - no-1-8-v; - keep-power-in-suspend; - wakeup-source; - status = "okay"; -}; - -&wdog1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_wdog>; - fsl,ext-reset-output; -}; - -&iomuxc { - pinctrl-names = "default"; - - pinctrl_csi1: csi1grp { - fsl,pins = < - MX6UL_PAD_CSI_MCLK__CSI_MCLK 0x1b088 - MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK 0x1b088 - MX6UL_PAD_CSI_VSYNC__CSI_VSYNC 0x1b088 - MX6UL_PAD_CSI_HSYNC__CSI_HSYNC 0x1b088 - MX6UL_PAD_CSI_DATA00__CSI_DATA02 0x1b088 - MX6UL_PAD_CSI_DATA01__CSI_DATA03 0x1b088 - MX6UL_PAD_CSI_DATA02__CSI_DATA04 0x1b088 - MX6UL_PAD_CSI_DATA03__CSI_DATA05 0x1b088 - MX6UL_PAD_CSI_DATA04__CSI_DATA06 0x1b088 - MX6UL_PAD_CSI_DATA05__CSI_DATA07 0x1b088 - MX6UL_PAD_CSI_DATA06__CSI_DATA08 0x1b088 - MX6UL_PAD_CSI_DATA07__CSI_DATA09 0x1b088 - >; - }; - - pinctrl_enet1: enet1grp { - fsl,pins = < - MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0 - MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0 - MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0 - MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0 - MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0 - MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0 - MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0 - MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031 - >; - }; - - pinctrl_enet2: enet2grp { - fsl,pins = < - MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0 - MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0 - MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0 - MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0 - MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0 - MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0 - MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0 - MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0 - MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0 - MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031 - >; - }; - - pinctrl_flexcan1: flexcan1grp{ - fsl,pins = < - MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x1b020 - MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x1b020 - >; - }; - - pinctrl_flexcan2: flexcan2grp{ - fsl,pins = < - MX6UL_PAD_UART2_RTS_B__FLEXCAN2_RX 0x1b020 - MX6UL_PAD_UART2_CTS_B__FLEXCAN2_TX 0x1b020 - >; - }; - - pinctrl_i2c1: i2c1grp { - fsl,pins = < - MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0 - MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0 - >; - }; - - pinctrl_i2c2: i2c2grp { - fsl,pins = < - MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0 - MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0 - >; - }; - - pinctrl_lcdif_dat: lcdifdatgrp { - fsl,pins = < - MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79 - MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79 - MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79 - MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79 - MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79 - MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79 - MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79 - MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79 - MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79 - MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79 - MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79 - MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79 - MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79 - MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79 - MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79 - MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79 - MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x79 - MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x79 - MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x79 - MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x79 - MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x79 - MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x79 - MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x79 - MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x79 - >; - }; - - pinctrl_lcdif_ctrl: lcdifctrlgrp { - fsl,pins = < - MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79 - MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79 - MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79 - MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79 - /* used for lcd reset */ - MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x79 - >; - }; - - pinctrl_qspi: qspigrp { - fsl,pins = < - MX6UL_PAD_NAND_WP_B__QSPI_A_SCLK 0x70a1 - MX6UL_PAD_NAND_READY_B__QSPI_A_DATA00 0x70a1 - MX6UL_PAD_NAND_CE0_B__QSPI_A_DATA01 0x70a1 - MX6UL_PAD_NAND_CE1_B__QSPI_A_DATA02 0x70a1 - MX6UL_PAD_NAND_CLE__QSPI_A_DATA03 0x70a1 - MX6UL_PAD_NAND_DQS__QSPI_A_SS0_B 0x70a1 - >; - }; - - pinctrl_sai2: sai2grp { - fsl,pins = < - MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK 0x17088 - MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC 0x17088 - MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x11088 - MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x11088 - MX6UL_PAD_JTAG_TMS__SAI2_MCLK 0x17088 - MX6UL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x17059 - >; - }; - - pinctrl_pwm1: pwm1grp { - fsl,pins = < - MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0 - >; - }; - - pinctrl_sim2: sim2grp { - fsl,pins = < - MX6UL_PAD_CSI_DATA03__SIM2_PORT1_PD 0xb808 - MX6UL_PAD_CSI_DATA04__SIM2_PORT1_CLK 0x31 - MX6UL_PAD_CSI_DATA05__SIM2_PORT1_RST_B 0xb808 - MX6UL_PAD_CSI_DATA06__SIM2_PORT1_SVEN 0xb808 - MX6UL_PAD_CSI_DATA07__SIM2_PORT1_TRXD 0xb809 - MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x3008 - >; - }; - - pinctrl_tsc: tscgrp { - fsl,pins = < - MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0 - MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0 - MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0 - MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0 - >; - }; - - pinctrl_uart1: uart1grp { - fsl,pins = < - MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1 - MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1 - >; - }; - - pinctrl_uart2: uart2grp { - fsl,pins = < - MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX 0x1b0b1 - MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX 0x1b0b1 - MX6UL_PAD_UART3_RX_DATA__UART2_DCE_RTS 0x1b0b1 - MX6UL_PAD_UART3_TX_DATA__UART2_DCE_CTS 0x1b0b1 - >; - }; - - pinctrl_usdhc1: usdhc1grp { - fsl,pins = < - MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059 - MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x10059 - MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059 - MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059 - MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059 - MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059 - MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */ - MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */ - MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */ - >; - }; - - pinctrl_usdhc1_100mhz: usdhc1grp100mhz { - fsl,pins = < - MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170b9 - MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100b9 - MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9 - MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9 - MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9 - MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9 - - >; - }; - - pinctrl_usdhc1_200mhz: usdhc1grp200mhz { - fsl,pins = < - MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170f9 - MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100f9 - MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9 - MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9 - MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9 - MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9 - >; - }; - - pinctrl_usdhc2: usdhc2grp { - fsl,pins = < - MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x17059 - MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x17059 - MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x17059 - MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x17059 - MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x17059 - MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x17059 - >; - }; - - pinctrl_wdog: wdoggrp { - fsl,pins = < - MX6UL_PAD_LCD_RESET__WDOG1_WDOG_ANY 0x30b0 - >; - }; }; diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi b/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi new file mode 100644 index 000000000000..32a07232c034 --- /dev/null +++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/ { + chosen { + stdout-path = &uart1; + }; + + memory@80000000 { + reg = <0x80000000 0x20000000>; + }; + + backlight_display: backlight-display { + compatible = "pwm-backlight"; + pwms = <&pwm1 0 5000000>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <6>; + status = "okay"; + }; + + + reg_sd1_vmmc: regulator-sd1-vmmc { + compatible = "regulator-fixed"; + regulator-name = "VSD_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "mx6ul-wm8960"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&dailink_master>; + simple-audio-card,frame-master = <&dailink_master>; + simple-audio-card,widgets = + "Microphone", "Mic Jack", + "Line", "Line In", + "Line", "Line Out", + "Speaker", "Speaker", + "Headphone", "Headphone Jack"; + simple-audio-card,routing = + "Headphone Jack", "HP_L", + "Headphone Jack", "HP_R", + "Speaker", "SPK_LP", + "Speaker", "SPK_LN", + "Speaker", "SPK_RP", + "Speaker", "SPK_RN", + "LINPUT1", "Mic Jack", + "LINPUT3", "Mic Jack", + "RINPUT1", "Mic Jack", + "RINPUT2", "Mic Jack"; + + simple-audio-card,cpu { + sound-dai = <&sai2>; + }; + + dailink_master: simple-audio-card,codec { + sound-dai = <&codec>; + clocks = <&clks IMX6UL_CLK_SAI2>; + }; + }; + + panel { + compatible = "innolux,at043tn24"; + backlight = <&backlight_display>; + + port { + panel_in: endpoint { + remote-endpoint = <&display_out>; + }; + }; + }; +}; + +&clks { + assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; + assigned-clock-rates = <786432000>; +}; + +&i2c2 { + clock_frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; + + codec: wm8960@1a { + #sound-dai-cells = <0>; + compatible = "wlf,wm8960"; + reg = <0x1a>; + wlf,shared-lrclk; + }; +}; + +&fec1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet1>; + phy-mode = "rmii"; + phy-handle = <ðphy0>; + status = "okay"; +}; + +&fec2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet2>; + phy-mode = "rmii"; + phy-handle = <ðphy1>; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethphy0: ethernet-phy@2 { + reg = <2>; + micrel,led-mode = <1>; + clocks = <&clks IMX6UL_CLK_ENET_REF>; + clock-names = "rmii-ref"; + }; + + ethphy1: ethernet-phy@1 { + reg = <1>; + micrel,led-mode = <1>; + clocks = <&clks IMX6UL_CLK_ENET2_REF>; + clock-names = "rmii-ref"; + }; + }; +}; + +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + mag3110@e { + compatible = "fsl,mag3110"; + reg = <0x0e>; + }; +}; + +&lcdif { + assigned-clocks = <&clks IMX6UL_CLK_LCDIF_PRE_SEL>; + assigned-clock-parents = <&clks IMX6UL_CLK_PLL5_VIDEO_DIV>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcdif_dat + &pinctrl_lcdif_ctrl>; + status = "okay"; + + port { + display_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; +}; + +&pwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm1>; + status = "okay"; +}; + +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi>; + status = "okay"; + + flash0: n25q256a@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "micron,n25q256a"; + spi-max-frequency = <29000000>; + reg = <0>; + }; +}; + +&sai2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai2>; + assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>, + <&clks IMX6UL_CLK_SAI2>; + assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; + assigned-clock-rates = <0>, <12288000>; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +&snvs_poweroff { + status = "okay"; +}; + +&tsc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tsc>; + xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; + measure-delay-time = <0xffff>; + pre-charge-time = <0xfff>; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + uart-has-rtscts; + status = "okay"; +}; + +&usbotg1 { + dr_mode = "otg"; + status = "okay"; +}; + +&usbotg2 { + dr_mode = "host"; + disable-over-current; + status = "okay"; +}; + +&usbphy1 { + fsl,tx-d-cal = <106>; +}; + +&usbphy2 { + fsl,tx-d-cal = <106>; +}; + +&usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; + keep-power-in-suspend; + wakeup-source; + vmmc-supply = <®_sd1_vmmc>; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc2>; + no-1-8-v; + keep-power-in-suspend; + wakeup-source; + status = "okay"; +}; + +&wdog1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wdog>; + fsl,ext-reset-output; +}; + +&iomuxc { + pinctrl-names = "default"; + + pinctrl_csi1: csi1grp { + fsl,pins = < + MX6UL_PAD_CSI_MCLK__CSI_MCLK 0x1b088 + MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK 0x1b088 + MX6UL_PAD_CSI_VSYNC__CSI_VSYNC 0x1b088 + MX6UL_PAD_CSI_HSYNC__CSI_HSYNC 0x1b088 + MX6UL_PAD_CSI_DATA00__CSI_DATA02 0x1b088 + MX6UL_PAD_CSI_DATA01__CSI_DATA03 0x1b088 + MX6UL_PAD_CSI_DATA02__CSI_DATA04 0x1b088 + MX6UL_PAD_CSI_DATA03__CSI_DATA05 0x1b088 + MX6UL_PAD_CSI_DATA04__CSI_DATA06 0x1b088 + MX6UL_PAD_CSI_DATA05__CSI_DATA07 0x1b088 + MX6UL_PAD_CSI_DATA06__CSI_DATA08 0x1b088 + MX6UL_PAD_CSI_DATA07__CSI_DATA09 0x1b088 + >; + }; + + pinctrl_enet1: enet1grp { + fsl,pins = < + MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0 + MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0 + MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0 + MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0 + MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0 + MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0 + MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0 + MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031 + >; + }; + + pinctrl_enet2: enet2grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0 + MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0 + MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0 + MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0 + MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0 + MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0 + MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0 + MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0 + MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0 + MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031 + >; + }; + + pinctrl_flexcan1: flexcan1grp{ + fsl,pins = < + MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x1b020 + MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x1b020 + >; + }; + + pinctrl_flexcan2: flexcan2grp{ + fsl,pins = < + MX6UL_PAD_UART2_RTS_B__FLEXCAN2_RX 0x1b020 + MX6UL_PAD_UART2_CTS_B__FLEXCAN2_TX 0x1b020 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0 + MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0 + MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0 + >; + }; + + pinctrl_lcdif_dat: lcdifdatgrp { + fsl,pins = < + MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79 + MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79 + MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79 + MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79 + MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79 + MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79 + MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79 + MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79 + MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79 + MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79 + MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79 + MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79 + MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79 + MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79 + MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79 + MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79 + MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x79 + MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x79 + MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x79 + MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x79 + MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x79 + MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x79 + MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x79 + MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x79 + >; + }; + + pinctrl_lcdif_ctrl: lcdifctrlgrp { + fsl,pins = < + MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79 + MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79 + MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79 + MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79 + /* used for lcd reset */ + MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x79 + >; + }; + + pinctrl_qspi: qspigrp { + fsl,pins = < + MX6UL_PAD_NAND_WP_B__QSPI_A_SCLK 0x70a1 + MX6UL_PAD_NAND_READY_B__QSPI_A_DATA00 0x70a1 + MX6UL_PAD_NAND_CE0_B__QSPI_A_DATA01 0x70a1 + MX6UL_PAD_NAND_CE1_B__QSPI_A_DATA02 0x70a1 + MX6UL_PAD_NAND_CLE__QSPI_A_DATA03 0x70a1 + MX6UL_PAD_NAND_DQS__QSPI_A_SS0_B 0x70a1 + >; + }; + + pinctrl_sai2: sai2grp { + fsl,pins = < + MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK 0x17088 + MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC 0x17088 + MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x11088 + MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x11088 + MX6UL_PAD_JTAG_TMS__SAI2_MCLK 0x17088 + MX6UL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x17059 + >; + }; + + pinctrl_pwm1: pwm1grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0 + >; + }; + + pinctrl_sim2: sim2grp { + fsl,pins = < + MX6UL_PAD_CSI_DATA03__SIM2_PORT1_PD 0xb808 + MX6UL_PAD_CSI_DATA04__SIM2_PORT1_CLK 0x31 + MX6UL_PAD_CSI_DATA05__SIM2_PORT1_RST_B 0xb808 + MX6UL_PAD_CSI_DATA06__SIM2_PORT1_SVEN 0xb808 + MX6UL_PAD_CSI_DATA07__SIM2_PORT1_TRXD 0xb809 + MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x3008 + >; + }; + + pinctrl_tsc: tscgrp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0 + MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0 + MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0 + MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1 + MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1 + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX 0x1b0b1 + MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX 0x1b0b1 + MX6UL_PAD_UART3_RX_DATA__UART2_DCE_RTS 0x1b0b1 + MX6UL_PAD_UART3_TX_DATA__UART2_DCE_CTS 0x1b0b1 + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x10059 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059 + MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */ + MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */ + MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */ + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1grp100mhz { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170b9 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100b9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9 + + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1grp200mhz { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170f9 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100f9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x17059 + MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x17059 + MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x17059 + MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x17059 + MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x17059 + MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x17059 + >; + }; + + pinctrl_wdog: wdoggrp { + fsl,pins = < + MX6UL_PAD_LCD_RESET__WDOG1_WDOG_ANY 0x30b0 + >; + }; +}; diff --git a/arch/arm/boot/dts/imx6ull-14x14-evk.dts b/arch/arm/boot/dts/imx6ull-14x14-evk.dts index 4741871434dd..30ef60344af3 100644 --- a/arch/arm/boot/dts/imx6ull-14x14-evk.dts +++ b/arch/arm/boot/dts/imx6ull-14x14-evk.dts @@ -39,7 +39,10 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include "imx6ul-14x14-evk.dts" +/dts-v1/; + +#include "imx6ull.dtsi" +#include "imx6ul-14x14-evk.dtsi" / { model = "Freescale i.MX6 UlltraLite 14x14 EVK Board"; -- cgit v1.3-14-g43fede From 0a929d9784ffb6f3a9d73ab59d9e3a9b10e2f719 Mon Sep 17 00:00:00 2001 From: Jörg Krause Date: Mon, 5 Feb 2018 21:47:59 +0100 Subject: ARM: dts: imx6ul: rename mux mode name REF_CLK_32K to OSC32K_32K_OUT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a rebased version of patch [0]. The 32 kHz reference clock on the i.MX6UL(L) can be output by setting the external signal XTALOSC_REF_CLK_32K in one of the following ways [1]: |----------------------------------------------------------| | Signal | Pad | Mode | Direction | |----------------------------------------------------------| | XTALOSC_REF_CLK_32K | ENET1_RX_EN | ALT2 | O | | | GPIO1_IO03 | ALT3 | | | | JTAG_TCK | ALT6 | | |----------------------------------------------------------| Before patch [2] the mux mode for the external reference clock was missing. The patch named the mux mode as used in the NXP Linux 4.9.11_1.0.0 release, but the Reference Manual uses the name OSC32K_32K_OUT, e.g. in [3]. As Philipp and Shawn suggest the name from the RM should be used instead. [0] https://patchwork.kernel.org/patch/10172187/ [1] IMX6ULRM, Rev. 1, 04/2016, Table 58-1, p. 3649 [2] https://patchwork.kernel.org/patch/10156121/ [3] IMX6ULRM, Rev. 1, 04/2016, 30.5.47 SW_MUX_CTL_PAD_ENET1_RX_EN SW MUX Control Register (IOMUXC_SW_MUX_CTL_PAD_ENET1_RX_EN), p. 1357 Signed-off-by: Jörg Krause Reviewed-by: Stefan Agner Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ul-pinfunc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/imx6ul-pinfunc.h b/arch/arm/boot/dts/imx6ul-pinfunc.h index 9538b0ed5c11..7b9a4dc38456 100644 --- a/arch/arm/boot/dts/imx6ul-pinfunc.h +++ b/arch/arm/boot/dts/imx6ul-pinfunc.h @@ -63,7 +63,7 @@ #define MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x0054 0x02e0 0x05f4 2 0 #define MX6UL_PAD_JTAG_TCK__PWM7_OUT 0x0054 0x02e0 0x0000 4 0 #define MX6UL_PAD_JTAG_TCK__GPIO1_IO14 0x0054 0x02e0 0x0000 5 0 -#define MX6UL_PAD_JTAG_TCK__REF_CLK_32K 0x0054 0x02e0 0x0000 6 0 +#define MX6UL_PAD_JTAG_TCK__OSC32K_32K_OUT 0x0054 0x02e0 0x0000 6 0 #define MX6UL_PAD_JTAG_TCK__SIM2_POWER_FAIL 0x0054 0x02e0 0x0000 8 0 #define MX6UL_PAD_JTAG_TRST_B__SJC_TRSTB 0x0058 0x02e4 0x0000 0 0 #define MX6UL_PAD_JTAG_TRST_B__GPT2_COMPARE3 0x0058 0x02e4 0x0000 1 0 @@ -103,7 +103,7 @@ #define MX6UL_PAD_GPIO1_IO03__I2C1_SDA 0x0068 0x02f4 0x05a8 0 1 #define MX6UL_PAD_GPIO1_IO03__GPT1_COMPARE3 0x0068 0x02f4 0x0000 1 0 #define MX6UL_PAD_GPIO1_IO03__USB_OTG2_OC 0x0068 0x02f4 0x0660 2 0 -#define MX6UL_PAD_GPIO1_IO03__REF_CLK_32K 0x0068 0x02f4 0x0000 3 0 +#define MX6UL_PAD_GPIO1_IO03__OSC32K_32K_OUT 0x0068 0x02f4 0x0000 3 0 #define MX6UL_PAD_GPIO1_IO03__USDHC1_CD_B 0x0068 0x02f4 0x0668 4 0 #define MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x0068 0x02f4 0x0000 5 0 #define MX6UL_PAD_GPIO1_IO03__CCM_DI0_EXT_CLK 0x0068 0x02f4 0x0000 6 0 @@ -320,7 +320,7 @@ #define MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x00cc 0x0358 0x0000 0 0 #define MX6UL_PAD_ENET1_RX_EN__UART5_DCE_RTS 0x00cc 0x0358 0x0640 1 3 #define MX6UL_PAD_ENET1_RX_EN__UART5_DTE_CTS 0x00cc 0x0358 0x0000 1 0 -#define MX6UL_PAD_ENET1_RX_EN__REF_CLK_32K 0x00cc 0x0358 0x0000 2 0 +#define MX6UL_PAD_ENET1_RX_EN__OSC32K_32K_OUT 0x00cc 0x0358 0x0000 2 0 #define MX6UL_PAD_ENET1_RX_EN__CSI_DATA18 0x00cc 0x0358 0x050c 3 0 #define MX6UL_PAD_ENET1_RX_EN__FLEXCAN2_TX 0x00cc 0x0358 0x0000 4 0 #define MX6UL_PAD_ENET1_RX_EN__GPIO2_IO02 0x00cc 0x0358 0x0000 5 0 -- cgit v1.3-14-g43fede From c2e4987e0e02095ed3d19e958b3891954ef38243 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 6 Feb 2018 17:49:03 +0100 Subject: ARM: dts: imx6ull: add Toradex Colibri iMX6ULL support Add support for the Computer on Module Colibri iMX6ULL and its Bluetooth/Wifi variant along with the development/evaluation carrier board device trees. Follow the usual hierarchic include model, maintaining shared configuration in imx6ull-colibri.dtsi and imx6ull-colibri-eval-v3.dtsi respectively. Signed-off-by: Stefan Agner Signed-off-by: Shawn Guo --- arch/arm/boot/dts/Makefile | 4 +- arch/arm/boot/dts/imx6ull-colibri-eval-v3.dts | 14 + arch/arm/boot/dts/imx6ull-colibri-eval-v3.dtsi | 157 ++++++ arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi | 23 + arch/arm/boot/dts/imx6ull-colibri-wifi-eval-v3.dts | 14 + arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi | 65 +++ arch/arm/boot/dts/imx6ull-colibri.dtsi | 553 +++++++++++++++++++++ 7 files changed, 829 insertions(+), 1 deletion(-) create mode 100644 arch/arm/boot/dts/imx6ull-colibri-eval-v3.dts create mode 100644 arch/arm/boot/dts/imx6ull-colibri-eval-v3.dtsi create mode 100644 arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi create mode 100644 arch/arm/boot/dts/imx6ull-colibri-wifi-eval-v3.dts create mode 100644 arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi create mode 100644 arch/arm/boot/dts/imx6ull-colibri.dtsi diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index e1c8954e9849..f7b73c825231 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -530,7 +530,9 @@ dtb-$(CONFIG_SOC_IMX6UL) += \ imx6ul-tx6ul-0010.dtb \ imx6ul-tx6ul-0011.dtb \ imx6ul-tx6ul-mainboard.dtb \ - imx6ull-14x14-evk.dtb + imx6ull-14x14-evk.dtb \ + imx6ull-colibri-eval-v3.dtb \ + imx6ull-colibri-wifi-eval-v3.dtb dtb-$(CONFIG_SOC_IMX7D) += \ imx7d-cl-som-imx7.dtb \ imx7d-colibri-emmc-eval-v3.dtb \ diff --git a/arch/arm/boot/dts/imx6ull-colibri-eval-v3.dts b/arch/arm/boot/dts/imx6ull-colibri-eval-v3.dts new file mode 100644 index 000000000000..08669a18349e --- /dev/null +++ b/arch/arm/boot/dts/imx6ull-colibri-eval-v3.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2018 Toradex AG + */ + +/dts-v1/; + +#include "imx6ull-colibri-nonwifi.dtsi" +#include "imx6ull-colibri-eval-v3.dtsi" + +/ { + model = "Toradex Colibri iMX6ULL 256MB on Colibri Evaluation Board V3"; + compatible = "toradex,colibri-imx6ull-eval", "fsl,imx6ull"; +}; diff --git a/arch/arm/boot/dts/imx6ull-colibri-eval-v3.dtsi b/arch/arm/boot/dts/imx6ull-colibri-eval-v3.dtsi new file mode 100644 index 000000000000..006690ea98c0 --- /dev/null +++ b/arch/arm/boot/dts/imx6ull-colibri-eval-v3.dtsi @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2017 Toradex AG + */ + +/ { + chosen { + stdout-path = "serial0:115200n8"; + }; + + /* fixed crystal dedicated to mcp2515 */ + clk16m: clk16m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <16000000>; + }; + + panel: panel { + compatible = "edt,et057090dhu"; + backlight = <&bl>; + power-supply = <®_3v3>; + + port { + panel_in: endpoint { + remote-endpoint = <&lcdif_out>; + }; + }; + }; + + reg_3v3: regulator-3v3 { + compatible = "regulator-fixed"; + regulator-name = "3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_5v0: regulator-5v0 { + compatible = "regulator-fixed"; + regulator-name = "5V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + reg_usbh_vbus: regulator-usbh-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbh_reg>; + regulator-name = "VCC_USB[1-4]"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio1 2 GPIO_ACTIVE_LOW>; + vin-supply = <®_5v0>; + }; +}; + +&adc1 { + status = "okay"; +}; + +&bl { + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <6>; + power-supply = <®_3v3>; + pwms = <&pwm4 0 5000000 1>; + status = "okay"; +}; + +&ecspi1 { + status = "okay"; + + mcp2515: can@0 { + compatible = "microchip,mcp2515"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can_int>; + reg = <0>; + clocks = <&clk16m>; + interrupt-parent = <&gpio2>; + interrupts = <4 IRQ_TYPE_EDGE_FALLING>; + spi-max-frequency = <10000000>; + vdd-supply = <®_3v3>; + xceiver-supply = <®_5v0>; + status = "okay"; + }; +}; + +&i2c1 { + status = "okay"; + + /* M41T0M6 real time clock on carrier board */ + m41t0m6: rtc@68 { + compatible = "st,m41t0"; + reg = <0x68>; + }; +}; + +&lcdif { + status = "okay"; + + port { + lcdif_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; +}; + +/* PWM */ +&pwm4 { + status = "okay"; +}; + +/* PWM */ +&pwm5 { + status = "okay"; +}; + +/* PWM */ +&pwm6 { + status = "okay"; +}; + +/* PWM */ +&pwm7 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart5 { + status = "okay"; +}; + +&usbotg1 { + status = "okay"; +}; + +&usbotg2 { + vbus-supply = <®_usbh_vbus>; + status = "okay"; +}; + +&usdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc1 &pinctrl_snvs_usdhc1_cd>; + no-1-8-v; + cd-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; + disable-wp; + wakeup-source; + keep-power-in-suspend; + vmmc-supply = <®_3v3>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi b/arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi new file mode 100644 index 000000000000..cfda666a34f4 --- /dev/null +++ b/arch/arm/boot/dts/imx6ull-colibri-nonwifi.dtsi @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2018 Toradex AG + */ + +#include "imx6ull-colibri.dtsi" + +/ { + memory { + reg = <0x80000000 0x10000000>; + }; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio1 &pinctrl_gpio2 &pinctrl_gpio3 + &pinctrl_gpio4 &pinctrl_gpio5 &pinctrl_gpio6>; +}; + +&iomuxc_snvs { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_snvs_gpio1 &pinctrl_snvs_gpio2 &pinctrl_snvs_gpio3>; +}; diff --git a/arch/arm/boot/dts/imx6ull-colibri-wifi-eval-v3.dts b/arch/arm/boot/dts/imx6ull-colibri-wifi-eval-v3.dts new file mode 100644 index 000000000000..df72ce1ae2cb --- /dev/null +++ b/arch/arm/boot/dts/imx6ull-colibri-wifi-eval-v3.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2018 Toradex AG + */ + +/dts-v1/; + +#include "imx6ull-colibri-wifi.dtsi" +#include "imx6ull-colibri-eval-v3.dtsi" + +/ { + model = "Toradex Colibri iMX6ULL 512MB on Colibri Evaluation Board V3"; + compatible = "toradex,colibri-imx6ull-wifi-eval", "fsl,imx6ull"; +}; diff --git a/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi b/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi new file mode 100644 index 000000000000..f48291b7cbf0 --- /dev/null +++ b/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2018 Toradex AG + */ + +#include "imx6ull-colibri.dtsi" + +/ { + memory { + reg = <0x80000000 0x20000000>; + }; + + wifi_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_snvs_wifi_pdn>; + reset-gpios = <&gpio5 11 GPIO_ACTIVE_LOW>; + }; +}; + +&cpu0 { + clock-frequency = <792000000>; + operating-points = < + /* kHz uV */ + 792000 1225000 + 528000 1175000 + 396000 1025000 + 198000 950000 + >; + fsl,soc-operating-points = < + /* KHz uV */ + 792000 1175000 + 528000 1175000 + 396000 1175000 + 198000 1175000 + >; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio1 &pinctrl_gpio2 &pinctrl_gpio3 + &pinctrl_gpio4 &pinctrl_gpio5>; + +}; + +&iomuxc_snvs { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_snvs_gpio1 &pinctrl_snvs_gpio2>; +}; + +&usdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc2>; + assigned-clocks = <&clks IMX6UL_CLK_USDHC2_SEL>, <&clks IMX6UL_CLK_USDHC2>; + assigned-clock-parents = <&clks IMX6UL_CLK_PLL2_PFD2>; + assigned-clock-rates = <0>, <198000000>; + cap-power-off-card; + keep-power-in-suspend; + mmc-pwrseq = <&wifi_pwrseq>; + no-1-8-v; + non-removable; + vmmc-supply = <®_module_3v3>; + wakeup-source; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6ull-colibri.dtsi b/arch/arm/boot/dts/imx6ull-colibri.dtsi new file mode 100644 index 000000000000..6c63a7384611 --- /dev/null +++ b/arch/arm/boot/dts/imx6ull-colibri.dtsi @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2018 Toradex AG + */ + +#include "imx6ull.dtsi" + +/ { + aliases { + ethernet0 = &fec2; + ethernet1 = &fec1; + }; + + bl: backlight { + compatible = "pwm-backlight"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_bl_on>; + enable-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; + status = "disabled"; + }; + + reg_module_3v3: regulator-module-3v3 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-name = "+V3.3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_module_3v3_avdd: regulator-module-3v3-avdd { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-name = "+V3.3_AVDD_AUDIO"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_sd1_vmmc: regulator-sd1-vmmc { + compatible = "regulator-gpio"; + gpio = <&gpio5 9 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_snvs_reg_sd>; + regulator-always-on; + regulator-name = "+V3.3_1.8_SD"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + states = <1800000 0x1 3300000 0x0>; + vin-supply = <®_module_3v3>; + }; +}; + +&adc1 { + num-channels = <10>; + vref-supply = <®_module_3v3_avdd>; +}; + +/* Colibri SPI */ +&ecspi1 { + cs-gpios = <&gpio3 26 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1 &pinctrl_ecspi1_cs>; +}; + +&fec2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet2>; + phy-mode = "rmii"; + phy-handle = <ðphy1>; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethphy1: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + max-speed = <100>; + reg = <2>; + }; + }; +}; + +&gpmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; + nand-ecc-mode = "hw"; + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + status = "okay"; +}; + +&i2c1 { + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_i2c1>; + pinctrl-1 = <&pinctrl_i2c1_gpio>; + sda-gpios = <&gpio1 29 GPIO_ACTIVE_LOW>; + scl-gpios = <&gpio1 28 GPIO_ACTIVE_LOW>; +}; + +&i2c2 { + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_i2c2>; + pinctrl-1 = <&pinctrl_i2c2_gpio>; + sda-gpios = <&gpio1 31 GPIO_ACTIVE_LOW>; + scl-gpios = <&gpio1 30 GPIO_ACTIVE_LOW>; + status = "okay"; + + ad7879@2c { + compatible = "adi,ad7879-1"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_snvs_ad7879_int>; + reg = <0x2c>; + interrupt-parent = <&gpio5>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + touchscreen-max-pressure = <4096>; + adi,resistance-plate-x = <120>; + adi,first-conversion-delay = /bits/ 8 <3>; + adi,acquisition-time = /bits/ 8 <1>; + adi,median-filter-size = /bits/ 8 <2>; + adi,averaging = /bits/ 8 <1>; + adi,conversion-interval = /bits/ 8 <255>; + }; +}; + +&lcdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcdif_dat + &pinctrl_lcdif_ctrl>; +}; + +&pwm4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm4>; + #pwm-cells = <3>; +}; + +&pwm5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm5>; + #pwm-cells = <3>; +}; + +&pwm6 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm6>; + #pwm-cells = <3>; +}; + +&pwm7 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm7>; + #pwm-cells = <3>; +}; + +&sdma { + status = "okay"; +}; + +&snvs_pwrkey { + status = "disabled"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1 &pinctrl_uart1_ctrl1>; + uart-has-rtscts; + fsl,dte-mode; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + uart-has-rtscts; + fsl,dte-mode; +}; + +&uart5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>; + fsl,dte-mode; +}; + +&usbotg1 { + dr_mode = "otg"; + srp-disable; + hnp-disable; + adp-disable; +}; + +&usbotg2 { + dr_mode = "host"; +}; + +&usdhc1 { + assigned-clocks = <&clks IMX6UL_CLK_USDHC1_SEL>, <&clks IMX6UL_CLK_USDHC1>; + assigned-clock-parents = <&clks IMX6UL_CLK_PLL2_PFD2>; + assigned-clock-rates = <0>, <198000000>; +}; + +&iomuxc { + pinctrl_can_int: canint-grp { + fsl,pins = < + MX6UL_PAD_ENET1_TX_DATA1__GPIO2_IO04 0X14 /* SODIMM 73 */ + >; + }; + + pinctrl_enet2: enet2-grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0 + MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0 + MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0 + MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0 + MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0 + MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0 + MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031 + MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0 + MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0 + MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0 + >; + }; + + pinctrl_ecspi1_cs: ecspi1-cs-grp { + fsl,pins = < + MX6UL_PAD_LCD_DATA21__GPIO3_IO26 0x000a0 + >; + }; + + pinctrl_ecspi1: ecspi1-grp { + fsl,pins = < + MX6UL_PAD_LCD_DATA20__ECSPI1_SCLK 0x000a0 + MX6UL_PAD_LCD_DATA22__ECSPI1_MOSI 0x000a0 + MX6UL_PAD_LCD_DATA23__ECSPI1_MISO 0x100a0 + >; + }; + + pinctrl_flexcan2: flexcan2-grp { + fsl,pins = < + MX6UL_PAD_ENET1_TX_DATA0__FLEXCAN2_RX 0x1b020 + MX6UL_PAD_ENET1_RX_EN__FLEXCAN2_TX 0x1b020 + >; + }; + + pinctrl_gpio_bl_on: gpio-bl-on-grp { + fsl,pins = < + MX6UL_PAD_JTAG_TMS__GPIO1_IO11 0x000a0 + >; + }; + + pinctrl_gpio1: gpio1-grp { + fsl,pins = < + MX6UL_PAD_ENET1_RX_DATA0__GPIO2_IO00 0x74 /* SODIMM 55 */ + MX6UL_PAD_ENET1_RX_DATA1__GPIO2_IO01 0x74 /* SODIMM 63 */ + MX6UL_PAD_UART3_RX_DATA__GPIO1_IO25 0X14 /* SODIMM 77 */ + MX6UL_PAD_JTAG_TCK__GPIO1_IO14 0x14 /* SODIMM 99 */ + MX6UL_PAD_NAND_CE1_B__GPIO4_IO14 0x14 /* SODIMM 133 */ + MX6UL_PAD_UART3_TX_DATA__GPIO1_IO24 0x14 /* SODIMM 135 */ + MX6UL_PAD_UART3_CTS_B__GPIO1_IO26 0x14 /* SODIMM 100 */ + MX6UL_PAD_JTAG_TRST_B__GPIO1_IO15 0x14 /* SODIMM 102 */ + MX6UL_PAD_ENET1_RX_ER__GPIO2_IO07 0x14 /* SODIMM 104 */ + MX6UL_PAD_UART3_RTS_B__GPIO1_IO27 0x14 /* SODIMM 186 */ + >; + }; + + pinctrl_gpio2: gpio2-grp { /* Camera */ + fsl,pins = < + MX6UL_PAD_CSI_DATA04__GPIO4_IO25 0x74 /* SODIMM 69 */ + MX6UL_PAD_CSI_MCLK__GPIO4_IO17 0x14 /* SODIMM 75 */ + MX6UL_PAD_CSI_DATA06__GPIO4_IO27 0x14 /* SODIMM 85 */ + MX6UL_PAD_CSI_PIXCLK__GPIO4_IO18 0x14 /* SODIMM 96 */ + MX6UL_PAD_CSI_DATA05__GPIO4_IO26 0x14 /* SODIMM 98 */ + >; + }; + + pinctrl_gpio3: gpio3-grp { /* CAN2 */ + fsl,pins = < + MX6UL_PAD_ENET1_RX_EN__GPIO2_IO02 0x14 /* SODIMM 178 */ + MX6UL_PAD_ENET1_TX_DATA0__GPIO2_IO03 0x14 /* SODIMM 188 */ + >; + }; + + pinctrl_gpio4: gpio4-grp { + fsl,pins = < + MX6UL_PAD_CSI_DATA07__GPIO4_IO28 0x74 /* SODIMM 65 */ + >; + }; + + pinctrl_gpio5: gpio5-grp { /* ATMEL MXT TOUCH */ + fsl,pins = < + MX6UL_PAD_JTAG_MOD__GPIO1_IO10 0x74 /* SODIMM 106 */ + >; + }; + + pinctrl_gpio6: gpio6-grp { /* Wifi pins */ + fsl,pins = < + MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x14 /* SODIMM 89 */ + MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x14 /* SODIMM 79 */ + MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x14 /* SODIMM 81 */ + MX6UL_PAD_CSI_DATA03__GPIO4_IO24 0x14 /* SODIMM 97 */ + MX6UL_PAD_CSI_DATA00__GPIO4_IO21 0x14 /* SODIMM 101 */ + MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x14 /* SODIMM 103 */ + MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x14 /* SODIMM 94 */ + >; + }; + + pinctrl_gpmi_nand: gpmi-nand-grp { + fsl,pins = < + MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00 0x100a9 + MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01 0x100a9 + MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02 0x100a9 + MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03 0x100a9 + MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04 0x100a9 + MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05 0x100a9 + MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06 0x100a9 + MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07 0x100a9 + MX6UL_PAD_NAND_CLE__RAWNAND_CLE 0x100a9 + MX6UL_PAD_NAND_ALE__RAWNAND_ALE 0x100a9 + MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B 0x100a9 + MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B 0x100a9 + MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B 0x100a9 + MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0x100a9 + >; + }; + + pinctrl_i2c1: i2c1-grp { + fsl,pins = < + MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0 + MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0 + >; + }; + + pinctrl_i2c1_gpio: i2c1-gpio-grp { + fsl,pins = < + MX6UL_PAD_UART4_TX_DATA__GPIO1_IO28 0x4001b8b0 + MX6UL_PAD_UART4_RX_DATA__GPIO1_IO29 0x4001b8b0 + >; + }; + + pinctrl_i2c2: i2c2-grp { + fsl,pins = < + MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0 + MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0 + >; + }; + + pinctrl_i2c2_gpio: i2c2-gpio-grp { + fsl,pins = < + MX6UL_PAD_UART5_TX_DATA__GPIO1_IO30 0x4001b8b0 + MX6UL_PAD_UART5_RX_DATA__GPIO1_IO31 0x4001b8b0 + >; + }; + + pinctrl_lcdif_dat: lcdif-dat-grp { + fsl,pins = < + MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x00079 + MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x00079 + MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x00079 + MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x00079 + MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x00079 + MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x00079 + MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x00079 + MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x00079 + MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x00079 + MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x00079 + MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x00079 + MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x00079 + MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x00079 + MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x00079 + MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x00079 + MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x00079 + MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x00079 + MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x00079 + >; + }; + + pinctrl_lcdif_ctrl: lcdif-ctrl-grp { + fsl,pins = < + MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x00079 + MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x00079 + MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x00079 + MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x00079 + >; + }; + + pinctrl_pwm4: pwm4-grp { + fsl,pins = < + MX6UL_PAD_NAND_WP_B__PWM4_OUT 0x00079 + >; + }; + + pinctrl_pwm5: pwm5-grp { + fsl,pins = < + MX6UL_PAD_NAND_DQS__PWM5_OUT 0x00079 + >; + }; + + pinctrl_pwm6: pwm6-grp { + fsl,pins = < + MX6UL_PAD_ENET1_TX_EN__PWM6_OUT 0x00079 + >; + }; + + pinctrl_pwm7: pwm7-grp { + fsl,pins = < + MX6UL_PAD_ENET1_TX_CLK__PWM7_OUT 0x00079 + >; + }; + + pinctrl_uart1: uart1-grp { + fsl,pins = < + MX6UL_PAD_UART1_TX_DATA__UART1_DTE_RX 0x1b0b1 + MX6UL_PAD_UART1_RX_DATA__UART1_DTE_TX 0x1b0b1 + MX6UL_PAD_UART1_RTS_B__UART1_DTE_CTS 0x1b0b1 + MX6UL_PAD_UART1_CTS_B__UART1_DTE_RTS 0x1b0b1 + >; + }; + + pinctrl_uart1_ctrl1: uart1-ctrl1-grp { /* Additional DTR, DCD */ + fsl,pins = < + MX6UL_PAD_JTAG_TDI__GPIO1_IO13 0x1b0b1 /* DCD */ + MX6UL_PAD_LCD_DATA18__GPIO3_IO23 0x1b0b1 /* DSR */ + MX6UL_PAD_JTAG_TDO__GPIO1_IO12 0x1b0b1 /* DTR */ + MX6UL_PAD_LCD_DATA19__GPIO3_IO24 0x1b0b1 /* RI */ + >; + }; + + pinctrl_uart2: uart2-grp { + fsl,pins = < + MX6UL_PAD_UART2_TX_DATA__UART2_DTE_RX 0x1b0b1 + MX6UL_PAD_UART2_RX_DATA__UART2_DTE_TX 0x1b0b1 + MX6UL_PAD_UART2_CTS_B__UART2_DTE_RTS 0x1b0b1 + MX6UL_PAD_UART2_RTS_B__UART2_DTE_CTS 0x1b0b1 + >; + }; + pinctrl_uart5: uart5-grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO04__UART5_DTE_RX 0x1b0b1 + MX6UL_PAD_GPIO1_IO05__UART5_DTE_TX 0x1b0b1 + >; + }; + + pinctrl_usbh_reg: gpio-usbh-reg { + fsl,pins = < + MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0x1b0b1 /* SODIMM 129 USBH PEN */ + >; + }; + + pinctrl_usdhc1: usdhc1-grp { + fsl,pins = < + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x17059 + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x10059 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059 + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhz-grp { + fsl,pins = < + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x170b9 + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x100b9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9 + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhz-grp { + fsl,pins = < + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x170f9 + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x100f9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9 + >; + }; + + pinctrl_usdhc2: usdhc2-grp { + fsl,pins = < + MX6UL_PAD_CSI_DATA00__USDHC2_DATA0 0x17059 + MX6UL_PAD_CSI_DATA01__USDHC2_DATA1 0x17059 + MX6UL_PAD_CSI_DATA02__USDHC2_DATA2 0x17059 + MX6UL_PAD_CSI_DATA03__USDHC2_DATA3 0x17059 + MX6UL_PAD_CSI_HSYNC__USDHC2_CMD 0x17059 + MX6UL_PAD_CSI_VSYNC__USDHC2_CLK 0x17059 + + MX6UL_PAD_GPIO1_IO03__OSC32K_32K_OUT 0x14 + >; + }; +}; + +&iomuxc_snvs { + pinctrl_snvs_gpio1: snvs-gpio1-grp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x14 /* SODIMM 93 */ + MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x14 /* SODIMM 95 */ + MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x74 /* SODIMM 105 */ + MX6ULL_PAD_SNVS_TAMPER5__GPIO5_IO05 0x14 /* SODIMM 131 USBH OC */ + MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x74 /* SODIMM 138 */ + >; + }; + + pinctrl_snvs_gpio2: snvs-gpio2-grp { /* ATMEL MXT TOUCH */ + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x74 /* SODIMM 107 */ + >; + }; + + pinctrl_snvs_gpio3: snvs-gpio3-grp { /* Wifi pins */ + fsl,pins = < + MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x14 /* SODIMM 127 */ + >; + }; + + pinctrl_snvs_ad7879_int: snvs-ad7879-int-grp { /* TOUCH Interrupt */ + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x1b0b0 + >; + }; + + pinctrl_snvs_reg_sd: snvs-reg-sd-grp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x4001b8b0 + >; + }; + + pinctrl_snvs_usbc_det: snvs-usbc-det-grp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x1b0b0 + >; + }; + + pinctrl_snvs_gpiokeys: snvs-gpiokeys-grp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x130b0 + >; + }; + + pinctrl_snvs_usdhc1_cd: snvs-usdhc1-cd-grp { + fsl,pins = < + MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x1b0b0 /* CD */ + >; + }; + + pinctrl_snvs_wifi_pdn: snvs-wifi-pdn-grp { + fsl,pins = < + MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x14 + >; + }; +}; -- cgit v1.3-14-g43fede From a377f681324a33bfa58ab88f7bb894cf515b2489 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Sun, 10 Dec 2017 20:03:19 +0100 Subject: clk: rockchip: fix hclk_vio_niu on rk3328 The clock wrongly was called hclk_vio and exported, while it actually is a clock of the interconnect-vio connection and should therefore be always on till we actually model the interconnect. So fix that and don't export it as HCLK_VIO. Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3328.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index b04f29774ee7..17816aec206a 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -727,7 +727,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { GATE(0, "pclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 13, GFLAGS), GATE(0, "hclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 14, GFLAGS), GATE(HCLK_HDCP, "hclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 0, GFLAGS), - GATE(HCLK_VIO, "hclk_vio", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS), + GATE(0, "hclk_vio_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS), GATE(PCLK_HDMI, "pclk_hdmi", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 4, GFLAGS), GATE(PCLK_HDCP, "pclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 5, GFLAGS), @@ -861,6 +861,7 @@ static const char *const rk3328_critical_clocks[] __initconst = { "aclk_rga_niu", "pclk_vio_h2p", "hclk_vio_h2p", + "hclk_vio_niu", }; static void __init rk3328_clk_init(struct device_node *np) -- cgit v1.3-14-g43fede From 224a63844173944817a7f7b966c14466abd2010f Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Sun, 10 Dec 2017 20:07:38 +0100 Subject: clk: rockchip: remove HCLK_VIO from rk3328 dt header This clock is not hclk_vio but hclk_vio_niu, the clock for the interconnect output. The clock got fixed and the id was never used in this incorrect form, so remove it. Signed-off-by: Heiko Stuebner --- include/dt-bindings/clock/rk3328-cru.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/dt-bindings/clock/rk3328-cru.h b/include/dt-bindings/clock/rk3328-cru.h index d2b26a4b43eb..a82a0109faff 100644 --- a/include/dt-bindings/clock/rk3328-cru.h +++ b/include/dt-bindings/clock/rk3328-cru.h @@ -193,7 +193,6 @@ #define HCLK_VPU_PRE 324 #define HCLK_VIO_PRE 325 #define HCLK_VPU 326 -#define HCLK_VIO 327 #define HCLK_BUS_PRE 328 #define HCLK_PERI_PRE 329 #define HCLK_H264 330 -- cgit v1.3-14-g43fede From 1d334427c924b849d590ea1aea4adaaa43c5fa7f Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 11 Dec 2017 01:11:14 +0100 Subject: clk: rockchip: export sclk_hdmi_sfc on rk3328 This clock is one of the dw-hdmi supplying clocks and thus needs to be exported. Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3328.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index 17816aec206a..b7a28f2814e2 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -588,7 +588,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { COMPOSITE(ACLK_VOP_PRE, "aclk_vop_pre", mux_4plls_p, 0, RK3328_CLKSEL_CON(39), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3328_CLKGATE_CON(5), 5, GFLAGS), - GATE(0, "clk_hdmi_sfc", "xin24m", 0, + GATE(SCLK_HDMI_SFC, "sclk_hdmi_sfc", "xin24m", 0, RK3328_CLKGATE_CON(5), 4, GFLAGS), COMPOSITE_NODIV(0, "clk_cif_src", mux_2plls_p, 0, -- cgit v1.3-14-g43fede From 51a70dbf689c913ad6e8efc6917aa669bd57d339 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 12 Dec 2017 22:49:11 +0100 Subject: clk: rockchip: protect all remaining rk3328 interconnect clocks NIU clocks are supplying the interconnect connections to specific peripherals and are currently not controlled in any way. So to prevent things falling apart at strange moments, mark all niu clocks as critical. Most of them where marked as CLK_IGNORE_UNUSED, but that doesn't help if a parent clock then gets disabled. Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3328.c | 56 ++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index b7a28f2814e2..c5b381ab23b1 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -304,7 +304,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "aclk_core", "armclk", CLK_IGNORE_UNUSED, RK3328_CLKSEL_CON(1), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3328_CLKGATE_CON(7), 1, GFLAGS), - GATE(0, "aclk_core_niu", "aclk_core", CLK_IGNORE_UNUSED, + GATE(0, "aclk_core_niu", "aclk_core", 0, RK3328_CLKGATE_CON(13), 0, GFLAGS), GATE(0, "aclk_gic400", "aclk_core", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(13), 1, GFLAGS), @@ -318,7 +318,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(6), 6, GFLAGS), GATE(ACLK_GPU, "aclk_gpu", "aclk_gpu_pre", CLK_SET_RATE_PARENT, RK3328_CLKGATE_CON(14), 0, GFLAGS), - GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", CLK_IGNORE_UNUSED, + GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", 0, RK3328_CLKGATE_CON(14), 1, GFLAGS), /* PD_DDR */ @@ -513,9 +513,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(24), 0, GFLAGS), GATE(HCLK_RKVDEC, "hclk_rkvdec", "hclk_rkvdec_pre", CLK_SET_RATE_PARENT, RK3328_CLKGATE_CON(24), 1, GFLAGS), - GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", CLK_IGNORE_UNUSED, + GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", 0, RK3328_CLKGATE_CON(24), 2, GFLAGS), - GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", CLK_IGNORE_UNUSED, + GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", 0, RK3328_CLKGATE_CON(24), 3, GFLAGS), COMPOSITE(SCLK_VDEC_CABAC, "sclk_vdec_cabac", mux_4plls_p, 0, @@ -535,9 +535,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(23), 0, GFLAGS), GATE(HCLK_VPU, "hclk_vpu", "hclk_vpu_pre", CLK_SET_RATE_PARENT, RK3328_CLKGATE_CON(23), 1, GFLAGS), - GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", CLK_IGNORE_UNUSED, + GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", 0, RK3328_CLKGATE_CON(23), 2, GFLAGS), - GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", CLK_IGNORE_UNUSED, + GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", 0, RK3328_CLKGATE_CON(23), 3, GFLAGS), COMPOSITE(ACLK_RKVENC, "aclk_rkvenc", mux_4plls_p, 0, @@ -545,9 +545,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(6), 3, GFLAGS), FACTOR_GATE(HCLK_RKVENC, "hclk_rkvenc", "aclk_rkvenc", 0, 1, 4, RK3328_CLKGATE_CON(11), 4, GFLAGS), - GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", CLK_IGNORE_UNUSED, + GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", 0, RK3328_CLKGATE_CON(25), 0, GFLAGS), - GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", CLK_IGNORE_UNUSED, + GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", 0, RK3328_CLKGATE_CON(25), 1, GFLAGS), GATE(ACLK_H265, "aclk_h265", "aclk_rkvenc", 0, RK3328_CLKGATE_CON(25), 0, GFLAGS), @@ -709,14 +709,14 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { /* PD_VOP */ GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3328_CLKGATE_CON(21), 10, GFLAGS), - GATE(0, "aclk_rga_niu", "aclk_rga_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 3, GFLAGS), + GATE(0, "aclk_rga_niu", "aclk_rga_pre", 0, RK3328_CLKGATE_CON(22), 3, GFLAGS), GATE(ACLK_VOP, "aclk_vop", "aclk_vop_pre", 0, RK3328_CLKGATE_CON(21), 2, GFLAGS), - GATE(0, "aclk_vop_niu", "aclk_vop_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 4, GFLAGS), + GATE(0, "aclk_vop_niu", "aclk_vop_pre", 0, RK3328_CLKGATE_CON(21), 4, GFLAGS), GATE(ACLK_IEP, "aclk_iep", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 6, GFLAGS), GATE(ACLK_CIF, "aclk_cif", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 8, GFLAGS), GATE(ACLK_HDCP, "aclk_hdcp", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 15, GFLAGS), - GATE(0, "aclk_vio_niu", "aclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 2, GFLAGS), + GATE(0, "aclk_vio_niu", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 2, GFLAGS), GATE(HCLK_VOP, "hclk_vop", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 3, GFLAGS), GATE(0, "hclk_vop_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 5, GFLAGS), @@ -743,19 +743,19 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { GATE(HCLK_HOST0_ARB, "hclk_host0_arb", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 7, GFLAGS), GATE(HCLK_OTG, "hclk_otg", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 8, GFLAGS), GATE(HCLK_OTG_PMU, "hclk_otg_pmu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 9, GFLAGS), - GATE(0, "hclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 12, GFLAGS), - GATE(0, "pclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 13, GFLAGS), + GATE(0, "hclk_peri_niu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 12, GFLAGS), + GATE(0, "pclk_peri_niu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 13, GFLAGS), /* PD_GMAC */ GATE(ACLK_MAC2PHY, "aclk_mac2phy", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 0, GFLAGS), GATE(ACLK_MAC2IO, "aclk_mac2io", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 2, GFLAGS), - GATE(0, "aclk_gmac_niu", "aclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 4, GFLAGS), + GATE(0, "aclk_gmac_niu", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 4, GFLAGS), GATE(PCLK_MAC2PHY, "pclk_mac2phy", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 1, GFLAGS), GATE(PCLK_MAC2IO, "pclk_mac2io", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 3, GFLAGS), - GATE(0, "pclk_gmac_niu", "pclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 5, GFLAGS), + GATE(0, "pclk_gmac_niu", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 5, GFLAGS), /* PD_BUS */ - GATE(0, "aclk_bus_niu", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 12, GFLAGS), + GATE(0, "aclk_bus_niu", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 12, GFLAGS), GATE(ACLK_DCF, "aclk_dcf", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 11, GFLAGS), GATE(ACLK_TSP, "aclk_tsp", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 12, GFLAGS), GATE(0, "aclk_intmem", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 0, GFLAGS), @@ -769,10 +769,10 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { GATE(HCLK_TSP, "hclk_tsp", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 11, GFLAGS), GATE(HCLK_CRYPTO_MST, "hclk_crypto_mst", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 7, GFLAGS), GATE(HCLK_CRYPTO_SLV, "hclk_crypto_slv", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 8, GFLAGS), - GATE(0, "hclk_bus_niu", "hclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 13, GFLAGS), + GATE(0, "hclk_bus_niu", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 13, GFLAGS), GATE(HCLK_PDM, "hclk_pdm", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(28), 0, GFLAGS), - GATE(0, "pclk_bus_niu", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 14, GFLAGS), + GATE(0, "pclk_bus_niu", "pclk_bus", 0, RK3328_CLKGATE_CON(15), 14, GFLAGS), GATE(0, "pclk_efuse", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 9, GFLAGS), GATE(0, "pclk_otp", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(28), 4, GFLAGS), GATE(PCLK_I2C0, "pclk_i2c0", "pclk_bus", 0, RK3328_CLKGATE_CON(15), 10, GFLAGS), @@ -807,7 +807,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { GATE(0, "pclk_acodecphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 5, GFLAGS), GATE(PCLK_HDMIPHY, "pclk_hdmiphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 7, GFLAGS), GATE(0, "pclk_vdacphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 8, GFLAGS), - GATE(0, "pclk_phy_niu", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 15, GFLAGS), + GATE(0, "pclk_phy_niu", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(15), 15, GFLAGS), /* PD_MMC */ MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", @@ -833,11 +833,16 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { static const char *const rk3328_critical_clocks[] __initconst = { "aclk_bus", + "aclk_bus_niu", "pclk_bus", + "pclk_bus_niu", "hclk_bus", + "hclk_bus_niu", "aclk_peri", "hclk_peri", + "hclk_peri_niu", "pclk_peri", + "pclk_peri_niu", "pclk_dbg", "aclk_core_niu", "aclk_gic400", @@ -861,7 +866,20 @@ static const char *const rk3328_critical_clocks[] __initconst = { "aclk_rga_niu", "pclk_vio_h2p", "hclk_vio_h2p", + "aclk_vio_niu", "hclk_vio_niu", + "aclk_vop_niu", + "hclk_vop_niu", + "aclk_gpu_niu", + "aclk_rkvdec_niu", + "hclk_rkvdec_niu", + "aclk_vpu_niu", + "hclk_vpu_niu", + "aclk_rkvenc_niu", + "hclk_rkvenc_niu", + "aclk_gmac_niu", + "pclk_gmac_niu", + "pclk_phy_niu", }; static void __init rk3328_clk_init(struct device_node *np) -- cgit v1.3-14-g43fede From 7f872cb362d312b0b75975441b3717253e323b81 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Sat, 23 Dec 2017 16:30:19 +0100 Subject: clk: rockchip: remove ignore_unused flag from rk3328 vio_h2p clocks These are already marked as critical, so there is no need for an additional ignore_unused flag. Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3328.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index c5b381ab23b1..983ad5760ce0 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -724,8 +724,8 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { GATE(HCLK_CIF, "hclk_cif", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 9, GFLAGS), GATE(HCLK_RGA, "hclk_rga", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 11, GFLAGS), GATE(0, "hclk_ahb1tom", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 12, GFLAGS), - GATE(0, "pclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 13, GFLAGS), - GATE(0, "hclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 14, GFLAGS), + GATE(0, "pclk_vio_h2p", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 13, GFLAGS), + GATE(0, "hclk_vio_h2p", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 14, GFLAGS), GATE(HCLK_HDCP, "hclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 0, GFLAGS), GATE(0, "hclk_vio_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS), GATE(PCLK_HDMI, "pclk_hdmi", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 4, GFLAGS), -- cgit v1.3-14-g43fede From 36ec03618c12ad3308f7a80994ee4b2129a1e381 Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Thu, 25 May 2017 18:00:24 +0800 Subject: clk: rockchip: add flags for rk3328 dclk_lcdc dclk_lcdc can be sourced from a general pll source as well as the hdmiphy's pll output. We will want to set this source by hand (to the system-pll-source in most cases) and also want rate changes to this clock to be able to also touch the pll source clock if needed, so add CLK_SET_RATE_PARENT and CLK_SET_RATE_NO_REPARENT for dclk_lcdc. Signed-off-by: Zheng Yang [ammended commit message] Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3328.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index 983ad5760ce0..f680b421b6d5 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -602,7 +602,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(5), 6, GFLAGS), DIV(DCLK_HDMIPHY, "dclk_hdmiphy", "dclk_lcdc_src", 0, RK3328_CLKSEL_CON(40), 3, 3, DFLAGS), - MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, 0, + MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, RK3328_CLKSEL_CON(40), 1, 1, MFLAGS), /* -- cgit v1.3-14-g43fede From dd5bdb797f0a6e0d3d2aa86af60f92b094705d24 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 25 Dec 2017 17:36:00 +0100 Subject: clk: rockchip: document hdmi_phy external input for rk3328 The hdmi-phy block inside the soc also loops its pll output back into the clock controller, so document that already used input clock. Signed-off-by: Heiko Stuebner Reviewed-by: Rob Herring --- Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt b/Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt index e71c675ba5da..904ae682ea90 100644 --- a/Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt +++ b/Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt @@ -32,6 +32,7 @@ clock-output-names: - "clkin_i2s" - external I2S clock - optional, - "gmac_clkin" - external GMAC clock - optional - "phy_50m_out" - output clock of the pll in the mac phy + - "hdmi_phy" - output clock of the hdmi phy pll - optional Example: Clock controller node: -- cgit v1.3-14-g43fede From f1a2879c1c57eb40747db3ff1b7b80cb9041e11b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 25 Apr 2017 14:04:35 +0200 Subject: clk: renesas: Stop enabling legacy DT clock support by default Since v4.15-rc1, the DTS files for all R-Car Gen2 SoCs have been converted to the new CPG/MSSR bindings. Hence it is now safe to no longer enable legacy DT clock support by default. Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 43b5a89c4b28..84b40b95ca0e 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -24,12 +24,13 @@ if CLK_RENESAS config CLK_RENESAS_LEGACY bool "Legacy DT clock support" depends on CLK_R8A7790 || CLK_R8A7791 || CLK_R8A7792 || CLK_R8A7794 - default y help Enable backward compatibility with old device trees describing a hierarchical representation of the various CPG and MSTP clocks. Say Y if you want your kernel to work with old DTBs. + It is safe to say N if you use the DTS that is supplied with the + current kernel source tree. # SoC config CLK_EMEV2 -- cgit v1.3-14-g43fede From 3391891fa9c82fd14bcddec2f422299b57ce8091 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Mon, 29 Jan 2018 19:01:49 +0100 Subject: clk: renesas: rcar-gen3: Add Z clock divider support This patch adds Z clock divider support for R-Car Gen3 SoC. Signed-off-by: Takeshi Kihara Signed-off-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rcar-gen3-cpg.c | 133 ++++++++++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen3-cpg.h | 1 + 2 files changed, 134 insertions(+) diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index 0904886f5501..b85918fa62c6 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -61,6 +62,134 @@ static void cpg_simple_notifier_register(struct raw_notifier_head *notifiers, raw_notifier_chain_register(notifiers, &csn->nb); } +/* + * Z Clock + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. clk->rate = (parent->rate * mult / 32 ) / 2 + * parent - fixed parent. No clk_set_parent support + */ +#define CPG_FRQCRB 0x00000004 +#define CPG_FRQCRB_KICK BIT(31) +#define CPG_FRQCRC 0x000000e0 +#define CPG_FRQCRC_ZFC_MASK GENMASK(12, 8) + +struct cpg_z_clk { + struct clk_hw hw; + void __iomem *reg; + void __iomem *kick_reg; +}; + +#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw) + +static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int mult; + + mult = 32 - FIELD_GET(CPG_FRQCRC_ZFC_MASK, clk_readl(zclk->reg)); + + /* Factor of 2 is for fixed divider */ + return DIV_ROUND_CLOSEST_ULL((u64)parent_rate * mult, 32 * 2); +} + +static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + /* Factor of 2 is for fixed divider */ + unsigned long prate = *parent_rate / 2; + unsigned int mult; + + mult = div_u64(rate * 32ULL, prate); + mult = clamp(mult, 1U, 32U); + + return (u64)prate * mult / 32; +} + +static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int mult; + unsigned int i; + u32 val, kick; + + /* Factor of 2 is for fixed divider */ + mult = DIV_ROUND_CLOSEST_ULL(rate * 32ULL * 2, parent_rate); + mult = clamp(mult, 1U, 32U); + + if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK) + return -EBUSY; + + val = clk_readl(zclk->reg) & ~CPG_FRQCRC_ZFC_MASK; + val |= FIELD_PREP(CPG_FRQCRC_ZFC_MASK, 32 - mult); + clk_writel(val, zclk->reg); + + /* + * Set KICK bit in FRQCRB to update hardware setting and wait for + * clock change completion. + */ + kick = clk_readl(zclk->kick_reg); + kick |= CPG_FRQCRB_KICK; + clk_writel(kick, zclk->kick_reg); + + /* + * Note: There is no HW information about the worst case latency. + * + * Using experimental measurements, it seems that no more than + * ~10 iterations are needed, independently of the CPU rate. + * Since this value might be dependent of external xtal rate, pll1 + * rate or even the other emulation clocks rate, use 1000 as a + * "super" safe value. + */ + for (i = 1000; i; i--) { + if (!(clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)) + return 0; + + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static const struct clk_ops cpg_z_clk_ops = { + .recalc_rate = cpg_z_clk_recalc_rate, + .round_rate = cpg_z_clk_round_rate, + .set_rate = cpg_z_clk_set_rate, +}; + +static struct clk * __init cpg_z_clk_register(const char *name, + const char *parent_name, + void __iomem *reg) +{ + struct clk_init_data init; + struct cpg_z_clk *zclk; + struct clk *clk; + + zclk = kzalloc(sizeof(*zclk), GFP_KERNEL); + if (!zclk) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &cpg_z_clk_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + zclk->reg = reg + CPG_FRQCRC; + zclk->kick_reg = reg + CPG_FRQCRB; + zclk->hw.init = &init; + + clk = clk_register(NULL, &zclk->hw); + if (IS_ERR(clk)) + kfree(zclk); + + return clk; +} + /* * SDn Clock */ @@ -420,6 +549,10 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, mult = 1; break; + case CLK_TYPE_GEN3_Z: + return cpg_z_clk_register(core->name, __clk_get_name(parent), + base); + default: return ERR_PTR(-EINVAL); } diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index 2e4284399f53..c73d4d6fdc85 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -21,6 +21,7 @@ enum rcar_gen3_clk_types { CLK_TYPE_GEN3_SD, CLK_TYPE_GEN3_R, CLK_TYPE_GEN3_PE, + CLK_TYPE_GEN3_Z, }; #define DEF_GEN3_SD(_name, _id, _parent, _offset) \ -- cgit v1.3-14-g43fede From 41ceeb5fef7719474a17a5a6052cae5b6c9e37c0 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Mon, 29 Jan 2018 19:01:50 +0100 Subject: clk: renesas: rcar-gen3: Add Z2 clock divider support This patch adds Z2 clock divider support for R-Car Gen3 SoC. Signed-off-by: Takeshi Kihara Signed-off-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rcar-gen3-cpg.c | 22 ++++++++++++++++------ drivers/clk/renesas/rcar-gen3-cpg.h | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index b85918fa62c6..0c8fe10d57fe 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -63,7 +63,7 @@ static void cpg_simple_notifier_register(struct raw_notifier_head *notifiers, } /* - * Z Clock + * Z Clock & Z2 Clock * * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared @@ -75,11 +75,13 @@ static void cpg_simple_notifier_register(struct raw_notifier_head *notifiers, #define CPG_FRQCRB_KICK BIT(31) #define CPG_FRQCRC 0x000000e0 #define CPG_FRQCRC_ZFC_MASK GENMASK(12, 8) +#define CPG_FRQCRC_Z2FC_MASK GENMASK(4, 0) struct cpg_z_clk { struct clk_hw hw; void __iomem *reg; void __iomem *kick_reg; + unsigned long mask; }; #define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw) @@ -89,8 +91,10 @@ static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw, { struct cpg_z_clk *zclk = to_z_clk(hw); unsigned int mult; + u32 val; - mult = 32 - FIELD_GET(CPG_FRQCRC_ZFC_MASK, clk_readl(zclk->reg)); + val = clk_readl(zclk->reg) & zclk->mask; + mult = 32 - (val >> __ffs(zclk->mask)); /* Factor of 2 is for fixed divider */ return DIV_ROUND_CLOSEST_ULL((u64)parent_rate * mult, 32 * 2); @@ -124,8 +128,8 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK) return -EBUSY; - val = clk_readl(zclk->reg) & ~CPG_FRQCRC_ZFC_MASK; - val |= FIELD_PREP(CPG_FRQCRC_ZFC_MASK, 32 - mult); + val = clk_readl(zclk->reg) & ~zclk->mask; + val |= ((32 - mult) << __ffs(zclk->mask)) & zclk->mask; clk_writel(val, zclk->reg); /* @@ -163,7 +167,8 @@ static const struct clk_ops cpg_z_clk_ops = { static struct clk * __init cpg_z_clk_register(const char *name, const char *parent_name, - void __iomem *reg) + void __iomem *reg, + unsigned long mask) { struct clk_init_data init; struct cpg_z_clk *zclk; @@ -182,6 +187,7 @@ static struct clk * __init cpg_z_clk_register(const char *name, zclk->reg = reg + CPG_FRQCRC; zclk->kick_reg = reg + CPG_FRQCRB; zclk->hw.init = &init; + zclk->mask = mask; clk = clk_register(NULL, &zclk->hw); if (IS_ERR(clk)) @@ -551,7 +557,11 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, case CLK_TYPE_GEN3_Z: return cpg_z_clk_register(core->name, __clk_get_name(parent), - base); + base, CPG_FRQCRC_ZFC_MASK); + + case CLK_TYPE_GEN3_Z2: + return cpg_z_clk_register(core->name, __clk_get_name(parent), + base, CPG_FRQCRC_Z2FC_MASK); default: return ERR_PTR(-EINVAL); diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index c73d4d6fdc85..ea4f8fc3c4c9 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -22,6 +22,7 @@ enum rcar_gen3_clk_types { CLK_TYPE_GEN3_R, CLK_TYPE_GEN3_PE, CLK_TYPE_GEN3_Z, + CLK_TYPE_GEN3_Z2, }; #define DEF_GEN3_SD(_name, _id, _parent, _offset) \ -- cgit v1.3-14-g43fede From 4003508b4f233104a17150463604dc9c36833815 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Mon, 29 Jan 2018 19:01:51 +0100 Subject: clk: renesas: r8a7795: Add Z clock This patch adds Z clock for R8A7795 SoC. Signed-off-by: Takeshi Kihara Signed-off-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a7795-cpg-mssr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index b1d9f48eae9e..995a4c4fb01e 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -74,6 +74,7 @@ static struct cpg_core_clk r8a7795_core_clks[] __initdata = { DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1), /* Core Clock Outputs */ + DEF_BASE("z", R8A7795_CLK_Z, CLK_TYPE_GEN3_Z, CLK_PLL0), DEF_FIXED("ztr", R8A7795_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), DEF_FIXED("ztrd2", R8A7795_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), DEF_FIXED("zt", R8A7795_CLK_ZT, CLK_PLL1_DIV2, 4, 1), -- cgit v1.3-14-g43fede From 1eadca3557f77afbb64100e077f48ac338d731dc Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Mon, 29 Jan 2018 19:01:52 +0100 Subject: clk: renesas: r8a7795: Add Z2 clock This patch adds Z2 clock for r8a7795 SoC. Signed-off-by: Takeshi Kihara Signed-off-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a7795-cpg-mssr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index 995a4c4fb01e..775b0ceaa337 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -75,6 +75,7 @@ static struct cpg_core_clk r8a7795_core_clks[] __initdata = { /* Core Clock Outputs */ DEF_BASE("z", R8A7795_CLK_Z, CLK_TYPE_GEN3_Z, CLK_PLL0), + DEF_BASE("z2", R8A7795_CLK_Z2, CLK_TYPE_GEN3_Z2, CLK_PLL2), DEF_FIXED("ztr", R8A7795_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), DEF_FIXED("ztrd2", R8A7795_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), DEF_FIXED("zt", R8A7795_CLK_ZT, CLK_PLL1_DIV2, 4, 1), -- cgit v1.3-14-g43fede From 72f2a6b31544da1978ab9fb032d0e17ded4af4a7 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Mon, 29 Jan 2018 19:01:53 +0100 Subject: clk: renesas: r8a7796: Add Z clock This patch adds Z clock for R8A7796 SoC. Signed-off-by: Takeshi Kihara Signed-off-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a7796-cpg-mssr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index 41e29734126b..799a9e574e79 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -74,6 +74,7 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = { DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1), /* Core Clock Outputs */ + DEF_BASE("z", R8A7796_CLK_Z, CLK_TYPE_GEN3_Z, CLK_PLL0), DEF_FIXED("ztr", R8A7796_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), DEF_FIXED("ztrd2", R8A7796_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), DEF_FIXED("zt", R8A7796_CLK_ZT, CLK_PLL1_DIV2, 4, 1), -- cgit v1.3-14-g43fede From c50378efa9aa16acfe16d7313d29a874c2c86e5e Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Mon, 29 Jan 2018 19:01:54 +0100 Subject: clk: renesas: r8a7796: Add Z2 clock This patch adds Z2 clock for R8A7796 SoC. Signed-off-by: Takeshi Kihara Signed-off-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a7796-cpg-mssr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index 799a9e574e79..dfb267a92f2a 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -75,6 +75,7 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = { /* Core Clock Outputs */ DEF_BASE("z", R8A7796_CLK_Z, CLK_TYPE_GEN3_Z, CLK_PLL0), + DEF_BASE("z2", R8A7796_CLK_Z2, CLK_TYPE_GEN3_Z2, CLK_PLL2), DEF_FIXED("ztr", R8A7796_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), DEF_FIXED("ztrd2", R8A7796_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), DEF_FIXED("zt", R8A7796_CLK_ZT, CLK_PLL1_DIV2, 4, 1), -- cgit v1.3-14-g43fede From d6d854cc1c2a055afdf5d22ee4aee0d20a59968c Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Mon, 8 Jan 2018 11:20:42 +0100 Subject: ARM: dts: STi: Fix bindings notation Remove leading 0x and 0s from bindings notation Add missing unit-address and remove some which are useless. This allows to fix several warnings like : Warning (unit_address_vs_reg): Node XXXX has a reg or ranges property, but no unit name Warning (simple_bus_reg): Node XXXX simple-bus unit address format error, expected "123456" Warning (unit_address_vs_reg): Node XXXX has a unit name, but no reg property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407-b2120.dts | 2 +- arch/arm/boot/dts/stih407-clock.dtsi | 4 ++-- arch/arm/boot/dts/stih407-family.dtsi | 8 ++++---- arch/arm/boot/dts/stih407-pinctrl.dtsi | 10 +++++----- arch/arm/boot/dts/stih410-b2120.dts | 2 +- arch/arm/boot/dts/stih410-b2260.dts | 4 ++-- arch/arm/boot/dts/stih410-clock.dtsi | 4 ++-- arch/arm/boot/dts/stih410-pinctrl.dtsi | 2 +- arch/arm/boot/dts/stih410.dtsi | 2 +- arch/arm/boot/dts/stih418-b2199.dts | 4 ++-- arch/arm/boot/dts/stih418-clock.dtsi | 4 ++-- arch/arm/boot/dts/stihxxx-b2120.dtsi | 4 ++-- 12 files changed, 25 insertions(+), 25 deletions(-) diff --git a/arch/arm/boot/dts/stih407-b2120.dts b/arch/arm/boot/dts/stih407-b2120.dts index c8ad905d0309..cf8bc8a8b947 100644 --- a/arch/arm/boot/dts/stih407-b2120.dts +++ b/arch/arm/boot/dts/stih407-b2120.dts @@ -18,7 +18,7 @@ linux,stdout-path = &sbc_serial0; }; - memory { + memory@40000000 { device_type = "memory"; reg = <0x40000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/stih407-clock.dtsi b/arch/arm/boot/dts/stih407-clock.dtsi index d0a24d9e517a..b882dcf3a649 100644 --- a/arch/arm/boot/dts/stih407-clock.dtsi +++ b/arch/arm/boot/dts/stih407-clock.dtsi @@ -83,7 +83,7 @@ * Bootloader initialized system infrastructure clock for * serial devices. */ - clk_ext2f_a9: clockgen-c0@13 { + clk_ext2f_a9: clockgen-c0 { #clock-cells = <0>; compatible = "fixed-clock"; clock-frequency = <200000000>; @@ -260,7 +260,7 @@ clock-frequency = <0>; }; - clockgen-d2@x9106000 { + clockgen-d2@9106000 { compatible = "st,clkgen-c32"; reg = <0x9106000 0x1000>; diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi index cf3756976c39..1608c70f05a9 100644 --- a/arch/arm/boot/dts/stih407-family.dtsi +++ b/arch/arm/boot/dts/stih407-family.dtsi @@ -92,7 +92,7 @@ clocks = <&arm_periph_clk>; }; - l2: cache-controller { + l2: cache-controller@8762000 { compatible = "arm,pl310-cache"; reg = <0x08762000 0x1000>; arm,data-latency = <3 3 3>; @@ -389,7 +389,7 @@ reset-names = "global", "port"; }; - miphy28lp_phy: miphy28lp@9b22000 { + miphy28lp_phy: miphy28lp { compatible = "st,miphy28lp-phy"; st,syscfg = <&syscfg_core>; #address-cells = <1>; @@ -803,7 +803,7 @@ status = "okay"; }; - st231_gp0: st231-gp0@0 { + st231_gp0: st231-gp0 { compatible = "st,st231-rproc"; memory-region = <&gp0_reserved>; resets = <&softreset STIH407_ST231_GP0_SOFTRESET>; @@ -816,7 +816,7 @@ mboxes = <&mailbox0 0 2>, <&mailbox2 0 1>, <&mailbox0 0 3>, <&mailbox2 0 0>; }; - st231_delta: st231-delta@0 { + st231_delta: st231-delta { compatible = "st,st231-rproc"; memory-region = <&delta_reserved>; resets = <&softreset STIH407_ST231_DMU_SOFTRESET>; diff --git a/arch/arm/boot/dts/stih407-pinctrl.dtsi b/arch/arm/boot/dts/stih407-pinctrl.dtsi index a29090077fdf..53c6888d1fc0 100644 --- a/arch/arm/boot/dts/stih407-pinctrl.dtsi +++ b/arch/arm/boot/dts/stih407-pinctrl.dtsi @@ -45,7 +45,7 @@ }; soc { - pin-controller-sbc { + pin-controller-sbc@961f080 { #address-cells = <1>; #size-cells = <1>; compatible = "st,stih407-sbc-pinctrl"; @@ -369,7 +369,7 @@ }; }; - pin-controller-front0 { + pin-controller-front0@920f080 { #address-cells = <1>; #size-cells = <1>; compatible = "st,stih407-front-pinctrl"; @@ -929,7 +929,7 @@ }; }; - pin-controller-front1 { + pin-controller-front1@921f080 { #address-cells = <1>; #size-cells = <1>; compatible = "st,stih407-front-pinctrl"; @@ -962,7 +962,7 @@ }; }; - pin-controller-rear { + pin-controller-rear@922f080 { #address-cells = <1>; #size-cells = <1>; compatible = "st,stih407-rear-pinctrl"; @@ -1157,7 +1157,7 @@ }; }; - pin-controller-flash { + pin-controller-flash@923f080 { #address-cells = <1>; #size-cells = <1>; compatible = "st,stih407-flash-pinctrl"; diff --git a/arch/arm/boot/dts/stih410-b2120.dts b/arch/arm/boot/dts/stih410-b2120.dts index 9830be577433..37a42afa0dd1 100644 --- a/arch/arm/boot/dts/stih410-b2120.dts +++ b/arch/arm/boot/dts/stih410-b2120.dts @@ -18,7 +18,7 @@ linux,stdout-path = &sbc_serial0; }; - memory { + memory@40000000 { device_type = "memory"; reg = <0x40000000 0x80000000>; }; diff --git a/arch/arm/boot/dts/stih410-b2260.dts b/arch/arm/boot/dts/stih410-b2260.dts index c663b70c43a7..faafc7b12951 100644 --- a/arch/arm/boot/dts/stih410-b2260.dts +++ b/arch/arm/boot/dts/stih410-b2260.dts @@ -19,7 +19,7 @@ linux,stdout-path = &uart1; }; - memory { + memory@40000000 { device_type = "memory"; reg = <0x40000000 0x40000000>; }; @@ -201,7 +201,7 @@ }; }; - miphy28lp_phy: miphy28lp@9b22000 { + miphy28lp_phy: miphy28lp { phy_port1: port@9b2a000 { st,osc-force-ext; diff --git a/arch/arm/boot/dts/stih410-clock.dtsi b/arch/arm/boot/dts/stih410-clock.dtsi index fde5df17f575..4df1b2187aa2 100644 --- a/arch/arm/boot/dts/stih410-clock.dtsi +++ b/arch/arm/boot/dts/stih410-clock.dtsi @@ -85,7 +85,7 @@ * Bootloader initialized system infrastructure clock for * serial devices. */ - clk_ext2f_a9: clockgen-c0@13 { + clk_ext2f_a9: clockgen-c0 { #clock-cells = <0>; compatible = "fixed-clock"; clock-frequency = <200000000>; @@ -272,7 +272,7 @@ clock-frequency = <0>; }; - clockgen-d2@x9106000 { + clockgen-d2@9106000 { compatible = "st,clkgen-c32"; reg = <0x9106000 0x1000>; diff --git a/arch/arm/boot/dts/stih410-pinctrl.dtsi b/arch/arm/boot/dts/stih410-pinctrl.dtsi index b3e9dfc81c07..5ae1fd66c0b8 100644 --- a/arch/arm/boot/dts/stih410-pinctrl.dtsi +++ b/arch/arm/boot/dts/stih410-pinctrl.dtsi @@ -10,7 +10,7 @@ / { soc { - pin-controller-rear { + pin-controller-rear@922f080 { usb0 { pinctrl_usb0: usb2-0 { diff --git a/arch/arm/boot/dts/stih410.dtsi b/arch/arm/boot/dts/stih410.dtsi index 68b5ff91d6a7..e4b7e3ddc9ee 100644 --- a/arch/arm/boot/dts/stih410.dtsi +++ b/arch/arm/boot/dts/stih410.dtsi @@ -235,7 +235,7 @@ <&clk_s_d2_quadfs 1>; }; - sti-hqvdp@9c000000 { + sti-hqvdp@9c00000 { compatible = "st,stih407-hqvdp"; reg = <0x9C00000 0x100000>; clock-names = "hqvdp", "pix_main"; diff --git a/arch/arm/boot/dts/stih418-b2199.dts b/arch/arm/boot/dts/stih418-b2199.dts index 4e6d915c85ff..5418a0ece659 100644 --- a/arch/arm/boot/dts/stih418-b2199.dts +++ b/arch/arm/boot/dts/stih418-b2199.dts @@ -18,7 +18,7 @@ linux,stdout-path = &sbc_serial0; }; - memory { + memory@40000000 { device_type = "memory"; reg = <0x40000000 0xc0000000>; }; @@ -88,7 +88,7 @@ non-removable; }; - miphy28lp_phy: miphy28lp@9b22000 { + miphy28lp_phy: miphy28lp { phy_port0: port@9b22000 { st,osc-rdy; diff --git a/arch/arm/boot/dts/stih418-clock.dtsi b/arch/arm/boot/dts/stih418-clock.dtsi index 9a157c1a99b1..e68bf28bd038 100644 --- a/arch/arm/boot/dts/stih418-clock.dtsi +++ b/arch/arm/boot/dts/stih418-clock.dtsi @@ -85,7 +85,7 @@ * Bootloader initialized system infrastructure clock for * serial devices. */ - clk_ext2f_a9: clockgen-c0@13 { + clk_ext2f_a9: clockgen-c0 { #clock-cells = <0>; compatible = "fixed-clock"; clock-frequency = <200000000>; @@ -265,7 +265,7 @@ clock-frequency = <0>; }; - clockgen-d2@x9106000 { + clockgen-d2@9106000 { compatible = "st,clkgen-c32"; reg = <0x9106000 0x1000>; diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi index 7f80c2c414c8..68783e8223b8 100644 --- a/arch/arm/boot/dts/stihxxx-b2120.dtsi +++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi @@ -80,7 +80,7 @@ st,i2c-min-sda-pulse-width-us = <5>; }; - miphy28lp_phy: miphy28lp@9b22000 { + miphy28lp_phy: miphy28lp { phy_port0: port@9b22000 { st,osc-rdy; @@ -126,7 +126,7 @@ clock-names = "c8sectpfe"; /* tsin0 is TSA on NIMA */ - tsin0: port@0 { + tsin0: port { tsin-num = <0>; serial-not-parallel; i2c-bus = <&ssc2>; -- cgit v1.3-14-g43fede From 7aef6b3a2179a4ad536831b343a820a4b7221c33 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Tue, 9 Jan 2018 16:38:57 +0100 Subject: ARM: dts: STi: Move leds node outside soc node Leds are not part of soc, so nove them outside soc node. This allows to fix the following warnings when compiling dtb with W=1 option : arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/leds missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/leds missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/leds missing or empty reg/ranges property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih410-b2260.dts | 49 ++++++++++++++++++------------------ arch/arm/boot/dts/stih418-b2199.dts | 26 +++++++++---------- arch/arm/boot/dts/stihxxx-b2120.dtsi | 26 +++++++++---------- 3 files changed, 50 insertions(+), 51 deletions(-) diff --git a/arch/arm/boot/dts/stih410-b2260.dts b/arch/arm/boot/dts/stih410-b2260.dts index faafc7b12951..69c2abcaeda8 100644 --- a/arch/arm/boot/dts/stih410-b2260.dts +++ b/arch/arm/boot/dts/stih410-b2260.dts @@ -29,36 +29,35 @@ ethernet0 = ðernet0; }; - soc { - - leds { - compatible = "gpio-leds"; - user_green_1 { - label = "User_green_1"; - gpios = <&pio1 3 GPIO_ACTIVE_LOW>; - linux,default-trigger = "heartbeat"; - default-state = "off"; - }; + leds { + compatible = "gpio-leds"; + user_green_1 { + label = "User_green_1"; + gpios = <&pio1 3 GPIO_ACTIVE_LOW>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; - user_green_2 { - label = "User_green_2"; - gpios = <&pio4 1 GPIO_ACTIVE_LOW>; - default-state = "off"; - }; + user_green_2 { + label = "User_green_2"; + gpios = <&pio4 1 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; - user_green_3 { - label = "User_green_3"; - gpios = <&pio2 1 GPIO_ACTIVE_LOW>; - default-state = "off"; - }; + user_green_3 { + label = "User_green_3"; + gpios = <&pio2 1 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; - user_green_4 { - label = "User_green_4"; - gpios = <&pio2 5 GPIO_ACTIVE_LOW>; - default-state = "off"; - }; + user_green_4 { + label = "User_green_4"; + gpios = <&pio2 5 GPIO_ACTIVE_LOW>; + default-state = "off"; }; + }; + soc { /* Low speed expansion connector */ uart0: serial@9830000 { label = "LS-UART0"; diff --git a/arch/arm/boot/dts/stih418-b2199.dts b/arch/arm/boot/dts/stih418-b2199.dts index 5418a0ece659..1ce38ce79952 100644 --- a/arch/arm/boot/dts/stih418-b2199.dts +++ b/arch/arm/boot/dts/stih418-b2199.dts @@ -28,24 +28,24 @@ ethernet0 = ðernet0; }; + leds { + compatible = "gpio-leds"; + red { + label = "Front Panel LED"; + gpios = <&pio4 1 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + green { + gpios = <&pio1 3 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; + soc { sbc_serial0: serial@9530000 { status = "okay"; }; - leds { - compatible = "gpio-leds"; - red { - label = "Front Panel LED"; - gpios = <&pio4 1 GPIO_ACTIVE_HIGH>; - linux,default-trigger = "heartbeat"; - }; - green { - gpios = <&pio1 3 GPIO_ACTIVE_HIGH>; - default-state = "off"; - }; - }; - i2c@9842000 { status = "okay"; }; diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi index 68783e8223b8..1fd3a2b5b938 100644 --- a/arch/arm/boot/dts/stihxxx-b2120.dtsi +++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi @@ -10,24 +10,24 @@ #include #include / { + leds { + compatible = "gpio-leds"; + red { + label = "Front Panel LED"; + gpios = <&pio4 1 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + green { + gpios = <&pio1 3 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; + soc { sbc_serial0: serial@9530000 { status = "okay"; }; - leds { - compatible = "gpio-leds"; - red { - label = "Front Panel LED"; - gpios = <&pio4 1 GPIO_ACTIVE_HIGH>; - linux,default-trigger = "heartbeat"; - }; - green { - gpios = <&pio1 3 GPIO_ACTIVE_HIGH>; - default-state = "off"; - }; - }; - pwm0: pwm@9810000 { status = "okay"; }; -- cgit v1.3-14-g43fede From 0b09a91a0d053c78429a0f0ffd169d7afcba70a7 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Tue, 9 Jan 2018 17:47:47 +0100 Subject: ARM: dts: STi: Add fake reg property for sti-display-subsystem As sti-display-subsystem sub-nodes (sti-compositor, sti-tvout sti-hdmi, sti-hda and sti-hqvdp) are SoC's IP, we add a fake reg property. This allows to fix the following warning when compiling dtb with W=1 option: arch/arm/boot/dts/stih410-b2120.dtb: Warning (unit_address_vs_reg): Node /soc/sti-display-subsystem/sti-hda@8d02000 has a unit name, but no reg property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407.dtsi | 4 ++-- arch/arm/boot/dts/stih410-b2120.dts | 2 +- arch/arm/boot/dts/stih410.dtsi | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/stih407.dtsi b/arch/arm/boot/dts/stih407.dtsi index 11fdecd9312e..4f01777082f5 100644 --- a/arch/arm/boot/dts/stih407.dtsi +++ b/arch/arm/boot/dts/stih407.dtsi @@ -11,11 +11,11 @@ #include / { soc { - sti-display-subsystem { + sti-display-subsystem@0 { compatible = "st,sti-display-subsystem"; #address-cells = <1>; #size-cells = <1>; - + reg = <0 0>; assigned-clocks = <&clk_s_d2_quadfs 0>, <&clk_s_d2_quadfs 1>, <&clk_s_c0_pll1 0>, diff --git a/arch/arm/boot/dts/stih410-b2120.dts b/arch/arm/boot/dts/stih410-b2120.dts index 37a42afa0dd1..23199b1b0991 100644 --- a/arch/arm/boot/dts/stih410-b2120.dts +++ b/arch/arm/boot/dts/stih410-b2120.dts @@ -61,7 +61,7 @@ status = "okay"; }; - sti-display-subsystem { + sti-display-subsystem@0 { sti-hda@8d02000 { status = "okay"; }; diff --git a/arch/arm/boot/dts/stih410.dtsi b/arch/arm/boot/dts/stih410.dtsi index e4b7e3ddc9ee..d9f964702933 100644 --- a/arch/arm/boot/dts/stih410.dtsi +++ b/arch/arm/boot/dts/stih410.dtsi @@ -102,11 +102,12 @@ status = "disabled"; }; - sti-display-subsystem { + sti-display-subsystem@0 { compatible = "st,sti-display-subsystem"; #address-cells = <1>; #size-cells = <1>; + reg = <0 0>; assigned-clocks = <&clk_s_d2_quadfs 0>, <&clk_s_d2_quadfs 1>, <&clk_s_c0_pll1 0>, -- cgit v1.3-14-g43fede From b2d81762ce896e488e79abe292b8700b8ba1a303 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Thu, 18 Jan 2018 17:34:59 +0100 Subject: ARM: dts: STi: Add fake reg property for miphy28lp_phy Add fake reg property to miphy28lp_phy. This allows to fix the following warning when compiling dtb with W=1 option: arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/miphy28lp missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/miphy28lp missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/miphy28lp missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/miphy28lp missing or empty reg/ranges property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407-family.dtsi | 3 ++- arch/arm/boot/dts/stih410-b2260.dts | 2 +- arch/arm/boot/dts/stih418-b2199.dts | 2 +- arch/arm/boot/dts/stihxxx-b2120.dtsi | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi index 1608c70f05a9..e279cd07ba67 100644 --- a/arch/arm/boot/dts/stih407-family.dtsi +++ b/arch/arm/boot/dts/stih407-family.dtsi @@ -389,12 +389,13 @@ reset-names = "global", "port"; }; - miphy28lp_phy: miphy28lp { + miphy28lp_phy: miphy28lp@0 { compatible = "st,miphy28lp-phy"; st,syscfg = <&syscfg_core>; #address-cells = <1>; #size-cells = <1>; ranges; + reg = <0 0>; phy_port0: port@9b22000 { reg = <0x9b22000 0xff>, diff --git a/arch/arm/boot/dts/stih410-b2260.dts b/arch/arm/boot/dts/stih410-b2260.dts index 69c2abcaeda8..cea5c840ca9f 100644 --- a/arch/arm/boot/dts/stih410-b2260.dts +++ b/arch/arm/boot/dts/stih410-b2260.dts @@ -200,7 +200,7 @@ }; }; - miphy28lp_phy: miphy28lp { + miphy28lp_phy: miphy28lp@0 { phy_port1: port@9b2a000 { st,osc-force-ext; diff --git a/arch/arm/boot/dts/stih418-b2199.dts b/arch/arm/boot/dts/stih418-b2199.dts index 1ce38ce79952..be0bbb05c5ec 100644 --- a/arch/arm/boot/dts/stih418-b2199.dts +++ b/arch/arm/boot/dts/stih418-b2199.dts @@ -88,7 +88,7 @@ non-removable; }; - miphy28lp_phy: miphy28lp { + miphy28lp_phy: miphy28lp@0 { phy_port0: port@9b22000 { st,osc-rdy; diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi index 1fd3a2b5b938..66c1c6a5eb76 100644 --- a/arch/arm/boot/dts/stihxxx-b2120.dtsi +++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi @@ -80,7 +80,7 @@ st,i2c-min-sda-pulse-width-us = <5>; }; - miphy28lp_phy: miphy28lp { + miphy28lp_phy: miphy28lp@0 { phy_port0: port@9b22000 { st,osc-rdy; -- cgit v1.3-14-g43fede From 07c5e5c1b198f05690bce7586b6872f159c438d1 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Thu, 18 Jan 2018 17:48:01 +0100 Subject: ARM: dts: STi: Add fake reg property for irq-syscfg Add fake reg property to irq-syscfg node. This allows to fix the following warning when compiling dtb with W=1 option: arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/irq-syscfg missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/irq-syscfg missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/irq-syscfg missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/irq-syscfg missing or empty reg/ranges property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407-family.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi index e279cd07ba67..c8dc659fd14e 100644 --- a/arch/arm/boot/dts/stih407-family.dtsi +++ b/arch/arm/boot/dts/stih407-family.dtsi @@ -181,8 +181,9 @@ reg = <0x94b5100 0x1000>; }; - irq-syscfg { + irq-syscfg@0 { compatible = "st,stih407-irq-syscfg"; + reg = <0 0>; st,syscfg = <&syscfg_core>; st,irq-device = , ; -- cgit v1.3-14-g43fede From 5eccdffb61570de0bdaebc19dfbe963f3eaabe4b Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 19 Jan 2018 09:09:05 +0100 Subject: ARM: dts: STi: Add fake reg property for remote processors As st231-gp0, st231-delta are part of SoC, we add a fake reg property instead of moving all these nodes outside soc node. This allows to fix the following warning when compiling dtb with W=1 option: arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/st231-gp0 missing or empty reg/ranges property arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/st231-delta missing or empty reg/ranges property arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/delta0 missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/st231-gp0 missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/st231-delta missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/delta0 missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/st231-gp0 missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/st231-delta missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/delta0 missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/st231-gp0 missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/st231-delta missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/delta0 missing or empty reg/ranges property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407-family.dtsi | 9 ++++++--- arch/arm/boot/dts/stih410.dtsi | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi index c8dc659fd14e..5a0bad07da9e 100644 --- a/arch/arm/boot/dts/stih407-family.dtsi +++ b/arch/arm/boot/dts/stih407-family.dtsi @@ -805,8 +805,9 @@ status = "okay"; }; - st231_gp0: st231-gp0 { + st231_gp0: st231-gp0@0 { compatible = "st,st231-rproc"; + reg = <0 0>; memory-region = <&gp0_reserved>; resets = <&softreset STIH407_ST231_GP0_SOFTRESET>; reset-names = "sw_reset"; @@ -818,8 +819,9 @@ mboxes = <&mailbox0 0 2>, <&mailbox2 0 1>, <&mailbox0 0 3>, <&mailbox2 0 0>; }; - st231_delta: st231-delta { + st231_delta: st231-delta@0 { compatible = "st,st231-rproc"; + reg = <0 0>; memory-region = <&delta_reserved>; resets = <&softreset STIH407_ST231_DMU_SOFTRESET>; reset-names = "sw_reset"; @@ -982,8 +984,9 @@ status = "disabled"; }; - delta0 { + delta0@0 { compatible = "st,st-delta"; + reg = <0 0>; clock-names = "delta", "delta-st231", "delta-flash-promip"; diff --git a/arch/arm/boot/dts/stih410.dtsi b/arch/arm/boot/dts/stih410.dtsi index d9f964702933..815df2f7c103 100644 --- a/arch/arm/boot/dts/stih410.dtsi +++ b/arch/arm/boot/dts/stih410.dtsi @@ -274,7 +274,7 @@ interrupts = ; }; - delta0 { + delta0@0 { compatible = "st,st-delta"; clock-names = "delta", "delta-st231", -- cgit v1.3-14-g43fede From a388871750f63e93b26cc475bf8a64309dc95031 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 19 Jan 2018 09:57:39 +0100 Subject: ARM: dts: STi: Add fake reg for restart, powerdown and picophy/softreset Add fake reg property for restart, powerdown, picophyreset and softreset. This allows to fix the following warning when compiling dtb with W=1 option: arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/restart missing or empty reg/ranges property arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/powerdown-controller missing or empty reg/ranges property arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/softreset-controller missing or empty reg/ranges property arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/picophyreset-controller missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/restart missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/powerdown-controller missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/softreset-controller missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/picophyreset-controller missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/restart missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/powerdown-controller missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/softreset-controller missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/picophyreset-controller missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/restart missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/powerdown-controller missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/softreset-controller missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/picophyreset-controller missing or empty reg/ranges property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407-family.dtsi | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi index 5a0bad07da9e..00a3838236d1 100644 --- a/arch/arm/boot/dts/stih407-family.dtsi +++ b/arch/arm/boot/dts/stih407-family.dtsi @@ -125,24 +125,28 @@ ranges; compatible = "simple-bus"; - restart { + restart: restart-controller@0 { compatible = "st,stih407-restart"; + reg = <0 0>; st,syscfg = <&syscfg_sbc_reg>; status = "okay"; }; - powerdown: powerdown-controller { + powerdown: powerdown-controller@0 { compatible = "st,stih407-powerdown"; + reg = <0 0>; #reset-cells = <1>; }; - softreset: softreset-controller { + softreset: softreset-controller@0 { compatible = "st,stih407-softreset"; + reg = <0 0>; #reset-cells = <1>; }; - picophyreset: picophyreset-controller { + picophyreset: picophyreset-controller@0 { compatible = "st,stih407-picophyreset"; + reg = <0 0>; #reset-cells = <1>; }; -- cgit v1.3-14-g43fede From 1d91958fbe11d11a0c982e12616ed23f935852f1 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 19 Jan 2018 11:18:19 +0100 Subject: ARM: dts: STi: Add fake reg property for usb2_picophyX nodes Add fake reg property for usb2_picophy nodes. This allows to fix the following warning when compiling dtb with W=1 option : arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/phy2 missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/phy3 missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/phy2 missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/phy3 missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/phy2 missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/phy3 missing or empty reg/ranges property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407-family.dtsi | 3 ++- arch/arm/boot/dts/stih410-b2120.dts | 4 ++-- arch/arm/boot/dts/stih410-b2260.dts | 4 ++-- arch/arm/boot/dts/stih410.dtsi | 6 ++++-- arch/arm/boot/dts/stih418.dtsi | 6 ++++-- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi index 00a3838236d1..5df827b00eb6 100644 --- a/arch/arm/boot/dts/stih407-family.dtsi +++ b/arch/arm/boot/dts/stih407-family.dtsi @@ -385,8 +385,9 @@ status = "disabled"; }; - usb2_picophy0: phy1 { + usb2_picophy0: phy1@0 { compatible = "st,stih407-usb2-phy"; + reg = <0 0>; #phy-cells = <0>; st,syscfg = <&syscfg_core 0x100 0xf4>; resets = <&softreset STIH407_PICOPHY_SOFTRESET>, diff --git a/arch/arm/boot/dts/stih410-b2120.dts b/arch/arm/boot/dts/stih410-b2120.dts index 23199b1b0991..d1d908b9e34c 100644 --- a/arch/arm/boot/dts/stih410-b2120.dts +++ b/arch/arm/boot/dts/stih410-b2120.dts @@ -37,11 +37,11 @@ sd-uhs-ddr50; }; - usb2_picophy1: phy2 { + usb2_picophy1: phy2@0 { status = "okay"; }; - usb2_picophy2: phy3 { + usb2_picophy2: phy3@0 { status = "okay"; }; diff --git a/arch/arm/boot/dts/stih410-b2260.dts b/arch/arm/boot/dts/stih410-b2260.dts index cea5c840ca9f..8bcd58118dba 100644 --- a/arch/arm/boot/dts/stih410-b2260.dts +++ b/arch/arm/boot/dts/stih410-b2260.dts @@ -127,11 +127,11 @@ status = "okay"; }; - usb2_picophy1: phy2 { + usb2_picophy1: phy2@0 { status = "okay"; }; - usb2_picophy2: phy3 { + usb2_picophy2: phy3@0 { status = "okay"; }; diff --git a/arch/arm/boot/dts/stih410.dtsi b/arch/arm/boot/dts/stih410.dtsi index 815df2f7c103..bfbc73743b29 100644 --- a/arch/arm/boot/dts/stih410.dtsi +++ b/arch/arm/boot/dts/stih410.dtsi @@ -16,8 +16,9 @@ }; soc { - usb2_picophy1: phy2 { + usb2_picophy1: phy2@0 { compatible = "st,stih407-usb2-phy"; + reg = <0 0>; #phy-cells = <0>; st,syscfg = <&syscfg_core 0xf8 0xf4>; resets = <&softreset STIH407_PICOPHY_SOFTRESET>, @@ -27,8 +28,9 @@ status = "disabled"; }; - usb2_picophy2: phy3 { + usb2_picophy2: phy3@0 { compatible = "st,stih407-usb2-phy"; + reg = <0 0>; #phy-cells = <0>; st,syscfg = <&syscfg_core 0xfc 0xf4>; resets = <&softreset STIH407_PICOPHY_SOFTRESET>, diff --git a/arch/arm/boot/dts/stih418.dtsi b/arch/arm/boot/dts/stih418.dtsi index e6525ab4d9bb..0efb3cd6a86e 100644 --- a/arch/arm/boot/dts/stih418.dtsi +++ b/arch/arm/boot/dts/stih418.dtsi @@ -30,8 +30,9 @@ }; soc { - usb2_picophy1: phy2 { + usb2_picophy1: phy2@0 { compatible = "st,stih407-usb2-phy"; + reg = <0 0>; #phy-cells = <0>; st,syscfg = <&syscfg_core 0xf8 0xf4>; resets = <&softreset STIH407_PICOPHY_SOFTRESET>, @@ -39,8 +40,9 @@ reset-names = "global", "port"; }; - usb2_picophy2: phy3 { + usb2_picophy2: phy3@0 { compatible = "st,stih407-usb2-phy"; + reg = <0 0>; #phy-cells = <0>; st,syscfg = <&syscfg_core 0xfc 0xf4>; resets = <&softreset STIH407_PICOPHY_SOFTRESET>, -- cgit v1.3-14-g43fede From bd6f2fd5a1d52198468c5cdc3c2472362dff5aaa Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jan 2018 18:36:16 -0800 Subject: of: Support parsing phandle argument lists through a nexus node Platforms like 96boards have a standardized connector/expansion slot that exposes signals like GPIOs to expansion boards in an SoC agnostic way. We'd like the DT overlays for the expansion boards to be written once without knowledge of the SoC on the other side of the connector. This avoids the unscalable combinatorial explosion of a different DT overlay for each expansion board and SoC pair. We need a way to describe the GPIOs routed through the connector in an SoC agnostic way. Let's introduce nexus property parsing into the OF core to do this. This is largely based on the interrupt nexus support we already have. This allows us to remap a phandle list in a consumer node (e.g. reset-gpios) through a connector in a generic way (e.g. via gpio-map). Do this in a generic routine so that we can remap any sort of variable length phandle list. Taking GPIOs as an example, the connector would be a GPIO nexus, supporting the remapping of a GPIO specifier space to multiple GPIO providers on the SoC. DT would look as shown below, where 'soc_gpio1' and 'soc_gpio2' are inside the SoC, 'connector' is an expansion port where boards can be plugged in, and 'expansion_device' is a device on the expansion board. soc { soc_gpio1: gpio-controller1 { #gpio-cells = <2>; }; soc_gpio2: gpio-controller2 { #gpio-cells = <2>; }; }; connector: connector { #gpio-cells = <2>; gpio-map = <0 0 &soc_gpio1 1 0>, <1 0 &soc_gpio2 4 0>, <2 0 &soc_gpio1 3 0>, <3 0 &soc_gpio2 2 0>; gpio-map-mask = <0xf 0x0>; gpio-map-pass-thru = <0x0 0x1> }; expansion_device { reset-gpios = <&connector 2 GPIO_ACTIVE_LOW>; }; The GPIO core would use of_parse_phandle_with_args_map() instead of of_parse_phandle_with_args() and arrive at the same type of result, a phandle and argument list. The difference is that the phandle and arguments will be remapped through the nexus node to the underlying SoC GPIO controller node. In the example above, we would remap 'reset-gpios' from <&connector 2 GPIO_ACTIVE_LOW> to <&soc_gpio1 3 GPIO_ACTIVE_LOW>. Cc: Pantelis Antoniou Cc: Linus Walleij Cc: Mark Brown Signed-off-by: Stephen Boyd Signed-off-by: Rob Herring --- drivers/of/base.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of.h | 12 ++++ 2 files changed, 196 insertions(+) diff --git a/drivers/of/base.c b/drivers/of/base.c index ad28de96e13f..091aa9449c3a 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1283,6 +1283,190 @@ int of_parse_phandle_with_args(const struct device_node *np, const char *list_na } EXPORT_SYMBOL(of_parse_phandle_with_args); +/** + * of_parse_phandle_with_args_map() - Find a node pointed by phandle in a list and remap it + * @np: pointer to a device tree node containing a list + * @list_name: property name that contains a list + * @stem_name: stem of property names that specify phandles' arguments count + * @index: index of a phandle to parse out + * @out_args: optional pointer to output arguments structure (will be filled) + * + * This function is useful to parse lists of phandles and their arguments. + * Returns 0 on success and fills out_args, on error returns appropriate errno + * value. The difference between this function and of_parse_phandle_with_args() + * is that this API remaps a phandle if the node the phandle points to has + * a <@stem_name>-map property. + * + * Caller is responsible to call of_node_put() on the returned out_args->np + * pointer. + * + * Example: + * + * phandle1: node1 { + * #list-cells = <2>; + * } + * + * phandle2: node2 { + * #list-cells = <1>; + * } + * + * phandle3: node3 { + * #list-cells = <1>; + * list-map = <0 &phandle2 3>, + * <1 &phandle2 2>, + * <2 &phandle1 5 1>; + * list-map-mask = <0x3>; + * }; + * + * node4 { + * list = <&phandle1 1 2 &phandle3 0>; + * } + * + * To get a device_node of the `node2' node you may call this: + * of_parse_phandle_with_args(node4, "list", "list", 1, &args); + */ +int of_parse_phandle_with_args_map(const struct device_node *np, + const char *list_name, + const char *stem_name, + int index, struct of_phandle_args *out_args) +{ + char *cells_name, *map_name = NULL, *mask_name = NULL; + char *pass_name = NULL; + struct device_node *cur, *new = NULL; + const __be32 *map, *mask, *pass; + static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 }; + static const __be32 dummy_pass[] = { [0 ... MAX_PHANDLE_ARGS] = 0 }; + __be32 initial_match_array[MAX_PHANDLE_ARGS]; + const __be32 *match_array = initial_match_array; + int i, ret, map_len, match; + u32 list_size, new_size; + + if (index < 0) + return -EINVAL; + + cells_name = kasprintf(GFP_KERNEL, "#%s-cells", stem_name); + if (!cells_name) + return -ENOMEM; + + ret = -ENOMEM; + map_name = kasprintf(GFP_KERNEL, "%s-map", stem_name); + if (!map_name) + goto free; + + mask_name = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name); + if (!mask_name) + goto free; + + pass_name = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name); + if (!pass_name) + goto free; + + ret = __of_parse_phandle_with_args(np, list_name, cells_name, 0, index, + out_args); + if (ret) + goto free; + + /* Get the #-cells property */ + cur = out_args->np; + ret = of_property_read_u32(cur, cells_name, &list_size); + if (ret < 0) + goto put; + + /* Precalculate the match array - this simplifies match loop */ + for (i = 0; i < list_size; i++) + initial_match_array[i] = cpu_to_be32(out_args->args[i]); + + ret = -EINVAL; + while (cur) { + /* Get the -map property */ + map = of_get_property(cur, map_name, &map_len); + if (!map) { + ret = 0; + goto free; + } + map_len /= sizeof(u32); + + /* Get the -map-mask property (optional) */ + mask = of_get_property(cur, mask_name, NULL); + if (!mask) + mask = dummy_mask; + /* Iterate through -map property */ + match = 0; + while (map_len > (list_size + 1) && !match) { + /* Compare specifiers */ + match = 1; + for (i = 0; i < list_size; i++, map_len--) + match &= !((match_array[i] ^ *map++) & mask[i]); + + of_node_put(new); + new = of_find_node_by_phandle(be32_to_cpup(map)); + map++; + map_len--; + + /* Check if not found */ + if (!new) + goto put; + + if (!of_device_is_available(new)) + match = 0; + + ret = of_property_read_u32(new, cells_name, &new_size); + if (ret) + goto put; + + /* Check for malformed properties */ + if (WARN_ON(new_size > MAX_PHANDLE_ARGS)) + goto put; + if (map_len < new_size) + goto put; + + /* Move forward by new node's #-cells amount */ + map += new_size; + map_len -= new_size; + } + if (!match) + goto put; + + /* Get the -map-pass-thru property (optional) */ + pass = of_get_property(cur, pass_name, NULL); + if (!pass) + pass = dummy_pass; + + /* + * Successfully parsed a -map translation; copy new + * specifier into the out_args structure, keeping the + * bits specified in -map-pass-thru. + */ + match_array = map - new_size; + for (i = 0; i < new_size; i++) { + __be32 val = *(map - new_size + i); + + if (i < list_size) { + val &= ~pass[i]; + val |= cpu_to_be32(out_args->args[i]) & pass[i]; + } + + out_args->args[i] = be32_to_cpu(val); + } + out_args->args_count = list_size = new_size; + /* Iterate again with new provider */ + out_args->np = new; + of_node_put(cur); + cur = new; + } +put: + of_node_put(cur); + of_node_put(new); +free: + kfree(mask_name); + kfree(map_name); + kfree(cells_name); + kfree(pass_name); + + return ret; +} +EXPORT_SYMBOL(of_parse_phandle_with_args_map); + /** * of_parse_phandle_with_fixed_args() - Find a node pointed by phandle in a list * @np: pointer to a device tree node containing a list diff --git a/include/linux/of.h b/include/linux/of.h index da1ee95241c1..7258bbc85e4e 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -363,6 +363,9 @@ extern struct device_node *of_parse_phandle(const struct device_node *np, extern int of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, int index, struct of_phandle_args *out_args); +extern int of_parse_phandle_with_args_map(const struct device_node *np, + const char *list_name, const char *stem_name, int index, + struct of_phandle_args *out_args); extern int of_parse_phandle_with_fixed_args(const struct device_node *np, const char *list_name, int cells_count, int index, struct of_phandle_args *out_args); @@ -815,6 +818,15 @@ static inline int of_parse_phandle_with_args(const struct device_node *np, return -ENOSYS; } +static inline int of_parse_phandle_with_args_map(const struct device_node *np, + const char *list_name, + const char *stem_name, + int index, + struct of_phandle_args *out_args) +{ + return -ENOSYS; +} + static inline int of_parse_phandle_with_fixed_args(const struct device_node *np, const char *list_name, int cells_count, int index, struct of_phandle_args *out_args) -- cgit v1.3-14-g43fede From 357aa4b61c82b59a40caf5a65d22e636a3639144 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jan 2018 18:36:17 -0800 Subject: of: unittest: Add phandle remapping test Test the functionality of of_parse_phandle_with_args_map(). Cc: Pantelis Antoniou Cc: Linus Walleij Cc: Mark Brown Signed-off-by: Stephen Boyd Signed-off-by: Rob Herring --- drivers/of/unittest-data/tests-phandle.dtsi | 25 ++++++ drivers/of/unittest.c | 120 ++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) diff --git a/drivers/of/unittest-data/tests-phandle.dtsi b/drivers/of/unittest-data/tests-phandle.dtsi index 3c2f09e56b61..6b33be4c4416 100644 --- a/drivers/of/unittest-data/tests-phandle.dtsi +++ b/drivers/of/unittest-data/tests-phandle.dtsi @@ -26,6 +26,18 @@ #phandle-cells = <3>; }; + provider4: provider4 { + #phandle-cells = <2>; + phandle-map = <0 1 &provider1 3>, + <4 0 &provider0>, + <16 5 &provider3 3 5 0>, + <200 8 &provider2 23 6>, + <19 0 &provider2 15 0>, + <2 3 &provider3 2 5 3>; + phandle-map-mask = <0xff 0xf>; + phandle-map-pass-thru = <0x0 0xf0>; + }; + consumer-a { phandle-list = <&provider1 1>, <&provider2 2 0>, @@ -44,6 +56,19 @@ unterminated-string = [40 41 42 43]; unterminated-string-list = "first", "second", [40 41 42 43]; }; + + consumer-b { + phandle-list = <&provider1 1>, + <&provider4 2 3>, + <0>, + <&provider4 4 0x100>, + <&provider4 0 0x61>, + <&provider0>, + <&provider4 19 0x20>; + phandle-list-bad-phandle = <12345678 0 0>; + phandle-list-bad-args = <&provider2 1 0>, + <&provider4 0>; + }; }; }; }; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 7a9abaae874d..8e23b51eead1 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -453,6 +453,125 @@ static void __init of_unittest_parse_phandle_with_args(void) unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); } +static void __init of_unittest_parse_phandle_with_args_map(void) +{ + struct device_node *np, *p0, *p1, *p2, *p3; + struct of_phandle_args args; + int i, rc; + + np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-b"); + if (!np) { + pr_err("missing testcase data\n"); + return; + } + + p0 = of_find_node_by_path("/testcase-data/phandle-tests/provider0"); + if (!p0) { + pr_err("missing testcase data\n"); + return; + } + + p1 = of_find_node_by_path("/testcase-data/phandle-tests/provider1"); + if (!p1) { + pr_err("missing testcase data\n"); + return; + } + + p2 = of_find_node_by_path("/testcase-data/phandle-tests/provider2"); + if (!p2) { + pr_err("missing testcase data\n"); + return; + } + + p3 = of_find_node_by_path("/testcase-data/phandle-tests/provider3"); + if (!p3) { + pr_err("missing testcase data\n"); + return; + } + + rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells"); + unittest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc); + + for (i = 0; i < 8; i++) { + bool passed = true; + + rc = of_parse_phandle_with_args_map(np, "phandle-list", + "phandle", i, &args); + + /* Test the values from tests-phandle.dtsi */ + switch (i) { + case 0: + passed &= !rc; + passed &= (args.np == p1); + passed &= (args.args_count == 1); + passed &= (args.args[0] == 1); + break; + case 1: + passed &= !rc; + passed &= (args.np == p3); + passed &= (args.args_count == 3); + passed &= (args.args[0] == 2); + passed &= (args.args[1] == 5); + passed &= (args.args[2] == 3); + break; + case 2: + passed &= (rc == -ENOENT); + break; + case 3: + passed &= !rc; + passed &= (args.np == p0); + passed &= (args.args_count == 0); + break; + case 4: + passed &= !rc; + passed &= (args.np == p1); + passed &= (args.args_count == 1); + passed &= (args.args[0] == 3); + break; + case 5: + passed &= !rc; + passed &= (args.np == p0); + passed &= (args.args_count == 0); + break; + case 6: + passed &= !rc; + passed &= (args.np == p2); + passed &= (args.args_count == 2); + passed &= (args.args[0] == 15); + passed &= (args.args[1] == 0x20); + break; + case 7: + passed &= (rc == -ENOENT); + break; + default: + passed = false; + } + + unittest(passed, "index %i - data error on node %s rc=%i\n", + i, args.np->full_name, rc); + } + + /* Check for missing list property */ + rc = of_parse_phandle_with_args_map(np, "phandle-list-missing", + "phandle", 0, &args); + unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc); + + /* Check for missing cells,map,mask property */ + rc = of_parse_phandle_with_args_map(np, "phandle-list", + "phandle-missing", 0, &args); + unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + + /* Check for bad phandle in list */ + rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-phandle", + "phandle", 0, &args); + unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + + /* Check for incorrectly formed argument list */ + rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-args", + "phandle", 1, &args); + unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); +} + static void __init of_unittest_property_string(void) { const char *strings[4]; @@ -2359,6 +2478,7 @@ static int __init of_unittest(void) of_unittest_find_node_by_name(); of_unittest_dynamic(); of_unittest_parse_phandle_with_args(); + of_unittest_parse_phandle_with_args_map(); of_unittest_printf(); of_unittest_property_string(); of_unittest_property_copy(); -- cgit v1.3-14-g43fede From c11e6f0f04dbc5e0384e7d530b024b2e3ae4bb17 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jan 2018 18:36:18 -0800 Subject: gpio: Support gpio nexus dt bindings Platforms like 96boards have a standardized connector/expansion slot that exposes signals like GPIOs to expansion boards in an SoC agnostic way. We'd like the DT overlays for the expansion boards to be written once without knowledge of the SoC on the other side of the connector. This avoids the unscalable combinatorial explosion of a different DT overlay for each expansion board and SoC pair. Now that we have nexus support in the OF core let's change the function call here that parses the phandle lists of gpios to use the nexus variant. This allows us to remap phandles and their arguments through any number of nexus nodes and end up with the actual gpio provider being used. Cc: Pantelis Antoniou Acked-by: Linus Walleij Cc: Mark Brown Signed-off-by: Stephen Boyd Signed-off-by: Rob Herring --- drivers/gpio/gpiolib-of.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 564bb7a31da4..1856ee5e70bb 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -111,8 +111,8 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, struct gpio_desc *desc; int ret; - ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index, - &gpiospec); + ret = of_parse_phandle_with_args_map(np, propname, "gpio", index, + &gpiospec); if (ret) { pr_debug("%s: can't parse '%s' property of node '%pOF[%d]'\n", __func__, propname, np, index); -- cgit v1.3-14-g43fede From 04673e38f56b30cd39b1fa0f386137d818b17781 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:45 -0800 Subject: scsi: lpfc: Fix frequency of Release WQE CQEs The driver controls when the hardware sends completions that communicate consumption of elements from the WQ. This is done by setting a WQEC bit on a WQE. The current driver sets it on every Nth WQE posting. However, the driver isn't clearing the bit if the WQE is reused. Thus, if the queue depth isn't evenly divisible by N, with enough time, it can be set on every element, creating a lot of overhead and risking CQ full conditions. Correct by clearing the bit when not setting it on an Nth element. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_sli.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 5f5528a12308..149f21f53b13 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -129,6 +129,8 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe) /* set consumption flag every once in a while */ if (!((q->host_index + 1) % q->entry_repost)) bf_set(wqe_wqec, &wqe->generic.wqe_com, 1); + else + bf_set(wqe_wqec, &wqe->generic.wqe_com, 0); if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED) bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id); lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size); -- cgit v1.3-14-g43fede From c176ffa0841c632593c5007f1d1c9ed126481daa Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:46 -0800 Subject: scsi: lpfc: Increase CQ and WQ sizes for SCSI Increased CQ and WQ sizes for SCSI FCP, matching those used for NVMe development. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 1 + drivers/scsi/lpfc/lpfc_hw4.h | 3 +++ drivers/scsi/lpfc/lpfc_init.c | 36 +++++++++++++++++++++++++----------- drivers/scsi/lpfc/lpfc_sli.c | 3 ++- drivers/scsi/lpfc/lpfc_sli4.h | 5 +++++ 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 61fb46da05d4..d042f9118e3b 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -760,6 +760,7 @@ struct lpfc_hba { uint8_t mds_diags_support; uint32_t initial_imax; uint8_t bbcredit_support; + uint8_t enab_exp_wqcq_pages; /* HBA Config Parameters */ uint32_t cfg_ack0; diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 73c2f6971d2b..ef469129fb71 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -3212,6 +3212,9 @@ struct lpfc_sli4_parameters { #define cfg_cqv_SHIFT 14 #define cfg_cqv_MASK 0x00000003 #define cfg_cqv_WORD word4 +#define cfg_cqpsize_SHIFT 16 +#define cfg_cqpsize_MASK 0x000000ff +#define cfg_cqpsize_WORD word4 uint32_t word5; uint32_t word6; #define cfg_mqv_SHIFT 14 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index f539c554588c..40ffa0111142 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -8011,9 +8011,10 @@ static int lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) { struct lpfc_queue *qdesc; + uint32_t wqesize; /* Create Fast Path FCP CQs */ - if (phba->fcp_embed_io) + if (phba->enab_exp_wqcq_pages) /* Increase the CQ size when WQEs contain an embedded cdb */ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, phba->sli4_hba.cq_esize, @@ -8031,15 +8032,18 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) phba->sli4_hba.fcp_cq[wqidx] = qdesc; /* Create Fast Path FCP WQs */ - if (phba->fcp_embed_io) + if (phba->enab_exp_wqcq_pages) { /* Increase the WQ size when WQEs contain an embedded cdb */ + wqesize = (phba->fcp_embed_io) ? + LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize; qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, - LPFC_WQE128_SIZE, + wqesize, LPFC_WQE_EXP_COUNT); - else + } else qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, phba->sli4_hba.wq_esize, phba->sli4_hba.wq_ecount); + if (!qdesc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0503 Failed allocate fast-path FCP WQ (%d)\n", @@ -10476,15 +10480,21 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) sli4_params->sge_supp_len = LPFC_MAX_SGE_SIZE; /* - * Issue IOs with CDB embedded in WQE to minimized the number - * of DMAs the firmware has to do. Setting this to 1 also forces - * the driver to use 128 bytes WQEs for FCP IOs. + * Check whether the adapter supports an embedded copy of the + * FCP CMD IU within the WQE for FCP_Ixxx commands. In order + * to use this option, 128-byte WQEs must be used. */ if (bf_get(cfg_ext_embed_cb, mbx_sli4_parameters)) phba->fcp_embed_io = 1; else phba->fcp_embed_io = 0; + if ((bf_get(cfg_cqpsize, mbx_sli4_parameters) & LPFC_CQ_16K_PAGE_SZ) && + (bf_get(cfg_wqpsize, mbx_sli4_parameters) & LPFC_WQ_16K_PAGE_SZ) && + (sli4_params->wqsize & LPFC_WQ_SZ128_SUPPORT)) + phba->enab_exp_wqcq_pages = 1; + else + phba->enab_exp_wqcq_pages = 0; /* * Check if the SLI port supports MDS Diagnostics */ @@ -12227,6 +12237,7 @@ int lpfc_fof_queue_create(struct lpfc_hba *phba) { struct lpfc_queue *qdesc; + uint32_t wqesize; /* Create FOF EQ */ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, @@ -12240,7 +12251,7 @@ lpfc_fof_queue_create(struct lpfc_hba *phba) if (phba->cfg_fof) { /* Create OAS CQ */ - if (phba->fcp_embed_io) + if (phba->enab_exp_wqcq_pages) qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, phba->sli4_hba.cq_esize, @@ -12256,16 +12267,19 @@ lpfc_fof_queue_create(struct lpfc_hba *phba) phba->sli4_hba.oas_cq = qdesc; /* Create OAS WQ */ - if (phba->fcp_embed_io) + if (phba->enab_exp_wqcq_pages) { + wqesize = (phba->fcp_embed_io) ? + LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize; qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, - LPFC_WQE128_SIZE, + wqesize, LPFC_WQE_EXP_COUNT); - else + } else qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, phba->sli4_hba.wq_esize, phba->sli4_hba.wq_ecount); + if (!qdesc) goto out_error; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 149f21f53b13..d08d9b48f6b1 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -14910,7 +14910,8 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, bf_set(lpfc_mbox_hdr_version, &shdr->request, phba->sli4_hba.pc_sli4_params.wqv); - if (phba->sli4_hba.pc_sli4_params.wqsize & LPFC_WQ_SZ128_SUPPORT) + if ((phba->sli4_hba.pc_sli4_params.wqsize & LPFC_WQ_SZ128_SUPPORT) || + (wq->page_size > SLI4_PAGE_SIZE)) wq_create_version = LPFC_Q_CREATE_VERSION_1; else wq_create_version = LPFC_Q_CREATE_VERSION_0; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 81fb58e59e60..a9af9980fc43 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -485,6 +485,11 @@ struct lpfc_pc_sli4_params { uint8_t wqpcnt; }; +#define LPFC_CQ_4K_PAGE_SZ 0x1 +#define LPFC_CQ_16K_PAGE_SZ 0x4 +#define LPFC_WQ_4K_PAGE_SZ 0x1 +#define LPFC_WQ_16K_PAGE_SZ 0x4 + struct lpfc_iov { uint32_t pf_number; uint32_t vf_number; -- cgit v1.3-14-g43fede From 281d61902ffbab47901f8616a38a45144627dd9e Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:47 -0800 Subject: scsi: lpfc: move placement of target destroy on driver detach Ensure nvme localports/targetports are torn down before dismantling the adapter sli interface on driver detachment. This aids leaving interfaces live while nvme may be making callbacks to abort it. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 40ffa0111142..bff5c95cf5df 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -11503,13 +11503,6 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) /* Remove FC host and then SCSI host with the physical port */ fc_remove_host(shost); scsi_remove_host(shost); - /* - * Bring down the SLI Layer. This step disables all interrupts, - * clears the rings, discards all mailbox commands, and resets - * the HBA FCoE function. - */ - lpfc_debugfs_terminate(vport); - lpfc_sli4_hba_unset(phba); /* Perform ndlp cleanup on the physical port. The nvme and nvmet * localports are destroyed after to cleanup all transport memory. @@ -11518,6 +11511,13 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) lpfc_nvmet_destroy_targetport(phba); lpfc_nvme_destroy_localport(vport); + /* + * Bring down the SLI Layer. This step disables all interrupts, + * clears the rings, discards all mailbox commands, and resets + * the HBA FCoE function. + */ + lpfc_debugfs_terminate(vport); + lpfc_sli4_hba_unset(phba); lpfc_stop_hba_timers(phba); spin_lock_irq(&phba->hbalock); -- cgit v1.3-14-g43fede From 8ae337013674d5c1e803429356b85cba2ce12067 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:48 -0800 Subject: scsi: lpfc: correct debug counters for abort Existing code was using the wrong field for the completion status when comparing whether to increment abort statistics Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvmet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 8dbf5c9d51aa..7927ac46d345 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -130,7 +130,7 @@ lpfc_nvmet_xmt_ls_rsp_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, if (tgtp) { if (status) { atomic_inc(&tgtp->xmt_ls_rsp_error); - if (status == IOERR_ABORT_REQUESTED) + if (result == IOERR_ABORT_REQUESTED) atomic_inc(&tgtp->xmt_ls_rsp_aborted); if (bf_get(lpfc_wcqe_c_xb, wcqe)) atomic_inc(&tgtp->xmt_ls_rsp_xb_set); @@ -541,7 +541,7 @@ lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, rsp->transferred_length = 0; if (tgtp) { atomic_inc(&tgtp->xmt_fcp_rsp_error); - if (status == IOERR_ABORT_REQUESTED) + if (result == IOERR_ABORT_REQUESTED) atomic_inc(&tgtp->xmt_fcp_rsp_aborted); } -- cgit v1.3-14-g43fede From 6e8e1c14c61e54253098521127cd5ac0b959dd32 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:49 -0800 Subject: scsi: lpfc: Add WQ Full Logic for NVME Target I/O conditions on the nvme target may have the driver submitting to a full hardware wq. The hardware wq is a shared resource among all nvme controllers. When the driver hit a full wq, it failed the io posting back to the nvme-fc transport, which then escalated it into errors. Correct by maintaining a sideband queue within the driver that is added to when the WQ full condition is hit, and drained from as soon as new WQ space opens up. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_crtn.h | 1 + drivers/scsi/lpfc/lpfc_nvmet.c | 116 +++++++++++++++++++++++++++++++++++++++++ drivers/scsi/lpfc/lpfc_nvmet.h | 1 + drivers/scsi/lpfc/lpfc_sli.c | 3 ++ drivers/scsi/lpfc/lpfc_sli4.h | 5 +- 5 files changed, 125 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 559f9aa0ed08..3ecf50df93f4 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -254,6 +254,7 @@ void lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctxp); int lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport, struct fc_frame_header *fc_hdr); +void lpfc_nvmet_wqfull_process(struct lpfc_hba *phba, struct lpfc_queue *wq); void lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba); void lpfc_nvme_wait_for_io_drain(struct lpfc_hba *phba); void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *, diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 7927ac46d345..9c2acf90212c 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -71,6 +71,8 @@ static int lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *, static int lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *, struct lpfc_nvmet_rcv_ctx *, uint32_t, uint16_t); +static void lpfc_nvmet_wqfull_flush(struct lpfc_hba *, struct lpfc_queue *, + struct lpfc_nvmet_rcv_ctx *); void lpfc_nvmet_defer_release(struct lpfc_hba *phba, struct lpfc_nvmet_rcv_ctx *ctxp) @@ -741,7 +743,10 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport, struct lpfc_nvmet_rcv_ctx *ctxp = container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req); struct lpfc_hba *phba = ctxp->phba; + struct lpfc_queue *wq; struct lpfc_iocbq *nvmewqeq; + struct lpfc_sli_ring *pring; + unsigned long iflags; int rc; if (phba->pport->load_flag & FC_UNLOADING) { @@ -820,6 +825,21 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport, return 0; } + if (rc == -EBUSY) { + /* + * WQ was full, so queue nvmewqeq to be sent after + * WQE release CQE + */ + ctxp->flag |= LPFC_NVMET_DEFER_WQFULL; + wq = phba->sli4_hba.nvme_wq[rsp->hwqid]; + pring = wq->pring; + spin_lock_irqsave(&pring->ring_lock, iflags); + list_add_tail(&nvmewqeq->list, &wq->wqfull_list); + wq->q_flag |= HBA_NVMET_WQFULL; + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return 0; + } + /* Give back resources */ atomic_inc(&lpfc_nvmep->xmt_fcp_drop); lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, @@ -851,6 +871,7 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport, struct lpfc_nvmet_rcv_ctx *ctxp = container_of(req, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req); struct lpfc_hba *phba = ctxp->phba; + struct lpfc_queue *wq; unsigned long flags; if (phba->pport->load_flag & FC_UNLOADING) @@ -880,6 +901,14 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport, } ctxp->flag |= LPFC_NVMET_ABORT_OP; + if (ctxp->flag & LPFC_NVMET_DEFER_WQFULL) { + lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid, + ctxp->oxid); + wq = phba->sli4_hba.nvme_wq[ctxp->wqeq->hba_wqidx]; + lpfc_nvmet_wqfull_flush(phba, wq, ctxp); + return; + } + /* An state of LPFC_NVMET_STE_RCV means we have just received * the NVME command and have not started processing it. * (by issuing any IO WQEs on this exchange yet) @@ -1435,16 +1464,103 @@ lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport, return 0; } +static void +lpfc_nvmet_wqfull_flush(struct lpfc_hba *phba, struct lpfc_queue *wq, + struct lpfc_nvmet_rcv_ctx *ctxp) +{ + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *nvmewqeq; + struct lpfc_iocbq *next_nvmewqeq; + unsigned long iflags; + struct lpfc_wcqe_complete wcqe; + struct lpfc_wcqe_complete *wcqep; + + pring = wq->pring; + wcqep = &wcqe; + + /* Fake an ABORT error code back to cmpl routine */ + memset(wcqep, 0, sizeof(struct lpfc_wcqe_complete)); + bf_set(lpfc_wcqe_c_status, wcqep, IOSTAT_LOCAL_REJECT); + wcqep->parameter = IOERR_ABORT_REQUESTED; + + spin_lock_irqsave(&pring->ring_lock, iflags); + list_for_each_entry_safe(nvmewqeq, next_nvmewqeq, + &wq->wqfull_list, list) { + if (ctxp) { + /* Checking for a specific IO to flush */ + if (nvmewqeq->context2 == ctxp) { + list_del(&nvmewqeq->list); + spin_unlock_irqrestore(&pring->ring_lock, + iflags); + lpfc_nvmet_xmt_fcp_op_cmp(phba, nvmewqeq, + wcqep); + return; + } + continue; + } else { + /* Flush all IOs */ + list_del(&nvmewqeq->list); + spin_unlock_irqrestore(&pring->ring_lock, iflags); + lpfc_nvmet_xmt_fcp_op_cmp(phba, nvmewqeq, wcqep); + spin_lock_irqsave(&pring->ring_lock, iflags); + } + } + if (!ctxp) + wq->q_flag &= ~HBA_NVMET_WQFULL; + spin_unlock_irqrestore(&pring->ring_lock, iflags); +} + +void +lpfc_nvmet_wqfull_process(struct lpfc_hba *phba, + struct lpfc_queue *wq) +{ +#if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *nvmewqeq; + unsigned long iflags; + int rc; + + /* + * Some WQE slots are available, so try to re-issue anything + * on the WQ wqfull_list. + */ + pring = wq->pring; + spin_lock_irqsave(&pring->ring_lock, iflags); + while (!list_empty(&wq->wqfull_list)) { + list_remove_head(&wq->wqfull_list, nvmewqeq, struct lpfc_iocbq, + list); + spin_unlock_irqrestore(&pring->ring_lock, iflags); + rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, nvmewqeq); + spin_lock_irqsave(&pring->ring_lock, iflags); + if (rc == -EBUSY) { + /* WQ was full again, so put it back on the list */ + list_add(&nvmewqeq->list, &wq->wqfull_list); + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return; + } + } + wq->q_flag &= ~HBA_NVMET_WQFULL; + spin_unlock_irqrestore(&pring->ring_lock, iflags); + +#endif +} + void lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba) { #if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) struct lpfc_nvmet_tgtport *tgtp; + struct lpfc_queue *wq; + uint32_t qidx; if (phba->nvmet_support == 0) return; if (phba->targetport) { tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) { + wq = phba->sli4_hba.nvme_wq[qidx]; + lpfc_nvmet_wqfull_flush(phba, wq, NULL); + } init_completion(&tgtp->tport_unreg_done); nvmet_fc_unregister_targetport(phba->targetport); wait_for_completion_timeout(&tgtp->tport_unreg_done, 5); diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index 5b32c9e4d4ef..354cce443c9f 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -132,6 +132,7 @@ struct lpfc_nvmet_rcv_ctx { #define LPFC_NVMET_CTX_RLS 0x8 /* ctx free requested */ #define LPFC_NVMET_ABTS_RCV 0x10 /* ABTS received on exchange */ #define LPFC_NVMET_DEFER_RCV_REPOST 0x20 /* repost to RQ on defer rcv */ +#define LPFC_NVMET_DEFER_WQFULL 0x40 /* Waiting on a free WQE */ struct rqb_dmabuf *rqb_buffer; struct lpfc_nvmet_ctxbuf *ctxbuf; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index d08d9b48f6b1..fbda2fbcbfec 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -13232,6 +13232,8 @@ lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, if (childwq->queue_id == hba_wqid) { lpfc_sli4_wq_release(childwq, bf_get(lpfc_wcqe_r_wqe_index, wcqe)); + if (childwq->q_flag & HBA_NVMET_WQFULL) + lpfc_nvmet_wqfull_process(phba, childwq); wqid_matched = true; break; } @@ -13950,6 +13952,7 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size, INIT_LIST_HEAD(&queue->list); INIT_LIST_HEAD(&queue->wq_list); + INIT_LIST_HEAD(&queue->wqfull_list); INIT_LIST_HEAD(&queue->page_list); INIT_LIST_HEAD(&queue->child_list); diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index a9af9980fc43..ac81bfa59278 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -145,6 +145,7 @@ struct lpfc_rqb { struct lpfc_queue { struct list_head list; struct list_head wq_list; + struct list_head wqfull_list; enum lpfc_sli4_queue_type type; enum lpfc_sli4_queue_subtype subtype; struct lpfc_hba *phba; @@ -173,9 +174,11 @@ struct lpfc_queue { #define LPFC_EXPANDED_PAGE_SIZE 16384 #define LPFC_DEFAULT_PAGE_SIZE 4096 uint16_t chann; /* IO channel this queue is associated with */ - uint16_t db_format; + uint8_t db_format; #define LPFC_DB_RING_FORMAT 0x01 #define LPFC_DB_LIST_FORMAT 0x02 + uint8_t q_flag; +#define HBA_NVMET_WQFULL 0x1 /* We hit WQ Full condition for NVMET */ void __iomem *db_regaddr; /* For q stats */ uint32_t q_cnt_1; -- cgit v1.3-14-g43fede From 2c3b2a8f652566c5b35d945f0c8146555d2062ec Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:50 -0800 Subject: scsi: lpfc: Fix PRLI handling when topology type changes The lpfc driver does not discover a target when the topology changes from switched-fabric to direct-connect. The target rejects the PRLI from the initiator in direct-connect as the driver is using the old S_ID from the switched topology. The driver was inappropriately clearing the VP bit to register the VPI, which is what is associated with the S_ID. Fix by leaving the VP bit set (it was set earlier) and as the VFI is being re-registered, set the UPDT bit. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_mbox.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index 81fb92967b11..c32d4a323db2 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -2170,10 +2170,8 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys) /* Only FC supports upd bit */ if ((phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC) && (vport->fc_flag & FC_VFI_REGISTERED) && - (!phba->fc_topology_changed)) { - bf_set(lpfc_reg_vfi_vp, reg_vfi, 0); + (!phba->fc_topology_changed)) bf_set(lpfc_reg_vfi_upd, reg_vfi, 1); - } bf_set(lpfc_reg_vfi_bbcr, reg_vfi, 0); bf_set(lpfc_reg_vfi_bbscn, reg_vfi, 0); -- cgit v1.3-14-g43fede From 91455b850956bc13708a074bd1400f54aae74890 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:51 -0800 Subject: scsi: lpfc: Fix IO failure during hba reset testing with nvme io. A stress test repeatedly resetting the adapter while performing io would eventually report I/O failures and missing nvme namespaces. The driver was setting the nvmefc_fcp_req->private pointer to NULL during the IO completion routine before upcalling done(). If the transport was also running an abort for that IO, the driver would fail the abort with message 6140. Failing the abort is not allowed by the nvme-fc transport, as it mandates that the io must be returned back to the transport. As that does not happen, the transport controller delete has an outstanding reference and can't complete teardown. The NULL-ing of the private pointer should be done only when the io is considered complete. It's complete when the adapter returns the exchange with the "exchange busy" flag clear. Move the NULL'ing of the structure to the done case. This leaves the io contexts set while it is busy and until the subsequent XRI_ABORTED completion which returns the exchange is received. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 81e3a4f10c3c..c6e5b9972585 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -980,14 +980,14 @@ out_err: phba->cpucheck_cmpl_io[lpfc_ncmd->cpu]++; } #endif - freqpriv = nCmd->private; - freqpriv->nvme_buf = NULL; /* NVME targets need completion held off until the abort exchange * completes unless the NVME Rport is getting unregistered. */ if (!(lpfc_ncmd->flags & LPFC_SBUF_XBUSY)) { + freqpriv = nCmd->private; + freqpriv->nvme_buf = NULL; nCmd->done(nCmd); lpfc_ncmd->nvmeCmd = NULL; } -- cgit v1.3-14-g43fede From 411de511c6943554cdc4173c3f522029db2f75c7 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:52 -0800 Subject: scsi: lpfc: Fix RQ empty firmware trap When nvme target deferred receive logic waits for exchange resources, the corresponding receive buffer is not replenished with the hardware. This can result in a lack of asynchronous receive buffer resources in the hardware, resulting in a "2885 Port Status Event: ... error 1=0x52004a01 ..." message. Correct by replenishing the buffer whenenver the deferred logic kicks in. Update corresponding debug messages and statistics as well. [mkp: applied by hand] Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 6 ++++++ drivers/scsi/lpfc/lpfc_mem.c | 10 +++++++--- drivers/scsi/lpfc/lpfc_nvmet.c | 31 +++++++++++++++++++++---------- drivers/scsi/lpfc/lpfc_nvmet.h | 7 +++++-- drivers/scsi/lpfc/lpfc_sli.c | 12 ++++++++++++ 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index ac77081e6e9e..c63a028385ab 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -259,6 +259,12 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, atomic_read(&tgtp->xmt_abort_rsp), atomic_read(&tgtp->xmt_abort_rsp_error)); + len += snprintf(buf + len, PAGE_SIZE - len, + "DELAY: ctx %08x fod %08x wqfull %08x\n", + atomic_read(&tgtp->defer_ctx), + atomic_read(&tgtp->defer_fod), + atomic_read(&tgtp->defer_wqfull)); + /* Calculate outstanding IOs */ tot = atomic_read(&tgtp->rcv_fcp_cmd_drop); tot += atomic_read(&tgtp->xmt_fcp_release); diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index 87c08ff37ddd..60078e61da5e 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -753,12 +753,16 @@ lpfc_rq_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp) drqe.address_hi = putPaddrHigh(rqb_entry->dbuf.phys); rc = lpfc_sli4_rq_put(rqb_entry->hrq, rqb_entry->drq, &hrqe, &drqe); if (rc < 0) { + (rqbp->rqb_free_buffer)(phba, rqb_entry); lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "6409 Cannot post to RQ %d: %x %x\n", + "6409 Cannot post to HRQ %d: %x %x %x " + "DRQ %x %x\n", rqb_entry->hrq->queue_id, rqb_entry->hrq->host_index, - rqb_entry->hrq->hba_index); - (rqbp->rqb_free_buffer)(phba, rqb_entry); + rqb_entry->hrq->hba_index, + rqb_entry->hrq->entry_count, + rqb_entry->drq->host_index, + rqb_entry->drq->hba_index); } else { list_add_tail(&rqb_entry->hbuf.list, &rqbp->rqb_buffer_list); rqbp->buffer_count++; diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 9c2acf90212c..0539585d32d4 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -270,8 +270,6 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf) "NVMET RCV BUSY: xri x%x sz %d " "from %06x\n", oxid, size, sid); - /* defer repost rcv buffer till .defer_rcv callback */ - ctxp->flag &= ~LPFC_NVMET_DEFER_RCV_REPOST; atomic_inc(&tgtp->rcv_fcp_cmd_out); return; } @@ -837,6 +835,7 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport, list_add_tail(&nvmewqeq->list, &wq->wqfull_list); wq->q_flag |= HBA_NVMET_WQFULL; spin_unlock_irqrestore(&pring->ring_lock, iflags); + atomic_inc(&lpfc_nvmep->defer_wqfull); return 0; } @@ -975,11 +974,9 @@ lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport, tgtp = phba->targetport->private; atomic_inc(&tgtp->rcv_fcp_cmd_defer); - if (ctxp->flag & LPFC_NVMET_DEFER_RCV_REPOST) - lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */ - else - nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf); - ctxp->flag &= ~LPFC_NVMET_DEFER_RCV_REPOST; + + /* Free the nvmebuf since a new buffer already replaced it */ + nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf); } static struct nvmet_fc_target_template lpfc_tgttemplate = { @@ -1309,6 +1306,9 @@ lpfc_nvmet_create_targetport(struct lpfc_hba *phba) atomic_set(&tgtp->xmt_abort_sol, 0); atomic_set(&tgtp->xmt_abort_rsp, 0); atomic_set(&tgtp->xmt_abort_rsp_error, 0); + atomic_set(&tgtp->defer_ctx, 0); + atomic_set(&tgtp->defer_fod, 0); + atomic_set(&tgtp->defer_wqfull, 0); } return error; } @@ -1810,6 +1810,8 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, lpfc_nvmeio_data(phba, "NVMET FCP RCV: xri x%x sz %d CPU %02x\n", oxid, size, smp_processor_id()); + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + if (!ctx_buf) { /* Queue this NVME IO to process later */ spin_lock_irqsave(&phba->sli4_hba.nvmet_io_wait_lock, iflag); @@ -1825,10 +1827,11 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, lpfc_post_rq_buffer( phba, phba->sli4_hba.nvmet_mrq_hdr[qno], phba->sli4_hba.nvmet_mrq_data[qno], 1, qno); + + atomic_inc(&tgtp->defer_ctx); return; } - tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; payload = (uint32_t *)(nvmebuf->dbuf.virt); sid = sli4_sid_from_fc_hdr(fc_hdr); @@ -1892,12 +1895,20 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, /* Processing of FCP command is deferred */ if (rc == -EOVERFLOW) { + /* + * Post a brand new DMA buffer to RQ and defer + * freeing rcv buffer till .defer_rcv callback + */ + qno = nvmebuf->idx; + lpfc_post_rq_buffer( + phba, phba->sli4_hba.nvmet_mrq_hdr[qno], + phba->sli4_hba.nvmet_mrq_data[qno], 1, qno); + lpfc_nvmeio_data(phba, "NVMET RCV BUSY: xri x%x sz %d from %06x\n", oxid, size, sid); - /* defer reposting rcv buffer till .defer_rcv callback */ - ctxp->flag |= LPFC_NVMET_DEFER_RCV_REPOST; atomic_inc(&tgtp->rcv_fcp_cmd_out); + atomic_inc(&tgtp->defer_fod); return; } ctxp->rqb_buffer = nvmebuf; diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index 354cce443c9f..5da35de5ea45 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -72,7 +72,6 @@ struct lpfc_nvmet_tgtport { atomic_t xmt_fcp_rsp_aborted; atomic_t xmt_fcp_rsp_drop; - /* Stats counters - lpfc_nvmet_xmt_fcp_abort */ atomic_t xmt_fcp_xri_abort_cqe; atomic_t xmt_fcp_abort; @@ -81,6 +80,11 @@ struct lpfc_nvmet_tgtport { atomic_t xmt_abort_unsol; atomic_t xmt_abort_rsp; atomic_t xmt_abort_rsp_error; + + /* Stats counters - defer IO */ + atomic_t defer_ctx; + atomic_t defer_fod; + atomic_t defer_wqfull; }; struct lpfc_nvmet_ctx_info { @@ -131,7 +135,6 @@ struct lpfc_nvmet_rcv_ctx { #define LPFC_NVMET_XBUSY 0x4 /* XB bit set on IO cmpl */ #define LPFC_NVMET_CTX_RLS 0x8 /* ctx free requested */ #define LPFC_NVMET_ABTS_RCV 0x10 /* ABTS received on exchange */ -#define LPFC_NVMET_DEFER_RCV_REPOST 0x20 /* repost to RQ on defer rcv */ #define LPFC_NVMET_DEFER_WQFULL 0x40 /* Waiting on a free WQE */ struct rqb_dmabuf *rqb_buffer; struct lpfc_nvmet_ctxbuf *ctxbuf; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index fbda2fbcbfec..8b2919a553d6 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -6535,9 +6535,11 @@ lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq, struct lpfc_rqe hrqe; struct lpfc_rqe drqe; struct lpfc_rqb *rqbp; + unsigned long flags; struct rqb_dmabuf *rqb_buffer; LIST_HEAD(rqb_buf_list); + spin_lock_irqsave(&phba->hbalock, flags); rqbp = hrq->rqbp; for (i = 0; i < count; i++) { /* IF RQ is already full, don't bother */ @@ -6561,6 +6563,15 @@ lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq, drqe.address_hi = putPaddrHigh(rqb_buffer->dbuf.phys); rc = lpfc_sli4_rq_put(hrq, drq, &hrqe, &drqe); if (rc < 0) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6421 Cannot post to HRQ %d: %x %x %x " + "DRQ %x %x\n", + hrq->queue_id, + hrq->host_index, + hrq->hba_index, + hrq->entry_count, + drq->host_index, + drq->hba_index); rqbp->rqb_free_buffer(phba, rqb_buffer); } else { list_add_tail(&rqb_buffer->hbuf.list, @@ -6568,6 +6579,7 @@ lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq, rqbp->buffer_count++; } } + spin_unlock_irqrestore(&phba->hbalock, flags); return 1; } -- cgit v1.3-14-g43fede From 64bf009933bc84a7fb44ff50f86af0201b8be0c3 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:53 -0800 Subject: scsi: lpfc: Allow set of maximum outstanding SCSI cmd limit for a target Make the attribute writeable. Remove the ramp up to logic as its unnecessary, simply set depth. Add debug message if depth changed, possibly reducing limit, yet our outstanding count has yet to catch up with it. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 4 ++-- drivers/scsi/lpfc/lpfc_scsi.c | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index c63a028385ab..06138b618220 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -3464,8 +3464,8 @@ LPFC_VPORT_ATTR_R(lun_queue_depth, 30, 1, 512, # tgt_queue_depth: This parameter is used to limit the number of outstanding # commands per target port. Value range is [10,65535]. Default value is 65535. */ -LPFC_VPORT_ATTR_R(tgt_queue_depth, 65535, 10, 65535, - "Max number of FCP commands we can queue to a specific target port"); +LPFC_VPORT_ATTR_RW(tgt_queue_depth, 65535, 10, 65535, + "Max number of FCP commands we can queue to a specific target port"); /* # hba_queue_depth: This parameter is used to limit the number of outstanding diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index c0cdaef4db24..dcc86936e6fc 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -3926,7 +3926,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, struct lpfc_rport_data *rdata = lpfc_cmd->rdata; struct lpfc_nodelist *pnode = rdata->pnode; struct scsi_cmnd *cmd; - int depth; unsigned long flags; struct lpfc_fast_path_event *fast_path_evt; struct Scsi_Host *shost; @@ -4132,16 +4131,11 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, } spin_unlock_irqrestore(shost->host_lock, flags); } else if (pnode && NLP_CHK_NODE_ACT(pnode)) { - if ((pnode->cmd_qdepth < vport->cfg_tgt_queue_depth) && - time_after(jiffies, pnode->last_change_time + + if ((pnode->cmd_qdepth != vport->cfg_tgt_queue_depth) && + time_after(jiffies, pnode->last_change_time + msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) { spin_lock_irqsave(shost->host_lock, flags); - depth = pnode->cmd_qdepth * LPFC_TGTQ_RAMPUP_PCENT - / 100; - depth = depth ? depth : 1; - pnode->cmd_qdepth += depth; - if (pnode->cmd_qdepth > vport->cfg_tgt_queue_depth) - pnode->cmd_qdepth = vport->cfg_tgt_queue_depth; + pnode->cmd_qdepth = vport->cfg_tgt_queue_depth; pnode->last_change_time = jiffies; spin_unlock_irqrestore(shost->host_lock, flags); } @@ -4564,9 +4558,32 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) */ if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) goto out_tgt_busy; - if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) + if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_ERROR, + "3377 Target Queue Full, scsi Id:%d Qdepth:%d" + " Pending command:%d" + " WWNN:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, " + " WWPN:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + ndlp->nlp_sid, ndlp->cmd_qdepth, + atomic_read(&ndlp->cmd_pending), + ndlp->nlp_nodename.u.wwn[0], + ndlp->nlp_nodename.u.wwn[1], + ndlp->nlp_nodename.u.wwn[2], + ndlp->nlp_nodename.u.wwn[3], + ndlp->nlp_nodename.u.wwn[4], + ndlp->nlp_nodename.u.wwn[5], + ndlp->nlp_nodename.u.wwn[6], + ndlp->nlp_nodename.u.wwn[7], + ndlp->nlp_portname.u.wwn[0], + ndlp->nlp_portname.u.wwn[1], + ndlp->nlp_portname.u.wwn[2], + ndlp->nlp_portname.u.wwn[3], + ndlp->nlp_portname.u.wwn[4], + ndlp->nlp_portname.u.wwn[5], + ndlp->nlp_portname.u.wwn[6], + ndlp->nlp_portname.u.wwn[7]); goto out_tgt_busy; - + } lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp); if (lpfc_cmd == NULL) { lpfc_rampdown_queue_depth(phba); -- cgit v1.3-14-g43fede From 161df4f09987ae2e9f0f97f0b38eee298b4a39ff Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:54 -0800 Subject: scsi: lpfc: Fix soft lockup in lpfc worker thread during LIP testing During link bounce testing in a point-to-point topology, the host may enter a soft lockup on the lpfc_worker thread: Call Trace: lpfc_work_done+0x1f3/0x1390 [lpfc] lpfc_do_work+0x16f/0x180 [lpfc] kthread+0xc7/0xe0 ret_from_fork+0x3f/0x70 The driver was simultaneously setting a combination of flags that caused lpfc_do_work()to effectively spin between slow path work and new event data, causing the lockup. Ensure in the typical wq completions, that new event data flags are set if the slow path flag is running. The slow path will eventually reschedule the wq handling. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hbadisc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index b159a5c4e388..9265906d956e 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -696,8 +696,9 @@ lpfc_work_done(struct lpfc_hba *phba) phba->hba_flag & HBA_SP_QUEUE_EVT)) { if (pring->flag & LPFC_STOP_IOCB_EVENT) { pring->flag |= LPFC_DEFERRED_RING_EVENT; - /* Set the lpfc data pending flag */ - set_bit(LPFC_DATA_READY, &phba->data_flags); + /* Preserve legacy behavior. */ + if (!(phba->hba_flag & HBA_SP_QUEUE_EVT)) + set_bit(LPFC_DATA_READY, &phba->data_flags); } else { if (phba->link_state >= LPFC_LINK_UP || phba->link_flag & LS_MDS_LOOPBACK) { -- cgit v1.3-14-g43fede From 2289e9598dde9705400559ca2606fb8c145c34f0 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:55 -0800 Subject: scsi: lpfc: Fix issue_lip if link is disabled The driver ignored checks on whether the link should be kept administratively down after a link bounce. Correct the checks. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 06138b618220..e25e63eb5a53 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -911,7 +911,12 @@ lpfc_issue_lip(struct Scsi_Host *shost) LPFC_MBOXQ_t *pmboxq; int mbxstatus = MBXERR_ERROR; + /* + * If the link is offline, disabled or BLOCK_MGMT_IO + * it doesn't make any sense to allow issue_lip + */ if ((vport->fc_flag & FC_OFFLINE_MODE) || + (phba->hba_flag & LINK_DISABLED) || (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO)) return -EPERM; -- cgit v1.3-14-g43fede From a5ff06817eb86d022bc11993850a42732d7e6979 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:56 -0800 Subject: scsi: lpfc: Indicate CONF support in NVMe PRLI Revise the NVME PRLI to indicate CONF support. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_els.c | 3 ++- drivers/scsi/lpfc/lpfc_hw4.h | 6 +++--- drivers/scsi/lpfc/lpfc_nportdisc.c | 3 --- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 234c7c015982..404e1af5e2ab 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -2293,10 +2293,11 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (phba->nvmet_support) { bf_set(prli_tgt, npr_nvme, 1); bf_set(prli_disc, npr_nvme, 1); - } else { bf_set(prli_init, npr_nvme, 1); + bf_set(prli_conf, npr_nvme, 1); } + npr_nvme->word1 = cpu_to_be32(npr_nvme->word1); npr_nvme->word4 = cpu_to_be32(npr_nvme->word4); elsiocb->iocb_flag |= LPFC_PRLI_NVME_REQ; diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index ef469129fb71..7c3afc3d3121 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -4346,9 +4346,9 @@ struct lpfc_nvme_prli { #define prli_init_SHIFT 5 #define prli_init_MASK 0x00000001 #define prli_init_WORD word4 -#define prli_recov_SHIFT 8 -#define prli_recov_MASK 0x00000001 -#define prli_recov_WORD word4 +#define prli_conf_SHIFT 7 +#define prli_conf_MASK 0x00000001 +#define prli_conf_WORD word4 uint32_t word5; #define prli_fb_sz_SHIFT 0 #define prli_fb_sz_MASK 0x0000ffff diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index d841aa42f607..bbf1e1342b09 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -2011,9 +2011,6 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } } - if (bf_get_be32(prli_recov, nvpr)) - ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; - lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, "6029 NVME PRLI Cmpl w1 x%08x " "w4 x%08x w5 x%08x flag x%x, " -- cgit v1.3-14-g43fede From c1dd9111b7f78a90bccd2e4abb9b9bb6319a4c64 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:57 -0800 Subject: scsi: lpfc: Fix SCSI io host reset causing kernel crash During SCSI error handling escalation to host reset, the SCSI io routines were moved off the txcmplq, but the individual io's ON_CMPLQ flag wasn't cleared. Thus, a background thread saw the io and attempted to access it as if on the txcmplq. Clear the flag upon removal. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 4 ++++ drivers/scsi/lpfc/lpfc_sli.c | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index bff5c95cf5df..aa7872a7b493 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -958,6 +958,7 @@ lpfc_hba_clean_txcmplq(struct lpfc_hba *phba) struct lpfc_sli_ring *pring; LIST_HEAD(completions); int i; + struct lpfc_iocbq *piocb, *next_iocb; if (phba->sli_rev != LPFC_SLI_REV4) { for (i = 0; i < psli->num_rings; i++) { @@ -983,6 +984,9 @@ lpfc_hba_clean_txcmplq(struct lpfc_hba *phba) if (!pring) continue; spin_lock_irq(&pring->ring_lock); + list_for_each_entry_safe(piocb, next_iocb, + &pring->txcmplq, list) + piocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ; list_splice_init(&pring->txcmplq, &completions); pring->txcmplq_cnt = 0; spin_unlock_irq(&pring->ring_lock); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 8b2919a553d6..d597e15a1974 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -3778,6 +3778,7 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba) struct lpfc_sli *psli = &phba->sli; struct lpfc_sli_ring *pring; uint32_t i; + struct lpfc_iocbq *piocb, *next_iocb; spin_lock_irq(&phba->hbalock); /* Indicate the I/O queues are flushed */ @@ -3792,6 +3793,9 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba) spin_lock_irq(&pring->ring_lock); /* Retrieve everything on txq */ list_splice_init(&pring->txq, &txq); + list_for_each_entry_safe(piocb, next_iocb, + &pring->txcmplq, list) + piocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ; /* Retrieve everything on the txcmplq */ list_splice_init(&pring->txcmplq, &txcmplq); pring->txq_cnt = 0; @@ -3813,6 +3817,9 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba) spin_lock_irq(&phba->hbalock); /* Retrieve everything on txq */ list_splice_init(&pring->txq, &txq); + list_for_each_entry_safe(piocb, next_iocb, + &pring->txcmplq, list) + piocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ; /* Retrieve everything on the txcmplq */ list_splice_init(&pring->txcmplq, &txcmplq); pring->txq_cnt = 0; @@ -3844,6 +3851,7 @@ lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba) LIST_HEAD(txcmplq); struct lpfc_sli_ring *pring; uint32_t i; + struct lpfc_iocbq *piocb, *next_iocb; if (phba->sli_rev < LPFC_SLI_REV4) return; @@ -3860,8 +3868,11 @@ lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba) for (i = 0; i < phba->cfg_nvme_io_channel; i++) { pring = phba->sli4_hba.nvme_wq[i]->pring; - /* Retrieve everything on the txcmplq */ spin_lock_irq(&pring->ring_lock); + list_for_each_entry_safe(piocb, next_iocb, + &pring->txcmplq, list) + piocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ; + /* Retrieve everything on the txcmplq */ list_splice_init(&pring->txcmplq, &txcmplq); pring->txcmplq_cnt = 0; spin_unlock_irq(&pring->ring_lock); -- cgit v1.3-14-g43fede From 20aefac3a9a23b56db43f1fe1b3ae72c87e39137 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:58 -0800 Subject: scsi: lpfc: Validate adapter support for SRIU option When using the special option to suppress the response iu, ensure the adapter fully supports the feature by checking feature flags from the adapter and validating the support when formatting the WQE. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hw4.h | 3 +++ drivers/scsi/lpfc/lpfc_init.c | 13 ++++++++++++- drivers/scsi/lpfc/lpfc_nvmet.c | 7 ++++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 7c3afc3d3121..52fe28ae50fa 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -3293,6 +3293,9 @@ struct lpfc_sli4_parameters { #define cfg_eqdr_SHIFT 8 #define cfg_eqdr_MASK 0x00000001 #define cfg_eqdr_WORD word19 +#define cfg_nosr_SHIFT 9 +#define cfg_nosr_MASK 0x00000001 +#define cfg_nosr_WORD word19 #define LPFC_NODELAY_MAX_IO 32 }; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index aa7872a7b493..f2d2faef8710 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -10473,8 +10473,19 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) phba->cfg_enable_fc4_type = LPFC_ENABLE_FCP; } - if (bf_get(cfg_xib, mbx_sli4_parameters) && phba->cfg_suppress_rsp) + /* + * To support Suppress Response feature we must satisfy 3 conditions. + * lpfc_suppress_rsp module parameter must be set (default). + * In SLI4-Parameters Descriptor: + * Extended Inline Buffers (XIB) must be supported. + * Suppress Response IU Not Supported (SRIUNS) must NOT be supported + * (double negative). + */ + if (phba->cfg_suppress_rsp && bf_get(cfg_xib, mbx_sli4_parameters) && + !(bf_get(cfg_nosr, mbx_sli4_parameters))) phba->sli.sli_flag |= LPFC_SLI_SUPPRESS_RSP; + else + phba->cfg_suppress_rsp = 0; if (bf_get(cfg_eqdr, mbx_sli4_parameters)) phba->sli.sli_flag |= LPFC_SLI_USE_EQDR; diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 0539585d32d4..6dd8535918f6 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -2290,9 +2290,10 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba, if (rsp->op == NVMET_FCOP_READDATA_RSP) { atomic_inc(&tgtp->xmt_fcp_read_rsp); bf_set(wqe_ar, &wqe->fcp_tsend.wqe_com, 1); - if ((ndlp->nlp_flag & NLP_SUPPRESS_RSP) && - (rsp->rsplen == 12)) { - bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 1); + if (rsp->rsplen == LPFC_NVMET_SUCCESS_LEN) { + if (ndlp->nlp_flag & NLP_SUPPRESS_RSP) + bf_set(wqe_sup, + &wqe->fcp_tsend.wqe_com, 1); bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 0); bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 0); bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com, 0); -- cgit v1.3-14-g43fede From 8d731d1aa993c44fcf4de0dbd42059e00cf37102 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:58:59 -0800 Subject: scsi: lpfc: Fix header inclusion in lpfc_nvmet The driver was inappropriately pulling in the nvme host's nvme.h header. What it really needed was the standard header. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvmet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 6dd8535918f6..823b6df0aec7 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -36,7 +36,7 @@ #include #include -#include <../drivers/nvme/host/nvme.h> +#include #include #include -- cgit v1.3-14-g43fede From 45634a86ca6e98dbcaddb763f8e90ad243057789 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:59:00 -0800 Subject: scsi: lpfc: Treat SCSI Write operation Underruns as an error Currently, write underruns (mismatch of amount transferred vs scsi status and its residual) detected by the adapter are not being flagged as an error. Its expected the target controls the data transfer and would appropriately set the RSP values. Only read underruns are treated as errors. Revise the SCSI error handling to treat write underruns as an error as well. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_scsi.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index dcc86936e6fc..10c2dc0cf1fa 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -3772,20 +3772,18 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId)); lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_UNDER, - "9025 FCP Read Underrun, expected %d, " + "9025 FCP Underrun, expected %d, " "residual %d Data: x%x x%x x%x\n", fcpDl, scsi_get_resid(cmnd), fcpi_parm, cmnd->cmnd[0], cmnd->underflow); /* - * If there is an under run check if under run reported by + * If there is an under run, check if under run reported by * storage array is same as the under run reported by HBA. * If this is not same, there is a dropped frame. */ - if ((cmnd->sc_data_direction == DMA_FROM_DEVICE) && - fcpi_parm && - (scsi_get_resid(cmnd) != fcpi_parm)) { + if (fcpi_parm && (scsi_get_resid(cmnd) != fcpi_parm)) { lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP | LOG_FCP_ERROR, "9026 FCP Read Check Error " -- cgit v1.3-14-g43fede From 815a9c437617e221842d12b3366ff6911b3df628 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:59:01 -0800 Subject: scsi: lpfc: Fix nonrecovery of NVME controller after cable swap. In a test that is doing large numbers of cable swaps on the target, the nvme controllers wouldn't reconnect. During the cable swaps, the targets n_port_id would change. This information was passed to the nvme-fc transport, in the new remoteport registration. However, the nvme-fc transport didn't update the n_port_id value in the remoteport struct when it reused an existing structure. Later, when a new association was attempted on the remoteport, the driver's NVME LS routine would use the stale n_port_id from the remoteport struct to address the LS. As the device is no longer at that address, the LS would go into never never land. Separately, the nvme-fc transport will be corrected to update the n_port_id value on a re-registration. However, for now, there's no reason to use the transports values. The private pointer points to the drivers node structure and the node structure is up to date. Therefore, revise the LS routine to use the drivers data structures for the LS. Augmented the debug message for better debugging in the future. Also removed a duplicate if check that seems to have slipped in. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvme.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index c6e5b9972585..6327f858c4c8 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -241,10 +241,11 @@ lpfc_nvme_cmpl_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, ndlp = (struct lpfc_nodelist *)cmdwqe->context1; lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, "6047 nvme cmpl Enter " - "Data %p DID %x Xri: %x status %x cmd:%p lsreg:%p " - "bmp:%p ndlp:%p\n", + "Data %p DID %x Xri: %x status %x reason x%x cmd:%p " + "lsreg:%p bmp:%p ndlp:%p\n", pnvme_lsreq, ndlp ? ndlp->nlp_DID : 0, cmdwqe->sli4_xritag, status, + (wcqe->parameter & 0xffff), cmdwqe, pnvme_lsreq, cmdwqe->context3, ndlp); lpfc_nvmeio_data(phba, "NVME LS CMPL: xri x%x stat x%x parm x%x\n", @@ -419,6 +420,7 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, { int ret = 0; struct lpfc_nvme_lport *lport; + struct lpfc_nvme_rport *rport; struct lpfc_vport *vport; struct lpfc_nodelist *ndlp; struct ulp_bde64 *bpl; @@ -437,19 +439,18 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, */ lport = (struct lpfc_nvme_lport *)pnvme_lport->private; + rport = (struct lpfc_nvme_rport *)pnvme_rport->private; vport = lport->vport; if (vport->load_flag & FC_UNLOADING) return -ENODEV; - if (vport->load_flag & FC_UNLOADING) - return -ENODEV; - - ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id); + /* Need the ndlp. It is stored in the driver's rport. */ + ndlp = rport->ndlp; if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, - "6051 DID x%06x not an active rport.\n", - pnvme_rport->port_id); + "6051 Remoteport %p, rport has invalid ndlp. " + "Failing LS Req\n", pnvme_rport); return -ENODEV; } @@ -500,8 +501,9 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, /* Expand print to include key fields. */ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, - "6149 ENTER. lport %p, rport %p lsreq%p rqstlen:%d " - "rsplen:%d %pad %pad\n", + "6149 Issue LS Req to DID 0x%06x lport %p, rport %p " + "lsreq%p rqstlen:%d rsplen:%d %pad %pad\n", + ndlp->nlp_DID, pnvme_lport, pnvme_rport, pnvme_lsreq, pnvme_lsreq->rqstlen, pnvme_lsreq->rsplen, &pnvme_lsreq->rqstdma, @@ -517,7 +519,7 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, ndlp, 2, 30, 0); if (ret != WQE_SUCCESS) { atomic_inc(&lport->xmt_ls_err); - lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC, "6052 EXIT. issue ls wqe failed lport %p, " "rport %p lsreq%p Status %x DID %x\n", pnvme_lport, pnvme_rport, pnvme_lsreq, -- cgit v1.3-14-g43fede From 6e9d2f1667ea12bd2f997a7529fb41cce8e0036d Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:59:02 -0800 Subject: scsi: lpfc: update driver version to 11.4.0.7 Update the driver version to 11.4.0.7 Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index c232bf0e8998..6f4092cb903e 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "11.4.0.6" +#define LPFC_DRIVER_VERSION "11.4.0.7" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ -- cgit v1.3-14-g43fede From 128bddacc4dd7c86070e1e0534687e3083a89d52 Mon Sep 17 00:00:00 2001 From: James Smart Date: Tue, 30 Jan 2018 15:59:03 -0800 Subject: scsi: lpfc: Update 11.4.0.7 modified files for 2018 Copyright Updated Copyright in files updated 11.4.0.7 Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 2 +- drivers/scsi/lpfc/lpfc_attr.c | 2 +- drivers/scsi/lpfc/lpfc_crtn.h | 2 +- drivers/scsi/lpfc/lpfc_els.c | 2 +- drivers/scsi/lpfc/lpfc_hbadisc.c | 2 +- drivers/scsi/lpfc/lpfc_hw4.h | 2 +- drivers/scsi/lpfc/lpfc_init.c | 2 +- drivers/scsi/lpfc/lpfc_mbox.c | 2 +- drivers/scsi/lpfc/lpfc_mem.c | 2 +- drivers/scsi/lpfc/lpfc_nportdisc.c | 4 ++-- drivers/scsi/lpfc/lpfc_nvme.c | 2 +- drivers/scsi/lpfc/lpfc_nvmet.c | 2 +- drivers/scsi/lpfc/lpfc_nvmet.h | 2 +- drivers/scsi/lpfc/lpfc_scsi.c | 2 +- drivers/scsi/lpfc/lpfc_sli.c | 3 +-- drivers/scsi/lpfc/lpfc_sli4.h | 2 +- drivers/scsi/lpfc/lpfc_version.h | 6 +++--- 17 files changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index d042f9118e3b..9698b9635058 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index e25e63eb5a53..7be4bdef4d42 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 3ecf50df93f4..14a86b5b51e4 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 404e1af5e2ab..ba896554a14f 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 9265906d956e..f5bbac3cadbb 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 52fe28ae50fa..8685d26e6929 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index f2d2faef8710..465d890220d5 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index c32d4a323db2..7313ceb0f23b 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index 60078e61da5e..41361662ff08 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index bbf1e1342b09..b63179d895e2 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1,7 +1,7 @@ - /******************************************************************* +/******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 6327f858c4c8..3a103d0895a2 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 823b6df0aec7..a332a6638b1b 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channsel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index 5da35de5ea45..c1bcef3f103c 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 10c2dc0cf1fa..c595046a521b 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index d597e15a1974..e97d080e9f65 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1,8 +1,7 @@ - /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index ac81bfa59278..4545c1fdcb55 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 6f4092cb903e..4adbf07880a2 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -32,6 +32,6 @@ #define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \ LPFC_DRIVER_VERSION -#define LPFC_COPYRIGHT "Copyright (C) 2017 Broadcom. All Rights Reserved. " \ - "The term \"Broadcom\" refers to Broadcom Limited " \ +#define LPFC_COPYRIGHT "Copyright (C) 2017-2018 Broadcom. All Rights " \ + "Reserved. The term \"Broadcom\" refers to Broadcom Limited " \ "and/or its subsidiaries." -- cgit v1.3-14-g43fede From da4704d941766ef61f125d57162eee4ba7f2deda Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 23 Jan 2018 16:33:47 -0800 Subject: scsi: qla2xxx: Use %p for printing pointers Using %p instead of %lx to print a pointer allows to remove a cast. Signed-off-by: Bart Van Assche Cc: Himanshu Madhani Acked-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index aececf664654..995579ea0f7f 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2688,8 +2688,8 @@ qla2x00_chip_diag(scsi_qla_host_t *vha) /* Assume a failed state */ rval = QLA_FUNCTION_FAILED; - ql_dbg(ql_dbg_init, vha, 0x007b, - "Testing device at %lx.\n", (u_long)®->flash_address); + ql_dbg(ql_dbg_init, vha, 0x007b, "Testing device at %p.\n", + ®->flash_address); spin_lock_irqsave(&ha->hardware_lock, flags); -- cgit v1.3-14-g43fede From 7843327a236c4cf103a8b1d5da2b27e7bace0260 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 23 Jan 2018 16:33:48 -0800 Subject: scsi: qla2xxx: Remove unused symbols Remove a few preprocessor macros that are not used anywhere. Signed-off-by: Bart Van Assche Cc: Himanshu Madhani Acked-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_nx2.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_nx2.h b/drivers/scsi/qla2xxx/qla_nx2.h index 83c1b7e17c80..8ba7c1db07c3 100644 --- a/drivers/scsi/qla2xxx/qla_nx2.h +++ b/drivers/scsi/qla2xxx/qla_nx2.h @@ -23,10 +23,6 @@ #define MD_MIU_TEST_AGT_WRDATA_HI 0x410000A4 #define MD_MIU_TEST_AGT_WRDATA_ULO 0x410000B0 #define MD_MIU_TEST_AGT_WRDATA_UHI 0x410000B4 -#define MD_MIU_TEST_AGT_RDDATA_LO 0x410000A8 -#define MD_MIU_TEST_AGT_RDDATA_HI 0x410000AC -#define MD_MIU_TEST_AGT_RDDATA_ULO 0x410000B8 -#define MD_MIU_TEST_AGT_RDDATA_UHI 0x410000BC /* MIU_TEST_AGT_CTRL flags. work for SIU as well */ #define MIU_TA_CTL_WRITE_ENABLE (MIU_TA_CTL_WRITE | MIU_TA_CTL_ENABLE) -- cgit v1.3-14-g43fede From 454d0d41d2718f6dbc4b447c732040c0e4636c73 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 23 Jan 2018 16:33:49 -0800 Subject: scsi: qla4xxx: Remove unused symbols Remove a few preprocessor macros that are not used anywhere. Signed-off-by: Bart Van Assche Cc: Himanshu Madhani Acked-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla4xxx/ql4_nx.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/scsi/qla4xxx/ql4_nx.h b/drivers/scsi/qla4xxx/ql4_nx.h index 337d9fcf6417..2c098cf9a1ac 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.h +++ b/drivers/scsi/qla4xxx/ql4_nx.h @@ -1022,11 +1022,6 @@ struct qla8xxx_minidump_entry_queue { #define MD_MIU_TEST_AGT_WRDATA_ULO 0x410000B0 #define MD_MIU_TEST_AGT_WRDATA_UHI 0x410000B4 -#define MD_MIU_TEST_AGT_RDDATA_LO 0x410000A8 -#define MD_MIU_TEST_AGT_RDDATA_HI 0x410000AC -#define MD_MIU_TEST_AGT_RDDATA_ULO 0x410000B8 -#define MD_MIU_TEST_AGT_RDDATA_UHI 0x410000BC - static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8, 0x410000AC, 0x410000B8, 0x410000BC }; #endif -- cgit v1.3-14-g43fede From bb83e59dae2fd41d0b15dede7fa882c3089f78b0 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 23 Jan 2018 16:33:50 -0800 Subject: scsi: qla4xxx: Move an array from a .h into a .c file This patch does not change any functionality but slightly reduces the size of the compiled kernel module. Signed-off-by: Bart Van Assche Cc: Himanshu Madhani Acked-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla4xxx/ql4_nx.c | 2 ++ drivers/scsi/qla4xxx/ql4_nx.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index 968bd85610f8..43f73583ef5c 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c @@ -45,6 +45,8 @@ qla4_8xxx_pci_base_offsetfset(struct scsi_qla_host *ha, unsigned long off) return NULL; } +static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8, + 0x410000AC, 0x410000B8, 0x410000BC }; #define MAX_CRB_XFORM 60 static unsigned long crb_addr_xform[MAX_CRB_XFORM]; static int qla4_8xxx_crb_table_initialized; diff --git a/drivers/scsi/qla4xxx/ql4_nx.h b/drivers/scsi/qla4xxx/ql4_nx.h index 2c098cf9a1ac..98fe78613eb7 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.h +++ b/drivers/scsi/qla4xxx/ql4_nx.h @@ -1022,6 +1022,4 @@ struct qla8xxx_minidump_entry_queue { #define MD_MIU_TEST_AGT_WRDATA_ULO 0x410000B0 #define MD_MIU_TEST_AGT_WRDATA_UHI 0x410000B4 -static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8, - 0x410000AC, 0x410000B8, 0x410000BC }; #endif -- cgit v1.3-14-g43fede From 2db6228d9cd13bc3bb83bf3436998ea82b0d56ae Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 23 Jan 2018 16:33:51 -0800 Subject: scsi: qla2xxx: Fix function argument descriptions Bring the kernel-doc headers in sync with the function argument lists. Signed-off-by: Bart Van Assche Cc: Himanshu Madhani Acked-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_dbg.c | 4 +- drivers/scsi/qla2xxx/qla_gs.c | 78 +++++++++++++++++++-------------------- drivers/scsi/qla2xxx/qla_init.c | 33 +++++++++-------- drivers/scsi/qla2xxx/qla_inline.h | 1 + drivers/scsi/qla2xxx/qla_iocb.c | 15 ++++++-- drivers/scsi/qla2xxx/qla_isr.c | 23 +++++++----- drivers/scsi/qla2xxx/qla_mbx.c | 5 ++- drivers/scsi/qla2xxx/qla_mr.c | 36 ++++++++++-------- drivers/scsi/qla2xxx/qla_nx.c | 7 ++-- drivers/scsi/qla2xxx/qla_nx2.c | 19 +++++----- drivers/scsi/qla2xxx/qla_sup.c | 1 + drivers/scsi/qla2xxx/qla_target.c | 7 ++-- 12 files changed, 125 insertions(+), 104 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 3e9dc54b89a3..7e9d8f08b9d5 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -717,7 +717,7 @@ qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval) /** * qla2300_fw_dump() - Dumps binary data from the 2300 firmware. - * @ha: HA context + * @vha: HA context * @hardware_locked: Called with the hardware_lock */ void @@ -887,7 +887,7 @@ qla2300_fw_dump_failed: /** * qla2100_fw_dump() - Dumps binary data from the 2100/2200 firmware. - * @ha: HA context + * @vha: HA context * @hardware_locked: Called with the hardware_lock */ void diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 5bf9a59432f6..e4d404c24506 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -21,11 +21,10 @@ static int qla_async_rsnn_nn(scsi_qla_host_t *); /** * qla2x00_prep_ms_iocb() - Prepare common MS/CT IOCB fields for SNS CT query. - * @ha: HA context - * @req_size: request size in bytes - * @rsp_size: response size in bytes + * @vha: HA context + * @arg: CT arguments * - * Returns a pointer to the @ha's ms_iocb. + * Returns a pointer to the @vha's ms_iocb. */ void * qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg) @@ -61,9 +60,8 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg) /** * qla24xx_prep_ms_iocb() - Prepare common CT IOCB fields for SNS CT query. - * @ha: HA context - * @req_size: request size in bytes - * @rsp_size: response size in bytes + * @vha: HA context + * @arg: CT arguments * * Returns a pointer to the @ha's ms_iocb. */ @@ -101,7 +99,7 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg) /** * qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query. - * @ct_req: CT request buffer + * @p: CT request buffer * @cmd: GS command * @rsp_size: response size in bytes * @@ -196,7 +194,7 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, /** * qla2x00_ga_nxt() - SNS scan for fabric devices via GA_NXT command. - * @ha: HA context + * @vha: HA context * @fcport: fcport entry to updated * * Returns 0 on success. @@ -283,7 +281,7 @@ qla2x00_gid_pt_rsp_size(scsi_qla_host_t *vha) /** * qla2x00_gid_pt() - SNS scan for fabric devices via GID_PT command. - * @ha: HA context + * @vha: HA context * @list: switch info entries to populate * * NOTE: Non-Nx_Ports are not requested. @@ -371,7 +369,7 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) /** * qla2x00_gpn_id() - SNS Get Port Name (GPN_ID) query. - * @ha: HA context + * @vha: HA context * @list: switch info entries to populate * * Returns 0 on success. @@ -441,7 +439,7 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) /** * qla2x00_gnn_id() - SNS Get Node Name (GNN_ID) query. - * @ha: HA context + * @vha: HA context * @list: switch info entries to populate * * Returns 0 on success. @@ -583,7 +581,7 @@ err2: /** * qla2x00_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -675,7 +673,8 @@ done: /** * qla2x00_rff_id() - SNS Register FC-4 Features (RFF_ID) supported by the HBA. - * @ha: HA context + * @vha: HA context + * @type: not used * * Returns 0 on success. */ @@ -769,7 +768,7 @@ done: /** * qla2x00_rnn_id() - SNS Register Node Name (RNN_ID) of the HBA. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -874,7 +873,7 @@ qla2x00_get_sym_node_name(scsi_qla_host_t *vha, uint8_t *snn, size_t size) /** * qla2x00_rsnn_nn() - SNS Register Symbolic Node Name (RSNN_NN) of the HBA. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -970,7 +969,7 @@ done: /** * qla2x00_prep_sns_cmd() - Prepare common SNS command request fields for query. - * @ha: HA context + * @vha: HA context * @cmd: GS command * @scmd_len: Subcommand length * @data_size: response size in bytes @@ -1003,7 +1002,7 @@ qla2x00_prep_sns_cmd(scsi_qla_host_t *vha, uint16_t cmd, uint16_t scmd_len, /** * qla2x00_sns_ga_nxt() - SNS scan for fabric devices via GA_NXT command. - * @ha: HA context + * @vha: HA context * @fcport: fcport entry to updated * * This command uses the old Exectute SNS Command mailbox routine. @@ -1067,7 +1066,7 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) /** * qla2x00_sns_gid_pt() - SNS scan for fabric devices via GID_PT command. - * @ha: HA context + * @vha: HA context * @list: switch info entries to populate * * This command uses the old Exectute SNS Command mailbox routine. @@ -1140,7 +1139,7 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) /** * qla2x00_sns_gpn_id() - SNS Get Port Name (GPN_ID) query. - * @ha: HA context + * @vha: HA context * @list: switch info entries to populate * * This command uses the old Exectute SNS Command mailbox routine. @@ -1196,7 +1195,7 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) /** * qla2x00_sns_gnn_id() - SNS Get Node Name (GNN_ID) query. - * @ha: HA context + * @vha: HA context * @list: switch info entries to populate * * This command uses the old Exectute SNS Command mailbox routine. @@ -1259,7 +1258,7 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) /** * qla2x00_snd_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA. - * @ha: HA context + * @vha: HA context * * This command uses the old Exectute SNS Command mailbox routine. * @@ -1308,8 +1307,7 @@ qla2x00_sns_rft_id(scsi_qla_host_t *vha) /** * qla2x00_sns_rnn_id() - SNS Register Node Name (RNN_ID) of the HBA. - * HBA. - * @ha: HA context + * @vha: HA context * * This command uses the old Exectute SNS Command mailbox routine. * @@ -1365,7 +1363,7 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *vha) /** * qla2x00_mgmt_svr_login() - Login to fabric Management Service. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -1401,7 +1399,7 @@ qla2x00_mgmt_svr_login(scsi_qla_host_t *vha) /** * qla2x00_prep_ms_fdmi_iocb() - Prepare common MS IOCB fields for FDMI query. - * @ha: HA context + * @vha: HA context * @req_size: request size in bytes * @rsp_size: response size in bytes * @@ -1439,7 +1437,7 @@ qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size, /** * qla24xx_prep_ms_fdmi_iocb() - Prepare common MS IOCB fields for FDMI query. - * @ha: HA context + * @vha: HA context * @req_size: request size in bytes * @rsp_size: response size in bytes * @@ -1496,7 +1494,7 @@ qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size) /** * qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query. - * @ct_req: CT request buffer + * @p: CT request buffer * @cmd: GS command * @rsp_size: response size in bytes * @@ -1518,8 +1516,8 @@ qla2x00_prep_ct_fdmi_req(struct ct_sns_pkt *p, uint16_t cmd, } /** - * qla2x00_fdmi_rhba() - - * @ha: HA context + * qla2x00_fdmi_rhba() - perform RHBA FDMI registration + * @vha: HA context * * Returns 0 on success. */ @@ -1728,8 +1726,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) } /** - * qla2x00_fdmi_rpa() - - * @ha: HA context + * qla2x00_fdmi_rpa() - perform RPA registration + * @vha: HA context * * Returns 0 on success. */ @@ -1940,8 +1938,8 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) } /** - * qla2x00_fdmiv2_rhba() - - * @ha: HA context + * qla2x00_fdmiv2_rhba() - perform RHBA FDMI v2 registration + * @vha: HA context * * Returns 0 on success. */ @@ -2257,7 +2255,7 @@ qla2x00_fdmiv2_rhba(scsi_qla_host_t *vha) /** * qla2x00_fdmi_dhba() - - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2305,7 +2303,7 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *vha) /** * qla2x00_fdmiv2_rpa() - - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2635,7 +2633,7 @@ qla2x00_fdmiv2_rpa(scsi_qla_host_t *vha) /** * qla2x00_fdmi_register() - - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2693,7 +2691,7 @@ out: /** * qla2x00_gfpn_id() - SNS Get Fabric Port Name (GFPN_ID) query. - * @ha: HA context + * @vha: HA context * @list: switch info entries to populate * * Returns 0 on success. @@ -2778,7 +2776,7 @@ qla24xx_prep_ct_fm_req(struct ct_sns_pkt *p, uint16_t cmd, /** * qla2x00_gpsc() - FCS Get Port Speed Capabilities (GPSC) query. - * @ha: HA context + * @vha: HA context * @list: switch info entries to populate * * Returns 0 on success. @@ -2892,7 +2890,7 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) /** * qla2x00_gff_id() - SNS Get FC-4 Features (GFF_ID) query. * - * @ha: HA context + * @vha: HA context * @list: switch info entries to populate * */ diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 995579ea0f7f..590aa904fdef 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2046,7 +2046,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) /** * qla2100_pci_config() - Setup ISP21xx PCI configuration registers. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2077,7 +2077,7 @@ qla2100_pci_config(scsi_qla_host_t *vha) /** * qla2300_pci_config() - Setup ISP23xx PCI configuration registers. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2159,7 +2159,7 @@ qla2300_pci_config(scsi_qla_host_t *vha) /** * qla24xx_pci_config() - Setup ISP24xx PCI configuration registers. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2203,7 +2203,7 @@ qla24xx_pci_config(scsi_qla_host_t *vha) /** * qla25xx_pci_config() - Setup ISP25xx PCI configuration registers. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2234,7 +2234,7 @@ qla25xx_pci_config(scsi_qla_host_t *vha) /** * qla2x00_isp_firmware() - Choose firmware image. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2270,7 +2270,7 @@ qla2x00_isp_firmware(scsi_qla_host_t *vha) /** * qla2x00_reset_chip() - Reset ISP chip. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2414,6 +2414,7 @@ qla2x00_reset_chip(scsi_qla_host_t *vha) /** * qla81xx_reset_mpi() - Reset's MPI FW via Write MPI Register MBC. + * @vha: HA context * * Returns 0 on success. */ @@ -2430,7 +2431,7 @@ qla81xx_reset_mpi(scsi_qla_host_t *vha) /** * qla24xx_reset_risc() - Perform full reset of ISP24xx RISC. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2645,7 +2646,7 @@ acquired: /** * qla24xx_reset_chip() - Reset ISP24xx chip. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2669,7 +2670,7 @@ qla24xx_reset_chip(scsi_qla_host_t *vha) /** * qla2x00_chip_diag() - Test chip for proper operation. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2793,7 +2794,7 @@ chip_diag_failed: /** * qla24xx_chip_diag() - Test ISP24xx for proper operation. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -3261,7 +3262,7 @@ out: /** * qla2x00_setup_chip() - Load and start RISC firmware. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -3416,7 +3417,7 @@ failed: /** * qla2x00_init_response_q_entries() - Initializes response queue entries. - * @ha: HA context + * @rsp: response queue * * Beginning of request ring has initialization control block already built * by nvram config routine. @@ -3441,7 +3442,7 @@ qla2x00_init_response_q_entries(struct rsp_que *rsp) /** * qla2x00_update_fw_options() - Read and process firmware options. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -3704,7 +3705,7 @@ qla24xx_config_rings(struct scsi_qla_host *vha) /** * qla2x00_init_rings() - Initializes firmware. - * @ha: HA context + * @vha: HA context * * Beginning of request ring has initialization control block already built * by nvram config routine. @@ -3812,7 +3813,7 @@ next_check: /** * qla2x00_fw_ready() - Waits for firmware ready. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -4480,7 +4481,7 @@ qla2x00_rport_del(void *data) /** * qla2x00_alloc_fcport() - Allocate a generic fcport. - * @ha: HA context + * @vha: HA context * @flags: allocation flags * * Returns a pointer to the allocated fcport, or NULL, if none available. diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 4d32426393c7..b7a05aebf065 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -10,6 +10,7 @@ * qla24xx_calc_iocbs() - Determine number of Command Type 3 and * Continuation Type 1 IOCBs to allocate. * + * @vha: HA context * @dsds: number of data segment decriptors needed * * Returns the number of IOCB entries needed to store @dsds. diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 1b62e943ec49..e62ccd931853 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -14,7 +14,7 @@ /** * qla2x00_get_cmd_direction() - Determine control_flag data direction. - * @cmd: SCSI command + * @sp: SCSI command * * Returns the proper CF_* direction based on CDB. */ @@ -86,7 +86,7 @@ qla2x00_calc_iocbs_64(uint16_t dsds) /** * qla2x00_prep_cont_type0_iocb() - Initialize a Continuation Type 0 IOCB. - * @ha: HA context + * @vha: HA context * * Returns a pointer to the Continuation Type 0 IOCB packet. */ @@ -114,7 +114,8 @@ qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha) /** * qla2x00_prep_cont_type1_iocb() - Initialize a Continuation Type 1 IOCB. - * @ha: HA context + * @vha: HA context + * @req: request queue * * Returns a pointer to the continuation type 1 IOCB packet. */ @@ -445,6 +446,8 @@ queuing_error: /** * qla2x00_start_iocbs() - Execute the IOCB command + * @vha: HA context + * @req: request queue */ void qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req) @@ -486,7 +489,9 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req) /** * qla2x00_marker() - Send a marker IOCB to the firmware. - * @ha: HA context + * @vha: HA context + * @req: request queue + * @rsp: response queue * @loop_id: loop ID * @lun: LUN * @type: marker modifier @@ -1190,6 +1195,8 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, * @sp: SRB command to process * @cmd_pkt: Command type 3 IOCB * @tot_dsds: Total number of segments to transfer + * @tot_prot_dsds: + * @fw_prot_opts: */ inline int qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 14109d86c3f6..16c43bd9bb83 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -259,7 +259,7 @@ qla2300_intr_handler(int irq, void *dev_id) /** * qla2x00_mbx_completion() - Process mailbox command completions. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context * @mb0: Mailbox0 register */ static void @@ -612,7 +612,8 @@ qla2x00_find_fcport_by_nportid(scsi_qla_host_t *vha, port_id_t *id, /** * qla2x00_async_event() - Process aynchronous events. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context + * @rsp: response queue * @mb: Mailbox registers (0 - 3) */ void @@ -1255,7 +1256,8 @@ global_port_update: /** * qla2x00_process_completed_request() - Process a Fast Post response. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context + * @req: request queue * @index: SRB index */ void @@ -1969,7 +1971,7 @@ static void qla_ctrlvp_completed(scsi_qla_host_t *vha, struct req_que *req, /** * qla2x00_process_response_queue() - Process response queue entries. - * @ha: SCSI driver HA context + * @rsp: response queue */ void qla2x00_process_response_queue(struct rsp_que *rsp) @@ -2373,7 +2375,8 @@ done: /** * qla2x00_status_entry() - Process a Status IOCB entry. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context + * @rsp: response queue * @pkt: Entry pointer */ static void @@ -2750,7 +2753,7 @@ out: /** * qla2x00_status_cont_entry() - Process a Status Continuations entry. - * @ha: SCSI driver HA context + * @rsp: response queue * @pkt: Entry pointer * * Extended sense data. @@ -2808,7 +2811,8 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt) /** * qla2x00_error_entry() - Process an error entry. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context + * @rsp: response queue * @pkt: Entry pointer * return : 1=allow further error analysis. 0=no additional error analysis. */ @@ -2867,7 +2871,7 @@ fatal: /** * qla24xx_mbx_completion() - Process mailbox command completions. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context * @mb0: Mailbox0 register */ static void @@ -2935,7 +2939,8 @@ void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, /** * qla24xx_process_response_queue() - Process response queue entries. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context + * @rsp: response queue */ void qla24xx_process_response_queue(struct scsi_qla_host *vha, struct rsp_que *rsp) diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 7397aeddd96c..41b0ee47c6a1 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -3385,7 +3385,10 @@ qla8044_read_serdes_word(scsi_qla_host_t *vha, uint32_t addr, uint32_t *data) /** * qla2x00_set_serdes_params() - - * @ha: HA context + * @vha: HA context + * @sw_em_1g: + * @sw_em_2g: + * @sw_em_4g: * * Returns */ diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c index d5da3981cefe..7113acf42ff3 100644 --- a/drivers/scsi/qla2xxx/qla_mr.c +++ b/drivers/scsi/qla2xxx/qla_mr.c @@ -490,7 +490,7 @@ qlafx00_mbx_reg_test(scsi_qla_host_t *vha) /** * qlafx00_pci_config() - Setup ISPFx00 PCI configuration registers. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -519,9 +519,9 @@ qlafx00_pci_config(scsi_qla_host_t *vha) /** * qlafx00_warm_reset() - Perform warm reset of iSA(CPUs being reset on SOC). - * @ha: HA context + * @vha: HA context * - */ + */ static inline void qlafx00_soc_cpu_reset(scsi_qla_host_t *vha) { @@ -625,7 +625,7 @@ qlafx00_soc_cpu_reset(scsi_qla_host_t *vha) /** * qlafx00_soft_reset() - Soft Reset ISPFx00. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -644,7 +644,7 @@ qlafx00_soft_reset(scsi_qla_host_t *vha) /** * qlafx00_chip_diag() - Test ISPFx00 for proper operation. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -1408,7 +1408,7 @@ qlafx00_abort_isp_cleanup(scsi_qla_host_t *vha, bool critemp) /** * qlafx00_init_response_q_entries() - Initializes response queue entries. - * @ha: HA context + * @rsp: response queue * * Beginning of request ring has initialization control block already built * by nvram config routine. @@ -2269,7 +2269,8 @@ qlafx00_ioctl_iosb_entry(scsi_qla_host_t *vha, struct req_que *req, /** * qlafx00_status_entry() - Process a Status IOCB entry. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context + * @rsp: response queue * @pkt: Entry pointer */ static void @@ -2542,7 +2543,7 @@ check_scsi_status: /** * qlafx00_status_cont_entry() - Process a Status Continuations entry. - * @ha: SCSI driver HA context + * @rsp: response queue * @pkt: Entry pointer * * Extended sense data. @@ -2620,7 +2621,9 @@ qlafx00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt) /** * qlafx00_multistatus_entry() - Process Multi response queue entries. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context + * @rsp: response queue + * @pkt: */ static void qlafx00_multistatus_entry(struct scsi_qla_host *vha, @@ -2674,8 +2677,11 @@ qlafx00_multistatus_entry(struct scsi_qla_host *vha, /** * qlafx00_error_entry() - Process an error entry. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context + * @rsp: response queue * @pkt: Entry pointer + * @estatus: + * @etype: */ static void qlafx00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, @@ -2705,7 +2711,8 @@ qlafx00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, /** * qlafx00_process_response_queue() - Process response queue entries. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context + * @rsp: response queue */ static void qlafx00_process_response_queue(struct scsi_qla_host *vha, @@ -2781,7 +2788,7 @@ qlafx00_process_response_queue(struct scsi_qla_host *vha, /** * qlafx00_async_event() - Process aynchronous events. - * @ha: SCSI driver HA context + * @vha: SCSI driver HA context */ static void qlafx00_async_event(scsi_qla_host_t *vha) @@ -2857,10 +2864,9 @@ qlafx00_async_event(scsi_qla_host_t *vha) } /** - * * qlafx00x_mbx_completion() - Process mailbox command completions. - * @ha: SCSI driver HA context - * @mb16: Mailbox16 register + * @vha: SCSI driver HA context + * @mb0: */ static void qlafx00_mbx_completion(scsi_qla_host_t *vha, uint32_t mb0) diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index a77c33987703..872d66dd79cd 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -1732,7 +1732,7 @@ iospace_error_exit: /** * qla82xx_pci_config() - Setup ISP82xx PCI configuration registers. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -1753,7 +1753,7 @@ qla82xx_pci_config(scsi_qla_host_t *vha) /** * qla82xx_reset_chip() - Setup ISP82xx PCI configuration registers. - * @ha: HA context + * @vha: HA context * * Returns 0 on success. */ @@ -2008,11 +2008,10 @@ qla82xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) "MBX pointer ERROR.\n"); } -/* +/** * qla82xx_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. * @irq: * @dev_id: SCSI driver HA context - * @regs: * * Called by system whenever the host adapter generates an interrupt. * diff --git a/drivers/scsi/qla2xxx/qla_nx2.c b/drivers/scsi/qla2xxx/qla_nx2.c index 525ac35a757b..3a2b0282df14 100644 --- a/drivers/scsi/qla2xxx/qla_nx2.c +++ b/drivers/scsi/qla2xxx/qla_nx2.c @@ -280,9 +280,8 @@ qla8044_clear_qsnt_ready(struct scsi_qla_host *vha) } /** - * * qla8044_lock_recovery - Recovers the idc_lock. - * @ha : Pointer to adapter structure + * @vha : Pointer to adapter structure * * Lock Recovery Register * 5-2 Lock recovery owner: Function ID of driver doing lock recovery, @@ -1639,10 +1638,10 @@ qla8044_set_rst_ready(struct scsi_qla_host *vha) /** * qla8044_need_reset_handler - Code to start reset sequence - * @ha: pointer to adapter structure + * @vha: pointer to adapter structure * * Note: IDC lock must be held upon entry - **/ + */ static void qla8044_need_reset_handler(struct scsi_qla_host *vha) { @@ -1859,8 +1858,8 @@ exit_update_idc_reg: /** * qla8044_need_qsnt_handler - Code to start qsnt - * @ha: pointer to adapter structure - **/ + * @vha: pointer to adapter structure + */ static void qla8044_need_qsnt_handler(struct scsi_qla_host *vha) { @@ -2031,10 +2030,10 @@ exit_error: /** * qla4_8xxx_check_temp - Check the ISP82XX temperature. - * @ha: adapter block pointer. + * @vha: adapter block pointer. * * Note: The caller should not hold the idc lock. - **/ + */ static int qla8044_check_temp(struct scsi_qla_host *vha) { @@ -2071,10 +2070,10 @@ int qla8044_read_temperature(scsi_qla_host_t *vha) /** * qla8044_check_fw_alive - Check firmware health - * @ha: Pointer to host adapter structure. + * @vha: Pointer to host adapter structure. * * Context: Interrupt - **/ + */ int qla8044_check_fw_alive(struct scsi_qla_host *vha) { diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index d2db86ea06b2..04458eb19d38 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -2226,6 +2226,7 @@ qla2x00_erase_flash_sector(struct qla_hw_data *ha, uint32_t addr, /** * qla2x00_get_flash_manufacturer() - Read manufacturer ID from flash chip. + * @ha: * @man_id: Flash manufacturer ID * @flash_id: Flash ID */ diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index fc89af8fe256..3735ebd83012 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -6299,10 +6299,11 @@ static void qlt_lport_dump(struct scsi_qla_host *vha, u64 wwpn, /** * qla_tgt_lport_register - register lport with external module * - * @qla_tgt_ops: Pointer for tcm_qla2xxx qla_tgt_ops - * @wwpn: Passwd FC target WWPN - * @callback: lport initialization callback for tcm_qla2xxx code * @target_lport_ptr: pointer for tcm_qla2xxx specific lport data + * @phys_wwpn: + * @npiv_wwpn: + * @npiv_wwnn: + * @callback: lport initialization callback for tcm_qla2xxx code */ int qlt_lport_register(void *target_lport_ptr, u64 phys_wwpn, u64 npiv_wwpn, u64 npiv_wwnn, -- cgit v1.3-14-g43fede From 34e81f7a720d8a638f46b18b35678712dbafb42d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 24 Jan 2018 09:07:58 +0100 Subject: scsi: raid_class: Add 'JBOD' RAID level Not a real RAID level, but some HBAs support JBOD in addition to the 'classical' RAID levels. Signed-off-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/raid_class.c | 1 + include/linux/raid_class.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/scsi/raid_class.c b/drivers/scsi/raid_class.c index 2c146b44d95f..ea88906d2cc5 100644 --- a/drivers/scsi/raid_class.c +++ b/drivers/scsi/raid_class.c @@ -157,6 +157,7 @@ static struct { { RAID_LEVEL_5, "raid5" }, { RAID_LEVEL_50, "raid50" }, { RAID_LEVEL_6, "raid6" }, + { RAID_LEVEL_JBOD, "jbod" }, }; static const char *raid_level_name(enum raid_level level) diff --git a/include/linux/raid_class.h b/include/linux/raid_class.h index 31e1ff69efc8..ec8655514283 100644 --- a/include/linux/raid_class.h +++ b/include/linux/raid_class.h @@ -38,6 +38,7 @@ enum raid_level { RAID_LEVEL_5, RAID_LEVEL_50, RAID_LEVEL_6, + RAID_LEVEL_JBOD, }; struct raid_data { -- cgit v1.3-14-g43fede From e9594acf821df249b458c5aa9fb173eec44db216 Mon Sep 17 00:00:00 2001 From: Michael Lyle Date: Thu, 1 Feb 2018 13:16:56 -0800 Subject: ARM: dts: exynos: Add support for wlan to Artik 5 On the Artik 520 module, there is a bcm4354 attached to mshc_1. Enable it and turn on the regulator used for it, so that both WiFi & Bluetooth will work. Verified to work on the Artik 520 evaluation board. Signed-off-by: Michael Lyle Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos3250-artik5.dtsi | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/arch/arm/boot/dts/exynos3250-artik5.dtsi b/arch/arm/boot/dts/exynos3250-artik5.dtsi index 0aa577fe9f95..620b50c19ead 100644 --- a/arch/arm/boot/dts/exynos3250-artik5.dtsi +++ b/arch/arm/boot/dts/exynos3250-artik5.dtsi @@ -245,6 +245,7 @@ regulator-name = "VLDO23_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; + regulator-always-on; }; ldo24_reg: LDO24 { @@ -316,6 +317,41 @@ status = "okay"; }; +&mshc_1 { + cap-sd-highspeed; + cap-sdio-irq; + disable-wp; + non-removable; + keep-power-in-suspend; + fifo-depth = <0x40>; + vqmmc-supply = <&ldo11_reg>; + /* + * Voltage negotiation is broken for the SDIO periph so we + * can't actually set the voltage here. + * vmmc-supply = <&ldo23_reg>; + */ + card-detect-delay = <500>; + clock-frequency = <100000000>; + max-frequency = <100000000>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <0 1>; + samsung,dw-mshc-ddr-timing = <1 2>; + pinctrl-names = "default"; + pinctrl-0 = <&sd1_cmd &sd1_clk &sd1_bus1 &sd1_bus4 &wlanen>; + bus-width = <4>; + status = "okay"; +}; + +&pinctrl_1 { + wlanen: wlanen { + samsung,pins = "gpx2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + samsung,pin-val = <1>; + }; +}; + &rtc { clocks = <&cmu CLK_RTC>, <&s2mps14_osc S2MPS11_CLK_AP>; clock-names = "rtc", "rtc_src"; -- cgit v1.3-14-g43fede From 2d02d158047643060b7d6e0829fcc81518ce663d Mon Sep 17 00:00:00 2001 From: Jitendra Sharma Date: Thu, 8 Feb 2018 17:11:02 +0530 Subject: remoteproc: Remove null character write of shared mem remoteproc is writing '\0' in the shared mem region. This region is shared among multiple clients that are also trying to read. Hence they miss first character. Remove this null character write, as this mem area is supposed to be Read only. Further during every subsystem reboot, this region is initialized with default, hence no need to write this region. Signed-off-by: Jitendra Sharma Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_adsp_pil.c | 3 --- drivers/remoteproc/qcom_q6v5_pil.c | 6 ------ drivers/remoteproc/qcom_wcnss.c | 3 --- 3 files changed, 12 deletions(-) diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index 373c167892d7..4a2ee6c5816c 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c @@ -201,9 +201,6 @@ static irqreturn_t adsp_fatal_interrupt(int irq, void *dev) rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR); - if (!IS_ERR(msg)) - msg[0] = '\0'; - return IRQ_HANDLED; } diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index b4e5e725848d..7293d45c2671 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -939,9 +939,6 @@ static irqreturn_t q6v5_wdog_interrupt(int irq, void *dev) rproc_report_crash(qproc->rproc, RPROC_WATCHDOG); - if (!IS_ERR(msg)) - msg[0] = '\0'; - return IRQ_HANDLED; } @@ -959,9 +956,6 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *dev) rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR); - if (!IS_ERR(msg)) - msg[0] = '\0'; - return IRQ_HANDLED; } diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 3f0609236a76..043f3d3dea7d 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -332,9 +332,6 @@ static irqreturn_t wcnss_fatal_interrupt(int irq, void *dev) rproc_report_crash(wcnss->rproc, RPROC_FATAL_ERROR); - if (!IS_ERR(msg)) - msg[0] = '\0'; - return IRQ_HANDLED; } -- cgit v1.3-14-g43fede From 70f4ce3b4ee263a3ace0e4d70fd7542ab388070b Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 11 Jan 2018 22:53:55 +0100 Subject: ARM: dts: sun8i: activate SPI on Orange Pi R1 This board has a SPI flash, activate it also in device tree by default. Signed-off-by: Hauke Mehrtens Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-h2-plus-orangepi-r1.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-r1.dts b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-r1.dts index 112f09c67d67..3356f4210d45 100644 --- a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-r1.dts +++ b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-r1.dts @@ -68,6 +68,14 @@ }; }; +&spi0 { + status = "okay"; + + flash@0 { + compatible = "mxicy,mx25l12805d", "jedec,spi-nor"; + }; +}; + &ohci1 { /* * RTL8152B USB-Ethernet adapter is connected to USB1, -- cgit v1.3-14-g43fede From 978e47cc8f8a9a5879fdf455ce5315f574e662a6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 22 Jan 2018 10:25:27 +0100 Subject: ARM: dts: sun8i: a33 Enable our display frontend The display frontend can be used to do hardware scaling, colorspaces conversion or to implement the buffer format output by the Cedar VPU. Since we're starting to have some support for it in the DRM driver, let's enable its DT node. Reviewed-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-a33.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/boot/dts/sun8i-a33.dtsi b/arch/arm/boot/dts/sun8i-a33.dtsi index 50eb84fa246a..a21f2ed07a52 100644 --- a/arch/arm/boot/dts/sun8i-a33.dtsi +++ b/arch/arm/boot/dts/sun8i-a33.dtsi @@ -289,7 +289,6 @@ clock-names = "ahb", "mod", "ram"; resets = <&ccu RST_BUS_DE_FE>; - status = "disabled"; ports { #address-cells = <1>; -- cgit v1.3-14-g43fede From 767ad239a658405116ec0dc91d8eccd6d0a75234 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 9 Jan 2018 18:42:23 +0100 Subject: ARM: dts: samsung: Remove unused samsung_k3pe0e000b The only user of DTSI for Samsung K3PE0E000B memory was removed in commit fa63d037283a ("ARM: dts: omap5: Make uevm as the official board and deprecate sevm support") so get rid of this DTSI as well. Signed-off-by: Krzysztof Kozlowski --- MAINTAINERS | 1 - arch/arm/boot/dts/samsung_k3pe0e000b.dtsi | 68 ------------------------------- 2 files changed, 69 deletions(-) delete mode 100644 arch/arm/boot/dts/samsung_k3pe0e000b.dtsi diff --git a/MAINTAINERS b/MAINTAINERS index 3bdc260e36b7..32edd1fd2365 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1863,7 +1863,6 @@ Q: https://patchwork.kernel.org/project/linux-samsung-soc/list/ S: Maintained F: arch/arm/boot/dts/s3c* F: arch/arm/boot/dts/s5p* -F: arch/arm/boot/dts/samsung* F: arch/arm/boot/dts/exynos* F: arch/arm64/boot/dts/exynos/ F: arch/arm/plat-samsung/ diff --git a/arch/arm/boot/dts/samsung_k3pe0e000b.dtsi b/arch/arm/boot/dts/samsung_k3pe0e000b.dtsi deleted file mode 100644 index dbdda36179ee..000000000000 --- a/arch/arm/boot/dts/samsung_k3pe0e000b.dtsi +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Timings and Geometry for Samsung K3PE0E000B memory part - */ - -/ { - samsung_K3PE0E000B: lpddr2 { - compatible = "Samsung,K3PE0E000B","jedec,lpddr2-s4"; - density = <4096>; - io-width = <32>; - - tRPab-min-tck = <3>; - tRCD-min-tck = <3>; - tWR-min-tck = <3>; - tRASmin-min-tck = <3>; - tRRD-min-tck = <2>; - tWTR-min-tck = <2>; - tXP-min-tck = <2>; - tRTP-min-tck = <2>; - tCKE-min-tck = <3>; - tCKESR-min-tck = <3>; - tFAW-min-tck = <8>; - - timings_samsung_K3PE0E000B_533MHz: lpddr2-timings@0 { - compatible = "jedec,lpddr2-timings"; - min-freq = <10000000>; - max-freq = <533333333>; - tRPab = <21000>; - tRCD = <18000>; - tWR = <15000>; - tRAS-min = <42000>; - tRRD = <10000>; - tWTR = <7500>; - tXP = <7500>; - tRTP = <7500>; - tCKESR = <15000>; - tDQSCK-max = <5500>; - tFAW = <50000>; - tZQCS = <90000>; - tZQCL = <360000>; - tZQinit = <1000000>; - tRAS-max-ns = <70000>; - tDQSCK-max-derated = <6000>; - }; - - timings_samsung_K3PE0E000B_266MHz: lpddr2-timings@1 { - compatible = "jedec,lpddr2-timings"; - min-freq = <10000000>; - max-freq = <266666666>; - tRPab = <21000>; - tRCD = <18000>; - tWR = <15000>; - tRAS-min = <42000>; - tRRD = <10000>; - tWTR = <7500>; - tXP = <7500>; - tRTP = <7500>; - tCKESR = <15000>; - tDQSCK-max = <5500>; - tFAW = <50000>; - tZQCS = <90000>; - tZQCL = <360000>; - tZQinit = <1000000>; - tRAS-max-ns = <70000>; - tDQSCK-max-derated = <6000>; - }; - }; -}; -- cgit v1.3-14-g43fede From f947153f92afcd957476b765dc4ac75d2680b17b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 9 Jan 2018 19:29:54 +0100 Subject: ARM: EXYNOS: Add SPDX license identifiers Replace GPL license statements with SPDX GPL-2.0 and GPL-2.0+ license identifiers. Signed-off-by: Krzysztof Kozlowski --- arch/arm/include/debug/exynos.S | 7 ++----- arch/arm/include/debug/samsung.S | 10 +++------- include/linux/serial_s3c.h | 17 ++--------------- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/arch/arm/include/debug/exynos.S b/arch/arm/include/debug/exynos.S index 60bf3c23200d..74b56769f9cb 100644 --- a/arch/arm/include/debug/exynos.S +++ b/arch/arm/include/debug/exynos.S @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ + */ /* pull in the relevant register and map files. */ diff --git a/arch/arm/include/debug/samsung.S b/arch/arm/include/debug/samsung.S index f4eeed2a1981..69201d7fb48f 100644 --- a/arch/arm/include/debug/samsung.S +++ b/arch/arm/include/debug/samsung.S @@ -1,13 +1,9 @@ -/* arch/arm/plat-samsung/include/plat/debug-macro.S - * +/* SPDX-License-Identifier: GPL-2.0 */ +/* * Copyright 2005, 2007 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ + */ #include diff --git a/include/linux/serial_s3c.h b/include/linux/serial_s3c.h index a7f004a3c177..463ed28d2b27 100644 --- a/include/linux/serial_s3c.h +++ b/include/linux/serial_s3c.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Internal header file for Samsung S3C2410 serial ports (UART0-2) * @@ -10,21 +11,7 @@ * Internal header file for MX1ADS serial ports (UART1 & 2) * * Copyright (C) 2002 Shane Nay (shane@minirl.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ + */ #ifndef __ASM_ARM_REGS_SERIAL_H #define __ASM_ARM_REGS_SERIAL_H -- cgit v1.3-14-g43fede From f12bb91624f9511c1ee87e7c358e4dc11fb44ab3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 9 Jan 2018 19:29:55 +0100 Subject: memory: samsung: Add SPDX license identifiers Replace GPL license statements with SPDX GPL-2.0 license identifiers. Signed-off-by: Krzysztof Kozlowski --- drivers/memory/samsung/Kconfig | 1 + drivers/memory/samsung/Makefile | 1 + drivers/memory/samsung/exynos-srom.c | 18 +++++++----------- drivers/memory/samsung/exynos-srom.h | 7 ++----- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/memory/samsung/Kconfig b/drivers/memory/samsung/Kconfig index 9de12222061c..79ce7ea58903 100644 --- a/drivers/memory/samsung/Kconfig +++ b/drivers/memory/samsung/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 config SAMSUNG_MC bool "Samsung Exynos Memory Controller support" if COMPILE_TEST help diff --git a/drivers/memory/samsung/Makefile b/drivers/memory/samsung/Makefile index 9c554d5522ad..00587be66211 100644 --- a/drivers/memory/samsung/Makefile +++ b/drivers/memory/samsung/Makefile @@ -1 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_EXYNOS_SROM) += exynos-srom.o diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c index bf827a666694..7edd7fb540f2 100644 --- a/drivers/memory/samsung/exynos-srom.c +++ b/drivers/memory/samsung/exynos-srom.c @@ -1,14 +1,10 @@ -/* - * Copyright (c) 2015 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * EXYNOS - SROM Controller support - * Author: Pankaj Dubey - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2015 Samsung Electronics Co., Ltd. +// http://www.samsung.com/ +// +// EXYNOS - SROM Controller support +// Author: Pankaj Dubey #include #include diff --git a/drivers/memory/samsung/exynos-srom.h b/drivers/memory/samsung/exynos-srom.h index 34660c6a57a9..da612797f522 100644 --- a/drivers/memory/samsung/exynos-srom.h +++ b/drivers/memory/samsung/exynos-srom.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Exynos SROMC register definitions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ + */ #ifndef __EXYNOS_SROM_H #define __EXYNOS_SROM_H __FILE__ -- cgit v1.3-14-g43fede From bcb41a53b0b075600cb821302e7177ca5ab62efd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 9 Jan 2018 19:29:56 +0100 Subject: soc: samsung: Add SPDX license identifiers to headers Replace GPL license statements with SPDX GPL-2.0 license identifiers. Signed-off-by: Krzysztof Kozlowski --- include/linux/soc/samsung/exynos-pmu.h | 5 +---- include/linux/soc/samsung/exynos-regs-pmu.h | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/include/linux/soc/samsung/exynos-pmu.h b/include/linux/soc/samsung/exynos-pmu.h index e57eb4b6cc5a..fc0b445bb36b 100644 --- a/include/linux/soc/samsung/exynos-pmu.h +++ b/include/linux/soc/samsung/exynos-pmu.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Header for EXYNOS PMU Driver support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __LINUX_SOC_EXYNOS_PMU_H diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h index bebdde5dccd6..66dcb9ec273a 100644 --- a/include/linux/soc/samsung/exynos-regs-pmu.h +++ b/include/linux/soc/samsung/exynos-regs-pmu.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2010-2015 Samsung Electronics Co., Ltd. * http://www.samsung.com * * EXYNOS - Power management unit definition * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * * Notice: * This is not a list of all Exynos Power Management Unit SFRs. * There are too many of them, not mentioning subtle differences -- cgit v1.3-14-g43fede From 0cfec8ed07f765893df08686d1ecdf49e0f1e968 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 4 Feb 2018 17:46:45 +0100 Subject: ARM: dts: exynos: Add SPDX license identifiers Few files missed recent conversion of GPL v2.0 license statements to SPDX identifiers Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi | 5 +---- arch/arm/boot/dts/exynos-syscon-restart.dtsi | 5 +---- arch/arm/boot/dts/exynos4412-tiny4412.dts | 7 ++----- arch/arm/boot/dts/exynos4412-tmu-sensor-conf.dtsi | 6 +----- arch/arm/boot/dts/exynos4412-trats2.dts | 7 ++----- arch/arm/boot/dts/exynos5800-peach-pi.dts | 5 +---- arch/arm/boot/dts/exynos5800.dtsi | 5 +---- 7 files changed, 9 insertions(+), 31 deletions(-) diff --git a/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi b/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi index 25186ac4188d..1dbf3bbff8d3 100644 --- a/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi +++ b/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Samsung's Exynos SoC MFC (Video Codec) reserved memory common definition. * * Copyright (c) 2016 Samsung Electronics Co., Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ / { diff --git a/arch/arm/boot/dts/exynos-syscon-restart.dtsi b/arch/arm/boot/dts/exynos-syscon-restart.dtsi index 09a2040054ed..4b3dd0549a54 100644 --- a/arch/arm/boot/dts/exynos-syscon-restart.dtsi +++ b/arch/arm/boot/dts/exynos-syscon-restart.dtsi @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Samsung's Exynos SoC syscon reboot/poweroff nodes common definition. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ / { diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts index 5504398e6e37..01f37b5ac9c4 100644 --- a/arch/arm/boot/dts/exynos4412-tiny4412.dts +++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * FriendlyARM's Exynos4412 based TINY4412 board device tree source * @@ -5,11 +6,7 @@ * * Device tree source file for FriendlyARM's TINY4412 board which is based on * Samsung's Exynos4412 SoC. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ + */ /dts-v1/; #include "exynos4412.dtsi" diff --git a/arch/arm/boot/dts/exynos4412-tmu-sensor-conf.dtsi b/arch/arm/boot/dts/exynos4412-tmu-sensor-conf.dtsi index e3f7934d19d0..489b58c619ee 100644 --- a/arch/arm/boot/dts/exynos4412-tmu-sensor-conf.dtsi +++ b/arch/arm/boot/dts/exynos4412-tmu-sensor-conf.dtsi @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Device tree sources for Exynos4412 TMU sensor configuration * * Copyright (c) 2014 Lukasz Majewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts index f285790e8e04..7da66003a3e3 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts +++ b/arch/arm/boot/dts/exynos4412-trats2.dts @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Samsung's Exynos4412 based Trats 2 board device tree source * @@ -6,11 +7,7 @@ * * Device tree source file for Samsung's Trats 2 board which is based on * Samsung's Exynos4412 SoC. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ + */ /dts-v1/; #include "exynos4412.dtsi" diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts index 0029ec27819c..c8a66a12dbeb 100644 --- a/arch/arm/boot/dts/exynos5800-peach-pi.dts +++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Google Peach Pi Rev 10+ board device tree source * * Copyright (c) 2014 Google, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ /dts-v1/; diff --git a/arch/arm/boot/dts/exynos5800.dtsi b/arch/arm/boot/dts/exynos5800.dtsi index 9ddb6bacac5a..57d3b319fd65 100644 --- a/arch/arm/boot/dts/exynos5800.dtsi +++ b/arch/arm/boot/dts/exynos5800.dtsi @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SAMSUNG EXYNOS5800 SoC device tree source * @@ -7,10 +8,6 @@ * SAMSUNG EXYNOS5800 SoC device nodes are listed in this file. * EXYNOS5800 based board files can include this file and provide * values for board specfic bindings. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include "exynos5420.dtsi" -- cgit v1.3-14-g43fede From 88c166cec136135b0b74f1887bd97f5870f20dea Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Mon, 5 Feb 2018 08:57:00 +0100 Subject: ARM: dts: exynos: Use pinctrl labels in exynos4210-pinctrl Labels for pinctrl's are defined in exynos4210.dtsi. Use them in exynos4210-pinctrl instead of full names, as it is done in other Exynos DTSes. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4210-pinctrl.dtsi | 1680 ++++++++++++++--------------- arch/arm/boot/dts/exynos4210.dtsi | 3 +- 2 files changed, 841 insertions(+), 842 deletions(-) diff --git a/arch/arm/boot/dts/exynos4210-pinctrl.dtsi b/arch/arm/boot/dts/exynos4210-pinctrl.dtsi index dbe6c052d8c1..520c5934a8d4 100644 --- a/arch/arm/boot/dts/exynos4210-pinctrl.dtsi +++ b/arch/arm/boot/dts/exynos4210-pinctrl.dtsi @@ -13,853 +13,851 @@ #include -/ { - pinctrl@11400000 { - gpa0: gpa0 { - gpio-controller; - #gpio-cells = <2>; +&pinctrl_0 { + gpa0: gpa0 { + gpio-controller; + #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - }; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpa1: gpa1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpb: gpb { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc0: gpc0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc1: gpc1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpd0: gpd0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpd1: gpd1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpe0: gpe0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpe1: gpe1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpe2: gpe2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpe3: gpe3 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpe4: gpe4 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf0: gpf0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf1: gpf1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf2: gpf2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf3: gpf3 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + uart0_data: uart0-data { + samsung,pins = "gpa0-0", "gpa0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart0_fctl: uart0-fctl { + samsung,pins = "gpa0-2", "gpa0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart1_data: uart1-data { + samsung,pins = "gpa0-4", "gpa0-5"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart1_fctl: uart1-fctl { + samsung,pins = "gpa0-6", "gpa0-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c2_bus: i2c2-bus { + samsung,pins = "gpa0-6", "gpa0-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart2_data: uart2-data { + samsung,pins = "gpa1-0", "gpa1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart2_fctl: uart2-fctl { + samsung,pins = "gpa1-2", "gpa1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart_audio_a: uart-audio-a { + samsung,pins = "gpa1-0", "gpa1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c3_bus: i2c3-bus { + samsung,pins = "gpa1-2", "gpa1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart3_data: uart3-data { + samsung,pins = "gpa1-4", "gpa1-5"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart_audio_b: uart-audio-b { + samsung,pins = "gpa1-4", "gpa1-5"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi0_bus: spi0-bus { + samsung,pins = "gpb-0", "gpb-2", "gpb-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c4_bus: i2c4-bus { + samsung,pins = "gpb-2", "gpb-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi1_bus: spi1-bus { + samsung,pins = "gpb-4", "gpb-6", "gpb-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c5_bus: i2c5-bus { + samsung,pins = "gpb-6", "gpb-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2s1_bus: i2s1-bus { + samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", + "gpc0-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pcm1_bus: pcm1-bus { + samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", + "gpc0-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + ac97_bus: ac97-bus { + samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", + "gpc0-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2s2_bus: i2s2-bus { + samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3", + "gpc1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pcm2_bus: pcm2-bus { + samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3", + "gpc1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spdif_bus: spdif-bus { + samsung,pins = "gpc1-0", "gpc1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c6_bus: i2c6-bus { + samsung,pins = "gpc1-3", "gpc1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi2_bus: spi2-bus { + samsung,pins = "gpc1-1", "gpc1-2", "gpc1-3", "gpc1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c7_bus: i2c7-bus { + samsung,pins = "gpd0-2", "gpd0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c0_bus: i2c0-bus { + samsung,pins = "gpd1-0", "gpd1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c1_bus: i2c1-bus { + samsung,pins = "gpd1-2", "gpd1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pwm0_out: pwm0-out { + samsung,pins = "gpd0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pwm1_out: pwm1-out { + samsung,pins = "gpd0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pwm2_out: pwm2-out { + samsung,pins = "gpd0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pwm3_out: pwm3-out { + samsung,pins = "gpd0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_ctrl: lcd-ctrl { + samsung,pins = "gpd0-0", "gpd0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_sync: lcd-sync { + samsung,pins = "gpf0-0", "gpf0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_en: lcd-en { + samsung,pins = "gpe3-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_clk: lcd-clk { + samsung,pins = "gpf0-0", "gpf0-1", "gpf0-2", "gpf0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_data16: lcd-data-width16 { + samsung,pins = "gpf0-7", "gpf1-0", "gpf1-1", "gpf1-2", + "gpf1-3", "gpf1-6", "gpf1-7", "gpf2-0", + "gpf2-1", "gpf2-2", "gpf2-3", "gpf2-7", + "gpf3-0", "gpf3-1", "gpf3-2", "gpf3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_data18: lcd-data-width18 { + samsung,pins = "gpf0-6", "gpf0-7", "gpf1-0", "gpf1-1", + "gpf1-2", "gpf1-3", "gpf1-6", "gpf1-7", + "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3", + "gpf2-6", "gpf2-7", "gpf3-0", "gpf3-1", + "gpf3-2", "gpf3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_data24: lcd-data-width24 { + samsung,pins = "gpf0-4", "gpf0-5", "gpf0-6", "gpf0-7", + "gpf1-0", "gpf1-1", "gpf1-2", "gpf1-3", + "gpf1-4", "gpf1-5", "gpf1-6", "gpf1-7", + "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3", + "gpf2-4", "gpf2-5", "gpf2-6", "gpf2-7", + "gpf3-0", "gpf3-1", "gpf3-2", "gpf3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&pinctrl_1 { + gpj0: gpj0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpj1: gpj1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpk0: gpk0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpk1: gpk1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpk2: gpk2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpk3: gpk3 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpl0: gpl0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpl1: gpl1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpl2: gpl2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpy0: gpy0 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy1: gpy1 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy2: gpy2 { + gpio-controller; + #gpio-cells = <2>; + }; - gpa1: gpa1 { - gpio-controller; - #gpio-cells = <2>; + gpy3: gpy3 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy4: gpy4 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy5: gpy5 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy6: gpy6 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpx0: gpx0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + interrupt-parent = <&gic>; + interrupts = , + , + , + , + , + , + , + ; + #interrupt-cells = <2>; + }; + + gpx1: gpx1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + interrupt-parent = <&gic>; + interrupts = , + , + , + , + , + , + , + ; + #interrupt-cells = <2>; + }; + + gpx2: gpx2 { + gpio-controller; + #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - }; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpx3: gpx3 { + gpio-controller; + #gpio-cells = <2>; - gpb: gpb { - gpio-controller; - #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; - interrupt-controller; - #interrupt-cells = <2>; - }; + sd0_clk: sd0-clk { + samsung,pins = "gpk0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; - gpc0: gpc0 { - gpio-controller; - #gpio-cells = <2>; + sd0_cmd: sd0-cmd { + samsung,pins = "gpk0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd0_cd: sd0-cd { + samsung,pins = "gpk0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd0_bus1: sd0-bus-width1 { + samsung,pins = "gpk0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd0_bus4: sd0-bus-width4 { + samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd0_bus8: sd0-bus-width8 { + samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_clk: sd4-clk { + samsung,pins = "gpk0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_cmd: sd4-cmd { + samsung,pins = "gpk0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_cd: sd4-cd { + samsung,pins = "gpk0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_bus1: sd4-bus-width1 { + samsung,pins = "gpk0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_bus4: sd4-bus-width4 { + samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_bus8: sd4-bus-width8 { + samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_clk: sd1-clk { + samsung,pins = "gpk1-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_cmd: sd1-cmd { + samsung,pins = "gpk1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_cd: sd1-cd { + samsung,pins = "gpk1-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_bus1: sd1-bus-width1 { + samsung,pins = "gpk1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_bus4: sd1-bus-width4 { + samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_clk: sd2-clk { + samsung,pins = "gpk2-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_cmd: sd2-cmd { + samsung,pins = "gpk2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_cd: sd2-cd { + samsung,pins = "gpk2-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_bus1: sd2-bus-width1 { + samsung,pins = "gpk2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_bus4: sd2-bus-width4 { + samsung,pins = "gpk2-3", "gpk2-4", "gpk2-5", "gpk2-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_bus8: sd2-bus-width8 { + samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_clk: sd3-clk { + samsung,pins = "gpk3-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_cmd: sd3-cmd { + samsung,pins = "gpk3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_cd: sd3-cd { + samsung,pins = "gpk3-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_bus1: sd3-bus-width1 { + samsung,pins = "gpk3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_bus4: sd3-bus-width4 { + samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint0: ext-int0 { + samsung,pins = "gpx0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint8: ext-int8 { + samsung,pins = "gpx1-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint15: ext-int15 { + samsung,pins = "gpx1-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint16: ext-int16 { + samsung,pins = "gpx2-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint31: ext-int31 { + samsung,pins = "gpx3-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + cam_port_a_io: cam-port-a-io { + samsung,pins = "gpj0-0", "gpj0-1", "gpj0-2", "gpj0-3", + "gpj0-4", "gpj0-5", "gpj0-6", "gpj0-7", + "gpj1-0", "gpj1-1", "gpj1-2", "gpj1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + cam_port_a_clk_active: cam-port-a-clk-active { + samsung,pins = "gpj1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + cam_port_a_clk_idle: cam-port-a-clk-idle { + samsung,pins = "gpj1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hdmi_cec: hdmi-cec { + samsung,pins = "gpx3-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&pinctrl_2 { + gpz: gpz { + gpio-controller; + #gpio-cells = <2>; + }; + + i2s0_bus: i2s0-bus { + samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3", + "gpz-4", "gpz-5", "gpz-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpc1: gpc1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpd0: gpd0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpd1: gpd1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpe0: gpe0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpe1: gpe1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpe2: gpe2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpe3: gpe3 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpe4: gpe4 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpf0: gpf0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpf1: gpf1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpf2: gpf2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpf3: gpf3 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - uart0_data: uart0-data { - samsung,pins = "gpa0-0", "gpa0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart0_fctl: uart0-fctl { - samsung,pins = "gpa0-2", "gpa0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart1_data: uart1-data { - samsung,pins = "gpa0-4", "gpa0-5"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart1_fctl: uart1-fctl { - samsung,pins = "gpa0-6", "gpa0-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c2_bus: i2c2-bus { - samsung,pins = "gpa0-6", "gpa0-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart2_data: uart2-data { - samsung,pins = "gpa1-0", "gpa1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart2_fctl: uart2-fctl { - samsung,pins = "gpa1-2", "gpa1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart_audio_a: uart-audio-a { - samsung,pins = "gpa1-0", "gpa1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c3_bus: i2c3-bus { - samsung,pins = "gpa1-2", "gpa1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart3_data: uart3-data { - samsung,pins = "gpa1-4", "gpa1-5"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart_audio_b: uart-audio-b { - samsung,pins = "gpa1-4", "gpa1-5"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - spi0_bus: spi0-bus { - samsung,pins = "gpb-0", "gpb-2", "gpb-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c4_bus: i2c4-bus { - samsung,pins = "gpb-2", "gpb-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - spi1_bus: spi1-bus { - samsung,pins = "gpb-4", "gpb-6", "gpb-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c5_bus: i2c5-bus { - samsung,pins = "gpb-6", "gpb-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2s1_bus: i2s1-bus { - samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", - "gpc0-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pcm1_bus: pcm1-bus { - samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", - "gpc0-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - ac97_bus: ac97-bus { - samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", - "gpc0-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2s2_bus: i2s2-bus { - samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3", - "gpc1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pcm2_bus: pcm2-bus { - samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3", - "gpc1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - spdif_bus: spdif-bus { - samsung,pins = "gpc1-0", "gpc1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c6_bus: i2c6-bus { - samsung,pins = "gpc1-3", "gpc1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - spi2_bus: spi2-bus { - samsung,pins = "gpc1-1", "gpc1-2", "gpc1-3", "gpc1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c7_bus: i2c7-bus { - samsung,pins = "gpd0-2", "gpd0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c0_bus: i2c0-bus { - samsung,pins = "gpd1-0", "gpd1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c1_bus: i2c1-bus { - samsung,pins = "gpd1-2", "gpd1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pwm0_out: pwm0-out { - samsung,pins = "gpd0-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pwm1_out: pwm1-out { - samsung,pins = "gpd0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pwm2_out: pwm2-out { - samsung,pins = "gpd0-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pwm3_out: pwm3-out { - samsung,pins = "gpd0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_ctrl: lcd-ctrl { - samsung,pins = "gpd0-0", "gpd0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_sync: lcd-sync { - samsung,pins = "gpf0-0", "gpf0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_en: lcd-en { - samsung,pins = "gpe3-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_clk: lcd-clk { - samsung,pins = "gpf0-0", "gpf0-1", "gpf0-2", "gpf0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_data16: lcd-data-width16 { - samsung,pins = "gpf0-7", "gpf1-0", "gpf1-1", "gpf1-2", - "gpf1-3", "gpf1-6", "gpf1-7", "gpf2-0", - "gpf2-1", "gpf2-2", "gpf2-3", "gpf2-7", - "gpf3-0", "gpf3-1", "gpf3-2", "gpf3-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_data18: lcd-data-width18 { - samsung,pins = "gpf0-6", "gpf0-7", "gpf1-0", "gpf1-1", - "gpf1-2", "gpf1-3", "gpf1-6", "gpf1-7", - "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3", - "gpf2-6", "gpf2-7", "gpf3-0", "gpf3-1", - "gpf3-2", "gpf3-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_data24: lcd-data-width24 { - samsung,pins = "gpf0-4", "gpf0-5", "gpf0-6", "gpf0-7", - "gpf1-0", "gpf1-1", "gpf1-2", "gpf1-3", - "gpf1-4", "gpf1-5", "gpf1-6", "gpf1-7", - "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3", - "gpf2-4", "gpf2-5", "gpf2-6", "gpf2-7", - "gpf3-0", "gpf3-1", "gpf3-2", "gpf3-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - }; - - pinctrl@11000000 { - gpj0: gpj0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpj1: gpj1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpk0: gpk0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpk1: gpk1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpk2: gpk2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpk3: gpk3 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpl0: gpl0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpl1: gpl1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpl2: gpl2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpy0: gpy0 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy1: gpy1 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy2: gpy2 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy3: gpy3 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy4: gpy4 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy5: gpy5 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy6: gpy6 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpx0: gpx0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - interrupt-parent = <&gic>; - interrupts = , - , - , - , - , - , - , - ; - #interrupt-cells = <2>; - }; - - gpx1: gpx1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - interrupt-parent = <&gic>; - interrupts = , - , - , - , - , - , - , - ; - #interrupt-cells = <2>; - }; - - gpx2: gpx2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpx3: gpx3 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - sd0_clk: sd0-clk { - samsung,pins = "gpk0-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_cmd: sd0-cmd { - samsung,pins = "gpk0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_cd: sd0-cd { - samsung,pins = "gpk0-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_bus1: sd0-bus-width1 { - samsung,pins = "gpk0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_bus4: sd0-bus-width4 { - samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_bus8: sd0-bus-width8 { - samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_clk: sd4-clk { - samsung,pins = "gpk0-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_cmd: sd4-cmd { - samsung,pins = "gpk0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_cd: sd4-cd { - samsung,pins = "gpk0-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_bus1: sd4-bus-width1 { - samsung,pins = "gpk0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_bus4: sd4-bus-width4 { - samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_bus8: sd4-bus-width8 { - samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_clk: sd1-clk { - samsung,pins = "gpk1-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_cmd: sd1-cmd { - samsung,pins = "gpk1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_cd: sd1-cd { - samsung,pins = "gpk1-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_bus1: sd1-bus-width1 { - samsung,pins = "gpk1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_bus4: sd1-bus-width4 { - samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_clk: sd2-clk { - samsung,pins = "gpk2-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_cmd: sd2-cmd { - samsung,pins = "gpk2-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_cd: sd2-cd { - samsung,pins = "gpk2-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_bus1: sd2-bus-width1 { - samsung,pins = "gpk2-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_bus4: sd2-bus-width4 { - samsung,pins = "gpk2-3", "gpk2-4", "gpk2-5", "gpk2-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_bus8: sd2-bus-width8 { - samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_clk: sd3-clk { - samsung,pins = "gpk3-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_cmd: sd3-cmd { - samsung,pins = "gpk3-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_cd: sd3-cd { - samsung,pins = "gpk3-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_bus1: sd3-bus-width1 { - samsung,pins = "gpk3-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_bus4: sd3-bus-width4 { - samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint0: ext-int0 { - samsung,pins = "gpx0-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint8: ext-int8 { - samsung,pins = "gpx1-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint15: ext-int15 { - samsung,pins = "gpx1-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint16: ext-int16 { - samsung,pins = "gpx2-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint31: ext-int31 { - samsung,pins = "gpx3-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - cam_port_a_io: cam-port-a-io { - samsung,pins = "gpj0-0", "gpj0-1", "gpj0-2", "gpj0-3", - "gpj0-4", "gpj0-5", "gpj0-6", "gpj0-7", - "gpj1-0", "gpj1-1", "gpj1-2", "gpj1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - cam_port_a_clk_active: cam-port-a-clk-active { - samsung,pins = "gpj1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - cam_port_a_clk_idle: cam-port-a-clk-idle { - samsung,pins = "gpj1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - hdmi_cec: hdmi-cec { - samsung,pins = "gpx3-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - }; - - pinctrl@3860000 { - gpz: gpz { - gpio-controller; - #gpio-cells = <2>; - }; - - i2s0_bus: i2s0-bus { - samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3", - "gpz-4", "gpz-5", "gpz-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pcm0_bus: pcm0-bus { - samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3", - "gpz-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; + pcm0_bus: pcm0-bus { + samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3", + "gpz-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; }; }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index cc978cf28267..82c1a3262b73 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -17,7 +17,6 @@ */ #include "exynos4.dtsi" -#include "exynos4210-pinctrl.dtsi" #include "exynos4-cpu-thermal.dtsi" / { @@ -468,3 +467,5 @@ &sysmmu_rotator { power-domains = <&pd_lcd0>; }; + +#include "exynos4210-pinctrl.dtsi" -- cgit v1.3-14-g43fede From 2666ca9197e3d352f43b02d7dfb7c6dd72e7c614 Mon Sep 17 00:00:00 2001 From: Sarangdhar Joshi Date: Fri, 5 Jan 2018 16:04:17 -0800 Subject: remoteproc: Add remote processor coredump support As the remoteproc framework restarts the remote processor after a fatal event, it's useful to be able to acquire a coredump of the remote processor's state, for post mortem debugging. This patch introduces a mechanism for extracting the memory contents after the remote has stopped and before the restart sequence has begun in the recovery path. The remoteproc framework builds the core dump in memory and use devcoredump to expose this to user space. Signed-off-by: Sarangdhar Joshi [bjorn: Use vmalloc instead of composing the ELF on the fly] Signed-off-by: Bjorn Andersson --- drivers/remoteproc/Kconfig | 1 + drivers/remoteproc/remoteproc_core.c | 128 +++++++++++++++++++++++++++++++++++ include/linux/remoteproc.h | 18 +++++ 3 files changed, 147 insertions(+) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index b609e1d3654b..3e4bca77188d 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -6,6 +6,7 @@ config REMOTEPROC select CRC32 select FW_LOADER select VIRTIO + select WANT_DEV_COREDUMP help Support for remote processors (such as DSP coprocessors). These are mainly used on embedded systems. diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 4170dfbd93bd..5af7547b9d8d 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -801,6 +802,20 @@ static void rproc_remove_subdevices(struct rproc *rproc) subdev->remove(subdev); } +/** + * rproc_coredump_cleanup() - clean up dump_segments list + * @rproc: the remote processor handle + */ +static void rproc_coredump_cleanup(struct rproc *rproc) +{ + struct rproc_dump_segment *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) { + list_del(&entry->node); + kfree(entry); + } +} + /** * rproc_resource_cleanup() - clean up and free all acquired resources * @rproc: rproc handle @@ -848,6 +863,8 @@ static void rproc_resource_cleanup(struct rproc *rproc) /* clean up remote vdev entries */ list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) kref_put(&rvdev->refcount, rproc_vdev_release); + + rproc_coredump_cleanup(rproc); } static int rproc_start(struct rproc *rproc, const struct firmware *fw) @@ -1017,6 +1034,113 @@ static int rproc_stop(struct rproc *rproc) return 0; } +/** + * rproc_coredump_add_segment() - add segment of device memory to coredump + * @rproc: handle of a remote processor + * @da: device address + * @size: size of segment + * + * Add device memory to the list of segments to be included in a coredump for + * the remoteproc. + * + * Return: 0 on success, negative errno on error. + */ +int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size) +{ + struct rproc_dump_segment *segment; + + segment = kzalloc(sizeof(*segment), GFP_KERNEL); + if (!segment) + return -ENOMEM; + + segment->da = da; + segment->size = size; + + list_add_tail(&segment->node, &rproc->dump_segments); + + return 0; +} +EXPORT_SYMBOL(rproc_coredump_add_segment); + +/** + * rproc_coredump() - perform coredump + * @rproc: rproc handle + * + * This function will generate an ELF header for the registered segments + * and create a devcoredump device associated with rproc. + */ +static void rproc_coredump(struct rproc *rproc) +{ + struct rproc_dump_segment *segment; + struct elf32_phdr *phdr; + struct elf32_hdr *ehdr; + size_t data_size; + size_t offset; + void *data; + void *ptr; + int phnum = 0; + + if (list_empty(&rproc->dump_segments)) + return; + + data_size = sizeof(*ehdr); + list_for_each_entry(segment, &rproc->dump_segments, node) { + data_size += sizeof(*phdr) + segment->size; + + phnum++; + } + + data = vmalloc(data_size); + if (!data) + return; + + ehdr = data; + + memset(ehdr, 0, sizeof(*ehdr)); + memcpy(ehdr->e_ident, ELFMAG, SELFMAG); + ehdr->e_ident[EI_CLASS] = ELFCLASS32; + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; + ehdr->e_type = ET_CORE; + ehdr->e_machine = EM_NONE; + ehdr->e_version = EV_CURRENT; + ehdr->e_entry = rproc->bootaddr; + ehdr->e_phoff = sizeof(*ehdr); + ehdr->e_ehsize = sizeof(*ehdr); + ehdr->e_phentsize = sizeof(*phdr); + ehdr->e_phnum = phnum; + + phdr = data + ehdr->e_phoff; + offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum; + list_for_each_entry(segment, &rproc->dump_segments, node) { + memset(phdr, 0, sizeof(*phdr)); + phdr->p_type = PT_LOAD; + phdr->p_offset = offset; + phdr->p_vaddr = segment->da; + phdr->p_paddr = segment->da; + phdr->p_filesz = segment->size; + phdr->p_memsz = segment->size; + phdr->p_flags = PF_R | PF_W | PF_X; + phdr->p_align = 0; + + ptr = rproc_da_to_va(rproc, segment->da, segment->size); + if (!ptr) { + dev_err(&rproc->dev, + "invalid coredump segment (%pad, %zu)\n", + &segment->da, segment->size); + memset(data + offset, 0xff, segment->size); + } else { + memcpy(data + offset, ptr, segment->size); + } + + offset += phdr->p_filesz; + phdr++; + } + + dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL); +} + /** * rproc_trigger_recovery() - recover a remoteproc * @rproc: the remote processor @@ -1043,6 +1167,9 @@ int rproc_trigger_recovery(struct rproc *rproc) if (ret) goto unlock_mutex; + /* generate coredump */ + rproc_coredump(rproc); + /* load firmware */ ret = request_firmware(&firmware_p, rproc->firmware, dev); if (ret < 0) { @@ -1443,6 +1570,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, INIT_LIST_HEAD(&rproc->traces); INIT_LIST_HEAD(&rproc->rvdevs); INIT_LIST_HEAD(&rproc->subdevs); + INIT_LIST_HEAD(&rproc->dump_segments); INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 728d421fffe9..b60c3a31b75d 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -394,6 +394,21 @@ enum rproc_crash_type { RPROC_FATAL_ERROR, }; +/** + * struct rproc_dump_segment - segment info from ELF header + * @node: list node related to the rproc segment list + * @da: device address of the segment + * @size: size of the segment + */ +struct rproc_dump_segment { + struct list_head node; + + dma_addr_t da; + size_t size; + + loff_t offset; +}; + /** * struct rproc - represents a physical remote processor device * @node: list node of this rproc object @@ -424,6 +439,7 @@ enum rproc_crash_type { * @cached_table: copy of the resource table * @table_sz: size of @cached_table * @has_iommu: flag to indicate if remote processor is behind an MMU + * @dump_segments: list of segments in the firmware */ struct rproc { struct list_head node; @@ -455,6 +471,7 @@ struct rproc { size_t table_sz; bool has_iommu; bool auto_boot; + struct list_head dump_segments; }; /** @@ -534,6 +551,7 @@ void rproc_free(struct rproc *rproc); int rproc_boot(struct rproc *rproc); void rproc_shutdown(struct rproc *rproc); void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type); +int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size); static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) { -- cgit v1.3-14-g43fede From c1d35c1ab4242464a0e5953ae69de8aa78156c6c Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 5 Jan 2018 16:04:18 -0800 Subject: remoteproc: Rename "load_rsc_table" to "parse_fw" The resource table is just one possible source of information that can be extracted from the firmware file. Generalize this interface to allow drivers to override this with parsers of other types of information. Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_core.c | 6 +++--- drivers/remoteproc/remoteproc_internal.h | 7 +++---- include/linux/remoteproc.h | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 5af7547b9d8d..fd257607a578 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -944,8 +944,8 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) rproc->bootaddr = rproc_get_boot_addr(rproc, fw); - /* load resource table */ - ret = rproc_load_rsc_table(rproc, fw); + /* Load resource table, core dump segment list etc from the firmware */ + ret = rproc_parse_fw(rproc, fw); if (ret) goto disable_iommu; @@ -1555,7 +1555,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, /* Default to ELF loader if no load function is specified */ if (!rproc->ops->load) { rproc->ops->load = rproc_elf_load_segments; - rproc->ops->load_rsc_table = rproc_elf_load_rsc_table; + rproc->ops->parse_fw = rproc_elf_load_rsc_table; rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; rproc->ops->sanity_check = rproc_elf_sanity_check; rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 55a2950c5cb7..7570beb035b5 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -88,11 +88,10 @@ int rproc_load_segments(struct rproc *rproc, const struct firmware *fw) return -EINVAL; } -static inline int rproc_load_rsc_table(struct rproc *rproc, - const struct firmware *fw) +static inline int rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { - if (rproc->ops->load_rsc_table) - return rproc->ops->load_rsc_table(rproc, fw); + if (rproc->ops->parse_fw) + return rproc->ops->parse_fw(rproc, fw); return 0; } diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index b60c3a31b75d..f16864acedad 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -344,7 +344,7 @@ struct rproc_ops { int (*stop)(struct rproc *rproc); void (*kick)(struct rproc *rproc, int vqid); void * (*da_to_va)(struct rproc *rproc, u64 da, int len); - int (*load_rsc_table)(struct rproc *rproc, const struct firmware *fw); + int (*parse_fw)(struct rproc *rproc, const struct firmware *fw); struct resource_table *(*find_loaded_rsc_table)( struct rproc *rproc, const struct firmware *fw); int (*load)(struct rproc *rproc, const struct firmware *fw); -- cgit v1.3-14-g43fede From 4dd27f544c84c4d079049dd716beee192fcc7e03 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 5 Jan 2018 16:04:19 -0800 Subject: soc: qcom: mdt-loader: Return relocation base In order to implement support for grabbing core dumps in remoteproc it's necessary to know the relocated base of the image, as the offsets from the virtual memory base might not be based on the physical address. Return the adjusted physical base address to the caller. Acked-by: Andy Gross Signed-off-by: Bjorn Andersson --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 4 ++-- drivers/media/platform/qcom/venus/firmware.c | 2 +- drivers/remoteproc/qcom_adsp_pil.c | 4 +++- drivers/remoteproc/qcom_wcnss.c | 3 ++- drivers/soc/qcom/mdt_loader.c | 7 ++++++- include/linux/soc/qcom/mdt_loader.h | 3 ++- 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 7e09d44e4a15..8676fa9a9f49 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -89,14 +89,14 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname) */ if (to_adreno_gpu(gpu)->fwloc == FW_LOCATION_LEGACY) { ret = qcom_mdt_load(dev, fw, fwname, GPU_PAS_ID, - mem_region, mem_phys, mem_size); + mem_region, mem_phys, mem_size, NULL); } else { char newname[strlen("qcom/") + strlen(fwname) + 1]; sprintf(newname, "qcom/%s", fwname); ret = qcom_mdt_load(dev, fw, newname, GPU_PAS_ID, - mem_region, mem_phys, mem_size); + mem_region, mem_phys, mem_size, NULL); } if (ret) goto out; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 521d4b36c090..c4a577848dd7 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -76,7 +76,7 @@ int venus_boot(struct device *dev, const char *fwname) } ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys, - mem_size); + mem_size, NULL); release_firmware(mdt); diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index 4a2ee6c5816c..ca2bda9bc71d 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c @@ -82,7 +82,9 @@ static int adsp_load(struct rproc *rproc, const struct firmware *fw) struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id, - adsp->mem_region, adsp->mem_phys, adsp->mem_size); + adsp->mem_region, adsp->mem_phys, adsp->mem_size, + &adsp->mem_reloc); + } static int adsp_start(struct rproc *rproc) diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 043f3d3dea7d..f1ae5ecbc392 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -153,7 +153,8 @@ static int wcnss_load(struct rproc *rproc, const struct firmware *fw) struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID, - wcnss->mem_region, wcnss->mem_phys, wcnss->mem_size); + wcnss->mem_region, wcnss->mem_phys, + wcnss->mem_size, &wcnss->mem_reloc); } static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss) diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 08bd8549242a..17b314d9a148 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -83,12 +83,14 @@ EXPORT_SYMBOL_GPL(qcom_mdt_get_size); * @mem_region: allocated memory region to load firmware into * @mem_phys: physical address of allocated memory region * @mem_size: size of the allocated memory region + * @reloc_base: adjusted physical address after relocation * * Returns 0 on success, negative errno otherwise. */ int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *firmware, int pas_id, void *mem_region, - phys_addr_t mem_phys, size_t mem_size) + phys_addr_t mem_phys, size_t mem_size, + phys_addr_t *reloc_base) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; @@ -192,6 +194,9 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); } + if (reloc_base) + *reloc_base = mem_reloc; + out: kfree(fw_name); diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index bd8e0864b059..5b98bbdabc25 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -14,6 +14,7 @@ struct firmware; ssize_t qcom_mdt_get_size(const struct firmware *fw); int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, - phys_addr_t mem_phys, size_t mem_size); + phys_addr_t mem_phys, size_t mem_size, + phys_addr_t *reloc_base); #endif -- cgit v1.3-14-g43fede From dcb57ed43d9ec5e16628c337143cd6b387f42778 Mon Sep 17 00:00:00 2001 From: Sarangdhar Joshi Date: Fri, 5 Jan 2018 16:04:20 -0800 Subject: remoteproc: qcom: Register segments for core dump Register MDT segments with the remoteproc core dump functionality in order to include them in a core dump, in case of a recovery of the remote processor. Signed-off-by: Sarangdhar Joshi Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_adsp_pil.c | 1 + drivers/remoteproc/qcom_common.c | 44 ++++++++++++++++++++++++++++++++++++++ drivers/remoteproc/qcom_common.h | 2 ++ drivers/remoteproc/qcom_wcnss.c | 1 + 4 files changed, 48 insertions(+) diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index ca2bda9bc71d..ac8f9a77b821 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c @@ -179,6 +179,7 @@ static const struct rproc_ops adsp_ops = { .start = adsp_start, .stop = adsp_stop, .da_to_va = adsp_da_to_va, + .parse_fw = qcom_register_dump_segments, .load = adsp_load, }; diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index 00602499713f..b7d53a9cf21f 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "remoteproc_internal.h" #include "qcom_common.h" @@ -79,6 +80,49 @@ void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glin } EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev); +/** + * qcom_register_dump_segments() - register segments for coredump + * @rproc: remoteproc handle + * @fw: firmware header + * + * Register all segments of the ELF in the remoteproc coredump segment list + * + * Return: 0 on success, negative errno on failure. + */ +int qcom_register_dump_segments(struct rproc *rproc, + const struct firmware *fw) +{ + const struct elf32_phdr *phdrs; + const struct elf32_phdr *phdr; + const struct elf32_hdr *ehdr; + int ret; + int i; + + ehdr = (struct elf32_hdr *)fw->data; + phdrs = (struct elf32_phdr *)(ehdr + 1); + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + + if (phdr->p_type != PT_LOAD) + continue; + + if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) + continue; + + if (!phdr->p_memsz) + continue; + + ret = rproc_coredump_add_segment(rproc, phdr->p_paddr, + phdr->p_memsz); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_register_dump_segments); + static int smd_subdev_probe(struct rproc_subdev *subdev) { struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h index 728be9834d8b..7e614520fb69 100644 --- a/drivers/remoteproc/qcom_common.h +++ b/drivers/remoteproc/qcom_common.h @@ -30,6 +30,8 @@ struct qcom_rproc_ssr { void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); +int qcom_register_dump_segments(struct rproc *rproc, const struct firmware *fw); + void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index f1ae5ecbc392..32a3a53589dc 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -309,6 +309,7 @@ static const struct rproc_ops wcnss_ops = { .start = wcnss_start, .stop = wcnss_stop, .da_to_va = wcnss_da_to_va, + .parse_fw = qcom_register_dump_segments, .load = wcnss_load, }; -- cgit v1.3-14-g43fede From a2730ed3e0f39c528014673cb96807bb16a8ce35 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 21 Jan 2018 23:14:12 +0100 Subject: ARM: dts: meson8b: grow the reset controller memory zone The reset controller in the Meson8b SoCs also supports level resets. These use the same defines (from dt-bindings/reset/amlogic,meson8b-reset.h) as the reset pulses. The reset-meson driver internally handles the difference if a consumer requests a reset pulse or a level reset. However, for this to work we must extend the memory zone of the reset controller. Suggested-by: Neil Armstrong Signed-off-by: Martin Blumenstingl Reviewed-by: Neil Armstrong Signed-off-by: Kevin Hilman --- arch/arm/boot/dts/meson8b.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/meson8b.dtsi b/arch/arm/boot/dts/meson8b.dtsi index 7cd03ed3742e..4c1ac3a44357 100644 --- a/arch/arm/boot/dts/meson8b.dtsi +++ b/arch/arm/boot/dts/meson8b.dtsi @@ -152,7 +152,7 @@ reset: reset-controller@4404 { compatible = "amlogic,meson8b-reset"; - reg = <0x4404 0x20>; + reg = <0x4404 0x9c>; #reset-cells = <1>; }; -- cgit v1.3-14-g43fede From e3087187e5f18231e48450e602220eb65c409b59 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 21 Jan 2018 23:14:13 +0100 Subject: ARM: dts: meson8: add the reset controller Meson8 uses the same reset controller as Meson8b. Add the node along with the #include for the reset lines to meson8.dtsi so we can use it from there as well. Signed-off-by: Martin Blumenstingl Reviewed-by: Neil Armstrong Signed-off-by: Kevin Hilman --- arch/arm/boot/dts/meson8.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/meson8.dtsi b/arch/arm/boot/dts/meson8.dtsi index d2e3eeaa1a5f..f48e89a7f7b4 100644 --- a/arch/arm/boot/dts/meson8.dtsi +++ b/arch/arm/boot/dts/meson8.dtsi @@ -46,6 +46,7 @@ #include #include #include +#include #include "meson.dtsi" / { @@ -187,6 +188,12 @@ reg = <0x8000 0x4>, <0x4000 0x460>; }; + reset: reset-controller@4404 { + compatible = "amlogic,meson8b-reset"; + reg = <0x4404 0x9c>; + #reset-cells = <1>; + }; + analog_top: analog-top@81a8 { compatible = "amlogic,meson8-analog-top", "syscon"; reg = <0x81a8 0x14>; -- cgit v1.3-14-g43fede From e1fa57dfd7e28b30d6419b7c309b4a890ff4410a Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 21 Jan 2018 23:14:14 +0100 Subject: ARM: dts: meson8: add the USB reset line Now that we support the reset controller on Meson8 we can add the reset line to the USB PHYs (just like on Meson8b). Signed-off-by: Martin Blumenstingl Reviewed-by: Neil Armstrong Signed-off-by: Kevin Hilman --- arch/arm/boot/dts/meson8.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/meson8.dtsi b/arch/arm/boot/dts/meson8.dtsi index f48e89a7f7b4..dcc9292d2ffa 100644 --- a/arch/arm/boot/dts/meson8.dtsi +++ b/arch/arm/boot/dts/meson8.dtsi @@ -390,10 +390,12 @@ compatible = "amlogic,meson8-usb2-phy", "amlogic,meson-mx-usb2-phy"; clocks = <&clkc CLKID_USB>, <&clkc CLKID_USB0>; clock-names = "usb_general", "usb"; + resets = <&reset RESET_USB_OTG>; }; &usb1_phy { compatible = "amlogic,meson8-usb2-phy", "amlogic,meson-mx-usb2-phy"; clocks = <&clkc CLKID_USB>, <&clkc CLKID_USB1>; clock-names = "usb_general", "usb"; + resets = <&reset RESET_USB_OTG>; }; -- cgit v1.3-14-g43fede From b96446541d8390ec22e6dc579282770453ec98a4 Mon Sep 17 00:00:00 2001 From: Emiliano Ingrassia Date: Fri, 19 Jan 2018 02:48:00 +0100 Subject: ARM: dts: meson8b: extend ethernet controller description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable S805 (aka Meson8b) ethernet pin multiplexing and extend the controller description. The programmable ethernet (PRG_ETHERNET) register address value (0xc1108108), contained in meson.dtsi, is overridden according to the value found in S805 SoC manual. This also required to switch to "amlogic,meson8b-dwmac" compatible to correctly configure that register. The two clock sources "clkin0" and "clkin1" are both equals to MPLL2 because, as reported in bit 9-7 register description, that is the only Meson8b ethernet clock source. Signed-off-by: Emiliano Ingrassia Tested-by: Linus Lüssing Reviewed-by: Martin Blumenstingl Signed-off-by: Kevin Hilman --- arch/arm/boot/dts/meson8b.dtsi | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/meson8b.dtsi b/arch/arm/boot/dts/meson8b.dtsi index 4c1ac3a44357..1a7c16640ea5 100644 --- a/arch/arm/boot/dts/meson8b.dtsi +++ b/arch/arm/boot/dts/meson8b.dtsi @@ -185,6 +185,27 @@ #gpio-cells = <2>; gpio-ranges = <&pinctrl_cbus 0 0 130>; }; + + eth_rgmii_pins: eth-rgmii { + mux { + groups = "eth_tx_clk", + "eth_tx_en", + "eth_txd1_0", + "eth_txd1_1", + "eth_txd0_0", + "eth_txd0_1", + "eth_rx_clk", + "eth_rx_dv", + "eth_rxd1", + "eth_rxd0", + "eth_mdio_en", + "eth_mdc", + "eth_ref_clk", + "eth_txd2", + "eth_txd3"; + function = "ethernet"; + }; + }; }; }; @@ -203,8 +224,18 @@ }; ðmac { - clocks = <&clkc CLKID_ETH>; - clock-names = "stmmaceth"; + compatible = "amlogic,meson8b-dwmac", "snps,dwmac-3.70a", "snps,dwmac"; + + reg = <0xc9410000 0x10000 + 0xc1108140 0x4>; + + clocks = <&clkc CLKID_ETH>, + <&clkc CLKID_MPLL2>, + <&clkc CLKID_MPLL2>; + clock-names = "stmmaceth", "clkin0", "clkin1"; + + resets = <&reset RESET_ETHERNET>; + reset-names = "stmmaceth"; }; &gpio_intc { -- cgit v1.3-14-g43fede From 9c15795a4f96cb4f82a0e1503b46621251644bc2 Mon Sep 17 00:00:00 2001 From: Emiliano Ingrassia Date: Fri, 19 Jan 2018 02:49:17 +0100 Subject: ARM: dts: meson8b-odroidc1: ethernet support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Odroid-C1+ board is equipped with an RTL8211F ethernet PHY which supports 10/100/1000 Mbps ethernet. The PHY reset and interrupt lines are controlled by the SoC via two GPIO lines (GPIOH_4 and GPIOH_3 respectively). The PHY energy efficient ethernet (eee) mode is marked as broken using "eee-broken-1000t" because, during tests, high packet losses were experienced without it. Signed-off-by: Emiliano Ingrassia Tested-by: Linus Lüssing Reviewed-by: Martin Blumenstingl Signed-off-by: Kevin Hilman --- arch/arm/boot/dts/meson8b-odroidc1.dts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/arch/arm/boot/dts/meson8b-odroidc1.dts b/arch/arm/boot/dts/meson8b-odroidc1.dts index 9ff6ca4e20d0..d5e83051bb54 100644 --- a/arch/arm/boot/dts/meson8b-odroidc1.dts +++ b/arch/arm/boot/dts/meson8b-odroidc1.dts @@ -99,3 +99,33 @@ &usb1 { status = "okay"; }; + +ðmac { + status = "okay"; + + snps,reset-gpio = <&gpio GPIOH_4 GPIO_ACTIVE_HIGH>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 30000>; + + pinctrl-0 = <ð_rgmii_pins>; + pinctrl-names = "default"; + + phy-mode = "rgmii"; + phy-handle = <ð_phy>; + amlogic,tx-delay-ns = <4>; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + /* Realtek RTL8211F (0x001cc916) */ + eth_phy: ethernet-phy@0 { + reg = <0>; + eee-broken-1000t; + interrupt-parent = <&gpio_intc>; + /* GPIOH_3 */ + interrupts = <17 IRQ_TYPE_LEVEL_LOW>; + }; + }; +}; -- cgit v1.3-14-g43fede From eafd53d3154af57d31a1307d9be356fdcfe642b6 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 18 Dec 2017 12:35:46 +0100 Subject: ARM64: meson-axg: enable hardware rng Enable the hardware random generator Signed-off-by: Jerome Brunet Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi index 70c776ef7aa7..994106d2da6e 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi @@ -234,6 +234,13 @@ #size-cells = <2>; ranges = <0x0 0x0 0x0 0xff634000 0x0 0x2000>; + hwrng: rng { + compatible = "amlogic,meson-rng"; + reg = <0x0 0x18 0x0 0x4>; + clocks = <&clkc CLKID_RNG0>; + clock-names = "core"; + }; + pinctrl_periphs: pinctrl@480 { compatible = "amlogic,meson-axg-periphs-pinctrl"; #address-cells = <2>; -- cgit v1.3-14-g43fede From dc6f858e26909ae7349daf19f9ddaa0dd8f0adaa Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Mon, 20 Nov 2017 22:54:13 +0800 Subject: ARM64: dts: meson-axg: add I2C DT info for Meson-AXG SoC There are four I2C masters in EE domain, and one I2C Master in AO domain, the DT info here should describe them all. Signed-off-by: Jian Hu Signed-off-by: Yixun Lan Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi index 994106d2da6e..9ac300fc54a1 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi @@ -163,6 +163,54 @@ status = "disabled"; }; + i2c0: i2c@1f000 { + compatible = "amlogic,meson-axg-i2c"; + status = "disabled"; + reg = <0x0 0x1f000 0x0 0x20>; + interrupts = , + ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkc CLKID_I2C>; + clock-names = "clk_i2c"; + }; + + i2c1: i2c@1e000 { + compatible = "amlogic,meson-axg-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x1e000 0x0 0x20>; + status = "disabled"; + interrupts = , + ; + clocks = <&clkc CLKID_I2C>; + clock-names = "clk_i2c"; + }; + + i2c2: i2c@1d000 { + compatible = "amlogic,meson-axg-i2c"; + status = "disabled"; + reg = <0x0 0x1d000 0x0 0x20>; + interrupts = , + ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkc CLKID_I2C>; + clock-names = "clk_i2c"; + }; + + i2c3: i2c@1c000 { + compatible = "amlogic,meson-axg-i2c"; + status = "disabled"; + reg = <0x0 0x1c000 0x0 0x20>; + interrupts = , + ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkc CLKID_I2C>; + clock-names = "clk_i2c"; + }; + uart_A: serial@24000 { compatible = "amlogic,meson-gx-uart", "amlogic,meson-uart"; reg = <0x0 0x24000 0x0 0x18>; @@ -517,6 +565,17 @@ status = "disabled"; }; + i2c_AO: i2c@5000 { + compatible = "amlogic,meson-axg-i2c"; + status = "disabled"; + reg = <0x0 0x05000 0x0 0x20>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkc CLKID_I2C>; + clock-names = "clk_i2c"; + }; + uart_AO: serial@3000 { compatible = "amlogic,meson-gx-uart", "amlogic,meson-ao-uart"; reg = <0x0 0x3000 0x0 0x18>; -- cgit v1.3-14-g43fede From 8a7669a53e6ad623aadfc716d233cd8d4901d1ef Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Mon, 20 Nov 2017 22:54:14 +0800 Subject: ARM64: dts: meson-axg: describe pin DT info for I2C controller Describe all the pin mux for the I2C controller which found in Meson-AXG SoC. Signed-off-by: Jian Hu Signed-off-by: Yixun Lan Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 64 ++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi index 9ac300fc54a1..a6b83e89a416 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi @@ -499,6 +499,70 @@ function = "spi1"; }; }; + + i2c0_pins: i2c0 { + mux { + groups = "i2c0_sck", + "i2c0_sda"; + function = "i2c0"; + }; + }; + + i2c1_z_pins: i2c1_z { + mux { + groups = "i2c1_sck_z", + "i2c1_sda_z"; + function = "i2c1"; + }; + }; + + i2c1_x_pins: i2c1_x { + mux { + groups = "i2c1_sck_x", + "i2c1_sda_x"; + function = "i2c1"; + }; + }; + + i2c2_x_pins: i2c2_x { + mux { + groups = "i2c2_sck_x", + "i2c2_sda_x"; + function = "i2c2"; + }; + }; + + i2c2_a_pins: i2c2_a { + mux { + groups = "i2c2_sck_a", + "i2c2_sda_a"; + function = "i2c2"; + }; + }; + + i2c3_a6_pins: i2c3_a6 { + mux { + groups = "i2c3_sda_a6", + "i2c3_sck_a7"; + function = "i2c3"; + }; + }; + + i2c3_a12_pins: i2c3_a12 { + mux { + groups = "i2c3_sda_a12", + "i2c3_sck_a13"; + function = "i2c3"; + }; + }; + + i2c3_a19_pins: i2c3_a19 { + mux { + groups = "i2c3_sda_a19", + "i2c3_sck_a20"; + function = "i2c3"; + }; + }; }; }; -- cgit v1.3-14-g43fede From 7d6d8a2053e6c0c83168ccc876b18bc652b65efc Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Mon, 20 Nov 2017 22:54:15 +0800 Subject: ARM64: dts: meson-axg: enable I2C Master-1 for the audio speaker In the S400 board, The I2C master-1 is connecting to the audio speaker daughter board. Signed-off-by: Jian Hu Signed-off-by: Yixun Lan Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-axg-s400.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts index 447b98d30921..644514cd8e23 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts +++ b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts @@ -33,3 +33,9 @@ pinctrl-0 = <&remote_input_ao_pins>; pinctrl-names = "default"; }; + +&i2c1 { + status = "okay"; + pinctrl-0 = <&i2c1_z_pins>; + pinctrl-names = "default"; +}; -- cgit v1.3-14-g43fede From 777fa58db6224c931aad6e8a6bb6f361d0448a93 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 11 Jan 2018 11:04:11 +0800 Subject: ARM64: dts: meson-axg: add RMII pins for ethernet controller Comparing to RGMII interface, the RMII interface require few pins. So it's worth describing them here. Signed-off-by: Yixun Lan Acked-by: Neil Armstrong Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi index a6b83e89a416..5b5ef27ee7d4 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi @@ -306,6 +306,36 @@ gpio-ranges = <&pinctrl_periphs 0 0 86>; }; + eth_rmii_x_pins: eth-x-rmii { + mux { + groups = "eth_mdio_x", + "eth_mdc_x", + "eth_rgmii_rx_clk_x", + "eth_rx_dv_x", + "eth_rxd0_x", + "eth_rxd1_x", + "eth_txen_x", + "eth_txd0_x", + "eth_txd1_x"; + function = "eth"; + }; + }; + + eth_rmii_y_pins: eth-y-rmii { + mux { + groups = "eth_mdio_y", + "eth_mdc_y", + "eth_rgmii_rx_clk_y", + "eth_rx_dv_y", + "eth_rxd0_y", + "eth_rxd1_y", + "eth_txen_y", + "eth_txd0_y", + "eth_txd1_y"; + function = "eth"; + }; + }; + eth_rgmii_x_pins: eth-x-rgmii { mux { groups = "eth_mdio_x", -- cgit v1.3-14-g43fede From 58662130226c82b520c0a71b110f1248521f6797 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 11 Jan 2018 10:33:58 +0800 Subject: ARM64: dts: meson-axg: uart: drop legacy compatible name from EE UART When update the clock info for the UART controller in the EE domain, the driver explicitly require 'pclk' in order to work properly. With current logic of the code, the driver will go for the legacy clock probe routine if it find current compatible string match to 'amlogic,meson-uart', which result in not requesting the 'pclk' clock, thus break the driver in the end. Acked-by: Jerome Brunet Signed-off-by: Yixun Lan Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi index 5b5ef27ee7d4..b14841499ee2 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi @@ -212,17 +212,21 @@ }; uart_A: serial@24000 { - compatible = "amlogic,meson-gx-uart", "amlogic,meson-uart"; + compatible = "amlogic,meson-gx-uart"; reg = <0x0 0x24000 0x0 0x18>; interrupts = ; status = "disabled"; + clocks = <&xtal>, <&clkc CLKID_UART0>, <&xtal>; + clock-names = "xtal", "pclk", "baud"; }; uart_B: serial@23000 { - compatible = "amlogic,meson-gx-uart", "amlogic,meson-uart"; + compatible = "amlogic,meson-gx-uart"; reg = <0x0 0x23000 0x0 0x18>; interrupts = ; status = "disabled"; + clocks = <&xtal>, <&clkc CLKID_UART1>, <&xtal>; + clock-names = "xtal", "pclk", "baud"; }; }; -- cgit v1.3-14-g43fede From 4eae66a6928b5d5d35e0e4b9604aaa5b8d5538e1 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 11 Jan 2018 10:33:59 +0800 Subject: ARM64: dts: meson-axg: uart: Add the pinctrl info description Describe the pinctrl info for the UART controller which is found in the Meson-AXG SoCs. Signed-off-by: Yixun Lan [khilman: s/uart_ao_b_gpioz/uart_ao_b_z/ ] Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 96 ++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi index b14841499ee2..bce914b0d5bb 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi @@ -597,6 +597,70 @@ function = "i2c3"; }; }; + + uart_a_pins: uart_a { + mux { + groups = "uart_tx_a", + "uart_rx_a"; + function = "uart_a"; + }; + }; + + uart_a_cts_rts_pins: uart_a_cts_rts { + mux { + groups = "uart_cts_a", + "uart_rts_a"; + function = "uart_a"; + }; + }; + + uart_b_x_pins: uart_b_x { + mux { + groups = "uart_tx_b_x", + "uart_rx_b_x"; + function = "uart_b"; + }; + }; + + uart_b_x_cts_rts_pins: uart_b_x_cts_rts { + mux { + groups = "uart_cts_b_x", + "uart_rts_b_x"; + function = "uart_b"; + }; + }; + + uart_b_z_pins: uart_b_z { + mux { + groups = "uart_tx_b_z", + "uart_rx_b_z"; + function = "uart_b"; + }; + }; + + uart_b_z_cts_rts_pins: uart_b_z_cts_rts { + mux { + groups = "uart_cts_b_z", + "uart_rts_b_z"; + function = "uart_b"; + }; + }; + + uart_ao_b_z_pins: uart_ao_b_z { + mux { + groups = "uart_ao_tx_b_z", + "uart_ao_rx_b_z"; + function = "uart_ao_b_z"; + }; + }; + + uart_ao_b_z_cts_rts_pins: uart_ao_b_z_cts_rts { + mux { + groups = "uart_ao_cts_b_z", + "uart_ao_rts_b_z"; + function = "uart_ao_b_z"; + }; + }; }; }; @@ -647,6 +711,38 @@ function = "remote_input_ao"; }; }; + + uart_ao_a_pins: uart_ao_a { + mux { + groups = "uart_ao_tx_a", + "uart_ao_rx_a"; + function = "uart_ao_a"; + }; + }; + + uart_ao_a_cts_rts_pins: uart_ao_a_cts_rts { + mux { + groups = "uart_ao_cts_a", + "uart_ao_rts_a"; + function = "uart_ao_a"; + }; + }; + + uart_ao_b_pins: uart_ao_b { + mux { + groups = "uart_ao_tx_b", + "uart_ao_rx_b"; + function = "uart_ao_b"; + }; + }; + + uart_ao_b_cts_rts_pins: uart_ao_b_cts_rts { + mux { + groups = "uart_ao_cts_b", + "uart_ao_rts_b"; + function = "uart_ao_b"; + }; + }; }; pwm_AO_ab: pwm@7000 { -- cgit v1.3-14-g43fede From e496c415a311a28a7d42679d172db6d3737c2f28 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 11 Jan 2018 10:34:00 +0800 Subject: ARM64: dts: meson-axg: complete the pinctrl info for UART_AO_A Explictly request the pinctrl info for the UART_AO_A controller, otherwise we may need to rely on bootloader for the initialization. Acked-by: Jerome Brunet Signed-off-by: Yixun Lan Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-axg-s400.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts index 644514cd8e23..0149bec424f4 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts +++ b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts @@ -26,6 +26,8 @@ &uart_AO { status = "okay"; + pinctrl-0 = <&uart_ao_a_pins>; + pinctrl-names = "default"; }; &ir { -- cgit v1.3-14-g43fede From 3e5925c622d7debf429b36d47d8904af4d387fc6 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 11 Jan 2018 10:34:01 +0800 Subject: ARM64: dts: meson-axg: enable the UART_A controller The UART_A is connected to a BT module on the S400 board. Acked-by: Jerome Brunet Signed-off-by: Yixun Lan Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-axg-s400.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts index 0149bec424f4..913e63e718ff 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts +++ b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts @@ -14,6 +14,7 @@ aliases { serial0 = &uart_AO; + serial1 = &uart_A; }; }; @@ -24,6 +25,12 @@ pinctrl-names = "default"; }; +&uart_A { + status = "okay"; + pinctrl-0 = <&uart_a_pins>; + pinctrl-names = "default"; +}; + &uart_AO { status = "okay"; pinctrl-0 = <&uart_ao_a_pins>; -- cgit v1.3-14-g43fede From f7c36209c46c4d162202b65eed2e66962ad8c3c1 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 17 Jan 2018 11:55:43 +0100 Subject: ARM64: dts: meson s905x: accept MAC addr from u-boot environment With the adequate configuration settings, u-boot will loop through the list of aliases looking for "ethernetX". By adding an ethernet alias, u-boot can fixup the local-mac-address property in the kernel's device tree using a value held in its environment variable ethaddr. Tested-by: Jorge Ramirez-Ortiz Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts index 9671f1e3c74a..a011d51fec24 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts @@ -18,6 +18,7 @@ aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { -- cgit v1.3-14-g43fede From 059a58fcd53a78b898f26c26a1c37816ba306933 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 17 Jan 2018 11:56:27 +0100 Subject: ARM64: dts: meson: accept MAC addr from u-boot environment Extend configuring the MAC address from u-boot to all meson boards. I didn't test this changeset but having checked libretech's u-boot tree I believe it should just work. Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Kevin Hilman --- arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 1 + arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts | 1 + arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts | 1 + arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 1 + arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 1 + arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95.dtsi | 1 + arch/arm64/boot/dts/amlogic/meson-gxl-s905x-hwacom-amazetv.dts | 1 + arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts | 1 + arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts | 1 + arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi | 1 + arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts | 1 + arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts | 1 + 12 files changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi index aeb6d21a3bec..4b28a6e31175 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi @@ -48,6 +48,7 @@ / { aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts index 011e8e08e429..2ce7258a147b 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts @@ -52,6 +52,7 @@ aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts index 818954b1d57f..855dd9ae0716 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts @@ -54,6 +54,7 @@ aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts index ee4ada61c59c..73a030a5ecf3 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts @@ -53,6 +53,7 @@ aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi index 932158a778ef..c6f7b51e2ec8 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi @@ -47,6 +47,7 @@ / { aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95.dtsi index 1fe8e24cf675..383f9de734c1 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95.dtsi @@ -47,6 +47,7 @@ aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-hwacom-amazetv.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-hwacom-amazetv.dts index e82582574160..386fab4d1c8f 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-hwacom-amazetv.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-hwacom-amazetv.dts @@ -16,6 +16,7 @@ aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts index 71a6e1ce7ad5..94cddf59646a 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts @@ -29,6 +29,7 @@ aliases { serial2 = &uart_AO_B; + ethernet0 = ðmac; }; gpio-keys-polled { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts index 271f14279180..6b05c0e57724 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts @@ -52,6 +52,7 @@ aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi index 7005068346a0..9cde3d2eef97 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi @@ -17,6 +17,7 @@ aliases { serial0 = &uart_AO; serial1 = &uart_A; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts index e7a228f6cc7e..15cdd938f6a2 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts @@ -54,6 +54,7 @@ aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts index a5e9b955d5ed..67fadea68e07 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts @@ -58,6 +58,7 @@ aliases { serial0 = &uart_AO; + ethernet0 = ðmac; }; chosen { -- cgit v1.3-14-g43fede From 3a2ad7bd3151cc282f06d18948dfb7a0e1138fb2 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 21 Dec 2017 20:40:52 +0100 Subject: soc: amlogic: meson-gx-pwrc-vpu: don't print error message on probe deferral The error message may be misleading in case of probe deferral (happens on my Odroid-C2). Therefore don't print it in this case. Fixes: 75fcb5ca4b46 "soc: amlogic: add Meson GX VPU Domains driver" Signed-off-by: Heiner Kallweit Acked-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c index 2bdeebc48901..3adb2f2ecefd 100644 --- a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c @@ -184,7 +184,8 @@ static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) rstc = devm_reset_control_array_get(&pdev->dev, false, false); if (IS_ERR(rstc)) { - dev_err(&pdev->dev, "failed to get reset lines\n"); + if (PTR_ERR(rstc) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get reset lines\n"); return PTR_ERR(rstc); } -- cgit v1.3-14-g43fede From 87f88732d25e6175cb4faa8070658f604660d720 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 21 Dec 2017 20:41:02 +0100 Subject: soc: amlogic: meson-gx-pwrc-vpu: fix error on shutdown when domain is powered off When operating the system headless headless, the domain is never powered on, leaving the clocks disabled. The shutdown function then tries to disable the already disabled clocks, resulting in errors. Therefore call meson_gx_pwrc_vpu_power_off() only if domain is powered on. This patch fixes the described issue on my system (Odorid-C2). Fixes: 339cd0ea0822 "soc: amlogic: meson-gx-pwrc-vpu: fix power-off when powered by bootloader" Signed-off-by: Heiner Kallweit Reviewed-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c index 3adb2f2ecefd..6289965c42e9 100644 --- a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c @@ -225,7 +225,11 @@ static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) { - meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); + bool powered_off; + + powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); + if (!powered_off) + meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); } static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { -- cgit v1.3-14-g43fede From 82a759c91801d1f9851196d73516a504064e472c Mon Sep 17 00:00:00 2001 From: "weiyongjun (A)" Date: Wed, 10 Jan 2018 14:19:40 +0000 Subject: meson-mx-socinfo: Make local function meson_mx_socinfo_init() static Fixes the following sparse warnings: drivers/soc/amlogic/meson-mx-socinfo.c:107:12: warning: symbol 'meson_mx_socinfo_init' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-mx-socinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/amlogic/meson-mx-socinfo.c b/drivers/soc/amlogic/meson-mx-socinfo.c index 7bfff5ff22a2..78f0f1aeca57 100644 --- a/drivers/soc/amlogic/meson-mx-socinfo.c +++ b/drivers/soc/amlogic/meson-mx-socinfo.c @@ -104,7 +104,7 @@ static const struct of_device_id meson_mx_socinfo_analog_top_ids[] = { { /* sentinel */ } }; -int __init meson_mx_socinfo_init(void) +static int __init meson_mx_socinfo_init(void) { struct soc_device_attribute *soc_dev_attr; struct soc_device *soc_dev; -- cgit v1.3-14-g43fede From 01517dfc819f003855c1893d9382581cafe2877b Mon Sep 17 00:00:00 2001 From: "weiyongjun (A)" Date: Wed, 10 Jan 2018 14:19:48 +0000 Subject: meson-gx-socinfo: make local function meson_gx_socinfo_init static Fixes the following sparse warnings: drivers/soc/amlogic/meson-gx-socinfo.c:100:12: warning: symbol 'meson_gx_socinfo_init' was not declared. Should it be static? Signed-off-by: Wei Yongjun Acked-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index f2d8c3c53ea4..ea091f1f7dae 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -97,7 +97,7 @@ static const char *socinfo_to_soc_id(u32 socinfo) return "Unknown"; } -int __init meson_gx_socinfo_init(void) +static int __init meson_gx_socinfo_init(void) { struct soc_device_attribute *soc_dev_attr; struct soc_device *soc_dev; -- cgit v1.3-14-g43fede From 266c157de3b21b6da3e015085dda48cfa9f0f76d Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Wed, 3 Jan 2018 16:54:52 +0100 Subject: arm64: defconfig: enable MESON EFUSE Enable nvmem meson efuse driver as a module Signed-off-by: Jerome Brunet Signed-off-by: Kevin Hilman --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 634b373785c4..6e7a9b29822c 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -562,6 +562,7 @@ CONFIG_PHY_XGENE=y CONFIG_PHY_TEGRA_XUSB=y CONFIG_QCOM_L2_PMU=y CONFIG_QCOM_L3_PMU=y +CONFIG_MESON_EFUSE=m CONFIG_UNIPHIER_EFUSE=y CONFIG_TEE=y CONFIG_OPTEE=y -- cgit v1.3-14-g43fede From 1549333167edcbab5e8170ae45fbee99eeb4ddb1 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 17 Jan 2018 12:32:06 +0100 Subject: mtd: nand: mxc: reorder functions to avoid forward declarations We'll call copy_spare() and mxc_do_addr_cycle() from another place during the next patches, so move functions up to avoid forward declarations. Signed-off-by: Sascha Hauer Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 206 ++++++++++++++++++++++---------------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index f3be0b2a8869..6741af990dc4 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -252,6 +252,109 @@ static void memcpy16_toio(void __iomem *trg, const void *src, int size) __raw_writew(*s++, t++); } +/* + * The controller splits a page into data chunks of 512 bytes + partial oob. + * There are writesize / 512 such chunks, the size of the partial oob parts is + * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then + * contains additionally the byte lost by rounding (if any). + * This function handles the needed shuffling between host->data_buf (which + * holds a page in natural order, i.e. writesize bytes data + oobsize bytes + * spare) and the NFC buffer. + */ +static void copy_spare(struct mtd_info *mtd, bool bfrom) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(this); + u16 i, oob_chunk_size; + u16 num_chunks = mtd->writesize / 512; + + u8 *d = host->data_buf + mtd->writesize; + u8 __iomem *s = host->spare0; + u16 sparebuf_size = host->devtype_data->spare_len; + + /* size of oob chunk for all but possibly the last one */ + oob_chunk_size = (host->used_oobsize / num_chunks) & ~1; + + if (bfrom) { + for (i = 0; i < num_chunks - 1; i++) + memcpy16_fromio(d + i * oob_chunk_size, + s + i * sparebuf_size, + oob_chunk_size); + + /* the last chunk */ + memcpy16_fromio(d + i * oob_chunk_size, + s + i * sparebuf_size, + host->used_oobsize - i * oob_chunk_size); + } else { + for (i = 0; i < num_chunks - 1; i++) + memcpy16_toio(&s[i * sparebuf_size], + &d[i * oob_chunk_size], + oob_chunk_size); + + /* the last chunk */ + memcpy16_toio(&s[i * sparebuf_size], + &d[i * oob_chunk_size], + host->used_oobsize - i * oob_chunk_size); + } +} + +/* + * MXC NANDFC can only perform full page+spare or spare-only read/write. When + * the upper layers perform a read/write buf operation, the saved column address + * is used to index into the full page. So usually this function is called with + * column == 0 (unless no column cycle is needed indicated by column == -1) + */ +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + /* Write out column address, if necessary */ + if (column != -1) { + host->devtype_data->send_addr(host, column & 0xff, + page_addr == -1); + if (mtd->writesize > 512) + /* another col addr cycle for 2k page */ + host->devtype_data->send_addr(host, + (column >> 8) & 0xff, + false); + } + + /* Write out page address, if necessary */ + if (page_addr != -1) { + /* paddr_0 - p_addr_7 */ + host->devtype_data->send_addr(host, (page_addr & 0xff), false); + + if (mtd->writesize > 512) { + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); + } else + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); + } else { + if (nand_chip->options & NAND_ROW_ADDR_3) { + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); + } else + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); + } + } +} + static int check_int_v3(struct mxc_nand_host *host) { uint32_t tmp; @@ -772,109 +875,6 @@ static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip) writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); } -/* - * The controller splits a page into data chunks of 512 bytes + partial oob. - * There are writesize / 512 such chunks, the size of the partial oob parts is - * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then - * contains additionally the byte lost by rounding (if any). - * This function handles the needed shuffling between host->data_buf (which - * holds a page in natural order, i.e. writesize bytes data + oobsize bytes - * spare) and the NFC buffer. - */ -static void copy_spare(struct mtd_info *mtd, bool bfrom) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(this); - u16 i, oob_chunk_size; - u16 num_chunks = mtd->writesize / 512; - - u8 *d = host->data_buf + mtd->writesize; - u8 __iomem *s = host->spare0; - u16 sparebuf_size = host->devtype_data->spare_len; - - /* size of oob chunk for all but possibly the last one */ - oob_chunk_size = (host->used_oobsize / num_chunks) & ~1; - - if (bfrom) { - for (i = 0; i < num_chunks - 1; i++) - memcpy16_fromio(d + i * oob_chunk_size, - s + i * sparebuf_size, - oob_chunk_size); - - /* the last chunk */ - memcpy16_fromio(d + i * oob_chunk_size, - s + i * sparebuf_size, - host->used_oobsize - i * oob_chunk_size); - } else { - for (i = 0; i < num_chunks - 1; i++) - memcpy16_toio(&s[i * sparebuf_size], - &d[i * oob_chunk_size], - oob_chunk_size); - - /* the last chunk */ - memcpy16_toio(&s[i * sparebuf_size], - &d[i * oob_chunk_size], - host->used_oobsize - i * oob_chunk_size); - } -} - -/* - * MXC NANDFC can only perform full page+spare or spare-only read/write. When - * the upper layers perform a read/write buf operation, the saved column address - * is used to index into the full page. So usually this function is called with - * column == 0 (unless no column cycle is needed indicated by column == -1) - */ -static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - /* Write out column address, if necessary */ - if (column != -1) { - host->devtype_data->send_addr(host, column & 0xff, - page_addr == -1); - if (mtd->writesize > 512) - /* another col addr cycle for 2k page */ - host->devtype_data->send_addr(host, - (column >> 8) & 0xff, - false); - } - - /* Write out page address, if necessary */ - if (page_addr != -1) { - /* paddr_0 - p_addr_7 */ - host->devtype_data->send_addr(host, (page_addr & 0xff), false); - - if (mtd->writesize > 512) { - if (mtd->size >= 0x10000000) { - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, - false); - host->devtype_data->send_addr(host, - (page_addr >> 16) & 0xff, - true); - } else - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, true); - } else { - if (nand_chip->options & NAND_ROW_ADDR_3) { - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, - false); - host->devtype_data->send_addr(host, - (page_addr >> 16) & 0xff, - true); - } else - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, true); - } - } -} - #define MXC_V1_ECCBYTES 5 static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, -- cgit v1.3-14-g43fede From 040bd3f6a3aa51a7182bba4243428e9df8507146 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 17 Jan 2018 12:32:07 +0100 Subject: mtd: nand: mxc: Add function to control hardware ECC For proper raw read/write support need to be able to control the hardware ECC engine. Add a function to enable/disable it. Signed-off-by: Sascha Hauer Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 6741af990dc4..019c58579dbc 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -154,6 +154,7 @@ struct mxc_nand_devtype_data { u_char *read_ecc, u_char *calc_ecc); int (*setup_data_interface)(struct mtd_info *mtd, int csline, const struct nand_data_interface *conf); + void (*enable_hwecc)(struct nand_chip *chip, bool enable); /* * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked @@ -678,6 +679,42 @@ static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) return ret; } +static void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + uint16_t config1; + + if (chip->ecc.mode != NAND_ECC_HW) + return; + + config1 = readw(NFC_V1_V2_CONFIG1); + + if (enable) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + else + config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN; + + writew(config1, NFC_V1_V2_CONFIG1); +} + +static void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + uint32_t config2; + + if (chip->ecc.mode != NAND_ECC_HW) + return; + + config2 = readl(NFC_V3_CONFIG2); + + if (enable) + config2 |= NFC_V3_CONFIG2_ECC_EN; + else + config2 &= ~NFC_V3_CONFIG2_ECC_EN; + + writel(config2, NFC_V3_CONFIG2); +} + /* This functions is used by upper layer to checks if device is ready */ static int mxc_nand_dev_ready(struct mtd_info *mtd) { @@ -1408,6 +1445,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .ooblayout = &mxc_v1_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v1, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .irqpending_quirk = 1, .needs_ip = 0, .regs_offset = 0xe00, @@ -1431,6 +1469,7 @@ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { .ooblayout = &mxc_v1_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v1, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .irqpending_quirk = 0, .needs_ip = 0, .regs_offset = 0xe00, @@ -1456,6 +1495,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .select_chip = mxc_nand_select_chip_v2, .correct_data = mxc_nand_correct_data_v2_v3, .setup_data_interface = mxc_nand_v2_setup_data_interface, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .irqpending_quirk = 0, .needs_ip = 0, .regs_offset = 0x1e00, @@ -1480,6 +1520,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v2_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v3, .irqpending_quirk = 0, .needs_ip = 1, .regs_offset = 0, @@ -1505,6 +1546,7 @@ static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v2_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v3, .irqpending_quirk = 0, .needs_ip = 1, .regs_offset = 0, -- cgit v1.3-14-g43fede From 1af0b3147448954c50e555496b80d94a0800a5e4 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 17 Jan 2018 12:32:08 +0100 Subject: mtd: nand: mxc: Add buffer argument to copy_spare With following patches we will have to copy the spare data to/from other buffers, so add the buffer as argument to copy_spare(). Signed-off-by: Sascha Hauer Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 019c58579dbc..ab9cd45237d3 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -262,14 +262,14 @@ static void memcpy16_toio(void __iomem *trg, const void *src, int size) * holds a page in natural order, i.e. writesize bytes data + oobsize bytes * spare) and the NFC buffer. */ -static void copy_spare(struct mtd_info *mtd, bool bfrom) +static void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf) { struct nand_chip *this = mtd_to_nand(mtd); struct mxc_nand_host *host = nand_get_controller_data(this); u16 i, oob_chunk_size; u16 num_chunks = mtd->writesize / 512; - u8 *d = host->data_buf + mtd->writesize; + u8 *d = buf; u8 __iomem *s = host->spare0; u16 sparebuf_size = host->devtype_data->spare_len; @@ -1295,7 +1295,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, memcpy32_fromio(host->data_buf, host->main_area0, mtd->writesize); - copy_spare(mtd, true); + copy_spare(mtd, true, host->data_buf + mtd->writesize); break; case NAND_CMD_SEQIN: @@ -1314,7 +1314,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_PAGEPROG: memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); - copy_spare(mtd, false); + copy_spare(mtd, false, host->data_buf + mtd->writesize); host->devtype_data->send_page(mtd, NFC_INPUT); host->devtype_data->send_cmd(host, command, true); WARN_ONCE(column != -1 || page_addr != -1, -- cgit v1.3-14-g43fede From 67b87f66cae0bca0dd6602739383381c10b0c9c4 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 17 Jan 2018 12:32:09 +0100 Subject: mtd: nand: mxc: Fix failed/corrected values for v2/v3 controllers Currently nand_read_page_hwecc() from nand_base calls mxc_nand_correct_data_v2_v3() for each subpage, but in this function we return the corrected/failed results for the whole page instead of a single subpage. On a 2k page size Nand this leads to results which are 4 times too high. The whole ecc.calculate/ecc.correct mechanism used by nand_read_page_hwecc() is not suitable for devices which correct the data in hardware, so fix this by using a driver specific read_page function which does the right thing. Also add read_page_raw and read_oob For proper raw and oob read support. Signed-off-by: Sascha Hauer Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 86 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index ab9cd45237d3..48804320056f 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -140,6 +140,8 @@ struct mxc_nand_host; struct mxc_nand_devtype_data { void (*preset)(struct mtd_info *); + int (*read_page)(struct nand_chip *chip, void *buf, void *oob, bool ecc, + int page); void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); void (*send_addr)(struct mxc_nand_host *, uint16_t, int); void (*send_page)(struct mtd_info *, unsigned int); @@ -757,13 +759,35 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + return 0; +} + +static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf, + void *oob, bool ecc, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + unsigned int max_bitflips = 0; u32 ecc_stat, err; - int no_subpages = 1; - int ret = 0; + int no_subpages; u8 ecc_bit_mask, err_limit; + host->devtype_data->enable_hwecc(chip, ecc); + + host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); + mxc_do_addr_cycle(mtd, 0, page); + + if (mtd->writesize > 512) + host->devtype_data->send_cmd(host, + NAND_CMD_READSTART, true); + + host->devtype_data->send_page(mtd, NFC_OUTPUT); + + if (buf) + memcpy32_fromio(buf, host->main_area0, mtd->writesize); + if (oob) + copy_spare(mtd, true, oob); + ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; err_limit = (host->eccsize == 4) ? 0x4 : 0x8; @@ -774,17 +798,53 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, do { err = ecc_stat & ecc_bit_mask; if (err > err_limit) { - dev_dbg(host->dev, "UnCorrectable RS-ECC Error\n"); - return -EBADMSG; + mtd->ecc_stats.failed++; } else { - ret += err; + mtd->ecc_stats.corrected += err; + max_bitflips = max_t(unsigned int, max_bitflips, err); } + ecc_stat >>= 4; } while (--no_subpages); - dev_dbg(host->dev, "%d Symbol Correctable RS-ECC Error\n", ret); + return max_bitflips; +} - return ret; +static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + void *oob_buf; + + if (oob_required) + oob_buf = chip->oob_poi; + else + oob_buf = NULL; + + return host->devtype_data->read_page(chip, buf, oob_buf, 1, page); +} + +static int mxc_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + void *oob_buf; + + if (oob_required) + oob_buf = chip->oob_poi; + else + oob_buf = NULL; + + return host->devtype_data->read_page(chip, buf, oob_buf, 0, page); +} + +static int mxc_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + return host->devtype_data->read_page(chip, NULL, chip->oob_poi, 0, + page); } static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, @@ -1483,6 +1543,7 @@ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { /* v21: i.MX25, i.MX35 */ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .preset = preset_v2, + .read_page = mxc_nand_read_page_v2_v3, .send_cmd = send_cmd_v1_v2, .send_addr = send_addr_v1_v2, .send_page = send_page_v2, @@ -1509,6 +1570,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { /* v3.2a: i.MX51 */ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .preset = preset_v3, + .read_page = mxc_nand_read_page_v2_v3, .send_cmd = send_cmd_v3, .send_addr = send_addr_v3, .send_page = send_page_v3, @@ -1535,6 +1597,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { /* v3.2b: i.MX53 */ static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { .preset = preset_v3, + .read_page = mxc_nand_read_page_v2_v3, .send_cmd = send_cmd_v3, .send_addr = send_addr_v3, .send_page = send_page_v3, @@ -1793,6 +1856,11 @@ static int mxcnd_probe(struct platform_device *pdev) switch (this->ecc.mode) { case NAND_ECC_HW: + if (host->devtype_data->read_page) { + this->ecc.read_page = mxc_nand_read_page; + this->ecc.read_page_raw = mxc_nand_read_page_raw; + this->ecc.read_oob = mxc_nand_read_oob; + } this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = host->devtype_data->correct_data; -- cgit v1.3-14-g43fede From 5039fc9f2d6db603cba48c770e29251d661ce4e2 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 17 Jan 2018 12:32:10 +0100 Subject: mtd: nand: mxc: Fix failed/corrected values for v1 controllers The v1 controller code has several flaws: - We do not forward the number of corrected bitflips to the upper layers - For 2k page NAND chips only the status results from the fourth subpage read are evaluated, so ECC failures in the other subpages remain uncovered - When there are uncorrectable errors we have to increase the statistics counter, but still have to return successfully. Currently we return an error This patch fixes this by introducing a v1 specific read_page function. Signed-off-by: Sascha Hauer Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 77 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 48804320056f..49885afbcf43 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -738,22 +738,65 @@ static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + return 0; +} - /* - * 1-Bit errors are automatically corrected in HW. No need for - * additional correction. 2-Bit errors cannot be corrected by - * HW ECC, so we need to return failure - */ - uint16_t ecc_status = get_ecc_status_v1(host); +static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, + bool ecc, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + unsigned int bitflips_corrected = 0; + int no_subpages; + int i; + + host->devtype_data->enable_hwecc(chip, ecc); + + host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); + mxc_do_addr_cycle(mtd, 0, page); + + if (mtd->writesize > 512) + host->devtype_data->send_cmd(host, NAND_CMD_READSTART, true); + + no_subpages = mtd->writesize >> 9; + + for (i = 0; i < no_subpages; i++) { + uint16_t ecc_stats; - if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { - dev_dbg(host->dev, "HWECC uncorrectable 2-bit ECC error\n"); - return -EBADMSG; + /* NANDFC buffer 0 is used for page read/write */ + writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); + + writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); + + ecc_stats = get_ecc_status_v1(host); + + ecc_stats >>= 2; + + if (buf && ecc) { + switch (ecc_stats & 0x3) { + case 0: + default: + break; + case 1: + mtd->ecc_stats.corrected++; + bitflips_corrected = 1; + break; + case 2: + mtd->ecc_stats.failed++; + break; + } + } } - return 0; + if (buf) + memcpy32_fromio(buf, host->main_area0, mtd->writesize); + if (oob) + copy_spare(mtd, true, oob); + + return bitflips_corrected; } static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, @@ -1494,6 +1537,7 @@ static struct nand_bbt_descr bbt_mirror_descr = { /* v1 + irqpending_quirk: i.MX21 */ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .preset = preset_v1, + .read_page = mxc_nand_read_page_v1, .send_cmd = send_cmd_v1_v2, .send_addr = send_addr_v1_v2, .send_page = send_page_v1, @@ -1518,6 +1562,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { /* v1 + !irqpending_quirk: i.MX27, i.MX31 */ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { .preset = preset_v1, + .read_page = mxc_nand_read_page_v1, .send_cmd = send_cmd_v1_v2, .send_addr = send_addr_v1_v2, .send_page = send_page_v1, @@ -1856,11 +1901,9 @@ static int mxcnd_probe(struct platform_device *pdev) switch (this->ecc.mode) { case NAND_ECC_HW: - if (host->devtype_data->read_page) { - this->ecc.read_page = mxc_nand_read_page; - this->ecc.read_page_raw = mxc_nand_read_page_raw; - this->ecc.read_oob = mxc_nand_read_oob; - } + this->ecc.read_page = mxc_nand_read_page; + this->ecc.read_page_raw = mxc_nand_read_page_raw; + this->ecc.read_oob = mxc_nand_read_oob; this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = host->devtype_data->correct_data; -- cgit v1.3-14-g43fede From 6811c4642aa0b40e3199aa7fb86053be1ee10ad8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 17 Jan 2018 12:32:11 +0100 Subject: mtd: nand: mxc: Add own write_page Now that we have our own read_page function add a write_page function for consistency aswell. This can be a lot easier than the generic function since we do not have to iterate over subpages but can write the whole page at once. Also add write_page_raw and write_oob for proper raw and oob write support. Signed-off-by: Sascha Hauer Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 49885afbcf43..2811102b0469 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -890,6 +890,50 @@ static int mxc_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, page); } +static int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf, + bool ecc, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + + host->devtype_data->enable_hwecc(chip, ecc); + + host->devtype_data->send_cmd(host, NAND_CMD_SEQIN, false); + mxc_do_addr_cycle(mtd, 0, page); + + memcpy32_toio(host->main_area0, buf, mtd->writesize); + copy_spare(mtd, false, chip->oob_poi); + + host->devtype_data->send_page(mtd, NFC_INPUT); + host->devtype_data->send_cmd(host, NAND_CMD_PAGEPROG, true); + mxc_do_addr_cycle(mtd, 0, page); + + return 0; +} + +static int mxc_nand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + return mxc_nand_write_page(chip, buf, true, page); +} + +static int mxc_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + return mxc_nand_write_page(chip, buf, false, page); +} + +static int mxc_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + memset(host->data_buf, 0xff, mtd->writesize); + + return mxc_nand_write_page(chip, host->data_buf, false, page); +} + static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { @@ -1904,6 +1948,9 @@ static int mxcnd_probe(struct platform_device *pdev) this->ecc.read_page = mxc_nand_read_page; this->ecc.read_page_raw = mxc_nand_read_page_raw; this->ecc.read_oob = mxc_nand_read_oob; + this->ecc.write_page = mxc_nand_write_page_ecc; + this->ecc.write_page_raw = mxc_nand_write_page_raw; + this->ecc.write_oob = mxc_nand_write_oob; this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = host->devtype_data->correct_data; -- cgit v1.3-14-g43fede From cd8b4655332d45b1507a7cf1db025b869d0eb89a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 17 Jan 2018 12:32:12 +0100 Subject: mtd: nand: mxc: Drop now unnecessary functions Since we have our own read_page/write_page functions correct_data and calculate are no longer needed. Remove them. Signed-off-by: Sascha Hauer Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 2811102b0469..a7c4fdd1988e 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -152,8 +152,6 @@ struct mxc_nand_devtype_data { u32 (*get_ecc_status)(struct mxc_nand_host *); const struct mtd_ooblayout_ops *ooblayout; void (*select_chip)(struct mtd_info *mtd, int chip); - int (*correct_data)(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc); int (*setup_data_interface)(struct mtd_info *mtd, int csline, const struct nand_data_interface *conf); void (*enable_hwecc)(struct nand_chip *chip, bool enable); @@ -727,20 +725,6 @@ static int mxc_nand_dev_ready(struct mtd_info *mtd) return 1; } -static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ - /* - * If HW ECC is enabled, we turn it on during init. There is - * no need to enable again here. - */ -} - -static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - return 0; -} - static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, bool ecc, int page) { @@ -799,12 +783,6 @@ static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, return bitflips_corrected; } -static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - return 0; -} - static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf, void *oob, bool ecc, int page) { @@ -934,12 +912,6 @@ static int mxc_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, return mxc_nand_write_page(chip, host->data_buf, false, page); } -static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - return 0; -} - static u_char mxc_nand_read_byte(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd_to_nand(mtd); @@ -1592,7 +1564,6 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .get_ecc_status = get_ecc_status_v1, .ooblayout = &mxc_v1_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, - .correct_data = mxc_nand_correct_data_v1, .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .irqpending_quirk = 1, .needs_ip = 0, @@ -1617,7 +1588,6 @@ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { .get_ecc_status = get_ecc_status_v1, .ooblayout = &mxc_v1_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, - .correct_data = mxc_nand_correct_data_v1, .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .irqpending_quirk = 0, .needs_ip = 0, @@ -1643,7 +1613,6 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .get_ecc_status = get_ecc_status_v2, .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v2, - .correct_data = mxc_nand_correct_data_v2_v3, .setup_data_interface = mxc_nand_v2_setup_data_interface, .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, .irqpending_quirk = 0, @@ -1670,7 +1639,6 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .get_ecc_status = get_ecc_status_v3, .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, - .correct_data = mxc_nand_correct_data_v2_v3, .enable_hwecc = mxc_nand_enable_hwecc_v3, .irqpending_quirk = 0, .needs_ip = 1, @@ -1697,7 +1665,6 @@ static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { .get_ecc_status = get_ecc_status_v3, .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, - .correct_data = mxc_nand_correct_data_v2_v3, .enable_hwecc = mxc_nand_enable_hwecc_v3, .irqpending_quirk = 0, .needs_ip = 1, @@ -1951,9 +1918,6 @@ static int mxcnd_probe(struct platform_device *pdev) this->ecc.write_page = mxc_nand_write_page_ecc; this->ecc.write_page_raw = mxc_nand_write_page_raw; this->ecc.write_oob = mxc_nand_write_oob; - this->ecc.calculate = mxc_nand_calculate_ecc; - this->ecc.hwctl = mxc_nand_enable_hwecc; - this->ecc.correct = host->devtype_data->correct_data; break; case NAND_ECC_SOFT: -- cgit v1.3-14-g43fede From 68dc8e41fdffcc26db8185dd336ee1ff91872770 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 17 Jan 2018 12:32:13 +0100 Subject: mtd: nand: mxc: remove now unused code Since we now have our own read_page/write_page functions mxc_nand_command() will no longer be called with NAND_CMD_READ0, NAND_CMD_READOOB, NAND_CMD_SEQIN and NAND_CMD_PAGEPROG. Remove the code handling these commands. Signed-off-by: Sascha Hauer Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 51 --------------------------------------------- 1 file changed, 51 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index a7c4fdd1988e..87b5ee66e501 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1391,57 +1391,6 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, mxc_do_addr_cycle(mtd, column, page_addr); break; - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - if (command == NAND_CMD_READ0) - host->buf_start = column; - else - host->buf_start = column + mtd->writesize; - - command = NAND_CMD_READ0; /* only READ0 is valid */ - - host->devtype_data->send_cmd(host, command, false); - WARN_ONCE(column < 0, - "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", - command, column, page_addr); - mxc_do_addr_cycle(mtd, 0, page_addr); - - if (mtd->writesize > 512) - host->devtype_data->send_cmd(host, - NAND_CMD_READSTART, true); - - host->devtype_data->send_page(mtd, NFC_OUTPUT); - - memcpy32_fromio(host->data_buf, host->main_area0, - mtd->writesize); - copy_spare(mtd, true, host->data_buf + mtd->writesize); - break; - - case NAND_CMD_SEQIN: - if (column >= mtd->writesize) - /* call ourself to read a page */ - mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); - - host->buf_start = column; - - host->devtype_data->send_cmd(host, command, false); - WARN_ONCE(column < -1, - "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", - command, column, page_addr); - mxc_do_addr_cycle(mtd, 0, page_addr); - break; - - case NAND_CMD_PAGEPROG: - memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); - copy_spare(mtd, false, host->data_buf + mtd->writesize); - host->devtype_data->send_page(mtd, NFC_INPUT); - host->devtype_data->send_cmd(host, command, true); - WARN_ONCE(column != -1 || page_addr != -1, - "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", - command, column, page_addr); - mxc_do_addr_cycle(mtd, column, page_addr); - break; - case NAND_CMD_READID: host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); -- cgit v1.3-14-g43fede From 07ad5a7214841704e71540493a00041fd7f4e3af Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 17 Jan 2018 00:19:34 +0100 Subject: mtd: nand: add ->setup_data_interface() support for Marvell NFCv1 First generation of Marvell NAND flash controllers (eg. embedded in PXA boards) did not make use of the NAND core hook ->setup_data_interface() to setup controller timings. Add support for it. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/marvell_nand.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/marvell_nand.c b/drivers/mtd/nand/marvell_nand.c index 2196f2a233d6..cc21f9633edd 100644 --- a/drivers/mtd/nand/marvell_nand.c +++ b/drivers/mtd/nand/marvell_nand.c @@ -379,6 +379,8 @@ struct marvell_nfc_timings { * return the number of clock periods. */ #define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns)) +#define TO_CYCLES64(ps, period_ns) (DIV_ROUND_UP_ULL(div_u64(ps, 1000), \ + period_ns)) /** * NAND driver structure filled during the parsing of the ->exec_op() subop @@ -2236,8 +2238,20 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr, nfc_tmg.tRHW = TO_CYCLES(max_t(int, sdr->tRHW_min, sdr->tCCS_min), period_ns); - /* Use WAIT_MODE (wait for RB line) instead of only relying on delays */ - nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns); + /* + * NFCv2: Use WAIT_MODE (wait for RB line), do not rely only on delays. + * NFCv1: No WAIT_MODE, tR must be maximal. + */ + if (nfc->caps->is_nfcv2) { + nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns); + } else { + nfc_tmg.tR = TO_CYCLES64(sdr->tWB_max + sdr->tR_max, + period_ns); + if (nfc_tmg.tR + 3 > nfc_tmg.tCH) + nfc_tmg.tR = nfc_tmg.tCH - 3; + else + nfc_tmg.tR = 0; + } if (chipnr < 0) return 0; @@ -2249,18 +2263,24 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr, NDTR0_TWP(nfc_tmg.tWP) | NDTR0_TWH(nfc_tmg.tWH) | NDTR0_TCS(nfc_tmg.tCS) | - NDTR0_TCH(nfc_tmg.tCH) | - NDTR0_RD_CNT_DEL(read_delay) | - NDTR0_SELCNTR | - NDTR0_TADL(nfc_tmg.tADL); + NDTR0_TCH(nfc_tmg.tCH); marvell_nand->ndtr1 = NDTR1_TAR(nfc_tmg.tAR) | NDTR1_TWHR(nfc_tmg.tWHR) | - NDTR1_TRHW(nfc_tmg.tRHW) | - NDTR1_WAIT_MODE | NDTR1_TR(nfc_tmg.tR); + if (nfc->caps->is_nfcv2) { + marvell_nand->ndtr0 |= + NDTR0_RD_CNT_DEL(read_delay) | + NDTR0_SELCNTR | + NDTR0_TADL(nfc_tmg.tADL); + + marvell_nand->ndtr1 |= + NDTR1_TRHW(nfc_tmg.tRHW) | + NDTR1_WAIT_MODE; + } + return 0; } @@ -2395,8 +2415,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, chip->exec_op = marvell_nfc_exec_op; chip->select_chip = marvell_nfc_select_chip; - if (nfc->caps->is_nfcv2 && - !of_property_read_bool(np, "marvell,nand-keep-config")) + if (!of_property_read_bool(np, "marvell,nand-keep-config")) chip->setup_data_interface = marvell_nfc_setup_data_interface; mtd = nand_to_mtd(chip); -- cgit v1.3-14-g43fede From 532a1cf9a0f22f1a8699284e74aecf0bda3ffd29 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 9 Feb 2018 00:59:19 +0100 Subject: mtd: nand: vf610_nfc: remove unused function The function count_written_bits has been replaced by the generic nand_check_erased_ecc_chunk() function with commit 48c25cf44118 ("mtd: nand: vf610_nfc: use nand_check_erased_ecc_chunk() helper"). Remove the unused function. Signed-off-by: Stefan Agner Signed-off-by: Boris Brezillon --- drivers/mtd/nand/vf610_nfc.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 80d31a58e558..2fa61cbdbaf7 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -511,21 +511,6 @@ static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp); } -/* Count the number of 0's in buff up to max_bits */ -static inline int count_written_bits(uint8_t *buff, int size, int max_bits) -{ - uint32_t *buff32 = (uint32_t *)buff; - int k, written_bits = 0; - - for (k = 0; k < (size / 4); k++) { - written_bits += hweight32(~buff32[k]); - if (unlikely(written_bits > max_bits)) - break; - } - - return written_bits; -} - static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, uint8_t *oob, int page) { -- cgit v1.3-14-g43fede From f5a666bdd585b47bea60cec3cb5f4d9fbbc6e409 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 10 Feb 2018 01:28:34 +0300 Subject: mtd: nand: vf610: remove the unnecessary of_node_put() Calling of_node_put() in vf610_nfc_probe() is wrong because nothing in this code retains a reference to the DT node. Signed-off-by: Alexey Khoroshilov Reviewed-by: Stefan Agner Signed-off-by: Boris Brezillon --- drivers/mtd/nand/vf610_nfc.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 2fa61cbdbaf7..9c6d87d282c3 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -667,7 +667,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) dev_err(nfc->dev, "Only one NAND chip supported!\n"); err = -EINVAL; - goto error; + goto err_clk; } nand_set_flash_node(chip, child); @@ -697,7 +697,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd); if (err) { dev_err(nfc->dev, "Error requesting IRQ!\n"); - goto error; + goto err_clk; } vf610_nfc_preinit_controller(nfc); @@ -705,7 +705,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) /* first scan to find the device and get the page size */ err = nand_scan_ident(mtd, 1, NULL); if (err) - goto error; + goto err_clk; vf610_nfc_init_controller(nfc); @@ -717,20 +717,20 @@ static int vf610_nfc_probe(struct platform_device *pdev) if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { dev_err(nfc->dev, "Unsupported flash page size\n"); err = -ENXIO; - goto error; + goto err_clk; } if (chip->ecc.mode == NAND_ECC_HW) { if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { dev_err(nfc->dev, "Unsupported flash with hwecc\n"); err = -ENXIO; - goto error; + goto err_clk; } if (chip->ecc.size != mtd->writesize) { dev_err(nfc->dev, "Step size needs to be page size\n"); err = -ENXIO; - goto error; + goto err_clk; } /* Only 64 byte ECC layouts known */ @@ -750,7 +750,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) } else { dev_err(nfc->dev, "Unsupported ECC strength\n"); err = -ENXIO; - goto error; + goto err_clk; } chip->ecc.read_page = vf610_nfc_read_page; @@ -762,15 +762,13 @@ static int vf610_nfc_probe(struct platform_device *pdev) /* second phase scan */ err = nand_scan_tail(mtd); if (err) - goto error; + goto err_clk; platform_set_drvdata(pdev, mtd); /* Register device in MTD */ return mtd_device_register(mtd, NULL, 0); -error: - of_node_put(nand_get_flash_node(chip)); err_clk: clk_disable_unprepare(nfc->clk); return err; -- cgit v1.3-14-g43fede From 196644fd09ab7bf47040c0bdd1b48fc3c2bc08a6 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 10 Feb 2018 01:28:35 +0300 Subject: mtd: nand: vf610: improve readability of error label Use clearer error labels as Boris Brezillon suggested. Signed-off-by: Alexey Khoroshilov Reviewed-by: Stefan Agner Signed-off-by: Boris Brezillon --- drivers/mtd/nand/vf610_nfc.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 9c6d87d282c3..1939503b0960 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -667,7 +667,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) dev_err(nfc->dev, "Only one NAND chip supported!\n"); err = -EINVAL; - goto err_clk; + goto err_disable_clk; } nand_set_flash_node(chip, child); @@ -677,7 +677,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) if (!nand_get_flash_node(chip)) { dev_err(nfc->dev, "NAND chip sub-node missing!\n"); err = -ENODEV; - goto err_clk; + goto err_disable_clk; } chip->dev_ready = vf610_nfc_dev_ready; @@ -697,7 +697,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd); if (err) { dev_err(nfc->dev, "Error requesting IRQ!\n"); - goto err_clk; + goto err_disable_clk; } vf610_nfc_preinit_controller(nfc); @@ -705,7 +705,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) /* first scan to find the device and get the page size */ err = nand_scan_ident(mtd, 1, NULL); if (err) - goto err_clk; + goto err_disable_clk; vf610_nfc_init_controller(nfc); @@ -717,20 +717,20 @@ static int vf610_nfc_probe(struct platform_device *pdev) if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { dev_err(nfc->dev, "Unsupported flash page size\n"); err = -ENXIO; - goto err_clk; + goto err_disable_clk; } if (chip->ecc.mode == NAND_ECC_HW) { if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { dev_err(nfc->dev, "Unsupported flash with hwecc\n"); err = -ENXIO; - goto err_clk; + goto err_disable_clk; } if (chip->ecc.size != mtd->writesize) { dev_err(nfc->dev, "Step size needs to be page size\n"); err = -ENXIO; - goto err_clk; + goto err_disable_clk; } /* Only 64 byte ECC layouts known */ @@ -750,7 +750,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) } else { dev_err(nfc->dev, "Unsupported ECC strength\n"); err = -ENXIO; - goto err_clk; + goto err_disable_clk; } chip->ecc.read_page = vf610_nfc_read_page; @@ -762,14 +762,14 @@ static int vf610_nfc_probe(struct platform_device *pdev) /* second phase scan */ err = nand_scan_tail(mtd); if (err) - goto err_clk; + goto err_disable_clk; platform_set_drvdata(pdev, mtd); /* Register device in MTD */ return mtd_device_register(mtd, NULL, 0); -err_clk: +err_disable_clk: clk_disable_unprepare(nfc->clk); return err; } -- cgit v1.3-14-g43fede From 1b8c90985f59ccd8a1adb723b58b42628b36e668 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 10 Feb 2018 01:28:36 +0300 Subject: mtd: nand: vf610: check mtd_device_register() return code vf610_nfc_probe() misses error handling of mtd_device_register(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Reviewed-by: Stefan Agner Signed-off-by: Boris Brezillon --- drivers/mtd/nand/vf610_nfc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 1939503b0960..5d7a1f8f580f 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -767,8 +767,13 @@ static int vf610_nfc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mtd); /* Register device in MTD */ - return mtd_device_register(mtd, NULL, 0); + err = mtd_device_register(mtd, NULL, 0); + if (err) + goto err_cleanup_nand; + return 0; +err_cleanup_nand: + nand_cleanup(chip); err_disable_clk: clk_disable_unprepare(nfc->clk); return err; -- cgit v1.3-14-g43fede From 880f5b388252fedb26c70bb80ad1d7c8abbc0607 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 30 Oct 2017 23:11:14 -0700 Subject: remoteproc: Pass type of shutdown to subdev remove remoteproc instances can be stopped either by invoking shutdown or by an attempt to recover from a crash. For some subdev types it's expected to clean up gracefully during a shutdown, but are unable to do so during a crash - so pass this information to the subdev remove functions. Acked-By: Chris Lew Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_common.c | 6 +++--- drivers/remoteproc/remoteproc_core.c | 18 +++++++++--------- include/linux/remoteproc.h | 7 ++++--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index b7d53a9cf21f..9e47a147c131 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -42,7 +42,7 @@ static int glink_subdev_probe(struct rproc_subdev *subdev) return PTR_ERR_OR_ZERO(glink->edge); } -static void glink_subdev_remove(struct rproc_subdev *subdev) +static void glink_subdev_remove(struct rproc_subdev *subdev, bool crashed) { struct qcom_rproc_glink *glink = to_glink_subdev(subdev); @@ -132,7 +132,7 @@ static int smd_subdev_probe(struct rproc_subdev *subdev) return PTR_ERR_OR_ZERO(smd->edge); } -static void smd_subdev_remove(struct rproc_subdev *subdev) +static void smd_subdev_remove(struct rproc_subdev *subdev, bool crashed) { struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); @@ -201,7 +201,7 @@ static int ssr_notify_start(struct rproc_subdev *subdev) return 0; } -static void ssr_notify_stop(struct rproc_subdev *subdev) +static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed) { struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index fd257607a578..6d9c5832ce47 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -308,7 +308,7 @@ static int rproc_vdev_do_probe(struct rproc_subdev *subdev) return rproc_add_virtio_dev(rvdev, rvdev->id); } -static void rproc_vdev_do_remove(struct rproc_subdev *subdev) +static void rproc_vdev_do_remove(struct rproc_subdev *subdev, bool crashed) { struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); @@ -789,17 +789,17 @@ static int rproc_probe_subdevices(struct rproc *rproc) unroll_registration: list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) - subdev->remove(subdev); + subdev->remove(subdev, true); return ret; } -static void rproc_remove_subdevices(struct rproc *rproc) +static void rproc_remove_subdevices(struct rproc *rproc, bool crashed) { struct rproc_subdev *subdev; list_for_each_entry_reverse(subdev, &rproc->subdevs, node) - subdev->remove(subdev); + subdev->remove(subdev, crashed); } /** @@ -1009,13 +1009,13 @@ static int rproc_trigger_auto_boot(struct rproc *rproc) return ret; } -static int rproc_stop(struct rproc *rproc) +static int rproc_stop(struct rproc *rproc, bool crashed) { struct device *dev = &rproc->dev; int ret; /* remove any subdevices for the remote processor */ - rproc_remove_subdevices(rproc); + rproc_remove_subdevices(rproc, crashed); /* the installed resource table is no longer accessible */ rproc->table_ptr = rproc->cached_table; @@ -1163,7 +1163,7 @@ int rproc_trigger_recovery(struct rproc *rproc) if (ret) return ret; - ret = rproc_stop(rproc); + ret = rproc_stop(rproc, false); if (ret) goto unlock_mutex; @@ -1316,7 +1316,7 @@ void rproc_shutdown(struct rproc *rproc) if (!atomic_dec_and_test(&rproc->power)) goto out; - ret = rproc_stop(rproc); + ret = rproc_stop(rproc, true); if (ret) { atomic_inc(&rproc->power); goto out; @@ -1663,7 +1663,7 @@ EXPORT_SYMBOL(rproc_del); void rproc_add_subdev(struct rproc *rproc, struct rproc_subdev *subdev, int (*probe)(struct rproc_subdev *subdev), - void (*remove)(struct rproc_subdev *subdev)) + void (*remove)(struct rproc_subdev *subdev, bool crashed)) { subdev->probe = probe; subdev->remove = remove; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index f16864acedad..d09a9c7af109 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -478,13 +478,14 @@ struct rproc { * struct rproc_subdev - subdevice tied to a remoteproc * @node: list node related to the rproc subdevs list * @probe: probe function, called as the rproc is started - * @remove: remove function, called as the rproc is stopped + * @remove: remove function, called as the rproc is being stopped, the @crashed + * parameter indicates if this originates from the a recovery */ struct rproc_subdev { struct list_head node; int (*probe)(struct rproc_subdev *subdev); - void (*remove)(struct rproc_subdev *subdev); + void (*remove)(struct rproc_subdev *subdev, bool crashed); }; /* we currently support only two vrings per rvdev */ @@ -568,7 +569,7 @@ static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev) void rproc_add_subdev(struct rproc *rproc, struct rproc_subdev *subdev, int (*probe)(struct rproc_subdev *subdev), - void (*remove)(struct rproc_subdev *subdev)); + void (*remove)(struct rproc_subdev *subdev, bool graceful)); void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev); -- cgit v1.3-14-g43fede From 1fb82ee806d170b92315f424eac9b5b34b9ead64 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sun, 27 Aug 2017 21:51:38 -0700 Subject: remoteproc: qcom: Introduce sysmon The sysmon client communicates either via a dedicated SMD/GLINK channel or via QMI encoded messages over IPCROUTER with remote processors in order to perform graceful shutdown and inform about other remote processors shutting down. Acked-By: Chris Lew Signed-off-by: Bjorn Andersson --- drivers/remoteproc/Kconfig | 17 ++ drivers/remoteproc/Makefile | 1 + drivers/remoteproc/qcom_adsp_pil.c | 12 + drivers/remoteproc/qcom_common.h | 21 ++ drivers/remoteproc/qcom_q6v5_pil.c | 3 + drivers/remoteproc/qcom_sysmon.c | 579 +++++++++++++++++++++++++++++++++++++ drivers/remoteproc/qcom_wcnss.c | 4 + 7 files changed, 637 insertions(+) create mode 100644 drivers/remoteproc/qcom_sysmon.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 3e4bca77188d..a3658aa01838 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -91,6 +91,7 @@ config QCOM_ADSP_PIL depends on QCOM_SMEM depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n + depends on QCOM_SYSMON || QCOM_SYSMON=n select MFD_SYSCON select QCOM_MDT_LOADER select QCOM_RPROC_COMMON @@ -108,6 +109,7 @@ config QCOM_Q6V5_PIL depends on QCOM_SMEM depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n + depends on QCOM_SYSMON || QCOM_SYSMON=n select MFD_SYSCON select QCOM_RPROC_COMMON select QCOM_SCM @@ -115,12 +117,27 @@ config QCOM_Q6V5_PIL Say y here to support the Qualcomm Peripherial Image Loader for the Hexagon V5 based remote processors. +config QCOM_SYSMON + tristate "Qualcomm sysmon driver" + depends on RPMSG + depends on ARCH_QCOM + select QCOM_QMI_HELPERS + help + The sysmon driver implements a sysmon QMI client and a handler for + the sys_mon SMD and GLINK channel, which are used for graceful + shutdown, retrieving failure information and propagating information + about other subsystems being shut down. + + Say y here if your system runs firmware on any other subsystems, e.g. + modem or DSP. + config QCOM_WCNSS_PIL tristate "Qualcomm WCNSS Peripheral Image Loader" depends on OF && ARCH_QCOM depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n depends on QCOM_SMEM + depends on QCOM_SYSMON || QCOM_SYSMON=n select QCOM_MDT_LOADER select QCOM_RPROC_COMMON select QCOM_SCM diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 6e16450ce11f..02627ede8d4a 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o +obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o qcom_wcnss_pil-y += qcom_wcnss.o qcom_wcnss_pil-y += qcom_wcnss_iris.o diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index ac8f9a77b821..89a86ce07f99 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c @@ -38,7 +38,10 @@ struct adsp_data { const char *firmware_name; int pas_id; bool has_aggre2_clk; + const char *ssr_name; + const char *sysmon_name; + int ssctl_id; }; struct qcom_adsp { @@ -75,6 +78,7 @@ struct qcom_adsp { struct qcom_rproc_glink glink_subdev; struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_ssr ssr_subdev; + struct qcom_sysmon *sysmon; }; static int adsp_load(struct rproc *rproc, const struct firmware *fw) @@ -398,6 +402,9 @@ static int adsp_probe(struct platform_device *pdev) qcom_add_glink_subdev(rproc, &adsp->glink_subdev); qcom_add_smd_subdev(rproc, &adsp->smd_subdev); qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); + adsp->sysmon = qcom_add_sysmon_subdev(rproc, + desc->sysmon_name, + desc->ssctl_id); ret = rproc_add(rproc); if (ret) @@ -419,6 +426,7 @@ static int adsp_remove(struct platform_device *pdev) rproc_del(adsp->rproc); qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); + qcom_remove_sysmon_subdev(adsp->sysmon); qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); rproc_free(adsp->rproc); @@ -432,6 +440,8 @@ static const struct adsp_data adsp_resource_init = { .pas_id = 1, .has_aggre2_clk = false, .ssr_name = "lpass", + .sysmon_name = "adsp", + .ssctl_id = 0x14, }; static const struct adsp_data slpi_resource_init = { @@ -440,6 +450,8 @@ static const struct adsp_data slpi_resource_init = { .pas_id = 12, .has_aggre2_clk = true, .ssr_name = "dsps", + .sysmon_name = "slpi", + .ssctl_id = 0x16, }; static const struct of_device_id adsp_of_match[] = { diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h index 7e614520fb69..58de71e4781c 100644 --- a/drivers/remoteproc/qcom_common.h +++ b/drivers/remoteproc/qcom_common.h @@ -4,6 +4,9 @@ #include #include "remoteproc_internal.h" +#include + +struct qcom_sysmon; struct qcom_rproc_glink { struct rproc_subdev subdev; @@ -39,4 +42,22 @@ void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, const char *ssr_name); void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr); +#if IS_ENABLED(CONFIG_QCOM_SYSMON) +struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, + const char *name, + int ssctl_instance); +void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon); +#else +static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, + const char *name, + int ssctl_instance) +{ + return NULL; +} + +static inline void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon) +{ +} +#endif + #endif diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 7293d45c2671..8e70a627e0bb 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -168,6 +168,7 @@ struct q6v5 { struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_ssr ssr_subdev; + struct qcom_sysmon *sysmon; bool need_mem_protection; int mpss_perm; int mba_perm; @@ -1209,6 +1210,7 @@ static int q6v5_probe(struct platform_device *pdev) qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); + qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); ret = rproc_add(rproc); if (ret) @@ -1228,6 +1230,7 @@ static int q6v5_remove(struct platform_device *pdev) rproc_del(qproc->rproc); + qcom_remove_sysmon_subdev(qproc->sysmon); qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); rproc_free(qproc->rproc); diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c new file mode 100644 index 000000000000..f085545d7da5 --- /dev/null +++ b/drivers/remoteproc/qcom_sysmon.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, Linaro Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_common.h" + +static BLOCKING_NOTIFIER_HEAD(sysmon_notifiers); + +struct qcom_sysmon { + struct rproc_subdev subdev; + struct rproc *rproc; + + struct list_head node; + + const char *name; + + int ssctl_version; + int ssctl_instance; + + struct notifier_block nb; + + struct device *dev; + + struct rpmsg_endpoint *ept; + struct completion comp; + struct mutex lock; + + bool ssr_ack; + + struct qmi_handle qmi; + struct sockaddr_qrtr ssctl; +}; + +static DEFINE_MUTEX(sysmon_lock); +static LIST_HEAD(sysmon_list); + +/** + * sysmon_send_event() - send notification of other remote's SSR event + * @sysmon: sysmon context + * @name: other remote's name + */ +static void sysmon_send_event(struct qcom_sysmon *sysmon, const char *name) +{ + char req[50]; + int len; + int ret; + + len = snprintf(req, sizeof(req), "ssr:%s:before_shutdown", name); + if (len >= sizeof(req)) + return; + + mutex_lock(&sysmon->lock); + reinit_completion(&sysmon->comp); + sysmon->ssr_ack = false; + + ret = rpmsg_send(sysmon->ept, req, len); + if (ret < 0) { + dev_err(sysmon->dev, "failed to send sysmon event\n"); + goto out_unlock; + } + + ret = wait_for_completion_timeout(&sysmon->comp, + msecs_to_jiffies(5000)); + if (!ret) { + dev_err(sysmon->dev, "timeout waiting for sysmon ack\n"); + goto out_unlock; + } + + if (!sysmon->ssr_ack) + dev_err(sysmon->dev, "unexpected response to sysmon event\n"); + +out_unlock: + mutex_unlock(&sysmon->lock); +} + +/** + * sysmon_request_shutdown() - request graceful shutdown of remote + * @sysmon: sysmon context + */ +static void sysmon_request_shutdown(struct qcom_sysmon *sysmon) +{ + char *req = "ssr:shutdown"; + int ret; + + mutex_lock(&sysmon->lock); + reinit_completion(&sysmon->comp); + sysmon->ssr_ack = false; + + ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1); + if (ret < 0) { + dev_err(sysmon->dev, "send sysmon shutdown request failed\n"); + goto out_unlock; + } + + ret = wait_for_completion_timeout(&sysmon->comp, + msecs_to_jiffies(5000)); + if (!ret) { + dev_err(sysmon->dev, "timeout waiting for sysmon ack\n"); + goto out_unlock; + } + + if (!sysmon->ssr_ack) + dev_err(sysmon->dev, + "unexpected response to sysmon shutdown request\n"); + +out_unlock: + mutex_unlock(&sysmon->lock); +} + +static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, + void *priv, u32 addr) +{ + struct qcom_sysmon *sysmon = priv; + const char *ssr_ack = "ssr:ack"; + const int ssr_ack_len = strlen(ssr_ack) + 1; + + if (!sysmon) + return -EINVAL; + + if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len)) + sysmon->ssr_ack = true; + + complete(&sysmon->comp); + + return 0; +} + +#define SSCTL_SHUTDOWN_REQ 0x21 +#define SSCTL_SUBSYS_EVENT_REQ 0x23 + +#define SSCTL_MAX_MSG_LEN 7 + +#define SSCTL_SUBSYS_NAME_LENGTH 15 + +enum { + SSCTL_SSR_EVENT_BEFORE_POWERUP, + SSCTL_SSR_EVENT_AFTER_POWERUP, + SSCTL_SSR_EVENT_BEFORE_SHUTDOWN, + SSCTL_SSR_EVENT_AFTER_SHUTDOWN, +}; + +enum { + SSCTL_SSR_EVENT_FORCED, + SSCTL_SSR_EVENT_GRACEFUL, +}; + +struct ssctl_shutdown_resp { + struct qmi_response_type_v01 resp; +}; + +static struct qmi_elem_info ssctl_shutdown_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ssctl_shutdown_resp, resp), + .ei_array = qmi_response_type_v01_ei, + }, + {} +}; + +struct ssctl_subsys_event_req { + u8 subsys_name_len; + char subsys_name[SSCTL_SUBSYS_NAME_LENGTH]; + u32 event; + u8 evt_driven_valid; + u32 evt_driven; +}; + +static struct qmi_elem_info ssctl_subsys_event_req_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ssctl_subsys_event_req, + subsys_name_len), + .ei_array = NULL, + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = SSCTL_SUBSYS_NAME_LENGTH, + .elem_size = sizeof(char), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct ssctl_subsys_event_req, + subsys_name), + .ei_array = NULL, + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ssctl_subsys_event_req, + event), + .ei_array = NULL, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ssctl_subsys_event_req, + evt_driven_valid), + .ei_array = NULL, + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct ssctl_subsys_event_req, + evt_driven), + .ei_array = NULL, + }, + {} +}; + +struct ssctl_subsys_event_resp { + struct qmi_response_type_v01 resp; +}; + +static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct ssctl_subsys_event_resp, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + {} +}; + +/** + * ssctl_request_shutdown() - request shutdown via SSCTL QMI service + * @sysmon: sysmon context + */ +static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) +{ + struct ssctl_shutdown_resp resp; + struct qmi_txn txn; + int ret; + + ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); + if (ret < 0) { + dev_err(sysmon->dev, "failed to allocate QMI txn\n"); + return; + } + + ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, + SSCTL_SHUTDOWN_REQ, 0, NULL, NULL); + if (ret < 0) { + dev_err(sysmon->dev, "failed to send shutdown request\n"); + qmi_txn_cancel(&txn); + return; + } + + ret = qmi_txn_wait(&txn, 5 * HZ); + if (ret < 0) + dev_err(sysmon->dev, "failed receiving QMI response\n"); + else if (resp.resp.result) + dev_err(sysmon->dev, "shutdown request failed\n"); + else + dev_dbg(sysmon->dev, "shutdown request completed\n"); +} + +/** + * ssctl_send_event() - send notification of other remote's SSR event + * @sysmon: sysmon context + * @name: other remote's name + */ +static void ssctl_send_event(struct qcom_sysmon *sysmon, const char *name) +{ + struct ssctl_subsys_event_resp resp; + struct ssctl_subsys_event_req req; + struct qmi_txn txn; + int ret; + + memset(&resp, 0, sizeof(resp)); + ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp); + if (ret < 0) { + dev_err(sysmon->dev, "failed to allocate QMI txn\n"); + return; + } + + memset(&req, 0, sizeof(req)); + strlcpy(req.subsys_name, name, sizeof(req.subsys_name)); + req.subsys_name_len = strlen(req.subsys_name); + req.event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN; + req.evt_driven_valid = true; + req.evt_driven = SSCTL_SSR_EVENT_FORCED; + + ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, + SSCTL_SUBSYS_EVENT_REQ, 40, + ssctl_subsys_event_req_ei, &req); + if (ret < 0) { + dev_err(sysmon->dev, "failed to send shutdown request\n"); + qmi_txn_cancel(&txn); + return; + } + + ret = qmi_txn_wait(&txn, 5 * HZ); + if (ret < 0) + dev_err(sysmon->dev, "failed receiving QMI response\n"); + else if (resp.resp.result) + dev_err(sysmon->dev, "ssr event send failed\n"); + else + dev_dbg(sysmon->dev, "ssr event send completed\n"); +} + +/** + * ssctl_new_server() - QMI callback indicating a new service + * @qmi: QMI handle + * @svc: service information + * + * Return: 0 if we're interested in this service, -EINVAL otherwise. + */ +static int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc) +{ + struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); + + switch (svc->version) { + case 1: + if (svc->instance != 0) + return -EINVAL; + if (strcmp(sysmon->name, "modem")) + return -EINVAL; + break; + case 2: + if (svc->instance != sysmon->ssctl_instance) + return -EINVAL; + break; + default: + return -EINVAL; + }; + + sysmon->ssctl_version = svc->version; + + sysmon->ssctl.sq_family = AF_QIPCRTR; + sysmon->ssctl.sq_node = svc->node; + sysmon->ssctl.sq_port = svc->port; + + svc->priv = sysmon; + + return 0; +} + +/** + * ssctl_del_server() - QMI callback indicating that @svc is removed + * @qmi: QMI handle + * @svc: service information + */ +static void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc) +{ + struct qcom_sysmon *sysmon = svc->priv; + + sysmon->ssctl_version = 0; +} + +static const struct qmi_ops ssctl_ops = { + .new_server = ssctl_new_server, + .del_server = ssctl_del_server, +}; + +static int sysmon_start(struct rproc_subdev *subdev) +{ + return 0; +} + +static void sysmon_stop(struct rproc_subdev *subdev, bool crashed) +{ + struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); + + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon->name); + + /* Don't request graceful shutdown if we've crashed */ + if (crashed) + return; + + if (sysmon->ssctl_version) + ssctl_request_shutdown(sysmon); + else if (sysmon->ept) + sysmon_request_shutdown(sysmon); +} + +/** + * sysmon_notify() - notify sysmon target of another's SSR + * @nb: notifier_block associated with sysmon instance + * @event: unused + * @data: SSR identifier of the remote that is going down + */ +static int sysmon_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb); + struct rproc *rproc = sysmon->rproc; + const char *ssr_name = data; + + /* Skip non-running rprocs and the originating instance */ + if (rproc->state != RPROC_RUNNING || !strcmp(data, sysmon->name)) { + dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name); + return NOTIFY_DONE; + } + + /* Only SSCTL version 2 supports SSR events */ + if (sysmon->ssctl_version == 2) + ssctl_send_event(sysmon, ssr_name); + else if (sysmon->ept) + sysmon_send_event(sysmon, ssr_name); + + return NOTIFY_DONE; +} + +/** + * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc + * @rproc: rproc context to associate the subdev with + * @name: name of this subdev, to use in SSR + * @ssctl_instance: instance id of the ssctl QMI service + * + * Return: A new qcom_sysmon object, or NULL on failure + */ +struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, + const char *name, + int ssctl_instance) +{ + struct qcom_sysmon *sysmon; + int ret; + + sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); + if (!sysmon) + return NULL; + + sysmon->dev = rproc->dev.parent; + sysmon->rproc = rproc; + + sysmon->name = name; + sysmon->ssctl_instance = ssctl_instance; + + init_completion(&sysmon->comp); + mutex_init(&sysmon->lock); + + ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL); + if (ret < 0) { + dev_err(sysmon->dev, "failed to initialize qmi handle\n"); + kfree(sysmon); + return NULL; + } + + qmi_add_lookup(&sysmon->qmi, 43, 0, 0); + + rproc_add_subdev(rproc, &sysmon->subdev, sysmon_start, sysmon_stop); + + sysmon->nb.notifier_call = sysmon_notify; + blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb); + + mutex_lock(&sysmon_lock); + list_add(&sysmon->node, &sysmon_list); + mutex_unlock(&sysmon_lock); + + return sysmon; +} +EXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev); + +/** + * qcom_remove_sysmon_subdev() - release a qcom_sysmon + * @sysmon: sysmon context, as retrieved by qcom_add_sysmon_subdev() + */ +void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon) +{ + if (!sysmon) + return; + + mutex_lock(&sysmon_lock); + list_del(&sysmon->node); + mutex_unlock(&sysmon_lock); + + blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb); + + rproc_remove_subdev(sysmon->rproc, &sysmon->subdev); + + qmi_handle_release(&sysmon->qmi); + + kfree(sysmon); +} +EXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev); + +/** + * sysmon_probe() - probe sys_mon channel + * @rpdev: rpmsg device handle + * + * Find the sysmon context associated with the ancestor remoteproc and assign + * this rpmsg device with said sysmon context. + * + * Return: 0 on success, negative errno on failure. + */ +static int sysmon_probe(struct rpmsg_device *rpdev) +{ + struct qcom_sysmon *sysmon; + struct rproc *rproc; + + rproc = rproc_get_by_child(&rpdev->dev); + if (!rproc) { + dev_err(&rpdev->dev, "sysmon device not child of rproc\n"); + return -EINVAL; + } + + mutex_lock(&sysmon_lock); + list_for_each_entry(sysmon, &sysmon_list, node) { + if (sysmon->rproc == rproc) + goto found; + } + mutex_unlock(&sysmon_lock); + + dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n"); + + return -EINVAL; + +found: + mutex_unlock(&sysmon_lock); + + rpdev->ept->priv = sysmon; + sysmon->ept = rpdev->ept; + + return 0; +} + +/** + * sysmon_remove() - sys_mon channel remove handler + * @rpdev: rpmsg device handle + * + * Disassociate the rpmsg device with the sysmon instance. + */ +static void sysmon_remove(struct rpmsg_device *rpdev) +{ + struct qcom_sysmon *sysmon = rpdev->ept->priv; + + sysmon->ept = NULL; +} + +static const struct rpmsg_device_id sysmon_match[] = { + { "sys_mon" }, + {} +}; + +static struct rpmsg_driver sysmon_driver = { + .probe = sysmon_probe, + .remove = sysmon_remove, + .callback = sysmon_callback, + .id_table = sysmon_match, + .drv = { + .name = "qcom_sysmon", + }, +}; + +module_rpmsg_driver(sysmon_driver); + +MODULE_DESCRIPTION("Qualcomm sysmon driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 32a3a53589dc..b0e07e9f42d5 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -40,6 +40,7 @@ #define WCNSS_CRASH_REASON_SMEM 422 #define WCNSS_FIRMWARE_NAME "wcnss.mdt" #define WCNSS_PAS_ID 6 +#define WCNSS_SSCTL_ID 0x13 #define WCNSS_SPARE_NVBIN_DLND BIT(25) @@ -98,6 +99,7 @@ struct qcom_wcnss { size_t mem_size; struct qcom_rproc_subdev smd_subdev; + struct qcom_sysmon *sysmon; }; static const struct wcnss_data riva_data = { @@ -550,6 +552,7 @@ static int wcnss_probe(struct platform_device *pdev) } qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); + wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID); ret = rproc_add(rproc); if (ret) @@ -572,6 +575,7 @@ static int wcnss_remove(struct platform_device *pdev) qcom_smem_state_put(wcnss->state); rproc_del(wcnss->rproc); + qcom_remove_sysmon_subdev(wcnss->sysmon); qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev); rproc_free(wcnss->rproc); -- cgit v1.3-14-g43fede From 842891be96bd2b90f0e7d44192d45f61bab17926 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 26 Jun 2017 09:02:28 -0700 Subject: samples: Introduce Qualcomm QMI sample client Introduce a sample driver that register for server notifications and spawn clients for each available test service (service 15). The spawned clients implements the interface for encoding "ping" and "data" requests and decode the responses from the remote. Acked-By: Chris Lew Signed-off-by: Bjorn Andersson --- samples/Kconfig | 9 + samples/Makefile | 2 +- samples/qmi/Makefile | 1 + samples/qmi/qmi_sample_client.c | 622 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 633 insertions(+), 1 deletion(-) create mode 100644 samples/qmi/Makefile create mode 100644 samples/qmi/qmi_sample_client.c diff --git a/samples/Kconfig b/samples/Kconfig index c332a3b9de05..4cb8af2f810f 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -62,6 +62,15 @@ config SAMPLE_KDB Build an example of how to dynamically add the hello command to the kdb shell. +config SAMPLE_QMI_CLIENT + tristate "Build qmi client sample -- loadable modules only" + depends on m + depends on ARCH_QCOM + select QCOM_QMI_HELPERS + help + Build an QMI client sample driver, which demonstrates how to + communicate with a remote QRTR service, using QMI encoded messages. + config SAMPLE_RPMSG_CLIENT tristate "Build rpmsg client sample -- loadable modules only" depends on RPMSG && m diff --git a/samples/Makefile b/samples/Makefile index db54e766ddb1..a30833a2a19e 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \ configfs/ connector/ v4l/ trace_printk/ blackfin/ \ - vfio-mdev/ statx/ + vfio-mdev/ statx/ qmi/ diff --git a/samples/qmi/Makefile b/samples/qmi/Makefile new file mode 100644 index 000000000000..2b111d2769df --- /dev/null +++ b/samples/qmi/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SAMPLE_QMI_CLIENT) += qmi_sample_client.o diff --git a/samples/qmi/qmi_sample_client.c b/samples/qmi/qmi_sample_client.c new file mode 100644 index 000000000000..c9e7276c3d83 --- /dev/null +++ b/samples/qmi/qmi_sample_client.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sample in-kernel QMI client driver + * + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PING_REQ1_TLV_TYPE 0x1 +#define PING_RESP1_TLV_TYPE 0x2 +#define PING_OPT1_TLV_TYPE 0x10 +#define PING_OPT2_TLV_TYPE 0x11 + +#define DATA_REQ1_TLV_TYPE 0x1 +#define DATA_RESP1_TLV_TYPE 0x2 +#define DATA_OPT1_TLV_TYPE 0x10 +#define DATA_OPT2_TLV_TYPE 0x11 + +#define TEST_MED_DATA_SIZE_V01 8192 +#define TEST_MAX_NAME_SIZE_V01 255 + +#define TEST_PING_REQ_MSG_ID_V01 0x20 +#define TEST_DATA_REQ_MSG_ID_V01 0x21 + +#define TEST_PING_REQ_MAX_MSG_LEN_V01 266 +#define TEST_DATA_REQ_MAX_MSG_LEN_V01 8456 + +struct test_name_type_v01 { + u32 name_len; + char name[TEST_MAX_NAME_SIZE_V01]; +}; + +static struct qmi_elem_info test_name_type_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof(struct test_name_type_v01, + name_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = TEST_MAX_NAME_SIZE_V01, + .elem_size = sizeof(char), + .array_type = VAR_LEN_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof(struct test_name_type_v01, + name), + }, + {} +}; + +struct test_ping_req_msg_v01 { + char ping[4]; + + u8 client_name_valid; + struct test_name_type_v01 client_name; +}; + +static struct qmi_elem_info test_ping_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 4, + .elem_size = sizeof(char), + .array_type = STATIC_ARRAY, + .tlv_type = PING_REQ1_TLV_TYPE, + .offset = offsetof(struct test_ping_req_msg_v01, + ping), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = PING_OPT1_TLV_TYPE, + .offset = offsetof(struct test_ping_req_msg_v01, + client_name_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct test_name_type_v01), + .array_type = NO_ARRAY, + .tlv_type = PING_OPT1_TLV_TYPE, + .offset = offsetof(struct test_ping_req_msg_v01, + client_name), + .ei_array = test_name_type_v01_ei, + }, + {} +}; + +struct test_ping_resp_msg_v01 { + struct qmi_response_type_v01 resp; + + u8 pong_valid; + char pong[4]; + + u8 service_name_valid; + struct test_name_type_v01 service_name; +}; + +static struct qmi_elem_info test_ping_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = PING_RESP1_TLV_TYPE, + .offset = offsetof(struct test_ping_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = PING_OPT1_TLV_TYPE, + .offset = offsetof(struct test_ping_resp_msg_v01, + pong_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 4, + .elem_size = sizeof(char), + .array_type = STATIC_ARRAY, + .tlv_type = PING_OPT1_TLV_TYPE, + .offset = offsetof(struct test_ping_resp_msg_v01, + pong), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = PING_OPT2_TLV_TYPE, + .offset = offsetof(struct test_ping_resp_msg_v01, + service_name_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct test_name_type_v01), + .array_type = NO_ARRAY, + .tlv_type = PING_OPT2_TLV_TYPE, + .offset = offsetof(struct test_ping_resp_msg_v01, + service_name), + .ei_array = test_name_type_v01_ei, + }, + {} +}; + +struct test_data_req_msg_v01 { + u32 data_len; + u8 data[TEST_MED_DATA_SIZE_V01]; + + u8 client_name_valid; + struct test_name_type_v01 client_name; +}; + +static struct qmi_elem_info test_data_req_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = DATA_REQ1_TLV_TYPE, + .offset = offsetof(struct test_data_req_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = TEST_MED_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .array_type = VAR_LEN_ARRAY, + .tlv_type = DATA_REQ1_TLV_TYPE, + .offset = offsetof(struct test_data_req_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = DATA_OPT1_TLV_TYPE, + .offset = offsetof(struct test_data_req_msg_v01, + client_name_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct test_name_type_v01), + .array_type = NO_ARRAY, + .tlv_type = DATA_OPT1_TLV_TYPE, + .offset = offsetof(struct test_data_req_msg_v01, + client_name), + .ei_array = test_name_type_v01_ei, + }, + {} +}; + +struct test_data_resp_msg_v01 { + struct qmi_response_type_v01 resp; + + u8 data_valid; + u32 data_len; + u8 data[TEST_MED_DATA_SIZE_V01]; + + u8 service_name_valid; + struct test_name_type_v01 service_name; +}; + +static struct qmi_elem_info test_data_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = DATA_RESP1_TLV_TYPE, + .offset = offsetof(struct test_data_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = DATA_OPT1_TLV_TYPE, + .offset = offsetof(struct test_data_resp_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = DATA_OPT1_TLV_TYPE, + .offset = offsetof(struct test_data_resp_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = TEST_MED_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .array_type = VAR_LEN_ARRAY, + .tlv_type = DATA_OPT1_TLV_TYPE, + .offset = offsetof(struct test_data_resp_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = DATA_OPT2_TLV_TYPE, + .offset = offsetof(struct test_data_resp_msg_v01, + service_name_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct test_name_type_v01), + .array_type = NO_ARRAY, + .tlv_type = DATA_OPT2_TLV_TYPE, + .offset = offsetof(struct test_data_resp_msg_v01, + service_name), + .ei_array = test_name_type_v01_ei, + }, + {} +}; + +/* + * ping_write() - ping_pong debugfs file write handler + * @file: debugfs file context + * @user_buf: reference to the user data (ignored) + * @count: number of bytes in @user_buf + * @ppos: offset in @file to write + * + * This function allows user space to send out a ping_pong QMI encoded message + * to the associated remote test service and will return with the result of the + * transaction. It serves as an example of how to provide a custom response + * handler. + * + * Return: @count, or negative errno on failure. + */ +static ssize_t ping_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct qmi_handle *qmi = file->private_data; + struct test_ping_req_msg_v01 req = {}; + struct qmi_txn txn; + int ret; + + memcpy(req.ping, "ping", sizeof(req.ping)); + + ret = qmi_txn_init(qmi, &txn, NULL, NULL); + if (ret < 0) + return ret; + + ret = qmi_send_request(qmi, NULL, &txn, + TEST_PING_REQ_MSG_ID_V01, + TEST_PING_REQ_MAX_MSG_LEN_V01, + test_ping_req_msg_v01_ei, &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + return ret; + } + + ret = qmi_txn_wait(&txn, 5 * HZ); + if (ret < 0) + count = ret; + + return count; +} + +static const struct file_operations ping_fops = { + .open = simple_open, + .write = ping_write, +}; + +static void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, + struct qmi_txn *txn, const void *data) +{ + const struct test_ping_resp_msg_v01 *resp = data; + + if (!txn) { + pr_err("spurious ping response\n"); + return; + } + + if (resp->resp.result == QMI_RESULT_FAILURE_V01) + txn->result = -ENXIO; + else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4)) + txn->result = -EINVAL; + + complete(&txn->completion); +} + +/* + * data_write() - data debugfs file write handler + * @file: debugfs file context + * @user_buf: reference to the user data + * @count: number of bytes in @user_buf + * @ppos: offset in @file to write + * + * This function allows user space to send out a data QMI encoded message to + * the associated remote test service and will return with the result of the + * transaction. It serves as an example of how to have the QMI helpers decode a + * transaction response into a provided object automatically. + * + * Return: @count, or negative errno on failure. + */ +static ssize_t data_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) + +{ + struct qmi_handle *qmi = file->private_data; + struct test_data_resp_msg_v01 *resp; + struct test_data_req_msg_v01 *req; + struct qmi_txn txn; + int ret; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) { + kfree(req); + return -ENOMEM; + } + + req->data_len = min_t(size_t, sizeof(req->data), count); + if (copy_from_user(req->data, user_buf, req->data_len)) { + ret = -EFAULT; + goto out; + } + + ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(qmi, NULL, &txn, + TEST_DATA_REQ_MSG_ID_V01, + TEST_DATA_REQ_MAX_MSG_LEN_V01, + test_data_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + goto out; + } + + ret = qmi_txn_wait(&txn, 5 * HZ); + if (ret < 0) { + goto out; + } else if (!resp->data_valid || + resp->data_len != req->data_len || + memcmp(resp->data, req->data, req->data_len)) { + pr_err("response data doesn't match expectation\n"); + ret = -EINVAL; + goto out; + } + + ret = count; + +out: + kfree(resp); + kfree(req); + + return ret; +} + +static const struct file_operations data_fops = { + .open = simple_open, + .write = data_write, +}; + +static struct qmi_msg_handler qmi_sample_handlers[] = { + { + .type = QMI_RESPONSE, + .msg_id = TEST_PING_REQ_MSG_ID_V01, + .ei = test_ping_resp_msg_v01_ei, + .decoded_size = sizeof(struct test_ping_req_msg_v01), + .fn = ping_pong_cb + }, + {} +}; + +struct qmi_sample { + struct qmi_handle qmi; + + struct dentry *de_dir; + struct dentry *de_data; + struct dentry *de_ping; +}; + +static struct dentry *qmi_debug_dir; + +static int qmi_sample_probe(struct platform_device *pdev) +{ + struct sockaddr_qrtr *sq; + struct qmi_sample *sample; + char path[20]; + int ret; + + sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL); + if (!sample) + return -ENOMEM; + + ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01, + NULL, + qmi_sample_handlers); + if (ret < 0) + return ret; + + sq = dev_get_platdata(&pdev->dev); + ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq, + sizeof(*sq), 0); + if (ret < 0) { + pr_err("failed to connect to remote service port\n"); + goto err_release_qmi_handle; + } + + snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port); + + sample->de_dir = debugfs_create_dir(path, qmi_debug_dir); + if (IS_ERR(sample->de_dir)) { + ret = PTR_ERR(sample->de_dir); + goto err_release_qmi_handle; + } + + sample->de_data = debugfs_create_file("data", 0600, sample->de_dir, + sample, &data_fops); + if (IS_ERR(sample->de_data)) { + ret = PTR_ERR(sample->de_data); + goto err_remove_de_dir; + } + + sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir, + sample, &ping_fops); + if (IS_ERR(sample->de_ping)) { + ret = PTR_ERR(sample->de_ping); + goto err_remove_de_data; + } + + platform_set_drvdata(pdev, sample); + + return 0; + +err_remove_de_data: + debugfs_remove(sample->de_data); +err_remove_de_dir: + debugfs_remove(sample->de_dir); +err_release_qmi_handle: + qmi_handle_release(&sample->qmi); + + return ret; +} + +static int qmi_sample_remove(struct platform_device *pdev) +{ + struct qmi_sample *sample = platform_get_drvdata(pdev); + + debugfs_remove(sample->de_ping); + debugfs_remove(sample->de_data); + debugfs_remove(sample->de_dir); + + qmi_handle_release(&sample->qmi); + + return 0; +} + +static struct platform_driver qmi_sample_driver = { + .probe = qmi_sample_probe, + .remove = qmi_sample_remove, + .driver = { + .name = "qmi_sample_client", + }, +}; + +static int qmi_sample_new_server(struct qmi_handle *qmi, + struct qmi_service *service) +{ + struct platform_device *pdev; + struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port }; + int ret; + + pdev = platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO); + if (!pdev) + return -ENOMEM; + + ret = platform_device_add_data(pdev, &sq, sizeof(sq)); + if (ret) + goto err_put_device; + + ret = platform_device_add(pdev); + if (ret) + goto err_put_device; + + service->priv = pdev; + + return 0; + +err_put_device: + platform_device_put(pdev); + + return ret; +} + +static void qmi_sample_del_server(struct qmi_handle *qmi, + struct qmi_service *service) +{ + struct platform_device *pdev = service->priv; + + platform_device_unregister(pdev); +} + +static struct qmi_handle lookup_client; + +static struct qmi_ops lookup_ops = { + .new_server = qmi_sample_new_server, + .del_server = qmi_sample_del_server, +}; + +static int qmi_sample_init(void) +{ + int ret; + + qmi_debug_dir = debugfs_create_dir("qmi_sample", NULL); + if (IS_ERR(qmi_debug_dir)) { + pr_err("failed to create qmi_sample dir\n"); + return PTR_ERR(qmi_debug_dir); + } + + ret = platform_driver_register(&qmi_sample_driver); + if (ret) + goto err_remove_debug_dir; + + ret = qmi_handle_init(&lookup_client, 0, &lookup_ops, NULL); + if (ret < 0) + goto err_unregister_driver; + + qmi_add_lookup(&lookup_client, 15, 0, 0); + + return 0; + +err_unregister_driver: + platform_driver_unregister(&qmi_sample_driver); +err_remove_debug_dir: + debugfs_remove(qmi_debug_dir); + + return ret; +} + +static void qmi_sample_exit(void) +{ + qmi_handle_release(&lookup_client); + + platform_driver_unregister(&qmi_sample_driver); + + debugfs_remove(qmi_debug_dir); +} + +module_init(qmi_sample_init); +module_exit(qmi_sample_exit); + +MODULE_DESCRIPTION("Sample QMI client driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.3-14-g43fede From 15981bab23cc1caf7070fa10ac39efa5d928dee9 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 10 Nov 2017 23:02:19 +0300 Subject: arm64: dts: renesas: r8a77970: add PFC support Define the generic R8A77970 part of the PFC device node. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77970.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77970.dtsi b/arch/arm64/boot/dts/renesas/r8a77970.dtsi index fc397ccd06b6..8eccfec83c9c 100644 --- a/arch/arm64/boot/dts/renesas/r8a77970.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77970.dtsi @@ -166,6 +166,11 @@ #iommu-cells = <1>; }; + pfc: pin-controller@e6060000 { + compatible = "renesas,pfc-r8a77970"; + reg = <0 0xe6060000 0 0x504>; + }; + intc_ex: interrupt-controller@e61c0000 { compatible = "renesas,intc-ex-r8a77970", "renesas,irqc"; #interrupt-cells = <2>; -- cgit v1.3-14-g43fede From 31bded67ad215be592deda0c9ee8acdfe2067243 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 10 Nov 2017 23:02:20 +0300 Subject: arm64: dts: renesas: eagle: add SCIF0 pins Add the (previously omitted) SCIF0 pin data to the Eagle board's device tree. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77970-eagle.dts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts b/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts index 8fe5c193e049..f174103d2206 100644 --- a/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts +++ b/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts @@ -52,11 +52,21 @@ clock-frequency = <32768>; }; +&pfc { + scif0_pins: scif0 { + groups = "scif0_data"; + function = "scif0"; + }; +}; + &rwdt { timeout-sec = <60>; status = "okay"; }; &scif0 { + pinctrl-0 = <&scif0_pins>; + pinctrl-names = "default"; + status = "okay"; }; -- cgit v1.3-14-g43fede From f90d738ef6718b35ac7267b05a2e027c90e19f4d Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 9 Feb 2018 18:34:18 +0100 Subject: ARM: shmobile: Enable RZA1 pin controller Enable PINCTRL_RZA1 option in shmobile_defconfig Signed-off-by: Jacopo Mondi Tested-by: Simon Horman Signed-off-by: Simon Horman --- arch/arm/configs/shmobile_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/shmobile_defconfig b/arch/arm/configs/shmobile_defconfig index 578434cfd1a0..79b2b7c12264 100644 --- a/arch/arm/configs/shmobile_defconfig +++ b/arch/arm/configs/shmobile_defconfig @@ -120,6 +120,7 @@ CONFIG_SPI=y CONFIG_SPI_RSPI=y CONFIG_SPI_SH_MSIOF=y CONFIG_SPI_SH_HSPI=y +CONFIG_PINCTRL_RZA1=y CONFIG_GPIO_EM=y CONFIG_GPIO_RCAR=y CONFIG_GPIO_PCF857X=y -- cgit v1.3-14-g43fede From 88c23c05ef18f543fad7b26c95a3acc0ffb378cc Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Tue, 23 Jan 2018 15:58:44 -0800 Subject: ARM: dts: nanopi-neo-air: Add WiFi / eMMC Enable the WiFi (AP6212) chip and eMMC support for the NanoPi NEO Air. This patch was originally submitted by Jelle van der Waa. Martin Kelly modified it to compile on the latest kernel, fixed up some review comments from Maxime Ripard, and re-tested the patch. Cc: Maxime Ripard Cc: linux-sunxi@googlegroups.com Cc: devicetree@vger.kernel.org Signed-off-by: Jelle van der Waa Signed-off-by: Martin Kelly Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts index 03ff6f8b93ff..920849092cc8 100644 --- a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts +++ b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts @@ -72,6 +72,11 @@ gpios = <&pio 0 10 GPIO_ACTIVE_HIGH>; /* PA10 */ }; }; + + wifi_pwrseq: wifi_pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&r_pio 0 7 GPIO_ACTIVE_LOW>; /* PL7 */ + }; }; &mmc0 { @@ -84,6 +89,25 @@ status = "okay"; }; +&mmc1 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins_a>; + vmmc-supply = <®_vcc3v3>; + vqmmc-supply = <®_vcc3v3>; + mmc-pwrseq = <&wifi_pwrseq>; + bus-width = <4>; + non-removable; + status = "okay"; + + brcmf: bcrmf@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + interrupt-parent = <&pio>; + interrupts = <6 10 IRQ_TYPE_LEVEL_LOW>; /* PG10 / EINT10 */ + interrupt-names = "host-wake"; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins_a>; -- cgit v1.3-14-g43fede From 45e01f401a2a16a8d7ca910fe01c124451980bd8 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Fri, 22 Dec 2017 22:57:37 +0200 Subject: ARM: dts: sunxi: Switch MMC nodes away from cd-inverted property Using the cd-inverted property is not useful when GPIOs are used as card detects since the polarity can be specified with the usual GPIO_ACTIVE_(HIGH|LOW) GPIO flags. It has also caused confusion for U-Boot developers, so migrate all sunxi boards away from cd-inverted. Signed-off-by: Tuomas Tynkkynen Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun4i-a10-a1000.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-ba10-tvbox.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-chuwi-v7-cw0825.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-dserve-dsrv9703c.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-gemei-g9.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-hackberry.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-hyundai-a7hd.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-inet1.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-inet97fv2.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-inet9f-rev03.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-itead-iteaduino-plus.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-jesurun-q5.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-marsboard.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-mini-xplus.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-mk802.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-mk802ii.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-pcduino.dts | 3 +-- arch/arm/boot/dts/sun4i-a10-pov-protab2-ips9.dts | 3 +-- arch/arm/boot/dts/sun5i-a10s-auxtek-t003.dts | 3 +-- arch/arm/boot/dts/sun5i-a10s-auxtek-t004.dts | 3 +-- arch/arm/boot/dts/sun5i-a10s-mk802.dts | 3 +-- arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 6 ++---- arch/arm/boot/dts/sun5i-a10s-r7-tv-dongle.dts | 3 +-- arch/arm/boot/dts/sun5i-a10s-wobo-i5.dts | 3 +-- arch/arm/boot/dts/sun5i-a13-empire-electronix-d709.dts | 3 +-- arch/arm/boot/dts/sun5i-a13-hsg-h702.dts | 3 +-- arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts | 3 +-- arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 3 +-- arch/arm/boot/dts/sun5i-gr8-evb.dts | 3 +-- arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi | 3 +-- arch/arm/boot/dts/sun6i-a31-colombus.dts | 3 +-- arch/arm/boot/dts/sun6i-a31-hummingbird.dts | 3 +-- arch/arm/boot/dts/sun6i-a31-i7.dts | 3 +-- arch/arm/boot/dts/sun6i-a31-m9.dts | 3 +-- arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts | 3 +-- arch/arm/boot/dts/sun6i-a31s-primo81.dts | 3 +-- arch/arm/boot/dts/sun6i-a31s-sina31s.dts | 3 +-- arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 3 +-- arch/arm/boot/dts/sun6i-a31s-yones-toptech-bs1078-v2.dts | 3 +-- arch/arm/boot/dts/sun6i-reference-design-tablet.dtsi | 3 +-- arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-bananapi.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-bananapro.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-hummingbird.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-icnova-swac.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-itead-ibox.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-m3.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-mk808c.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts | 6 ++---- arch/arm/boot/dts/sun7i-a20-olinuxino-lime.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 6 ++---- arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts | 6 ++---- arch/arm/boot/dts/sun7i-a20-orangepi.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-pcduino3-nano.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-pcduino3.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-wexler-tab7200.dts | 3 +-- arch/arm/boot/dts/sun7i-a20-wits-pro-a20-dkt.dts | 3 +-- arch/arm/boot/dts/sun8i-a23-evb.dts | 3 +-- arch/arm/boot/dts/sun8i-a33-olinuxino.dts | 3 +-- arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts | 3 +-- arch/arm/boot/dts/sun8i-a83t-allwinner-h8homlet-v2.dts | 3 +-- arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts | 3 +-- arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts | 3 +-- arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts | 3 +-- arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts | 3 +-- arch/arm/boot/dts/sun8i-h3-beelink-x2.dts | 3 +-- arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts | 3 +-- arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts | 3 +-- arch/arm/boot/dts/sun8i-h3-nanopi.dtsi | 3 +-- arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 3 +-- arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts | 3 +-- arch/arm/boot/dts/sun8i-h3-orangepi-one.dts | 3 +-- arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts | 3 +-- arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts | 3 +-- arch/arm/boot/dts/sun8i-r40-bananapi-m2-ultra.dts | 3 +-- arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi | 3 +-- arch/arm/boot/dts/sun8i-v40-bananapi-m2-berry.dts | 3 +-- arch/arm/boot/dts/sun9i-a80-cubieboard4.dts | 3 +-- arch/arm/boot/dts/sun9i-a80-optimus.dts | 3 +-- 86 files changed, 90 insertions(+), 180 deletions(-) diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts index 09e909576c61..6c254ec4c85b 100644 --- a/arch/arm/boot/dts/sun4i-a10-a1000.dts +++ b/arch/arm/boot/dts/sun4i-a10-a1000.dts @@ -164,8 +164,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-ba10-tvbox.dts b/arch/arm/boot/dts/sun4i-a10-ba10-tvbox.dts index 39ba4ccb9e2e..38a2c4134952 100644 --- a/arch/arm/boot/dts/sun4i-a10-ba10-tvbox.dts +++ b/arch/arm/boot/dts/sun4i-a10-ba10-tvbox.dts @@ -106,8 +106,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-chuwi-v7-cw0825.dts b/arch/arm/boot/dts/sun4i-a10-chuwi-v7-cw0825.dts index dfc88aee4fe3..cf7b392dff31 100644 --- a/arch/arm/boot/dts/sun4i-a10-chuwi-v7-cw0825.dts +++ b/arch/arm/boot/dts/sun4i-a10-chuwi-v7-cw0825.dts @@ -123,8 +123,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts index 1982c8c238c5..197a1f2b75ff 100644 --- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts @@ -162,8 +162,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-dserve-dsrv9703c.dts b/arch/arm/boot/dts/sun4i-a10-dserve-dsrv9703c.dts index 147cbc5e08ac..896e27a08727 100644 --- a/arch/arm/boot/dts/sun4i-a10-dserve-dsrv9703c.dts +++ b/arch/arm/boot/dts/sun4i-a10-dserve-dsrv9703c.dts @@ -150,8 +150,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-gemei-g9.dts b/arch/arm/boot/dts/sun4i-a10-gemei-g9.dts index 41ca8bded89f..ea7a59dcf8f9 100644 --- a/arch/arm/boot/dts/sun4i-a10-gemei-g9.dts +++ b/arch/arm/boot/dts/sun4i-a10-gemei-g9.dts @@ -141,8 +141,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH01 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH01 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-hackberry.dts b/arch/arm/boot/dts/sun4i-a10-hackberry.dts index f33e42d6ce8b..cc988ccd5ca7 100644 --- a/arch/arm/boot/dts/sun4i-a10-hackberry.dts +++ b/arch/arm/boot/dts/sun4i-a10-hackberry.dts @@ -106,8 +106,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-hyundai-a7hd.dts b/arch/arm/boot/dts/sun4i-a10-hyundai-a7hd.dts index 35c57d065dd8..f63767cddd8e 100644 --- a/arch/arm/boot/dts/sun4i-a10-hyundai-a7hd.dts +++ b/arch/arm/boot/dts/sun4i-a10-hyundai-a7hd.dts @@ -78,8 +78,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-inet1.dts b/arch/arm/boot/dts/sun4i-a10-inet1.dts index 9482e831a9a1..26d0c1d6a02b 100644 --- a/arch/arm/boot/dts/sun4i-a10-inet1.dts +++ b/arch/arm/boot/dts/sun4i-a10-inet1.dts @@ -152,8 +152,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts b/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts index 4b5c91c8e85b..5d096528e75a 100644 --- a/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts +++ b/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts @@ -142,8 +142,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-inet9f-rev03.dts b/arch/arm/boot/dts/sun4i-a10-inet9f-rev03.dts index 13224f5ac166..221acd10f6c8 100644 --- a/arch/arm/boot/dts/sun4i-a10-inet9f-rev03.dts +++ b/arch/arm/boot/dts/sun4i-a10-inet9f-rev03.dts @@ -300,8 +300,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-itead-iteaduino-plus.dts b/arch/arm/boot/dts/sun4i-a10-itead-iteaduino-plus.dts index d22bd79562d8..80ecd78247ac 100644 --- a/arch/arm/boot/dts/sun4i-a10-itead-iteaduino-plus.dts +++ b/arch/arm/boot/dts/sun4i-a10-itead-iteaduino-plus.dts @@ -106,8 +106,7 @@ pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-jesurun-q5.dts b/arch/arm/boot/dts/sun4i-a10-jesurun-q5.dts index 879141ca6027..247fa27ef717 100644 --- a/arch/arm/boot/dts/sun4i-a10-jesurun-q5.dts +++ b/arch/arm/boot/dts/sun4i-a10-jesurun-q5.dts @@ -133,8 +133,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-marsboard.dts b/arch/arm/boot/dts/sun4i-a10-marsboard.dts index 435c551aef0f..0dbf69576512 100644 --- a/arch/arm/boot/dts/sun4i-a10-marsboard.dts +++ b/arch/arm/boot/dts/sun4i-a10-marsboard.dts @@ -132,8 +132,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts index 1b639e5f9172..f9d74e21031d 100644 --- a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts +++ b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts @@ -96,8 +96,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-mk802.dts b/arch/arm/boot/dts/sun4i-a10-mk802.dts index 7198b34e2e50..face1925ee5b 100644 --- a/arch/arm/boot/dts/sun4i-a10-mk802.dts +++ b/arch/arm/boot/dts/sun4i-a10-mk802.dts @@ -73,8 +73,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-mk802ii.dts b/arch/arm/boot/dts/sun4i-a10-mk802ii.dts index e460da2eb139..17dcdf031118 100644 --- a/arch/arm/boot/dts/sun4i-a10-mk802ii.dts +++ b/arch/arm/boot/dts/sun4i-a10-mk802ii.dts @@ -82,8 +82,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts index 49247fbe6acd..f15f01955fdf 100644 --- a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts +++ b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts @@ -165,8 +165,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-pcduino.dts b/arch/arm/boot/dts/sun4i-a10-pcduino.dts index 6e140547b638..b97a0f2f20b9 100644 --- a/arch/arm/boot/dts/sun4i-a10-pcduino.dts +++ b/arch/arm/boot/dts/sun4i-a10-pcduino.dts @@ -140,8 +140,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10-pov-protab2-ips9.dts b/arch/arm/boot/dts/sun4i-a10-pov-protab2-ips9.dts index 5081303f79e7..84b25be1ac94 100644 --- a/arch/arm/boot/dts/sun4i-a10-pov-protab2-ips9.dts +++ b/arch/arm/boot/dts/sun4i-a10-pov-protab2-ips9.dts @@ -138,8 +138,7 @@ &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a10s-auxtek-t003.dts b/arch/arm/boot/dts/sun5i-a10s-auxtek-t003.dts index d2dee8d434bf..39504d720efc 100644 --- a/arch/arm/boot/dts/sun5i-a10s-auxtek-t003.dts +++ b/arch/arm/boot/dts/sun5i-a10s-auxtek-t003.dts @@ -93,8 +93,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_t003>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 1 GPIO_ACTIVE_HIGH>; /* PG1 */ - cd-inverted; + cd-gpios = <&pio 6 1 GPIO_ACTIVE_LOW>; /* PG1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a10s-auxtek-t004.dts b/arch/arm/boot/dts/sun5i-a10s-auxtek-t004.dts index 16f839df4227..8d4fb9331212 100644 --- a/arch/arm/boot/dts/sun5i-a10s-auxtek-t004.dts +++ b/arch/arm/boot/dts/sun5i-a10s-auxtek-t004.dts @@ -104,8 +104,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_t004>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 1 GPIO_ACTIVE_HIGH>; /* PG1 */ - cd-inverted; + cd-gpios = <&pio 6 1 GPIO_ACTIVE_LOW>; /* PG1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a10s-mk802.dts b/arch/arm/boot/dts/sun5i-a10s-mk802.dts index 020aa9d6c31d..dd7fd5c3d76f 100644 --- a/arch/arm/boot/dts/sun5i-a10s-mk802.dts +++ b/arch/arm/boot/dts/sun5i-a10s-mk802.dts @@ -92,8 +92,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_mk802>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 1 GPIO_ACTIVE_HIGH>; /* PG1 */ - cd-inverted; + cd-gpios = <&pio 6 1 GPIO_ACTIVE_LOW>; /* PG1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts index da95118af4dc..2c902ed2c87a 100644 --- a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts +++ b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts @@ -201,8 +201,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_olinuxino_micro>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 1 GPIO_ACTIVE_HIGH>; /* PG1 */ - cd-inverted; + cd-gpios = <&pio 6 1 GPIO_ACTIVE_LOW>; /* PG1 */ status = "okay"; }; @@ -211,8 +210,7 @@ pinctrl-0 = <&mmc1_pins_a>, <&mmc1_cd_pin_olinuxino_micro>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 13 GPIO_ACTIVE_HIGH>; /* PG13 */ - cd-inverted; + cd-gpios = <&pio 6 13 GPIO_ACTIVE_LOW>; /* PG13 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a10s-r7-tv-dongle.dts b/arch/arm/boot/dts/sun5i-a10s-r7-tv-dongle.dts index 262b3669f04d..034853d1c08f 100644 --- a/arch/arm/boot/dts/sun5i-a10s-r7-tv-dongle.dts +++ b/arch/arm/boot/dts/sun5i-a10s-r7-tv-dongle.dts @@ -80,8 +80,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_r7>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 1 GPIO_ACTIVE_HIGH>; /* PG1 */ - cd-inverted; + cd-gpios = <&pio 6 1 GPIO_ACTIVE_LOW>; /* PG1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a10s-wobo-i5.dts b/arch/arm/boot/dts/sun5i-a10s-wobo-i5.dts index 5482be174e12..3f68ef5d92a0 100644 --- a/arch/arm/boot/dts/sun5i-a10s-wobo-i5.dts +++ b/arch/arm/boot/dts/sun5i-a10s-wobo-i5.dts @@ -130,8 +130,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_wobo_i5>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */ - cd-inverted; + cd-gpios = <&pio 1 3 GPIO_ACTIVE_LOW>; /* PB3 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a13-empire-electronix-d709.dts b/arch/arm/boot/dts/sun5i-a13-empire-electronix-d709.dts index 3dbb0d7c2f8c..378214d8316e 100644 --- a/arch/arm/boot/dts/sun5i-a13-empire-electronix-d709.dts +++ b/arch/arm/boot/dts/sun5i-a13-empire-electronix-d709.dts @@ -125,8 +125,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_d709>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 0 GPIO_ACTIVE_HIGH>; /* PG0 */ - cd-inverted; + cd-gpios = <&pio 6 0 GPIO_ACTIVE_LOW>; /* PG0 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a13-hsg-h702.dts b/arch/arm/boot/dts/sun5i-a13-hsg-h702.dts index 584fa579ded2..7ee0c3f6d7a1 100644 --- a/arch/arm/boot/dts/sun5i-a13-hsg-h702.dts +++ b/arch/arm/boot/dts/sun5i-a13-hsg-h702.dts @@ -120,8 +120,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_h702>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 0 GPIO_ACTIVE_HIGH>; /* PG0 */ - cd-inverted; + cd-gpios = <&pio 6 0 GPIO_ACTIVE_LOW>; /* PG0 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts index 3a831eaf1dfc..aa4b34fd9126 100644 --- a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts +++ b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts @@ -99,8 +99,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_olinuxinom>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 0 GPIO_ACTIVE_HIGH>; /* PG0 */ - cd-inverted; + cd-gpios = <&pio 6 0 GPIO_ACTIVE_LOW>; /* PG0 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts index 4b9af423c6d5..437ad913a373 100644 --- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts @@ -194,8 +194,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_olinuxino>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 0 GPIO_ACTIVE_HIGH>; /* PG0 */ - cd-inverted; + cd-gpios = <&pio 6 0 GPIO_ACTIVE_LOW>; /* PG0 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-gr8-evb.dts b/arch/arm/boot/dts/sun5i-gr8-evb.dts index 558c16a30543..5f0adc0f7bb4 100644 --- a/arch/arm/boot/dts/sun5i-gr8-evb.dts +++ b/arch/arm/boot/dts/sun5i-gr8-evb.dts @@ -236,8 +236,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_gr8_evb>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 6 0 GPIO_ACTIVE_HIGH>; /* PG0 */ - cd-inverted; + cd-gpios = <&pio 6 0 GPIO_ACTIVE_LOW>; /* PG0 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi index 49229b3d5492..8acbaab14fe5 100644 --- a/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi +++ b/arch/arm/boot/dts/sun5i-reference-design-tablet.dtsi @@ -127,8 +127,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; vmmc-supply = <®_vcc3v0>; bus-width = <4>; - cd-gpios = <&pio 6 0 GPIO_ACTIVE_HIGH>; /* PG0 */ - cd-inverted; + cd-gpios = <&pio 6 0 GPIO_ACTIVE_LOW>; /* PG0 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-a31-colombus.dts b/arch/arm/boot/dts/sun6i-a31-colombus.dts index 85eff0307ca4..939c497a6f70 100644 --- a/arch/arm/boot/dts/sun6i-a31-colombus.dts +++ b/arch/arm/boot/dts/sun6i-a31-colombus.dts @@ -117,8 +117,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_colombus>; vmmc-supply = <®_vcc3v0>; bus-width = <4>; - cd-gpios = <&pio 0 8 GPIO_ACTIVE_HIGH>; /* PA8 */ - cd-inverted; + cd-gpios = <&pio 0 8 GPIO_ACTIVE_LOW>; /* PA8 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts index 19e382a11297..ce4f9e9834bf 100644 --- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts +++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts @@ -218,8 +218,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_hummingbird>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 0 8 GPIO_ACTIVE_HIGH>; /* PA8 */ - cd-inverted; + cd-gpios = <&pio 0 8 GPIO_ACTIVE_LOW>; /* PA8 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-a31-i7.dts b/arch/arm/boot/dts/sun6i-a31-i7.dts index 010a84c7c012..2e0a672c8031 100644 --- a/arch/arm/boot/dts/sun6i-a31-i7.dts +++ b/arch/arm/boot/dts/sun6i-a31-i7.dts @@ -124,8 +124,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_i7>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */ - cd-inverted; + cd-gpios = <&pio 7 22 GPIO_ACTIVE_LOW>; /* PH22 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-a31-m9.dts b/arch/arm/boot/dts/sun6i-a31-m9.dts index 50605fd4449e..9698f6d38d03 100644 --- a/arch/arm/boot/dts/sun6i-a31-m9.dts +++ b/arch/arm/boot/dts/sun6i-a31-m9.dts @@ -107,8 +107,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_m9>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */ - cd-inverted; + cd-gpios = <&pio 7 22 GPIO_ACTIVE_LOW>; /* PH22 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts index 5219556e9f73..bb14b171b160 100644 --- a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts +++ b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts @@ -107,8 +107,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_m9>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */ - cd-inverted; + cd-gpios = <&pio 7 22 GPIO_ACTIVE_LOW>; /* PH22 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-a31s-primo81.dts b/arch/arm/boot/dts/sun6i-a31s-primo81.dts index 0cdb38ab3377..4cb9664cdb29 100644 --- a/arch/arm/boot/dts/sun6i-a31s-primo81.dts +++ b/arch/arm/boot/dts/sun6i-a31s-primo81.dts @@ -151,8 +151,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_primo81>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 0 8 GPIO_ACTIVE_HIGH>; /* PA8 */ - cd-inverted; + cd-gpios = <&pio 0 8 GPIO_ACTIVE_LOW>; /* PA8 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-a31s-sina31s.dts b/arch/arm/boot/dts/sun6i-a31s-sina31s.dts index 298476485bb4..da0ccf5a2c44 100644 --- a/arch/arm/boot/dts/sun6i-a31s-sina31s.dts +++ b/arch/arm/boot/dts/sun6i-a31s-sina31s.dts @@ -167,8 +167,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_sina31s>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 0 4 GPIO_ACTIVE_HIGH>; /* PA4 */ - cd-inverted; + cd-gpios = <&pio 0 4 GPIO_ACTIVE_LOW>; /* PA4 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts b/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts index 51e6f1d21c32..3077e8ec9cd9 100644 --- a/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts +++ b/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts @@ -120,8 +120,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_bpi_m2>; vmmc-supply = <®_vcc3v0>; bus-width = <4>; - cd-gpios = <&pio 0 4 GPIO_ACTIVE_HIGH>; /* PA4 */ - cd-inverted; + cd-gpios = <&pio 0 4 GPIO_ACTIVE_LOW>; /* PA4 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-a31s-yones-toptech-bs1078-v2.dts b/arch/arm/boot/dts/sun6i-a31s-yones-toptech-bs1078-v2.dts index f3edf9ca435c..aab6c1720ef7 100644 --- a/arch/arm/boot/dts/sun6i-a31s-yones-toptech-bs1078-v2.dts +++ b/arch/arm/boot/dts/sun6i-a31s-yones-toptech-bs1078-v2.dts @@ -102,8 +102,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_bs1078v2>; vmmc-supply = <®_vcc3v0>; bus-width = <4>; - cd-gpios = <&pio 0 8 GPIO_ACTIVE_HIGH>; /* PA8 */ - cd-inverted; + cd-gpios = <&pio 0 8 GPIO_ACTIVE_LOW>; /* PA8 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun6i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun6i-reference-design-tablet.dtsi index 3cc4046b904a..4e72e4f3ef96 100644 --- a/arch/arm/boot/dts/sun6i-reference-design-tablet.dtsi +++ b/arch/arm/boot/dts/sun6i-reference-design-tablet.dtsi @@ -69,8 +69,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_e708_q1>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 0 8 GPIO_ACTIVE_HIGH>; /* PA8 */ - cd-inverted; + cd-gpios = <&pio 0 8 GPIO_ACTIVE_LOW>; /* PA8 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts index 4ed3162e3e5a..763cb03033c4 100644 --- a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts +++ b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts @@ -184,8 +184,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_bpi_m1p>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 10 GPIO_ACTIVE_HIGH>; /* PH10 */ - cd-inverted; + cd-gpios = <&pio 7 10 GPIO_ACTIVE_LOW>; /* PH10 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi.dts b/arch/arm/boot/dts/sun7i-a20-bananapi.dts index 88a1c2363c6c..6f744f8931f0 100644 --- a/arch/arm/boot/dts/sun7i-a20-bananapi.dts +++ b/arch/arm/boot/dts/sun7i-a20-bananapi.dts @@ -159,8 +159,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_bananapi>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 10 GPIO_ACTIVE_HIGH>; /* PH10 */ - cd-inverted; + cd-gpios = <&pio 7 10 GPIO_ACTIVE_LOW>; /* PH10 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-bananapro.dts b/arch/arm/boot/dts/sun7i-a20-bananapro.dts index e7af1b7c33d5..0898eb6162f5 100644 --- a/arch/arm/boot/dts/sun7i-a20-bananapro.dts +++ b/arch/arm/boot/dts/sun7i-a20-bananapro.dts @@ -158,8 +158,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_bananapro>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 10 GPIO_ACTIVE_HIGH>; /* PH10 */ - cd-inverted; + cd-gpios = <&pio 7 10 GPIO_ACTIVE_LOW>; /* PH10 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts index 39f43e4eb742..942ac9dfd4a5 100644 --- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts @@ -165,8 +165,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts index 8c9bedc602ec..5649161de1d7 100644 --- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts @@ -206,8 +206,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-hummingbird.dts b/arch/arm/boot/dts/sun7i-a20-hummingbird.dts index 6e6264cd69f8..1f0e5ecbf0c4 100644 --- a/arch/arm/boot/dts/sun7i-a20-hummingbird.dts +++ b/arch/arm/boot/dts/sun7i-a20-hummingbird.dts @@ -163,8 +163,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v0>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts b/arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts index 55809973a568..2e3f2f29d124 100644 --- a/arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts +++ b/arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts @@ -160,8 +160,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-icnova-swac.dts b/arch/arm/boot/dts/sun7i-a20-icnova-swac.dts index 794e7617f545..926fa194eb1b 100644 --- a/arch/arm/boot/dts/sun7i-a20-icnova-swac.dts +++ b/arch/arm/boot/dts/sun7i-a20-icnova-swac.dts @@ -107,8 +107,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 8 5 GPIO_ACTIVE_HIGH>; /* PI5 */ - cd-inverted; + cd-gpios = <&pio 8 5 GPIO_ACTIVE_LOW>; /* PI5 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-itead-ibox.dts b/arch/arm/boot/dts/sun7i-a20-itead-ibox.dts index 8a8a6dbcd414..1b05ba466e7d 100644 --- a/arch/arm/boot/dts/sun7i-a20-itead-ibox.dts +++ b/arch/arm/boot/dts/sun7i-a20-itead-ibox.dts @@ -124,8 +124,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts b/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts index 442f3c755f36..b1ab7c1c33e3 100644 --- a/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts +++ b/arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts @@ -227,8 +227,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_lamobo_r1>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 10 GPIO_ACTIVE_HIGH>; /* PH10 */ - cd-inverted; + cd-gpios = <&pio 7 10 GPIO_ACTIVE_LOW>; /* PH10 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-m3.dts b/arch/arm/boot/dts/sun7i-a20-m3.dts index 43c94787ef07..e91a209850bc 100644 --- a/arch/arm/boot/dts/sun7i-a20-m3.dts +++ b/arch/arm/boot/dts/sun7i-a20-m3.dts @@ -120,8 +120,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-mk808c.dts b/arch/arm/boot/dts/sun7i-a20-mk808c.dts index f7413094183c..ce3f09aaeed2 100644 --- a/arch/arm/boot/dts/sun7i-a20-mk808c.dts +++ b/arch/arm/boot/dts/sun7i-a20-mk808c.dts @@ -112,8 +112,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v0>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts b/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts index 64c8ef9a2756..8f34731dee34 100644 --- a/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts +++ b/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts @@ -190,8 +190,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; @@ -200,8 +199,7 @@ pinctrl-0 = <&mmc3_pins_a>, <&mmc3_cd_pin_olimex_som_evb>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 0 GPIO_ACTIVE_HIGH>; /* PH0 */ - cd-inverted; + cd-gpios = <&pio 7 0 GPIO_ACTIVE_LOW>; /* PH0 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime.dts index edf9c3c6c0d7..d20fd03596e9 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime.dts @@ -158,8 +158,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts index ba250189d07f..b828677f331d 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts @@ -159,8 +159,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts index dffbaa24b3ee..866d230593be 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts @@ -226,8 +226,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; @@ -236,8 +235,7 @@ pinctrl-0 = <&mmc3_pins_a>, <&mmc3_cd_pin_olinuxinom>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 11 GPIO_ACTIVE_HIGH>; /* PH11 */ - cd-inverted; + cd-gpios = <&pio 7 11 GPIO_ACTIVE_LOW>; /* PH11 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts b/arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts index 7af4c8fc1865..a5ae0c55bf5b 100644 --- a/arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts +++ b/arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts @@ -144,8 +144,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_orangepi>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 10 GPIO_ACTIVE_HIGH>; /* PH10 */ - cd-inverted; + cd-gpios = <&pio 7 10 GPIO_ACTIVE_LOW>; /* PH10 */ status = "okay"; }; @@ -154,8 +153,7 @@ pinctrl-0 = <&mmc3_pins_a>, <&mmc3_cd_pin_orangepi>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 11 GPIO_ACTIVE_HIGH>; /* PH11 */ - cd-inverted; + cd-gpios = <&pio 7 11 GPIO_ACTIVE_LOW>; /* PH11 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-orangepi.dts b/arch/arm/boot/dts/sun7i-a20-orangepi.dts index 0a8d4a05e8a0..7a4244e57589 100644 --- a/arch/arm/boot/dts/sun7i-a20-orangepi.dts +++ b/arch/arm/boot/dts/sun7i-a20-orangepi.dts @@ -135,8 +135,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_orangepi>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 10 GPIO_ACTIVE_HIGH>; /* PH10 */ - cd-inverted; + cd-gpios = <&pio 7 10 GPIO_ACTIVE_LOW>; /* PH10 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-pcduino3-nano.dts b/arch/arm/boot/dts/sun7i-a20-pcduino3-nano.dts index fb591f32252c..bfca960b03e0 100644 --- a/arch/arm/boot/dts/sun7i-a20-pcduino3-nano.dts +++ b/arch/arm/boot/dts/sun7i-a20-pcduino3-nano.dts @@ -158,8 +158,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-pcduino3.dts b/arch/arm/boot/dts/sun7i-a20-pcduino3.dts index 777152a3df0f..c576f101fbde 100644 --- a/arch/arm/boot/dts/sun7i-a20-pcduino3.dts +++ b/arch/arm/boot/dts/sun7i-a20-pcduino3.dts @@ -159,8 +159,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-wexler-tab7200.dts b/arch/arm/boot/dts/sun7i-a20-wexler-tab7200.dts index f8d0aafb9f88..8202c87ca6a3 100644 --- a/arch/arm/boot/dts/sun7i-a20-wexler-tab7200.dts +++ b/arch/arm/boot/dts/sun7i-a20-wexler-tab7200.dts @@ -154,8 +154,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-wits-pro-a20-dkt.dts b/arch/arm/boot/dts/sun7i-a20-wits-pro-a20-dkt.dts index 7f8405a0dd0f..ff5c1086585c 100644 --- a/arch/arm/boot/dts/sun7i-a20-wits-pro-a20-dkt.dts +++ b/arch/arm/boot/dts/sun7i-a20-wits-pro-a20-dkt.dts @@ -123,8 +123,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */ - cd-inverted; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-a23-evb.dts b/arch/arm/boot/dts/sun8i-a23-evb.dts index 87289a60c520..8a93697df3a5 100644 --- a/arch/arm/boot/dts/sun8i-a23-evb.dts +++ b/arch/arm/boot/dts/sun8i-a23-evb.dts @@ -107,8 +107,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_evb>; vmmc-supply = <®_vcc3v0>; bus-width = <4>; - cd-gpios = <&pio 1 4 GPIO_ACTIVE_HIGH>; /* PB4 */ - cd-inverted; + cd-gpios = <&pio 1 4 GPIO_ACTIVE_LOW>; /* PB4 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-a33-olinuxino.dts b/arch/arm/boot/dts/sun8i-a33-olinuxino.dts index be9a6b8d7a1e..9757265c58d4 100644 --- a/arch/arm/boot/dts/sun8i-a33-olinuxino.dts +++ b/arch/arm/boot/dts/sun8i-a33-olinuxino.dts @@ -81,8 +81,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_olinuxino>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 1 4 GPIO_ACTIVE_HIGH>; /* PB4 */ - cd-inverted; + cd-gpios = <&pio 1 4 GPIO_ACTIVE_LOW>; /* PB4 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts b/arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts index 433cf2a2a9a2..541acb4d2b91 100644 --- a/arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts +++ b/arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts @@ -144,8 +144,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_sina33>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 1 4 GPIO_ACTIVE_HIGH>; /* PB4 */ - cd-inverted; + cd-gpios = <&pio 1 4 GPIO_ACTIVE_LOW>; /* PB4 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-a83t-allwinner-h8homlet-v2.dts b/arch/arm/boot/dts/sun8i-a83t-allwinner-h8homlet-v2.dts index 5091cecbcd1e..36ecebaff3c0 100644 --- a/arch/arm/boot/dts/sun8i-a83t-allwinner-h8homlet-v2.dts +++ b/arch/arm/boot/dts/sun8i-a83t-allwinner-h8homlet-v2.dts @@ -87,9 +87,8 @@ pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ bus-width = <4>; - cd-inverted; status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts index 6550bf0e594b..1890d41b7fae 100644 --- a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts +++ b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts @@ -112,8 +112,7 @@ pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts b/arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts index 6da08cd0e107..88decb0747ac 100644 --- a/arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts +++ b/arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts @@ -176,8 +176,7 @@ pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts index 6713d0f2b3f4..9a5017bb1440 100644 --- a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts +++ b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts @@ -116,8 +116,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts index f1c3f1cc4d97..3a196a86a984 100644 --- a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts +++ b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts @@ -140,8 +140,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts index 10da56e86ab8..7824f0dbf022 100644 --- a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts +++ b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts @@ -119,8 +119,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts b/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts index d406571a0dd6..d9dc14fe2aa3 100644 --- a/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts +++ b/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts @@ -154,8 +154,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc_io>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts index 920849092cc8..48563adc7430 100644 --- a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts +++ b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts @@ -84,8 +84,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi b/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi index 7646e331bd29..e9af61394f5d 100644 --- a/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi +++ b/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi @@ -95,8 +95,7 @@ &mmc0 { bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; status = "okay"; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts index b20be95b49d5..629f76b85005 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts @@ -136,8 +136,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts index a70a1daf4e2c..398f975b380d 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts @@ -110,8 +110,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts index 82e5d28cd698..40941fef4ff3 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts @@ -110,8 +110,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts index d22546df1b82..c4769def8cd6 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts @@ -132,8 +132,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts b/arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts index eaf09666720d..0dbdb29a8fff 100644 --- a/arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts +++ b/arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts @@ -150,8 +150,7 @@ pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 1 4 GPIO_ACTIVE_HIGH>; /* PB4 */ - cd-inverted; + cd-gpios = <&pio 1 4 GPIO_ACTIVE_LOW>; /* PB4 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-r40-bananapi-m2-ultra.dts b/arch/arm/boot/dts/sun8i-r40-bananapi-m2-ultra.dts index 8c5efe2a9881..27d9ccd0ef2f 100644 --- a/arch/arm/boot/dts/sun8i-r40-bananapi-m2-ultra.dts +++ b/arch/arm/boot/dts/sun8i-r40-bananapi-m2-ultra.dts @@ -164,8 +164,7 @@ &mmc0 { vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 7 13 GPIO_ACTIVE_HIGH>; /* PH13 */ - cd-inverted; + cd-gpios = <&pio 7 13 GPIO_ACTIVE_LOW>; /* PH13 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi index d6bd15898db6..f51f37cc721d 100644 --- a/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi +++ b/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi @@ -85,8 +85,7 @@ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 1 4 GPIO_ACTIVE_HIGH>; /* PB4 */ - cd-inverted; + cd-gpios = <&pio 1 4 GPIO_ACTIVE_LOW>; /* PB4 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun8i-v40-bananapi-m2-berry.dts b/arch/arm/boot/dts/sun8i-v40-bananapi-m2-berry.dts index fe16fc0eb518..a26d72c3f9b5 100644 --- a/arch/arm/boot/dts/sun8i-v40-bananapi-m2-berry.dts +++ b/arch/arm/boot/dts/sun8i-v40-bananapi-m2-berry.dts @@ -150,8 +150,7 @@ &mmc0 { vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 7 13 GPIO_ACTIVE_HIGH>; /* PH13 */ - cd-inverted; + cd-gpios = <&pio 7 13 GPIO_ACTIVE_LOW>; /* PH13 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts b/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts index 4024639aa005..31b06ecbc306 100644 --- a/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts +++ b/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts @@ -88,8 +88,7 @@ pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 7 18 GPIO_ACTIVE_HIGH>; /* PH18 */ - cd-inverted; + cd-gpios = <&pio 7 18 GPIO_ACTIVE_LOW>; /* PH18 */ status = "okay"; }; diff --git a/arch/arm/boot/dts/sun9i-a80-optimus.dts b/arch/arm/boot/dts/sun9i-a80-optimus.dts index a9b807be99a0..58a199b0e494 100644 --- a/arch/arm/boot/dts/sun9i-a80-optimus.dts +++ b/arch/arm/boot/dts/sun9i-a80-optimus.dts @@ -125,8 +125,7 @@ pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; bus-width = <4>; - cd-gpios = <&pio 7 18 GPIO_ACTIVE_HIGH>; /* PH8 */ - cd-inverted; + cd-gpios = <&pio 7 18 GPIO_ACTIVE_LOW>; /* PH8 */ status = "okay"; }; -- cgit v1.3-14-g43fede From 0d059d325746811ff3b3ca5e466f5d5d3ea04a8a Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Mon, 29 Jan 2018 10:23:10 +0100 Subject: ARM: dts: sun7i: Enable HDMI support on the MK808C Enable the display pipeline and HDMI output Signed-off-by: Marcus Cooper Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun7i-a20-mk808c.dts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-mk808c.dts b/arch/arm/boot/dts/sun7i-a20-mk808c.dts index f7413094183c..5b5773b34640 100644 --- a/arch/arm/boot/dts/sun7i-a20-mk808c.dts +++ b/arch/arm/boot/dts/sun7i-a20-mk808c.dts @@ -66,12 +66,27 @@ chosen { stdout-path = "serial0:115200n8"; }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; }; &codec { status = "okay"; }; +&de { + status = "okay"; +}; + &ehci0 { status = "okay"; }; @@ -80,6 +95,16 @@ status = "okay"; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &i2c0 { pinctrl-names = "default"; pinctrl-0 = <&i2c0_pins_a>; -- cgit v1.3-14-g43fede From a816d946285a31d3336ddf61e9e2d119cfa96fae Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Tue, 30 Jan 2018 19:32:54 +0100 Subject: ARM: dts: sun4i: Enable HDMI support on the MK802 Enable the display pipeline and HDMI output. Signed-off-by: Marcus Cooper Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun4i-a10-mk802.dts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/boot/dts/sun4i-a10-mk802.dts b/arch/arm/boot/dts/sun4i-a10-mk802.dts index 7198b34e2e50..982a704d5663 100644 --- a/arch/arm/boot/dts/sun4i-a10-mk802.dts +++ b/arch/arm/boot/dts/sun4i-a10-mk802.dts @@ -56,12 +56,27 @@ chosen { stdout-path = "serial0:115200n8"; }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; }; &codec { status = "okay"; }; +&de { + status = "okay"; +}; + &ehci0 { status = "okay"; }; @@ -70,6 +85,16 @@ status = "okay"; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &mmc0 { vmmc-supply = <®_vcc3v3>; bus-width = <4>; -- cgit v1.3-14-g43fede From a84e0d636d64e863700f5cd7cd7b6994800f5dd1 Mon Sep 17 00:00:00 2001 From: Stefan Mavrodiev Date: Mon, 29 Jan 2018 15:56:39 +0200 Subject: ARM: dts: sunxi: Add Olimex A20-SOM204-EVB board This is new System-On-Module platform with universal dimm socket for easy insertation. The EVB board is designed to be universal with future modules. Product page is located here [1]. There are two dts files - one for base model and another for eMMC variant. Base features of A20-SOM204 board includes: * 1GB DDR3 RAM * AXP209 PMU * KSZ9031 Gigabit PHY * AT24C16 EEPROM * Status LED * LCD connector * GPIO connector There will be variants with the following options: * Second LAN8710A Megabit PHY * 16MB SPI Flash memory * eMMC card * ATECC508 crypto device The EVB board has: * Debug UART * MicroSD card connector * USB-OTG connector * Two USB host * RTL8723BS WiFi/BT combo * IrDA transceiver/receiver * HDMI connector * VGA connector * Megabit ethernet transceiver * Gigabit ethernet transceiver * SATA connector * CAN driver * CSI camera * MIC and HP connectors * PCIe x4 connector * USB3 connector * Two UEXT connectors * Two user LEDs Some of the features are multiplexed and cannot be used the same time: CAN and Megabit PHY. Others are not usable with A20 SoC: PCIe and USB3. [1] https://www.olimex.com/Products/SOM204/ Signed-off-by: Stefan Mavrodiev Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/sun7i-a20-olimex-som204-evb.dts | 335 ++++++++++++++++++++++ 2 files changed, 336 insertions(+) create mode 100644 arch/arm/boot/dts/sun7i-a20-olimex-som204-evb.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index ade7a38543dc..aa3d28e7887c 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -942,6 +942,7 @@ dtb-$(CONFIG_MACH_SUN7I) += \ sun7i-a20-m3.dtb \ sun7i-a20-mk808c.dtb \ sun7i-a20-olimex-som-evb.dtb \ + sun7i-a20-olimex-som204-evb.dtb \ sun7i-a20-olinuxino-lime.dtb \ sun7i-a20-olinuxino-lime2.dtb \ sun7i-a20-olinuxino-lime2-emmc.dtb \ diff --git a/arch/arm/boot/dts/sun7i-a20-olimex-som204-evb.dts b/arch/arm/boot/dts/sun7i-a20-olimex-som204-evb.dts new file mode 100644 index 000000000000..eae8e267b9ef --- /dev/null +++ b/arch/arm/boot/dts/sun7i-a20-olimex-som204-evb.dts @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree Source for A20-SOM204-EVB Board + * + * Copyright (C) 2018 Olimex Ltd. + * Author: Stefan Mavrodiev + */ + +/dts-v1/; +#include "sun7i-a20.dtsi" +#include "sunxi-common-regulators.dtsi" + + +#include +#include +#include + +/ { + model = "Olimex A20-SOM204-EVB"; + compatible = "olimex,a20-olimex-som204-evb", "allwinner,sun7i-a20"; + + aliases { + serial0 = &uart0; + serial1 = &uart4; + serial2 = &uart7; + spi0 = &spi1; + spi1 = &spi2; + ethernet1 = &rtl8723bs; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + + leds { + compatible = "gpio-leds"; + + stat { + label = "a20-som204-evb:green:stat"; + gpios = <&pio 8 0 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; + + led1 { + label = "a20-som204-evb:green:led1"; + gpios = <&pio 8 10 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; + + led2 { + label = "a20-som204-evb:yellow:led2"; + gpios = <&pio 8 11 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; + }; + + rtl_pwrseq: rtl_pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pio 6 9 GPIO_ACTIVE_LOW>; + }; +}; + +&ahci { + target-supply = <®_ahci_5v>; + status = "okay"; +}; + +&can0 { + pinctrl-names = "default"; + pinctrl-0 = <&can0_pins_a>; + status = "okay"; +}; + +&codec { + status = "okay"; +}; + +&cpu0 { + cpu-supply = <®_dcdc2>; +}; + +&de { + status = "okay"; +}; + +&ehci0 { + status = "okay"; +}; + +&ehci1 { + status = "okay"; +}; + +&gmac { + pinctrl-names = "default"; + pinctrl-0 = <&gmac_pins_rgmii_a>; + phy = <&phy3>; + phy-mode = "rgmii"; + phy-supply = <®_vcc3v3>; + + snps,reset-gpio = <&pio 0 17 GPIO_ACTIVE_HIGH>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 1000000>; + status = "okay"; + + phy3: ethernet-phy@3 { + reg = <3>; + }; +}; + +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; + + axp209: pmic@34 { + reg = <0x34>; + interrupt-parent = <&nmi_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; + }; +}; + +/* Exposed to UEXT1 */ +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins_a>; + status = "okay"; + + eeprom: eeprom@50 { + compatible = "atmel,24c16"; + reg = <0x50>; + pagesize = <16>; + }; +}; + +/* Exposed to UEXT2 */ +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins_a>; + status = "okay"; +}; + +&ir0 { + pinctrl-names = "default"; + pinctrl-0 = <&ir0_rx_pins_a>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins_a>; + vmmc-supply = <®_vcc3v3>; + bus-width = <4>; + cd-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; + cd-inverted; + status = "okay"; +}; + +&mmc3 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins_a>; + vmmc-supply = <®_vcc3v3>; + mmc-pwrseq = <&rtl_pwrseq>; + bus-width = <4>; + non-removable; + status = "okay"; + + rtl8723bs: sdio_wifi@1 { + reg = <1>; + }; +}; + +&ohci0 { + status = "okay"; +}; + +&ohci1 { + status = "okay"; +}; + +&otg_sram { + status = "okay"; +}; + +&pio { + bt_uart_pins: bt_uart_pins@0 { + pins = "PG6", "PG7", "PG8"; + function = "uart3"; + }; +}; + +#include "axp209.dtsi" + +&ac_power_supply { + status = "okay"; +}; + +&battery_power_supply { + status = "okay"; +}; + +®_ahci_5v { + gpio = <&pio 2 3 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +®_dcdc2 { + regulator-always-on; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1400000>; + regulator-name = "vdd-cpu"; +}; + +®_dcdc3 { + regulator-always-on; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1400000>; + regulator-name = "vdd-int-dll"; +}; + +®_ldo1 { + regulator-always-on; + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1300000>; + regulator-name = "vdd-rtc"; +}; + +®_ldo2 { + regulator-always-on; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-name = "avcc"; +}; + +®_ldo4 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-pg"; +}; + +®_usb0_vbus { + gpio = <&pio 2 17 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +®_usb1_vbus { + status = "okay"; +}; + +®_usb2_vbus { + status = "okay"; +}; + +/* Exposed to UEXT1 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_pins_a>, + <&spi1_cs0_pins_a>; + status = "okay"; +}; + +/* Exposed to UEXT2 */ +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_pins_a>, + <&spi2_cs0_pins_a>; + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins_a>; + status = "okay"; +}; + +/* Used for RTL8723BS bluetooth */ +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&bt_uart_pins>; + status = "okay"; +}; + +/* Exposed to UEXT1 */ +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&uart4_pins_a>; + status = "okay"; +}; + +/* Exposed to UEXT2 */ +&uart7 { + pinctrl-names = "default"; + pinctrl-0 = <&uart7_pins_a>; + status = "okay"; +}; + +&usb_otg { + dr_mode = "otg"; + status = "okay"; +}; + +&usb_power_supply { + status = "okay"; +}; + +&usbphy { + usb0_id_det-gpio = <&pio 7 4 GPIO_ACTIVE_HIGH>; /* PH4 */ + usb0_vbus_det-gpio = <&pio 7 5 GPIO_ACTIVE_HIGH>; /* PH5 */ + usb0_vbus_power-supply = <&usb_power_supply>; + usb0_vbus-supply = <®_usb0_vbus>; + usb1_vbus-supply = <®_usb1_vbus>; + usb2_vbus-supply = <®_usb2_vbus>; + status = "okay"; +}; -- cgit v1.3-14-g43fede From 8edf68ef0dbd5829cf839245a5daebbced5861ca Mon Sep 17 00:00:00 2001 From: Stefan Mavrodiev Date: Mon, 29 Jan 2018 15:56:40 +0200 Subject: ARM: dts: sunxi: Add Olimex A20-SOM204-EVB-eMMC board A20-SOM204 board has option with onboard 16GB eMMC. The chip is wired to MMC2 slot. Signed-off-by: Stefan Mavrodiev Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/Makefile | 1 + .../boot/dts/sun7i-a20-olimex-som204-evb-emmc.dts | 36 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 arch/arm/boot/dts/sun7i-a20-olimex-som204-evb-emmc.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index aa3d28e7887c..a48dc145d54f 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -943,6 +943,7 @@ dtb-$(CONFIG_MACH_SUN7I) += \ sun7i-a20-mk808c.dtb \ sun7i-a20-olimex-som-evb.dtb \ sun7i-a20-olimex-som204-evb.dtb \ + sun7i-a20-olimex-som204-evb-emmc.dtb \ sun7i-a20-olinuxino-lime.dtb \ sun7i-a20-olinuxino-lime2.dtb \ sun7i-a20-olinuxino-lime2-emmc.dtb \ diff --git a/arch/arm/boot/dts/sun7i-a20-olimex-som204-evb-emmc.dts b/arch/arm/boot/dts/sun7i-a20-olimex-som204-evb-emmc.dts new file mode 100644 index 000000000000..c56620a8fb20 --- /dev/null +++ b/arch/arm/boot/dts/sun7i-a20-olimex-som204-evb-emmc.dts @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree Source for A20-SOM204-EVB-eMMC Board + * + * Copyright (C) 2018 Olimex Ltd. + * Author: Stefan Mavrodiev + */ + +/dts-v1/; +#include "sun7i-a20-olimex-som204-evb.dts" + +/ { + model = "Olimex A20-SOM204-EVB-eMMC"; + compatible = "olimex,a20-olimex-som204-evb-emmc", "allwinner,sun7i-a20"; + + mmc2_pwrseq: mmc2_pwrseq { + compatible = "mmc-pwrseq-emmc"; + reset-gpios = <&pio 2 16 GPIO_ACTIVE_LOW>; + }; +}; + +&mmc2 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc2_pins_a>; + vmmc-supply = <®_vcc3v3>; + mmc-pwrseq = <&mmc2_pwrseq>; + bus-width = <4>; + non-removable; + status = "okay"; + + emmc: emmc@0 { + reg = <0>; + compatible = "mmc-card"; + broken-hpi; + }; +}; -- cgit v1.3-14-g43fede From fd25ab90121864f29bef47018872173e50094dd6 Mon Sep 17 00:00:00 2001 From: Giulio Benetti Date: Wed, 31 Jan 2018 20:31:26 +0100 Subject: ARM: dts: sun7i: include correct ccu clock header Including sun4i header instead of sun7i prevents using sun7i specific defines. Substitute header inclusion in sun7i-a20.dtsi using right one. Signed-off-by: Giulio Benetti Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun7i-a20.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index bd0cd3204273..84dbf9cb4eac 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include / { -- cgit v1.3-14-g43fede From 2f9d5a7bbc191d9e10c23d02650544d76d5c36d3 Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Thu, 1 Feb 2018 09:37:00 +0100 Subject: ARM: dts: sun7i: Enable HDMI support on the Olimex EVB Enable the display pipeline and HDMI output on the Olimex A20-SOM-EVB. Signed-off-by: Marcus Cooper Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts b/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts index 64c8ef9a2756..44b9eefdbbf9 100644 --- a/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts +++ b/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts @@ -61,6 +61,17 @@ stdout-path = "serial0:115200n8"; }; + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + leds { compatible = "gpio-leds"; pinctrl-names = "default"; @@ -79,6 +90,10 @@ status = "okay"; }; +&de { + status = "okay"; +}; + &ehci0 { status = "okay"; }; @@ -107,6 +122,16 @@ }; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &i2c0 { pinctrl-names = "default"; pinctrl-0 = <&i2c0_pins_a>; -- cgit v1.3-14-g43fede From 75af6fa42dc68b730e85f51034512c61e52eb0c0 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 19 Jan 2018 15:19:06 +0100 Subject: clk: sunxi-ng: remove select on obsolete SUNXI_CCU_X kconfig name The following symbols: SUNXI_CCU_DIV SUNXI_CCU_MULT SUNXI_CCU_NK SUNXI_CCU_NKM SUNXI_CCU_NM SUNXI_CCU_MP SUNXI_CCU_PHASE were removed with the commit 06e226c7fb23 ("clk: sunxi-ng: Move all clock types to a library") So selecting them is useless. Fixes: c84f5683f6e9 ("clk: sunxi-ng: Add sun4i/sun7i CCU driver") Signed-off-by: Corentin Labbe Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/Kconfig | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index 6427d0ebe2de..33168f94ee39 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -13,13 +13,6 @@ config SUN50I_A64_CCU config SUN4I_A10_CCU bool "Support for the Allwinner A10/A20 CCU" - select SUNXI_CCU_DIV - select SUNXI_CCU_MULT - select SUNXI_CCU_NK - select SUNXI_CCU_NKM - select SUNXI_CCU_NM - select SUNXI_CCU_MP - select SUNXI_CCU_PHASE default MACH_SUN4I default MACH_SUN7I depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST -- cgit v1.3-14-g43fede From 254adb06bfd112fca8a6355dc74a925ec423f984 Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Thu, 1 Feb 2018 09:37:37 +0100 Subject: ARM: dts: sun6i: a31: Enable HDMI support on the Mele I7 The Mele I7 has an HDMI connector wired to the HDMI pins on the SoC. Enable the display pipeline and HDMI output. Signed-off-by: Marcus Cooper Signed-off-by: Maxime Ripard [wens@csie.org: Added "dts" prefix] Signed-off-by: Chen-Yu Tsai --- arch/arm/boot/dts/sun6i-a31-i7.dts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/arch/arm/boot/dts/sun6i-a31-i7.dts b/arch/arm/boot/dts/sun6i-a31-i7.dts index 010a84c7c012..a3b70173c527 100644 --- a/arch/arm/boot/dts/sun6i-a31-i7.dts +++ b/arch/arm/boot/dts/sun6i-a31-i7.dts @@ -58,6 +58,17 @@ stdout-path = "serial0:115200n8"; }; + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + leds { compatible = "gpio-leds"; pinctrl-names = "default"; @@ -93,6 +104,10 @@ status = "okay"; }; +&de { + status = "okay"; +}; + &ehci0 { status = "okay"; }; @@ -113,6 +128,16 @@ }; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &ir { pinctrl-names = "default"; pinctrl-0 = <&ir_pins_a>; @@ -161,6 +186,10 @@ status = "okay"; }; +&tcon0 { + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins_a>; -- cgit v1.3-14-g43fede From 52b5f53f4b60fe645f3203e2e85df2189275def3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 9 Feb 2018 14:28:04 +0530 Subject: ARM: dts: sun[4-7]i: Remove "cooling-{min|max}-level" for CPU nodes The "cooling-min-level" and "cooling-max-level" properties are not parsed by any part of the kernel currently and the max cooling state of a CPU cooling device is found by referring to the cpufreq table instead. Remove the unused properties from the CPU nodes. Signed-off-by: Viresh Kumar Signed-off-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts | 1 - arch/arm/boot/dts/sun4i-a10.dtsi | 2 -- arch/arm/boot/dts/sun5i-a13.dtsi | 2 -- arch/arm/boot/dts/sun6i-a31.dtsi | 2 -- arch/arm/boot/dts/sun7i-a20.dtsi | 2 -- 5 files changed, 9 deletions(-) diff --git a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts index 49247fbe6acd..e3d1d0f63caf 100644 --- a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts +++ b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts @@ -97,7 +97,6 @@ 864000 1300000 624000 1250000 >; - cooling-max-level = <2>; }; &de { diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi index 4f2f2eea0755..77e8436beed4 100644 --- a/arch/arm/boot/dts/sun4i-a10.dtsi +++ b/arch/arm/boot/dts/sun4i-a10.dtsi @@ -123,8 +123,6 @@ 624000 1250000 >; #cooling-cells = <2>; - cooling-min-level = <0>; - cooling-max-level = <3>; }; }; diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi index 4e830f5cb7f1..b1d827765530 100644 --- a/arch/arm/boot/dts/sun5i-a13.dtsi +++ b/arch/arm/boot/dts/sun5i-a13.dtsi @@ -116,8 +116,6 @@ 432000 1200000 >; #cooling-cells = <2>; - cooling-min-level = <0>; - cooling-max-level = <5>; }; &pio { diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index 72d3fe44ecaf..c72992556a86 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -113,8 +113,6 @@ 480000 1000000 >; #cooling-cells = <2>; - cooling-min-level = <0>; - cooling-max-level = <3>; }; cpu@1 { diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 84dbf9cb4eac..1d95885d9cf9 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -116,8 +116,6 @@ 144000 1000000 >; #cooling-cells = <2>; - cooling-min-level = <0>; - cooling-max-level = <6>; }; cpu@1 { -- cgit v1.3-14-g43fede From 9ae65654996e06bf84a765388a62659a61aa72d8 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 3 Jan 2018 14:28:44 +0800 Subject: ARM: dts: sun8i: a83t: bananapi-m3: Add LED device nodes The Bananapi M3 has two controllable LEDs, blue and green, that are tied to the PMIC's two GPIO pins. Acked-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts index 6550bf0e594b..78fe61d97790 100644 --- a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts +++ b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts @@ -60,6 +60,20 @@ stdout-path = "serial0:115200n8"; }; + leds { + compatible = "gpio-leds"; + + blue { + label = "bananapi-m3:blue:usr"; + gpios = <&axp_gpio 1 GPIO_ACTIVE_HIGH>; + }; + + green { + label = "bananapi-m3:green:usr"; + gpios = <&axp_gpio 0 GPIO_ACTIVE_HIGH>; + }; + }; + reg_usb1_vbus: reg-usb1-vbus { compatible = "regulator-fixed"; regulator-name = "usb1-vbus"; -- cgit v1.3-14-g43fede From 5d16b9e33804076b8ead97a0f4cce7446db5b267 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Tue, 13 Feb 2018 12:53:43 +0100 Subject: ARM: dts: STi: Update sound related nodes location Move sound node outside soc node and sti_sasg_codec node as child of syscfg_core node. This allows to fix the following warning when compiling dtb with W=1 option: arch/arm/boot/dts/stih407-b2120.dtb: Warning (unit_address_vs_reg): Node /soc/sound/simple-audio-card,dai-link@0 has a unit name, but no reg property arch/arm/boot/dts/stih407-b2120.dtb: Warning (unit_address_vs_reg): Node /soc/sound/simple-audio-card,dai-link@1 has a unit name, but no reg property arch/arm/boot/dts/stih407-b2120.dtb: Warning (unit_address_vs_reg): Node /soc/sound/simple-audio-card,dai-link@2 has a unit name, but no reg property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/sti-sasg-codec missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (unit_address_vs_reg): Node /soc/sound/simple-audio-card,dai-link@0 has a unit name, but no reg property arch/arm/boot/dts/stih410-b2120.dtb: Warning (unit_address_vs_reg): Node /soc/sound/simple-audio-card,dai-link@1 has a unit name, but no reg property arch/arm/boot/dts/stih410-b2120.dtb: Warning (unit_address_vs_reg): Node /soc/sound/simple-audio-card,dai-link@2 has a unit name, but no reg property arch/arm/boot/dts/stih407-b2120.dtb: Warning (simple_bus_reg): Node /soc/sound missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /soc/sti-sasg-codec missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (unit_address_vs_reg): Node /soc/sound/simple-audio-card,dai-link@0 has a unit name, but no reg property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/sti-sasg-codec missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /soc/sound missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /soc/sti-sasg-codec missing or empty reg/ranges property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407-family.dtsi | 14 ++--- arch/arm/boot/dts/stih410-b2260.dts | 38 +++++++------- arch/arm/boot/dts/stihxxx-b2120.dtsi | 98 ++++++++++++++++++----------------- 3 files changed, 77 insertions(+), 73 deletions(-) diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi index 5df827b00eb6..f7362c31de29 100644 --- a/arch/arm/boot/dts/stih407-family.dtsi +++ b/arch/arm/boot/dts/stih407-family.dtsi @@ -178,6 +178,13 @@ syscfg_core: core-syscfg@92b0000 { compatible = "st,stih407-core-syscfg", "syscon"; reg = <0x92b0000 0x1000>; + + sti_sasg_codec: sti-sasg-codec { + compatible = "st,stih407-sas-codec"; + #sound-dai-cells = <1>; + status = "disabled"; + st,syscfg = <&syscfg_core>; + }; }; syscfg_lpm: lpm-syscfg@94b5100 { @@ -894,13 +901,6 @@ status = "disabled"; }; - sti_sasg_codec: sti-sasg-codec { - compatible = "st,stih407-sas-codec"; - #sound-dai-cells = <1>; - status = "disabled"; - st,syscfg = <&syscfg_core>; - }; - sti_uni_player0: sti-uni-player@8d80000 { compatible = "st,stih407-uni-player-hdmi"; #sound-dai-cells = <0>; diff --git a/arch/arm/boot/dts/stih410-b2260.dts b/arch/arm/boot/dts/stih410-b2260.dts index 8bcd58118dba..5ccc6bb050df 100644 --- a/arch/arm/boot/dts/stih410-b2260.dts +++ b/arch/arm/boot/dts/stih410-b2260.dts @@ -57,6 +57,25 @@ }; }; + sound: sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "STI-B2260"; + status = "okay"; + + simple-audio-card,dai-link0 { + /* DAC */ + format = "i2s"; + mclk-fs = <128>; + cpu { + sound-dai = <&sti_uni_player0>; + }; + + codec { + sound-dai = <&sti_hdmi>; + }; + }; + }; + soc { /* Low speed expansion connector */ uart0: serial@9830000 { @@ -181,25 +200,6 @@ status = "okay"; }; - sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "STI-B2260"; - status = "okay"; - - simple-audio-card,dai-link@0 { - /* DAC */ - format = "i2s"; - mclk-fs = <128>; - cpu { - sound-dai = <&sti_uni_player0>; - }; - - codec { - sound-dai = <&sti_hdmi>; - }; - }; - }; - miphy28lp_phy: miphy28lp@0 { phy_port1: port@9b2a000 { diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi index 66c1c6a5eb76..c67edb1a8121 100644 --- a/arch/arm/boot/dts/stihxxx-b2120.dtsi +++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi @@ -23,6 +23,52 @@ }; }; + sound: sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "STI-B2120"; + status = "okay"; + + simple-audio-card,dai-link0 { + /* HDMI */ + format = "i2s"; + mclk-fs = <128>; + cpu { + sound-dai = <&sti_uni_player0>; + }; + + codec { + sound-dai = <&sti_hdmi>; + }; + }; + + simple-audio-card,dai-link1 { + /* DAC */ + format = "i2s"; + mclk-fs = <256>; + frame-inversion = <1>; + cpu { + sound-dai = <&sti_uni_player2>; + }; + + codec { + sound-dai = <&sti_sasg_codec 1>; + }; + }; + + simple-audio-card,dai-link2 { + /* SPDIF */ + format = "left_j"; + mclk-fs = <128>; + cpu { + sound-dai = <&sti_uni_player3>; + }; + + codec { + sound-dai = <&sti_sasg_codec 0>; + }; + }; + }; + soc { sbc_serial0: serial@9530000 { status = "okay"; @@ -147,53 +193,11 @@ status = "okay"; }; - sti_sasg_codec: sti-sasg-codec { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_spdif_out>; - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "STI-B2120"; - status = "okay"; - - simple-audio-card,dai-link@0 { - /* HDMI */ - format = "i2s"; - mclk-fs = <128>; - cpu { - sound-dai = <&sti_uni_player0>; - }; - - codec { - sound-dai = <&sti_hdmi>; - }; - }; - simple-audio-card,dai-link@1 { - /* DAC */ - format = "i2s"; - mclk-fs = <256>; - frame-inversion = <1>; - cpu { - sound-dai = <&sti_uni_player2>; - }; - - codec { - sound-dai = <&sti_sasg_codec 1>; - }; - }; - simple-audio-card,dai-link@2 { - /* SPDIF */ - format = "left_j"; - mclk-fs = <128>; - cpu { - sound-dai = <&sti_uni_player3>; - }; - - codec { - sound-dai = <&sti_sasg_codec 0>; - }; + syscfg_core: core-syscfg@92b0000 { + sti_sasg_codec: sti-sasg-codec { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif_out>; }; }; }; -- cgit v1.3-14-g43fede From cb10ca8107c2ccc9dc58858471652de67db1fa0e Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Tue, 13 Feb 2018 12:59:51 +0100 Subject: ARM: dts: STi: Update clocks node location Move: _ arm_periph_clk node as child of clockgen-a9@92b0000 node _ clk_m_a9_ext2f_div2 node as child of clk_s_c0_flexgen node _ clk-tmdsout-hdmi node outiside soc node This allows to fix the following warnings when compiling dtb with W=1 option : arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /clocks/clk-m-a9-periphs missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /clocks/clk-m-a9-ext2f-div2s missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /clocks/clk-tmdsout-hdmi missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /clocks/clk-tmdsout-hdmi missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /clocks/clk-m-a9-periphs missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /clocks/clk-m-a9-ext2f-div2s missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /clocks/clk-tmdsout-hdmi missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /clocks/clk-tmdsout-hdmi missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /clocks/clk-m-a9-periphs missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /clocks/clk-m-a9-ext2f-div2s missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /clocks/clk-tmdsout-hdmi missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /clocks/clk-tmdsout-hdmi missing or empty reg/ranges property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407-clock.dtsi | 79 +++++++++++++++++----------------- arch/arm/boot/dts/stih410-clock.dtsi | 83 ++++++++++++++++++------------------ arch/arm/boot/dts/stih418-clock.dtsi | 82 +++++++++++++++++------------------ 3 files changed, 122 insertions(+), 122 deletions(-) diff --git a/arch/arm/boot/dts/stih407-clock.dtsi b/arch/arm/boot/dts/stih407-clock.dtsi index b882dcf3a649..084c02926f33 100644 --- a/arch/arm/boot/dts/stih407-clock.dtsi +++ b/arch/arm/boot/dts/stih407-clock.dtsi @@ -7,32 +7,26 @@ */ #include / { + /* + * Fixed 30MHz oscillator inputs to SoC + */ + clk_sysin: clk-sysin { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <30000000>; + }; + + clk_tmdsout_hdmi: clk-tmdsout-hdmi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <0>; + }; + clocks { #address-cells = <1>; #size-cells = <1>; ranges; - /* - * Fixed 30MHz oscillator inputs to SoC - */ - clk_sysin: clk-sysin { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <30000000>; - }; - - /* - * ARM Peripheral clock for timers - */ - arm_periph_clk: clk-m-a9-periphs { - #clock-cells = <0>; - compatible = "fixed-factor-clock"; - - clocks = <&clk_m_a9>; - clock-div = <2>; - clock-mult = <1>; - }; - /* * A9 PLL. */ @@ -62,21 +56,19 @@ <&clockgen_a9_pll 0>, <&clk_s_c0_flexgen 13>, <&clk_m_a9_ext2f_div2>; - }; - - /* - * ARM Peripheral clock for timers - */ - clk_m_a9_ext2f_div2: clk-m-a9-ext2f-div2s { - #clock-cells = <0>; - compatible = "fixed-factor-clock"; - clocks = <&clk_s_c0_flexgen 13>; - clock-output-names = "clk-m-a9-ext2f-div2"; + /* + * ARM Peripheral clock for timers + */ + arm_periph_clk: clk-m-a9-periphs { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; - clock-div = <2>; - clock-mult = <1>; + clocks = <&clk_m_a9>; + clock-div = <2>; + clock-mult = <1>; + }; }; /* @@ -204,6 +196,21 @@ , , ; + + /* + * ARM Peripheral clock for timers + */ + clk_m_a9_ext2f_div2: clk-m-a9-ext2f-div2s { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + + clocks = <&clk_s_c0_flexgen 13>; + + clock-output-names = "clk-m-a9-ext2f-div2"; + + clock-div = <2>; + clock-mult = <1>; + }; }; }; @@ -254,12 +261,6 @@ "clk-s-d2-fs0-ch3"; }; - clk_tmdsout_hdmi: clk-tmdsout-hdmi { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <0>; - }; - clockgen-d2@9106000 { compatible = "st,clkgen-c32"; reg = <0x9106000 0x1000>; diff --git a/arch/arm/boot/dts/stih410-clock.dtsi b/arch/arm/boot/dts/stih410-clock.dtsi index 4df1b2187aa2..b2c814f1261a 100644 --- a/arch/arm/boot/dts/stih410-clock.dtsi +++ b/arch/arm/boot/dts/stih410-clock.dtsi @@ -7,6 +7,22 @@ */ #include / { + /* + * Fixed 30MHz oscillator inputs to SoC + */ + clk_sysin: clk-sysin { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <30000000>; + clock-output-names = "CLK_SYSIN"; + }; + + clk_tmdsout_hdmi: clk-tmdsout-hdmi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <0>; + }; + clocks { #address-cells = <1>; #size-cells = <1>; @@ -14,27 +30,6 @@ compatible = "st,stih410-clk", "simple-bus"; - /* - * Fixed 30MHz oscillator inputs to SoC - */ - clk_sysin: clk-sysin { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <30000000>; - clock-output-names = "CLK_SYSIN"; - }; - - /* - * ARM Peripheral clock for timers - */ - arm_periph_clk: clk-m-a9-periphs { - #clock-cells = <0>; - compatible = "fixed-factor-clock"; - clocks = <&clk_m_a9>; - clock-div = <2>; - clock-mult = <1>; - }; - /* * A9 PLL. */ @@ -64,21 +59,16 @@ <&clockgen_a9_pll 0>, <&clk_s_c0_flexgen 13>, <&clk_m_a9_ext2f_div2>; - }; - - /* - * ARM Peripheral clock for timers - */ - clk_m_a9_ext2f_div2: clk-m-a9-ext2f-div2s { - #clock-cells = <0>; - compatible = "fixed-factor-clock"; - - clocks = <&clk_s_c0_flexgen 13>; - - clock-output-names = "clk-m-a9-ext2f-div2"; - - clock-div = <2>; - clock-mult = <1>; + /* + * ARM Peripheral clock for timers + */ + arm_periph_clk: clk-m-a9-periphs { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + clocks = <&clk_m_a9>; + clock-div = <2>; + clock-mult = <1>; + }; }; /* @@ -214,6 +204,21 @@ , , ; + + /* + * ARM Peripheral clock for timers + */ + clk_m_a9_ext2f_div2: clk-m-a9-ext2f-div2s { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + + clocks = <&clk_s_c0_flexgen 13>; + + clock-output-names = "clk-m-a9-ext2f-div2"; + + clock-div = <2>; + clock-mult = <1>; + }; }; }; @@ -266,12 +271,6 @@ "clk-s-d2-fs0-ch3"; }; - clk_tmdsout_hdmi: clk-tmdsout-hdmi { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <0>; - }; - clockgen-d2@9106000 { compatible = "st,clkgen-c32"; reg = <0x9106000 0x1000>; diff --git a/arch/arm/boot/dts/stih418-clock.dtsi b/arch/arm/boot/dts/stih418-clock.dtsi index e68bf28bd038..a192608ff689 100644 --- a/arch/arm/boot/dts/stih418-clock.dtsi +++ b/arch/arm/boot/dts/stih418-clock.dtsi @@ -7,6 +7,22 @@ */ #include / { + /* + * Fixed 30MHz oscillator inputs to SoC + */ + clk_sysin: clk-sysin { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <30000000>; + clock-output-names = "CLK_SYSIN"; + }; + + clk_tmdsout_hdmi: clk-tmdsout-hdmi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <0>; + }; + clocks { #address-cells = <1>; #size-cells = <1>; @@ -14,27 +30,6 @@ compatible = "st,stih418-clk", "simple-bus"; - /* - * Fixed 30MHz oscillator inputs to SoC - */ - clk_sysin: clk-sysin { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <30000000>; - clock-output-names = "CLK_SYSIN"; - }; - - /* - * ARM Peripheral clock for timers - */ - arm_periph_clk: clk-m-a9-periphs { - #clock-cells = <0>; - compatible = "fixed-factor-clock"; - clocks = <&clk_m_a9>; - clock-div = <2>; - clock-mult = <1>; - }; - /* * A9 PLL. */ @@ -64,21 +59,17 @@ <&clockgen_a9_pll 0>, <&clk_s_c0_flexgen 13>, <&clk_m_a9_ext2f_div2>; - }; - /* - * ARM Peripheral clock for timers - */ - clk_m_a9_ext2f_div2: clk-m-a9-ext2f-div2s { - #clock-cells = <0>; - compatible = "fixed-factor-clock"; - - clocks = <&clk_s_c0_flexgen 13>; - - clock-output-names = "clk-m-a9-ext2f-div2"; - - clock-div = <2>; - clock-mult = <1>; + /* + * ARM Peripheral clock for timers + */ + arm_periph_clk: clk-m-a9-periphs { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + clocks = <&clk_m_a9>; + clock-div = <2>; + clock-mult = <1>; + }; }; /* @@ -207,6 +198,21 @@ "clk-proc-mixer", "clk-proc-sc", "clk-avsp-hevc"; + + /* + * ARM Peripheral clock for timers + */ + clk_m_a9_ext2f_div2: clk-m-a9-ext2f-div2s { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + + clocks = <&clk_s_c0_flexgen 13>; + + clock-output-names = "clk-m-a9-ext2f-div2"; + + clock-div = <2>; + clock-mult = <1>; + }; }; }; @@ -259,12 +265,6 @@ "clk-s-d2-fs0-ch3"; }; - clk_tmdsout_hdmi: clk-tmdsout-hdmi { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <0>; - }; - clockgen-d2@9106000 { compatible = "st,clkgen-c32"; reg = <0x9106000 0x1000>; -- cgit v1.3-14-g43fede From 0e04ce02e6a2ac30d21294d8bb2aeceb8489e52b Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Mon, 12 Feb 2018 14:58:01 +0100 Subject: ARM: dts: STi: Remove unused clk_ext2f_a9 clock This clock node is unused, remove it. This allows to fix the following warnings when compiling dtb with W=1 option : arch/arm/boot/dts/stih410-b2120.dtb: Warning (simple_bus_reg): Node /clocks/clockgen-c0 missing or empty reg/ranges property arch/arm/boot/dts/stih410-b2260.dtb: Warning (simple_bus_reg): Node /clocks/clockgen-c0 missing or empty reg/ranges property arch/arm/boot/dts/stih418-b2199.dtb: Warning (simple_bus_reg): Node /clocks/clockgen-c0 missing or empty reg/ranges property Signed-off-by: Patrice Chotard --- arch/arm/boot/dts/stih407-clock.dtsi | 11 ----------- arch/arm/boot/dts/stih410-clock.dtsi | 11 ----------- arch/arm/boot/dts/stih418-clock.dtsi | 11 ----------- 3 files changed, 33 deletions(-) diff --git a/arch/arm/boot/dts/stih407-clock.dtsi b/arch/arm/boot/dts/stih407-clock.dtsi index 084c02926f33..ea7833489832 100644 --- a/arch/arm/boot/dts/stih407-clock.dtsi +++ b/arch/arm/boot/dts/stih407-clock.dtsi @@ -71,17 +71,6 @@ }; }; - /* - * Bootloader initialized system infrastructure clock for - * serial devices. - */ - clk_ext2f_a9: clockgen-c0 { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <200000000>; - clock-output-names = "clk-s-icn-reg-0"; - }; - clockgen-a@90ff000 { compatible = "st,clkgen-c32"; reg = <0x90ff000 0x1000>; diff --git a/arch/arm/boot/dts/stih410-clock.dtsi b/arch/arm/boot/dts/stih410-clock.dtsi index b2c814f1261a..5f11d09cb030 100644 --- a/arch/arm/boot/dts/stih410-clock.dtsi +++ b/arch/arm/boot/dts/stih410-clock.dtsi @@ -71,17 +71,6 @@ }; }; - /* - * Bootloader initialized system infrastructure clock for - * serial devices. - */ - clk_ext2f_a9: clockgen-c0 { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <200000000>; - clock-output-names = "clk-s-icn-reg-0"; - }; - clockgen-a@90ff000 { compatible = "st,clkgen-c32"; reg = <0x90ff000 0x1000>; diff --git a/arch/arm/boot/dts/stih418-clock.dtsi b/arch/arm/boot/dts/stih418-clock.dtsi index a192608ff689..13fb8db52fc1 100644 --- a/arch/arm/boot/dts/stih418-clock.dtsi +++ b/arch/arm/boot/dts/stih418-clock.dtsi @@ -72,17 +72,6 @@ }; }; - /* - * Bootloader initialized system infrastructure clock for - * serial devices. - */ - clk_ext2f_a9: clockgen-c0 { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <200000000>; - clock-output-names = "clk-s-icn-reg-0"; - }; - clockgen-a@90ff000 { compatible = "st,clkgen-c32"; reg = <0x90ff000 0x1000>; -- cgit v1.3-14-g43fede From 27790398c2aed917828dc3c6f81240d57f1584c9 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Sun, 21 Jan 2018 03:28:54 -0600 Subject: iommu/amd: Use raw locks on atomic context paths Several functions in this driver are called from atomic context, and thus raw locks must be used in order to be safe on PREEMPT_RT. This includes paths that must wait for command completion, which is a potential PREEMPT_RT latency concern but not easily avoidable. Signed-off-by: Scott Wood Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 30 +++++++++++++++--------------- drivers/iommu/amd_iommu_init.c | 2 +- drivers/iommu/amd_iommu_types.h | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 74788fdeb773..d227f761cc21 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -1056,9 +1056,9 @@ static int iommu_queue_command_sync(struct amd_iommu *iommu, unsigned long flags; int ret; - spin_lock_irqsave(&iommu->lock, flags); + raw_spin_lock_irqsave(&iommu->lock, flags); ret = __iommu_queue_command_sync(iommu, cmd, sync); - spin_unlock_irqrestore(&iommu->lock, flags); + raw_spin_unlock_irqrestore(&iommu->lock, flags); return ret; } @@ -1084,7 +1084,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu) build_completion_wait(&cmd, (u64)&iommu->cmd_sem); - spin_lock_irqsave(&iommu->lock, flags); + raw_spin_lock_irqsave(&iommu->lock, flags); iommu->cmd_sem = 0; @@ -1095,7 +1095,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu) ret = wait_on_sem(&iommu->cmd_sem); out_unlock: - spin_unlock_irqrestore(&iommu->lock, flags); + raw_spin_unlock_irqrestore(&iommu->lock, flags); return ret; } @@ -3627,7 +3627,7 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic) goto out_unlock; /* Initialize table spin-lock */ - spin_lock_init(&table->lock); + raw_spin_lock_init(&table->lock); if (ioapic) /* Keep the first 32 indexes free for IOAPIC interrupts */ @@ -3689,7 +3689,7 @@ static int alloc_irq_index(u16 devid, int count, bool align) if (align) alignment = roundup_pow_of_two(count); - spin_lock_irqsave(&table->lock, flags); + raw_spin_lock_irqsave(&table->lock, flags); /* Scan table for free entries */ for (index = ALIGN(table->min_index, alignment), c = 0; @@ -3716,7 +3716,7 @@ static int alloc_irq_index(u16 devid, int count, bool align) index = -ENOSPC; out: - spin_unlock_irqrestore(&table->lock, flags); + raw_spin_unlock_irqrestore(&table->lock, flags); return index; } @@ -3737,7 +3737,7 @@ static int modify_irte_ga(u16 devid, int index, struct irte_ga *irte, if (!table) return -ENOMEM; - spin_lock_irqsave(&table->lock, flags); + raw_spin_lock_irqsave(&table->lock, flags); entry = (struct irte_ga *)table->table; entry = &entry[index]; @@ -3748,7 +3748,7 @@ static int modify_irte_ga(u16 devid, int index, struct irte_ga *irte, if (data) data->ref = entry; - spin_unlock_irqrestore(&table->lock, flags); + raw_spin_unlock_irqrestore(&table->lock, flags); iommu_flush_irt(iommu, devid); iommu_completion_wait(iommu); @@ -3770,9 +3770,9 @@ static int modify_irte(u16 devid, int index, union irte *irte) if (!table) return -ENOMEM; - spin_lock_irqsave(&table->lock, flags); + raw_spin_lock_irqsave(&table->lock, flags); table->table[index] = irte->val; - spin_unlock_irqrestore(&table->lock, flags); + raw_spin_unlock_irqrestore(&table->lock, flags); iommu_flush_irt(iommu, devid); iommu_completion_wait(iommu); @@ -3794,9 +3794,9 @@ static void free_irte(u16 devid, int index) if (!table) return; - spin_lock_irqsave(&table->lock, flags); + raw_spin_lock_irqsave(&table->lock, flags); iommu->irte_ops->clear_allocated(table, index); - spin_unlock_irqrestore(&table->lock, flags); + raw_spin_unlock_irqrestore(&table->lock, flags); iommu_flush_irt(iommu, devid); iommu_completion_wait(iommu); @@ -4397,7 +4397,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data) if (!irt) return -ENODEV; - spin_lock_irqsave(&irt->lock, flags); + raw_spin_lock_irqsave(&irt->lock, flags); if (ref->lo.fields_vapic.guest_mode) { if (cpu >= 0) @@ -4406,7 +4406,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data) barrier(); } - spin_unlock_irqrestore(&irt->lock, flags); + raw_spin_unlock_irqrestore(&irt->lock, flags); iommu_flush_irt(iommu, devid); iommu_completion_wait(iommu); diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 4e4a615bf13f..904c575d1677 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -1474,7 +1474,7 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h) { int ret; - spin_lock_init(&iommu->lock); + raw_spin_lock_init(&iommu->lock); /* Add IOMMU to internal data structures */ list_add_tail(&iommu->list, &amd_iommu_list); diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index 6a877ebd058b..da886b0095aa 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -408,7 +408,7 @@ extern bool amd_iommu_iotlb_sup; #define IRQ_TABLE_ALIGNMENT 128 struct irq_remap_table { - spinlock_t lock; + raw_spinlock_t lock; unsigned min_index; u32 *table; }; @@ -490,7 +490,7 @@ struct amd_iommu { int index; /* locks the accesses to the hardware */ - spinlock_t lock; + raw_spinlock_t lock; /* Pointer to PCI device of this IOMMU */ struct pci_dev *dev; -- cgit v1.3-14-g43fede From 01ee04badefd296eb7a4430497373be9b7b16783 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Sun, 28 Jan 2018 14:22:19 -0600 Subject: iommu/amd: Don't use dev_data in irte_ga_set_affinity() search_dev_data() acquires a non-raw lock, which can't be done from atomic context on PREEMPT_RT. There is no need to look at dev_data because guest_mode should never be set if use_vapic is not set. Signed-off-by: Scott Wood Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index d227f761cc21..d666246ac30b 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3877,10 +3877,8 @@ static void irte_ga_set_affinity(void *entry, u16 devid, u16 index, u8 vector, u32 dest_apicid) { struct irte_ga *irte = (struct irte_ga *) entry; - struct iommu_dev_data *dev_data = search_dev_data(devid); - if (!dev_data || !dev_data->use_vapic || - !irte->lo.fields_remap.guest_mode) { + if (!irte->lo.fields_remap.guest_mode) { irte->hi.fields.vector = vector; irte->lo.fields_remap.destination = dest_apicid; modify_irte_ga(devid, index, irte, NULL); -- cgit v1.3-14-g43fede From 6d7cf02a8660cf5570b20017df4b14a2ddbd1694 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 24 Jan 2018 14:22:09 +0000 Subject: iommu/exynos: Use generic group callback Since iommu_group_get_for_dev() already tries iommu_group_get() and will not call ops->device_group if the group is already non-NULL, the check in get_device_iommu_group() is always redundant and it reduces to a duplicate of the generic version; let's just use that one instead. Signed-off-by: Robin Murphy Tested-by: Marek Szyprowski Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 2138102ef611..210715f08cf6 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1238,17 +1238,6 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain, return phys; } -static struct iommu_group *get_device_iommu_group(struct device *dev) -{ - struct iommu_group *group; - - group = iommu_group_get(dev); - if (!group) - group = iommu_group_alloc(); - - return group; -} - static int exynos_iommu_add_device(struct device *dev) { struct exynos_iommu_owner *owner = dev->archdata.iommu; @@ -1344,7 +1333,7 @@ static const struct iommu_ops exynos_iommu_ops = { .unmap = exynos_iommu_unmap, .map_sg = default_iommu_map_sg, .iova_to_phys = exynos_iommu_iova_to_phys, - .device_group = get_device_iommu_group, + .device_group = generic_device_group, .add_device = exynos_iommu_add_device, .remove_device = exynos_iommu_remove_device, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, -- cgit v1.3-14-g43fede From f3e827d73ed454053d2ce6a4bf021b5adde3ac54 Mon Sep 17 00:00:00 2001 From: Yong Wu Date: Fri, 26 Jan 2018 15:11:28 +0800 Subject: iommu/mediatek: Move attach_device after iommu-group is ready for M4Uv1 In the commit 05f80300dc8b ("iommu: Finish making iommu_group support mandatory"), the iommu framework has supposed all the iommu drivers have their owner iommu-group, it get rid of the FIXME workarounds while the group is NULL. But the flow of Mediatek M4U gen1 looks a bit trick that it will hang at this case: ========================================== Unable to handle kernel NULL pointer dereference at virtual address 00000030 pgd = c0004000 [00000030] *pgd=00000000 PC is at mutex_lock+0x28/0x54 LR is at iommu_attach_device+0xa4/0xd4 pc : [] lr : [] psr: 60000013 sp : df0edbb8 ip : df0edbc8 fp : df0edbc4 r10: c114da14 r9 : df2a3e40 r8 : 00000003 r7 : df27a210 r6 : df2a90c4 r5 : 00000030 r4 : 00000000 r3 : df0f8000 r2 : fffff000 r1 : df29c610 r0 : 00000030 Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none xxx (mutex_lock) from [] (iommu_attach_device+0xa4/0xd4) (iommu_attach_device) from [] (__arm_iommu_attach_device+0x28/0x90) (__arm_iommu_attach_device) from [] (arm_iommu_attach_device+0x1c/0x30) (arm_iommu_attach_device) from [] (mtk_iommu_add_device+0xfc/0x214) (mtk_iommu_add_device) from [] (add_iommu_group+0x3c/0x68) (add_iommu_group) from [] (bus_for_each_dev+0x78/0xac) (bus_for_each_dev) from [] (bus_set_iommu+0xb0/0xec) (bus_set_iommu) from [] (mtk_iommu_probe+0x328/0x368) (mtk_iommu_probe) from [] (platform_drv_probe+0x5c/0xc0) (platform_drv_probe) from [] (driver_probe_device+0x2f4/0x4d8) (driver_probe_device) from [] (__driver_attach+0x10c/0x128) (__driver_attach) from [] (bus_for_each_dev+0x78/0xac) (bus_for_each_dev) from [] (driver_attach+0x2c/0x30) (driver_attach) from [] (bus_add_driver+0x1e0/0x278) (bus_add_driver) from [] (driver_register+0x88/0x108) (driver_register) from [] (__platform_driver_register+0x50/0x58) (__platform_driver_register) from [] (m4u_init+0x24/0x28) (m4u_init) from [] (do_one_initcall+0xf0/0x17c) ========================= The root cause is that the device's iommu-group is NULL while arm_iommu_attach_device is called. This patch prepare a new iommu-group for the iommu consumer devices to fix this issue. CC: Robin Murphy CC: Honghui Zhang Fixes: 05f80300dc8b ("iommu: Finish making iommu_group support mandatory") Reported-by: Ryder Lee Signed-off-by: Yong Wu Signed-off-by: Joerg Roedel --- drivers/iommu/mtk_iommu_v1.c | 54 +++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c index 542930cd183d..1b1b77594897 100644 --- a/drivers/iommu/mtk_iommu_v1.c +++ b/drivers/iommu/mtk_iommu_v1.c @@ -418,20 +418,12 @@ static int mtk_iommu_create_mapping(struct device *dev, m4udev->archdata.iommu = mtk_mapping; } - ret = arm_iommu_attach_device(dev, mtk_mapping); - if (ret) - goto err_release_mapping; - return 0; - -err_release_mapping: - arm_iommu_release_mapping(mtk_mapping); - m4udev->archdata.iommu = NULL; - return ret; } static int mtk_iommu_add_device(struct device *dev) { + struct dma_iommu_mapping *mtk_mapping; struct of_phandle_args iommu_spec; struct of_phandle_iterator it; struct mtk_iommu_data *data; @@ -452,15 +444,30 @@ static int mtk_iommu_add_device(struct device *dev) if (!dev->iommu_fwspec || dev->iommu_fwspec->ops != &mtk_iommu_ops) return -ENODEV; /* Not a iommu client device */ - data = dev->iommu_fwspec->iommu_priv; - iommu_device_link(&data->iommu, dev); - - group = iommu_group_get_for_dev(dev); + /* + * This is a short-term bodge because the ARM DMA code doesn't + * understand multi-device groups, but we have to call into it + * successfully (and not just rely on a normal IOMMU API attach + * here) in order to set the correct DMA API ops on @dev. + */ + group = iommu_group_alloc(); if (IS_ERR(group)) return PTR_ERR(group); + err = iommu_group_add_device(group, dev); iommu_group_put(group); - return 0; + if (err) + return err; + + data = dev->iommu_fwspec->iommu_priv; + mtk_mapping = data->dev->archdata.iommu; + err = arm_iommu_attach_device(dev, mtk_mapping); + if (err) { + iommu_group_remove_device(dev); + return err; + } + + return iommu_device_link(&data->iommu, dev);; } static void mtk_iommu_remove_device(struct device *dev) @@ -477,24 +484,6 @@ static void mtk_iommu_remove_device(struct device *dev) iommu_fwspec_free(dev); } -static struct iommu_group *mtk_iommu_device_group(struct device *dev) -{ - struct mtk_iommu_data *data = dev->iommu_fwspec->iommu_priv; - - if (!data) - return ERR_PTR(-ENODEV); - - /* All the client devices are in the same m4u iommu-group */ - if (!data->m4u_group) { - data->m4u_group = iommu_group_alloc(); - if (IS_ERR(data->m4u_group)) - dev_err(dev, "Failed to allocate M4U IOMMU group\n"); - } else { - iommu_group_ref_get(data->m4u_group); - } - return data->m4u_group; -} - static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) { u32 regval; @@ -547,7 +536,6 @@ static struct iommu_ops mtk_iommu_ops = { .iova_to_phys = mtk_iommu_iova_to_phys, .add_device = mtk_iommu_add_device, .remove_device = mtk_iommu_remove_device, - .device_group = mtk_iommu_device_group, .pgsize_bitmap = ~0UL << MT2701_IOMMU_PAGE_SHIFT, }; -- cgit v1.3-14-g43fede From bd1de6a3c6926945a19c4cc691241e26d1f2ac4c Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sat, 10 Feb 2018 07:41:30 +0100 Subject: ARM: dts: armada: netgear-rn*: fix rtc node name The node name should be generic and must not contain the part number. Signed-off-by: Alexandre Belloni Signed-off-by: Gregory CLEMENT --- arch/arm/boot/dts/armada-370-netgear-rn102.dts | 2 +- arch/arm/boot/dts/armada-370-netgear-rn104.dts | 2 +- arch/arm/boot/dts/armada-xp-netgear-rn2120.dts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/armada-370-netgear-rn102.dts b/arch/arm/boot/dts/armada-370-netgear-rn102.dts index b1a96e95e921..d2225bd4a76f 100644 --- a/arch/arm/boot/dts/armada-370-netgear-rn102.dts +++ b/arch/arm/boot/dts/armada-370-netgear-rn102.dts @@ -103,7 +103,7 @@ status = "okay"; - isl12057: isl12057@68 { + isl12057: rtc@68 { compatible = "isil,isl12057"; reg = <0x68>; wakeup-source; diff --git a/arch/arm/boot/dts/armada-370-netgear-rn104.dts b/arch/arm/boot/dts/armada-370-netgear-rn104.dts index d67e7aa42b54..b73cae836bc1 100644 --- a/arch/arm/boot/dts/armada-370-netgear-rn104.dts +++ b/arch/arm/boot/dts/armada-370-netgear-rn104.dts @@ -105,7 +105,7 @@ status = "okay"; - isl12057: isl12057@68 { + isl12057: rtc@68 { compatible = "isil,isl12057"; reg = <0x68>; wakeup-source; diff --git a/arch/arm/boot/dts/armada-xp-netgear-rn2120.dts b/arch/arm/boot/dts/armada-xp-netgear-rn2120.dts index 40c6fe21e720..e66fe80cf279 100644 --- a/arch/arm/boot/dts/armada-xp-netgear-rn2120.dts +++ b/arch/arm/boot/dts/armada-xp-netgear-rn2120.dts @@ -115,7 +115,7 @@ reg = <0x4c>; }; - isl12057: isl12057@68 { + isl12057: rtc@68 { compatible = "isil,isl12057"; reg = <0x68>; wakeup-source; -- cgit v1.3-14-g43fede From 983cfd77d70557cf83582f96f1dd946128c0abd9 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 13 Feb 2018 17:14:55 +0100 Subject: ARM: dts: at91: sam9rl: Properly assign copyright The copyright holder for this work is Microchip (formerly Atmel) Acked-by: Nicolas Ferre Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91sam9rl.dtsi | 3 ++- arch/arm/boot/dts/at91sam9rlek.dts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/at91sam9rl.dtsi b/arch/arm/boot/dts/at91sam9rl.dtsi index 3cae687dccbd..6765cee8ce21 100644 --- a/arch/arm/boot/dts/at91sam9rl.dtsi +++ b/arch/arm/boot/dts/at91sam9rl.dtsi @@ -1,7 +1,8 @@ /* * at91sam9rl.dtsi - Device Tree Include file for AT91SAM9RL family SoC * - * Copyright (C) 2014 Alexandre Belloni + * Copyright (C) 2014 Microchip + * Alexandre Belloni * * Licensed under GPLv2 or later. */ diff --git a/arch/arm/boot/dts/at91sam9rlek.dts b/arch/arm/boot/dts/at91sam9rlek.dts index 4bde9f245e61..27d8a1f44233 100644 --- a/arch/arm/boot/dts/at91sam9rlek.dts +++ b/arch/arm/boot/dts/at91sam9rlek.dts @@ -1,7 +1,8 @@ /* * at91sam9rlek.dts - Device Tree file for Atmel at91sam9rl reference board * - * Copyright (C) 2014 Alexandre Belloni + * Copyright (C) 2014 Microchip + * Alexandre Belloni * * Licensed under GPLv2 only */ -- cgit v1.3-14-g43fede From 6b65933008a6bbe5ff79a344b41175826848682d Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Tue, 16 Jan 2018 17:06:17 +0100 Subject: ARM: dts: at91: nattis: use the correct compatible for the eeprom The used part does contain an eeprom compatible with an Atmel 24c02 chip and it is from NXP, but it is not called 24c02. It's actually a se97b chip. Adjust the compatible accordingly. Fixes: 0e4323899973 ("ARM: dts: at91: add devicetree for the Axentia Nattis with Natte power") Signed-off-by: Peter Rosin Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91-nattis-2-natte-2.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/at91-nattis-2-natte-2.dts b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts index 3ea1d26e1c68..c457eff25911 100644 --- a/arch/arm/boot/dts/at91-nattis-2-natte-2.dts +++ b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts @@ -146,7 +146,7 @@ }; eeprom@50 { - compatible = "nxp,24c02"; + compatible = "nxp,se97b", "atmel,24c02"; reg = <0x50>; pagesize = <16>; }; -- cgit v1.3-14-g43fede From 7981190fb5dd710dea08c2613cee3d05e795ca5e Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Tue, 16 Jan 2018 17:06:18 +0100 Subject: ARM: dts: at91: tse850: use the correct compatible for the eeprom The used part does contain an eeprom compatible with an Atmel 24c02 chip and it is from NXP, but it is not called 24c02. It's actually a se97b chip. Adjust the compatible accordingly. Fixes: 21dd0ece34c2 ("ARM: dts: at91: add devicetree for the Axentia TSE-850") Signed-off-by: Peter Rosin Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91-tse850-3.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/at91-tse850-3.dts b/arch/arm/boot/dts/at91-tse850-3.dts index 9b82cc8843e1..97b227693658 100644 --- a/arch/arm/boot/dts/at91-tse850-3.dts +++ b/arch/arm/boot/dts/at91-tse850-3.dts @@ -246,7 +246,7 @@ }; eeprom@50 { - compatible = "nxp,24c02", "atmel,24c02"; + compatible = "nxp,se97b", "atmel,24c02"; reg = <0x50>; pagesize = <16>; }; -- cgit v1.3-14-g43fede From cf438f51c6af8c4d0030d681d14c3a59f0ec5d93 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Thu, 18 Jan 2018 12:46:14 +0100 Subject: ARM: dts: at91: nattis: use up-to-date mtd partitions The nattis currently relies on the cmdline to correctly configure the MTD partition table, and the one given in the device tree is simply ignored. Which is fortunate, since the device tree partition table is bonkers and does not match reality. So, in order to avoid confusion, fix the device tree partition table to match what is expected (i.e. what is normally provided on the cmdline). In particular, the rootfs partition should be mtd6. Fixes: 0e4323899973 ("ARM: dts: at91: add devicetree for the Axentia Nattis with Natte power") Signed-off-by: Peter Rosin Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91-nattis-2-natte-2.dts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/arch/arm/boot/dts/at91-nattis-2-natte-2.dts b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts index c457eff25911..f94c0613fb0d 100644 --- a/arch/arm/boot/dts/at91-nattis-2-natte-2.dts +++ b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts @@ -216,29 +216,34 @@ reg = <0x0 0x40000>; }; - bootloader@40000 { - label = "bootloader"; - reg = <0x40000 0x80000>; + barebox@40000 { + label = "barebox"; + reg = <0x40000 0x60000>; }; - bootloaderenv@c0000 { - label = "bootloader env"; - reg = <0xc0000 0xc0000>; + bareboxenv@c0000 { + label = "bareboxenv"; + reg = <0xc0000 0x40000>; }; - dtb@180000 { - label = "device tree"; - reg = <0x180000 0x80000>; + bareboxenv2@100000 { + label = "bareboxenv2"; + reg = <0x100000 0x40000>; + }; + + oftree@180000 { + label = "oftree"; + reg = <0x180000 0x20000>; }; kernel@200000 { label = "kernel"; - reg = <0x200000 0x600000>; + reg = <0x200000 0x500000>; }; rootfs@800000 { label = "rootfs"; - reg = <0x800000 0x0f800000>; + reg = <0x800000 0x1f800000>; }; }; }; -- cgit v1.3-14-g43fede From 2f8bd78412ed5807350ec0c1bb56488d9b7993cf Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Thu, 18 Jan 2018 12:46:15 +0100 Subject: ARM: dts: at91: nattis: add lvds-encoder The lvds encoder chip was omitted by oversight. Add it. Fixes: 0e4323899973 ("ARM: dts: at91: add devicetree for the Axentia Nattis with Natte power") Signed-off-by: Peter Rosin Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91-nattis-2-natte-2.dts | 31 ++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/at91-nattis-2-natte-2.dts b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts index f94c0613fb0d..af9f38456d04 100644 --- a/arch/arm/boot/dts/at91-nattis-2-natte-2.dts +++ b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts @@ -109,7 +109,32 @@ port { panel_input: endpoint { - remote-endpoint = <&hlcdc_panel_output>; + remote-endpoint = <&lvds_encoder_output>; + }; + }; + }; + + lvds-encoder { + compatible = "lvds-encoder"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + lvds_encoder_input: endpoint { + remote-endpoint = <&hlcdc_output>; + }; + }; + + port@1 { + reg = <1>; + + lvds_encoder_output: endpoint { + remote-endpoint = <&panel_input>; + }; }; }; }; @@ -176,8 +201,8 @@ &pinctrl_lcd_hipow0>; port@0 { - hlcdc_panel_output: endpoint { - remote-endpoint = <&panel_input>; + hlcdc_output: endpoint { + remote-endpoint = <&lvds_encoder_input>; }; }; }; -- cgit v1.3-14-g43fede From 8b97da49cc3401233d72b10adea1f09e4651f355 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Thu, 18 Jan 2018 12:46:16 +0100 Subject: ARM: dts: at91: tse850: make the sound dai cell count explicit The node is referred to as a DAI from the sound node and should therefore have a #sound-dai-cells property. Fixes: 21dd0ece34c2 ("ARM: dts: at91: add devicetree for the Axentia TSE-850") Signed-off-by: Peter Rosin Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91-tse850-3.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/at91-tse850-3.dts b/arch/arm/boot/dts/at91-tse850-3.dts index 97b227693658..2fbec69d9cd6 100644 --- a/arch/arm/boot/dts/at91-tse850-3.dts +++ b/arch/arm/boot/dts/at91-tse850-3.dts @@ -234,6 +234,7 @@ compatible = "ti,pcm5142"; reg = <0x4c>; + #sound-dai-cells = <0>; AVDD-supply = <®_3v3>; DVDD-supply = <®_3v3>; -- cgit v1.3-14-g43fede From 9a06757dcc8509c162ac00488c8c82fc98e04227 Mon Sep 17 00:00:00 2001 From: Santiago Esteban Date: Thu, 18 Jan 2018 15:38:47 +0100 Subject: ARM: dts: at91: sama5d4: fix pinctrl compatible string The compatible string is incorrect. Add atmel,sama5d3-pinctrl since it's the appropriate compatible string. Remove the atmel,at91rm9200-pinctrl compatible string, this fallback is useless, there are too many changes. Signed-off-by: Santiago Esteban Signed-off-by: Ludovic Desroches Cc: stable@vger.kernel.org #v3.18 Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/sama5d4.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/sama5d4.dtsi b/arch/arm/boot/dts/sama5d4.dtsi index 373b3621b536..c7105096c623 100644 --- a/arch/arm/boot/dts/sama5d4.dtsi +++ b/arch/arm/boot/dts/sama5d4.dtsi @@ -1379,7 +1379,7 @@ pinctrl@fc06a000 { #address-cells = <1>; #size-cells = <1>; - compatible = "atmel,at91sam9x5-pinctrl", "atmel,at91rm9200-pinctrl", "simple-bus"; + compatible = "atmel,sama5d3-pinctrl", "atmel,at91sam9x5-pinctrl", "simple-bus"; ranges = <0xfc068000 0xfc068000 0x100 0xfc06a000 0xfc06a000 0x4000>; /* WARNING: revisit as pin spec has changed */ -- cgit v1.3-14-g43fede From 3f5243cb54fe0e5486ee5c3a10ad7bfcf23c492c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2018 22:23:46 +0100 Subject: ARM: dts: at91: use 'atmel' as at24 manufacturer for sama5d34ek Using compatible strings without the part for at24 is now deprecated. Use a correct 'atmel,' value. Signed-off-by: Bartosz Golaszewski Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/sama5d34ek.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/sama5d34ek.dts b/arch/arm/boot/dts/sama5d34ek.dts index c8b8449fdc3e..15d5c46013a4 100644 --- a/arch/arm/boot/dts/sama5d34ek.dts +++ b/arch/arm/boot/dts/sama5d34ek.dts @@ -38,7 +38,7 @@ status = "okay"; 24c256@50 { - compatible = "24c256"; + compatible = "atmel,24c256"; reg = <0x50>; pagesize = <64>; }; -- cgit v1.3-14-g43fede From e3a5ce64502a9c0662162d71dd944c3d893a0028 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2018 22:25:01 +0100 Subject: ARM: dts: at91: use 'atmel' as at24 manufacturer for at91sam9260ek Using compatible strings without the part for at24 is now deprecated. Use a correct 'atmel,' value. Signed-off-by: Bartosz Golaszewski Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91sam9260ek.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/at91sam9260ek.dts b/arch/arm/boot/dts/at91sam9260ek.dts index e16c706d91ef..d2b865f60293 100644 --- a/arch/arm/boot/dts/at91sam9260ek.dts +++ b/arch/arm/boot/dts/at91sam9260ek.dts @@ -201,7 +201,7 @@ status = "okay"; 24c512@50 { - compatible = "24c512"; + compatible = "atmel,24c512"; reg = <0x50>; }; }; -- cgit v1.3-14-g43fede From b2f24db37968477b33d97c82becaba7886cb0a12 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2018 22:25:44 +0100 Subject: ARM: dts: at91: use 'atmel' as at24 manufacturer for at91sam9g20ek Using compatible strings without the part for at24 is now deprecated. Use a correct 'atmel,' value. Signed-off-by: Bartosz Golaszewski Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91sam9g20ek_common.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi index 50561b7b7939..71df3adfc7ca 100644 --- a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi +++ b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi @@ -211,7 +211,7 @@ status = "okay"; 24c512@50 { - compatible = "24c512"; + compatible = "atmel,24c512"; reg = <0x50>; }; -- cgit v1.3-14-g43fede From 282e8874e4af45ccc52fbd450fea76c1d0d6646a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2018 22:26:56 +0100 Subject: ARM: dts: at91: use 'atmel' as at24 manufacturer for at91-sama5d2_ptc_ek Using compatible strings without the part for at24 is now deprecated. Use a correct 'atmel,' value. Signed-off-by: Bartosz Golaszewski Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91-sama5d2_ptc_ek.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/at91-sama5d2_ptc_ek.dts b/arch/arm/boot/dts/at91-sama5d2_ptc_ek.dts index e603a267bdf1..b10dccd0958f 100644 --- a/arch/arm/boot/dts/at91-sama5d2_ptc_ek.dts +++ b/arch/arm/boot/dts/at91-sama5d2_ptc_ek.dts @@ -230,7 +230,7 @@ status = "okay"; at24@50 { - compatible = "24c02"; + compatible = "atmel,24c02"; reg = <0x50>; pagesize = <8>; }; -- cgit v1.3-14-g43fede From 6fa65edf87886f85a18680ed7bdedc15cb810065 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2018 22:29:09 +0100 Subject: ARM: dts: at91: use 'atmel' as at24 manufacturer for at91sam9263ek Using compatible strings without the part for at24 is now deprecated. Use a correct 'atmel,' value. Signed-off-by: Bartosz Golaszewski Signed-off-by: Alexandre Belloni --- arch/arm/boot/dts/at91sam9263ek.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/at91sam9263ek.dts b/arch/arm/boot/dts/at91sam9263ek.dts index e9a7c70830a8..727096f24f7c 100644 --- a/arch/arm/boot/dts/at91sam9263ek.dts +++ b/arch/arm/boot/dts/at91sam9263ek.dts @@ -250,7 +250,7 @@ status = "okay"; 24c512@50 { - compatible = "24c512"; + compatible = "atmel,24c512"; reg = <0x50>; pagesize = <128>; }; -- cgit v1.3-14-g43fede From 5beaf4d7ce9e842c7198a00616c2061ca4e46016 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 13 Feb 2018 15:15:32 +0100 Subject: ARM: dts: kirkwood: Fix "debounce-interval" property misspelling "debounce_interval" was never supported. Signed-off-by: Geert Uytterhoeven Cc: Jason Cooper Cc: Andrew Lunn Cc: Gregory Clement Cc: Sebastian Hesselbarth Reviewed-by: Andrew Lunn Signed-off-by: Gregory CLEMENT --- arch/arm/boot/dts/kirkwood-pogoplug-series-4.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/kirkwood-pogoplug-series-4.dts b/arch/arm/boot/dts/kirkwood-pogoplug-series-4.dts index 5ce220ac9611..5aa4669ae254 100644 --- a/arch/arm/boot/dts/kirkwood-pogoplug-series-4.dts +++ b/arch/arm/boot/dts/kirkwood-pogoplug-series-4.dts @@ -35,7 +35,7 @@ pinctrl-names = "default"; eject { - debounce_interval = <50>; + debounce-interval = <50>; wakeup-source; linux,code = ; label = "Eject Button"; -- cgit v1.3-14-g43fede From d15a339eaca6fd6eb00ebc36edf3c289b65000fe Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Mon, 12 Feb 2018 16:48:20 +0000 Subject: iommu/vt-d: Add __init for dmar_register_bus_notifier() It's called only from intel_iommu_init(), which is init function. Signed-off-by: Dmitry Safonov Signed-off-by: Joerg Roedel --- drivers/iommu/dmar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 9a7ffd13c7f0..accf58388bdb 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -806,7 +806,7 @@ int __init dmar_dev_scope_init(void) return dmar_dev_scope_status; } -void dmar_register_bus_notifier(void) +void __init dmar_register_bus_notifier(void) { bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb); } -- cgit v1.3-14-g43fede From b1d03c1d12abbfa7de127772f281b309cf1650c3 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Mon, 12 Feb 2018 16:48:21 +0000 Subject: iommu/vt-d: Clean/document fault status flags So one could decode them without opening the specification. Signed-off-by: Dmitry Safonov Signed-off-by: Joerg Roedel --- include/linux/intel-iommu.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 8dad3dd26eae..ef169d67df92 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -209,12 +209,12 @@ #define DMA_FECTL_IM (((u32)1) << 31) /* FSTS_REG */ -#define DMA_FSTS_PPF ((u32)2) -#define DMA_FSTS_PFO ((u32)1) -#define DMA_FSTS_IQE (1 << 4) -#define DMA_FSTS_ICE (1 << 5) -#define DMA_FSTS_ITE (1 << 6) -#define DMA_FSTS_PRO (1 << 7) +#define DMA_FSTS_PFO (1 << 0) /* Primary Fault Overflow */ +#define DMA_FSTS_PPF (1 << 1) /* Primary Pending Fault */ +#define DMA_FSTS_IQE (1 << 4) /* Invalidation Queue Error */ +#define DMA_FSTS_ICE (1 << 5) /* Invalidation Completion Error */ +#define DMA_FSTS_ITE (1 << 6) /* Invalidation Time-out Error */ +#define DMA_FSTS_PRO (1 << 7) /* Page Request Overflow */ #define dma_fsts_fault_record_index(s) (((s) >> 8) & 0xff) /* FRCD_REG, 32 bits access */ -- cgit v1.3-14-g43fede From e58864515240bcb657ee77c3ad27da1929b83914 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Mon, 5 Feb 2018 08:57:01 +0100 Subject: ARM: dts: exynos: Use pinctrl labels in exynos4412-pinctrl Define pinctrl labels in exynos4412.dtsi and use them in exynos4412-pinctrl.dtsi, as it is done in other Exynos DTSes. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4412-pinctrl.dtsi | 1914 ++++++++++++++--------------- arch/arm/boot/dts/exynos4412.dtsi | 65 +- 2 files changed, 990 insertions(+), 989 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-pinctrl.dtsi b/arch/arm/boot/dts/exynos4412-pinctrl.dtsi index e8dd5f2d976f..d7d5fdc230d8 100644 --- a/arch/arm/boot/dts/exynos4412-pinctrl.dtsi +++ b/arch/arm/boot/dts/exynos4412-pinctrl.dtsi @@ -18,964 +18,962 @@ samsung,pin-pud-pdn = ; \ } -/ { - pinctrl_0: pinctrl@11400000 { - gpa0: gpa0 { - gpio-controller; - #gpio-cells = <2>; +&pinctrl_0 { + gpa0: gpa0 { + gpio-controller; + #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpa1: gpa1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpb: gpb { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpc0: gpc0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpc1: gpc1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpd0: gpd0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpd1: gpd1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpf0: gpf0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpf1: gpf1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpf2: gpf2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpf3: gpf3 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpj0: gpj0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpj1: gpj1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - uart0_data: uart0-data { - samsung,pins = "gpa0-0", "gpa0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart0_fctl: uart0-fctl { - samsung,pins = "gpa0-2", "gpa0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart1_data: uart1-data { - samsung,pins = "gpa0-4", "gpa0-5"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart1_fctl: uart1-fctl { - samsung,pins = "gpa0-6", "gpa0-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c2_bus: i2c2-bus { - samsung,pins = "gpa0-6", "gpa0-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart2_data: uart2-data { - samsung,pins = "gpa1-0", "gpa1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart2_fctl: uart2-fctl { - samsung,pins = "gpa1-2", "gpa1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart_audio_a: uart-audio-a { - samsung,pins = "gpa1-0", "gpa1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c3_bus: i2c3-bus { - samsung,pins = "gpa1-2", "gpa1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart3_data: uart3-data { - samsung,pins = "gpa1-4", "gpa1-5"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - uart_audio_b: uart-audio-b { - samsung,pins = "gpa1-4", "gpa1-5"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - spi0_bus: spi0-bus { - samsung,pins = "gpb-0", "gpb-2", "gpb-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c4_bus: i2c4-bus { - samsung,pins = "gpb-0", "gpb-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - spi1_bus: spi1-bus { - samsung,pins = "gpb-4", "gpb-6", "gpb-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c5_bus: i2c5-bus { - samsung,pins = "gpb-2", "gpb-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2s1_bus: i2s1-bus { - samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", - "gpc0-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pcm1_bus: pcm1-bus { - samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", - "gpc0-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - ac97_bus: ac97-bus { - samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", - "gpc0-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2s2_bus: i2s2-bus { - samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3", - "gpc1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pcm2_bus: pcm2-bus { - samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3", - "gpc1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - spdif_bus: spdif-bus { - samsung,pins = "gpc1-0", "gpc1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c6_bus: i2c6-bus { - samsung,pins = "gpc1-3", "gpc1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - spi2_bus: spi2-bus { - samsung,pins = "gpc1-1", "gpc1-3", "gpc1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pwm0_out: pwm0-out { - samsung,pins = "gpd0-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pwm1_out: pwm1-out { - samsung,pins = "gpd0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_ctrl: lcd-ctrl { - samsung,pins = "gpd0-0", "gpd0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c7_bus: i2c7-bus { - samsung,pins = "gpd0-2", "gpd0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pwm2_out: pwm2-out { - samsung,pins = "gpd0-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pwm3_out: pwm3-out { - samsung,pins = "gpd0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c0_bus: i2c0-bus { - samsung,pins = "gpd1-0", "gpd1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - mipi0_clk: mipi0-clk { - samsung,pins = "gpd1-0", "gpd1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - i2c1_bus: i2c1-bus { - samsung,pins = "gpd1-2", "gpd1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - mipi1_clk: mipi1-clk { - samsung,pins = "gpd1-2", "gpd1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_clk: lcd-clk { - samsung,pins = "gpf0-0", "gpf0-1", "gpf0-2", "gpf0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_data16: lcd-data-width16 { - samsung,pins = "gpf0-7", "gpf1-0", "gpf1-1", "gpf1-2", - "gpf1-3", "gpf1-6", "gpf1-7", "gpf2-0", - "gpf2-1", "gpf2-2", "gpf2-3", "gpf2-7", - "gpf3-0", "gpf3-1", "gpf3-2", "gpf3-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_data18: lcd-data-width18 { - samsung,pins = "gpf0-6", "gpf0-7", "gpf1-0", "gpf1-1", - "gpf1-2", "gpf1-3", "gpf1-6", "gpf1-7", - "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3", - "gpf2-6", "gpf2-7", "gpf3-0", "gpf3-1", - "gpf3-2", "gpf3-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_data24: lcd-data-width24 { - samsung,pins = "gpf0-4", "gpf0-5", "gpf0-6", "gpf0-7", - "gpf1-0", "gpf1-1", "gpf1-2", "gpf1-3", - "gpf1-4", "gpf1-5", "gpf1-6", "gpf1-7", - "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3", - "gpf2-4", "gpf2-5", "gpf2-6", "gpf2-7", - "gpf3-0", "gpf3-1", "gpf3-2", "gpf3-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - lcd_ldi: lcd-ldi { - samsung,pins = "gpf3-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - cam_port_a_io: cam-port-a-io { - samsung,pins = "gpj0-0", "gpj0-1", "gpj0-2", "gpj0-3", - "gpj0-4", "gpj0-5", "gpj0-6", "gpj0-7", - "gpj1-0", "gpj1-1", "gpj1-2", "gpj1-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - cam_port_a_clk_active: cam-port-a-clk-active { - samsung,pins = "gpj1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - cam_port_a_clk_idle: cam-port-a-clk-idle { - samsung,pins = "gpj1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - }; - - pinctrl_1: pinctrl@11000000 { - gpk0: gpk0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpk1: gpk1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpk2: gpk2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpk3: gpk3 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpl0: gpl0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpl1: gpl1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpl2: gpl2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpm0: gpm0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpm1: gpm1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpm2: gpm2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpm3: gpm3 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpm4: gpm4 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpy0: gpy0 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy1: gpy1 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy2: gpy2 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy3: gpy3 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy4: gpy4 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy5: gpy5 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpy6: gpy6 { - gpio-controller; - #gpio-cells = <2>; - }; - - gpx0: gpx0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - interrupt-parent = <&gic>; - interrupts = , - , - , - , - , - , - , - ; - #interrupt-cells = <2>; - }; - - gpx1: gpx1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - interrupt-parent = <&gic>; - interrupts = , - , - , - , - , - , - , - ; - #interrupt-cells = <2>; - }; - - gpx2: gpx2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpx3: gpx3 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - sd0_clk: sd0-clk { - samsung,pins = "gpk0-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_cmd: sd0-cmd { - samsung,pins = "gpk0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_cd: sd0-cd { - samsung,pins = "gpk0-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_bus1: sd0-bus-width1 { - samsung,pins = "gpk0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_bus4: sd0-bus-width4 { - samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd0_bus8: sd0-bus-width8 { - samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_clk: sd4-clk { - samsung,pins = "gpk0-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_cmd: sd4-cmd { - samsung,pins = "gpk0-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_cd: sd4-cd { - samsung,pins = "gpk0-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_bus1: sd4-bus-width1 { - samsung,pins = "gpk0-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_bus4: sd4-bus-width4 { - samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd4_bus8: sd4-bus-width8 { - samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_clk: sd1-clk { - samsung,pins = "gpk1-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_cmd: sd1-cmd { - samsung,pins = "gpk1-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_cd: sd1-cd { - samsung,pins = "gpk1-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_bus1: sd1-bus-width1 { - samsung,pins = "gpk1-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd1_bus4: sd1-bus-width4 { - samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_clk: sd2-clk { - samsung,pins = "gpk2-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_cmd: sd2-cmd { - samsung,pins = "gpk2-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_cd: sd2-cd { - samsung,pins = "gpk2-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_bus1: sd2-bus-width1 { - samsung,pins = "gpk2-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_bus4: sd2-bus-width4 { - samsung,pins = "gpk2-3", "gpk2-4", "gpk2-5", "gpk2-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd2_bus8: sd2-bus-width8 { - samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_clk: sd3-clk { - samsung,pins = "gpk3-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_cmd: sd3-cmd { - samsung,pins = "gpk3-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_cd: sd3-cd { - samsung,pins = "gpk3-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_bus1: sd3-bus-width1 { - samsung,pins = "gpk3-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sd3_bus4: sd3-bus-width4 { - samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - cam_port_b_io: cam-port-b-io { - samsung,pins = "gpm0-0", "gpm0-1", "gpm0-2", "gpm0-3", - "gpm0-4", "gpm0-5", "gpm0-6", "gpm0-7", - "gpm1-0", "gpm1-1", "gpm2-0", "gpm2-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - cam_port_b_clk_active: cam-port-b-clk-active { - samsung,pins = "gpm2-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - cam_port_b_clk_idle: cam-port-b-clk-idle { - samsung,pins = "gpm2-2"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint0: ext-int0 { - samsung,pins = "gpx0-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint8: ext-int8 { - samsung,pins = "gpx1-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint15: ext-int15 { - samsung,pins = "gpx1-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint16: ext-int16 { - samsung,pins = "gpx2-0"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - eint31: ext-int31 { - samsung,pins = "gpx3-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - fimc_is_i2c0: fimc-is-i2c0 { - samsung,pins = "gpm4-0", "gpm4-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - fimc_is_i2c1: fimc-is-i2c1 { - samsung,pins = "gpm4-2", "gpm4-3"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - fimc_is_uart: fimc-is-uart { - samsung,pins = "gpm3-5", "gpm3-7"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - hdmi_cec: hdmi-cec { - samsung,pins = "gpx3-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - }; - - pinctrl_2: pinctrl@3860000 { - gpz: gpz { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - i2s0_bus: i2s0-bus { - samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3", - "gpz-4", "gpz-5", "gpz-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - pcm0_bus: pcm0-bus { - samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3", - "gpz-4"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - }; - - pinctrl_3: pinctrl@106e0000 { - gpv0: gpv0 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpv1: gpv1 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpv2: gpv2 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpv3: gpv3 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpv4: gpv4 { - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - c2c_bus: c2c-bus { - samsung,pins = "gpv0-0", "gpv0-1", "gpv0-2", "gpv0-3", - "gpv0-4", "gpv0-5", "gpv0-6", "gpv0-7", - "gpv1-0", "gpv1-1", "gpv1-2", "gpv1-3", - "gpv1-4", "gpv1-5", "gpv1-6", "gpv1-7", - "gpv2-0", "gpv2-1", "gpv2-2", "gpv2-3", - "gpv2-4", "gpv2-5", "gpv2-6", "gpv2-7", - "gpv3-0", "gpv3-1", "gpv3-2", "gpv3-3", - "gpv3-4", "gpv3-5", "gpv3-6", "gpv3-7", - "gpv4-0", "gpv4-1"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpa1: gpa1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpb: gpb { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc0: gpc0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpc1: gpc1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpd0: gpd0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpd1: gpd1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf0: gpf0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf1: gpf1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf2: gpf2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf3: gpf3 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpj0: gpj0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpj1: gpj1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + uart0_data: uart0-data { + samsung,pins = "gpa0-0", "gpa0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart0_fctl: uart0-fctl { + samsung,pins = "gpa0-2", "gpa0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart1_data: uart1-data { + samsung,pins = "gpa0-4", "gpa0-5"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart1_fctl: uart1-fctl { + samsung,pins = "gpa0-6", "gpa0-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c2_bus: i2c2-bus { + samsung,pins = "gpa0-6", "gpa0-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart2_data: uart2-data { + samsung,pins = "gpa1-0", "gpa1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart2_fctl: uart2-fctl { + samsung,pins = "gpa1-2", "gpa1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart_audio_a: uart-audio-a { + samsung,pins = "gpa1-0", "gpa1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c3_bus: i2c3-bus { + samsung,pins = "gpa1-2", "gpa1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart3_data: uart3-data { + samsung,pins = "gpa1-4", "gpa1-5"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + uart_audio_b: uart-audio-b { + samsung,pins = "gpa1-4", "gpa1-5"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi0_bus: spi0-bus { + samsung,pins = "gpb-0", "gpb-2", "gpb-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c4_bus: i2c4-bus { + samsung,pins = "gpb-0", "gpb-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi1_bus: spi1-bus { + samsung,pins = "gpb-4", "gpb-6", "gpb-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c5_bus: i2c5-bus { + samsung,pins = "gpb-2", "gpb-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2s1_bus: i2s1-bus { + samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", + "gpc0-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pcm1_bus: pcm1-bus { + samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", + "gpc0-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + ac97_bus: ac97-bus { + samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3", + "gpc0-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2s2_bus: i2s2-bus { + samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3", + "gpc1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pcm2_bus: pcm2-bus { + samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3", + "gpc1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spdif_bus: spdif-bus { + samsung,pins = "gpc1-0", "gpc1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c6_bus: i2c6-bus { + samsung,pins = "gpc1-3", "gpc1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + spi2_bus: spi2-bus { + samsung,pins = "gpc1-1", "gpc1-3", "gpc1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pwm0_out: pwm0-out { + samsung,pins = "gpd0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pwm1_out: pwm1-out { + samsung,pins = "gpd0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_ctrl: lcd-ctrl { + samsung,pins = "gpd0-0", "gpd0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c7_bus: i2c7-bus { + samsung,pins = "gpd0-2", "gpd0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pwm2_out: pwm2-out { + samsung,pins = "gpd0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pwm3_out: pwm3-out { + samsung,pins = "gpd0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c0_bus: i2c0-bus { + samsung,pins = "gpd1-0", "gpd1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + mipi0_clk: mipi0-clk { + samsung,pins = "gpd1-0", "gpd1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + i2c1_bus: i2c1-bus { + samsung,pins = "gpd1-2", "gpd1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + mipi1_clk: mipi1-clk { + samsung,pins = "gpd1-2", "gpd1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_clk: lcd-clk { + samsung,pins = "gpf0-0", "gpf0-1", "gpf0-2", "gpf0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_data16: lcd-data-width16 { + samsung,pins = "gpf0-7", "gpf1-0", "gpf1-1", "gpf1-2", + "gpf1-3", "gpf1-6", "gpf1-7", "gpf2-0", + "gpf2-1", "gpf2-2", "gpf2-3", "gpf2-7", + "gpf3-0", "gpf3-1", "gpf3-2", "gpf3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_data18: lcd-data-width18 { + samsung,pins = "gpf0-6", "gpf0-7", "gpf1-0", "gpf1-1", + "gpf1-2", "gpf1-3", "gpf1-6", "gpf1-7", + "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3", + "gpf2-6", "gpf2-7", "gpf3-0", "gpf3-1", + "gpf3-2", "gpf3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_data24: lcd-data-width24 { + samsung,pins = "gpf0-4", "gpf0-5", "gpf0-6", "gpf0-7", + "gpf1-0", "gpf1-1", "gpf1-2", "gpf1-3", + "gpf1-4", "gpf1-5", "gpf1-6", "gpf1-7", + "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3", + "gpf2-4", "gpf2-5", "gpf2-6", "gpf2-7", + "gpf3-0", "gpf3-1", "gpf3-2", "gpf3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + lcd_ldi: lcd-ldi { + samsung,pins = "gpf3-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + cam_port_a_io: cam-port-a-io { + samsung,pins = "gpj0-0", "gpj0-1", "gpj0-2", "gpj0-3", + "gpj0-4", "gpj0-5", "gpj0-6", "gpj0-7", + "gpj1-0", "gpj1-1", "gpj1-2", "gpj1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + cam_port_a_clk_active: cam-port-a-clk-active { + samsung,pins = "gpj1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + cam_port_a_clk_idle: cam-port-a-clk-idle { + samsung,pins = "gpj1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&pinctrl_1 { + gpk0: gpk0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpk1: gpk1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpk2: gpk2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpk3: gpk3 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpl0: gpl0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpl1: gpl1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpl2: gpl2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpm0: gpm0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpm1: gpm1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpm2: gpm2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpm3: gpm3 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpm4: gpm4 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpy0: gpy0 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy1: gpy1 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy2: gpy2 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy3: gpy3 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy4: gpy4 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy5: gpy5 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpy6: gpy6 { + gpio-controller; + #gpio-cells = <2>; + }; + + gpx0: gpx0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + interrupt-parent = <&gic>; + interrupts = , + , + , + , + , + , + , + ; + #interrupt-cells = <2>; + }; + + gpx1: gpx1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + interrupt-parent = <&gic>; + interrupts = , + , + , + , + , + , + , + ; + #interrupt-cells = <2>; + }; + + gpx2: gpx2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpx3: gpx3 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + sd0_clk: sd0-clk { + samsung,pins = "gpk0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd0_cmd: sd0-cmd { + samsung,pins = "gpk0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd0_cd: sd0-cd { + samsung,pins = "gpk0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd0_bus1: sd0-bus-width1 { + samsung,pins = "gpk0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd0_bus4: sd0-bus-width4 { + samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd0_bus8: sd0-bus-width8 { + samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_clk: sd4-clk { + samsung,pins = "gpk0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_cmd: sd4-cmd { + samsung,pins = "gpk0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_cd: sd4-cd { + samsung,pins = "gpk0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_bus1: sd4-bus-width1 { + samsung,pins = "gpk0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_bus4: sd4-bus-width4 { + samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd4_bus8: sd4-bus-width8 { + samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_clk: sd1-clk { + samsung,pins = "gpk1-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_cmd: sd1-cmd { + samsung,pins = "gpk1-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_cd: sd1-cd { + samsung,pins = "gpk1-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_bus1: sd1-bus-width1 { + samsung,pins = "gpk1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd1_bus4: sd1-bus-width4 { + samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_clk: sd2-clk { + samsung,pins = "gpk2-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_cmd: sd2-cmd { + samsung,pins = "gpk2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_cd: sd2-cd { + samsung,pins = "gpk2-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_bus1: sd2-bus-width1 { + samsung,pins = "gpk2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_bus4: sd2-bus-width4 { + samsung,pins = "gpk2-3", "gpk2-4", "gpk2-5", "gpk2-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd2_bus8: sd2-bus-width8 { + samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_clk: sd3-clk { + samsung,pins = "gpk3-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_cmd: sd3-cmd { + samsung,pins = "gpk3-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_cd: sd3-cd { + samsung,pins = "gpk3-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_bus1: sd3-bus-width1 { + samsung,pins = "gpk3-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sd3_bus4: sd3-bus-width4 { + samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + cam_port_b_io: cam-port-b-io { + samsung,pins = "gpm0-0", "gpm0-1", "gpm0-2", "gpm0-3", + "gpm0-4", "gpm0-5", "gpm0-6", "gpm0-7", + "gpm1-0", "gpm1-1", "gpm2-0", "gpm2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + cam_port_b_clk_active: cam-port-b-clk-active { + samsung,pins = "gpm2-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + cam_port_b_clk_idle: cam-port-b-clk-idle { + samsung,pins = "gpm2-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint0: ext-int0 { + samsung,pins = "gpx0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint8: ext-int8 { + samsung,pins = "gpx1-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint15: ext-int15 { + samsung,pins = "gpx1-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint16: ext-int16 { + samsung,pins = "gpx2-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + eint31: ext-int31 { + samsung,pins = "gpx3-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + fimc_is_i2c0: fimc-is-i2c0 { + samsung,pins = "gpm4-0", "gpm4-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + fimc_is_i2c1: fimc-is-i2c1 { + samsung,pins = "gpm4-2", "gpm4-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + fimc_is_uart: fimc-is-uart { + samsung,pins = "gpm3-5", "gpm3-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hdmi_cec: hdmi-cec { + samsung,pins = "gpx3-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&pinctrl_2 { + gpz: gpz { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + i2s0_bus: i2s0-bus { + samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3", + "gpz-4", "gpz-5", "gpz-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pcm0_bus: pcm0-bus { + samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3", + "gpz-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&pinctrl_3 { + gpv0: gpv0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpv1: gpv1 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpv2: gpv2 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpv3: gpv3 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpv4: gpv4 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + c2c_bus: c2c-bus { + samsung,pins = "gpv0-0", "gpv0-1", "gpv0-2", "gpv0-3", + "gpv0-4", "gpv0-5", "gpv0-6", "gpv0-7", + "gpv1-0", "gpv1-1", "gpv1-2", "gpv1-3", + "gpv1-4", "gpv1-5", "gpv1-6", "gpv1-7", + "gpv2-0", "gpv2-1", "gpv2-2", "gpv2-3", + "gpv2-4", "gpv2-5", "gpv2-6", "gpv2-7", + "gpv3-0", "gpv3-1", "gpv3-2", "gpv3-3", + "gpv3-4", "gpv3-5", "gpv3-6", "gpv3-7", + "gpv4-0", "gpv4-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; }; }; diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi index e4ad2fc0329e..a49daf92c623 100644 --- a/arch/arm/boot/dts/exynos4412.dtsi +++ b/arch/arm/boot/dts/exynos4412.dtsi @@ -15,7 +15,7 @@ */ #include "exynos4.dtsi" -#include "exynos4412-pinctrl.dtsi" + #include "exynos4-cpu-thermal.dtsi" / { @@ -147,6 +147,37 @@ }; }; + pinctrl_0: pinctrl@11400000 { + compatible = "samsung,exynos4x12-pinctrl"; + reg = <0x11400000 0x1000>; + interrupts = ; + }; + + pinctrl_1: pinctrl@11000000 { + compatible = "samsung,exynos4x12-pinctrl"; + reg = <0x11000000 0x1000>; + interrupts = ; + + wakup_eint: wakeup-interrupt-controller { + compatible = "samsung,exynos4210-wakeup-eint"; + interrupt-parent = <&gic>; + interrupts = ; + }; + }; + + pinctrl_2: pinctrl@3860000 { + compatible = "samsung,exynos4x12-pinctrl"; + reg = <0x03860000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <10 0>; + }; + + pinctrl_3: pinctrl@106e0000 { + compatible = "samsung,exynos4x12-pinctrl"; + reg = <0x106E0000 0x1000>; + interrupts = ; + }; + sysram@2020000 { compatible = "mmio-sram"; reg = <0x02020000 0x40000>; @@ -693,36 +724,6 @@ <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>; }; -&pinctrl_0 { - compatible = "samsung,exynos4x12-pinctrl"; - reg = <0x11400000 0x1000>; - interrupts = ; -}; - -&pinctrl_1 { - compatible = "samsung,exynos4x12-pinctrl"; - reg = <0x11000000 0x1000>; - interrupts = ; - - wakup_eint: wakeup-interrupt-controller { - compatible = "samsung,exynos4210-wakeup-eint"; - interrupt-parent = <&gic>; - interrupts = ; - }; -}; - -&pinctrl_2 { - compatible = "samsung,exynos4x12-pinctrl"; - reg = <0x03860000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <10 0>; -}; - -&pinctrl_3 { - compatible = "samsung,exynos4x12-pinctrl"; - reg = <0x106E0000 0x1000>; - interrupts = ; -}; &pmu_system_controller { compatible = "samsung,exynos4412-pmu", "syscon"; @@ -743,3 +744,5 @@ clock-names = "tmu_apbif"; status = "disabled"; }; + +#include "exynos4412-pinctrl.dtsi" -- cgit v1.3-14-g43fede From e030be47ac48aa4f832d0dc2d5614eeb8627a59d Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Mon, 5 Feb 2018 08:57:02 +0100 Subject: ARM: dts: exynos: Use labels instead of full paths in exynos4210 Extend camera, mixer and tmu nodes by labels, not by full path in Exynos 4210 DTSI. This avoids error-prone redefinition of nodes. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4.dtsi | 6 ++- arch/arm/boot/dts/exynos4210.dtsi | 98 +++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 6d59cc827649..6d4775a0b402 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -195,7 +195,7 @@ #size-cells = <0>; }; - camera { + camera: camera { compatible = "samsung,fimc", "simple-bus"; status = "disabled"; #address-cells = <1>; @@ -718,6 +718,10 @@ }; tmu: tmu@100c0000 { + interrupt-parent = <&combiner>; + reg = <0x100C0000 0x100>; + interrupts = <2 4>; + status = "disabled"; #include "exynos4412-tmu-sensor-conf.dtsi" }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 82c1a3262b73..645b976b6842 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -152,18 +152,6 @@ reg = <0x03860000 0x1000>; }; - tmu: tmu@100c0000 { - compatible = "samsung,exynos4210-tmu"; - interrupt-parent = <&combiner>; - reg = <0x100C0000 0x100>; - interrupts = <2 4>; - clocks = <&clock CLK_TMU_APBIF>; - clock-names = "tmu_apbif"; - samsung,tmu_gain = <15>; - samsung,tmu_reference_voltage = <7>; - status = "disabled"; - }; - thermal-zones { cpu_thermal: cpu-thermal { polling-delay-passive = <0>; @@ -194,45 +182,6 @@ iommus = <&sysmmu_g2d>; }; - camera { - clocks = <&clock CLK_SCLK_CAM0>, <&clock CLK_SCLK_CAM1>, - <&clock CLK_PIXELASYNCM0>, <&clock CLK_PIXELASYNCM1>; - clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0", "pxl_async1"; - - fimc_0: fimc@11800000 { - samsung,pix-limits = <4224 8192 1920 4224>; - samsung,mainscaler-ext; - samsung,cam-if; - }; - - fimc_1: fimc@11810000 { - samsung,pix-limits = <4224 8192 1920 4224>; - samsung,mainscaler-ext; - samsung,cam-if; - }; - - fimc_2: fimc@11820000 { - samsung,pix-limits = <4224 8192 1920 4224>; - samsung,mainscaler-ext; - samsung,lcd-wb; - }; - - fimc_3: fimc@11830000 { - samsung,pix-limits = <1920 8192 1366 1920>; - samsung,rotators = <0>; - samsung,mainscaler-ext; - samsung,lcd-wb; - }; - }; - - mixer: mixer@12c10000 { - clock-names = "mixer", "hdmi", "sclk_hdmi", "vp", "mout_mixer", - "sclk_mixer"; - clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>, - <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>, - <&clock CLK_MOUT_MIXER>, <&clock CLK_SCLK_MIXER>; - }; - ppmu_lcd1: ppmu_lcd1@12240000 { compatible = "samsung,exynos-ppmu"; reg = <0x12240000 0x2000>; @@ -427,6 +376,12 @@ cpu-offset = <0x8000>; }; +&camera { + clocks = <&clock CLK_SCLK_CAM0>, <&clock CLK_SCLK_CAM1>, + <&clock CLK_PIXELASYNCM0>, <&clock CLK_PIXELASYNCM1>; + clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0", "pxl_async1"; +}; + &combiner { samsung,combiner-nr = <16>; interrupts = , @@ -447,10 +402,43 @@ ; }; +&fimc_0 { + samsung,pix-limits = <4224 8192 1920 4224>; + samsung,mainscaler-ext; + samsung,cam-if; +}; + +&fimc_1 { + samsung,pix-limits = <4224 8192 1920 4224>; + samsung,mainscaler-ext; + samsung,cam-if; +}; + +&fimc_2 { + samsung,pix-limits = <4224 8192 1920 4224>; + samsung,mainscaler-ext; + samsung,lcd-wb; +}; + +&fimc_3 { + samsung,pix-limits = <1920 8192 1366 1920>; + samsung,rotators = <0>; + samsung,mainscaler-ext; + samsung,lcd-wb; +}; + &mdma1 { power-domains = <&pd_lcd0>; }; +&mixer { + clock-names = "mixer", "hdmi", "sclk_hdmi", "vp", "mout_mixer", + "sclk_mixer"; + clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>, + <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>, + <&clock CLK_MOUT_MIXER>, <&clock CLK_SCLK_MIXER>; +}; + &pmu_system_controller { clock-names = "clkout0", "clkout1", "clkout2", "clkout3", "clkout4", "clkout8", "clkout9"; @@ -468,4 +456,12 @@ power-domains = <&pd_lcd0>; }; +&tmu { + compatible = "samsung,exynos4210-tmu"; + clocks = <&clock CLK_TMU_APBIF>; + clock-names = "tmu_apbif"; + samsung,tmu_gain = <15>; + samsung,tmu_reference_voltage = <7>; +}; + #include "exynos4210-pinctrl.dtsi" -- cgit v1.3-14-g43fede From 0f895e4ef75d36f6e1f05bbd4c075fac869c9b9d Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Mon, 5 Feb 2018 08:57:03 +0100 Subject: ARM: dts: exynos: Use labels instead of full paths in exynos4210-trats Extend camera and fimc nodes by labels, not by full path in Exynos 4210 Trats board. This avoids error-prone redefinition of nodes. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4210-trats.dts | 73 +++++++++++++++++----------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts index aaade17b140e..268bd38510e7 100644 --- a/arch/arm/boot/dts/exynos4210-trats.dts +++ b/arch/arm/boot/dts/exynos4210-trats.dts @@ -148,43 +148,12 @@ }; }; - camera { - pinctrl-names = "default"; - pinctrl-0 = <>; - status = "okay"; - - fimc_0: fimc@11800000 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC0>, - <&clock CLK_SCLK_FIMC0>; - assigned-clock-parents = <&clock CLK_SCLK_MPLL>; - assigned-clock-rates = <0>, <160000000>; - }; - - fimc_1: fimc@11810000 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC1>, - <&clock CLK_SCLK_FIMC1>; - assigned-clock-parents = <&clock CLK_SCLK_MPLL>; - assigned-clock-rates = <0>, <160000000>; - }; - - fimc_2: fimc@11820000 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC2>, - <&clock CLK_SCLK_FIMC2>; - assigned-clock-parents = <&clock CLK_SCLK_MPLL>; - assigned-clock-rates = <0>, <160000000>; - }; +}; - fimc_3: fimc@11830000 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC3>, - <&clock CLK_SCLK_FIMC3>; - assigned-clock-parents = <&clock CLK_SCLK_MPLL>; - assigned-clock-rates = <0>, <160000000>; - }; - }; +&camera { + pinctrl-names = "default"; + pinctrl-0 = <>; + status = "okay"; }; &cpu0 { @@ -234,6 +203,38 @@ vbus-supply = <&safe1_sreg>; }; +&fimc_0 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC0>, + <&clock CLK_SCLK_FIMC0>; + assigned-clock-parents = <&clock CLK_SCLK_MPLL>; + assigned-clock-rates = <0>, <160000000>; +}; + +&fimc_1 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC1>, + <&clock CLK_SCLK_FIMC1>; + assigned-clock-parents = <&clock CLK_SCLK_MPLL>; + assigned-clock-rates = <0>, <160000000>; +}; + +&fimc_2 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC2>, + <&clock CLK_SCLK_FIMC2>; + assigned-clock-parents = <&clock CLK_SCLK_MPLL>; + assigned-clock-rates = <0>, <160000000>; +}; + +&fimc_3 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC3>, + <&clock CLK_SCLK_FIMC3>; + assigned-clock-parents = <&clock CLK_SCLK_MPLL>; + assigned-clock-rates = <0>, <160000000>; +}; + &fimd { status = "okay"; }; -- cgit v1.3-14-g43fede From 7eba413cb741b1b39fabea72ccf1913f1b71087c Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Mon, 5 Feb 2018 08:57:04 +0100 Subject: ARM: dts: exynos: Use labels instead of full paths in exynos4210-universal_c210 Extend camera, fimc, mct and sysram nodes by labels, not by full path in Exynos 4210 Universal C210 board. This avoids error-prone redefinition of nodes. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4210-universal_c210.dts | 115 ++++++++++++------------ 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts index 21fff7cd3aa4..4e6ff97e1ec4 100644 --- a/arch/arm/boot/dts/exynos4210-universal_c210.dts +++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts @@ -28,24 +28,6 @@ stdout-path = &serial_2; }; - sysram@2020000 { - smp-sysram@0 { - status = "disabled"; - }; - - smp-sysram@5000 { - compatible = "samsung,exynos4210-sysram"; - reg = <0x5000 0x1000>; - }; - - smp-sysram@1f000 { - status = "disabled"; - }; - }; - - mct@10050000 { - compatible = "none"; - }; fixed-rate-clocks { xxti { @@ -173,45 +155,6 @@ }; }; - camera { - status = "okay"; - - pinctrl-names = "default"; - pinctrl-0 = <>; - - fimc_0: fimc@11800000 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC0>, - <&clock CLK_SCLK_FIMC0>; - assigned-clock-parents = <&clock CLK_SCLK_MPLL>; - assigned-clock-rates = <0>, <160000000>; - }; - - fimc_1: fimc@11810000 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC1>, - <&clock CLK_SCLK_FIMC1>; - assigned-clock-parents = <&clock CLK_SCLK_MPLL>; - assigned-clock-rates = <0>, <160000000>; - }; - - fimc_2: fimc@11820000 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC2>, - <&clock CLK_SCLK_FIMC2>; - assigned-clock-parents = <&clock CLK_SCLK_MPLL>; - assigned-clock-rates = <0>, <160000000>; - }; - - fimc_3: fimc@11830000 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC3>, - <&clock CLK_SCLK_FIMC3>; - assigned-clock-parents = <&clock CLK_SCLK_MPLL>; - assigned-clock-rates = <0>, <160000000>; - }; - }; - hdmi_en: voltage-regulator-hdmi-5v { compatible = "regulator-fixed"; regulator-name = "HDMI_5V"; @@ -234,6 +177,13 @@ }; }; +&camera { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + &cpu0 { cpu0-supply = <&vdd_arm_reg>; }; @@ -250,6 +200,38 @@ vbus-supply = <&safeout1_reg>; }; +&fimc_0 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC0>, + <&clock CLK_SCLK_FIMC0>; + assigned-clock-parents = <&clock CLK_SCLK_MPLL>; + assigned-clock-rates = <0>, <160000000>; +}; + +&fimc_1 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC1>, + <&clock CLK_SCLK_FIMC1>; + assigned-clock-parents = <&clock CLK_SCLK_MPLL>; + assigned-clock-rates = <0>, <160000000>; +}; + +&fimc_2 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC2>, + <&clock CLK_SCLK_FIMC2>; + assigned-clock-parents = <&clock CLK_SCLK_MPLL>; + assigned-clock-rates = <0>, <160000000>; +}; + +&fimc_3 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC3>, + <&clock CLK_SCLK_FIMC3>; + assigned-clock-parents = <&clock CLK_SCLK_MPLL>; + assigned-clock-rates = <0>, <160000000>; +}; + &fimd { pinctrl-0 = <&lcd_clk>, <&lcd_data24>; pinctrl-names = "default"; @@ -501,6 +483,10 @@ status = "okay"; }; +&mct { + compatible = "none"; +}; + &mdma1 { reg = <0x12840000 0x1000>; }; @@ -579,3 +565,18 @@ /delete-property/dmas; /delete-property/dma-names; }; + +&sysram { + smp-sysram@0 { + status = "disabled"; + }; + + smp-sysram@5000 { + compatible = "samsung,exynos4210-sysram"; + reg = <0x5000 0x1000>; + }; + + smp-sysram@1f000 { + status = "disabled"; + }; +}; -- cgit v1.3-14-g43fede From 8cc347de3650cb2f46bf2b0fe4882a6aa14a10d6 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Mon, 5 Feb 2018 08:57:05 +0100 Subject: ARM: dts: exynos: Use label instead of full path in exynos4412 Extend camera node by label, not by full path in Exynos 4412 DTSI. This avoids error-prone redefinition of nodes. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4412.dtsi | 172 +++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi index a49daf92c623..a6f3f59fce35 100644 --- a/arch/arm/boot/dts/exynos4412.dtsi +++ b/arch/arm/boot/dts/exynos4412.dtsi @@ -283,92 +283,6 @@ iommus = <&sysmmu_g2d>; }; - camera { - clocks = <&clock CLK_SCLK_CAM0>, <&clock CLK_SCLK_CAM1>, - <&clock CLK_PIXELASYNCM0>, <&clock CLK_PIXELASYNCM1>; - clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0", "pxl_async1"; - - /* fimc_[0-3] are configured outside, under phandles */ - fimc_lite_0: fimc-lite@12390000 { - compatible = "samsung,exynos4212-fimc-lite"; - reg = <0x12390000 0x1000>; - interrupts = ; - power-domains = <&pd_isp>; - clocks = <&isp_clock CLK_ISP_FIMC_LITE0>; - clock-names = "flite"; - iommus = <&sysmmu_fimc_lite0>; - status = "disabled"; - }; - - fimc_lite_1: fimc-lite@123a0000 { - compatible = "samsung,exynos4212-fimc-lite"; - reg = <0x123A0000 0x1000>; - interrupts = ; - power-domains = <&pd_isp>; - clocks = <&isp_clock CLK_ISP_FIMC_LITE1>; - clock-names = "flite"; - iommus = <&sysmmu_fimc_lite1>; - status = "disabled"; - }; - - fimc_is: fimc-is@12000000 { - compatible = "samsung,exynos4212-fimc-is"; - reg = <0x12000000 0x260000>; - interrupts = , - ; - power-domains = <&pd_isp>; - clocks = <&isp_clock CLK_ISP_FIMC_LITE0>, - <&isp_clock CLK_ISP_FIMC_LITE1>, - <&isp_clock CLK_ISP_PPMUISPX>, - <&isp_clock CLK_ISP_PPMUISPMX>, - <&isp_clock CLK_ISP_FIMC_ISP>, - <&isp_clock CLK_ISP_FIMC_DRC>, - <&isp_clock CLK_ISP_FIMC_FD>, - <&isp_clock CLK_ISP_MCUISP>, - <&isp_clock CLK_ISP_GICISP>, - <&isp_clock CLK_ISP_MCUCTL_ISP>, - <&isp_clock CLK_ISP_PWM_ISP>, - <&isp_clock CLK_ISP_DIV_ISP0>, - <&isp_clock CLK_ISP_DIV_ISP1>, - <&isp_clock CLK_ISP_DIV_MCUISP0>, - <&isp_clock CLK_ISP_DIV_MCUISP1>, - <&clock CLK_MOUT_MPLL_USER_T>, - <&clock CLK_ACLK200>, - <&clock CLK_ACLK400_MCUISP>, - <&clock CLK_DIV_ACLK200>, - <&clock CLK_DIV_ACLK400_MCUISP>, - <&clock CLK_UART_ISP_SCLK>; - clock-names = "lite0", "lite1", "ppmuispx", - "ppmuispmx", "isp", - "drc", "fd", "mcuisp", - "gicisp", "mcuctl_isp", "pwm_isp", - "ispdiv0", "ispdiv1", "mcuispdiv0", - "mcuispdiv1", "mpll", "aclk200", - "aclk400mcuisp", "div_aclk200", - "div_aclk400mcuisp", "uart"; - iommus = <&sysmmu_fimc_isp>, <&sysmmu_fimc_drc>, - <&sysmmu_fimc_fd>, <&sysmmu_fimc_mcuctl>; - iommu-names = "isp", "drc", "fd", "mcuctl"; - #address-cells = <1>; - #size-cells = <1>; - ranges; - status = "disabled"; - - pmu@10020000 { - reg = <0x10020000 0x3000>; - }; - - i2c1_isp: i2c-isp@12140000 { - compatible = "samsung,exynos4212-i2c-isp"; - reg = <0x12140000 0x100>; - clocks = <&isp_clock CLK_ISP_I2C1_ISP>; - clock-names = "i2c_isp"; - #address-cells = <1>; - #size-cells = <0>; - }; - }; - }; - mshc_0: mmc@12550000 { compatible = "samsung,exynos4412-dw-mshc"; reg = <0x12550000 0x1000>; @@ -662,6 +576,92 @@ ; }; +&camera { + clocks = <&clock CLK_SCLK_CAM0>, <&clock CLK_SCLK_CAM1>, + <&clock CLK_PIXELASYNCM0>, <&clock CLK_PIXELASYNCM1>; + clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0", "pxl_async1"; + + /* fimc_[0-3] are configured outside, under phandles */ + fimc_lite_0: fimc-lite@12390000 { + compatible = "samsung,exynos4212-fimc-lite"; + reg = <0x12390000 0x1000>; + interrupts = ; + power-domains = <&pd_isp>; + clocks = <&isp_clock CLK_ISP_FIMC_LITE0>; + clock-names = "flite"; + iommus = <&sysmmu_fimc_lite0>; + status = "disabled"; + }; + + fimc_lite_1: fimc-lite@123a0000 { + compatible = "samsung,exynos4212-fimc-lite"; + reg = <0x123A0000 0x1000>; + interrupts = ; + power-domains = <&pd_isp>; + clocks = <&isp_clock CLK_ISP_FIMC_LITE1>; + clock-names = "flite"; + iommus = <&sysmmu_fimc_lite1>; + status = "disabled"; + }; + + fimc_is: fimc-is@12000000 { + compatible = "samsung,exynos4212-fimc-is"; + reg = <0x12000000 0x260000>; + interrupts = , + ; + power-domains = <&pd_isp>; + clocks = <&isp_clock CLK_ISP_FIMC_LITE0>, + <&isp_clock CLK_ISP_FIMC_LITE1>, + <&isp_clock CLK_ISP_PPMUISPX>, + <&isp_clock CLK_ISP_PPMUISPMX>, + <&isp_clock CLK_ISP_FIMC_ISP>, + <&isp_clock CLK_ISP_FIMC_DRC>, + <&isp_clock CLK_ISP_FIMC_FD>, + <&isp_clock CLK_ISP_MCUISP>, + <&isp_clock CLK_ISP_GICISP>, + <&isp_clock CLK_ISP_MCUCTL_ISP>, + <&isp_clock CLK_ISP_PWM_ISP>, + <&isp_clock CLK_ISP_DIV_ISP0>, + <&isp_clock CLK_ISP_DIV_ISP1>, + <&isp_clock CLK_ISP_DIV_MCUISP0>, + <&isp_clock CLK_ISP_DIV_MCUISP1>, + <&clock CLK_MOUT_MPLL_USER_T>, + <&clock CLK_ACLK200>, + <&clock CLK_ACLK400_MCUISP>, + <&clock CLK_DIV_ACLK200>, + <&clock CLK_DIV_ACLK400_MCUISP>, + <&clock CLK_UART_ISP_SCLK>; + clock-names = "lite0", "lite1", "ppmuispx", + "ppmuispmx", "isp", + "drc", "fd", "mcuisp", + "gicisp", "mcuctl_isp", "pwm_isp", + "ispdiv0", "ispdiv1", "mcuispdiv0", + "mcuispdiv1", "mpll", "aclk200", + "aclk400mcuisp", "div_aclk200", + "div_aclk400mcuisp", "uart"; + iommus = <&sysmmu_fimc_isp>, <&sysmmu_fimc_drc>, + <&sysmmu_fimc_fd>, <&sysmmu_fimc_mcuctl>; + iommu-names = "isp", "drc", "fd", "mcuctl"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + status = "disabled"; + + pmu@10020000 { + reg = <0x10020000 0x3000>; + }; + + i2c1_isp: i2c-isp@12140000 { + compatible = "samsung,exynos4212-i2c-isp"; + reg = <0x12140000 0x100>; + clocks = <&isp_clock CLK_ISP_I2C1_ISP>; + clock-names = "i2c_isp"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; +}; + &exynos_usbphy { compatible = "samsung,exynos4x12-usb2-phy"; samsung,sysreg-phandle = <&sys_reg>; -- cgit v1.3-14-g43fede From 47c7df789eb0c7f4447c22cef2572361784706f8 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Mon, 5 Feb 2018 08:57:06 +0100 Subject: ARM: dts: exynos: Use label instead of full path in exynos4412-odroid-common Extend camera node by label, not by full path in Exynos 4412 Odroid boards. This avoids error-prone redefinition of nodes. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 556ea78b8e32..d7ad07fd48f9 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -61,12 +61,6 @@ reset-gpios = <&gpk1 2 GPIO_ACTIVE_LOW>; }; - camera { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <>; - }; - fixed-rate-clocks { xxti { compatible = "samsung,clock-xxti"; @@ -142,6 +136,12 @@ status = "okay"; }; +&camera { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + &clock_audss { assigned-clocks = <&clock_audss EXYNOS_MOUT_AUDSS>, <&clock_audss EXYNOS_MOUT_I2S>, -- cgit v1.3-14-g43fede From 8ce5c46d02300e4632140d0e9d560c0a164ba9ff Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Mon, 5 Feb 2018 08:57:07 +0100 Subject: ARM: dts: exynos: Use labels instead of full paths in exynos4412-trats2 Extend camera and i2c1_isp nodes by labels, not by full path in Exynos 4412 Trats2. This avoids error-prone redefinition of nodes. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4412-trats2.dts | 73 ++++++++++++++++----------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts index 7da66003a3e3..95b638fd281f 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts +++ b/arch/arm/boot/dts/exynos4412-trats2.dts @@ -285,17 +285,6 @@ }; }; - camera: camera { - pinctrl-0 = <&cam_port_a_clk_active &cam_port_b_clk_active>; - pinctrl-names = "default"; - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_CAM0>, - <&clock CLK_MOUT_CAM1>; - assigned-clock-parents = <&clock CLK_XUSBXTI>, - <&clock CLK_XUSBXTI>; - - - }; wlan_pwrseq: sdhci3-pwrseq { compatible = "mmc-pwrseq-simple"; @@ -400,6 +389,16 @@ status = "okay"; }; +&camera { + pinctrl-0 = <&cam_port_a_clk_active &cam_port_b_clk_active>; + pinctrl-names = "default"; + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_CAM0>, + <&clock CLK_MOUT_CAM1>; + assigned-clock-parents = <&clock CLK_XUSBXTI>, + <&clock CLK_XUSBXTI>; +}; + &cpu0 { cpu0-supply = <&buck2_reg>; }; @@ -524,32 +523,6 @@ pinctrl-0 = <&fimc_is_uart>; pinctrl-names = "default"; status = "okay"; - - i2c1_isp: i2c-isp@12140000 { - pinctrl-0 = <&fimc_is_i2c1>; - pinctrl-names = "default"; - - s5k6a3@10 { - compatible = "samsung,s5k6a3"; - reg = <0x10>; - svdda-supply = <&cam_io_reg>; - svddio-supply = <&ldo19_reg>; - afvdd-supply = <&ldo19_reg>; - clock-frequency = <24000000>; - /* CAM_B_CLKOUT */ - clocks = <&camera 1>; - clock-names = "extclk"; - samsung,camclk-out = <1>; - gpios = <&gpm1 6 GPIO_ACTIVE_HIGH>; - - port { - is_s5k6a3_ep: endpoint { - remote-endpoint = <&csis1_ep>; - data-lanes = <1>; - }; - }; - }; - }; }; &fimc_lite_0 { @@ -626,6 +599,32 @@ }; }; +&i2c1_isp { + pinctrl-0 = <&fimc_is_i2c1>; + pinctrl-names = "default"; + + s5k6a3@10 { + compatible = "samsung,s5k6a3"; + reg = <0x10>; + svdda-supply = <&cam_io_reg>; + svddio-supply = <&ldo19_reg>; + afvdd-supply = <&ldo19_reg>; + clock-frequency = <24000000>; + /* CAM_B_CLKOUT */ + clocks = <&camera 1>; + clock-names = "extclk"; + samsung,camclk-out = <1>; + gpios = <&gpm1 6 GPIO_ACTIVE_HIGH>; + + port { + is_s5k6a3_ep: endpoint { + remote-endpoint = <&csis1_ep>; + data-lanes = <1>; + }; + }; + }; +}; + &i2c_3 { samsung,i2c-sda-delay = <100>; samsung,i2c-slave-addr = <0x10>; -- cgit v1.3-14-g43fede From de67509a65cee5bf90877521c3eee65d2566586c Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Mon, 5 Feb 2018 08:57:08 +0100 Subject: ARM: dts: exynos: Use label instead of full path in exynos4412-itop-elite Extend camera node by label, not by full path in Exynos 4412 Itop Elite. This avoids error-prone redefinition of nodes. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4412-itop-elite.dts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-itop-elite.dts b/arch/arm/boot/dts/exynos4412-itop-elite.dts index a4cd4939fe9a..0dedeba89b5f 100644 --- a/arch/arm/boot/dts/exynos4412-itop-elite.dts +++ b/arch/arm/boot/dts/exynos4412-itop-elite.dts @@ -116,14 +116,6 @@ compatible = "pwm-beeper"; pwms = <&pwm 0 4000000 PWM_POLARITY_INVERTED>; }; - - camera: camera { - pinctrl-0 = <&cam_port_a_clk_active>; - pinctrl-names = "default"; - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_CAM0>; - assigned-clock-parents = <&clock CLK_XUSBXTI>; - }; }; &adc { @@ -131,6 +123,14 @@ status = "okay"; }; +&camera { + pinctrl-0 = <&cam_port_a_clk_active>; + pinctrl-names = "default"; + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_CAM0>; + assigned-clock-parents = <&clock CLK_XUSBXTI>; +}; + &clock_audss { assigned-clocks = <&clock_audss EXYNOS_MOUT_AUDSS>, <&clock_audss EXYNOS_MOUT_I2S>, -- cgit v1.3-14-g43fede From 32d8a836154fb90771c4dd2674f4c7a2c1d92112 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 30 Jan 2018 22:18:17 +0100 Subject: ARM: dts: exynos: Remove duplicated inclusion of syscon restart nodes on Exynos5410 The exynos-syscon-restart.dtsi is already included by exynos5.dtsi (through exynos54xx.dtsi). Signed-off-by: Krzysztof Kozlowski Tested-by: Marek Szyprowski --- arch/arm/boot/dts/exynos5410.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/boot/dts/exynos5410.dtsi b/arch/arm/boot/dts/exynos5410.dtsi index 1886aa00b2db..1e6b57d013c4 100644 --- a/arch/arm/boot/dts/exynos5410.dtsi +++ b/arch/arm/boot/dts/exynos5410.dtsi @@ -11,7 +11,6 @@ */ #include "exynos54xx.dtsi" -#include "exynos-syscon-restart.dtsi" #include #include #include -- cgit v1.3-14-g43fede From 7353c54620732797dcc3b4b1fc6f3cc0c0d9b6ef Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 30 Jan 2018 22:18:16 +0100 Subject: soc: samsung: pmu: Populate children syscon nodes The syscon poweroff and restart nodes logically belong to the Power Management Unit so populate possible children. This also requires providing compatibles for Exynos5410 and Exynos7 so the PMU device and its children will be instantiated for them as well. Just like Exynos5433, these chipsets are not yet supported by the PMU driver. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Tested-by: Marek Szyprowski --- Documentation/devicetree/bindings/arm/samsung/pmu.txt | 6 ++++++ drivers/soc/samsung/exynos-pmu.c | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.txt b/Documentation/devicetree/bindings/arm/samsung/pmu.txt index 779f5614bcee..16685787d2bd 100644 --- a/Documentation/devicetree/bindings/arm/samsung/pmu.txt +++ b/Documentation/devicetree/bindings/arm/samsung/pmu.txt @@ -43,6 +43,12 @@ following properties: - interrupt-parent: a phandle indicating which interrupt controller this PMU signals interrupts to. + +Optional nodes: + +- nodes defining the restart and poweroff syscon children + + Example : pmu_system_controller: system-controller@10040000 { compatible = "samsung,exynos5250-pmu", "syscon"; diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index f56adbd9fb8b..d34ca201b8b7 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -84,11 +84,15 @@ static const struct of_device_id exynos_pmu_of_device_ids[] = { }, { .compatible = "samsung,exynos5250-pmu", .data = exynos_pmu_data_arm_ptr(exynos5250_pmu_data), + }, { + .compatible = "samsung,exynos5410-pmu", }, { .compatible = "samsung,exynos5420-pmu", .data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data), }, { .compatible = "samsung,exynos5433-pmu", + }, { + .compatible = "samsung,exynos7-pmu", }, { /*sentinel*/ }, }; @@ -126,6 +130,9 @@ static int exynos_pmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pmu_context); + if (devm_of_platform_populate(dev)) + dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n"); + dev_dbg(dev, "Exynos PMU Driver probe done\n"); return 0; } -- cgit v1.3-14-g43fede From 9097b4bd9fceae342afce480f4c1379b9f67052e Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Tue, 6 Feb 2018 12:25:52 +0100 Subject: ARM: dts: exynos: Use pmu label in exynos4412 In order to use soc node, we need to reference soc child nodes by label in exynos4412. Define pmu label in exynos4.dtsi. Use it in exynos4412 instead of redefining the node by full path. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4.dtsi | 2 +- arch/arm/boot/dts/exynos4412.dtsi | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 6d4775a0b402..da284f383279 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -162,7 +162,7 @@ reg = <0x10440000 0x1000>; }; - pmu { + pmu: pmu { compatible = "arm,cortex-a9-pmu"; interrupt-parent = <&combiner>; interrupts = <2 2>, <3 2>; diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi index a6f3f59fce35..e012cdad6595 100644 --- a/arch/arm/boot/dts/exynos4412.dtsi +++ b/arch/arm/boot/dts/exynos4412.dtsi @@ -546,10 +546,6 @@ opp-hz = /bits/ 64 <100000000>; }; }; - - pmu { - interrupts = <2 2>, <3 2>, <18 2>, <19 2>; - }; }; &combiner { @@ -724,6 +720,9 @@ <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>; }; +&pmu { + interrupts = <2 2>, <3 2>, <18 2>, <19 2>; +}; &pmu_system_controller { compatible = "samsung,exynos4412-pmu", "syscon"; -- cgit v1.3-14-g43fede From ca42d8d86ca25a291852c4f235e9cc871dced598 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Tue, 6 Feb 2018 12:31:50 +0100 Subject: ARM: dts: exynos: Add soc node to exynos5440 The exynos5440 device tree is the only one left, which does not use "soc" node. Add a "soc" node to exynos5440.dtsi in order to make it consistent with other exynos DTS. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos5440.dtsi | 514 +++++++++++++++++++------------------- 1 file changed, 261 insertions(+), 253 deletions(-) diff --git a/arch/arm/boot/dts/exynos5440.dtsi b/arch/arm/boot/dts/exynos5440.dtsi index fce9e26b5930..f3abecc44657 100644 --- a/arch/arm/boot/dts/exynos5440.dtsi +++ b/arch/arm/boot/dts/exynos5440.dtsi @@ -26,24 +26,6 @@ tmuctrl2 = &tmuctrl_2; }; - clock: clock-controller@160000 { - compatible = "samsung,exynos5440-clock"; - reg = <0x160000 0x1000>; - #clock-cells = <1>; - }; - - gic: interrupt-controller@2e0000 { - compatible = "arm,cortex-a15-gic"; - #interrupt-cells = <3>; - interrupt-controller; - reg = <0x2E1000 0x1000>, - <0x2E2000 0x2000>, - <0x2E4000 0x2000>, - <0x2E6000 0x2000>; - interrupts = ; - }; - cpus { #address-cells = <1>; #size-cells = <0>; @@ -70,182 +52,290 @@ }; }; - arm-pmu { - compatible = "arm,cortex-a15-pmu", "arm,cortex-a9-pmu"; - interrupts = , - , - , - ; - }; + soc: soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; - timer { - compatible = "arm,cortex-a15-timer", - "arm,armv7-timer"; - interrupts = , - , - , - ; - clock-frequency = <50000000>; - }; + clock: clock-controller@160000 { + compatible = "samsung,exynos5440-clock"; + reg = <0x160000 0x1000>; + #clock-cells = <1>; + }; - cpufreq@160000 { - compatible = "samsung,exynos5440-cpufreq"; - reg = <0x160000 0x1000>; - interrupts = ; - operating-points = < - /* KHz uV */ - 1500000 1100000 - 1400000 1075000 - 1300000 1050000 - 1200000 1025000 - 1100000 1000000 - 1000000 975000 - 900000 950000 - 800000 925000 - >; - }; + gic: interrupt-controller@2e0000 { + compatible = "arm,cortex-a15-gic"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x2E1000 0x1000>, + <0x2E2000 0x2000>, + <0x2E4000 0x2000>, + <0x2E6000 0x2000>; + interrupts = ; + }; - serial_0: serial@b0000 { - compatible = "samsung,exynos4210-uart"; - reg = <0xB0000 0x1000>; - interrupts = ; - clocks = <&clock CLK_B_125>, <&clock CLK_B_125>; - clock-names = "uart", "clk_uart_baud0"; - }; - serial_1: serial@c0000 { - compatible = "samsung,exynos4210-uart"; - reg = <0xC0000 0x1000>; - interrupts = ; - clocks = <&clock CLK_B_125>, <&clock CLK_B_125>; - clock-names = "uart", "clk_uart_baud0"; - }; + arm-pmu { + compatible = "arm,cortex-a15-pmu", "arm,cortex-a9-pmu"; + interrupts = , + , + , + ; + }; - spi_0: spi@d0000 { - compatible = "samsung,exynos5440-spi"; - reg = <0xD0000 0x100>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - samsung,spi-src-clk = <0>; - num-cs = <1>; - clocks = <&clock CLK_B_125>, <&clock CLK_SPI_BAUD>; - clock-names = "spi", "spi_busclk0"; - }; + timer { + compatible = "arm,cortex-a15-timer", + "arm,armv7-timer"; + interrupts = , + , + , + ; + clock-frequency = <50000000>; + }; - pin_ctrl: pinctrl@e0000 { - compatible = "samsung,exynos5440-pinctrl"; - reg = <0xE0000 0x1000>; - interrupts = , - , - , - , - , - , - , - ; - interrupt-controller; - #interrupt-cells = <2>; - #gpio-cells = <2>; + cpufreq@160000 { + compatible = "samsung,exynos5440-cpufreq"; + reg = <0x160000 0x1000>; + interrupts = ; + operating-points = < + /* KHz uV */ + 1500000 1100000 + 1400000 1075000 + 1300000 1050000 + 1200000 1025000 + 1100000 1000000 + 1000000 975000 + 900000 950000 + 800000 925000 + >; + }; - fan: fan { - samsung,exynos5440-pin-function = <1>; + serial_0: serial@b0000 { + compatible = "samsung,exynos4210-uart"; + reg = <0xB0000 0x1000>; + interrupts = ; + clocks = <&clock CLK_B_125>, <&clock CLK_B_125>; + clock-names = "uart", "clk_uart_baud0"; }; - hdd_led0: hdd_led0 { - samsung,exynos5440-pin-function = <2>; + serial_1: serial@c0000 { + compatible = "samsung,exynos4210-uart"; + reg = <0xC0000 0x1000>; + interrupts = ; + clocks = <&clock CLK_B_125>, <&clock CLK_B_125>; + clock-names = "uart", "clk_uart_baud0"; }; - hdd_led1: hdd_led1 { - samsung,exynos5440-pin-function = <3>; + spi_0: spi@d0000 { + compatible = "samsung,exynos5440-spi"; + reg = <0xD0000 0x100>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + samsung,spi-src-clk = <0>; + num-cs = <1>; + clocks = <&clock CLK_B_125>, <&clock CLK_SPI_BAUD>; + clock-names = "spi", "spi_busclk0"; }; - uart1: uart1 { - samsung,exynos5440-pin-function = <4>; + pin_ctrl: pinctrl@e0000 { + compatible = "samsung,exynos5440-pinctrl"; + reg = <0xE0000 0x1000>; + interrupts = , + , + , + , + , + , + , + ; + interrupt-controller; + #interrupt-cells = <2>; + #gpio-cells = <2>; + + fan: fan { + samsung,exynos5440-pin-function = <1>; + }; + + hdd_led0: hdd_led0 { + samsung,exynos5440-pin-function = <2>; + }; + + hdd_led1: hdd_led1 { + samsung,exynos5440-pin-function = <3>; + }; + + uart1: uart1 { + samsung,exynos5440-pin-function = <4>; + }; }; - }; - i2c@f0000 { - compatible = "samsung,exynos5440-i2c"; - reg = <0xF0000 0x1000>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&clock CLK_B_125>; - clock-names = "i2c"; - }; + i2c@f0000 { + compatible = "samsung,exynos5440-i2c"; + reg = <0xF0000 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clock CLK_B_125>; + clock-names = "i2c"; + }; - i2c@100000 { - compatible = "samsung,exynos5440-i2c"; - reg = <0x100000 0x1000>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&clock CLK_B_125>; - clock-names = "i2c"; - }; + i2c@100000 { + compatible = "samsung,exynos5440-i2c"; + reg = <0x100000 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clock CLK_B_125>; + clock-names = "i2c"; + }; - watchdog@110000 { - compatible = "samsung,s3c6410-wdt"; - reg = <0x110000 0x1000>; - interrupts = ; - clocks = <&clock CLK_B_125>; - clock-names = "watchdog"; - }; + watchdog@110000 { + compatible = "samsung,s3c6410-wdt"; + reg = <0x110000 0x1000>; + interrupts = ; + clocks = <&clock CLK_B_125>; + clock-names = "watchdog"; + }; - gmac: ethernet@230000 { - compatible = "snps,dwmac-3.70a", "snps,dwmac"; - reg = <0x00230000 0x8000>; - interrupt-parent = <&gic>; - interrupts = ; - interrupt-names = "macirq"; - phy-mode = "sgmii"; - clocks = <&clock CLK_GMAC0>; - clock-names = "stmmaceth"; - }; + gmac: ethernet@230000 { + compatible = "snps,dwmac-3.70a", "snps,dwmac"; + reg = <0x00230000 0x8000>; + interrupt-parent = <&gic>; + interrupts = ; + interrupt-names = "macirq"; + phy-mode = "sgmii"; + clocks = <&clock CLK_GMAC0>; + clock-names = "stmmaceth"; + }; - amba { - #address-cells = <1>; - #size-cells = <1>; - compatible = "simple-bus"; - interrupt-parent = <&gic>; - ranges; - }; + amba { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&gic>; + ranges; + }; - rtc@130000 { - compatible = "samsung,s3c6410-rtc"; - reg = <0x130000 0x1000>; - interrupts = , - ; - clocks = <&clock CLK_B_125>; - clock-names = "rtc"; - }; + rtc@130000 { + compatible = "samsung,s3c6410-rtc"; + reg = <0x130000 0x1000>; + interrupts = , + ; + clocks = <&clock CLK_B_125>; + clock-names = "rtc"; + }; - tmuctrl_0: tmuctrl@160118 { - compatible = "samsung,exynos5440-tmu"; - reg = <0x160118 0x230>, <0x160368 0x10>; - interrupts = ; - clocks = <&clock CLK_B_125>; - clock-names = "tmu_apbif"; - #include "exynos5440-tmu-sensor-conf.dtsi" - }; + tmuctrl_0: tmuctrl@160118 { + compatible = "samsung,exynos5440-tmu"; + reg = <0x160118 0x230>, <0x160368 0x10>; + interrupts = ; + clocks = <&clock CLK_B_125>; + clock-names = "tmu_apbif"; + #include "exynos5440-tmu-sensor-conf.dtsi" + }; - tmuctrl_1: tmuctrl@16011c { - compatible = "samsung,exynos5440-tmu"; - reg = <0x16011C 0x230>, <0x160368 0x10>; - interrupts = ; - clocks = <&clock CLK_B_125>; - clock-names = "tmu_apbif"; - #include "exynos5440-tmu-sensor-conf.dtsi" - }; + tmuctrl_1: tmuctrl@16011c { + compatible = "samsung,exynos5440-tmu"; + reg = <0x16011C 0x230>, <0x160368 0x10>; + interrupts = ; + clocks = <&clock CLK_B_125>; + clock-names = "tmu_apbif"; + #include "exynos5440-tmu-sensor-conf.dtsi" + }; + + tmuctrl_2: tmuctrl@160120 { + compatible = "samsung,exynos5440-tmu"; + reg = <0x160120 0x230>, <0x160368 0x10>; + interrupts = ; + clocks = <&clock CLK_B_125>; + clock-names = "tmu_apbif"; + #include "exynos5440-tmu-sensor-conf.dtsi" + }; - tmuctrl_2: tmuctrl@160120 { - compatible = "samsung,exynos5440-tmu"; - reg = <0x160120 0x230>, <0x160368 0x10>; - interrupts = ; - clocks = <&clock CLK_B_125>; - clock-names = "tmu_apbif"; - #include "exynos5440-tmu-sensor-conf.dtsi" + sata@210000 { + compatible = "snps,exynos5440-ahci"; + reg = <0x210000 0x10000>; + interrupts = ; + clocks = <&clock CLK_SATA>; + clock-names = "sata"; + }; + + ohci@220000 { + compatible = "samsung,exynos5440-ohci"; + reg = <0x220000 0x1000>; + interrupts = ; + clocks = <&clock CLK_USB>; + clock-names = "usbhost"; + }; + + ehci@221000 { + compatible = "samsung,exynos5440-ehci"; + reg = <0x221000 0x1000>; + interrupts = ; + clocks = <&clock CLK_USB>; + clock-names = "usbhost"; + }; + + pcie_phy0: pcie-phy@270000 { + #phy-cells = <0>; + compatible = "samsung,exynos5440-pcie-phy"; + reg = <0x270000 0x1000>, <0x271000 0x40>; + }; + + pcie_phy1: pcie-phy@272000 { + #phy-cells = <0>; + compatible = "samsung,exynos5440-pcie-phy"; + reg = <0x272000 0x1000>, <0x271040 0x40>; + }; + + pcie_0: pcie@290000 { + compatible = "samsung,exynos5440-pcie", "snps,dw-pcie"; + reg = <0x290000 0x1000>, <0x40000000 0x1000>; + reg-names = "elbi", "config"; + interrupts = , + , + ; + clocks = <&clock CLK_PR0_250_O>, <&clock CLK_PB0_250_O>; + clock-names = "pcie", "pcie_bus"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + phys = <&pcie_phy0>; + ranges = <0x81000000 0 0 0x40001000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>; /* non-prefetchable memory */ + bus-range = <0x00 0xff>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0x0 0 &gic 53>; + num-lanes = <4>; + status = "disabled"; + }; + + pcie_1: pcie@2a0000 { + compatible = "samsung,exynos5440-pcie", "snps,dw-pcie"; + reg = <0x2a0000 0x1000>, <0x60000000 0x1000>; + reg-names = "elbi", "config"; + interrupts = , + , + ; + clocks = <&clock CLK_PR1_250_O>, <&clock CLK_PB0_250_O>; + clock-names = "pcie", "pcie_bus"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + phys = <&pcie_phy1>; + ranges = <0x81000000 0 0 0x60001000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>; /* non-prefetchable memory */ + bus-range = <0x00 0xff>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0x0 0 &gic 56>; + num-lanes = <4>; + status = "disabled"; + }; }; thermal-zones { @@ -262,86 +352,4 @@ #include "exynos5440-trip-points.dtsi" }; }; - - sata@210000 { - compatible = "snps,exynos5440-ahci"; - reg = <0x210000 0x10000>; - interrupts = ; - clocks = <&clock CLK_SATA>; - clock-names = "sata"; - }; - - ohci@220000 { - compatible = "samsung,exynos5440-ohci"; - reg = <0x220000 0x1000>; - interrupts = ; - clocks = <&clock CLK_USB>; - clock-names = "usbhost"; - }; - - ehci@221000 { - compatible = "samsung,exynos5440-ehci"; - reg = <0x221000 0x1000>; - interrupts = ; - clocks = <&clock CLK_USB>; - clock-names = "usbhost"; - }; - - pcie_phy0: pcie-phy@270000 { - #phy-cells = <0>; - compatible = "samsung,exynos5440-pcie-phy"; - reg = <0x270000 0x1000>, <0x271000 0x40>; - }; - - pcie_phy1: pcie-phy@272000 { - #phy-cells = <0>; - compatible = "samsung,exynos5440-pcie-phy"; - reg = <0x272000 0x1000>, <0x271040 0x40>; - }; - - pcie_0: pcie@290000 { - compatible = "samsung,exynos5440-pcie", "snps,dw-pcie"; - reg = <0x290000 0x1000>, <0x40000000 0x1000>; - reg-names = "elbi", "config"; - interrupts = , - , - ; - clocks = <&clock CLK_PR0_250_O>, <&clock CLK_PB0_250_O>; - clock-names = "pcie", "pcie_bus"; - #address-cells = <3>; - #size-cells = <2>; - device_type = "pci"; - phys = <&pcie_phy0>; - ranges = <0x81000000 0 0 0x40001000 0 0x00010000 /* downstream I/O */ - 0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>; /* non-prefetchable memory */ - bus-range = <0x00 0xff>; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0x0 0 &gic 53>; - num-lanes = <4>; - status = "disabled"; - }; - - pcie_1: pcie@2a0000 { - compatible = "samsung,exynos5440-pcie", "snps,dw-pcie"; - reg = <0x2a0000 0x1000>, <0x60000000 0x1000>; - reg-names = "elbi", "config"; - interrupts = , - , - ; - clocks = <&clock CLK_PR1_250_O>, <&clock CLK_PB0_250_O>; - clock-names = "pcie", "pcie_bus"; - #address-cells = <3>; - #size-cells = <2>; - device_type = "pci"; - phys = <&pcie_phy1>; - ranges = <0x81000000 0 0 0x60001000 0 0x00010000 /* downstream I/O */ - 0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>; /* non-prefetchable memory */ - bus-range = <0x00 0xff>; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0x0 0 &gic 56>; - num-lanes = <4>; - status = "disabled"; - }; }; -- cgit v1.3-14-g43fede From 73a901d09a2156d77a8ba2944f51faea127c31b5 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Tue, 6 Feb 2018 12:29:40 +0100 Subject: ARM: dts: exynos: Add soc node to exynos4 Soc nodes are used in other exynos DTS. Exynos4 boards should use them as well. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4.dtsi | 1727 ++++++++++++++++++++-------------------- 1 file changed, 872 insertions(+), 855 deletions(-) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index da284f383279..967cd02401c5 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -52,965 +52,982 @@ serial3 = &serial_3; }; - clock_audss: clock-controller@3810000 { - compatible = "samsung,exynos4210-audss-clock"; - reg = <0x03810000 0x0C>; - #clock-cells = <1>; - clocks = <&clock CLK_FIN_PLL>, <&clock CLK_FOUT_EPLL>, - <&clock CLK_SCLK_AUDIO0>, <&clock CLK_SCLK_AUDIO0>; - clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in"; - }; - - i2s0: i2s@3830000 { - compatible = "samsung,s5pv210-i2s"; - reg = <0x03830000 0x100>; - clocks = <&clock_audss EXYNOS_I2S_BUS>, - <&clock_audss EXYNOS_DOUT_AUD_BUS>, - <&clock_audss EXYNOS_SCLK_I2S>; - clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; - #clock-cells = <1>; - clock-output-names = "i2s_cdclk0"; - dmas = <&pdma0 12>, <&pdma0 11>, <&pdma0 10>; - dma-names = "tx", "rx", "tx-sec"; - samsung,idma-addr = <0x03000000>; - #sound-dai-cells = <1>; - status = "disabled"; - }; + soc: soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; - chipid@10000000 { - compatible = "samsung,exynos4210-chipid"; - reg = <0x10000000 0x100>; - }; + clock_audss: clock-controller@3810000 { + compatible = "samsung,exynos4210-audss-clock"; + reg = <0x03810000 0x0C>; + #clock-cells = <1>; + clocks = <&clock CLK_FIN_PLL>, <&clock CLK_FOUT_EPLL>, + <&clock CLK_SCLK_AUDIO0>, + <&clock CLK_SCLK_AUDIO0>; + clock-names = "pll_ref", "pll_in", "sclk_audio", + "sclk_pcm_in"; + }; - scu: snoop-control-unit@10500000 { - compatible = "arm,cortex-a9-scu"; - reg = <0x10500000 0x2000>; - }; + i2s0: i2s@3830000 { + compatible = "samsung,s5pv210-i2s"; + reg = <0x03830000 0x100>; + clocks = <&clock_audss EXYNOS_I2S_BUS>, + <&clock_audss EXYNOS_DOUT_AUD_BUS>, + <&clock_audss EXYNOS_SCLK_I2S>; + clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; + #clock-cells = <1>; + clock-output-names = "i2s_cdclk0"; + dmas = <&pdma0 12>, <&pdma0 11>, <&pdma0 10>; + dma-names = "tx", "rx", "tx-sec"; + samsung,idma-addr = <0x03000000>; + #sound-dai-cells = <1>; + status = "disabled"; + }; - memory-controller@12570000 { - compatible = "samsung,exynos4210-srom"; - reg = <0x12570000 0x14>; - }; + chipid@10000000 { + compatible = "samsung,exynos4210-chipid"; + reg = <0x10000000 0x100>; + }; - mipi_phy: video-phy { - compatible = "samsung,s5pv210-mipi-video-phy"; - #phy-cells = <1>; - syscon = <&pmu_system_controller>; - }; + scu: snoop-control-unit@10500000 { + compatible = "arm,cortex-a9-scu"; + reg = <0x10500000 0x2000>; + }; - pd_mfc: mfc-power-domain@10023c40 { - compatible = "samsung,exynos4210-pd"; - reg = <0x10023C40 0x20>; - #power-domain-cells = <0>; - label = "MFC"; - }; + memory-controller@12570000 { + compatible = "samsung,exynos4210-srom"; + reg = <0x12570000 0x14>; + }; - pd_g3d: g3d-power-domain@10023c60 { - compatible = "samsung,exynos4210-pd"; - reg = <0x10023C60 0x20>; - #power-domain-cells = <0>; - label = "G3D"; - }; + mipi_phy: video-phy { + compatible = "samsung,s5pv210-mipi-video-phy"; + #phy-cells = <1>; + syscon = <&pmu_system_controller>; + }; - pd_lcd0: lcd0-power-domain@10023c80 { - compatible = "samsung,exynos4210-pd"; - reg = <0x10023C80 0x20>; - #power-domain-cells = <0>; - label = "LCD0"; - }; + pd_mfc: mfc-power-domain@10023c40 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C40 0x20>; + #power-domain-cells = <0>; + label = "MFC"; + }; - pd_tv: tv-power-domain@10023c20 { - compatible = "samsung,exynos4210-pd"; - reg = <0x10023C20 0x20>; - #power-domain-cells = <0>; - power-domains = <&pd_lcd0>; - label = "TV"; - }; + pd_g3d: g3d-power-domain@10023c60 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C60 0x20>; + #power-domain-cells = <0>; + label = "G3D"; + }; - pd_cam: cam-power-domain@10023c00 { - compatible = "samsung,exynos4210-pd"; - reg = <0x10023C00 0x20>; - #power-domain-cells = <0>; - label = "CAM"; - }; + pd_lcd0: lcd0-power-domain@10023c80 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C80 0x20>; + #power-domain-cells = <0>; + label = "LCD0"; + }; - pd_gps: gps-power-domain@10023ce0 { - compatible = "samsung,exynos4210-pd"; - reg = <0x10023CE0 0x20>; - #power-domain-cells = <0>; - label = "GPS"; - }; + pd_tv: tv-power-domain@10023c20 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C20 0x20>; + #power-domain-cells = <0>; + power-domains = <&pd_lcd0>; + label = "TV"; + }; - pd_gps_alive: gps-alive-power-domain@10023d00 { - compatible = "samsung,exynos4210-pd"; - reg = <0x10023D00 0x20>; - #power-domain-cells = <0>; - label = "GPS alive"; - }; + pd_cam: cam-power-domain@10023c00 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C00 0x20>; + #power-domain-cells = <0>; + label = "CAM"; + }; - gic: interrupt-controller@10490000 { - compatible = "arm,cortex-a9-gic"; - #interrupt-cells = <3>; - interrupt-controller; - reg = <0x10490000 0x10000>, <0x10480000 0x10000>; - }; + pd_gps: gps-power-domain@10023ce0 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023CE0 0x20>; + #power-domain-cells = <0>; + label = "GPS"; + }; - combiner: interrupt-controller@10440000 { - compatible = "samsung,exynos4210-combiner"; - #interrupt-cells = <2>; - interrupt-controller; - reg = <0x10440000 0x1000>; - }; + pd_gps_alive: gps-alive-power-domain@10023d00 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023D00 0x20>; + #power-domain-cells = <0>; + label = "GPS alive"; + }; - pmu: pmu { - compatible = "arm,cortex-a9-pmu"; - interrupt-parent = <&combiner>; - interrupts = <2 2>, <3 2>; - }; + gic: interrupt-controller@10490000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x10490000 0x10000>, <0x10480000 0x10000>; + }; - sys_reg: syscon@10010000 { - compatible = "samsung,exynos4-sysreg", "syscon"; - reg = <0x10010000 0x400>; - }; + combiner: interrupt-controller@10440000 { + compatible = "samsung,exynos4210-combiner"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0x10440000 0x1000>; + }; - pmu_system_controller: system-controller@10020000 { - compatible = "samsung,exynos4210-pmu", "syscon"; - reg = <0x10020000 0x4000>; - interrupt-controller; - #interrupt-cells = <3>; - interrupt-parent = <&gic>; - }; + pmu: pmu { + compatible = "arm,cortex-a9-pmu"; + interrupt-parent = <&combiner>; + interrupts = <2 2>, <3 2>; + }; - dsi_0: dsi@11c80000 { - compatible = "samsung,exynos4210-mipi-dsi"; - reg = <0x11C80000 0x10000>; - interrupts = ; - power-domains = <&pd_lcd0>; - phys = <&mipi_phy 1>; - phy-names = "dsim"; - clocks = <&clock CLK_DSIM0>, <&clock CLK_SCLK_MIPI0>; - clock-names = "bus_clk", "sclk_mipi"; - status = "disabled"; - #address-cells = <1>; - #size-cells = <0>; - }; + sys_reg: syscon@10010000 { + compatible = "samsung,exynos4-sysreg", "syscon"; + reg = <0x10010000 0x400>; + }; - camera: camera { - compatible = "samsung,fimc", "simple-bus"; - status = "disabled"; - #address-cells = <1>; - #size-cells = <1>; - #clock-cells = <1>; - clock-output-names = "cam_a_clkout", "cam_b_clkout"; - ranges; + pmu_system_controller: system-controller@10020000 { + compatible = "samsung,exynos4210-pmu", "syscon"; + reg = <0x10020000 0x4000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + }; - fimc_0: fimc@11800000 { - compatible = "samsung,exynos4210-fimc"; - reg = <0x11800000 0x1000>; - interrupts = ; - clocks = <&clock CLK_FIMC0>, <&clock CLK_SCLK_FIMC0>; - clock-names = "fimc", "sclk_fimc"; - power-domains = <&pd_cam>; - samsung,sysreg = <&sys_reg>; - iommus = <&sysmmu_fimc0>; + dsi_0: dsi@11c80000 { + compatible = "samsung,exynos4210-mipi-dsi"; + reg = <0x11C80000 0x10000>; + interrupts = ; + power-domains = <&pd_lcd0>; + phys = <&mipi_phy 1>; + phy-names = "dsim"; + clocks = <&clock CLK_DSIM0>, <&clock CLK_SCLK_MIPI0>; + clock-names = "bus_clk", "sclk_mipi"; status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; }; - fimc_1: fimc@11810000 { - compatible = "samsung,exynos4210-fimc"; - reg = <0x11810000 0x1000>; - interrupts = ; - clocks = <&clock CLK_FIMC1>, <&clock CLK_SCLK_FIMC1>; - clock-names = "fimc", "sclk_fimc"; - power-domains = <&pd_cam>; - samsung,sysreg = <&sys_reg>; - iommus = <&sysmmu_fimc1>; + camera: camera { + compatible = "samsung,fimc", "simple-bus"; status = "disabled"; + #address-cells = <1>; + #size-cells = <1>; + #clock-cells = <1>; + clock-output-names = "cam_a_clkout", "cam_b_clkout"; + ranges; + + fimc_0: fimc@11800000 { + compatible = "samsung,exynos4210-fimc"; + reg = <0x11800000 0x1000>; + interrupts = ; + clocks = <&clock CLK_FIMC0>, + <&clock CLK_SCLK_FIMC0>; + clock-names = "fimc", "sclk_fimc"; + power-domains = <&pd_cam>; + samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc0>; + status = "disabled"; + }; + + fimc_1: fimc@11810000 { + compatible = "samsung,exynos4210-fimc"; + reg = <0x11810000 0x1000>; + interrupts = ; + clocks = <&clock CLK_FIMC1>, + <&clock CLK_SCLK_FIMC1>; + clock-names = "fimc", "sclk_fimc"; + power-domains = <&pd_cam>; + samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc1>; + status = "disabled"; + }; + + fimc_2: fimc@11820000 { + compatible = "samsung,exynos4210-fimc"; + reg = <0x11820000 0x1000>; + interrupts = ; + clocks = <&clock CLK_FIMC2>, + <&clock CLK_SCLK_FIMC2>; + clock-names = "fimc", "sclk_fimc"; + power-domains = <&pd_cam>; + samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc2>; + status = "disabled"; + }; + + fimc_3: fimc@11830000 { + compatible = "samsung,exynos4210-fimc"; + reg = <0x11830000 0x1000>; + interrupts = ; + clocks = <&clock CLK_FIMC3>, + <&clock CLK_SCLK_FIMC3>; + clock-names = "fimc", "sclk_fimc"; + power-domains = <&pd_cam>; + samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc3>; + status = "disabled"; + }; + + csis_0: csis@11880000 { + compatible = "samsung,exynos4210-csis"; + reg = <0x11880000 0x4000>; + interrupts = ; + clocks = <&clock CLK_CSIS0>, + <&clock CLK_SCLK_CSIS0>; + clock-names = "csis", "sclk_csis"; + bus-width = <4>; + power-domains = <&pd_cam>; + phys = <&mipi_phy 0>; + phy-names = "csis"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + csis_1: csis@11890000 { + compatible = "samsung,exynos4210-csis"; + reg = <0x11890000 0x4000>; + interrupts = ; + clocks = <&clock CLK_CSIS1>, + <&clock CLK_SCLK_CSIS1>; + clock-names = "csis", "sclk_csis"; + bus-width = <2>; + power-domains = <&pd_cam>; + phys = <&mipi_phy 2>; + phy-names = "csis"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; }; - fimc_2: fimc@11820000 { - compatible = "samsung,exynos4210-fimc"; - reg = <0x11820000 0x1000>; - interrupts = ; - clocks = <&clock CLK_FIMC2>, <&clock CLK_SCLK_FIMC2>; - clock-names = "fimc", "sclk_fimc"; - power-domains = <&pd_cam>; - samsung,sysreg = <&sys_reg>; - iommus = <&sysmmu_fimc2>; + rtc: rtc@10070000 { + compatible = "samsung,s3c6410-rtc"; + reg = <0x10070000 0x100>; + interrupt-parent = <&pmu_system_controller>; + interrupts = , + ; + clocks = <&clock CLK_RTC>; + clock-names = "rtc"; status = "disabled"; }; - fimc_3: fimc@11830000 { - compatible = "samsung,exynos4210-fimc"; - reg = <0x11830000 0x1000>; - interrupts = ; - clocks = <&clock CLK_FIMC3>, <&clock CLK_SCLK_FIMC3>; - clock-names = "fimc", "sclk_fimc"; - power-domains = <&pd_cam>; - samsung,sysreg = <&sys_reg>; - iommus = <&sysmmu_fimc3>; + keypad: keypad@100a0000 { + compatible = "samsung,s5pv210-keypad"; + reg = <0x100A0000 0x100>; + interrupts = ; + clocks = <&clock CLK_KEYIF>; + clock-names = "keypad"; status = "disabled"; }; - csis_0: csis@11880000 { - compatible = "samsung,exynos4210-csis"; - reg = <0x11880000 0x4000>; - interrupts = ; - clocks = <&clock CLK_CSIS0>, <&clock CLK_SCLK_CSIS0>; - clock-names = "csis", "sclk_csis"; - bus-width = <4>; - power-domains = <&pd_cam>; - phys = <&mipi_phy 0>; - phy-names = "csis"; + sdhci_0: sdhci@12510000 { + compatible = "samsung,exynos4210-sdhci"; + reg = <0x12510000 0x100>; + interrupts = ; + clocks = <&clock CLK_SDMMC0>, <&clock CLK_SCLK_MMC0>; + clock-names = "hsmmc", "mmc_busclk.2"; status = "disabled"; - #address-cells = <1>; - #size-cells = <0>; }; - csis_1: csis@11890000 { - compatible = "samsung,exynos4210-csis"; - reg = <0x11890000 0x4000>; - interrupts = ; - clocks = <&clock CLK_CSIS1>, <&clock CLK_SCLK_CSIS1>; - clock-names = "csis", "sclk_csis"; - bus-width = <2>; - power-domains = <&pd_cam>; - phys = <&mipi_phy 2>; - phy-names = "csis"; + sdhci_1: sdhci@12520000 { + compatible = "samsung,exynos4210-sdhci"; + reg = <0x12520000 0x100>; + interrupts = ; + clocks = <&clock CLK_SDMMC1>, <&clock CLK_SCLK_MMC1>; + clock-names = "hsmmc", "mmc_busclk.2"; status = "disabled"; - #address-cells = <1>; - #size-cells = <0>; }; - }; - - rtc: rtc@10070000 { - compatible = "samsung,s3c6410-rtc"; - reg = <0x10070000 0x100>; - interrupt-parent = <&pmu_system_controller>; - interrupts = , - ; - clocks = <&clock CLK_RTC>; - clock-names = "rtc"; - status = "disabled"; - }; - - keypad: keypad@100a0000 { - compatible = "samsung,s5pv210-keypad"; - reg = <0x100A0000 0x100>; - interrupts = ; - clocks = <&clock CLK_KEYIF>; - clock-names = "keypad"; - status = "disabled"; - }; - - sdhci_0: sdhci@12510000 { - compatible = "samsung,exynos4210-sdhci"; - reg = <0x12510000 0x100>; - interrupts = ; - clocks = <&clock CLK_SDMMC0>, <&clock CLK_SCLK_MMC0>; - clock-names = "hsmmc", "mmc_busclk.2"; - status = "disabled"; - }; - sdhci_1: sdhci@12520000 { - compatible = "samsung,exynos4210-sdhci"; - reg = <0x12520000 0x100>; - interrupts = ; - clocks = <&clock CLK_SDMMC1>, <&clock CLK_SCLK_MMC1>; - clock-names = "hsmmc", "mmc_busclk.2"; - status = "disabled"; - }; - - sdhci_2: sdhci@12530000 { - compatible = "samsung,exynos4210-sdhci"; - reg = <0x12530000 0x100>; - interrupts = ; - clocks = <&clock CLK_SDMMC2>, <&clock CLK_SCLK_MMC2>; - clock-names = "hsmmc", "mmc_busclk.2"; - status = "disabled"; - }; - - sdhci_3: sdhci@12540000 { - compatible = "samsung,exynos4210-sdhci"; - reg = <0x12540000 0x100>; - interrupts = ; - clocks = <&clock CLK_SDMMC3>, <&clock CLK_SCLK_MMC3>; - clock-names = "hsmmc", "mmc_busclk.2"; - status = "disabled"; - }; + sdhci_2: sdhci@12530000 { + compatible = "samsung,exynos4210-sdhci"; + reg = <0x12530000 0x100>; + interrupts = ; + clocks = <&clock CLK_SDMMC2>, <&clock CLK_SCLK_MMC2>; + clock-names = "hsmmc", "mmc_busclk.2"; + status = "disabled"; + }; - exynos_usbphy: exynos-usbphy@125b0000 { - compatible = "samsung,exynos4210-usb2-phy"; - reg = <0x125B0000 0x100>; - samsung,pmureg-phandle = <&pmu_system_controller>; - clocks = <&clock CLK_USB_DEVICE>, <&clock CLK_XUSBXTI>; - clock-names = "phy", "ref"; - #phy-cells = <1>; - status = "disabled"; - }; + sdhci_3: sdhci@12540000 { + compatible = "samsung,exynos4210-sdhci"; + reg = <0x12540000 0x100>; + interrupts = ; + clocks = <&clock CLK_SDMMC3>, <&clock CLK_SCLK_MMC3>; + clock-names = "hsmmc", "mmc_busclk.2"; + status = "disabled"; + }; - hsotg: hsotg@12480000 { - compatible = "samsung,s3c6400-hsotg"; - reg = <0x12480000 0x20000>; - interrupts = ; - clocks = <&clock CLK_USB_DEVICE>; - clock-names = "otg"; - phys = <&exynos_usbphy 0>; - phy-names = "usb2-phy"; - status = "disabled"; - }; + exynos_usbphy: exynos-usbphy@125b0000 { + compatible = "samsung,exynos4210-usb2-phy"; + reg = <0x125B0000 0x100>; + samsung,pmureg-phandle = <&pmu_system_controller>; + clocks = <&clock CLK_USB_DEVICE>, <&clock CLK_XUSBXTI>; + clock-names = "phy", "ref"; + #phy-cells = <1>; + status = "disabled"; + }; - ehci: ehci@12580000 { - compatible = "samsung,exynos4210-ehci"; - reg = <0x12580000 0x100>; - interrupts = ; - clocks = <&clock CLK_USB_HOST>; - clock-names = "usbhost"; - status = "disabled"; - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&exynos_usbphy 1>; + hsotg: hsotg@12480000 { + compatible = "samsung,s3c6400-hsotg"; + reg = <0x12480000 0x20000>; + interrupts = ; + clocks = <&clock CLK_USB_DEVICE>; + clock-names = "otg"; + phys = <&exynos_usbphy 0>; + phy-names = "usb2-phy"; status = "disabled"; }; - port@1 { - reg = <1>; - phys = <&exynos_usbphy 2>; + + ehci: ehci@12580000 { + compatible = "samsung,exynos4210-ehci"; + reg = <0x12580000 0x100>; + interrupts = ; + clocks = <&clock CLK_USB_HOST>; + clock-names = "usbhost"; status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + phys = <&exynos_usbphy 1>; + status = "disabled"; + }; + port@1 { + reg = <1>; + phys = <&exynos_usbphy 2>; + status = "disabled"; + }; + port@2 { + reg = <2>; + phys = <&exynos_usbphy 3>; + status = "disabled"; + }; }; - port@2 { - reg = <2>; - phys = <&exynos_usbphy 3>; + + ohci: ohci@12590000 { + compatible = "samsung,exynos4210-ohci"; + reg = <0x12590000 0x100>; + interrupts = ; + clocks = <&clock CLK_USB_HOST>; + clock-names = "usbhost"; status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + phys = <&exynos_usbphy 1>; + status = "disabled"; + }; }; - }; - ohci: ohci@12590000 { - compatible = "samsung,exynos4210-ohci"; - reg = <0x12590000 0x100>; - interrupts = ; - clocks = <&clock CLK_USB_HOST>; - clock-names = "usbhost"; - status = "disabled"; - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - phys = <&exynos_usbphy 1>; + i2s1: i2s@13960000 { + compatible = "samsung,s3c6410-i2s"; + reg = <0x13960000 0x100>; + clocks = <&clock CLK_I2S1>; + clock-names = "iis"; + #clock-cells = <1>; + clock-output-names = "i2s_cdclk1"; + dmas = <&pdma1 12>, <&pdma1 11>; + dma-names = "tx", "rx"; + #sound-dai-cells = <1>; status = "disabled"; }; - }; - i2s1: i2s@13960000 { - compatible = "samsung,s3c6410-i2s"; - reg = <0x13960000 0x100>; - clocks = <&clock CLK_I2S1>; - clock-names = "iis"; - #clock-cells = <1>; - clock-output-names = "i2s_cdclk1"; - dmas = <&pdma1 12>, <&pdma1 11>; - dma-names = "tx", "rx"; - #sound-dai-cells = <1>; - status = "disabled"; - }; + i2s2: i2s@13970000 { + compatible = "samsung,s3c6410-i2s"; + reg = <0x13970000 0x100>; + clocks = <&clock CLK_I2S2>; + clock-names = "iis"; + #clock-cells = <1>; + clock-output-names = "i2s_cdclk2"; + dmas = <&pdma0 14>, <&pdma0 13>; + dma-names = "tx", "rx"; + #sound-dai-cells = <1>; + status = "disabled"; + }; - i2s2: i2s@13970000 { - compatible = "samsung,s3c6410-i2s"; - reg = <0x13970000 0x100>; - clocks = <&clock CLK_I2S2>; - clock-names = "iis"; - #clock-cells = <1>; - clock-output-names = "i2s_cdclk2"; - dmas = <&pdma0 14>, <&pdma0 13>; - dma-names = "tx", "rx"; - #sound-dai-cells = <1>; - status = "disabled"; - }; + mfc: codec@13400000 { + compatible = "samsung,mfc-v5"; + reg = <0x13400000 0x10000>; + interrupts = ; + power-domains = <&pd_mfc>; + clocks = <&clock CLK_MFC>, <&clock CLK_SCLK_MFC>; + clock-names = "mfc", "sclk_mfc"; + iommus = <&sysmmu_mfc_l>, <&sysmmu_mfc_r>; + iommu-names = "left", "right"; + }; - mfc: codec@13400000 { - compatible = "samsung,mfc-v5"; - reg = <0x13400000 0x10000>; - interrupts = ; - power-domains = <&pd_mfc>; - clocks = <&clock CLK_MFC>, <&clock CLK_SCLK_MFC>; - clock-names = "mfc", "sclk_mfc"; - iommus = <&sysmmu_mfc_l>, <&sysmmu_mfc_r>; - iommu-names = "left", "right"; - }; + serial_0: serial@13800000 { + compatible = "samsung,exynos4210-uart"; + reg = <0x13800000 0x100>; + interrupts = ; + clocks = <&clock CLK_UART0>, <&clock CLK_SCLK_UART0>; + clock-names = "uart", "clk_uart_baud0"; + dmas = <&pdma0 15>, <&pdma0 16>; + dma-names = "rx", "tx"; + status = "disabled"; + }; - serial_0: serial@13800000 { - compatible = "samsung,exynos4210-uart"; - reg = <0x13800000 0x100>; - interrupts = ; - clocks = <&clock CLK_UART0>, <&clock CLK_SCLK_UART0>; - clock-names = "uart", "clk_uart_baud0"; - dmas = <&pdma0 15>, <&pdma0 16>; - dma-names = "rx", "tx"; - status = "disabled"; - }; + serial_1: serial@13810000 { + compatible = "samsung,exynos4210-uart"; + reg = <0x13810000 0x100>; + interrupts = ; + clocks = <&clock CLK_UART1>, <&clock CLK_SCLK_UART1>; + clock-names = "uart", "clk_uart_baud0"; + dmas = <&pdma1 15>, <&pdma1 16>; + dma-names = "rx", "tx"; + status = "disabled"; + }; - serial_1: serial@13810000 { - compatible = "samsung,exynos4210-uart"; - reg = <0x13810000 0x100>; - interrupts = ; - clocks = <&clock CLK_UART1>, <&clock CLK_SCLK_UART1>; - clock-names = "uart", "clk_uart_baud0"; - dmas = <&pdma1 15>, <&pdma1 16>; - dma-names = "rx", "tx"; - status = "disabled"; - }; + serial_2: serial@13820000 { + compatible = "samsung,exynos4210-uart"; + reg = <0x13820000 0x100>; + interrupts = ; + clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>; + clock-names = "uart", "clk_uart_baud0"; + dmas = <&pdma0 17>, <&pdma0 18>; + dma-names = "rx", "tx"; + status = "disabled"; + }; - serial_2: serial@13820000 { - compatible = "samsung,exynos4210-uart"; - reg = <0x13820000 0x100>; - interrupts = ; - clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>; - clock-names = "uart", "clk_uart_baud0"; - dmas = <&pdma0 17>, <&pdma0 18>; - dma-names = "rx", "tx"; - status = "disabled"; - }; + serial_3: serial@13830000 { + compatible = "samsung,exynos4210-uart"; + reg = <0x13830000 0x100>; + interrupts = ; + clocks = <&clock CLK_UART3>, <&clock CLK_SCLK_UART3>; + clock-names = "uart", "clk_uart_baud0"; + dmas = <&pdma1 17>, <&pdma1 18>; + dma-names = "rx", "tx"; + status = "disabled"; + }; - serial_3: serial@13830000 { - compatible = "samsung,exynos4210-uart"; - reg = <0x13830000 0x100>; - interrupts = ; - clocks = <&clock CLK_UART3>, <&clock CLK_SCLK_UART3>; - clock-names = "uart", "clk_uart_baud0"; - dmas = <&pdma1 17>, <&pdma1 18>; - dma-names = "rx", "tx"; - status = "disabled"; - }; + i2c_0: i2c@13860000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-i2c"; + reg = <0x13860000 0x100>; + interrupts = ; + clocks = <&clock CLK_I2C0>; + clock-names = "i2c"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_bus>; + status = "disabled"; + }; - i2c_0: i2c@13860000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "samsung,s3c2440-i2c"; - reg = <0x13860000 0x100>; - interrupts = ; - clocks = <&clock CLK_I2C0>; - clock-names = "i2c"; - pinctrl-names = "default"; - pinctrl-0 = <&i2c0_bus>; - status = "disabled"; - }; + i2c_1: i2c@13870000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-i2c"; + reg = <0x13870000 0x100>; + interrupts = ; + clocks = <&clock CLK_I2C1>; + clock-names = "i2c"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_bus>; + status = "disabled"; + }; - i2c_1: i2c@13870000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "samsung,s3c2440-i2c"; - reg = <0x13870000 0x100>; - interrupts = ; - clocks = <&clock CLK_I2C1>; - clock-names = "i2c"; - pinctrl-names = "default"; - pinctrl-0 = <&i2c1_bus>; - status = "disabled"; - }; + i2c_2: i2c@13880000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-i2c"; + reg = <0x13880000 0x100>; + interrupts = ; + clocks = <&clock CLK_I2C2>; + clock-names = "i2c"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_bus>; + status = "disabled"; + }; - i2c_2: i2c@13880000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "samsung,s3c2440-i2c"; - reg = <0x13880000 0x100>; - interrupts = ; - clocks = <&clock CLK_I2C2>; - clock-names = "i2c"; - pinctrl-names = "default"; - pinctrl-0 = <&i2c2_bus>; - status = "disabled"; - }; + i2c_3: i2c@13890000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-i2c"; + reg = <0x13890000 0x100>; + interrupts = ; + clocks = <&clock CLK_I2C3>; + clock-names = "i2c"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c3_bus>; + status = "disabled"; + }; - i2c_3: i2c@13890000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "samsung,s3c2440-i2c"; - reg = <0x13890000 0x100>; - interrupts = ; - clocks = <&clock CLK_I2C3>; - clock-names = "i2c"; - pinctrl-names = "default"; - pinctrl-0 = <&i2c3_bus>; - status = "disabled"; - }; + i2c_4: i2c@138a0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-i2c"; + reg = <0x138A0000 0x100>; + interrupts = ; + clocks = <&clock CLK_I2C4>; + clock-names = "i2c"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c4_bus>; + status = "disabled"; + }; - i2c_4: i2c@138a0000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "samsung,s3c2440-i2c"; - reg = <0x138A0000 0x100>; - interrupts = ; - clocks = <&clock CLK_I2C4>; - clock-names = "i2c"; - pinctrl-names = "default"; - pinctrl-0 = <&i2c4_bus>; - status = "disabled"; - }; + i2c_5: i2c@138b0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-i2c"; + reg = <0x138B0000 0x100>; + interrupts = ; + clocks = <&clock CLK_I2C5>; + clock-names = "i2c"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c5_bus>; + status = "disabled"; + }; - i2c_5: i2c@138b0000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "samsung,s3c2440-i2c"; - reg = <0x138B0000 0x100>; - interrupts = ; - clocks = <&clock CLK_I2C5>; - clock-names = "i2c"; - pinctrl-names = "default"; - pinctrl-0 = <&i2c5_bus>; - status = "disabled"; - }; + i2c_6: i2c@138c0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-i2c"; + reg = <0x138C0000 0x100>; + interrupts = ; + clocks = <&clock CLK_I2C6>; + clock-names = "i2c"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c6_bus>; + status = "disabled"; + }; - i2c_6: i2c@138c0000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "samsung,s3c2440-i2c"; - reg = <0x138C0000 0x100>; - interrupts = ; - clocks = <&clock CLK_I2C6>; - clock-names = "i2c"; - pinctrl-names = "default"; - pinctrl-0 = <&i2c6_bus>; - status = "disabled"; - }; + i2c_7: i2c@138d0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-i2c"; + reg = <0x138D0000 0x100>; + interrupts = ; + clocks = <&clock CLK_I2C7>; + clock-names = "i2c"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c7_bus>; + status = "disabled"; + }; - i2c_7: i2c@138d0000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "samsung,s3c2440-i2c"; - reg = <0x138D0000 0x100>; - interrupts = ; - clocks = <&clock CLK_I2C7>; - clock-names = "i2c"; - pinctrl-names = "default"; - pinctrl-0 = <&i2c7_bus>; - status = "disabled"; - }; + i2c_8: i2c@138e0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-hdmiphy-i2c"; + reg = <0x138E0000 0x100>; + interrupts = ; + clocks = <&clock CLK_I2C_HDMI>; + clock-names = "i2c"; + status = "disabled"; - i2c_8: i2c@138e0000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "samsung,s3c2440-hdmiphy-i2c"; - reg = <0x138E0000 0x100>; - interrupts = ; - clocks = <&clock CLK_I2C_HDMI>; - clock-names = "i2c"; - status = "disabled"; - - hdmi_i2c_phy: hdmiphy@38 { - compatible = "exynos4210-hdmiphy"; - reg = <0x38>; + hdmi_i2c_phy: hdmiphy@38 { + compatible = "exynos4210-hdmiphy"; + reg = <0x38>; + }; }; - }; - spi_0: spi@13920000 { - compatible = "samsung,exynos4210-spi"; - reg = <0x13920000 0x100>; - interrupts = ; - dmas = <&pdma0 7>, <&pdma0 6>; - dma-names = "tx", "rx"; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&clock CLK_SPI0>, <&clock CLK_SCLK_SPI0>; - clock-names = "spi", "spi_busclk0"; - pinctrl-names = "default"; - pinctrl-0 = <&spi0_bus>; - status = "disabled"; - }; - - spi_1: spi@13930000 { - compatible = "samsung,exynos4210-spi"; - reg = <0x13930000 0x100>; - interrupts = ; - dmas = <&pdma1 7>, <&pdma1 6>; - dma-names = "tx", "rx"; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&clock CLK_SPI1>, <&clock CLK_SCLK_SPI1>; - clock-names = "spi", "spi_busclk0"; - pinctrl-names = "default"; - pinctrl-0 = <&spi1_bus>; - status = "disabled"; - }; + spi_0: spi@13920000 { + compatible = "samsung,exynos4210-spi"; + reg = <0x13920000 0x100>; + interrupts = ; + dmas = <&pdma0 7>, <&pdma0 6>; + dma-names = "tx", "rx"; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clock CLK_SPI0>, <&clock CLK_SCLK_SPI0>; + clock-names = "spi", "spi_busclk0"; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_bus>; + status = "disabled"; + }; - spi_2: spi@13940000 { - compatible = "samsung,exynos4210-spi"; - reg = <0x13940000 0x100>; - interrupts = ; - dmas = <&pdma0 9>, <&pdma0 8>; - dma-names = "tx", "rx"; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&clock CLK_SPI2>, <&clock CLK_SCLK_SPI2>; - clock-names = "spi", "spi_busclk0"; - pinctrl-names = "default"; - pinctrl-0 = <&spi2_bus>; - status = "disabled"; - }; + spi_1: spi@13930000 { + compatible = "samsung,exynos4210-spi"; + reg = <0x13930000 0x100>; + interrupts = ; + dmas = <&pdma1 7>, <&pdma1 6>; + dma-names = "tx", "rx"; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clock CLK_SPI1>, <&clock CLK_SCLK_SPI1>; + clock-names = "spi", "spi_busclk0"; + pinctrl-names = "default"; + pinctrl-0 = <&spi1_bus>; + status = "disabled"; + }; - pwm: pwm@139d0000 { - compatible = "samsung,exynos4210-pwm"; - reg = <0x139D0000 0x1000>; - interrupts = , - , - , - , - ; - clocks = <&clock CLK_PWM>; - clock-names = "timers"; - #pwm-cells = <3>; - status = "disabled"; - }; + spi_2: spi@13940000 { + compatible = "samsung,exynos4210-spi"; + reg = <0x13940000 0x100>; + interrupts = ; + dmas = <&pdma0 9>, <&pdma0 8>; + dma-names = "tx", "rx"; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clock CLK_SPI2>, <&clock CLK_SCLK_SPI2>; + clock-names = "spi", "spi_busclk0"; + pinctrl-names = "default"; + pinctrl-0 = <&spi2_bus>; + status = "disabled"; + }; - amba { - #address-cells = <1>; - #size-cells = <1>; - compatible = "simple-bus"; - interrupt-parent = <&gic>; - ranges; + pwm: pwm@139d0000 { + compatible = "samsung,exynos4210-pwm"; + reg = <0x139D0000 0x1000>; + interrupts = , + , + , + , + ; + clocks = <&clock CLK_PWM>; + clock-names = "timers"; + #pwm-cells = <3>; + status = "disabled"; + }; - pdma0: pdma@12680000 { - compatible = "arm,pl330", "arm,primecell"; - reg = <0x12680000 0x1000>; - interrupts = ; - clocks = <&clock CLK_PDMA0>; - clock-names = "apb_pclk"; - #dma-cells = <1>; - #dma-channels = <8>; - #dma-requests = <32>; - }; - - pdma1: pdma@12690000 { - compatible = "arm,pl330", "arm,primecell"; - reg = <0x12690000 0x1000>; - interrupts = ; - clocks = <&clock CLK_PDMA1>; - clock-names = "apb_pclk"; - #dma-cells = <1>; - #dma-channels = <8>; - #dma-requests = <32>; - }; - - mdma1: mdma@12850000 { - compatible = "arm,pl330", "arm,primecell"; - reg = <0x12850000 0x1000>; - interrupts = ; - clocks = <&clock CLK_MDMA>; - clock-names = "apb_pclk"; - #dma-cells = <1>; - #dma-channels = <8>; - #dma-requests = <1>; + amba { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&gic>; + ranges; + + pdma0: pdma@12680000 { + compatible = "arm,pl330", "arm,primecell"; + reg = <0x12680000 0x1000>; + interrupts = ; + clocks = <&clock CLK_PDMA0>; + clock-names = "apb_pclk"; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; + }; + + pdma1: pdma@12690000 { + compatible = "arm,pl330", "arm,primecell"; + reg = <0x12690000 0x1000>; + interrupts = ; + clocks = <&clock CLK_PDMA1>; + clock-names = "apb_pclk"; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; + }; + + mdma1: mdma@12850000 { + compatible = "arm,pl330", "arm,primecell"; + reg = <0x12850000 0x1000>; + interrupts = ; + clocks = <&clock CLK_MDMA>; + clock-names = "apb_pclk"; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <1>; + }; }; - }; - fimd: fimd@11c00000 { - compatible = "samsung,exynos4210-fimd"; - interrupt-parent = <&combiner>; - reg = <0x11c00000 0x20000>; - interrupt-names = "fifo", "vsync", "lcd_sys"; - interrupts = <11 0>, <11 1>, <11 2>; - clocks = <&clock CLK_SCLK_FIMD0>, <&clock CLK_FIMD0>; - clock-names = "sclk_fimd", "fimd"; - power-domains = <&pd_lcd0>; - iommus = <&sysmmu_fimd0>; - samsung,sysreg = <&sys_reg>; - status = "disabled"; - }; + fimd: fimd@11c00000 { + compatible = "samsung,exynos4210-fimd"; + interrupt-parent = <&combiner>; + reg = <0x11c00000 0x20000>; + interrupt-names = "fifo", "vsync", "lcd_sys"; + interrupts = <11 0>, <11 1>, <11 2>; + clocks = <&clock CLK_SCLK_FIMD0>, <&clock CLK_FIMD0>; + clock-names = "sclk_fimd", "fimd"; + power-domains = <&pd_lcd0>; + iommus = <&sysmmu_fimd0>; + samsung,sysreg = <&sys_reg>; + status = "disabled"; + }; - tmu: tmu@100c0000 { - interrupt-parent = <&combiner>; - reg = <0x100C0000 0x100>; - interrupts = <2 4>; - status = "disabled"; - #include "exynos4412-tmu-sensor-conf.dtsi" - }; + tmu: tmu@100c0000 { + interrupt-parent = <&combiner>; + reg = <0x100C0000 0x100>; + interrupts = <2 4>; + status = "disabled"; + #include "exynos4412-tmu-sensor-conf.dtsi" + }; - jpeg_codec: jpeg-codec@11840000 { - compatible = "samsung,exynos4210-jpeg"; - reg = <0x11840000 0x1000>; - interrupts = ; - clocks = <&clock CLK_JPEG>; - clock-names = "jpeg"; - power-domains = <&pd_cam>; - iommus = <&sysmmu_jpeg>; - }; + jpeg_codec: jpeg-codec@11840000 { + compatible = "samsung,exynos4210-jpeg"; + reg = <0x11840000 0x1000>; + interrupts = ; + clocks = <&clock CLK_JPEG>; + clock-names = "jpeg"; + power-domains = <&pd_cam>; + iommus = <&sysmmu_jpeg>; + }; - rotator: rotator@12810000 { - compatible = "samsung,exynos4210-rotator"; - reg = <0x12810000 0x64>; - interrupts = ; - clocks = <&clock CLK_ROTATOR>; - clock-names = "rotator"; - iommus = <&sysmmu_rotator>; - }; + rotator: rotator@12810000 { + compatible = "samsung,exynos4210-rotator"; + reg = <0x12810000 0x64>; + interrupts = ; + clocks = <&clock CLK_ROTATOR>; + clock-names = "rotator"; + iommus = <&sysmmu_rotator>; + }; - hdmi: hdmi@12d00000 { - compatible = "samsung,exynos4210-hdmi"; - reg = <0x12D00000 0x70000>; - interrupts = ; - clock-names = "hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy", - "mout_hdmi"; - clocks = <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>, - <&clock CLK_SCLK_PIXEL>, <&clock CLK_SCLK_HDMIPHY>, - <&clock CLK_MOUT_HDMI>; - phy = <&hdmi_i2c_phy>; - power-domains = <&pd_tv>; - samsung,syscon-phandle = <&pmu_system_controller>; - #sound-dai-cells = <0>; - status = "disabled"; - }; + hdmi: hdmi@12d00000 { + compatible = "samsung,exynos4210-hdmi"; + reg = <0x12D00000 0x70000>; + interrupts = ; + clock-names = "hdmi", "sclk_hdmi", "sclk_pixel", + "sclk_hdmiphy", "mout_hdmi"; + clocks = <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>, + <&clock CLK_SCLK_PIXEL>, + <&clock CLK_SCLK_HDMIPHY>, + <&clock CLK_MOUT_HDMI>; + phy = <&hdmi_i2c_phy>; + power-domains = <&pd_tv>; + samsung,syscon-phandle = <&pmu_system_controller>; + #sound-dai-cells = <0>; + status = "disabled"; + }; - hdmicec: cec@100b0000 { - compatible = "samsung,s5p-cec"; - reg = <0x100B0000 0x200>; - interrupts = ; - clocks = <&clock CLK_HDMI_CEC>; - clock-names = "hdmicec"; - samsung,syscon-phandle = <&pmu_system_controller>; - hdmi-phandle = <&hdmi>; - pinctrl-names = "default"; - pinctrl-0 = <&hdmi_cec>; - status = "disabled"; - }; + hdmicec: cec@100b0000 { + compatible = "samsung,s5p-cec"; + reg = <0x100B0000 0x200>; + interrupts = ; + clocks = <&clock CLK_HDMI_CEC>; + clock-names = "hdmicec"; + samsung,syscon-phandle = <&pmu_system_controller>; + hdmi-phandle = <&hdmi>; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_cec>; + status = "disabled"; + }; - mixer: mixer@12c10000 { - compatible = "samsung,exynos4210-mixer"; - interrupts = ; - reg = <0x12C10000 0x2100>, <0x12c00000 0x300>; - power-domains = <&pd_tv>; - iommus = <&sysmmu_tv>; - status = "disabled"; - }; + mixer: mixer@12c10000 { + compatible = "samsung,exynos4210-mixer"; + interrupts = ; + reg = <0x12C10000 0x2100>, <0x12c00000 0x300>; + power-domains = <&pd_tv>; + iommus = <&sysmmu_tv>; + status = "disabled"; + }; - ppmu_dmc0: ppmu_dmc0@106a0000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x106a0000 0x2000>; - clocks = <&clock CLK_PPMUDMC0>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_dmc0: ppmu_dmc0@106a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106a0000 0x2000>; + clocks = <&clock CLK_PPMUDMC0>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_dmc1: ppmu_dmc1@106b0000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x106b0000 0x2000>; - clocks = <&clock CLK_PPMUDMC1>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_dmc1: ppmu_dmc1@106b0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106b0000 0x2000>; + clocks = <&clock CLK_PPMUDMC1>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_cpu: ppmu_cpu@106c0000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x106c0000 0x2000>; - clocks = <&clock CLK_PPMUCPU>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_cpu: ppmu_cpu@106c0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106c0000 0x2000>; + clocks = <&clock CLK_PPMUCPU>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_acp: ppmu_acp@10ae0000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x106e0000 0x2000>; - status = "disabled"; - }; + ppmu_acp: ppmu_acp@10ae0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106e0000 0x2000>; + status = "disabled"; + }; - ppmu_rightbus: ppmu_rightbus@112a0000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x112a0000 0x2000>; - clocks = <&clock CLK_PPMURIGHT>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_rightbus: ppmu_rightbus@112a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x112a0000 0x2000>; + clocks = <&clock CLK_PPMURIGHT>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_leftbus: ppmu_leftbus0@116a0000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x116a0000 0x2000>; - clocks = <&clock CLK_PPMULEFT>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_leftbus: ppmu_leftbus0@116a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x116a0000 0x2000>; + clocks = <&clock CLK_PPMULEFT>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_camif: ppmu_camif@11ac0000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x11ac0000 0x2000>; - clocks = <&clock CLK_PPMUCAMIF>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_camif: ppmu_camif@11ac0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x11ac0000 0x2000>; + clocks = <&clock CLK_PPMUCAMIF>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_lcd0: ppmu_lcd0@11e40000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x11e40000 0x2000>; - clocks = <&clock CLK_PPMULCD0>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_lcd0: ppmu_lcd0@11e40000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x11e40000 0x2000>; + clocks = <&clock CLK_PPMULCD0>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_fsys: ppmu_g3d@12630000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x12630000 0x2000>; - status = "disabled"; - }; + ppmu_fsys: ppmu_g3d@12630000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12630000 0x2000>; + status = "disabled"; + }; - ppmu_image: ppmu_image@12aa0000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x12aa0000 0x2000>; - clocks = <&clock CLK_PPMUIMAGE>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_image: ppmu_image@12aa0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12aa0000 0x2000>; + clocks = <&clock CLK_PPMUIMAGE>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_tv: ppmu_tv@12e40000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x12e40000 0x2000>; - clocks = <&clock CLK_PPMUTV>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_tv: ppmu_tv@12e40000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12e40000 0x2000>; + clocks = <&clock CLK_PPMUTV>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_g3d: ppmu_g3d@13220000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x13220000 0x2000>; - clocks = <&clock CLK_PPMUG3D>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_g3d: ppmu_g3d@13220000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13220000 0x2000>; + clocks = <&clock CLK_PPMUG3D>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_mfc_left: ppmu_mfc_left@13660000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x13660000 0x2000>; - clocks = <&clock CLK_PPMUMFC_L>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_mfc_left: ppmu_mfc_left@13660000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13660000 0x2000>; + clocks = <&clock CLK_PPMUMFC_L>; + clock-names = "ppmu"; + status = "disabled"; + }; - ppmu_mfc_right: ppmu_mfc_right@13670000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x13670000 0x2000>; - clocks = <&clock CLK_PPMUMFC_R>; - clock-names = "ppmu"; - status = "disabled"; - }; + ppmu_mfc_right: ppmu_mfc_right@13670000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13670000 0x2000>; + clocks = <&clock CLK_PPMUMFC_R>; + clock-names = "ppmu"; + status = "disabled"; + }; - sysmmu_mfc_l: sysmmu@13620000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x13620000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <5 5>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_MFCL>, <&clock CLK_MFC>; - power-domains = <&pd_mfc>; - #iommu-cells = <0>; - }; + sysmmu_mfc_l: sysmmu@13620000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13620000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 5>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCL>, <&clock CLK_MFC>; + power-domains = <&pd_mfc>; + #iommu-cells = <0>; + }; - sysmmu_mfc_r: sysmmu@13630000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x13630000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <5 6>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_MFCR>, <&clock CLK_MFC>; - power-domains = <&pd_mfc>; - #iommu-cells = <0>; - }; + sysmmu_mfc_r: sysmmu@13630000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13630000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 6>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCR>, <&clock CLK_MFC>; + power-domains = <&pd_mfc>; + #iommu-cells = <0>; + }; - sysmmu_tv: sysmmu@12e20000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x12E20000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <5 4>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_TV>, <&clock CLK_MIXER>; - power-domains = <&pd_tv>; - #iommu-cells = <0>; - }; + sysmmu_tv: sysmmu@12e20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12E20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 4>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_TV>, <&clock CLK_MIXER>; + power-domains = <&pd_tv>; + #iommu-cells = <0>; + }; - sysmmu_fimc0: sysmmu@11a20000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x11A20000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <4 2>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_FIMC0>, <&clock CLK_FIMC0>; - power-domains = <&pd_cam>; - #iommu-cells = <0>; - }; + sysmmu_fimc0: sysmmu@11a20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC0>, <&clock CLK_FIMC0>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; - sysmmu_fimc1: sysmmu@11a30000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x11A30000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <4 3>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_FIMC1>, <&clock CLK_FIMC1>; - power-domains = <&pd_cam>; - #iommu-cells = <0>; - }; + sysmmu_fimc1: sysmmu@11a30000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A30000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 3>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC1>, <&clock CLK_FIMC1>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; - sysmmu_fimc2: sysmmu@11a40000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x11A40000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <4 4>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_FIMC2>, <&clock CLK_FIMC2>; - power-domains = <&pd_cam>; - #iommu-cells = <0>; - }; + sysmmu_fimc2: sysmmu@11a40000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A40000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 4>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC2>, <&clock CLK_FIMC2>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; - sysmmu_fimc3: sysmmu@11a50000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x11A50000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <4 5>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_FIMC3>, <&clock CLK_FIMC3>; - power-domains = <&pd_cam>; - #iommu-cells = <0>; - }; + sysmmu_fimc3: sysmmu@11a50000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A50000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 5>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC3>, <&clock CLK_FIMC3>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; - sysmmu_jpeg: sysmmu@11a60000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x11A60000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <4 6>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_JPEG>, <&clock CLK_JPEG>; - power-domains = <&pd_cam>; - #iommu-cells = <0>; - }; + sysmmu_jpeg: sysmmu@11a60000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A60000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 6>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_JPEG>, <&clock CLK_JPEG>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; - sysmmu_rotator: sysmmu@12a30000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x12A30000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <5 0>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_ROTATOR>, <&clock CLK_ROTATOR>; - #iommu-cells = <0>; - }; + sysmmu_rotator: sysmmu@12a30000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12A30000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 0>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_ROTATOR>, + <&clock CLK_ROTATOR>; + #iommu-cells = <0>; + }; - sysmmu_fimd0: sysmmu@11e20000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x11E20000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <5 2>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_FIMD0>, <&clock CLK_FIMD0>; - power-domains = <&pd_lcd0>; - #iommu-cells = <0>; - }; + sysmmu_fimd0: sysmmu@11e20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11E20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMD0>, <&clock CLK_FIMD0>; + power-domains = <&pd_lcd0>; + #iommu-cells = <0>; + }; - sss: sss@10830000 { - compatible = "samsung,exynos4210-secss"; - reg = <0x10830000 0x300>; - interrupts = ; - clocks = <&clock CLK_SSS>; - clock-names = "secss"; - }; + sss: sss@10830000 { + compatible = "samsung,exynos4210-secss"; + reg = <0x10830000 0x300>; + interrupts = ; + clocks = <&clock CLK_SSS>; + clock-names = "secss"; + }; - prng: rng@10830400 { - compatible = "samsung,exynos4-rng"; - reg = <0x10830400 0x200>; - clocks = <&clock CLK_SSS>; - clock-names = "secss"; + prng: rng@10830400 { + compatible = "samsung,exynos4-rng"; + reg = <0x10830400 0x200>; + clocks = <&clock CLK_SSS>; + clock-names = "secss"; + }; }; }; -- cgit v1.3-14-g43fede From 9a8665ab920be393c5dbdd30628477bf176f47e8 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Tue, 6 Feb 2018 12:29:41 +0100 Subject: ARM: dts: exynos: Add soc node to exynos4210 Soc nodes are used in other exynos DTS. Exynos4210 boards should use them as well. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4210.dtsi | 513 +++++++++++++++++++------------------- 1 file changed, 258 insertions(+), 255 deletions(-) diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 645b976b6842..80c1972a2ecf 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -60,314 +60,317 @@ }; }; - sysram: sysram@2020000 { - compatible = "mmio-sram"; - reg = <0x02020000 0x20000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0x02020000 0x20000>; + soc: soc { + sysram: sysram@2020000 { + compatible = "mmio-sram"; + reg = <0x02020000 0x20000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x02020000 0x20000>; + + smp-sysram@0 { + compatible = "samsung,exynos4210-sysram"; + reg = <0x0 0x1000>; + }; - smp-sysram@0 { - compatible = "samsung,exynos4210-sysram"; - reg = <0x0 0x1000>; + smp-sysram@1f000 { + compatible = "samsung,exynos4210-sysram-ns"; + reg = <0x1f000 0x1000>; + }; }; - smp-sysram@1f000 { - compatible = "samsung,exynos4210-sysram-ns"; - reg = <0x1f000 0x1000>; + pd_lcd1: lcd1-power-domain@10023ca0 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023CA0 0x20>; + #power-domain-cells = <0>; + label = "LCD1"; }; - }; - - pd_lcd1: lcd1-power-domain@10023ca0 { - compatible = "samsung,exynos4210-pd"; - reg = <0x10023CA0 0x20>; - #power-domain-cells = <0>; - label = "LCD1"; - }; - l2c: l2-cache-controller@10502000 { - compatible = "arm,pl310-cache"; - reg = <0x10502000 0x1000>; - cache-unified; - cache-level = <2>; - arm,tag-latency = <2 2 1>; - arm,data-latency = <2 2 1>; - }; + l2c: l2-cache-controller@10502000 { + compatible = "arm,pl310-cache"; + reg = <0x10502000 0x1000>; + cache-unified; + cache-level = <2>; + arm,tag-latency = <2 2 1>; + arm,data-latency = <2 2 1>; + }; - mct: mct@10050000 { - compatible = "samsung,exynos4210-mct"; - reg = <0x10050000 0x800>; - interrupt-parent = <&mct_map>; - interrupts = <0>, <1>, <2>, <3>, <4>, <5>; - clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MCT>; - clock-names = "fin_pll", "mct"; - - mct_map: mct-map { - #interrupt-cells = <1>; - #address-cells = <0>; - #size-cells = <0>; - interrupt-map = <0 &gic 0 57 IRQ_TYPE_LEVEL_HIGH>, + mct: mct@10050000 { + compatible = "samsung,exynos4210-mct"; + reg = <0x10050000 0x800>; + interrupt-parent = <&mct_map>; + interrupts = <0>, <1>, <2>, <3>, <4>, <5>; + clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MCT>; + clock-names = "fin_pll", "mct"; + + mct_map: mct-map { + #interrupt-cells = <1>; + #address-cells = <0>; + #size-cells = <0>; + interrupt-map = + <0 &gic 0 57 IRQ_TYPE_LEVEL_HIGH>, <1 &gic 0 69 IRQ_TYPE_LEVEL_HIGH>, <2 &combiner 12 6>, <3 &combiner 12 7>, <4 &gic 0 42 IRQ_TYPE_LEVEL_HIGH>, <5 &gic 0 48 IRQ_TYPE_LEVEL_HIGH>; + }; }; - }; - watchdog: watchdog@10060000 { - compatible = "samsung,s3c6410-wdt"; - reg = <0x10060000 0x100>; - interrupts = ; - clocks = <&clock CLK_WDT>; - clock-names = "watchdog"; - }; + watchdog: watchdog@10060000 { + compatible = "samsung,s3c6410-wdt"; + reg = <0x10060000 0x100>; + interrupts = ; + clocks = <&clock CLK_WDT>; + clock-names = "watchdog"; + }; - clock: clock-controller@10030000 { - compatible = "samsung,exynos4210-clock"; - reg = <0x10030000 0x20000>; - #clock-cells = <1>; - }; + clock: clock-controller@10030000 { + compatible = "samsung,exynos4210-clock"; + reg = <0x10030000 0x20000>; + #clock-cells = <1>; + }; - pinctrl_0: pinctrl@11400000 { - compatible = "samsung,exynos4210-pinctrl"; - reg = <0x11400000 0x1000>; - interrupts = ; - }; + pinctrl_0: pinctrl@11400000 { + compatible = "samsung,exynos4210-pinctrl"; + reg = <0x11400000 0x1000>; + interrupts = ; + }; - pinctrl_1: pinctrl@11000000 { - compatible = "samsung,exynos4210-pinctrl"; - reg = <0x11000000 0x1000>; - interrupts = ; + pinctrl_1: pinctrl@11000000 { + compatible = "samsung,exynos4210-pinctrl"; + reg = <0x11000000 0x1000>; + interrupts = ; - wakup_eint: wakeup-interrupt-controller { - compatible = "samsung,exynos4210-wakeup-eint"; - interrupt-parent = <&gic>; - interrupts = ; + wakup_eint: wakeup-interrupt-controller { + compatible = "samsung,exynos4210-wakeup-eint"; + interrupt-parent = <&gic>; + interrupts = ; + }; }; - }; - pinctrl_2: pinctrl@3860000 { - compatible = "samsung,exynos4210-pinctrl"; - reg = <0x03860000 0x1000>; - }; + pinctrl_2: pinctrl@3860000 { + compatible = "samsung,exynos4210-pinctrl"; + reg = <0x03860000 0x1000>; + }; - thermal-zones { - cpu_thermal: cpu-thermal { - polling-delay-passive = <0>; - polling-delay = <0>; - thermal-sensors = <&tmu 0>; + g2d: g2d@12800000 { + compatible = "samsung,s5pv210-g2d"; + reg = <0x12800000 0x1000>; + interrupts = ; + clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>; + clock-names = "sclk_fimg2d", "fimg2d"; + power-domains = <&pd_lcd0>; + iommus = <&sysmmu_g2d>; + }; - trips { - cpu_alert0: cpu-alert-0 { - temperature = <85000>; /* millicelsius */ - }; - cpu_alert1: cpu-alert-1 { - temperature = <100000>; /* millicelsius */ - }; - cpu_alert2: cpu-alert-2 { - temperature = <110000>; /* millicelsius */ - }; - }; + ppmu_lcd1: ppmu_lcd1@12240000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12240000 0x2000>; + clocks = <&clock CLK_PPMULCD1>; + clock-names = "ppmu"; + status = "disabled"; }; - }; - g2d: g2d@12800000 { - compatible = "samsung,s5pv210-g2d"; - reg = <0x12800000 0x1000>; - interrupts = ; - clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>; - clock-names = "sclk_fimg2d", "fimg2d"; - power-domains = <&pd_lcd0>; - iommus = <&sysmmu_g2d>; - }; + sysmmu_g2d: sysmmu@12a20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12A20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 7>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; + power-domains = <&pd_lcd0>; + #iommu-cells = <0>; + }; - ppmu_lcd1: ppmu_lcd1@12240000 { - compatible = "samsung,exynos-ppmu"; - reg = <0x12240000 0x2000>; - clocks = <&clock CLK_PPMULCD1>; - clock-names = "ppmu"; - status = "disabled"; - }; + sysmmu_fimd1: sysmmu@12220000 { + compatible = "samsung,exynos-sysmmu"; + interrupt-parent = <&combiner>; + reg = <0x12220000 0x1000>; + interrupts = <5 3>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMD1>, <&clock CLK_FIMD1>; + power-domains = <&pd_lcd1>; + #iommu-cells = <0>; + }; - sysmmu_g2d: sysmmu@12a20000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x12A20000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <4 7>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; - power-domains = <&pd_lcd0>; - #iommu-cells = <0>; - }; + bus_dmc: bus_dmc { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_DIV_DMC>; + clock-names = "bus"; + operating-points-v2 = <&bus_dmc_opp_table>; + status = "disabled"; + }; - sysmmu_fimd1: sysmmu@12220000 { - compatible = "samsung,exynos-sysmmu"; - interrupt-parent = <&combiner>; - reg = <0x12220000 0x1000>; - interrupts = <5 3>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_FIMD1>, <&clock CLK_FIMD1>; - power-domains = <&pd_lcd1>; - #iommu-cells = <0>; - }; + bus_acp: bus_acp { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_DIV_ACP>; + clock-names = "bus"; + operating-points-v2 = <&bus_acp_opp_table>; + status = "disabled"; + }; - bus_dmc: bus_dmc { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_DIV_DMC>; - clock-names = "bus"; - operating-points-v2 = <&bus_dmc_opp_table>; - status = "disabled"; - }; + bus_peri: bus_peri { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_ACLK100>; + clock-names = "bus"; + operating-points-v2 = <&bus_peri_opp_table>; + status = "disabled"; + }; - bus_acp: bus_acp { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_DIV_ACP>; - clock-names = "bus"; - operating-points-v2 = <&bus_acp_opp_table>; - status = "disabled"; - }; + bus_fsys: bus_fsys { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_ACLK133>; + clock-names = "bus"; + operating-points-v2 = <&bus_fsys_opp_table>; + status = "disabled"; + }; - bus_peri: bus_peri { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_ACLK100>; - clock-names = "bus"; - operating-points-v2 = <&bus_peri_opp_table>; - status = "disabled"; - }; + bus_display: bus_display { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_ACLK160>; + clock-names = "bus"; + operating-points-v2 = <&bus_display_opp_table>; + status = "disabled"; + }; - bus_fsys: bus_fsys { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_ACLK133>; - clock-names = "bus"; - operating-points-v2 = <&bus_fsys_opp_table>; - status = "disabled"; - }; + bus_lcd0: bus_lcd0 { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_ACLK200>; + clock-names = "bus"; + operating-points-v2 = <&bus_leftbus_opp_table>; + status = "disabled"; + }; - bus_display: bus_display { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_ACLK160>; - clock-names = "bus"; - operating-points-v2 = <&bus_display_opp_table>; - status = "disabled"; - }; + bus_leftbus: bus_leftbus { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_DIV_GDL>; + clock-names = "bus"; + operating-points-v2 = <&bus_leftbus_opp_table>; + status = "disabled"; + }; - bus_lcd0: bus_lcd0 { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_ACLK200>; - clock-names = "bus"; - operating-points-v2 = <&bus_leftbus_opp_table>; - status = "disabled"; - }; + bus_rightbus: bus_rightbus { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_DIV_GDR>; + clock-names = "bus"; + operating-points-v2 = <&bus_leftbus_opp_table>; + status = "disabled"; + }; - bus_leftbus: bus_leftbus { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_DIV_GDL>; - clock-names = "bus"; - operating-points-v2 = <&bus_leftbus_opp_table>; - status = "disabled"; - }; + bus_mfc: bus_mfc { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_SCLK_MFC>; + clock-names = "bus"; + operating-points-v2 = <&bus_leftbus_opp_table>; + status = "disabled"; + }; - bus_rightbus: bus_rightbus { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_DIV_GDR>; - clock-names = "bus"; - operating-points-v2 = <&bus_leftbus_opp_table>; - status = "disabled"; - }; + bus_dmc_opp_table: opp_table1 { + compatible = "operating-points-v2"; + opp-shared; - bus_mfc: bus_mfc { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_SCLK_MFC>; - clock-names = "bus"; - operating-points-v2 = <&bus_leftbus_opp_table>; - status = "disabled"; - }; + opp-134000000 { + opp-hz = /bits/ 64 <134000000>; + opp-microvolt = <1025000>; + }; + opp-267000000 { + opp-hz = /bits/ 64 <267000000>; + opp-microvolt = <1050000>; + }; + opp-400000000 { + opp-hz = /bits/ 64 <400000000>; + opp-microvolt = <1150000>; + }; + }; - bus_dmc_opp_table: opp_table1 { - compatible = "operating-points-v2"; - opp-shared; + bus_acp_opp_table: opp_table2 { + compatible = "operating-points-v2"; + opp-shared; - opp-134000000 { - opp-hz = /bits/ 64 <134000000>; - opp-microvolt = <1025000>; - }; - opp-267000000 { - opp-hz = /bits/ 64 <267000000>; - opp-microvolt = <1050000>; - }; - opp-400000000 { - opp-hz = /bits/ 64 <400000000>; - opp-microvolt = <1150000>; + opp-134000000 { + opp-hz = /bits/ 64 <134000000>; + }; + opp-160000000 { + opp-hz = /bits/ 64 <160000000>; + }; + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + }; }; - }; - bus_acp_opp_table: opp_table2 { - compatible = "operating-points-v2"; - opp-shared; + bus_peri_opp_table: opp_table3 { + compatible = "operating-points-v2"; + opp-shared; - opp-134000000 { - opp-hz = /bits/ 64 <134000000>; - }; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; - }; - opp-200000000 { - opp-hz = /bits/ 64 <200000000>; + opp-5000000 { + opp-hz = /bits/ 64 <5000000>; + }; + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + }; }; - }; - bus_peri_opp_table: opp_table3 { - compatible = "operating-points-v2"; - opp-shared; + bus_fsys_opp_table: opp_table4 { + compatible = "operating-points-v2"; + opp-shared; - opp-5000000 { - opp-hz = /bits/ 64 <5000000>; - }; - opp-100000000 { - opp-hz = /bits/ 64 <100000000>; + opp-10000000 { + opp-hz = /bits/ 64 <10000000>; + }; + opp-134000000 { + opp-hz = /bits/ 64 <134000000>; + }; }; - }; - bus_fsys_opp_table: opp_table4 { - compatible = "operating-points-v2"; - opp-shared; + bus_display_opp_table: opp_table5 { + compatible = "operating-points-v2"; + opp-shared; - opp-10000000 { - opp-hz = /bits/ 64 <10000000>; - }; - opp-134000000 { - opp-hz = /bits/ 64 <134000000>; + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + }; + opp-134000000 { + opp-hz = /bits/ 64 <134000000>; + }; + opp-160000000 { + opp-hz = /bits/ 64 <160000000>; + }; }; - }; - bus_display_opp_table: opp_table5 { - compatible = "operating-points-v2"; - opp-shared; + bus_leftbus_opp_table: opp_table6 { + compatible = "operating-points-v2"; + opp-shared; - opp-100000000 { - opp-hz = /bits/ 64 <100000000>; - }; - opp-134000000 { - opp-hz = /bits/ 64 <134000000>; - }; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + }; + opp-160000000 { + opp-hz = /bits/ 64 <160000000>; + }; + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + }; }; }; - bus_leftbus_opp_table: opp_table6 { - compatible = "operating-points-v2"; - opp-shared; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tmu 0>; - opp-100000000 { - opp-hz = /bits/ 64 <100000000>; - }; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; - }; - opp-200000000 { - opp-hz = /bits/ 64 <200000000>; + trips { + cpu_alert0: cpu-alert-0 { + temperature = <85000>; /* millicelsius */ + }; + cpu_alert1: cpu-alert-1 { + temperature = <100000>; /* millicelsius */ + }; + cpu_alert2: cpu-alert-2 { + temperature = <110000>; /* millicelsius */ + }; + }; }; }; }; -- cgit v1.3-14-g43fede From cd109198ad056f298a77340f4945c1e63b24c7fc Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Tue, 6 Feb 2018 12:29:42 +0100 Subject: ARM: dts: exynos: Add soc node to exynos4412 Soc nodes are used in other exynos DTS. Exynos4412 boards should use them as well. Signed-off-by: Maciej Purski Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4412.dtsi | 772 +++++++++++++++++++------------------- 1 file changed, 389 insertions(+), 383 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi index e012cdad6595..b765c099a864 100644 --- a/arch/arm/boot/dts/exynos4412.dtsi +++ b/arch/arm/boot/dts/exynos4412.dtsi @@ -147,403 +147,409 @@ }; }; - pinctrl_0: pinctrl@11400000 { - compatible = "samsung,exynos4x12-pinctrl"; - reg = <0x11400000 0x1000>; - interrupts = ; - }; - pinctrl_1: pinctrl@11000000 { - compatible = "samsung,exynos4x12-pinctrl"; - reg = <0x11000000 0x1000>; - interrupts = ; + soc: soc { - wakup_eint: wakeup-interrupt-controller { - compatible = "samsung,exynos4210-wakeup-eint"; - interrupt-parent = <&gic>; - interrupts = ; + pinctrl_0: pinctrl@11400000 { + compatible = "samsung,exynos4x12-pinctrl"; + reg = <0x11400000 0x1000>; + interrupts = ; }; - }; - pinctrl_2: pinctrl@3860000 { - compatible = "samsung,exynos4x12-pinctrl"; - reg = <0x03860000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <10 0>; - }; - - pinctrl_3: pinctrl@106e0000 { - compatible = "samsung,exynos4x12-pinctrl"; - reg = <0x106E0000 0x1000>; - interrupts = ; - }; + pinctrl_1: pinctrl@11000000 { + compatible = "samsung,exynos4x12-pinctrl"; + reg = <0x11000000 0x1000>; + interrupts = ; - sysram@2020000 { - compatible = "mmio-sram"; - reg = <0x02020000 0x40000>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0x02020000 0x40000>; - - smp-sysram@0 { - compatible = "samsung,exynos4210-sysram"; - reg = <0x0 0x1000>; + wakup_eint: wakeup-interrupt-controller { + compatible = "samsung,exynos4210-wakeup-eint"; + interrupt-parent = <&gic>; + interrupts = ; + }; }; - smp-sysram@2f000 { - compatible = "samsung,exynos4210-sysram-ns"; - reg = <0x2f000 0x1000>; + pinctrl_2: pinctrl@3860000 { + compatible = "samsung,exynos4x12-pinctrl"; + reg = <0x03860000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <10 0>; }; - }; - pd_isp: isp-power-domain@10023ca0 { - compatible = "samsung,exynos4210-pd"; - reg = <0x10023CA0 0x20>; - #power-domain-cells = <0>; - label = "ISP"; - }; - - l2c: l2-cache-controller@10502000 { - compatible = "arm,pl310-cache"; - reg = <0x10502000 0x1000>; - cache-unified; - cache-level = <2>; - arm,tag-latency = <2 2 1>; - arm,data-latency = <3 2 1>; - arm,double-linefill = <1>; - arm,double-linefill-incr = <0>; - arm,double-linefill-wrap = <1>; - arm,prefetch-drop = <1>; - arm,prefetch-offset = <7>; - }; - - clock: clock-controller@10030000 { - compatible = "samsung,exynos4412-clock"; - reg = <0x10030000 0x18000>; - #clock-cells = <1>; - }; - - isp_clock: clock-controller@10048000 { - compatible = "samsung,exynos4412-isp-clock"; - reg = <0x10048000 0x1000>; - #clock-cells = <1>; - power-domains = <&pd_isp>; - clocks = <&clock CLK_ACLK200>, <&clock CLK_ACLK400_MCUISP>; - clock-names = "aclk200", "aclk400_mcuisp"; - }; + pinctrl_3: pinctrl@106e0000 { + compatible = "samsung,exynos4x12-pinctrl"; + reg = <0x106E0000 0x1000>; + interrupts = ; + }; - mct@10050000 { - compatible = "samsung,exynos4412-mct"; - reg = <0x10050000 0x800>; - interrupt-parent = <&mct_map>; - interrupts = <0>, <1>, <2>, <3>, <4>; - clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MCT>; - clock-names = "fin_pll", "mct"; - - mct_map: mct-map { - #interrupt-cells = <1>; - #address-cells = <0>; - #size-cells = <0>; - interrupt-map = <0 &gic 0 57 IRQ_TYPE_LEVEL_HIGH>, + sysram@2020000 { + compatible = "mmio-sram"; + reg = <0x02020000 0x40000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x02020000 0x40000>; + + smp-sysram@0 { + compatible = "samsung,exynos4210-sysram"; + reg = <0x0 0x1000>; + }; + + smp-sysram@2f000 { + compatible = "samsung,exynos4210-sysram-ns"; + reg = <0x2f000 0x1000>; + }; + }; + + pd_isp: isp-power-domain@10023ca0 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023CA0 0x20>; + #power-domain-cells = <0>; + label = "ISP"; + }; + + l2c: l2-cache-controller@10502000 { + compatible = "arm,pl310-cache"; + reg = <0x10502000 0x1000>; + cache-unified; + cache-level = <2>; + arm,tag-latency = <2 2 1>; + arm,data-latency = <3 2 1>; + arm,double-linefill = <1>; + arm,double-linefill-incr = <0>; + arm,double-linefill-wrap = <1>; + arm,prefetch-drop = <1>; + arm,prefetch-offset = <7>; + }; + + clock: clock-controller@10030000 { + compatible = "samsung,exynos4412-clock"; + reg = <0x10030000 0x18000>; + #clock-cells = <1>; + }; + + isp_clock: clock-controller@10048000 { + compatible = "samsung,exynos4412-isp-clock"; + reg = <0x10048000 0x1000>; + #clock-cells = <1>; + power-domains = <&pd_isp>; + clocks = <&clock CLK_ACLK200>, + <&clock CLK_ACLK400_MCUISP>; + clock-names = "aclk200", "aclk400_mcuisp"; + }; + + mct@10050000 { + compatible = "samsung,exynos4412-mct"; + reg = <0x10050000 0x800>; + interrupt-parent = <&mct_map>; + interrupts = <0>, <1>, <2>, <3>, <4>; + clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MCT>; + clock-names = "fin_pll", "mct"; + + mct_map: mct-map { + #interrupt-cells = <1>; + #address-cells = <0>; + #size-cells = <0>; + interrupt-map = + <0 &gic 0 57 IRQ_TYPE_LEVEL_HIGH>, <1 &combiner 12 5>, <2 &combiner 12 6>, <3 &combiner 12 7>, <4 &gic 1 12 IRQ_TYPE_LEVEL_HIGH>; - }; - }; - - watchdog: watchdog@10060000 { - compatible = "samsung,exynos5250-wdt"; - reg = <0x10060000 0x100>; - interrupts = ; - clocks = <&clock CLK_WDT>; - clock-names = "watchdog"; - samsung,syscon-phandle = <&pmu_system_controller>; - }; - - adc: adc@126c0000 { - compatible = "samsung,exynos-adc-v1"; - reg = <0x126C0000 0x100>; - interrupt-parent = <&combiner>; - interrupts = <10 3>; - clocks = <&clock CLK_TSADC>; - clock-names = "adc"; - #io-channel-cells = <1>; - io-channel-ranges; - samsung,syscon-phandle = <&pmu_system_controller>; - status = "disabled"; - }; - - g2d: g2d@10800000 { - compatible = "samsung,exynos4212-g2d"; - reg = <0x10800000 0x1000>; - interrupts = ; - clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>; - clock-names = "sclk_fimg2d", "fimg2d"; - iommus = <&sysmmu_g2d>; - }; - - mshc_0: mmc@12550000 { - compatible = "samsung,exynos4412-dw-mshc"; - reg = <0x12550000 0x1000>; - interrupts = ; - #address-cells = <1>; - #size-cells = <0>; - fifo-depth = <0x80>; - clocks = <&clock CLK_SDMMC4>, <&clock CLK_SCLK_MMC4>; - clock-names = "biu", "ciu"; - status = "disabled"; - }; - - sysmmu_g2d: sysmmu@10A40000{ - compatible = "samsung,exynos-sysmmu"; - reg = <0x10A40000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <4 7>; - clock-names = "sysmmu", "master"; - clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; - #iommu-cells = <0>; - }; - - sysmmu_fimc_isp: sysmmu@12260000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x12260000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <16 2>; - power-domains = <&pd_isp>; - clock-names = "sysmmu"; - clocks = <&isp_clock CLK_ISP_SMMU_ISP>; - #iommu-cells = <0>; - }; - - sysmmu_fimc_drc: sysmmu@12270000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x12270000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <16 3>; - power-domains = <&pd_isp>; - clock-names = "sysmmu"; - clocks = <&isp_clock CLK_ISP_SMMU_DRC>; - #iommu-cells = <0>; - }; - - sysmmu_fimc_fd: sysmmu@122a0000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x122A0000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <16 4>; - power-domains = <&pd_isp>; - clock-names = "sysmmu"; - clocks = <&isp_clock CLK_ISP_SMMU_FD>; - #iommu-cells = <0>; - }; - - sysmmu_fimc_mcuctl: sysmmu@122b0000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x122B0000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <16 5>; - power-domains = <&pd_isp>; - clock-names = "sysmmu"; - clocks = <&isp_clock CLK_ISP_SMMU_ISPCX>; - #iommu-cells = <0>; - }; - - sysmmu_fimc_lite0: sysmmu@123b0000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x123B0000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <16 0>; - power-domains = <&pd_isp>; - clock-names = "sysmmu", "master"; - clocks = <&isp_clock CLK_ISP_SMMU_LITE0>, - <&isp_clock CLK_ISP_FIMC_LITE0>; - #iommu-cells = <0>; - }; - - sysmmu_fimc_lite1: sysmmu@123c0000 { - compatible = "samsung,exynos-sysmmu"; - reg = <0x123C0000 0x1000>; - interrupt-parent = <&combiner>; - interrupts = <16 1>; - power-domains = <&pd_isp>; - clock-names = "sysmmu", "master"; - clocks = <&isp_clock CLK_ISP_SMMU_LITE1>, - <&isp_clock CLK_ISP_FIMC_LITE1>; - #iommu-cells = <0>; - }; - - bus_dmc: bus_dmc { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_DIV_DMC>; - clock-names = "bus"; - operating-points-v2 = <&bus_dmc_opp_table>; - status = "disabled"; - }; - - bus_acp: bus_acp { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_DIV_ACP>; - clock-names = "bus"; - operating-points-v2 = <&bus_acp_opp_table>; - status = "disabled"; - }; - - bus_c2c: bus_c2c { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_DIV_C2C>; - clock-names = "bus"; - operating-points-v2 = <&bus_dmc_opp_table>; - status = "disabled"; - }; - - bus_dmc_opp_table: opp_table1 { - compatible = "operating-points-v2"; - opp-shared; - - opp-100000000 { - opp-hz = /bits/ 64 <100000000>; - opp-microvolt = <900000>; - }; - opp-134000000 { - opp-hz = /bits/ 64 <134000000>; - opp-microvolt = <900000>; - }; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; - opp-microvolt = <900000>; - }; - opp-267000000 { - opp-hz = /bits/ 64 <267000000>; - opp-microvolt = <950000>; - }; - opp-400000000 { - opp-hz = /bits/ 64 <400000000>; - opp-microvolt = <1050000>; - }; - }; - - bus_acp_opp_table: opp_table2 { - compatible = "operating-points-v2"; - opp-shared; - - opp-100000000 { - opp-hz = /bits/ 64 <100000000>; - }; - opp-134000000 { - opp-hz = /bits/ 64 <134000000>; - }; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; - }; - opp-267000000 { - opp-hz = /bits/ 64 <267000000>; - }; - }; - - bus_leftbus: bus_leftbus { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_DIV_GDL>; - clock-names = "bus"; - operating-points-v2 = <&bus_leftbus_opp_table>; - status = "disabled"; - }; - - bus_rightbus: bus_rightbus { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_DIV_GDR>; - clock-names = "bus"; - operating-points-v2 = <&bus_leftbus_opp_table>; - status = "disabled"; - }; - - bus_display: bus_display { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_ACLK160>; - clock-names = "bus"; - operating-points-v2 = <&bus_display_opp_table>; - status = "disabled"; - }; - - bus_fsys: bus_fsys { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_ACLK133>; - clock-names = "bus"; - operating-points-v2 = <&bus_fsys_opp_table>; - status = "disabled"; - }; - - bus_peri: bus_peri { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_ACLK100>; - clock-names = "bus"; - operating-points-v2 = <&bus_peri_opp_table>; - status = "disabled"; - }; - - bus_mfc: bus_mfc { - compatible = "samsung,exynos-bus"; - clocks = <&clock CLK_SCLK_MFC>; - clock-names = "bus"; - operating-points-v2 = <&bus_leftbus_opp_table>; - status = "disabled"; - }; - - bus_leftbus_opp_table: opp_table3 { - compatible = "operating-points-v2"; - opp-shared; - - opp-100000000 { - opp-hz = /bits/ 64 <100000000>; - opp-microvolt = <900000>; - }; - opp-134000000 { - opp-hz = /bits/ 64 <134000000>; - opp-microvolt = <925000>; - }; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; - opp-microvolt = <950000>; - }; - opp-200000000 { - opp-hz = /bits/ 64 <200000000>; - opp-microvolt = <1000000>; - }; - }; - - bus_display_opp_table: opp_table4 { - compatible = "operating-points-v2"; - opp-shared; - - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; - }; - opp-200000000 { - opp-hz = /bits/ 64 <200000000>; - }; - }; - - bus_fsys_opp_table: opp_table5 { - compatible = "operating-points-v2"; - opp-shared; - - opp-100000000 { - opp-hz = /bits/ 64 <100000000>; - }; - opp-134000000 { - opp-hz = /bits/ 64 <134000000>; - }; - }; - - bus_peri_opp_table: opp_table6 { - compatible = "operating-points-v2"; - opp-shared; - - opp-50000000 { - opp-hz = /bits/ 64 <50000000>; - }; - opp-100000000 { - opp-hz = /bits/ 64 <100000000>; + }; + }; + + watchdog: watchdog@10060000 { + compatible = "samsung,exynos5250-wdt"; + reg = <0x10060000 0x100>; + interrupts = ; + clocks = <&clock CLK_WDT>; + clock-names = "watchdog"; + samsung,syscon-phandle = <&pmu_system_controller>; + }; + + adc: adc@126c0000 { + compatible = "samsung,exynos-adc-v1"; + reg = <0x126C0000 0x100>; + interrupt-parent = <&combiner>; + interrupts = <10 3>; + clocks = <&clock CLK_TSADC>; + clock-names = "adc"; + #io-channel-cells = <1>; + io-channel-ranges; + samsung,syscon-phandle = <&pmu_system_controller>; + status = "disabled"; + }; + + g2d: g2d@10800000 { + compatible = "samsung,exynos4212-g2d"; + reg = <0x10800000 0x1000>; + interrupts = ; + clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>; + clock-names = "sclk_fimg2d", "fimg2d"; + iommus = <&sysmmu_g2d>; + }; + + mshc_0: mmc@12550000 { + compatible = "samsung,exynos4412-dw-mshc"; + reg = <0x12550000 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + fifo-depth = <0x80>; + clocks = <&clock CLK_SDMMC4>, <&clock CLK_SCLK_MMC4>; + clock-names = "biu", "ciu"; + status = "disabled"; + }; + + sysmmu_g2d: sysmmu@10A40000{ + compatible = "samsung,exynos-sysmmu"; + reg = <0x10A40000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 7>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_isp: sysmmu@12260000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12260000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 2>; + power-domains = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&isp_clock CLK_ISP_SMMU_ISP>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_drc: sysmmu@12270000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12270000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 3>; + power-domains = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&isp_clock CLK_ISP_SMMU_DRC>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_fd: sysmmu@122a0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x122A0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 4>; + power-domains = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&isp_clock CLK_ISP_SMMU_FD>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_mcuctl: sysmmu@122b0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x122B0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 5>; + power-domains = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&isp_clock CLK_ISP_SMMU_ISPCX>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_lite0: sysmmu@123b0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x123B0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 0>; + power-domains = <&pd_isp>; + clock-names = "sysmmu", "master"; + clocks = <&isp_clock CLK_ISP_SMMU_LITE0>, + <&isp_clock CLK_ISP_FIMC_LITE0>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_lite1: sysmmu@123c0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x123C0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 1>; + power-domains = <&pd_isp>; + clock-names = "sysmmu", "master"; + clocks = <&isp_clock CLK_ISP_SMMU_LITE1>, + <&isp_clock CLK_ISP_FIMC_LITE1>; + #iommu-cells = <0>; + }; + + bus_dmc: bus_dmc { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_DIV_DMC>; + clock-names = "bus"; + operating-points-v2 = <&bus_dmc_opp_table>; + status = "disabled"; + }; + + bus_acp: bus_acp { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_DIV_ACP>; + clock-names = "bus"; + operating-points-v2 = <&bus_acp_opp_table>; + status = "disabled"; + }; + + bus_c2c: bus_c2c { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_DIV_C2C>; + clock-names = "bus"; + operating-points-v2 = <&bus_dmc_opp_table>; + status = "disabled"; + }; + + bus_dmc_opp_table: opp_table1 { + compatible = "operating-points-v2"; + opp-shared; + + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + opp-microvolt = <900000>; + }; + opp-134000000 { + opp-hz = /bits/ 64 <134000000>; + opp-microvolt = <900000>; + }; + opp-160000000 { + opp-hz = /bits/ 64 <160000000>; + opp-microvolt = <900000>; + }; + opp-267000000 { + opp-hz = /bits/ 64 <267000000>; + opp-microvolt = <950000>; + }; + opp-400000000 { + opp-hz = /bits/ 64 <400000000>; + opp-microvolt = <1050000>; + }; + }; + + bus_acp_opp_table: opp_table2 { + compatible = "operating-points-v2"; + opp-shared; + + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + }; + opp-134000000 { + opp-hz = /bits/ 64 <134000000>; + }; + opp-160000000 { + opp-hz = /bits/ 64 <160000000>; + }; + opp-267000000 { + opp-hz = /bits/ 64 <267000000>; + }; + }; + + bus_leftbus: bus_leftbus { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_DIV_GDL>; + clock-names = "bus"; + operating-points-v2 = <&bus_leftbus_opp_table>; + status = "disabled"; + }; + + bus_rightbus: bus_rightbus { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_DIV_GDR>; + clock-names = "bus"; + operating-points-v2 = <&bus_leftbus_opp_table>; + status = "disabled"; + }; + + bus_display: bus_display { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_ACLK160>; + clock-names = "bus"; + operating-points-v2 = <&bus_display_opp_table>; + status = "disabled"; + }; + + bus_fsys: bus_fsys { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_ACLK133>; + clock-names = "bus"; + operating-points-v2 = <&bus_fsys_opp_table>; + status = "disabled"; + }; + + bus_peri: bus_peri { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_ACLK100>; + clock-names = "bus"; + operating-points-v2 = <&bus_peri_opp_table>; + status = "disabled"; + }; + + bus_mfc: bus_mfc { + compatible = "samsung,exynos-bus"; + clocks = <&clock CLK_SCLK_MFC>; + clock-names = "bus"; + operating-points-v2 = <&bus_leftbus_opp_table>; + status = "disabled"; + }; + + bus_leftbus_opp_table: opp_table3 { + compatible = "operating-points-v2"; + opp-shared; + + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + opp-microvolt = <900000>; + }; + opp-134000000 { + opp-hz = /bits/ 64 <134000000>; + opp-microvolt = <925000>; + }; + opp-160000000 { + opp-hz = /bits/ 64 <160000000>; + opp-microvolt = <950000>; + }; + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + opp-microvolt = <1000000>; + }; + }; + + bus_display_opp_table: opp_table4 { + compatible = "operating-points-v2"; + opp-shared; + + opp-160000000 { + opp-hz = /bits/ 64 <160000000>; + }; + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + }; + }; + + bus_fsys_opp_table: opp_table5 { + compatible = "operating-points-v2"; + opp-shared; + + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + }; + opp-134000000 { + opp-hz = /bits/ 64 <134000000>; + }; + }; + + bus_peri_opp_table: opp_table6 { + compatible = "operating-points-v2"; + opp-shared; + + opp-50000000 { + opp-hz = /bits/ 64 <50000000>; + }; + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + }; }; }; }; -- cgit v1.3-14-g43fede From bd010d6066ba3267909bf8a318e0d2f2eaf3fb53 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Thu, 8 Feb 2018 11:01:34 +1100 Subject: dt-bindings: samsung: Document bindings for Midas family boards Document GT-I9300, GT-I9305, GT-N7100, and GT-N7105 bindings, along with the shared "midas" binding. Signed-off-by: Simon Shields Reviewed-by: Rob Herring Signed-off-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/arm/samsung/samsung-boards.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/samsung/samsung-boards.txt b/Documentation/devicetree/bindings/arm/samsung/samsung-boards.txt index 469ac98ecf8f..14510b215480 100644 --- a/Documentation/devicetree/bindings/arm/samsung/samsung-boards.txt +++ b/Documentation/devicetree/bindings/arm/samsung/samsung-boards.txt @@ -9,7 +9,11 @@ Required root node properties: - "samsung,smdkv310" - for Exynos4210-based Samsung SMDKV310 eval board. - "samsung,trats" - for Exynos4210-based Tizen Reference board. - "samsung,universal_c210" - for Exynos4210-based Samsung board. + - "samsung,i9300" - for Exynos4412-based Samsung GT-I9300 board. + - "samsung,i9305" - for Exynos4412-based Samsung GT-I9305 board. + - "samsung,midas" - for Exynos4412-based Samsung Midas board. - "samsung,smdk4412", - for Exynos4412-based Samsung SMDK4412 eval board. + - "samsung,n710x" - for Exynos4412-based Samsung GT-N7100/GT-N7105 board. - "samsung,trats2" - for Exynos4412-based Tizen Reference board. - "samsung,smdk5250" - for Exynos5250-based Samsung SMDK5250 eval board. - "samsung,xyref5260" - for Exynos5260-based Samsung board. -- cgit v1.3-14-g43fede From cd6f55457eb449a388e793abd676e3a5b73510bc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 9 Feb 2018 14:28:01 +0530 Subject: ARM: dts: exynos: Remove "cooling-{min|max}-level" for CPU nodes The "cooling-min-level" and "cooling-max-level" properties are not parsed by any part of the kernel currently and the max cooling state of a CPU cooling device is found by referring to the cpufreq table instead. Remove the unused properties from the CPU nodes. Signed-off-by: Viresh Kumar Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4210.dtsi | 2 -- arch/arm/boot/dts/exynos4412.dtsi | 2 -- arch/arm/boot/dts/exynos5250.dtsi | 2 -- arch/arm/boot/dts/exynos5420-cpus.dtsi | 16 ---------------- arch/arm/boot/dts/exynos5422-cpus.dtsi | 16 ---------------- 5 files changed, 38 deletions(-) diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 80c1972a2ecf..853604d91158 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -48,8 +48,6 @@ 400000 975000 200000 950000 >; - cooling-min-level = <4>; - cooling-max-level = <2>; #cooling-cells = <2>; /* min followed by max */ }; diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi index b765c099a864..2ae1ab602f4b 100644 --- a/arch/arm/boot/dts/exynos4412.dtsi +++ b/arch/arm/boot/dts/exynos4412.dtsi @@ -42,8 +42,6 @@ clocks = <&clock CLK_ARM_CLK>; clock-names = "cpu"; operating-points-v2 = <&cpu0_opp_table>; - cooling-min-level = <13>; - cooling-max-level = <7>; #cooling-cells = <2>; /* min followed by max */ }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 56626d1a4235..bb4180ef7885 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -77,8 +77,6 @@ 300000 937500 200000 925000 >; - cooling-min-level = <15>; - cooling-max-level = <9>; #cooling-cells = <2>; /* min followed by max */ }; cpu@1 { diff --git a/arch/arm/boot/dts/exynos5420-cpus.dtsi b/arch/arm/boot/dts/exynos5420-cpus.dtsi index 123f0cef658d..a8e449471304 100644 --- a/arch/arm/boot/dts/exynos5420-cpus.dtsi +++ b/arch/arm/boot/dts/exynos5420-cpus.dtsi @@ -30,8 +30,6 @@ clock-frequency = <1800000000>; cci-control-port = <&cci_control1>; operating-points-v2 = <&cluster_a15_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <11>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <1024>; }; @@ -43,8 +41,6 @@ clock-frequency = <1800000000>; cci-control-port = <&cci_control1>; operating-points-v2 = <&cluster_a15_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <11>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <1024>; }; @@ -56,8 +52,6 @@ clock-frequency = <1800000000>; cci-control-port = <&cci_control1>; operating-points-v2 = <&cluster_a15_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <11>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <1024>; }; @@ -69,8 +63,6 @@ clock-frequency = <1800000000>; cci-control-port = <&cci_control1>; operating-points-v2 = <&cluster_a15_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <11>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <1024>; }; @@ -83,8 +75,6 @@ clock-frequency = <1000000000>; cci-control-port = <&cci_control0>; operating-points-v2 = <&cluster_a7_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <7>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <539>; }; @@ -96,8 +86,6 @@ clock-frequency = <1000000000>; cci-control-port = <&cci_control0>; operating-points-v2 = <&cluster_a7_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <7>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <539>; }; @@ -109,8 +97,6 @@ clock-frequency = <1000000000>; cci-control-port = <&cci_control0>; operating-points-v2 = <&cluster_a7_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <7>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <539>; }; @@ -122,8 +108,6 @@ clock-frequency = <1000000000>; cci-control-port = <&cci_control0>; operating-points-v2 = <&cluster_a7_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <7>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <539>; }; diff --git a/arch/arm/boot/dts/exynos5422-cpus.dtsi b/arch/arm/boot/dts/exynos5422-cpus.dtsi index c593809c7f08..7c130a00d1a8 100644 --- a/arch/arm/boot/dts/exynos5422-cpus.dtsi +++ b/arch/arm/boot/dts/exynos5422-cpus.dtsi @@ -29,8 +29,6 @@ clock-frequency = <1000000000>; cci-control-port = <&cci_control0>; operating-points-v2 = <&cluster_a7_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <11>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <539>; }; @@ -42,8 +40,6 @@ clock-frequency = <1000000000>; cci-control-port = <&cci_control0>; operating-points-v2 = <&cluster_a7_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <11>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <539>; }; @@ -55,8 +51,6 @@ clock-frequency = <1000000000>; cci-control-port = <&cci_control0>; operating-points-v2 = <&cluster_a7_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <11>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <539>; }; @@ -68,8 +62,6 @@ clock-frequency = <1000000000>; cci-control-port = <&cci_control0>; operating-points-v2 = <&cluster_a7_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <11>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <539>; }; @@ -82,8 +74,6 @@ clock-frequency = <1800000000>; cci-control-port = <&cci_control1>; operating-points-v2 = <&cluster_a15_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <15>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <1024>; }; @@ -95,8 +85,6 @@ clock-frequency = <1800000000>; cci-control-port = <&cci_control1>; operating-points-v2 = <&cluster_a15_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <15>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <1024>; }; @@ -108,8 +96,6 @@ clock-frequency = <1800000000>; cci-control-port = <&cci_control1>; operating-points-v2 = <&cluster_a15_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <15>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <1024>; }; @@ -121,8 +107,6 @@ clock-frequency = <1800000000>; cci-control-port = <&cci_control1>; operating-points-v2 = <&cluster_a15_opp_table>; - cooling-min-level = <0>; - cooling-max-level = <15>; #cooling-cells = <2>; /* min followed by max */ capacity-dmips-mhz = <1024>; }; -- cgit v1.3-14-g43fede From c5611a8751e67595e4e7d3feaff3c900b92094b9 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Mon, 5 Feb 2018 05:45:53 -0500 Subject: iommu: Do not return error code for APIs with size_t return type Currently, iommu_unmap, iommu_unmap_fast and iommu_map_sg return size_t. However, some of the return values are error codes (< 0), which can be misinterpreted as large size. Therefore, returning size 0 instead to signify failure to map/unmap. Cc: Joerg Roedel Cc: Alex Williamson Signed-off-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 2 +- drivers/iommu/iommu.c | 6 +++--- include/linux/iommu.h | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 74788fdeb773..ecdeb045deef 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3050,7 +3050,7 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova, size_t unmap_size; if (domain->mode == PAGE_MODE_NONE) - return -EINVAL; + return 0; mutex_lock(&domain->api_lock); unmap_size = iommu_unmap_page(domain, iova, page_size); diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 69fef991c651..d2aa23202bb9 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1573,10 +1573,10 @@ static size_t __iommu_unmap(struct iommu_domain *domain, if (unlikely(ops->unmap == NULL || domain->pgsize_bitmap == 0UL)) - return -ENODEV; + return 0; if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) - return -EINVAL; + return 0; /* find out the minimum page size supported */ min_pagesz = 1 << __ffs(domain->pgsize_bitmap); @@ -1589,7 +1589,7 @@ static size_t __iommu_unmap(struct iommu_domain *domain, if (!IS_ALIGNED(iova | size, min_pagesz)) { pr_err("unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", iova, size, min_pagesz); - return -EINVAL; + return 0; } pr_debug("unmap this: iova 0x%lx size 0x%zx\n", iova, size); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 41b8c5757859..19938ee6eb31 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -465,23 +465,23 @@ static inline int iommu_map(struct iommu_domain *domain, unsigned long iova, return -ENODEV; } -static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t size) +static inline size_t iommu_unmap(struct iommu_domain *domain, + unsigned long iova, size_t size) { - return -ENODEV; + return 0; } -static inline int iommu_unmap_fast(struct iommu_domain *domain, unsigned long iova, - int gfp_order) +static inline size_t iommu_unmap_fast(struct iommu_domain *domain, + unsigned long iova, int gfp_order) { - return -ENODEV; + return 0; } static inline size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova, struct scatterlist *sg, unsigned int nents, int prot) { - return -ENODEV; + return 0; } static inline void iommu_flush_tlb_all(struct iommu_domain *domain) -- cgit v1.3-14-g43fede From 193e67c00e33e188d68815fb78806317c77f156c Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Mon, 5 Feb 2018 23:29:19 +0530 Subject: iommu/io-pgtable: Use size_t return type for all foo_unmap Unmap returns a size_t all throughout the IOMMU framework. Make io-pgtable match this convention. Moreover, there isn't a need to have a signed int return type as we return 0 in case of failures. Signed-off-by: Vivek Gautam Acked-by: Robin Murphy Signed-off-by: Joerg Roedel --- drivers/iommu/io-pgtable-arm-v7s.c | 21 +++++++++++---------- drivers/iommu/io-pgtable-arm.c | 24 ++++++++++++------------ drivers/iommu/io-pgtable.h | 4 ++-- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c index 2ca08dc9331c..10e4a3d11c02 100644 --- a/drivers/iommu/io-pgtable-arm-v7s.c +++ b/drivers/iommu/io-pgtable-arm-v7s.c @@ -357,8 +357,8 @@ static bool arm_v7s_pte_is_cont(arm_v7s_iopte pte, int lvl) return false; } -static int __arm_v7s_unmap(struct arm_v7s_io_pgtable *, unsigned long, - size_t, int, arm_v7s_iopte *); +static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *, unsigned long, + size_t, int, arm_v7s_iopte *); static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data, unsigned long iova, phys_addr_t paddr, int prot, @@ -541,9 +541,10 @@ static arm_v7s_iopte arm_v7s_split_cont(struct arm_v7s_io_pgtable *data, return pte; } -static int arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data, - unsigned long iova, size_t size, - arm_v7s_iopte blk_pte, arm_v7s_iopte *ptep) +static size_t arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data, + unsigned long iova, size_t size, + arm_v7s_iopte blk_pte, + arm_v7s_iopte *ptep) { struct io_pgtable_cfg *cfg = &data->iop.cfg; arm_v7s_iopte pte, *tablep; @@ -584,9 +585,9 @@ static int arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data, return size; } -static int __arm_v7s_unmap(struct arm_v7s_io_pgtable *data, - unsigned long iova, size_t size, int lvl, - arm_v7s_iopte *ptep) +static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data, + unsigned long iova, size_t size, int lvl, + arm_v7s_iopte *ptep) { arm_v7s_iopte pte[ARM_V7S_CONT_PAGES]; struct io_pgtable *iop = &data->iop; @@ -656,8 +657,8 @@ static int __arm_v7s_unmap(struct arm_v7s_io_pgtable *data, return __arm_v7s_unmap(data, iova, size, lvl + 1, ptep); } -static int arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova, - size_t size) +static size_t arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova, + size_t size) { struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops); diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 51e5c43caed1..8faaaa04d7a7 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -268,9 +268,9 @@ static void __arm_lpae_set_pte(arm_lpae_iopte *ptep, arm_lpae_iopte pte, __arm_lpae_sync_pte(ptep, cfg); } -static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, - unsigned long iova, size_t size, int lvl, - arm_lpae_iopte *ptep); +static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, + unsigned long iova, size_t size, int lvl, + arm_lpae_iopte *ptep); static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, phys_addr_t paddr, arm_lpae_iopte prot, @@ -506,10 +506,10 @@ static void arm_lpae_free_pgtable(struct io_pgtable *iop) kfree(data); } -static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, - unsigned long iova, size_t size, - arm_lpae_iopte blk_pte, int lvl, - arm_lpae_iopte *ptep) +static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, + unsigned long iova, size_t size, + arm_lpae_iopte blk_pte, int lvl, + arm_lpae_iopte *ptep) { struct io_pgtable_cfg *cfg = &data->iop.cfg; arm_lpae_iopte pte, *tablep; @@ -560,9 +560,9 @@ static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, return size; } -static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, - unsigned long iova, size_t size, int lvl, - arm_lpae_iopte *ptep) +static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, + unsigned long iova, size_t size, int lvl, + arm_lpae_iopte *ptep) { arm_lpae_iopte pte; struct io_pgtable *iop = &data->iop; @@ -606,8 +606,8 @@ static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, return __arm_lpae_unmap(data, iova, size, lvl + 1, ptep); } -static int arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova, - size_t size) +static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova, + size_t size) { struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); arm_lpae_iopte *ptep = data->pgd; diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h index cd2e1eafffe6..2df79093cad9 100644 --- a/drivers/iommu/io-pgtable.h +++ b/drivers/iommu/io-pgtable.h @@ -119,8 +119,8 @@ struct io_pgtable_cfg { struct io_pgtable_ops { int (*map)(struct io_pgtable_ops *ops, unsigned long iova, phys_addr_t paddr, size_t size, int prot); - int (*unmap)(struct io_pgtable_ops *ops, unsigned long iova, - size_t size); + size_t (*unmap)(struct io_pgtable_ops *ops, unsigned long iova, + size_t size); phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops, unsigned long iova); }; -- cgit v1.3-14-g43fede From 29a1f599c0cc37004f92ba455d1ccda3db0b6a94 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Thu, 14 Dec 2017 13:31:43 +0800 Subject: rtc: Add tracepoints for RTC system It will be more helpful to add some tracepoints to track RTC actions when debugging RTC driver. Below sample is that we set/read the RTC time, then set 2 alarms, so we can see the trace logs: set/read RTC time: kworker/0:1-67 [000] 21.814245: rtc_set_time: UTC (1510301580) (0) kworker/0:1-67 [000] 21.814312: rtc_read_time: UTC (1510301580) (0) set the first alarm timer: kworker/0:1-67 [000] 21.829238: rtc_timer_enqueue: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0 kworker/0:1-67 [000] 22.018279: rtc_set_alarm: UTC (1510301700) (0) set the second alarm timer: kworker/0:1-67 [000] 22.230284: rtc_timer_enqueue: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0 the first alarm timer was expired: kworker/0:1-67 [000] 145.155584: rtc_timer_dequeue: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0 kworker/0:1-67 [000] 145.155593: rtc_timer_fired: RTC timer:(ffffffc15eb49bc8) expires:1510301700000000000 period:0 kworker/0:1-67 [000] 145.172504: rtc_set_alarm: UTC (1510301820) (0) the second alarm timer was expired: kworker/0:1-67 [000] 269.102353: rtc_timer_dequeue: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0 kworker/0:1-67 [000] 269.102360: rtc_timer_fired: RTC timer:(ffffff80088e6430) expires:1510301820000000000 period:0 disable alarm irq: kworker/0:1-67 [000] 269.102469: rtc_alarm_irq_enable: disable RTC alarm IRQ (0) Signed-off-by: Baolin Wang Signed-off-by: Alexandre Belloni --- drivers/rtc/interface.c | 30 +++++++ include/trace/events/rtc.h | 206 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 include/trace/events/rtc.h diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 672b192f8153..7e253be19ba7 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -17,6 +17,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer); static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer); @@ -53,6 +56,8 @@ int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) err = __rtc_read_time(rtc, tm); mutex_unlock(&rtc->ops_lock); + + trace_rtc_read_time(rtc_tm_to_time64(tm), err); return err; } EXPORT_SYMBOL_GPL(rtc_read_time); @@ -87,6 +92,8 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) mutex_unlock(&rtc->ops_lock); /* A timer might have just expired */ schedule_work(&rtc->irqwork); + + trace_rtc_set_time(rtc_tm_to_time64(tm), err); return err; } EXPORT_SYMBOL_GPL(rtc_set_time); @@ -119,6 +126,8 @@ static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *al } mutex_unlock(&rtc->ops_lock); + + trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err); return err; } @@ -316,6 +325,7 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } mutex_unlock(&rtc->ops_lock); + trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err); return err; } EXPORT_SYMBOL_GPL(rtc_read_alarm); @@ -352,6 +362,7 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) else err = rtc->ops->set_alarm(rtc->dev.parent, alarm); + trace_rtc_set_alarm(rtc_tm_to_time64(&alarm->time), err); return err; } @@ -406,6 +417,7 @@ int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) rtc->aie_timer.enabled = 1; timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node); + trace_rtc_timer_enqueue(&rtc->aie_timer); } mutex_unlock(&rtc->ops_lock); return err; @@ -435,6 +447,8 @@ int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled); mutex_unlock(&rtc->ops_lock); + + trace_rtc_alarm_irq_enable(enabled, err); return err; } EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable); @@ -709,6 +723,8 @@ retry: rtc->pie_enabled = enabled; } spin_unlock_irqrestore(&rtc->irq_task_lock, flags); + + trace_rtc_irq_set_state(enabled, err); return err; } EXPORT_SYMBOL_GPL(rtc_irq_set_state); @@ -745,6 +761,8 @@ retry: } } spin_unlock_irqrestore(&rtc->irq_task_lock, flags); + + trace_rtc_irq_set_freq(freq, err); return err; } EXPORT_SYMBOL_GPL(rtc_irq_set_freq); @@ -779,6 +797,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) } timerqueue_add(&rtc->timerqueue, &timer->node); + trace_rtc_timer_enqueue(timer); if (!next || ktime_before(timer->node.expires, next->expires)) { struct rtc_wkalrm alarm; int err; @@ -790,6 +809,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) schedule_work(&rtc->irqwork); } else if (err) { timerqueue_del(&rtc->timerqueue, &timer->node); + trace_rtc_timer_dequeue(timer); timer->enabled = 0; return err; } @@ -803,6 +823,7 @@ static void rtc_alarm_disable(struct rtc_device *rtc) return; rtc->ops->alarm_irq_enable(rtc->dev.parent, false); + trace_rtc_alarm_irq_enable(0, 0); } /** @@ -821,6 +842,7 @@ static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) { struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); timerqueue_del(&rtc->timerqueue, &timer->node); + trace_rtc_timer_dequeue(timer); timer->enabled = 0; if (next == &timer->node) { struct rtc_wkalrm alarm; @@ -871,16 +893,19 @@ again: /* expire timer */ timer = container_of(next, struct rtc_timer, node); timerqueue_del(&rtc->timerqueue, &timer->node); + trace_rtc_timer_dequeue(timer); timer->enabled = 0; if (timer->task.func) timer->task.func(timer->task.private_data); + trace_rtc_timer_fired(timer); /* Re-add/fwd periodic timers */ if (ktime_to_ns(timer->period)) { timer->node.expires = ktime_add(timer->node.expires, timer->period); timer->enabled = 1; timerqueue_add(&rtc->timerqueue, &timer->node); + trace_rtc_timer_enqueue(timer); } } @@ -902,6 +927,7 @@ reprogram: timer = container_of(next, struct rtc_timer, node); timerqueue_del(&rtc->timerqueue, &timer->node); + trace_rtc_timer_dequeue(timer); timer->enabled = 0; dev_err(&rtc->dev, "__rtc_set_alarm: err=%d\n", err); goto again; @@ -992,6 +1018,8 @@ int rtc_read_offset(struct rtc_device *rtc, long *offset) mutex_lock(&rtc->ops_lock); ret = rtc->ops->read_offset(rtc->dev.parent, offset); mutex_unlock(&rtc->ops_lock); + + trace_rtc_read_offset(*offset, ret); return ret; } @@ -1025,5 +1053,7 @@ int rtc_set_offset(struct rtc_device *rtc, long offset) mutex_lock(&rtc->ops_lock); ret = rtc->ops->set_offset(rtc->dev.parent, offset); mutex_unlock(&rtc->ops_lock); + + trace_rtc_set_offset(offset, ret); return ret; } diff --git a/include/trace/events/rtc.h b/include/trace/events/rtc.h new file mode 100644 index 000000000000..621333f1c890 --- /dev/null +++ b/include/trace/events/rtc.h @@ -0,0 +1,206 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM rtc + +#if !defined(_TRACE_RTC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_RTC_H + +#include +#include + +DECLARE_EVENT_CLASS(rtc_time_alarm_class, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err), + + TP_STRUCT__entry( + __field(time64_t, secs) + __field(int, err) + ), + + TP_fast_assign( + __entry->secs = secs; + __entry->err = err; + ), + + TP_printk("UTC (%lld) (%d)", + __entry->secs, __entry->err + ) +); + +DEFINE_EVENT(rtc_time_alarm_class, rtc_set_time, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err) +); + +DEFINE_EVENT(rtc_time_alarm_class, rtc_read_time, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err) +); + +DEFINE_EVENT(rtc_time_alarm_class, rtc_set_alarm, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err) +); + +DEFINE_EVENT(rtc_time_alarm_class, rtc_read_alarm, + + TP_PROTO(time64_t secs, int err), + + TP_ARGS(secs, err) +); + +TRACE_EVENT(rtc_irq_set_freq, + + TP_PROTO(int freq, int err), + + TP_ARGS(freq, err), + + TP_STRUCT__entry( + __field(int, freq) + __field(int, err) + ), + + TP_fast_assign( + __entry->freq = freq; + __entry->err = err; + ), + + TP_printk("set RTC periodic IRQ frequency:%u (%d)", + __entry->freq, __entry->err + ) +); + +TRACE_EVENT(rtc_irq_set_state, + + TP_PROTO(int enabled, int err), + + TP_ARGS(enabled, err), + + TP_STRUCT__entry( + __field(int, enabled) + __field(int, err) + ), + + TP_fast_assign( + __entry->enabled = enabled; + __entry->err = err; + ), + + TP_printk("%s RTC 2^N Hz periodic IRQs (%d)", + __entry->enabled ? "enable" : "disable", + __entry->err + ) +); + +TRACE_EVENT(rtc_alarm_irq_enable, + + TP_PROTO(unsigned int enabled, int err), + + TP_ARGS(enabled, err), + + TP_STRUCT__entry( + __field(unsigned int, enabled) + __field(int, err) + ), + + TP_fast_assign( + __entry->enabled = enabled; + __entry->err = err; + ), + + TP_printk("%s RTC alarm IRQ (%d)", + __entry->enabled ? "enable" : "disable", + __entry->err + ) +); + +DECLARE_EVENT_CLASS(rtc_offset_class, + + TP_PROTO(long offset, int err), + + TP_ARGS(offset, err), + + TP_STRUCT__entry( + __field(long, offset) + __field(int, err) + ), + + TP_fast_assign( + __entry->offset = offset; + __entry->err = err; + ), + + TP_printk("RTC offset: %ld (%d)", + __entry->offset, __entry->err + ) +); + +DEFINE_EVENT(rtc_offset_class, rtc_set_offset, + + TP_PROTO(long offset, int err), + + TP_ARGS(offset, err) +); + +DEFINE_EVENT(rtc_offset_class, rtc_read_offset, + + TP_PROTO(long offset, int err), + + TP_ARGS(offset, err) +); + +DECLARE_EVENT_CLASS(rtc_timer_class, + + TP_PROTO(struct rtc_timer *timer), + + TP_ARGS(timer), + + TP_STRUCT__entry( + __field(struct rtc_timer *, timer) + __field(ktime_t, expires) + __field(ktime_t, period) + ), + + TP_fast_assign( + __entry->timer = timer; + __entry->expires = timer->node.expires; + __entry->period = timer->period; + ), + + TP_printk("RTC timer:(%p) expires:%lld period:%lld", + __entry->timer, __entry->expires, __entry->period + ) +); + +DEFINE_EVENT(rtc_timer_class, rtc_timer_enqueue, + + TP_PROTO(struct rtc_timer *timer), + + TP_ARGS(timer) +); + +DEFINE_EVENT(rtc_timer_class, rtc_timer_dequeue, + + TP_PROTO(struct rtc_timer *timer), + + TP_ARGS(timer) +); + +DEFINE_EVENT(rtc_timer_class, rtc_timer_fired, + + TP_PROTO(struct rtc_timer *timer), + + TP_ARGS(timer) +); + +#endif /* _TRACE_RTC_H */ + +/* This part must be outside protection */ +#include -- cgit v1.3-14-g43fede From 36d46cdb43efea74043e29e2a62b13e9aca31452 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 25 Dec 2017 19:10:37 +0800 Subject: rtc: Fix overflow when converting time64_t to rtc_time If we convert one large time values to rtc_time, in the original formula 'days * 86400' can be overflowed in 'unsigned int' type to make the formula get one incorrect remain seconds value. Thus we can use div_s64_rem() function to avoid this situation. Signed-off-by: Baolin Wang Acked-by: Arnd Bergmann Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-lib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index 1ae7da5cfc60..ad5bb21908e5 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -52,13 +52,11 @@ EXPORT_SYMBOL(rtc_year_days); */ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) { - unsigned int month, year; - unsigned long secs; + unsigned int month, year, secs; int days; /* time must be positive */ - days = div_s64(time, 86400); - secs = time - (unsigned int) days * 86400; + days = div_s64_rem(time, 86400, &secs); /* day of the week, 1970-01-01 was a Thursday */ tm->tm_wday = (days + 4) % 7; -- cgit v1.3-14-g43fede From 82d632b85eb89f97051530f556cb49ee1c04bde7 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 16 Jan 2018 14:45:21 +0000 Subject: rtc: goldfish: Add missing MODULE_LICENSE Fix the following warning in MIPS allmodconfig by adding a MODULE_LICENSE() at the end of rtc-goldfish.c, based on the file header comment which says GNU General Public License version 2: WARNING: modpost: missing MODULE_LICENSE() in drivers/rtc/rtc-goldfish.o Fixes: f22d9cdcb5eb ("rtc: goldfish: Add RTC driver for Android emulator") Signed-off-by: James Hogan Cc: Miodrag Dinic Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: linux-rtc@vger.kernel.org Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-goldfish.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/rtc/rtc-goldfish.c b/drivers/rtc/rtc-goldfish.c index d67769265185..a1c44d0c8557 100644 --- a/drivers/rtc/rtc-goldfish.c +++ b/drivers/rtc/rtc-goldfish.c @@ -235,3 +235,5 @@ static struct platform_driver goldfish_rtc = { }; module_platform_driver(goldfish_rtc); + +MODULE_LICENSE("GPL v2"); -- cgit v1.3-14-g43fede From 5134d2fd9fe1278ecb309421275d88b6c80ebddd Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 23 Jan 2018 10:17:27 +0000 Subject: rtc: ds1302: remove redundant initializations of pointer bp Pointe bp is being initialized and this value is never read, it is being updated to the same value later just before it is going to be used. Remove the initialization as it is never read and keep the setting of bp closer to the use of bp. Cleans up clang warnings: drivers/rtc/rtc-ds1302.c:115:7: warning: Value stored to 'bp' during its initialization is never read drivers/rtc/rtc-ds1302.c:46:7: warning: Value stored to 'bp' during its initialization is never read Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1302.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index 0ec4be62322b..43bcb17c922e 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -43,7 +43,7 @@ static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *time) { struct spi_device *spi = dev_get_drvdata(dev); u8 buf[1 + RTC_CLCK_LEN]; - u8 *bp = buf; + u8 *bp; int status; /* Enable writing */ @@ -112,7 +112,7 @@ static int ds1302_probe(struct spi_device *spi) struct rtc_device *rtc; u8 addr; u8 buf[4]; - u8 *bp = buf; + u8 *bp; int status; /* Sanity check board setup data. This may be hooked up -- cgit v1.3-14-g43fede From 458df78b1c513de9311afd913bdf93777af2d157 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 26 Jan 2018 08:52:19 -0800 Subject: scsi: scsi_debug: Simplify request tag decoding Since commit 64d513ac31bd ("scsi: use host wide tags by default") all SCSI requests have a tag, whether or not scsi-mq is enabled. Additionally, it is safe to use blk_mq_unique_tag() and blk_mq_unique_tag_to_hwq() for legacy SCSI queues. Since this means that the sdebug_mq_active variable is superfluous, remove it. Signed-off-by: Bart Van Assche Cc: Douglas Gilbert Cc: Christoph Hellwig Cc: Hannes Reinecke Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index a5986dae9020..7d2ce0cc915a 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -649,7 +649,6 @@ static bool sdebug_any_injecting_opt; static bool sdebug_verbose; static bool have_dif_prot; static bool sdebug_statistics = DEF_STATISTICS; -static bool sdebug_mq_active; static unsigned int sdebug_store_sectors; static sector_t sdebug_capacity; /* in sectors */ @@ -3727,20 +3726,13 @@ static int resp_xdwriteread_10(struct scsi_cmnd *scp, static struct sdebug_queue *get_queue(struct scsi_cmnd *cmnd) { - struct sdebug_queue *sqp = sdebug_q_arr; + u32 tag = blk_mq_unique_tag(cmnd->request); + u16 hwq = blk_mq_unique_tag_to_hwq(tag); - if (sdebug_mq_active) { - u32 tag = blk_mq_unique_tag(cmnd->request); - u16 hwq = blk_mq_unique_tag_to_hwq(tag); - - if (unlikely(hwq >= submit_queues)) { - pr_warn("Unexpected hwq=%d, apply modulo\n", hwq); - hwq %= submit_queues; - } - pr_debug("tag=%u, hwq=%d\n", tag, hwq); - return sqp + hwq; - } else - return sqp; + pr_debug("tag=%#x, hwq=%d\n", tag, hwq); + if (WARN_ON_ONCE(hwq >= submit_queues)) + hwq = 0; + return sdebug_q_arr + hwq; } /* Queued (deferred) command completions converge here. */ @@ -4587,9 +4579,8 @@ static int scsi_debug_show_info(struct seq_file *m, struct Scsi_Host *host) num_host_resets); seq_printf(m, "dix_reads=%d, dix_writes=%d, dif_errors=%d\n", dix_reads, dix_writes, dif_errors); - seq_printf(m, "usec_in_jiffy=%lu, %s=%d, mq_active=%d\n", - TICK_NSEC / 1000, "statistics", sdebug_statistics, - sdebug_mq_active); + seq_printf(m, "usec_in_jiffy=%lu, statistics=%d\n", TICK_NSEC / 1000, + sdebug_statistics); seq_printf(m, "cmnd_count=%d, completions=%d, %s=%d, a_tsf=%d\n", atomic_read(&sdebug_cmnd_count), atomic_read(&sdebug_completions), @@ -5612,13 +5603,8 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost, n += scnprintf(b + n, sb - n, "%02x ", (u32)cmd[k]); } - if (sdebug_mq_active) - sdev_printk(KERN_INFO, sdp, "%s: tag=%u, cmd %s\n", - my_name, blk_mq_unique_tag(scp->request), - b); - else - sdev_printk(KERN_INFO, sdp, "%s: cmd %s\n", my_name, - b); + sdev_printk(KERN_INFO, sdp, "%s: tag=%#x, cmd %s\n", my_name, + blk_mq_unique_tag(scp->request), b); } if (fake_host_busy(scp)) return SCSI_MLQUEUE_HOST_BUSY; @@ -5782,8 +5768,7 @@ static int sdebug_driver_probe(struct device * dev) } /* Decide whether to tell scsi subsystem that we want mq */ /* Following should give the same answer for each host */ - sdebug_mq_active = shost_use_blk_mq(hpnt) && (submit_queues > 1); - if (sdebug_mq_active) + if (shost_use_blk_mq(hpnt)) hpnt->nr_hw_queues = submit_queues; sdbg_host->shost = hpnt; -- cgit v1.3-14-g43fede From d9da891a892a38db4ec7abcc2df57f9ae3777d99 Mon Sep 17 00:00:00 2001 From: Laurence Oberman Date: Sat, 3 Feb 2018 13:38:35 -0500 Subject: scsi: scsi_debug: Add two new parameters to scsi_debug driver This patch adds two new parameters to the scsi_debug driver. During various fault injection scenarios it would be useful to be able to pick a specific starting sector and number of follow on sectors where a MEDIUM ERROR for reads would be returned against a scsi-debug device. Right now this only works against sector 0x1234 and OPT_MEDIUM_ERR_NUM follow on sectors. However during testing of md-raid and other scenarios I wanted more flexibility. The idea is add 2 new parameters: medium_error_start medium_error_count If medium_error_start is set then we don't use the default of OPT_MEDIUM_ERR_ADDR, but use that set value. If medium_error_count is set we use that value otherwise default to OPT_MEDIUM_ERR_NUM. Signed-off-by: Laurence Oberman Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 7d2ce0cc915a..8480e4849e29 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -616,6 +616,8 @@ static unsigned int sdebug_guard = DEF_GUARD; static int sdebug_lowest_aligned = DEF_LOWEST_ALIGNED; static int sdebug_max_luns = DEF_MAX_LUNS; static int sdebug_max_queue = SDEBUG_CANQUEUE; /* per submit queue */ +static unsigned int sdebug_medium_error_start = OPT_MEDIUM_ERR_ADDR; +static int sdebug_medium_error_count = OPT_MEDIUM_ERR_NUM; static atomic_t retired_max_queue; /* if > 0 then was prior max_queue */ static int sdebug_ndelay = DEF_NDELAY; /* if > 0 then unit is nanoseconds */ static int sdebug_no_lun_0 = DEF_NO_LUN_0; @@ -2711,8 +2713,8 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) } if (unlikely((SDEBUG_OPT_MEDIUM_ERR & sdebug_opts) && - (lba <= (OPT_MEDIUM_ERR_ADDR + OPT_MEDIUM_ERR_NUM - 1)) && - ((lba + num) > OPT_MEDIUM_ERR_ADDR))) { + (lba <= (sdebug_medium_error_start + sdebug_medium_error_count - 1)) && + ((lba + num) > sdebug_medium_error_start))) { /* claim unrecoverable read error */ mk_sense_buffer(scp, MEDIUM_ERROR, UNRECOVERED_READ_ERR, 0); /* set info field and valid bit for fixed descriptor */ @@ -4432,6 +4434,8 @@ module_param_named(lbprz, sdebug_lbprz, int, S_IRUGO); module_param_named(lowest_aligned, sdebug_lowest_aligned, int, S_IRUGO); module_param_named(max_luns, sdebug_max_luns, int, S_IRUGO | S_IWUSR); module_param_named(max_queue, sdebug_max_queue, int, S_IRUGO | S_IWUSR); +module_param_named(medium_error_start, sdebug_medium_error_start, int, S_IRUGO | S_IWUSR); +module_param_named(medium_error_count, sdebug_medium_error_count, int, S_IRUGO | S_IWUSR); module_param_named(ndelay, sdebug_ndelay, int, S_IRUGO | S_IWUSR); module_param_named(no_lun_0, sdebug_no_lun_0, int, S_IRUGO | S_IWUSR); module_param_named(no_uld, sdebug_no_uld, int, S_IRUGO); @@ -4489,6 +4493,8 @@ MODULE_PARM_DESC(lbprz, MODULE_PARM_DESC(lowest_aligned, "lowest aligned lba (def=0)"); MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)"); MODULE_PARM_DESC(max_queue, "max number of queued commands (1 to max(def))"); +MODULE_PARM_DESC(medium_error_start, "starting sector number to return MEDIUM error"); +MODULE_PARM_DESC(medium_error_count, "count of sectors to return follow on MEDIUM error"); MODULE_PARM_DESC(ndelay, "response delay in nanoseconds (def=0 -> ignore)"); MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)"); MODULE_PARM_DESC(no_uld, "stop ULD (e.g. sd driver) attaching (def=0))"); -- cgit v1.3-14-g43fede From 779936faf4f1210a2a6c3a2ccc63cd74a61769ed Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Feb 2018 14:12:19 +0100 Subject: scsi: qedi: fix building with LTO When link-time optimizations are enabled, qedi fails to build because of mismatched prototypes: drivers/scsi/qedi/qedi_gbl.h:27:37: error: type of 'qedi_dbg_fops' does not match original declaration [-Werror=lto-type-mismatch] extern const struct file_operations qedi_dbg_fops; ^ drivers/scsi/qedi/qedi_debugfs.c:239:30: note: 'qedi_dbg_fops' was previously declared here const struct file_operations qedi_dbg_fops[] = { ^ drivers/scsi/qedi/qedi_gbl.h:26:32: error: type of 'qedi_debugfs_ops' does not match original declaration [-Werror=lto-type-mismatch] extern struct qedi_debugfs_ops qedi_debugfs_ops; ^ drivers/scsi/qedi/qedi_debugfs.c:102:25: note: 'qedi_debugfs_ops' was previously declared here struct qedi_debugfs_ops qedi_debugfs_ops[] = { This changes the declaration to match the definition, and adapts the users as necessary. Since both array can be constant here, I'm adding the 'const' everywhere for consistency. Signed-off-by: Arnd Bergmann Acked-by: Manish Rangankar Signed-off-by: Martin K. Petersen --- drivers/scsi/qedi/qedi_dbg.h | 2 +- drivers/scsi/qedi/qedi_debugfs.c | 4 ++-- drivers/scsi/qedi/qedi_gbl.h | 4 ++-- drivers/scsi/qedi/qedi_main.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/qedi/qedi_dbg.h b/drivers/scsi/qedi/qedi_dbg.h index c55572badfb0..358f40567849 100644 --- a/drivers/scsi/qedi/qedi_dbg.h +++ b/drivers/scsi/qedi/qedi_dbg.h @@ -134,7 +134,7 @@ struct qedi_debugfs_ops { } void qedi_dbg_host_init(struct qedi_dbg_ctx *qedi, - struct qedi_debugfs_ops *dops, + const struct qedi_debugfs_ops *dops, const struct file_operations *fops); void qedi_dbg_host_exit(struct qedi_dbg_ctx *qedi); void qedi_dbg_init(char *drv_name); diff --git a/drivers/scsi/qedi/qedi_debugfs.c b/drivers/scsi/qedi/qedi_debugfs.c index fd8a1eea3163..fd914ca4149a 100644 --- a/drivers/scsi/qedi/qedi_debugfs.c +++ b/drivers/scsi/qedi/qedi_debugfs.c @@ -19,7 +19,7 @@ static struct dentry *qedi_dbg_root; void qedi_dbg_host_init(struct qedi_dbg_ctx *qedi, - struct qedi_debugfs_ops *dops, + const struct qedi_debugfs_ops *dops, const struct file_operations *fops) { char host_dirname[32]; @@ -99,7 +99,7 @@ static struct qedi_list_of_funcs qedi_dbg_do_not_recover_ops[] = { { NULL, NULL } }; -struct qedi_debugfs_ops qedi_debugfs_ops[] = { +const struct qedi_debugfs_ops qedi_debugfs_ops[] = { { "gbl_ctx", NULL }, { "do_not_recover", qedi_dbg_do_not_recover_ops}, { "io_trace", NULL }, diff --git a/drivers/scsi/qedi/qedi_gbl.h b/drivers/scsi/qedi/qedi_gbl.h index f5b5a31999aa..a2aa06ed1620 100644 --- a/drivers/scsi/qedi/qedi_gbl.h +++ b/drivers/scsi/qedi/qedi_gbl.h @@ -23,8 +23,8 @@ extern uint qedi_io_tracing; extern struct scsi_host_template qedi_host_template; extern struct iscsi_transport qedi_iscsi_transport; extern const struct qed_iscsi_ops *qedi_ops; -extern struct qedi_debugfs_ops qedi_debugfs_ops; -extern const struct file_operations qedi_dbg_fops; +extern const struct qedi_debugfs_ops qedi_debugfs_ops[]; +extern const struct file_operations qedi_dbg_fops[]; extern struct device_attribute *qedi_shost_attrs[]; int qedi_alloc_sq(struct qedi_ctx *qedi, struct qedi_endpoint *ep); diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c index 029e2e69b29f..e992f9d3ef00 100644 --- a/drivers/scsi/qedi/qedi_main.c +++ b/drivers/scsi/qedi/qedi_main.c @@ -2303,8 +2303,8 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) } #ifdef CONFIG_DEBUG_FS - qedi_dbg_host_init(&qedi->dbg_ctx, &qedi_debugfs_ops, - &qedi_dbg_fops); + qedi_dbg_host_init(&qedi->dbg_ctx, qedi_debugfs_ops, + qedi_dbg_fops); #endif QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, "QLogic FastLinQ iSCSI Module qedi %s, FW %d.%d.%d.%d\n", -- cgit v1.3-14-g43fede From 8d6febb0ccac88261fd50d425fceeca215551f11 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 6 Feb 2018 14:03:16 +0000 Subject: scsi: qedf: remove redundant initialization of 'fcport' Pointer fcport is initialized with a value that is never read, it is re-assigned a new value later on, hence the initialization is redundant and can be removed. Cleans up clang warning: drivers/scsi/qedf/qedf_io.c:920:21: warning: Value stored to 'fcport' during its initialization is never read Signed-off-by: Colin Ian King Acked-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index b15e69586a36..50a50c4249d0 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -917,7 +917,7 @@ qedf_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd) struct qedf_ctx *qedf = lport_priv(lport); struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); struct fc_rport_libfc_priv *rp = rport->dd_data; - struct qedf_rport *fcport = rport->dd_data; + struct qedf_rport *fcport; struct qedf_ioreq *io_req; int rc = 0; int rval; -- cgit v1.3-14-g43fede From bc2e1299a828a13a44c3140a3c0a183c87872606 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 6 Feb 2018 14:21:57 +0000 Subject: scsi: libfc: remove redundant initialization of 'disc' Pointer disc is being intializated a value that is never read and then re-assigned the same value later on, hence the initialization is redundant and can be removed. Cleans up clang warning: drivers/scsi/libfc/fc_disc.c:734:18: warning: Value stored to 'disc' during its initialization is never read Signed-off-by: Colin Ian King Acked-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/libfc/fc_disc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index 8660f923ace0..3f3569ec5ce3 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -731,7 +731,7 @@ static void fc_disc_stop_final(struct fc_lport *lport) */ void fc_disc_config(struct fc_lport *lport, void *priv) { - struct fc_disc *disc = &lport->disc; + struct fc_disc *disc; if (!lport->tt.disc_start) lport->tt.disc_start = fc_disc_start; -- cgit v1.3-14-g43fede From d9ea463a1cc7f0a6254cbd8228e1948c345a4acd Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Feb 2018 14:12:18 +0100 Subject: scsi: qedf: fix LTO-enabled build The prototype for qedf_dbg_fops/qedf_debugfs_ops doesn't match the definition, which causes the final link to fail with link-time optimizations: drivers/scsi/qedf/qedf_main.c:34: error: type of 'qedf_dbg_fops' does not match original declaration [-Werror=lto-type-mismatch] extern struct file_operations qedf_dbg_fops; drivers/scsi/qedf/qedf_debugfs.c:443: note: 'qedf_dbg_fops' was previously declared here const struct file_operations qedf_dbg_fops[] = { drivers/scsi/qedf/qedf_main.c:33: error: type of 'qedf_debugfs_ops' does not match original declaration [-Werror=lto-type-mismatch] extern struct qedf_debugfs_ops qedf_debugfs_ops; drivers/scsi/qedf/qedf_debugfs.c:102: note: 'qedf_debugfs_ops' was previously declared here struct qedf_debugfs_ops qedf_debugfs_ops[] = { This corrects the prototype and moves it into a shared header file where it belongs. The file operations can also be marked 'const' like the qedf_debugfs_ops. Signed-off-by: Arnd Bergmann Acked-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_dbg.h | 17 ++++++++++------- drivers/scsi/qedf/qedf_debugfs.c | 6 +++--- drivers/scsi/qedf/qedf_main.c | 8 +++----- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/qedf/qedf_dbg.h b/drivers/scsi/qedf/qedf_dbg.h index 50083cae84c3..77c27e888969 100644 --- a/drivers/scsi/qedf/qedf_dbg.h +++ b/drivers/scsi/qedf/qedf_dbg.h @@ -116,6 +116,14 @@ extern int qedf_create_sysfs_attr(struct Scsi_Host *shost, extern void qedf_remove_sysfs_attr(struct Scsi_Host *shost, struct sysfs_bin_attrs *iter); +struct qedf_debugfs_ops { + char *name; + struct qedf_list_of_funcs *qedf_funcs; +}; + +extern const struct qedf_debugfs_ops qedf_debugfs_ops[]; +extern const struct file_operations qedf_dbg_fops[]; + #ifdef CONFIG_DEBUG_FS /* DebugFS related code */ struct qedf_list_of_funcs { @@ -123,11 +131,6 @@ struct qedf_list_of_funcs { ssize_t (*oper_func)(struct qedf_dbg_ctx *qedf); }; -struct qedf_debugfs_ops { - char *name; - struct qedf_list_of_funcs *qedf_funcs; -}; - #define qedf_dbg_fileops(drv, ops) \ { \ .owner = THIS_MODULE, \ @@ -147,8 +150,8 @@ struct qedf_debugfs_ops { } extern void qedf_dbg_host_init(struct qedf_dbg_ctx *qedf, - struct qedf_debugfs_ops *dops, - struct file_operations *fops); + const struct qedf_debugfs_ops *dops, + const struct file_operations *fops); extern void qedf_dbg_host_exit(struct qedf_dbg_ctx *qedf); extern void qedf_dbg_init(char *drv_name); extern void qedf_dbg_exit(void); diff --git a/drivers/scsi/qedf/qedf_debugfs.c b/drivers/scsi/qedf/qedf_debugfs.c index 2b1ef3075e93..c539a7ae3a7e 100644 --- a/drivers/scsi/qedf/qedf_debugfs.c +++ b/drivers/scsi/qedf/qedf_debugfs.c @@ -23,8 +23,8 @@ static struct dentry *qedf_dbg_root; **/ void qedf_dbg_host_init(struct qedf_dbg_ctx *qedf, - struct qedf_debugfs_ops *dops, - struct file_operations *fops) + const struct qedf_debugfs_ops *dops, + const struct file_operations *fops) { char host_dirname[32]; struct dentry *file_dentry = NULL; @@ -99,7 +99,7 @@ qedf_dbg_exit(void) qedf_dbg_root = NULL; } -struct qedf_debugfs_ops qedf_debugfs_ops[] = { +const struct qedf_debugfs_ops qedf_debugfs_ops[] = { { "fp_int", NULL }, { "io_trace", NULL }, { "debug", NULL }, diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index ccd9a08ea030..284ccb566b19 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -23,6 +23,7 @@ #include #include #include "qedf.h" +#include "qedf_dbg.h" #include const struct qed_fcoe_ops *qed_ops; @@ -30,9 +31,6 @@ const struct qed_fcoe_ops *qed_ops; static int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id); static void qedf_remove(struct pci_dev *pdev); -extern struct qedf_debugfs_ops qedf_debugfs_ops; -extern struct file_operations qedf_dbg_fops; - /* * Driver module parameters. */ @@ -3155,8 +3153,8 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) } #ifdef CONFIG_DEBUG_FS - qedf_dbg_host_init(&(qedf->dbg_ctx), &qedf_debugfs_ops, - &qedf_dbg_fops); + qedf_dbg_host_init(&(qedf->dbg_ctx), qedf_debugfs_ops, + qedf_dbg_fops); #endif /* Start LL2 */ -- cgit v1.3-14-g43fede From a8db6140d7d5396263935a48f0bd7eca3b90f15d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Feb 2018 14:12:20 +0100 Subject: scsi: qedf: use correct strncpy() size gcc-8 warns during link-time optimization that the strncpy() call passes the size of the source buffer rather than the destination: drivers/scsi/qedf/qedf_dbg.c: In function 'qedf_uevent_emit': include/linux/string.h:253: error: 'strncpy' specified bound depends on the length of the source argument [-Werror=stringop-overflow=] This changes it to strscpy() with the correct length, guaranteeing a properly nul-terminated string of the right size. Signed-off-by: Arnd Bergmann Acked-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_dbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qedf/qedf_dbg.c b/drivers/scsi/qedf/qedf_dbg.c index e023f5d0dc12..bd1cef25a900 100644 --- a/drivers/scsi/qedf/qedf_dbg.c +++ b/drivers/scsi/qedf/qedf_dbg.c @@ -160,7 +160,7 @@ qedf_uevent_emit(struct Scsi_Host *shost, u32 code, char *msg) switch (code) { case QEDF_UEVENT_CODE_GRCDUMP: if (msg) - strncpy(event_string, msg, strlen(msg)); + strscpy(event_string, msg, sizeof(event_string)); else sprintf(event_string, "GRCDUMP=%u", shost->host_no); break; -- cgit v1.3-14-g43fede From 44f1ce7d2f4a90e8976ece898777e53337fd1bee Mon Sep 17 00:00:00 2001 From: Raghava Aditya Renukunta Date: Wed, 7 Feb 2018 08:40:56 -0800 Subject: scsi: aacraid: Implement DropIO sync command IOP_RESET takes a long time to complete. If controller is in a state where we can bring it back with init struct, send a DropIO sync command instead. - If controller is faulted perform standard IOP_RESET in aac_srcv_init. - If controller is not faulted get adapter properties and extended properties. - Update the sa_firmware variable and determine if DropIO request is supported. - Issue DropIO request, and get the number of outstanding commands. - If all commands are complete with success (CT_OK), consider IOP_RESET is complete. - If any commands timeout, Perform the IOP_RESET. Signed-off-by: Prasad B Munirathnam Signed-off-by: Raghava Aditya Renukunta Reviewed-by: Dave Carroll Signed-off-by: Martin K. Petersen --- drivers/scsi/aacraid/aacraid.h | 4 + drivers/scsi/aacraid/src.c | 161 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 159 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 0095fcbd1c88..c3fdec9d817a 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -1528,6 +1528,7 @@ struct aac_bus_info_response { #define AAC_COMM_MESSAGE_TYPE3 5 #define AAC_EXTOPT_SA_FIRMWARE cpu_to_le32(1<<1) +#define AAC_EXTOPT_SOFT_RESET cpu_to_le32(1<<16) /* MSIX context */ struct aac_msix_ctx { @@ -1662,6 +1663,7 @@ struct aac_dev u8 raw_io_64; u8 printf_enabled; u8 in_reset; + u8 in_soft_reset; u8 msi; u8 sa_firmware; int management_fib_count; @@ -2504,6 +2506,7 @@ struct aac_hba_info { #define RCV_TEMP_READINGS 0x00000025 #define GET_COMM_PREFERRED_SETTINGS 0x00000026 #define IOP_RESET_FW_FIB_DUMP 0x00000034 +#define DROP_IO 0x00000035 #define IOP_RESET 0x00001000 #define IOP_RESET_ALWAYS 0x00001001 #define RE_INIT_ADAPTER 0x000000ee @@ -2539,6 +2542,7 @@ struct aac_hba_info { #define FLASH_UPD_PENDING 0x00002000 #define FLASH_UPD_SUCCESS 0x00004000 #define FLASH_UPD_FAILED 0x00008000 +#define INVALID_OMR 0xffffffff #define FWUPD_TIMEOUT (5 * 60) /* diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index fde6b6aa86e3..de4884577ad7 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -255,7 +255,8 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command, */ src_writel(dev, MUnit.IDR, INBOUNDDOORBELL_0 << SRC_IDR_SHIFT); - if (!dev->sync_mode || command != SEND_SYNCHRONOUS_FIB) { + if ((!dev->sync_mode || command != SEND_SYNCHRONOUS_FIB) && + !dev->in_soft_reset) { ok = 0; start = jiffies; @@ -992,6 +993,148 @@ error_iounmap: return -1; } +static int aac_src_wait_sync(struct aac_dev *dev, int *status) +{ + unsigned long start = jiffies; + unsigned long usecs = 0; + int delay = 5 * HZ; + int rc = 1; + + while (time_before(jiffies, start+delay)) { + /* + * Delay 5 microseconds to let Mon960 get info. + */ + udelay(5); + + /* + * Mon960 will set doorbell0 bit when it has completed the + * command. + */ + if (aac_src_get_sync_status(dev) & OUTBOUNDDOORBELL_0) { + /* + * Clear: the doorbell. + */ + if (dev->msi_enabled) + aac_src_access_devreg(dev, AAC_CLEAR_SYNC_BIT); + else + src_writel(dev, MUnit.ODR_C, + OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT); + rc = 0; + + break; + } + + /* + * Yield the processor in case we are slow + */ + usecs = 1 * USEC_PER_MSEC; + usleep_range(usecs, usecs + 50); + } + /* + * Pull the synch status from Mailbox 0. + */ + if (status && !rc) { + status[0] = readl(&dev->IndexRegs->Mailbox[0]); + status[1] = readl(&dev->IndexRegs->Mailbox[1]); + status[2] = readl(&dev->IndexRegs->Mailbox[2]); + status[3] = readl(&dev->IndexRegs->Mailbox[3]); + status[4] = readl(&dev->IndexRegs->Mailbox[4]); + } + + return rc; +} + +/** + * aac_src_soft_reset - perform soft reset to speed up + * access + * + * Assumptions: That the controller is in a state where we can + * bring it back to life with an init struct. We can only use + * fast sync commands, as the timeout is 5 seconds. + * + * @dev: device to configure + * + */ + +static int aac_src_soft_reset(struct aac_dev *dev) +{ + u32 status_omr = src_readl(dev, MUnit.OMR); + u32 status[5]; + int rc = 1; + int state = 0; + char *state_str[7] = { + "GET_ADAPTER_PROPERTIES Failed", + "GET_ADAPTER_PROPERTIES timeout", + "SOFT_RESET not supported", + "DROP_IO Failed", + "DROP_IO timeout", + "Check Health failed" + }; + + if (status_omr == INVALID_OMR) + return 1; // pcie hosed + + if (!(status_omr & KERNEL_UP_AND_RUNNING)) + return 1; // not up and running + + /* + * We go into soft reset mode to allow us to handle response + */ + dev->in_soft_reset = 1; + dev->msi_enabled = status_omr & AAC_INT_MODE_MSIX; + + /* Get adapter properties */ + rc = aac_adapter_sync_cmd(dev, GET_ADAPTER_PROPERTIES, 0, 0, 0, + 0, 0, 0, status+0, status+1, status+2, status+3, status+4); + if (rc) + goto out; + + state++; + if (aac_src_wait_sync(dev, status)) { + rc = 1; + goto out; + } + + state++; + if (!(status[1] & le32_to_cpu(AAC_OPT_EXTENDED) && + (status[4] & le32_to_cpu(AAC_EXTOPT_SOFT_RESET)))) { + rc = 2; + goto out; + } + + if ((status[1] & le32_to_cpu(AAC_OPT_EXTENDED)) && + (status[4] & le32_to_cpu(AAC_EXTOPT_SA_FIRMWARE))) + dev->sa_firmware = 1; + + state++; + rc = aac_adapter_sync_cmd(dev, DROP_IO, 0, 0, 0, 0, 0, 0, + status+0, status+1, status+2, status+3, status+4); + + if (rc) + goto out; + + state++; + if (aac_src_wait_sync(dev, status)) { + rc = 3; + goto out; + } + + if (status[1]) + dev_err(&dev->pdev->dev, "%s: %d outstanding I/O pending\n", + __func__, status[1]); + + state++; + rc = aac_src_check_health(dev); + +out: + dev->in_soft_reset = 0; + dev->msi_enabled = 0; + if (rc) + dev_err(&dev->pdev->dev, "%s: %s status = %d", __func__, + state_str[state], rc); + +return rc; +} /** * aac_srcv_init - initialize an SRCv card * @dev: device to configure @@ -1021,8 +1164,10 @@ int aac_srcv_init(struct aac_dev *dev) if (dev->init_reset) { dev->init_reset = false; - if (!aac_src_restart_adapter(dev, 0, IOP_HWSOFT_RESET)) + if (aac_src_soft_reset(dev)) { + aac_src_restart_adapter(dev, 0, IOP_HWSOFT_RESET); ++restart; + } } /* @@ -1072,13 +1217,16 @@ int aac_srcv_init(struct aac_dev *dev) printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance); goto error_iounmap; } + start = jiffies; /* * Wait for the adapter to be up and running. Wait up to 3 minutes */ - while (!((status = src_readl(dev, MUnit.OMR)) & - KERNEL_UP_AND_RUNNING) || - status == 0xffffffff) { + do { + status = src_readl(dev, MUnit.OMR); + if (status == INVALID_OMR) + status = 0; + if ((restart && (status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC))) || time_after(jiffies, start+HZ*startup_timeout)) { @@ -1098,7 +1246,8 @@ int aac_srcv_init(struct aac_dev *dev) ++restart; } msleep(1); - } + } while (!(status & KERNEL_UP_AND_RUNNING)); + if (restart && aac_commit) aac_commit = 1; /* -- cgit v1.3-14-g43fede From a5799d74d965c8a04812ae6a6b26cafb2c099981 Mon Sep 17 00:00:00 2001 From: Raghava Aditya Renukunta Date: Wed, 7 Feb 2018 08:40:57 -0800 Subject: scsi: aacraid: Preserve MSIX mode in the OMR register Preserve the current MSIX mode value in the OMR before rewriting the OMR to initiate the IOP or Soft Reset. Signed-off-by: Prasad B Munirathnam Signed-off-by: Raghava Aditya Renukunta Reviewed-by: Dave Carroll Signed-off-by: Martin K. Petersen --- drivers/scsi/aacraid/src.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index de4884577ad7..09b82d3325fc 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -680,6 +680,25 @@ void aac_set_intx_mode(struct aac_dev *dev) } } +static void aac_clear_omr(struct aac_dev *dev) +{ + u32 omr_value = 0; + + omr_value = src_readl(dev, MUnit.OMR); + + /* + * Check for PCI Errors or Kernel Panic + */ + if ((omr_value == INVALID_OMR) || (omr_value & KERNEL_PANIC)) + omr_value = 0; + + /* + * Preserve MSIX Value if any + */ + src_writel(dev, MUnit.OMR, omr_value & AAC_INT_MODE_MSIX); + src_readl(dev, MUnit.OMR); +} + static void aac_dump_fw_fib_iop_reset(struct aac_dev *dev) { __le32 supported_options3; @@ -740,6 +759,8 @@ static void aac_send_iop_reset(struct aac_dev *dev) aac_set_intx_mode(dev); + aac_clear_omr(dev); + src_writel(dev, MUnit.IDR, IOP_SRC_RESET_MASK); msleep(5000); @@ -749,6 +770,7 @@ static void aac_send_hardware_soft_reset(struct aac_dev *dev) { u_int32_t val; + aac_clear_omr(dev); val = readl(((char *)(dev->base) + IBW_SWR_OFFSET)); val |= 0x01; writel(val, ((char *)(dev->base) + IBW_SWR_OFFSET)); -- cgit v1.3-14-g43fede From eee549e1e3a4ec87353a5b85a51a7896394981d5 Mon Sep 17 00:00:00 2001 From: Raghava Aditya Renukunta Date: Wed, 7 Feb 2018 08:40:58 -0800 Subject: scsi: aacraid: Auto detect INTx or MSIx mode during sync cmd processing During sync command processing, if legacy INTx status indicates command is not completed, sample the MSIx register and check if it indicates command completion, set controller MSIx enabled flag. Signed-off-by: Prasad B Munirathnam Signed-off-by: Raghava Aditya Renukunta Reviewed-by: Dave Carroll Signed-off-by: Martin K. Petersen --- drivers/scsi/aacraid/aacraid.h | 1 + drivers/scsi/aacraid/src.c | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index c3fdec9d817a..29bf1e60f542 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -1231,6 +1231,7 @@ struct src_registers { #define SRC_ODR_SHIFT 12 #define SRC_IDR_SHIFT 9 +#define SRC_MSI_READ_MASK 0x1000 typedef void (*fib_callback)(void *ctxt, struct fib *fibctx); diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index 09b82d3325fc..3122389f380f 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -1405,13 +1405,23 @@ void aac_src_access_devreg(struct aac_dev *dev, int mode) static int aac_src_get_sync_status(struct aac_dev *dev) { + int msix_val = 0; + int legacy_val = 0; - int val; + msix_val = src_readl(dev, MUnit.ODR_MSI) & SRC_MSI_READ_MASK ? 1 : 0; - if (dev->msi_enabled) - val = src_readl(dev, MUnit.ODR_MSI) & 0x1000 ? 1 : 0; - else - val = src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT; + if (!dev->msi_enabled) { + /* + * if Legacy int status indicates cmd is not complete + * sample MSIx register to see if it indiactes cmd complete, + * if yes set the controller in MSIx mode and consider cmd + * completed + */ + legacy_val = src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT; + if (!(legacy_val & 1) && msix_val) + dev->msi_enabled = 1; + return legacy_val; + } - return val; + return msix_val; } -- cgit v1.3-14-g43fede From 80e6e9c1dabb940dc7900f37c05f07280af90ac8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Feb 2018 14:12:16 +0100 Subject: scsi: NCR53c406a: avoid section mismatch with LTO Building with link time optimizations produces a false-positive section mismatch warning: WARNING: vmlinux.o(.data+0xf7e8): Section mismatch in reference from the variable driver_template.lto_priv.6914 to the function .init.text:NCR53c406a_detect() The variable driver_template.lto_priv.6914 references the function __init NCR53c406a_detect() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console The ->detect callback is always entered from the init_this_scsi_driver() init function, but apparently LTO turns the optimized direct function call into an indirect call through a non-__initdata pointer. All drivers using init_this_scsi_driver() are for ancient hardware, and most don't mark the detect() callback as __init(), so I'm just removing the annotation here to kill off the warning instead of doing a larger rework. [mkp: typo] Signed-off-by: Arnd Bergmann Signed-off-by: Martin K. Petersen --- drivers/scsi/NCR53c406a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c index 6e110c630d2c..44b09870bf51 100644 --- a/drivers/scsi/NCR53c406a.c +++ b/drivers/scsi/NCR53c406a.c @@ -448,7 +448,7 @@ static __inline__ int NCR53c406a_pio_write(unsigned char *request, unsigned int } #endif /* USE_PIO */ -static int __init NCR53c406a_detect(struct scsi_host_template * tpnt) +static int NCR53c406a_detect(struct scsi_host_template * tpnt) { int present = 0; struct Scsi_Host *shpnt = NULL; -- cgit v1.3-14-g43fede From 230816d48ed0213f25a1835f5e0e1bbef96bb896 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Feb 2018 14:12:17 +0100 Subject: scsi: sym53c416: avoid section mismatch with LTO Building with link time optimizations produces a false-positive section mismatch warning: WARNING: vmlinux.o(.data+0xf8c8): Section mismatch in reference from the variable driver_template.lto_priv.6915 to the function .init.text:sym53c416_detect() The variable driver_template.lto_priv.6915 references the function __init sym53c416_detect() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console The ->detect callback is always entered from the init_this_scsi_driver() init function, but apparently LTO turns the optimized direct function call into an indirect call through a non-__initdata pointer. All drivers using init_this_scsi_driver() are for ancient hardware, and most don't mark the detect() callback as __init(), so I'm just removing the annotation here to kill off the warning instead of doing a larger rework. [mkp: typo] Signed-off-by: Arnd Bergmann Signed-off-by: Martin K. Petersen --- drivers/scsi/sym53c416.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c index 5bdcbe8fa958..e68bcdc75bc3 100644 --- a/drivers/scsi/sym53c416.c +++ b/drivers/scsi/sym53c416.c @@ -608,7 +608,7 @@ static void sym53c416_probe(void) } } -int __init sym53c416_detect(struct scsi_host_template *tpnt) +int sym53c416_detect(struct scsi_host_template *tpnt) { unsigned long flags; struct Scsi_Host * shpnt = NULL; -- cgit v1.3-14-g43fede From 2976fbb6a7aff04076d13d2a37e4b8dbe0d6fd4c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 6 Feb 2018 14:12:36 +0000 Subject: scsi: isci: remove redundant initialization to 'bit' Variable bit is initialized with a value that is never read and is being updated immediately after the initialization, hence the initialization is redundant and can be removed. Cleans up clang warning: drivers/scsi/isci/host.c:2769:8: warning: Value stored to 'bit' during its initialization is never read Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/isci/host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 13b37cdffa8e..1ee3868ade07 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -2766,7 +2766,7 @@ static int sci_write_gpio_tx_gp(struct isci_host *ihost, u8 reg_index, u8 reg_co int i; for (i = 0; i < 3; i++) { - int bit = (i << 2) + 2; + int bit; bit = try_test_sas_gpio_gp_bit(to_sas_gpio_od(d, i), write_data, reg_index, -- cgit v1.3-14-g43fede From b22ee87d84296233886139654e8a125d8f79b44e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 6 Feb 2018 14:34:10 +0000 Subject: scsi: pmcraid: remove redundant initializations of pointer 'ioadl' There are several occurrances where pointer ioadl is initialized with a value that is never read and where it is re-assigned a new value later on, hence the initialization is redundant and can be removed. Cleans up clang warnings: drivers/scsi/pmcraid.c:1028:29: warning: Value stored to 'ioadl' during its initialization is never read drivers/scsi/pmcraid.c:3178:29: warning: Value stored to 'ioadl' during its initialization is never read drivers/scsi/pmcraid.c:5495:29: warning: Value stored to 'ioadl' during its initialization is never read drivers/scsi/pmcraid.c:5668:29: warning: Value stored to 'ioadl' during its initialization is never read Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/pmcraid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 201c8de1853d..9330cc5ba34d 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -1025,7 +1025,7 @@ static void pmcraid_get_fwversion_done(struct pmcraid_cmd *cmd) static void pmcraid_get_fwversion(struct pmcraid_cmd *cmd) { struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; - struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl; + struct pmcraid_ioadl_desc *ioadl; struct pmcraid_instance *pinstance = cmd->drv_inst; u16 data_size = sizeof(struct pmcraid_inquiry_data); @@ -3175,7 +3175,7 @@ static int pmcraid_build_ioadl( struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb); - struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl; + struct pmcraid_ioadl_desc *ioadl; u32 length = scsi_bufflen(scsi_cmd); @@ -5492,7 +5492,7 @@ static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd) struct pmcraid_instance *pinstance = cmd->drv_inst; struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; __be32 time_stamp_len = cpu_to_be32(PMCRAID_TIMESTAMP_LEN); - struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl; + struct pmcraid_ioadl_desc *ioadl; u64 timestamp; timestamp = ktime_get_real_seconds() * 1000; @@ -5665,7 +5665,7 @@ static void pmcraid_init_res_table(struct pmcraid_cmd *cmd) static void pmcraid_querycfg(struct pmcraid_cmd *cmd) { struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; - struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl; + struct pmcraid_ioadl_desc *ioadl; struct pmcraid_instance *pinstance = cmd->drv_inst; __be32 cfg_table_size = cpu_to_be32(sizeof(struct pmcraid_config_table)); -- cgit v1.3-14-g43fede From f95dc1bb32d5eb9ff484f7f5a44f856d6fc39f20 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 12 Feb 2018 08:58:17 -0800 Subject: scsi: ipr: Use sgl_alloc_order() and sgl_free_order() Use the sgl_alloc_order() and sgl_free_order() functions instead of open coding these functions. Signed-off-by: Bart Van Assche Acked-by: Brian King Reviewed-by: Johannes Thumshirn Reviewed-by: Hannes Reinecke Cc: Martin K. Petersen Cc: linux-scsi@vger.kernel.org Signed-off-by: Martin K. Petersen --- drivers/scsi/Kconfig | 1 + drivers/scsi/ipr.c | 49 ++++++++----------------------------------------- drivers/scsi/ipr.h | 2 +- 3 files changed, 10 insertions(+), 42 deletions(-) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 8a739b74cfb7..7e3f6e37fb66 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1059,6 +1059,7 @@ config SCSI_IPR depends on PCI && SCSI && ATA select FW_LOADER select IRQ_POLL + select SGL_ALLOC ---help--- This driver supports the IBM Power Linux family RAID adapters. This includes IBM pSeries 5712, 5703, 5709, and 570A, as well diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e07dd990e585..52735162444f 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3816,10 +3816,8 @@ static struct device_attribute ipr_iopoll_weight_attr = { **/ static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len) { - int sg_size, order, bsize_elem, num_elem, i, j; + int sg_size, order; struct ipr_sglist *sglist; - struct scatterlist *scatterlist; - struct page *page; /* Get the minimum size per scatter/gather element */ sg_size = buf_len / (IPR_MAX_SGLIST - 1); @@ -3827,45 +3825,18 @@ static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len) /* Get the actual size per element */ order = get_order(sg_size); - /* Determine the actual number of bytes per element */ - bsize_elem = PAGE_SIZE * (1 << order); - - /* Determine the actual number of sg entries needed */ - if (buf_len % bsize_elem) - num_elem = (buf_len / bsize_elem) + 1; - else - num_elem = buf_len / bsize_elem; - /* Allocate a scatter/gather list for the DMA */ - sglist = kzalloc(sizeof(struct ipr_sglist) + - (sizeof(struct scatterlist) * (num_elem - 1)), - GFP_KERNEL); - + sglist = kzalloc(sizeof(struct ipr_sglist), GFP_KERNEL); if (sglist == NULL) { ipr_trace; return NULL; } - - scatterlist = sglist->scatterlist; - sg_init_table(scatterlist, num_elem); - sglist->order = order; - sglist->num_sg = num_elem; - - /* Allocate a bunch of sg elements */ - for (i = 0; i < num_elem; i++) { - page = alloc_pages(GFP_KERNEL, order); - if (!page) { - ipr_trace; - - /* Free up what we already allocated */ - for (j = i - 1; j >= 0; j--) - __free_pages(sg_page(&scatterlist[j]), order); - kfree(sglist); - return NULL; - } - - sg_set_page(&scatterlist[i], page, 0, 0); + sglist->scatterlist = sgl_alloc_order(buf_len, order, false, GFP_KERNEL, + &sglist->num_sg); + if (!sglist->scatterlist) { + kfree(sglist); + return NULL; } return sglist; @@ -3883,11 +3854,7 @@ static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len) **/ static void ipr_free_ucode_buffer(struct ipr_sglist *sglist) { - int i; - - for (i = 0; i < sglist->num_sg; i++) - __free_pages(sg_page(&sglist->scatterlist[i]), sglist->order); - + sgl_free_order(sglist->scatterlist, sglist->order); kfree(sglist); } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index c7f0e9e3cd7d..93570734cbfb 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1454,7 +1454,7 @@ struct ipr_sglist { u32 num_sg; u32 num_dma_sg; u32 buffer_len; - struct scatterlist scatterlist[1]; + struct scatterlist *scatterlist; }; enum ipr_sdt_state { -- cgit v1.3-14-g43fede From f5594686f5f06637bea144159dfe147dae3040d2 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 12 Feb 2018 08:58:18 -0800 Subject: scsi: pmcraid: Remove an unused structure member Signed-off-by: Bart Van Assche Reviewed-by: Johannes Thumshirn Reviewed-by: Hannes Reinecke Cc: Anil Ravindranath Signed-off-by: Martin K. Petersen --- drivers/scsi/pmcraid.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h index 8bfac72a242b..44da91712115 100644 --- a/drivers/scsi/pmcraid.h +++ b/drivers/scsi/pmcraid.h @@ -542,7 +542,6 @@ struct pmcraid_sglist { u32 order; u32 num_sg; u32 num_dma_sg; - u32 buffer_len; struct scatterlist scatterlist[1]; }; -- cgit v1.3-14-g43fede From ed4414cef2ad0ad0afb3d77a6fdde425d7f7ec4d Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 12 Feb 2018 08:58:19 -0800 Subject: scsi: pmcraid: Use sgl_alloc_order() and sgl_free_order() Use the sgl_alloc_order() and sgl_free_order() functions instead of open coding these functions. Signed-off-by: Bart Van Assche Reviewed-by: Johannes Thumshirn Reviewed-by: Hannes Reinecke Cc: Anil Ravindranath Signed-off-by: Martin K. Petersen --- drivers/scsi/Kconfig | 1 + drivers/scsi/pmcraid.c | 43 ++++--------------------------------------- drivers/scsi/pmcraid.h | 2 +- 3 files changed, 6 insertions(+), 40 deletions(-) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 7e3f6e37fb66..8647ca9199b3 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1577,6 +1577,7 @@ config ZFCP config SCSI_PMCRAID tristate "PMC SIERRA Linux MaxRAID adapter support" depends on PCI && SCSI && NET + select SGL_ALLOC ---help--- This driver supports the PMC SIERRA MaxRAID adapters. diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 9330cc5ba34d..95530393872d 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -3225,12 +3225,7 @@ static int pmcraid_build_ioadl( */ static void pmcraid_free_sglist(struct pmcraid_sglist *sglist) { - int i; - - for (i = 0; i < sglist->num_sg; i++) - __free_pages(sg_page(&(sglist->scatterlist[i])), - sglist->order); - + sgl_free_order(sglist->scatterlist, sglist->order); kfree(sglist); } @@ -3247,50 +3242,20 @@ static void pmcraid_free_sglist(struct pmcraid_sglist *sglist) static struct pmcraid_sglist *pmcraid_alloc_sglist(int buflen) { struct pmcraid_sglist *sglist; - struct scatterlist *scatterlist; - struct page *page; - int num_elem, i, j; int sg_size; int order; - int bsize_elem; sg_size = buflen / (PMCRAID_MAX_IOADLS - 1); order = (sg_size > 0) ? get_order(sg_size) : 0; - bsize_elem = PAGE_SIZE * (1 << order); - - /* Determine the actual number of sg entries needed */ - if (buflen % bsize_elem) - num_elem = (buflen / bsize_elem) + 1; - else - num_elem = buflen / bsize_elem; /* Allocate a scatter/gather list for the DMA */ - sglist = kzalloc(sizeof(struct pmcraid_sglist) + - (sizeof(struct scatterlist) * (num_elem - 1)), - GFP_KERNEL); - + sglist = kzalloc(sizeof(struct pmcraid_sglist), GFP_KERNEL); if (sglist == NULL) return NULL; - scatterlist = sglist->scatterlist; - sg_init_table(scatterlist, num_elem); sglist->order = order; - sglist->num_sg = num_elem; - sg_size = buflen; - - for (i = 0; i < num_elem; i++) { - page = alloc_pages(GFP_KERNEL|GFP_DMA|__GFP_ZERO, order); - if (!page) { - for (j = i - 1; j >= 0; j--) - __free_pages(sg_page(&scatterlist[j]), order); - kfree(sglist); - return NULL; - } - - sg_set_page(&scatterlist[i], page, - sg_size < bsize_elem ? sg_size : bsize_elem, 0); - sg_size -= bsize_elem; - } + sgl_alloc_order(buflen, order, false, + GFP_KERNEL | GFP_DMA | __GFP_ZERO, &sglist->num_sg); return sglist; } diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h index 44da91712115..754ef30927e2 100644 --- a/drivers/scsi/pmcraid.h +++ b/drivers/scsi/pmcraid.h @@ -542,7 +542,7 @@ struct pmcraid_sglist { u32 order; u32 num_sg; u32 num_dma_sg; - struct scatterlist scatterlist[1]; + struct scatterlist *scatterlist; }; /* page D0 inquiry data of focal point resource */ -- cgit v1.3-14-g43fede From 923f46f9e9b0a2352c93500cda989996ff875cbd Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 12 Feb 2018 10:38:05 -0800 Subject: scsi: core: scmd_eh_abort_handler(): Add a comment After the patch that introduced this function was posted on the linux-scsi mailing list an explanation was posted why this patch is correct. Since that explanation contains important information, add a summary of it above the code that explanation applies to. See also http://www.spinics.net/lists/linux-scsi/msg106326.html. References: e494f6a72839 ("[SCSI] improved eh timeout handler") Signed-off-by: Bart Van Assche Reviewed-by: Hannes Reinecke Cc: Christoph Hellwig Cc: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_error.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index d042915ce895..96f988a7efda 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -117,6 +117,12 @@ static int scsi_host_eh_past_deadline(struct Scsi_Host *shost) /** * scmd_eh_abort_handler - Handle command aborts * @work: command to be aborted. + * + * Note: this function must be called only for a command that has timed out. + * Because the block layer marks a request as complete before it calls + * scsi_times_out(), a .scsi_done() call from the LLD for a command that has + * timed out do not have any effect. Hence it is safe to call + * scsi_finish_command() from this function. */ void scmd_eh_abort_handler(struct work_struct *work) -- cgit v1.3-14-g43fede From 9866306795c207b52d49ac5f8dc7cdec9363b67f Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 12 Feb 2018 10:41:29 -0800 Subject: scsi: core: Move the eh_deadline module parameter definition The eh_deadline definition occurs in the middle of the code for releasing a host. Avoid splitting the host release code by moving the definition of the eh_deadline parameter to the top of the hosts.c source file. Signed-off-by: Bart Van Assche Reviewed-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/hosts.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 57bf43e34863..a0a7e4ff255c 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -42,6 +42,12 @@ #include "scsi_logging.h" +static int shost_eh_deadline = -1; + +module_param_named(eh_deadline, shost_eh_deadline, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(eh_deadline, + "SCSI EH timeout in seconds (should be between 0 and 2^31-1)"); + static DEFINE_IDA(host_index_ida); @@ -358,12 +364,6 @@ static void scsi_host_dev_release(struct device *dev) kfree(shost); } -static int shost_eh_deadline = -1; - -module_param_named(eh_deadline, shost_eh_deadline, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(eh_deadline, - "SCSI EH timeout in seconds (should be between 0 and 2^31-1)"); - static struct device_type scsi_host_type = { .name = "scsi_host", .release = scsi_host_dev_release, -- cgit v1.3-14-g43fede From 9b91fd34b46ded81232de3ce41b9b662ec74b07f Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 12 Feb 2018 10:57:52 -0800 Subject: scsi: core: Reduce number of scsi_test_unit_ready() retries Make scsi_test_unit_ready() send at most as many TURs as specified in the 'retries' argument instead of retries * (retries + 1) / 2. Signed-off-by: Bart Van Assche Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index a86df9ca7d1c..aea5a1ae318b 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -2607,7 +2607,7 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, /* try to eat the UNIT_ATTENTION if there are enough retries */ do { result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr, - timeout, retries, NULL); + timeout, 1, NULL); if (sdev->removable && scsi_sense_valid(sshdr) && sshdr->sense_key == UNIT_ATTENTION) sdev->changed = 1; -- cgit v1.3-14-g43fede From 91d4c7520d0c514f404b9098b467184421cb9cc5 Mon Sep 17 00:00:00 2001 From: John Pittman Date: Fri, 9 Feb 2018 21:12:43 -0500 Subject: scsi: scsi_debug: Fix pointer styling issues Pointer styling issues exposed by checkpatch.pl in scsi_debug.c: ERROR: "foo * bar" should be "foo *bar" Fixed 37 total errors reported. [mkp: fixed typo noticed by Doug] Signed-off-by: John Pittman Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 72 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 8480e4849e29..108b1159d55d 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1156,8 +1156,8 @@ static int inquiry_vpd_84(unsigned char *arr) static int inquiry_vpd_85(unsigned char *arr) { int num = 0; - const char * na1 = "https://www.kernel.org/config"; - const char * na2 = "http://www.kernel.org/log"; + const char *na1 = "https://www.kernel.org/config"; + const char *na2 = "http://www.kernel.org/log"; int plen, olen; arr[num++] = 0x1; /* lu, storage config */ @@ -1373,7 +1373,7 @@ static int inquiry_vpd_b2(unsigned char *arr) static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) { unsigned char pq_pdt; - unsigned char * arr; + unsigned char *arr; unsigned char *cmd = scp->cmnd; int alloc_len, n, ret; bool have_wlun, is_disk; @@ -1524,10 +1524,10 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x0}; -static int resp_requests(struct scsi_cmnd * scp, - struct sdebug_dev_info * devip) +static int resp_requests(struct scsi_cmnd *scp, + struct sdebug_dev_info *devip) { - unsigned char * sbuff; + unsigned char *sbuff; unsigned char *cmd = scp->cmnd; unsigned char arr[SCSI_SENSE_BUFFERSIZE]; bool dsense; @@ -1585,8 +1585,8 @@ static int resp_requests(struct scsi_cmnd * scp, return fill_from_dev_buffer(scp, arr, len); } -static int resp_start_stop(struct scsi_cmnd * scp, - struct sdebug_dev_info * devip) +static int resp_start_stop(struct scsi_cmnd *scp, + struct sdebug_dev_info *devip) { unsigned char *cmd = scp->cmnd; int power_cond, stop; @@ -1613,8 +1613,8 @@ static sector_t get_sdebug_capacity(void) } #define SDEBUG_READCAP_ARR_SZ 8 -static int resp_readcap(struct scsi_cmnd * scp, - struct sdebug_dev_info * devip) +static int resp_readcap(struct scsi_cmnd *scp, + struct sdebug_dev_info *devip) { unsigned char arr[SDEBUG_READCAP_ARR_SZ]; unsigned int capac; @@ -1632,8 +1632,8 @@ static int resp_readcap(struct scsi_cmnd * scp, } #define SDEBUG_READCAP16_ARR_SZ 32 -static int resp_readcap16(struct scsi_cmnd * scp, - struct sdebug_dev_info * devip) +static int resp_readcap16(struct scsi_cmnd *scp, + struct sdebug_dev_info *devip) { unsigned char *cmd = scp->cmnd; unsigned char arr[SDEBUG_READCAP16_ARR_SZ]; @@ -1671,11 +1671,11 @@ static int resp_readcap16(struct scsi_cmnd * scp, #define SDEBUG_MAX_TGTPGS_ARR_SZ 1412 -static int resp_report_tgtpgs(struct scsi_cmnd * scp, - struct sdebug_dev_info * devip) +static int resp_report_tgtpgs(struct scsi_cmnd *scp, + struct sdebug_dev_info *devip) { unsigned char *cmd = scp->cmnd; - unsigned char * arr; + unsigned char *arr; int host_no = devip->sdbg_host->shost->host_no; int n, ret, alen, rlen; int port_group_a, port_group_b, port_a, port_b; @@ -1927,7 +1927,7 @@ static int resp_rsup_tmfs(struct scsi_cmnd *scp, /* <> */ -static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target) +static int resp_err_recov_pg(unsigned char *p, int pcontrol, int target) { /* Read-Write Error Recovery page for mode_sense */ unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0, 5, 0, 0xff, 0xff}; @@ -1938,7 +1938,7 @@ static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target) return sizeof(err_recov_pg); } -static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target) +static int resp_disconnect_pg(unsigned char *p, int pcontrol, int target) { /* Disconnect-Reconnect page for mode_sense */ unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -1949,7 +1949,7 @@ static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target) return sizeof(disconnect_pg); } -static int resp_format_pg(unsigned char * p, int pcontrol, int target) +static int resp_format_pg(unsigned char *p, int pcontrol, int target) { /* Format device page for mode_sense */ unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1969,7 +1969,7 @@ static unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0}; -static int resp_caching_pg(unsigned char * p, int pcontrol, int target) +static int resp_caching_pg(unsigned char *p, int pcontrol, int target) { /* Caching page for mode_sense */ unsigned char ch_caching_pg[] = {/* 0x8, 18, */ 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -1989,7 +1989,7 @@ static int resp_caching_pg(unsigned char * p, int pcontrol, int target) static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0, 0, 0, 0x2, 0x4b}; -static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target) +static int resp_ctrl_m_pg(unsigned char *p, int pcontrol, int target) { /* Control mode page for mode_sense */ unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -2013,7 +2013,7 @@ static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target) } -static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target) +static int resp_iec_m_pg(unsigned char *p, int pcontrol, int target) { /* Informational Exceptions control mode page for mode_sense */ unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0, 0, 0, 0x0, 0x0}; @@ -2028,7 +2028,7 @@ static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target) return sizeof(iec_m_pg); } -static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target) +static int resp_sas_sf_m_pg(unsigned char *p, int pcontrol, int target) { /* SAS SSP mode page - short format for mode_sense */ unsigned char sas_sf_m_pg[] = {0x19, 0x6, 0x6, 0x0, 0x7, 0xd0, 0x0, 0x0}; @@ -2040,7 +2040,7 @@ static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target) } -static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target, +static int resp_sas_pcd_m_spg(unsigned char *p, int pcontrol, int target, int target_dev_id) { /* SAS phy control and discover mode page for mode_sense */ unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2, @@ -2073,7 +2073,7 @@ static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target, return sizeof(sas_pcd_m_pg); } -static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol) +static int resp_sas_sha_m_spg(unsigned char *p, int pcontrol) { /* SAS SSP shared protocol specific port mode subpage */ unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2094,7 +2094,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp, unsigned char dev_spec; int alloc_len, offset, len, target_dev_id; int target = scp->device->id; - unsigned char * ap; + unsigned char *ap; unsigned char arr[SDEBUG_MAX_MSENSE_SZ]; unsigned char *cmd = scp->cmnd; bool dbd, llbaa, msense_6, is_disk, bad_pcode; @@ -2325,7 +2325,7 @@ set_mode_changed_ua: return 0; } -static int resp_temp_l_pg(unsigned char * arr) +static int resp_temp_l_pg(unsigned char *arr) { unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38, 0x0, 0x1, 0x3, 0x2, 0x0, 65, @@ -2335,7 +2335,7 @@ static int resp_temp_l_pg(unsigned char * arr) return sizeof(temp_l_pg); } -static int resp_ie_l_pg(unsigned char * arr) +static int resp_ie_l_pg(unsigned char *arr) { unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38, }; @@ -4060,7 +4060,7 @@ static int scsi_debug_abort(struct scsi_cmnd *SCpnt) return SUCCESS; } -static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt) +static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt) { ++num_dev_resets; if (SCpnt && SCpnt->device) { @@ -4112,7 +4112,7 @@ lie: return SUCCESS; } -static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt) +static int scsi_debug_bus_reset(struct scsi_cmnd *SCpnt) { struct sdebug_host_info *sdbg_host; struct sdebug_dev_info *devip; @@ -4145,9 +4145,9 @@ lie: return SUCCESS; } -static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt) +static int scsi_debug_host_reset(struct scsi_cmnd *SCpnt) { - struct sdebug_host_info * sdbg_host; + struct sdebug_host_info *sdbg_host; struct sdebug_dev_info *devip; int k = 0; @@ -4173,7 +4173,7 @@ static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt) static void __init sdebug_build_parts(unsigned char *ramp, unsigned long store_size) { - struct partition * pp; + struct partition *pp; int starts[SDEBUG_MAX_PARTS + 2]; int sectors_per_part, num_sectors, k; int heads_by_sects, start_sec, end_sec; @@ -4524,7 +4524,7 @@ MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xff #define SDEBUG_INFO_LEN 256 static char sdebug_info[SDEBUG_INFO_LEN]; -static const char * scsi_debug_info(struct Scsi_Host * shp) +static const char *scsi_debug_info(struct Scsi_Host *shp) { int k; @@ -5447,7 +5447,7 @@ static void __exit scsi_debug_exit(void) device_initcall(scsi_debug_init); module_exit(scsi_debug_exit); -static void sdebug_release_adapter(struct device * dev) +static void sdebug_release_adapter(struct device *dev) { struct sdebug_host_info *sdbg_host; @@ -5749,7 +5749,7 @@ static struct scsi_host_template sdebug_driver_template = { .track_queue_depth = 1, }; -static int sdebug_driver_probe(struct device * dev) +static int sdebug_driver_probe(struct device *dev) { int error = 0; struct sdebug_host_info *sdbg_host; @@ -5846,7 +5846,7 @@ static int sdebug_driver_probe(struct device * dev) return error; } -static int sdebug_driver_remove(struct device * dev) +static int sdebug_driver_remove(struct device *dev) { struct sdebug_host_info *sdbg_host; struct sdebug_dev_info *sdbg_devinfo, *tmp; -- cgit v1.3-14-g43fede From 80c49563e2506731372c04eec6f8c853d8b0151f Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Fri, 9 Feb 2018 21:36:39 -0500 Subject: scsi: scsi_debug: implement IMMED bit The Start Stop Unit (SSU) command takes in the order of a second to complete on some SAS SSDs and longer on hard disks. Synchronize Cache (SC) can also take some time. Both commands have an IMMED bit in their cdbs for those apps that don't want to wait. This patch introduces a long delay for those commands when the IMMED bit is clear. Since SC is a media access command then when the fake_rw option is active, its cdb processing is skipped and it returns immediately. The SSU command is not altered by the setting of the fake_rw option. These actions are not changed by this patch. Changes since v1: - clear the cdb mask of SYNCHRONIZE CACHE(16) cdb in byte 1, bit 0 Changes: - add the SYNCHRONIZE CACHE(16) command - together with the existing START STOP UNIT and SYNCHRONIZE CACHE(10) commands process the IMMED bit in their cdbs - if the IMMED bit is set, return immediately - if the IMMED bit is clear, treat the delay parameter as having a unit of one second - in the SYNCHRONIZE CACHE processing do a bounds check Signed-off-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 76 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 108b1159d55d..52ccf4f3da70 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -6,7 +6,7 @@ * anything out of the ordinary is seen. * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * - * Copyright (C) 2001 - 2017 Douglas Gilbert + * Copyright (C) 2001 - 2018 Douglas Gilbert * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -61,8 +61,8 @@ #include "scsi_logging.h" /* make sure inq_product_rev string corresponds to this version */ -#define SDEBUG_VERSION "0187" /* format to fit INQUIRY revision field */ -static const char *sdebug_version_date = "20171202"; +#define SDEBUG_VERSION "0188" /* format to fit INQUIRY revision field */ +static const char *sdebug_version_date = "20180128"; #define MY_NAME "scsi_debug" @@ -234,6 +234,7 @@ static const char *sdebug_version_date = "20171202"; #define F_INV_OP 0x200 #define F_FAKE_RW 0x400 #define F_M_ACCESS 0x800 /* media access */ +#define F_LONG_DELAY 0x1000 #define FF_RESPOND (F_RL_WLUN_OK | F_SKIP_UA | F_DELAY_OVERR) #define FF_MEDIA_IO (F_M_ACCESS | F_FAKE_RW) @@ -349,7 +350,7 @@ enum sdeb_opcode_index { SDEB_I_XDWRITEREAD = 25, /* 10 only */ SDEB_I_WRITE_BUFFER = 26, SDEB_I_WRITE_SAME = 27, /* 10, 16 */ - SDEB_I_SYNC_CACHE = 28, /* 10 only */ + SDEB_I_SYNC_CACHE = 28, /* 10, 16 */ SDEB_I_COMP_WRITE = 29, SDEB_I_LAST_ELEMENT = 30, /* keep this last (previous + 1) */ }; @@ -382,7 +383,7 @@ static const unsigned char opcode_ind_arr[256] = { /* 0x80; 0x80->0x9f: 16 byte cdbs */ 0, 0, 0, 0, 0, SDEB_I_ATA_PT, 0, 0, SDEB_I_READ, SDEB_I_COMP_WRITE, SDEB_I_WRITE, 0, 0, 0, 0, 0, - 0, 0, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0, + 0, SDEB_I_SYNC_CACHE, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SDEB_I_SERV_ACT_IN_16, SDEB_I_SERV_ACT_OUT_16, /* 0xa0; 0xa0->0xbf: 12 byte cdbs */ SDEB_I_REPORT_LUNS, SDEB_I_ATA_PT, 0, SDEB_I_MAINT_IN, @@ -398,6 +399,14 @@ static const unsigned char opcode_ind_arr[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +/* + * The following "response" functions return the SCSI mid-level's 4 byte + * tuple-in-an-int. To handle commands with an IMMED bit, for a faster + * command completion, they can mask their return value with + * SDEG_RES_IMMED_MASK . + */ +#define SDEG_RES_IMMED_MASK 0x40000000 + static int resp_inquiry(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_report_luns(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_requests(struct scsi_cmnd *, struct sdebug_dev_info *); @@ -420,6 +429,7 @@ static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_sync_cache(struct scsi_cmnd *, struct sdebug_dev_info *); /* * The following are overflow arrays for cdbs that "hit" the same index in @@ -499,6 +509,12 @@ static const struct opcode_info_t release_iarr[] = { {6, 0x1f, 0xff, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, }; +static const struct opcode_info_t sync_cache_iarr[] = { + {0, 0x91, 0, F_LONG_DELAY | F_M_ACCESS, resp_sync_cache, NULL, + {16, 0x6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x3f, 0xc7} }, /* SYNC_CACHE (16) */ +}; + /* This array is accessed via SDEB_I_* values. Make sure all are mapped, * plus the terminating elements for logic that scans this table such as @@ -536,8 +552,8 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { {ARRAY_SIZE(write_iarr), 0x8a, 0, F_D_OUT | FF_MEDIA_IO, resp_write_dt0, write_iarr, /* WRITE(16) */ {16, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7} }, /* WRITE(16) */ - {0, 0x1b, 0, 0, resp_start_stop, NULL, /* START STOP UNIT */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7} }, + {0, 0x1b, 0, F_LONG_DELAY, resp_start_stop, NULL,/* START STOP UNIT */ {6, 0x1, 0, 0xf, 0xf7, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {ARRAY_SIZE(sa_in_16_iarr), 0x9e, 0x10, F_SA_LOW | F_D_IN, resp_readcap16, sa_in_16_iarr, /* SA_IN(16), READ CAPACITY(16) */ @@ -590,9 +606,10 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { resp_write_same_10, write_same_iarr, /* WRITE SAME(10) */ {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, - {0, 0x35, 0, F_DELAY_OVERR | FF_MEDIA_IO, NULL, NULL, /* SYNC_CACHE */ + {ARRAY_SIZE(sync_cache_iarr), 0x35, 0, F_LONG_DELAY | F_M_ACCESS, + resp_sync_cache, sync_cache_iarr, {10, 0x7, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, - 0, 0, 0, 0} }, + 0, 0, 0, 0} }, /* SYNC_CACHE (10) */ {0, 0x89, 0, F_D_OUT | FF_MEDIA_IO, resp_comp_write, NULL, {16, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0xff, 0x3f, 0xc7} }, /* COMPARE AND WRITE */ @@ -1598,7 +1615,7 @@ static int resp_start_stop(struct scsi_cmnd *scp, } stop = !(cmd[4] & 1); atomic_xchg(&devip->stopped, stop); - return 0; + return (cmd[1] & 0x1) ? SDEG_RES_IMMED_MASK : 0; /* check IMMED bit */ } static sector_t get_sdebug_capacity(void) @@ -3563,6 +3580,27 @@ static int resp_get_lba_status(struct scsi_cmnd *scp, return fill_from_dev_buffer(scp, arr, SDEBUG_GET_LBA_STATUS_LEN); } +static int resp_sync_cache(struct scsi_cmnd *scp, + struct sdebug_dev_info *devip) +{ + u64 lba; + u32 num_blocks; + u8 *cmd = scp->cmnd; + + if (cmd[0] == SYNCHRONIZE_CACHE) { /* 10 byte cdb */ + lba = get_unaligned_be32(cmd + 2); + num_blocks = get_unaligned_be16(cmd + 7); + } else { /* SYNCHRONIZE_CACHE(16) */ + lba = get_unaligned_be64(cmd + 2); + num_blocks = get_unaligned_be32(cmd + 10); + } + if (lba + num_blocks > sdebug_capacity) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); + return check_condition_result; + } + return (cmd[1] & 0x2) ? SDEG_RES_IMMED_MASK : 0; /* check IMMED bit */ +} + #define RL_BUCKET_ELEMS 8 /* Even though each pseudo target has a REPORT LUNS "well known logical unit" @@ -5709,11 +5747,27 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost, errsts = oip->pfp(scp, devip); /* calls a resp_* function */ else if (r_pfp) /* if leaf function ptr NULL, try the root's */ errsts = r_pfp(scp, devip); + if (errsts & SDEG_RES_IMMED_MASK) { + errsts &= ~SDEG_RES_IMMED_MASK; + flags |= F_DELAY_OVERR; + flags &= ~F_LONG_DELAY; + } + fini: if (F_DELAY_OVERR & flags) return schedule_resp(scp, devip, errsts, 0, 0); - else + else if ((sdebug_jdelay || sdebug_ndelay) && (flags & F_LONG_DELAY)) { + /* + * If any delay is active, want F_LONG_DELAY to be at least 1 + * second and if sdebug_jdelay>0 want a long delay of that + * many seconds. + */ + int jdelay = (sdebug_jdelay < 2) ? 1 : sdebug_jdelay; + + jdelay = mult_frac(USER_HZ * jdelay, HZ, USER_HZ); + return schedule_resp(scp, devip, errsts, jdelay, 0); + } else return schedule_resp(scp, devip, errsts, sdebug_jdelay, sdebug_ndelay); check_cond: -- cgit v1.3-14-g43fede From f9ba7af8105f792d2204742810f9647aa32699cd Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Tue, 30 Jan 2018 00:35:52 +0100 Subject: scsi: scsi_debug: reset injection flags for every_nth > 0 If every_nth > 0, the injection flags must be reset for commands that aren't supposed to fail (i.e. that aren't "nth"). Otherwise, commands will continue to fail, like in the every_nth < 0 case. Signed-off-by: Martin Wilck Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 52ccf4f3da70..905501075ec6 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -4294,8 +4294,13 @@ static void clear_queue_stats(void) static void setup_inject(struct sdebug_queue *sqp, struct sdebug_queued_cmd *sqcp) { - if ((atomic_read(&sdebug_cmnd_count) % abs(sdebug_every_nth)) > 0) + if ((atomic_read(&sdebug_cmnd_count) % abs(sdebug_every_nth)) > 0) { + if (sdebug_every_nth > 0) + sqcp->inj_recovered = sqcp->inj_transport + = sqcp->inj_dif + = sqcp->inj_dix = sqcp->inj_short = 0; return; + } sqcp->inj_recovered = !!(SDEBUG_OPT_RECOVERED_ERR & sdebug_opts); sqcp->inj_transport = !!(SDEBUG_OPT_TRANSPORT_ERR & sdebug_opts); sqcp->inj_dif = !!(SDEBUG_OPT_DIF_ERR & sdebug_opts); -- cgit v1.3-14-g43fede From 2d3c2d56a3355d86a10fc74c82cc8753fb7f9eb5 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Tue, 13 Feb 2018 15:35:48 +0100 Subject: arm64: dts: rockchip: add cdn-dp node for rk3399. Add a node for the cdn DP controller which is embedded in the rk3399 SoC. Signed-off-by: Chris Zhong Signed-off-by: Enric Balletbo i Serra [fixed whitespaces instead of tabs, dropped unnecessary address+size-cells and fixed the number of interrupt cells] Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399.dtsi | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index f689b1363f9c..65a42eee01de 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi @@ -457,6 +457,42 @@ }; }; + cdn_dp: dp@fec00000 { + compatible = "rockchip,rk3399-cdn-dp"; + reg = <0x0 0xfec00000 0x0 0x100000>; + interrupts = ; + assigned-clocks = <&cru SCLK_DP_CORE>; + assigned-clock-rates = <100000000>; + clocks = <&cru SCLK_DP_CORE>, <&cru PCLK_DP_CTRL>, + <&cru SCLK_SPDIF_REC_DPTX>, <&cru PCLK_VIO_GRF>; + clock-names = "core-clk", "pclk", "spdif", "grf"; + phys = <&tcphy0_dp>, <&tcphy1_dp>; + power-domains = <&power RK3399_PD_HDCP>; + resets = <&cru SRST_DPTX_SPDIF_REC>, <&cru SRST_P_UPHY0_DPTX>, + <&cru SRST_P_UPHY0_APB>, <&cru SRST_DP_CORE>; + reset-names = "spdif", "dptx", "apb", "core"; + rockchip,grf = <&grf>; + #sound-dai-cells = <1>; + status = "disabled"; + + ports { + dp_in: port { + #address-cells = <1>; + #size-cells = <0>; + + dp_in_vopb: endpoint@0 { + reg = <0>; + remote-endpoint = <&vopb_out_dp>; + }; + + dp_in_vopl: endpoint@1 { + reg = <1>; + remote-endpoint = <&vopl_out_dp>; + }; + }; + }; + }; + gic: interrupt-controller@fee00000 { compatible = "arm,gic-v3"; #interrupt-cells = <4>; @@ -1547,6 +1583,11 @@ reg = <3>; remote-endpoint = <&mipi1_in_vopl>; }; + + vopl_out_dp: endpoint@4 { + reg = <4>; + remote-endpoint = <&dp_in_vopl>; + }; }; }; @@ -1599,6 +1640,11 @@ reg = <3>; remote-endpoint = <&mipi1_in_vopb>; }; + + vopb_out_dp: endpoint@4 { + reg = <4>; + remote-endpoint = <&dp_in_vopb>; + }; }; }; -- cgit v1.3-14-g43fede From 2490add2549bc46c6007600c01563cc9787ad59e Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Tue, 13 Feb 2018 15:35:49 +0100 Subject: arm64: dts: rockchip: enable DP for rk3399-gru Enable cdn_dp and create a cdn-dp-sound for the DP audio. Delete the endpoints between dp and vopL for gru, since we want the DP only use VOP big, which can support 4K mode. Signed-off-by: Chris Zhong Signed-off-by: Enric Balletbo i Serra [dropped vop-hacks] Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi index 03f195025390..6e50768a34ce 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi @@ -515,10 +515,15 @@ compatible = "rockchip,rk3399-gru-sound"; rockchip,cpu = <&i2s0 &i2s2>; rockchip,codec = <&max98357a &headsetcodec - &codec &wacky_spi_audio>; + &codec &wacky_spi_audio &cdn_dp>; }; }; +&cdn_dp { + status = "okay"; + extcon = <&usbc_extcon0>, <&usbc_extcon1>; +}; + /* * Set some suspend operating points to avoid OVP in suspend * -- cgit v1.3-14-g43fede From e1603b6effe177210701d3d7132d1b68e7bd2c93 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 9 Feb 2018 18:04:54 +0300 Subject: inotify: Extend ioctl to allow to request id of new watch descriptor Watch descriptor is id of the watch created by inotify_add_watch(). It is allocated in inotify_add_to_idr(), and takes the numbers starting from 1. Every new inotify watch obtains next available number (usually, old + 1), as served by idr_alloc_cyclic(). CRIU (Checkpoint/Restore In Userspace) project supports inotify files, and restores watched descriptors with the same numbers, they had before dump. Since there was no kernel support, we had to use cycle to add a watch with specific descriptor id: while (1) { int wd; wd = inotify_add_watch(inotify_fd, path, mask); if (wd < 0) { break; } else if (wd == desired_wd_id) { ret = 0; break; } inotify_rm_watch(inotify_fd, wd); } (You may find the actual code at the below link: https://github.com/checkpoint-restore/criu/blob/v3.7/criu/fsnotify.c#L577) The cycle is suboptiomal and very expensive, but since there is no better kernel support, it was the only way to restore that. Happily, we had met mostly descriptors with small id, and this approach had worked somehow. But recent time containers with inotify with big watch descriptors begun to come, and this way stopped to work at all. When descriptor id is something about 0x34d71d6, the restoring process spins in busy loop for a long time, and the restore hungs and delay of migration from node to node could easily be watched. This patch aims to solve this problem. It introduces new ioctl INOTIFY_IOC_SETNEXTWD, which allows to request the number of next created watch descriptor from userspace. It simply calls idr_set_cursor() primitive to populate idr::idr_next, so that next idr_alloc_cyclic() allocation will return this id, if it is not occupied. This is the way which is used to restore some other resources from userspace. For example, /proc/sys/kernel/ns_last_pid works the same for task pids. The new code is under CONFIG_CHECKPOINT_RESTORE #define, so small system may exclude it. v2: Use INT_MAX instead of custom definition of max id, as IDR subsystem guarantees id is between 0 and INT_MAX. CC: Jan Kara CC: Matthew Wilcox CC: Andrew Morton CC: Amir Goldstein Signed-off-by: Kirill Tkhai Reviewed-by: Cyrill Gorcunov Reviewed-by: Matthew Wilcox Reviewed-by: Andrew Morton Signed-off-by: Jan Kara --- fs/notify/inotify/inotify_user.c | 14 ++++++++++++++ include/uapi/linux/inotify.h | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 2c908b31d6c9..8f17719842ec 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -307,6 +307,20 @@ static long inotify_ioctl(struct file *file, unsigned int cmd, spin_unlock(&group->notification_lock); ret = put_user(send_len, (int __user *) p); break; +#ifdef CONFIG_CHECKPOINT_RESTORE + case INOTIFY_IOC_SETNEXTWD: + ret = -EINVAL; + if (arg >= 1 && arg <= INT_MAX) { + struct inotify_group_private_data *data; + + data = &group->inotify_data; + spin_lock(&data->idr_lock); + idr_set_cursor(&data->idr, (unsigned int)arg); + spin_unlock(&data->idr_lock); + ret = 0; + } + break; +#endif /* CONFIG_CHECKPOINT_RESTORE */ } return ret; diff --git a/include/uapi/linux/inotify.h b/include/uapi/linux/inotify.h index 5474461683db..4800bf2a531d 100644 --- a/include/uapi/linux/inotify.h +++ b/include/uapi/linux/inotify.h @@ -71,5 +71,13 @@ struct inotify_event { #define IN_CLOEXEC O_CLOEXEC #define IN_NONBLOCK O_NONBLOCK +/* + * ioctl numbers: inotify uses 'I' prefix for all ioctls, + * except historical FIONREAD, which is based on 'T'. + * + * INOTIFY_IOC_SETNEXTWD: set desired number of next created + * watch descriptor. + */ +#define INOTIFY_IOC_SETNEXTWD _IOW('I', 0, __s32) #endif /* _UAPI_LINUX_INOTIFY_H */ -- cgit v1.3-14-g43fede From a7cbf0b2d9c2f7d549da0834599fdba28245d6dd Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Tue, 13 Feb 2018 17:32:10 +0100 Subject: ARM64: dts: marvell: armada-cp110: Add registers clock for SPI nodes This extra clock is needed to access the registers of the SPI controller used on Armada 7K/8K SoCs. This follows the changes already made in the binding documentation (as well as in the driver) in: 'commit 92ae112e477ac412decc3fdd5c1eeb6c90c266b4 ("spi: orion: Fix clock resource by adding an optional bus clock")'. Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-cp110.dtsi | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi index a8af4136dbe7..0ab921861a2f 100644 --- a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi @@ -257,7 +257,9 @@ reg = <0x700600 0x50>; #address-cells = <0x1>; #size-cells = <0x0>; - clocks = <&CP110_LABEL(clk) 1 21>; + clock-names = "core", "axi"; + clocks = <&CP110_LABEL(clk) 1 21>, + <&CP110_LABEL(clk) 1 17>; status = "disabled"; }; @@ -266,7 +268,9 @@ reg = <0x700680 0x50>; #address-cells = <1>; #size-cells = <0>; - clocks = <&CP110_LABEL(clk) 1 21>; + clock-names = "core", "axi"; + clocks = <&CP110_LABEL(clk) 1 21>, + <&CP110_LABEL(clk) 1 17>; status = "disabled"; }; -- cgit v1.3-14-g43fede From afe8e5a90037dd59edcffcccad2a247a912a337a Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Tue, 13 Feb 2018 17:32:31 +0100 Subject: ARM64: dts: marvell: armada-cp110: Add registers clock for I2C nodes This extra clock is needed to access the registers of the I2C controller used on the Armada 7K/8K SoCs. This follows the changes already made in the binding documentation (as well as in the driver) in: commit 1534156e999735fe0befad958e1447600c0c20e7 ("i2c: mv64xxx: Fix clock resource by adding an optional bus clock") Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-cp110.dtsi | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi index 0ab921861a2f..c57bf6661622 100644 --- a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi @@ -280,7 +280,9 @@ #address-cells = <1>; #size-cells = <0>; interrupts = ; - clocks = <&CP110_LABEL(clk) 1 21>; + clock-names = "core", "reg"; + clocks = <&CP110_LABEL(clk) 1 21>, + <&CP110_LABEL(clk) 1 17>; status = "disabled"; }; @@ -290,7 +292,9 @@ #address-cells = <1>; #size-cells = <0>; interrupts = ; - clocks = <&CP110_LABEL(clk) 1 21>; + clock-names = "core", "reg"; + clocks = <&CP110_LABEL(clk) 1 21>, + <&CP110_LABEL(clk) 1 17>; status = "disabled"; }; -- cgit v1.3-14-g43fede From ff1c516ed1187a23a9c35ca1d8ed179983546ad1 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 31 Jan 2018 08:56:37 +0200 Subject: arm64: dts: marvell: add CP110 uart peripherals The CP110 component has 4 uart peripherals. All of them use the same clock gate for slow peripherals that is shared with the i2c and spi peripherals. Signed-off-by: Baruch Siach Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-cp110.dtsi | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi index c57bf6661622..9513e461c441 100644 --- a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi @@ -298,6 +298,46 @@ status = "disabled"; }; + CP110_LABEL(uart0): serial@702000 { + compatible = "snps,dw-apb-uart"; + reg = <0x702000 0x100>; + reg-shift = <2>; + interrupts = ; + reg-io-width = <1>; + clocks = <&CP110_LABEL(clk) 1 21>; + status = "disabled"; + }; + + CP110_LABEL(uart1): serial@702100 { + compatible = "snps,dw-apb-uart"; + reg = <0x702100 0x100>; + reg-shift = <2>; + interrupts = ; + reg-io-width = <1>; + clocks = <&CP110_LABEL(clk) 1 21>; + status = "disabled"; + }; + + CP110_LABEL(uart2): serial@702200 { + compatible = "snps,dw-apb-uart"; + reg = <0x702200 0x100>; + reg-shift = <2>; + interrupts = ; + reg-io-width = <1>; + clocks = <&CP110_LABEL(clk) 1 21>; + status = "disabled"; + }; + + CP110_LABEL(uart3): serial@702300 { + compatible = "snps,dw-apb-uart"; + reg = <0x702300 0x100>; + reg-shift = <2>; + interrupts = ; + reg-io-width = <1>; + clocks = <&CP110_LABEL(clk) 1 21>; + status = "disabled"; + }; + CP110_LABEL(nand): nand@720000 { /* * Due to the limitation of the pins available -- cgit v1.3-14-g43fede From 4d5a12493518041f6fa66be40e7123dc90c004cd Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 31 Jan 2018 08:56:38 +0200 Subject: arm64: dts: marvell: mcbin: enable uart headers Add description of the J25 and J27 UART headers of the Macchiatobin. They use uart peripherals that the CP0 (J25) and CP1 (J27) provide. Even though J25 and J27 are labeled as UART header, the pins on these headers can be muxed for other purposes. But the UART functionality is useful when the board is mounted in an ATX style enclosure, since the console UART is not accessible through the microUSB at CON9. Signed-off-by: Baruch Siach Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts index 626e9d0462c3..122d2257ebc4 100644 --- a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts +++ b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts @@ -163,6 +163,13 @@ }; }; +/* J25 UART header */ +&cp0_uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&cp0_uart1_pins>; + status = "okay"; +}; + &cp0_mdio { pinctrl-names = "default"; pinctrl-0 = <&cp0_ge_mdio_pins>; @@ -195,6 +202,10 @@ marvell,pins = "mpp37", "mpp38"; marvell,function = "i2c0"; }; + cp0_uart1_pins: uart1-pins { + marvell,pins = "mpp40", "mpp41"; + marvell,function = "uart1"; + }; cp0_xhci_vbus_pins: xhci0-vbus-pins { marvell,pins = "mpp47"; marvell,function = "gpio"; @@ -290,6 +301,17 @@ marvell,pins = "mpp12", "mpp13", "mpp14", "mpp15", "mpp16"; marvell,function = "spi1"; }; + cp1_uart0_pins: uart0-pins { + marvell,pins = "mpp6", "mpp7"; + marvell,function = "uart0"; + }; +}; + +/* J27 UART header */ +&cp1_uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&cp1_uart0_pins>; + status = "okay"; }; &cp1_sata0 { -- cgit v1.3-14-g43fede From 8f667425f9797731738a2cfe9b085e3a222ae706 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 31 Jan 2018 09:00:55 +0200 Subject: arm64: dts: marvell: mcbin: fix board name typo A 'C' was missing in the model name, this patch fixes it. Signed-off-by: Baruch Siach Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts index 122d2257ebc4..787bc6c44e2d 100644 --- a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts +++ b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts @@ -49,7 +49,7 @@ #include / { - model = "Marvell 8040 MACHIATOBin"; + model = "Marvell 8040 MACCHIATOBin"; compatible = "marvell,armada8040-mcbin", "marvell,armada8040", "marvell,armada-ap806-quad", "marvell,armada-ap806"; -- cgit v1.3-14-g43fede From 8b8061fcbfaecd4b15deb01819ddd2cb3c4d40c6 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 8 Feb 2018 18:06:13 +0800 Subject: ARM: dts: sun8i: h2+: add support for Banana Pi M2 Zero board Banana Pi M2 Zero board is a H2+-based board by Sinovoip, with a form factor and GPIO holes similar to Raspberry Pi Zero. It features: - Allwinner H2+ SoC - Single-chip (16-bit) 512MiB DDR3 DRAM - Ampak AP6212 Wi-Fi/Bluetooth module - MicroSD slot - Two MicroUSB Type-B ports (one can only be used to power the board and the other features OTG functionality) - Two keys, a reset and a GPIO-connected key. - HDMI Type-C (miniHDMI) connector connected to the HDMI part of H2+. - CSI connector to connect the camera sensor provided by Sinovoip. Signed-off-by: Icenowy Zheng Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/Makefile | 1 + .../boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts | 125 +++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index ade7a38543dc..d5a17e6e8b0f 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -974,6 +974,7 @@ dtb-$(CONFIG_MACH_SUN8I) += \ sun8i-a83t-cubietruck-plus.dtb \ sun8i-a83t-tbs-a711.dtb \ sun8i-h2-plus-orangepi-r1.dtb \ + sun8i-h2-plus-bananapi-m2-zero.dtb \ sun8i-h2-plus-orangepi-zero.dtb \ sun8i-h3-bananapi-m2-plus.dtb \ sun8i-h3-beelink-x2.dtb \ diff --git a/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts new file mode 100644 index 000000000000..1cdbd9a3ef57 --- /dev/null +++ b/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2017 Icenowy Zheng + * + * Based on sun8i-h3-bananapi-m2-plus.dts, which is: + * Copyright (C) 2016 Chen-Yu Tsai + */ + +/dts-v1/; +#include "sun8i-h3.dtsi" +#include "sunxi-common-regulators.dtsi" + +#include +#include + +/ { + model = "Banana Pi BPI-M2-Zero"; + compatible = "sinovoip,bpi-m2-zero", "allwinner,sun8i-h2-plus"; + + aliases { + serial0 = &uart0; + serial1 = &uart1; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + + pwr_led { + label = "bananapi-m2-zero:red:pwr"; + gpios = <&r_pio 0 10 GPIO_ACTIVE_HIGH>; /* PL10 */ + default-state = "on"; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + + sw4 { + label = "power"; + linux,code = ; + gpios = <&r_pio 0 3 GPIO_ACTIVE_LOW>; + }; + }; + + wifi_pwrseq: wifi_pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + reset-gpios = <&r_pio 0 7 GPIO_ACTIVE_LOW>; /* PL7 */ + }; +}; + +&ehci0 { + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins_a>; + vmmc-supply = <®_vcc3v3>; + bus-width = <4>; + /* + * On the production batch of this board the card detect GPIO is + * high active (card inserted), although on the early samples it's + * low active. + */ + cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ + status = "okay"; +}; + +&mmc1 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins_a>; + vmmc-supply = <®_vcc3v3>; + vqmmc-supply = <®_vcc3v3>; + mmc-pwrseq = <&wifi_pwrseq>; + bus-width = <4>; + non-removable; + status = "okay"; + + brcmf: wifi@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + interrupt-parent = <&pio>; + interrupts = <6 10 IRQ_TYPE_LEVEL_LOW>; /* PG10 / EINT10 */ + interrupt-names = "host-wake"; + }; +}; + +&ohci0 { + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins_a>; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>, <&uart1_rts_cts_pins>; + status = "okay"; +}; + +&usb_otg { + dr_mode = "otg"; + status = "okay"; +}; + +&usbphy { + usb0_id_det-gpios = <&r_pio 0 6 GPIO_ACTIVE_HIGH>; /* PL6 */ + /* + * There're two micro-USB connectors, one is power-only and another is + * OTG. The Vbus of these two connectors are connected together, so + * the external USB device will be powered just by the power input + * from the power-only USB port. + */ + status = "okay"; +}; -- cgit v1.3-14-g43fede From f8d5fe8fc31ad96f5d67a8c5250b101bb83116c4 Mon Sep 17 00:00:00 2001 From: Joonas Kylmälä Date: Thu, 8 Feb 2018 14:01:38 +0200 Subject: ARM: dts: sunxi: h3-h5: remove mmc0 card detection pin from pinctrl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GPIO card detection pin (mmc0_cd_pin) is already requested and configured by mmc_gpiod_request_cd() in drivers/mmc/core/slot-gpio.c so pinctrl is not needed. Signed-off-by: Joonas Kylmälä Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts | 2 +- arch/arm/boot/dts/sun8i-h3-beelink-x2.dts | 2 +- arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts | 2 +- arch/arm/boot/dts/sun8i-h3-nanopi.dtsi | 2 +- arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 2 +- arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts | 2 +- arch/arm/boot/dts/sun8i-h3-orangepi-one.dts | 2 +- arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts | 2 +- arch/arm/boot/dts/sunxi-h3-h5.dtsi | 6 ------ 9 files changed, 8 insertions(+), 14 deletions(-) diff --git a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts index 3a196a86a984..781270fb79c3 100644 --- a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts +++ b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts @@ -137,7 +137,7 @@ &mmc0 { pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; + pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts index 7824f0dbf022..20bbe2d712d0 100644 --- a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts +++ b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts @@ -116,7 +116,7 @@ &mmc0 { pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; + pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts index 48563adc7430..1e2aa6bc7a7f 100644 --- a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts +++ b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts @@ -81,7 +81,7 @@ &mmc0 { pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; + pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi b/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi index e9af61394f5d..32bf624dc59d 100644 --- a/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi +++ b/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi @@ -97,7 +97,7 @@ bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; + pinctrl-0 = <&mmc0_pins_a>; status = "okay"; vmmc-supply = <®_vcc3v3>; }; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts index 629f76b85005..99bce9822cad 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts @@ -133,7 +133,7 @@ &mmc0 { pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; + pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts index 398f975b380d..df444056001a 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts @@ -107,7 +107,7 @@ &mmc0 { pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; + pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts index 40941fef4ff3..4a4236b13220 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts @@ -107,7 +107,7 @@ &mmc0 { pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; + pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts index c4769def8cd6..583aae37694d 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts @@ -129,7 +129,7 @@ &mmc0 { pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; + pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi index 7a83b15225c7..6521d0f6a282 100644 --- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi +++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi @@ -356,12 +356,6 @@ bias-pull-up; }; - mmc0_cd_pin: mmc0_cd_pin { - pins = "PF6"; - function = "gpio_in"; - bias-pull-up; - }; - mmc1_pins_a: mmc1 { pins = "PG0", "PG1", "PG2", "PG3", "PG4", "PG5"; -- cgit v1.3-14-g43fede From 50caa75681357737b6c44b7c212964217ffc00de Mon Sep 17 00:00:00 2001 From: Joonas Kylmälä Date: Thu, 8 Feb 2018 14:01:39 +0200 Subject: ARM: dts: sunxi: h3-h5: Move pinctrl of mmc0 from dts to dtsi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the boards use the mmc0 pins and their attributes defined in mmc0_pins_a. Let's default to those by moving the pinctrl attributes to the dtsi file. This makes it easier to modify device trees in the future as there is only one place to change the pinctrl attributes. Signed-off-by: Joonas Kylmälä Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts | 2 -- arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts | 2 -- arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts | 2 -- arch/arm/boot/dts/sun8i-h3-beelink-x2.dts | 2 -- arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts | 2 -- arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts | 2 -- arch/arm/boot/dts/sun8i-h3-nanopi.dtsi | 2 -- arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 2 -- arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts | 2 -- arch/arm/boot/dts/sun8i-h3-orangepi-one.dts | 2 -- arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts | 2 -- arch/arm/boot/dts/sunxi-h3-h5.dtsi | 2 ++ arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts | 2 -- arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo2.dts | 2 -- arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts | 2 -- arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts | 2 -- arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts | 2 -- 17 files changed, 2 insertions(+), 32 deletions(-) diff --git a/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts index 1cdbd9a3ef57..29a85eb14927 100644 --- a/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts +++ b/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts @@ -60,8 +60,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; /* diff --git a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts index 9a5017bb1440..f3b066ff63cb 100644 --- a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts +++ b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts @@ -112,8 +112,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts index 781270fb79c3..bea49ed89cc7 100644 --- a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts +++ b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts @@ -136,8 +136,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts index 20bbe2d712d0..9fc07593e907 100644 --- a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts +++ b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts @@ -115,8 +115,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts b/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts index d9dc14fe2aa3..d0d41eb86cb4 100644 --- a/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts +++ b/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts @@ -150,8 +150,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc_io>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts index 1e2aa6bc7a7f..07e2e6180792 100644 --- a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts +++ b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts @@ -80,8 +80,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi b/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi index 32bf624dc59d..f110ee382239 100644 --- a/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi +++ b/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi @@ -96,8 +96,6 @@ &mmc0 { bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; status = "okay"; vmmc-supply = <®_vcc3v3>; }; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts index 99bce9822cad..ac6f52f3fa62 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts @@ -132,8 +132,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts index df444056001a..82ab5b6b730b 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts @@ -106,8 +106,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts index 4a4236b13220..c1a8cd93c463 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts @@ -106,8 +106,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts index 583aae37694d..537227b85935 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts @@ -128,8 +128,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi index 6521d0f6a282..3a0854a96a04 100644 --- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi +++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi @@ -141,6 +141,8 @@ mmc0: mmc@1c0f000 { /* compatible and clocks are in per SoC .dtsi file */ reg = <0x01c0f000 0x1000>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins_a>; resets = <&ccu RST_BUS_MMC0>; reset-names = "ahb"; interrupts = ; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts index 1ed9f219deaf..1ef70cb4c9bc 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts @@ -151,8 +151,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo2.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo2.dts index f1447003ea3c..cc268a69786c 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo2.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo2.dts @@ -126,8 +126,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts index 9e51d3a5f4e6..58505fbc2667 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dts @@ -160,8 +160,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts index 0f25c4a6f15d..586ec67f4101 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts @@ -167,8 +167,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts index af43533c7134..feee125a4e9a 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts @@ -74,8 +74,6 @@ }; &mmc0 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; -- cgit v1.3-14-g43fede From b399d2aca7e2ea4dd7dcc6e079d89e65d16bd331 Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Mon, 29 Jan 2018 10:18:59 +0100 Subject: arm64: dts: allwinner: a64: Add the SPDIF block and pin Add the SPDIF transceiver controller block and pin to the A64 dtsi. Signed-off-by: Marcus Cooper Signed-off-by: Maxime Ripard --- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index d783d164b9c3..43c54a2133f0 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -336,6 +336,11 @@ drive-strength = <40>; }; + spdif_tx_pin: spdif { + pins = "PH8"; + function = "spdif"; + }; + spi0_pins: spi0 { pins = "PC0", "PC1", "PC2", "PC3"; function = "spi0"; @@ -382,6 +387,22 @@ }; }; + spdif: spdif@1c21000 { + #sound-dai-cells = <0>; + compatible = "allwinner,sun50i-a64-spdif", + "allwinner,sun8i-h3-spdif"; + reg = <0x01c21000 0x400>; + interrupts = ; + clocks = <&ccu CLK_BUS_SPDIF>, <&ccu CLK_SPDIF>; + resets = <&ccu RST_BUS_SPDIF>; + clock-names = "apb", "spdif"; + dmas = <&dma 2>; + dma-names = "tx"; + pinctrl-names = "default"; + pinctrl-0 = <&spdif_tx_pin>; + status = "disabled"; + }; + uart0: serial@1c28000 { compatible = "snps,dw-apb-uart"; reg = <0x01c28000 0x400>; -- cgit v1.3-14-g43fede From 78e071370a86473f25923e03b51cbbadacf8be0f Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Mon, 29 Jan 2018 10:19:00 +0100 Subject: arm64: dts: allwinner: a64: Add SPDIF to the A64 Add the device tree sound bindings for the S/PDIF block. Signed-off-by: Marcus Cooper Signed-off-by: Maxime Ripard --- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index 43c54a2133f0..b9e69657a2e8 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -112,6 +112,24 @@ method = "smc"; }; + sound_spdif { + compatible = "simple-audio-card"; + simple-audio-card,name = "On-board SPDIF"; + + simple-audio-card,cpu { + sound-dai = <&spdif>; + }; + + simple-audio-card,codec { + sound-dai = <&spdif_out>; + }; + }; + + spdif_out: spdif-out { + #sound-dai-cells = <0>; + compatible = "linux,spdif-dit"; + }; + timer { compatible = "arm,armv8-timer"; interrupts = Date: Mon, 29 Jan 2018 10:19:01 +0100 Subject: arm64: dts: allwinner: a64: Add SPDIF to the Pine64 The S/PDIF transmitter can be reached on the Euler connector. But as this is a GPIO then leave it disabled so that an overlay can override the status property. Signed-off-by: Marcus Cooper Signed-off-by: Maxime Ripard --- arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts index 604cdaedac38..d1161069af9e 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts @@ -230,6 +230,11 @@ regulator-name = "vcc-rtc"; }; +/* On Euler connector */ +&spdif { + status = "disabled"; +}; + /* On Exp and Euler connectors */ &uart0 { pinctrl-names = "default"; -- cgit v1.3-14-g43fede From 1c92c00908256b736e2ce52562cd3236fcf9a5e9 Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Mon, 29 Jan 2018 10:19:02 +0100 Subject: arm64: dts: allwinner: a64: Add DAI nodes Add the DAI blocks to the device tree. I2S0 and I2S1 are for connecting to an external codec. Signed-off-by: Marcus Cooper Signed-off-by: Maxime Ripard --- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index b9e69657a2e8..1b6dc31e7d91 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -421,6 +421,34 @@ status = "disabled"; }; + i2s0: i2s@1c22000 { + #sound-dai-cells = <0>; + compatible = "allwinner,sun50i-a64-i2s", + "allwinner,sun8i-h3-i2s"; + reg = <0x01c22000 0x400>; + interrupts = ; + clocks = <&ccu CLK_BUS_I2S0>, <&ccu CLK_I2S0>; + clock-names = "apb", "mod"; + resets = <&ccu RST_BUS_I2S0>; + dma-names = "rx", "tx"; + dmas = <&dma 3>, <&dma 3>; + status = "disabled"; + }; + + i2s1: i2s@1c22400 { + #sound-dai-cells = <0>; + compatible = "allwinner,sun50i-a64-i2s", + "allwinner,sun8i-h3-i2s"; + reg = <0x01c22400 0x400>; + interrupts = ; + clocks = <&ccu CLK_BUS_I2S1>, <&ccu CLK_I2S1>; + clock-names = "apb", "mod"; + resets = <&ccu RST_BUS_I2S1>; + dma-names = "rx", "tx"; + dmas = <&dma 4>, <&dma 4>; + status = "disabled"; + }; + uart0: serial@1c28000 { compatible = "snps,dw-apb-uart"; reg = <0x01c28000 0x400>; -- cgit v1.3-14-g43fede From b75cb68d83fd55584b96380a2274de59bb1e6406 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Fri, 22 Dec 2017 22:57:38 +0200 Subject: arm64: dts: sunxi: Switch MMC nodes away from cd-inverted property Using the cd-inverted property is not useful when GPIOs are used as card detects since the polarity can be specified with the usual GPIO_ACTIVE_(HIGH|LOW) GPIO flags. It has also caused confusion for U-Boot developers, so migrate all sunxi boards away from cd-inverted. Signed-off-by: Tuomas Tynkkynen Signed-off-by: Maxime Ripard --- arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts | 3 +-- arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts | 3 +-- arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts | 3 +-- arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts | 3 +-- arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts index a6975670cd1c..2250dec9974c 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts @@ -120,8 +120,7 @@ pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; disable-wp; bus-width = <4>; status = "okay"; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts index 2beef9e6cb88..e2dce48fa29a 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts @@ -82,8 +82,7 @@ pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; disable-wp; bus-width = <4>; status = "okay"; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts index 8807664f363a..3b3081b10ecb 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts @@ -68,8 +68,7 @@ pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; disable-wp; bus-width = <4>; status = "okay"; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts index 240d35731d10..bf42690a3361 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts @@ -67,8 +67,7 @@ pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts index d1161069af9e..a75825798a71 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts @@ -103,8 +103,7 @@ pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins>; vmmc-supply = <®_dcdc1>; - cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; - cd-inverted; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; disable-wp; bus-width = <4>; status = "okay"; -- cgit v1.3-14-g43fede From 292816a637acc363ce024e6ead22a902ccfcec46 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 14 Feb 2018 13:17:20 +0100 Subject: arm64: dts: marvell: use SPDX-License-Identifier for Armada SoCs Follow the recent trend for the license description, and also fix the wrongly stated X11 to MIT. As already pointed on the DT ML, the X11 license text [1] is explicitly for the X Consortium and has a couple of extra clauses. The MIT license text [2] is actually what the current DT files claim. [1] https://spdx.org/licenses/X11.html [2] https://spdx.org/licenses/MIT.html Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-371x.dtsi | 38 +------------------- arch/arm64/boot/dts/marvell/armada-372x.dtsi | 38 +------------------- arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 38 +------------------- arch/arm64/boot/dts/marvell/armada-7020.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-7040.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-70x0.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-8020.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-8040.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-8080.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-80x0.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-ap806.dtsi | 41 +--------------------- .../dts/marvell/armada-ap810-ap0-octa-core.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-ap810-ap0.dtsi | 41 +--------------------- arch/arm64/boot/dts/marvell/armada-common.dtsi | 2 +- arch/arm64/boot/dts/marvell/armada-cp110.dtsi | 6 ++-- 17 files changed, 18 insertions(+), 596 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-371x.dtsi b/arch/arm64/boot/dts/marvell/armada-371x.dtsi index 11226f7b9ed9..dc1182ec9fa1 100644 --- a/arch/arm64/boot/dts/marvell/armada-371x.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-371x.dtsi @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Device Tree Include file for Marvell Armada 371x family of SoCs * (also named 88F3710) @@ -6,43 +7,6 @@ * * Gregory CLEMENT * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include "armada-37xx.dtsi" diff --git a/arch/arm64/boot/dts/marvell/armada-372x.dtsi b/arch/arm64/boot/dts/marvell/armada-372x.dtsi index 2554e0baea6b..97558a64e276 100644 --- a/arch/arm64/boot/dts/marvell/armada-372x.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-372x.dtsi @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Device Tree Include file for Marvell Armada 372x family of SoCs * (also named 88F3720) @@ -6,43 +7,6 @@ * * Gregory CLEMENT * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include "armada-37xx.dtsi" diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi index 375026867342..97207a61bc79 100644 --- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Device Tree Include file for Marvell Armada 37xx family of SoCs. * @@ -5,43 +6,6 @@ * * Gregory CLEMENT * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #include diff --git a/arch/arm64/boot/dts/marvell/armada-7020.dtsi b/arch/arm64/boot/dts/marvell/armada-7020.dtsi index 4ab012991d9d..4e46326dd123 100644 --- a/arch/arm64/boot/dts/marvell/armada-7020.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-7020.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for the Armada 7020 SoC, made of an AP806 Dual and * one CP110. */ diff --git a/arch/arm64/boot/dts/marvell/armada-7040.dtsi b/arch/arm64/boot/dts/marvell/armada-7040.dtsi index cbe460b8fc00..47247215770d 100644 --- a/arch/arm64/boot/dts/marvell/armada-7040.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-7040.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for the Armada 7040 SoC, made of an AP806 Quad and * one CP110. */ diff --git a/arch/arm64/boot/dts/marvell/armada-70x0.dtsi b/arch/arm64/boot/dts/marvell/armada-70x0.dtsi index f63b4fbd642b..e5c6d7c25819 100644 --- a/arch/arm64/boot/dts/marvell/armada-70x0.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-70x0.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2017 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for the Armada 70x0 SoC */ diff --git a/arch/arm64/boot/dts/marvell/armada-8020.dtsi b/arch/arm64/boot/dts/marvell/armada-8020.dtsi index 3318d6b0214b..ba1307c0fadb 100644 --- a/arch/arm64/boot/dts/marvell/armada-8020.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-8020.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for the Armada 8020 SoC, made of an AP806 Dual and * two CP110. */ diff --git a/arch/arm64/boot/dts/marvell/armada-8040.dtsi b/arch/arm64/boot/dts/marvell/armada-8040.dtsi index 83d2b40e5981..7699b19224c2 100644 --- a/arch/arm64/boot/dts/marvell/armada-8040.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-8040.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for the Armada 8040 SoC, made of an AP806 Quad and * two CP110. */ diff --git a/arch/arm64/boot/dts/marvell/armada-8080.dtsi b/arch/arm64/boot/dts/marvell/armada-8080.dtsi index d5535b716735..299e814d1ded 100644 --- a/arch/arm64/boot/dts/marvell/armada-8080.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-8080.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2017 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for Marvell Armada-8080 SoC, made of an AP810 OCTA. */ diff --git a/arch/arm64/boot/dts/marvell/armada-80x0.dtsi b/arch/arm64/boot/dts/marvell/armada-80x0.dtsi index e9c84a1d3c4d..8129b40f12a4 100644 --- a/arch/arm64/boot/dts/marvell/armada-80x0.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-80x0.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2017 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for the Armada 80x0 SoC family */ diff --git a/arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi index b98ea137371d..64b5e61a698e 100644 --- a/arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for Marvell Armada AP806. */ diff --git a/arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi index 116164ff260f..746e792767f5 100644 --- a/arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for Marvell Armada AP806. */ diff --git a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi index f9b66b81f9fc..176e38d54872 100644 --- a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for Marvell Armada AP806. */ diff --git a/arch/arm64/boot/dts/marvell/armada-ap810-ap0-octa-core.dtsi b/arch/arm64/boot/dts/marvell/armada-ap810-ap0-octa-core.dtsi index 7f0661e12f5e..7d00ae78fc79 100644 --- a/arch/arm64/boot/dts/marvell/armada-ap810-ap0-octa-core.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-ap810-ap0-octa-core.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2017 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for Marvell Armada AP810 OCTA cores. */ diff --git a/arch/arm64/boot/dts/marvell/armada-ap810-ap0.dtsi b/arch/arm64/boot/dts/marvell/armada-ap810-ap0.dtsi index 7e6f039f0f80..8107d120a8a7 100644 --- a/arch/arm64/boot/dts/marvell/armada-ap810-ap0.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-ap810-ap0.dtsi @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2017 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for Marvell Armada AP810. */ diff --git a/arch/arm64/boot/dts/marvell/armada-common.dtsi b/arch/arm64/boot/dts/marvell/armada-common.dtsi index c6dd1d81c68d..d5e8aedec188 100644 --- a/arch/arm64/boot/dts/marvell/armada-common.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-common.dtsi @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR X11) +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. */ diff --git a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi index 9513e461c441..0709b19eb328 100644 --- a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi @@ -1,9 +1,7 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR X11) +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. - */ - -/* + * * Device Tree file for Marvell Armada CP110. */ -- cgit v1.3-14-g43fede From 87ebfa3e553240fdbecbcda31ec4e6299894632a Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 14 Feb 2018 13:17:24 +0100 Subject: arm64: dts: marvell: armada-3720-db: use SPDX-License-Identifier Follow the recent trend for the license description, and also fix the wrongly stated X11 to MIT. As already pointed on the DT ML, the X11 license text [1] is explicitly for the X Consortium and has a couple of extra clauses. The MIT license text [2] is actually what the current DT files claim. [1] https://spdx.org/licenses/X11.html [2] https://spdx.org/licenses/MIT.html Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-3720-db.dts | 39 +------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts index 0f3468e777f7..f2cc00594d64 100644 --- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts +++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Device Tree file for Marvell Armada 3720 development board * (DB-88F3720-DDR3) @@ -5,44 +6,6 @@ * * Gregory CLEMENT * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * * This file is compatible with the version 1.4 and the version 2.0 of * the board, however the CON numbers are different between the 2 * version -- cgit v1.3-14-g43fede From 6b44feb7d90639c7be6ab7bf8e8aad6c8e9cd7d6 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 14 Feb 2018 13:17:26 +0100 Subject: arm64: dts: marvell: armada-3720-espressobin: use SPDX-License-Identifier Follow the recent trend for the license description, and also fix the wrongly stated X11 to MIT. As already pointed on the DT ML, the X11 license text [1] is explicitly for the X Consortium and has a couple of extra clauses. The MIT license text [2] is actually what the current DT files claim. [1] https://spdx.org/licenses/X11.html [2] https://spdx.org/licenses/MIT.html Signed-off-by: Gregory CLEMENT --- .../boot/dts/marvell/armada-3720-espressobin.dts | 38 +--------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts index bdfb5553ddb5..31efd6a96e9d 100644 --- a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts +++ b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts @@ -1,46 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Device Tree file for Globalscale Marvell ESPRESSOBin Board * Copyright (C) 2016 Marvell * * Romain Perier * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ /dts-v1/; -- cgit v1.3-14-g43fede From 75dba886fd67e38bda25cc432caf033a32ba5de8 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 14 Feb 2018 13:17:27 +0100 Subject: arm64: dts: marvell: armada-7040-db: use SPDX-License-Identifier Follow the recent trend for the license description, and also fix the wrongly stated X11 to MIT. As already pointed on the DT ML, the X11 license text [1] is explicitly for the X Consortium and has a couple of extra clauses. The MIT license text [2] is actually what the current DT files claim. [1] https://spdx.org/licenses/X11.html [2] https://spdx.org/licenses/MIT.html Cc: Thomas Petazzoni Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-7040-db.dts | 41 +------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-7040-db.dts b/arch/arm64/boot/dts/marvell/armada-7040-db.dts index 3ae05eee2c9a..c470ea89a864 100644 --- a/arch/arm64/boot/dts/marvell/armada-7040-db.dts +++ b/arch/arm64/boot/dts/marvell/armada-7040-db.dts @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for Marvell Armada 7040 Development board platform */ -- cgit v1.3-14-g43fede From 4e6a62b6a01d5c9aae4ec892a5f0be3b8ab16c36 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 14 Feb 2018 13:17:29 +0100 Subject: arm64: dts: marvell: armada-8040-db: use SPDX-License-Identifier Follow the recent trend for the license description, and also fix the wrongly stated X11 to MIT. As already pointed on the DT ML, the X11 license text [1] is explicitly for the X Consortium and has a couple of extra clauses. The MIT license text [2] is actually what the current DT files claim. [1] https://spdx.org/licenses/X11.html [2] https://spdx.org/licenses/MIT.html Cc: Thomas Petazzoni Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-8040-db.dts | 41 +------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-8040-db.dts b/arch/arm64/boot/dts/marvell/armada-8040-db.dts index dba55baff20f..3ecea815f861 100644 --- a/arch/arm64/boot/dts/marvell/armada-8040-db.dts +++ b/arch/arm64/boot/dts/marvell/armada-8040-db.dts @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for Marvell Armada 8040 Development board platform */ -- cgit v1.3-14-g43fede From aef6a7f65140274150ae3625dd9c6433d1de4433 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 14 Feb 2018 13:17:31 +0100 Subject: arm64: dts: marvell: armada-8040-mcbin: use SPDX-License-Identifier Follow the recent trend for the license description, and also fix the wrongly stated X11 to MIT. As already pointed on the DT ML, the X11 license text [1] is explicitly for the X Consortium and has a couple of extra clauses. The MIT license text [2] is actually what the current DT files claim. [1] https://spdx.org/licenses/X11.html [2] https://spdx.org/licenses/MIT.html Cc: Russell King Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts | 41 +---------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts index 787bc6c44e2d..81de03ef860d 100644 --- a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts +++ b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2016 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for MACCHIATOBin Armada 8040 community board platform */ -- cgit v1.3-14-g43fede From 75a2867b76791fe4c76b03df34aac6e200e1d963 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 14 Feb 2018 13:17:32 +0100 Subject: arm64: dts: marvell: armada-8080-db: use SPDX-License-Identifier Follow the recent trend for the license description, and also fix the wrongly stated X11 to MIT. As already pointed on the DT ML, the X11 license text [1] is explicitly for the X Consortium and has a couple of extra clauses. The MIT license text [2] is actually what the current DT files claim. [1] https://spdx.org/licenses/X11.html [2] https://spdx.org/licenses/MIT.html Cc: Hanna Hawa Signed-off-by: Gregory CLEMENT --- arch/arm64/boot/dts/marvell/armada-8080-db.dts | 41 +------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/arch/arm64/boot/dts/marvell/armada-8080-db.dts b/arch/arm64/boot/dts/marvell/armada-8080-db.dts index 85b58a19a9fb..4ba158f415ce 100644 --- a/arch/arm64/boot/dts/marvell/armada-8080-db.dts +++ b/arch/arm64/boot/dts/marvell/armada-8080-db.dts @@ -1,46 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (C) 2017 Marvell Technology Group Ltd. * - * This file is dual-licensed: you can use it either under the terms - * of the GPLv2 or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Device Tree file for Marvell Armada-8080 Development board platform */ -- cgit v1.3-14-g43fede From 82b904b6746aad0cf3aec57d235f1315e201f039 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Wed, 14 Feb 2018 14:35:26 +0100 Subject: ARM: dts: stm32: add exti support for stm32h743 This patch adds support of external interrupt (exti) for stm32h743. Signed-off-by: Ludovic Barre Signed-off-by: Alexandre Torgue --- arch/arm/boot/dts/stm32h743.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/stm32h743.dtsi b/arch/arm/boot/dts/stm32h743.dtsi index bbfcbaca0b36..dcb5f311c3db 100644 --- a/arch/arm/boot/dts/stm32h743.dtsi +++ b/arch/arm/boot/dts/stm32h743.dtsi @@ -227,6 +227,14 @@ dma-requests = <32>; }; + exti: interrupt-controller@58000000 { + compatible = "st,stm32h7-exti"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x58000000 0x400>; + interrupts = <1>, <2>, <3>, <6>, <7>, <8>, <9>, <10>, <23>, <40>, <41>, <62>, <76>; + }; + lptimer2: timer@58002400 { #address-cells = <1>; #size-cells = <0>; -- cgit v1.3-14-g43fede From 8e0a29d8606c93e6ea21d4b807a623da48e8a1fa Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Mon, 6 Nov 2017 18:03:00 +0100 Subject: ARM: dts: stm32: add system config bank node for stm32h743 This patch adds system config support for stm32h743. Signed-off-by: Ludovic Barre Signed-off-by: Alexandre Torgue --- arch/arm/boot/dts/stm32h743.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/stm32h743.dtsi b/arch/arm/boot/dts/stm32h743.dtsi index dcb5f311c3db..3b25180d2a07 100644 --- a/arch/arm/boot/dts/stm32h743.dtsi +++ b/arch/arm/boot/dts/stm32h743.dtsi @@ -235,6 +235,11 @@ interrupts = <1>, <2>, <3>, <6>, <7>, <8>, <9>, <10>, <23>, <40>, <41>, <62>, <76>; }; + syscfg: system-config@58000400 { + compatible = "syscon"; + reg = <0x58000400 0x400>; + }; + lptimer2: timer@58002400 { #address-cells = <1>; #size-cells = <0>; -- cgit v1.3-14-g43fede From 8b4282e6b8e239d8ce68ab884c89335cc6fdd7c7 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Tue, 13 Feb 2018 15:20:50 +0000 Subject: ACPI/IORT: Add msi address regions reservation helper On some platforms msi parent address regions have to be excluded from normal IOVA allocation in that they are detected and decoded in a HW specific way by system components and so they cannot be considered normal IOVA address space. Add a helper function that retrieves ITS address regions - the msi parent - through IORT device <-> ITS mappings and reserves it so that these regions will not be translated by IOMMU and will be excluded from IOVA allocations. The function checks for the smmu model number and only applies the msi reservation if the platform requires it. Signed-off-by: Shameer Kolothum Reviewed-by: Lorenzo Pieralisi [For the ITS part] Reviewed-by: Marc Zyngier Signed-off-by: Joerg Roedel --- drivers/acpi/arm64/iort.c | 111 +++++++++++++++++++++++++++++++++++++-- drivers/irqchip/irq-gic-v3-its.c | 3 +- include/linux/acpi_iort.h | 7 ++- 3 files changed, 116 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index 95255ecfae7c..e2f7bddf5522 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -39,6 +39,7 @@ struct iort_its_msi_chip { struct list_head list; struct fwnode_handle *fw_node; + phys_addr_t base_addr; u32 translation_id; }; @@ -161,14 +162,16 @@ static LIST_HEAD(iort_msi_chip_list); static DEFINE_SPINLOCK(iort_msi_chip_lock); /** - * iort_register_domain_token() - register domain token and related ITS ID - * to the list from where we can get it back later on. + * iort_register_domain_token() - register domain token along with related + * ITS ID and base address to the list from where we can get it back later on. * @trans_id: ITS ID. + * @base: ITS base address. * @fw_node: Domain token. * * Returns: 0 on success, -ENOMEM if no memory when allocating list element */ -int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node) +int iort_register_domain_token(int trans_id, phys_addr_t base, + struct fwnode_handle *fw_node) { struct iort_its_msi_chip *its_msi_chip; @@ -178,6 +181,7 @@ int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node) its_msi_chip->fw_node = fw_node; its_msi_chip->translation_id = trans_id; + its_msi_chip->base_addr = base; spin_lock(&iort_msi_chip_lock); list_add(&its_msi_chip->list, &iort_msi_chip_list); @@ -581,6 +585,24 @@ int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) return -ENODEV; } +static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) +{ + struct iort_its_msi_chip *its_msi_chip; + int ret = -ENODEV; + + spin_lock(&iort_msi_chip_lock); + list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { + if (its_msi_chip->translation_id == its_id) { + *base = its_msi_chip->base_addr; + ret = 0; + break; + } + } + spin_unlock(&iort_msi_chip_lock); + + return ret; +} + /** * iort_dev_find_its_id() - Find the ITS identifier for a device * @dev: The device. @@ -766,6 +788,24 @@ static inline bool iort_iommu_driver_enabled(u8 type) } #ifdef CONFIG_IOMMU_API +static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev) +{ + struct acpi_iort_node *iommu; + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + + iommu = iort_get_iort_node(fwspec->iommu_fwnode); + + if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) { + struct acpi_iort_smmu_v3 *smmu; + + smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data; + if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X) + return iommu; + } + + return NULL; +} + static inline const struct iommu_ops *iort_fwspec_iommu_ops( struct iommu_fwspec *fwspec) { @@ -782,6 +822,69 @@ static inline int iort_add_device_replay(const struct iommu_ops *ops, return err; } + +/** + * iort_iommu_msi_get_resv_regions - Reserved region driver helper + * @dev: Device from iommu_get_resv_regions() + * @head: Reserved region list from iommu_get_resv_regions() + * + * Returns: Number of msi reserved regions on success (0 if platform + * doesn't require the reservation or no associated msi regions), + * appropriate error value otherwise. The ITS interrupt translation + * spaces (ITS_base + SZ_64K, SZ_64K) associated with the device + * are the msi reserved regions. + */ +int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) +{ + struct acpi_iort_its_group *its; + struct acpi_iort_node *iommu_node, *its_node = NULL; + int i, resv = 0; + + iommu_node = iort_get_msi_resv_iommu(dev); + if (!iommu_node) + return 0; + + /* + * Current logic to reserve ITS regions relies on HW topologies + * where a given PCI or named component maps its IDs to only one + * ITS group; if a PCI or named component can map its IDs to + * different ITS groups through IORT mappings this function has + * to be reworked to ensure we reserve regions for all ITS groups + * a given PCI or named component may map IDs to. + */ + + for (i = 0; i < dev->iommu_fwspec->num_ids; i++) { + its_node = iort_node_map_id(iommu_node, + dev->iommu_fwspec->ids[i], + NULL, IORT_MSI_TYPE); + if (its_node) + break; + } + + if (!its_node) + return 0; + + /* Move to ITS specific data */ + its = (struct acpi_iort_its_group *)its_node->node_data; + + for (i = 0; i < its->its_count; i++) { + phys_addr_t base; + + if (!iort_find_its_base(its->identifiers[i], &base)) { + int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; + struct iommu_resv_region *region; + + region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K, + prot, IOMMU_RESV_MSI); + if (region) { + list_add_tail(®ion->list, head); + resv++; + } + } + } + + return (resv == its->its_count) ? resv : -ENODEV; +} #else static inline const struct iommu_ops *iort_fwspec_iommu_ops( struct iommu_fwspec *fwspec) @@ -789,6 +892,8 @@ static inline const struct iommu_ops *iort_fwspec_iommu_ops( static inline int iort_add_device_replay(const struct iommu_ops *ops, struct device *dev) { return 0; } +int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) +{ return 0; } #endif static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 06f025fd5726..ab99d1bd7087 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -3450,7 +3450,8 @@ static int __init gic_acpi_parse_madt_its(struct acpi_subtable_header *header, return -ENOMEM; } - err = iort_register_domain_token(its_entry->translation_id, dom_handle); + err = iort_register_domain_token(its_entry->translation_id, res.start, + dom_handle); if (err) { pr_err("ITS@%pa: Unable to register GICv3 ITS domain token (ITS ID %d) to IORT\n", &res.start, its_entry->translation_id); diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h index 2f7a29242b87..38cd77b39a64 100644 --- a/include/linux/acpi_iort.h +++ b/include/linux/acpi_iort.h @@ -26,7 +26,8 @@ #define IORT_IRQ_MASK(irq) (irq & 0xffffffffULL) #define IORT_IRQ_TRIGGER_MASK(irq) ((irq >> 32) & 0xffffffffULL) -int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node); +int iort_register_domain_token(int trans_id, phys_addr_t base, + struct fwnode_handle *fw_node); void iort_deregister_domain_token(int trans_id); struct fwnode_handle *iort_find_domain_token(int trans_id); #ifdef CONFIG_ACPI_IORT @@ -38,6 +39,7 @@ int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id); /* IOMMU interface */ void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *size); const struct iommu_ops *iort_iommu_configure(struct device *dev); +int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head); #else static inline void acpi_iort_init(void) { } static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id) @@ -52,6 +54,9 @@ static inline void iort_dma_setup(struct device *dev, u64 *dma_addr, static inline const struct iommu_ops *iort_iommu_configure( struct device *dev) { return NULL; } +static inline +int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) +{ return 0; } #endif #endif /* __ACPI_IORT_H__ */ -- cgit v1.3-14-g43fede From f51dc89265dabad36f1cd832a581106cf4e50158 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Tue, 13 Feb 2018 15:20:51 +0000 Subject: iommu/dma: Add HW MSI(GICv3 ITS) address regions reservation Modified iommu_dma_get_resv_regions() to include GICv3 ITS region on ACPI based ARM platfiorms which may require HW MSI reservations. Signed-off-by: Shameer Kolothum Reviewed-by: Robin Murphy Signed-off-by: Joerg Roedel --- drivers/iommu/dma-iommu.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 25914d36c5ac..f05f3cf90756 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -19,6 +19,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -167,13 +168,18 @@ EXPORT_SYMBOL(iommu_put_dma_cookie); * * IOMMU drivers can use this to implement their .get_resv_regions callback * for general non-IOMMU-specific reservations. Currently, this covers host - * bridge windows for PCI devices. + * bridge windows for PCI devices and GICv3 ITS region reservation on ACPI + * based ARM platforms that may require HW MSI reservation. */ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list) { struct pci_host_bridge *bridge; struct resource_entry *window; + if (!is_of_node(dev->iommu_fwspec->iommu_fwnode) && + iort_iommu_msi_get_resv_regions(dev, list) < 0) + return; + if (!dev_is_pci(dev)) return; -- cgit v1.3-14-g43fede From 35615917ef027386a9acbe3cd8752f02b125b074 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 27 Nov 2017 11:31:48 +0900 Subject: clk: samsung: s3c: Remove unneeded enumeration This patch just removes the unneeded enumeration for PLL index. Signed-off-by: Chanwoo Choi Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-s3c2412.c | 11 ++--------- drivers/clk/samsung/clk-s3c2443.c | 17 ++++------------- drivers/clk/samsung/clk-s3c64xx.c | 17 ++++++----------- 3 files changed, 12 insertions(+), 33 deletions(-) diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c index b8340a49921b..1555e407529e 100644 --- a/drivers/clk/samsung/clk-s3c2412.c +++ b/drivers/clk/samsung/clk-s3c2412.c @@ -27,11 +27,6 @@ #define CLKSRC 0x1c #define SWRST 0x30 -/* list of PLLs to be registered */ -enum s3c2412_plls { - mpll, upll, -}; - static void __iomem *reg_base; #ifdef CONFIG_PM_SLEEP @@ -144,10 +139,8 @@ struct samsung_mux_clock s3c2412_muxes[] __initdata = { }; static struct samsung_pll_clock s3c2412_plls[] __initdata = { - [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti", - LOCKTIME, MPLLCON, NULL), - [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "urefclk", - LOCKTIME, UPLLCON, NULL), + PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti", LOCKTIME, MPLLCON, NULL), + PLL(pll_s3c2410_upll, UPLL, "upll", "urefclk", LOCKTIME, UPLLCON, NULL), }; struct samsung_gate_clock s3c2412_gates[] __initdata = { diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c index d94b85a42356..9580a6baf4d7 100644 --- a/drivers/clk/samsung/clk-s3c2443.c +++ b/drivers/clk/samsung/clk-s3c2443.c @@ -41,11 +41,6 @@ enum supported_socs { S3C2450, }; -/* list of PLLs to be registered */ -enum s3c2443_plls { - mpll, epll, -}; - static void __iomem *reg_base; #ifdef CONFIG_PM_SLEEP @@ -225,10 +220,8 @@ struct samsung_clock_alias s3c2443_common_aliases[] __initdata = { /* S3C2416 specific clocks */ static struct samsung_pll_clock s3c2416_pll_clks[] __initdata = { - [mpll] = PLL(pll_6552_s3c2416, MPLL, "mpll", "mpllref", - LOCKCON0, MPLLCON, NULL), - [epll] = PLL(pll_6553, EPLL, "epll", "epllref", - LOCKCON1, EPLLCON, NULL), + PLL(pll_6552_s3c2416, MPLL, "mpll", "mpllref", LOCKCON0, MPLLCON, NULL), + PLL(pll_6553, EPLL, "epll", "epllref", LOCKCON1, EPLLCON, NULL), }; PNAME(s3c2416_hsmmc0_p) = { "sclk_hsmmc0", "sclk_hsmmcext" }; @@ -279,10 +272,8 @@ struct samsung_clock_alias s3c2416_aliases[] __initdata = { /* S3C2443 specific clocks */ static struct samsung_pll_clock s3c2443_pll_clks[] __initdata = { - [mpll] = PLL(pll_3000, MPLL, "mpll", "mpllref", - LOCKCON0, MPLLCON, NULL), - [epll] = PLL(pll_2126, EPLL, "epll", "epllref", - LOCKCON1, EPLLCON, NULL), + PLL(pll_3000, MPLL, "mpll", "mpllref", LOCKCON0, MPLLCON, NULL), + PLL(pll_2126, EPLL, "epll", "epllref", LOCKCON1, EPLLCON, NULL), }; static struct clk_div_table armdiv_s3c2443_d[] = { diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c index 7306867a0ab8..6db01cf5ab83 100644 --- a/drivers/clk/samsung/clk-s3c64xx.c +++ b/drivers/clk/samsung/clk-s3c64xx.c @@ -56,11 +56,6 @@ #define GATE_ON(_id, cname, pname, o, b) \ GATE(_id, cname, pname, o, b, CLK_IGNORE_UNUSED, 0) -/* list of PLLs to be registered */ -enum s3c64xx_plls { - apll, mpll, epll, -}; - static void __iomem *reg_base; static bool is_s3c6400; @@ -364,12 +359,12 @@ GATE_CLOCKS(s3c6410_gate_clks) __initdata = { /* List of PLL clocks. */ static struct samsung_pll_clock s3c64xx_pll_clks[] __initdata = { - [apll] = PLL(pll_6552, FOUT_APLL, "fout_apll", "fin_pll", - APLL_LOCK, APLL_CON, NULL), - [mpll] = PLL(pll_6552, FOUT_MPLL, "fout_mpll", "fin_pll", - MPLL_LOCK, MPLL_CON, NULL), - [epll] = PLL(pll_6553, FOUT_EPLL, "fout_epll", "fin_pll", - EPLL_LOCK, EPLL_CON0, NULL), + PLL(pll_6552, FOUT_APLL, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON, NULL), + PLL(pll_6552, FOUT_MPLL, "fout_mpll", "fin_pll", + MPLL_LOCK, MPLL_CON, NULL), + PLL(pll_6553, FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), }; /* Aliases for common s3c64xx clocks. */ -- cgit v1.3-14-g43fede From a3834c76a24c379347ea7ee316a9154178f98f4d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 17 Jan 2018 11:26:27 +0000 Subject: clk: samsung: Remove redundant dev_err call in exynos5433_cmu_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index db270908037a..9323e6b7eed5 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -5513,10 +5513,8 @@ static int __init exynos5433_cmu_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg_base = devm_ioremap_resource(dev, res); - if (IS_ERR(reg_base)) { - dev_err(dev, "failed to map registers\n"); + if (IS_ERR(reg_base)) return PTR_ERR(reg_base); - } for (i = 0; i < info->nr_clk_ids; ++i) ctx->clk_data.hws[i] = ERR_PTR(-ENOENT); -- cgit v1.3-14-g43fede From 073f698dbe6bb37b226c22ac8dc69d567a08f4cb Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 17 Jan 2018 11:26:18 +0000 Subject: clk: samsung: Remove redundant dev_err call in exynos_audss_clk_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos-audss.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c index 5bfc92ee3129..b4b057c7301c 100644 --- a/drivers/clk/samsung/clk-exynos-audss.c +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -143,10 +143,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg_base = devm_ioremap_resource(dev, res); - if (IS_ERR(reg_base)) { - dev_err(dev, "failed to map audss registers\n"); + if (IS_ERR(reg_base)) return PTR_ERR(reg_base); - } epll = ERR_PTR(-ENODEV); -- cgit v1.3-14-g43fede From c99fc5a352b24397f50e6e71d26d4e95c2cce776 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 5 Feb 2018 15:41:58 +0100 Subject: clk: samsung: Add a git tree entry to MAINTAINERS Add a missing git tree entry for drivers/clk/samsung. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Acked-by: Chanwoo Choi --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3bdc260e36b7..395ea06c427d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12189,6 +12189,7 @@ M: Tomasz Figa M: Chanwoo Choi S: Supported L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers) +T: git git://git.kernel.org/pub/scm/linux/kernel/git/snawrocki/clk.git F: drivers/clk/samsung/ F: include/dt-bindings/clock/exynos*.h F: Documentation/devicetree/bindings/clock/exynos*.txt -- cgit v1.3-14-g43fede From 5c7979246ead98a7269f418d81a637dd056a1be7 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 5 Feb 2018 15:22:30 +0100 Subject: clk: exynos5433: Add CLK_IGNORE_UNUSED flag to sclk_ioclk_i2s1_bclk The sclk_ioclk_i2s1_bclk clock is not currently handled by any driver and disabling this clock by the clk core prevents proper operation of the I2S1 block. CLK_IGNORE_UNUSED flag is added as a temporary fix. Signed-off-by: Sylwester Nawrocki Reviewed-by: Chanwoo Choi --- drivers/clk/samsung/clk-exynos5433.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 9323e6b7eed5..c84326dc4b05 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -1672,7 +1672,7 @@ static const struct samsung_gate_clock peric_gate_clks[] __initconst = { ENABLE_SCLK_PERIC, 11, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_IOCLK_I2S1_BCLK, "sclk_ioclk_i2s1_bclk", "ioclk_i2s1_bclk_in", ENABLE_SCLK_PERIC, 10, - CLK_SET_RATE_PARENT, 0), + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_SPDIF, "sclk_spdif", "sclk_spdif_peric", ENABLE_SCLK_PERIC, 8, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_PCM1, "sclk_pcm1", "sclk_pcm1_peric", -- cgit v1.3-14-g43fede From 83942bdd992ff347442ec72d86c2d77fe51a0270 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 12 Feb 2018 16:52:27 +0100 Subject: clk: exynos5433: Extend list of available AUD_PLL output frequencies Add one more entry to the exynos5433_aud_pll_rates table, this allows to support audio sample rates: 48000, 96000, 192000 Hz with minimum error. The M, P, S, K values re confirmed by the HW team. Signed-off-by: Sylwester Nawrocki Acked-by: Chanwoo Choi --- drivers/clk/samsung/clk-exynos5433.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index c84326dc4b05..57a41824ee2e 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -765,6 +765,7 @@ static const struct samsung_pll_rate_table exynos5433_aud_pll_rates[] __initcons PLL_36XX_RATE(294912000U, 98, 1, 3, 19923), PLL_36XX_RATE(288000000U, 96, 1, 3, 0), PLL_36XX_RATE(252000000U, 84, 1, 3, 0), + PLL_36XX_RATE(196608001U, 197, 3, 3, -25690), { /* sentinel */ } }; -- cgit v1.3-14-g43fede From 01c5c8c7062a8617a47fbad21f7a80ca4050363f Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Mon, 6 Nov 2017 18:03:00 +0100 Subject: ARM: dts: stm32: add support of exti on stm32h743 pinctrl This patch adds support of external interrupt (exti) on all gpio ports of stm32h743. Signed-off-by: Ludovic Barre Signed-off-by: Alexandre Torgue --- arch/arm/boot/dts/stm32h743-pinctrl.dtsi | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/arm/boot/dts/stm32h743-pinctrl.dtsi b/arch/arm/boot/dts/stm32h743-pinctrl.dtsi index 65c1cd043987..ee5202d88b2f 100644 --- a/arch/arm/boot/dts/stm32h743-pinctrl.dtsi +++ b/arch/arm/boot/dts/stm32h743-pinctrl.dtsi @@ -49,6 +49,8 @@ #size-cells = <1>; compatible = "st,stm32h743-pinctrl"; ranges = <0 0x58020000 0x3000>; + interrupt-parent = <&exti>; + st,syscfg = <&syscfg 0x8>; pins-are-numbered; gpioa: gpio@58020000 { @@ -57,6 +59,8 @@ reg = <0x0 0x400>; clocks = <&rcc GPIOA_CK>; st,bank-name = "GPIOA"; + interrupt-controller; + #interrupt-cells = <2>; }; gpiob: gpio@58020400 { @@ -65,6 +69,8 @@ reg = <0x400 0x400>; clocks = <&rcc GPIOB_CK>; st,bank-name = "GPIOB"; + interrupt-controller; + #interrupt-cells = <2>; }; gpioc: gpio@58020800 { @@ -73,6 +79,8 @@ reg = <0x800 0x400>; clocks = <&rcc GPIOC_CK>; st,bank-name = "GPIOC"; + interrupt-controller; + #interrupt-cells = <2>; }; gpiod: gpio@58020c00 { @@ -81,6 +89,8 @@ reg = <0xc00 0x400>; clocks = <&rcc GPIOD_CK>; st,bank-name = "GPIOD"; + interrupt-controller; + #interrupt-cells = <2>; }; gpioe: gpio@58021000 { @@ -89,6 +99,8 @@ reg = <0x1000 0x400>; clocks = <&rcc GPIOE_CK>; st,bank-name = "GPIOE"; + interrupt-controller; + #interrupt-cells = <2>; }; gpiof: gpio@58021400 { @@ -97,6 +109,8 @@ reg = <0x1400 0x400>; clocks = <&rcc GPIOF_CK>; st,bank-name = "GPIOF"; + interrupt-controller; + #interrupt-cells = <2>; }; gpiog: gpio@58021800 { @@ -105,6 +119,8 @@ reg = <0x1800 0x400>; clocks = <&rcc GPIOG_CK>; st,bank-name = "GPIOG"; + interrupt-controller; + #interrupt-cells = <2>; }; gpioh: gpio@58021c00 { @@ -113,6 +129,8 @@ reg = <0x1c00 0x400>; clocks = <&rcc GPIOH_CK>; st,bank-name = "GPIOH"; + interrupt-controller; + #interrupt-cells = <2>; }; gpioi: gpio@58022000 { @@ -121,6 +139,8 @@ reg = <0x2000 0x400>; clocks = <&rcc GPIOI_CK>; st,bank-name = "GPIOI"; + interrupt-controller; + #interrupt-cells = <2>; }; gpioj: gpio@58022400 { @@ -129,6 +149,8 @@ reg = <0x2400 0x400>; clocks = <&rcc GPIOJ_CK>; st,bank-name = "GPIOJ"; + interrupt-controller; + #interrupt-cells = <2>; }; gpiok: gpio@58022800 { @@ -137,6 +159,8 @@ reg = <0x2800 0x400>; clocks = <&rcc GPIOK_CK>; st,bank-name = "GPIOK"; + interrupt-controller; + #interrupt-cells = <2>; }; usart1_pins: usart1@0 { -- cgit v1.3-14-g43fede From b433cdc7f07e50d66bbfb89fbc64a6ea9e9958ed Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Fri, 15 Dec 2017 13:46:00 +0100 Subject: ARM: dts: stm32: dts: Remove leading 0x and 0s from bindings notation Improve the DTS files by removing all the leading "0x" and zeros to fix the following dtc warnings: Warning (unit_address_format): Node /XXX unit name should not have leading "0x" and Warning (unit_address_format): Node /XXX unit name should not have leading 0s Converted using the following command: find . -type f \( -iname *.dts -o -iname *.dtsi \) -exec sed -i -e "s/@\([0-9a-fA-FxX\.;:#]+\)\s*{/@\L\1 {/g" -e "s/@0x\(.*\) {/@\1 {/g" -e "s/@0+\(.*\) {/@\1 {/g" {} +^C For simplicity, two sed expressions were used to solve each warnings separately. To make the regex expression more robust a few other issues were resolved, namely setting unit-address to lower case, and adding a whitespace before the the opening curly brace: https://elinux.org/Device_Tree_Linux#Linux_conventions This will solve as a side effect warning: Warning (simple_bus_reg): Node /XXX@ simple-bus unit address format error, expected "" This is a follow up to commit 4c9847b7375a ("dt-bindings: Remove leading 0x from bindings notation") Reported-by: David Daney Suggested-by: Rob Herring Signed-off-by: Mathieu Malaterre Signed-off-by: Alexandre Torgue --- arch/arm/boot/dts/stm32h743.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/stm32h743.dtsi b/arch/arm/boot/dts/stm32h743.dtsi index 3b25180d2a07..38d7cb8681fc 100644 --- a/arch/arm/boot/dts/stm32h743.dtsi +++ b/arch/arm/boot/dts/stm32h743.dtsi @@ -317,7 +317,7 @@ }; }; - vrefbuf: regulator@58003C00 { + vrefbuf: regulator@58003c00 { compatible = "st,stm32-vrefbuf"; reg = <0x58003C00 0x8>; clocks = <&rcc VREF_CK>; -- cgit v1.3-14-g43fede From 1736353f17e10bee352ec690757c66b568140724 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 19 Dec 2017 22:38:29 -0200 Subject: PCI: xgene: Fix the xgene_msi_probe() return code If devm_ioremap_resource() detects an error condition in the return value through IS_ERR(), the return value should be retrieved through PTR_ERR() instead of hardcoding it. Fix the xgene_msi_probe() error return code. Signed-off-by: Fabio Estevam [lorenzo.pieralisi@arm.com: rewrote commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/host/pci-xgene-msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c index df8e4bd5ddb2..f4c02da84e59 100644 --- a/drivers/pci/host/pci-xgene-msi.c +++ b/drivers/pci/host/pci-xgene-msi.c @@ -456,7 +456,7 @@ static int xgene_msi_probe(struct platform_device *pdev) xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(xgene_msi->msi_regs)) { dev_err(&pdev->dev, "no reg space\n"); - rc = -EINVAL; + rc = PTR_ERR(xgene_msi->msi_regs); goto error; } xgene_msi->msi_addr = res->start; -- cgit v1.3-14-g43fede From a66e9437cffc965866efef28bc3e15f711c98ee9 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Wed, 3 Jan 2018 15:12:49 +0100 Subject: ARM: dts: nokia N9: Add support for magnetometer This adds dts support for magnetometer on Nokia N9. Signed-off-by: Pavel Machek [tony@atomide.com: fix missing bracket] Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/omap3-n9.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/omap3-n9.dts b/arch/arm/boot/dts/omap3-n9.dts index e44d93fc644c..ded5fcf084eb 100644 --- a/arch/arm/boot/dts/omap3-n9.dts +++ b/arch/arm/boot/dts/omap3-n9.dts @@ -39,6 +39,13 @@ }; }; +&i2c3 { + ak8975@0f { + compatible = "asahi-kasei,ak8975"; + reg = <0x0f>; + }; +}; + &isp { vdd-csiphy1-supply = <&vaux2>; vdd-csiphy2-supply = <&vaux2>; -- cgit v1.3-14-g43fede From ca308fc2d5cceb2ebdd3f4e2c814d6b3362517b4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2018 22:22:13 +0100 Subject: ARM: dts: use 'atmel' as at24 manufacturer in am335x-boneblue Using 'at' as the part of the compatible string is now deprecated. Use a correct string: 'atmel,'. Signed-off-by: Bartosz Golaszewski Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/am335x-boneblue.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/am335x-boneblue.dts b/arch/arm/boot/dts/am335x-boneblue.dts index 3f2480d05a3b..58baee158e64 100644 --- a/arch/arm/boot/dts/am335x-boneblue.dts +++ b/arch/arm/boot/dts/am335x-boneblue.dts @@ -342,7 +342,7 @@ }; baseboard_eeprom: baseboard_eeprom@50 { - compatible = "at,24c256"; + compatible = "atmel,24c256"; reg = <0x50>; #address-cells = <1>; -- cgit v1.3-14-g43fede From f88bf4fceaa17159dfabb27949caf91c436bfac2 Mon Sep 17 00:00:00 2001 From: Felix Brack Date: Tue, 30 Jan 2018 15:28:14 +0100 Subject: ARM: dts: am33xx: add PDU001 board This patch adds support for the PDU001 board from EETS GmbH. The board is built around the byteENGINE module AM335X from bytes at work AG which in turn is built around a Sitara SoC from TI. Signed-off-by: Felix Brack Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/am335x-pdu001.dts | 595 ++++++++++++++++++++++++++++++++++++ 2 files changed, 596 insertions(+) create mode 100644 arch/arm/boot/dts/am335x-pdu001.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index ade7a38543dc..944192dac4e7 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -673,6 +673,7 @@ dtb-$(CONFIG_SOC_AM33XX) += \ am335x-lxm.dtb \ am335x-moxa-uc-8100-me-t.dtb \ am335x-nano.dtb \ + am335x-pdu001.dtb \ am335x-pepper.dtb \ am335x-phycore-rdk.dtb \ am335x-shc.dtb \ diff --git a/arch/arm/boot/dts/am335x-pdu001.dts b/arch/arm/boot/dts/am335x-pdu001.dts new file mode 100644 index 000000000000..1ad530a39a95 --- /dev/null +++ b/arch/arm/boot/dts/am335x-pdu001.dts @@ -0,0 +1,595 @@ +/* + * pdu001.dts + * + * EETS GmbH PDU001 board device tree file + * + * Copyright (C) 2018 EETS GmbH - http://www.eets.ch/ + * + * Copyright (C) 2011, Texas Instruments, Incorporated - http://www.ti.com/ + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; + +#include "am33xx.dtsi" +#include +#include + +/ { + model = "EETS,PDU001"; + compatible = "ti,am33xx"; + + chosen { + stdout-path = &uart3; + }; + + cpus { + cpu@0 { + cpu0-supply = <&vdd1_reg>; + }; + }; + + memory { + device_type = "memory"; + reg = <0x80000000 0x10000000>; /* 256 MB */ + }; + + vbat: fixedregulator@0 { + compatible = "regulator-fixed"; + regulator-name = "vbat"; + regulator-min-microvolt = <3600000>; + regulator-max-microvolt = <3600000>; + regulator-boot-on; + }; + + lis3_reg: fixedregulator@1 { + compatible = "regulator-fixed"; + regulator-name = "lis3_reg"; + regulator-boot-on; + }; + + panel { + compatible = "ti,tilcdc,panel"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&lcd_pins_s0>; + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + + display-timings { + 240x320p16 { + clock-frequency = <6500000>; + hactive = <240>; + vactive = <320>; + hfront-porch = <6>; + hback-porch = <6>; + hsync-len = <1>; + vback-porch = <6>; + vfront-porch = <6>; + vsync-len = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <1>; + de-active = <0>; + }; + }; + }; +}; + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&clkout2_pin>; + + i2c0_pins: pinmux_i2c0_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x988, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c0_sda.i2c0_sda */ + AM33XX_IOPAD(0x98c, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c0_scl.i2c0_scl */ + >; + }; + + i2c1_pins: pinmux_i2c1_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x958, PIN_INPUT_PULLUP | MUX_MODE2) /* spi0_d1.i2c1_sda */ + AM33XX_IOPAD(0x95c, PIN_INPUT_PULLUP | MUX_MODE2) /* spi0_cs0.i2c1_scl */ + >; + }; + + i2c2_pins: pinmux_i2c2_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x950, PIN_INPUT_PULLUP | MUX_MODE2) /* spi0_clk.i2c2_sda */ + AM33XX_IOPAD(0x954, PIN_INPUT_PULLUP | MUX_MODE2) /* spi0_d0.i2c2_scl */ + >; + }; + + spi1_pins: pinmux_spi1_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x990, PIN_OUTPUT | MUX_MODE3) /* mcasp0_aclkx.spi1_sclk */ + AM33XX_IOPAD(0x994, PIN_OUTPUT | MUX_MODE3) /* mcasp0_fsx.spi1_d0 */ + AM33XX_IOPAD(0x998, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcasp0_axr0.spi1_d1 */ + AM33XX_IOPAD(0x99C, PIN_OUTPUT | MUX_MODE3) /* mcasp0_ahclkr.spi1_cs0 */ + >; + }; + + uart0_pins: pinmux_uart0_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x96C, PIN_OUTPUT | MUX_MODE7) /* uart0_rtsn.gpio1_9 */ + AM33XX_IOPAD(0x970, PIN_INPUT_PULLUP | MUX_MODE0) /* uart0_rxd.uart0_rxd */ + AM33XX_IOPAD(0x974, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* uart0_txd.uart0_txd */ + >; + }; + + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x980, PIN_INPUT_PULLUP | MUX_MODE0) /* uart1_rxd.uart1_rxd */ + AM33XX_IOPAD(0x984, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* uart1_txd.uart1_txd */ + >; + }; + + uart3_pins: pinmux_uart3_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x960, PIN_INPUT_PULLUP | MUX_MODE1) /* spi0_cs1.uart3_rxd */ + AM33XX_IOPAD(0x964, PIN_OUTPUT_PULLDOWN | MUX_MODE1) /* ecap0_in_pwm0_out.uart3_txd */ + >; + }; + + clkout2_pin: pinmux_clkout2_pin { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b4, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr1.clkout2 */ + >; + }; + + cpsw_default: cpsw_default { + pinctrl-single,pins = < + /* Port 1 (emac0) */ + AM33XX_IOPAD(0x908, PIN_INPUT | MUX_MODE0) /* mii1_col.mii1_col */ + AM33XX_IOPAD(0x90C, PIN_INPUT | MUX_MODE0) /* mii1_crs.mii1_crs */ + AM33XX_IOPAD(0x910, PIN_INPUT | MUX_MODE0) /* mii1_rxer.mii1_rxer */ + AM33XX_IOPAD(0x914, PIN_OUTPUT | MUX_MODE0) /* mii1_txen.mii1_txen */ + AM33XX_IOPAD(0x918, PIN_INPUT | MUX_MODE0) /* mii1_rxdv.mii1_rxdv */ + AM33XX_IOPAD(0x91c, PIN_OUTPUT | MUX_MODE0) /* mii1_txd3.mii1_txd3 */ + AM33XX_IOPAD(0x920, PIN_OUTPUT | MUX_MODE0) /* mii1_txd2.mii1_txd2 */ + AM33XX_IOPAD(0x924, PIN_OUTPUT | MUX_MODE0) /* mii1_txd1.mii1_txd1 */ + AM33XX_IOPAD(0x928, PIN_OUTPUT | MUX_MODE0) /* mii1_txd0.mii1_txd0 */ + AM33XX_IOPAD(0x92c, PIN_INPUT | MUX_MODE0) /* mii1_txclk.mii1_txclk */ + AM33XX_IOPAD(0x930, PIN_INPUT | MUX_MODE0) /* mii1_rxclk.mii1_rxclk */ + AM33XX_IOPAD(0x934, PIN_INPUT | MUX_MODE0) /* mii1_rxd3.mii1_rxd3 */ + AM33XX_IOPAD(0x938, PIN_INPUT | MUX_MODE0) /* mii1_rxd2.mii1_rxd2 */ + AM33XX_IOPAD(0x93c, PIN_INPUT | MUX_MODE0) /* mii1_rxd1.mii1_rxd1 */ + AM33XX_IOPAD(0x940, PIN_INPUT | MUX_MODE0) /* mii1_rxd0.mii1_rxd0 */ + + /* Port 2 (emac1) */ + AM33XX_IOPAD(0x840, PIN_OUTPUT | MUX_MODE1) /* mii2_txen.gpmc_a0 */ + AM33XX_IOPAD(0x844, PIN_INPUT | MUX_MODE1) /* mii2_rxdv.gpmc_a1 */ + AM33XX_IOPAD(0x848, PIN_OUTPUT | MUX_MODE1) /* mii2_txd3.gpmc_a2 */ + AM33XX_IOPAD(0x84c, PIN_OUTPUT | MUX_MODE1) /* mii2_txd2.gpmc_a3 */ + AM33XX_IOPAD(0x850, PIN_OUTPUT | MUX_MODE1) /* mii2_txd1.gpmc_a4 */ + AM33XX_IOPAD(0x854, PIN_OUTPUT | MUX_MODE1) /* mii2_txd0.gpmc_a5 */ + AM33XX_IOPAD(0x858, PIN_INPUT | MUX_MODE1) /* mii2_txclk.gpmc_a6 */ + AM33XX_IOPAD(0x85c, PIN_INPUT | MUX_MODE1) /* mii2_rxclk.gpmc_a7 */ + AM33XX_IOPAD(0x860, PIN_INPUT | MUX_MODE1) /* mii2_rxd3.gpmc_a8 */ + AM33XX_IOPAD(0x864, PIN_INPUT | MUX_MODE1) /* mii2_rxd2.gpmc_a9 */ + AM33XX_IOPAD(0x868, PIN_INPUT | MUX_MODE1) /* mii2_rxd1.gpmc_a10 */ + AM33XX_IOPAD(0x86C, PIN_INPUT | MUX_MODE1) /* mii2_rxd0.gpmc_a11 */ + AM33XX_IOPAD(0x870, PIN_INPUT | MUX_MODE1) /* mii2_crs.gpmc_wait0 */ + AM33XX_IOPAD(0x874, PIN_INPUT | MUX_MODE1) /* mii2_rxer.gpmc_wpn */ + AM33XX_IOPAD(0x878, PIN_INPUT | MUX_MODE1) /* mii2_col.gpmc_ben1 */ + >; + }; + + davinci_mdio_default: davinci_mdio_default { + pinctrl-single,pins = < + AM33XX_IOPAD(0x948, PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* mdio_data.mdio_data */ + AM33XX_IOPAD(0x94c, PIN_OUTPUT_PULLUP | MUX_MODE0) /* mdio_clk.mdio_clk */ + >; + }; + + mmc1_pins: pinmux_mmc1_pins { + /* eMMC */ + pinctrl-single,pins = < + AM33XX_IOPAD(0x8f0, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat3 */ + AM33XX_IOPAD(0x8f4, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat2 */ + AM33XX_IOPAD(0x8f8, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat1 */ + AM33XX_IOPAD(0x8fc, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat0 */ + AM33XX_IOPAD(0x900, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_clk */ + AM33XX_IOPAD(0x904, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_cmd */ + >; + }; + + mmc2_pins: pinmux_mmc2_pins { + /* SD cardcage */ + pinctrl-single,pins = < + AM33XX_IOPAD(0x80c, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */ + AM33XX_IOPAD(0x808, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */ + AM33XX_IOPAD(0x804, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */ + AM33XX_IOPAD(0x800, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */ + AM33XX_IOPAD(0x880, PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn1.mmc1_clk */ + AM33XX_IOPAD(0x884, PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn2.mmc1_cmd */ + /* card change signal for frontpanel SD cardcage */ + AM33XX_IOPAD(0x890, PIN_INPUT | MUX_MODE7) /* gpmc_advn_ale.gpio2_2 */ + >; + }; + + lcd_pins_s0: lcd_pins_s0 { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8a0, PIN_OUTPUT | MUX_MODE0) /* lcd_data0.lcd_data0 */ + AM33XX_IOPAD(0x8a4, PIN_OUTPUT | MUX_MODE0) /* lcd_data1.lcd_data1 */ + AM33XX_IOPAD(0x8a8, PIN_OUTPUT | MUX_MODE0) /* lcd_data2.lcd_data2 */ + AM33XX_IOPAD(0x8ac, PIN_OUTPUT | MUX_MODE0) /* lcd_data3.lcd_data3 */ + AM33XX_IOPAD(0x8b0, PIN_OUTPUT | MUX_MODE0) /* lcd_data4.lcd_data4 */ + AM33XX_IOPAD(0x8b4, PIN_OUTPUT | MUX_MODE0) /* lcd_data5.lcd_data5 */ + AM33XX_IOPAD(0x8b8, PIN_OUTPUT | MUX_MODE0) /* lcd_data6.lcd_data6 */ + AM33XX_IOPAD(0x8bc, PIN_OUTPUT | MUX_MODE0) /* lcd_data7.lcd_data7 */ + AM33XX_IOPAD(0x8c0, PIN_OUTPUT | MUX_MODE0) /* lcd_data8.lcd_data8 */ + AM33XX_IOPAD(0x8c4, PIN_OUTPUT | MUX_MODE0) /* lcd_data9.lcd_data9 */ + AM33XX_IOPAD(0x8c8, PIN_OUTPUT | MUX_MODE0) /* lcd_data10.lcd_data10 */ + AM33XX_IOPAD(0x8cc, PIN_OUTPUT | MUX_MODE0) /* lcd_data11.lcd_data11 */ + AM33XX_IOPAD(0x8d0, PIN_OUTPUT | MUX_MODE0) /* lcd_data12.lcd_data12 */ + AM33XX_IOPAD(0x8d4, PIN_OUTPUT | MUX_MODE0) /* lcd_data13.lcd_data13 */ + AM33XX_IOPAD(0x8d8, PIN_OUTPUT | MUX_MODE0) /* lcd_data14.lcd_data14 */ + AM33XX_IOPAD(0x8dc, PIN_OUTPUT | MUX_MODE0) /* lcd_data15.lcd_data15 */ + AM33XX_IOPAD(0x8e0, PIN_OUTPUT | MUX_MODE0) /* lcd_vsync.lcd_vsync */ + AM33XX_IOPAD(0x8e4, PIN_OUTPUT | MUX_MODE0) /* lcd_hsync.lcd_hsync */ + AM33XX_IOPAD(0x8e8, PIN_OUTPUT | MUX_MODE0) /* lcd_pclk.lcd_pclk */ + AM33XX_IOPAD(0x8ec, PIN_OUTPUT | MUX_MODE0) /* lcd_ac_bias_en.lcd_ac_bias_en */ + >; + }; + + dcan0_pins: pinmux_dcan0_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x978, PIN_OUTPUT | MUX_MODE2) /* uart1_ctsn.d_can0_tx */ + AM33XX_IOPAD(0x97c, PIN_INPUT_PULLDOWN | MUX_MODE2) /* uart1_rtsn.d_can0_rx */ + >; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; + + rts-gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; + rs485-rts-active-high; + rs485-rts-delay = <0 0>; + linux,rs485-enabled-at-boot-time; + + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; + + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + + status = "okay"; + clock-frequency = <400000>; + + tps: tps@2d { + reg = <0x2d>; + }; + + m2_eeprom: m2_eeprom@50 { + compatible = "atmel,24c256"; + reg = <0x50>; + status = "okay"; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + + status = "okay"; + clock-frequency = <100000>; + + board_24aa025e48: board_24aa025e48@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + }; + + backplane_24aa025e48: backplane_24aa025e48@53 { + compatible = "atmel,24c02"; + reg = <0x53>; + }; + + pca9532: pca9532@60 { + compatible = "nxp,pca9532"; + reg = <0x60>; + psc0 = <0x97>; + pwm0 = <0x80>; + psc1 = <0x97>; + pwm1 = <0x10>; + + run.red@0 { + type = ; + }; + run.green@1 { + type = ; + default-state = "on"; + }; + s2.red@2 { + type = ; + }; + s2.green@3 { + type = ; + }; + s1.yellow@4 { + type = ; + }; + s1.green@5 { + type = ; + }; + }; + + pca9530: pca9530@61 { + compatible = "nxp,pca9530"; + reg = <0x61>; + + tft-panel@0 { + type = ; + linux,default-trigger = "backlight"; + default-state = "on"; + }; + }; + + mcp79400: mcp79400@6f { + compatible = "microchip,mcp7940x"; + reg = <0x6f>; + }; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + + status = "okay"; + clock-frequency = <100000>; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_pins>; + ti,pindir-d0-out-d1-in; + status = "okay"; + + cfaf240320a032t { + compatible = "orisetech,otm3225a"; + reg = <0>; + spi-max-frequency = <1000000>; + // SPI mode 3 + spi-cpol; + spi-cpha; + status = "okay"; + }; +}; + +&usb { + status = "okay"; +}; + +&usb_ctrl_mod { + status = "okay"; +}; + +&usb0_phy { + status = "okay"; +}; + +&usb1_phy { + status = "okay"; +}; + +&usb0 { + status = "okay"; +}; + +&usb1 { + status = "okay"; +}; + +&cppi41dma { + status = "okay"; +}; + +/* + * Disable soc's rtc as we have no VBAT for it. This makes the board + * rtc (Microchip MCP79400) the default rtc device 'rtc0'. + */ +&rtc { + status = "disabled"; +}; + +&lcdc { + status = "okay"; +}; + +&elm { + status = "okay"; +}; + +#include "tps65910.dtsi" + +&tps { + vcc1-supply = <&vbat>; + vcc2-supply = <&vbat>; + vcc3-supply = <&vbat>; + vcc4-supply = <&vbat>; + vcc5-supply = <&vbat>; + vcc6-supply = <&vbat>; + vcc7-supply = <&vbat>; + vccio-supply = <&vbat>; + + regulators { + vrtc_reg: regulator@0 { + regulator-name = "ldo_vrtc"; + regulator-always-on; + }; + + vio_reg: regulator@1 { + regulator-name = "buck_vdd_ddr"; + regulator-always-on; + }; + + vdd1_reg: regulator@2 { + /* VDD_MPU voltage limits */ + regulator-name = "buck_vdd_mpu"; + regulator-min-microvolt = <912500>; + regulator-max-microvolt = <1312500>; + regulator-boot-on; + regulator-always-on; + }; + + vdd2_reg: regulator@3 { + /* VDD_CORE voltage limits */ + regulator-name = "buck_vdd_core"; + regulator-min-microvolt = <912500>; + regulator-max-microvolt = <1150000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd3_reg: regulator@4 { + regulator-name = "boost_res"; + regulator-always-on; + }; + + vdig1_reg: regulator@5 { + regulator-name = "ldo_vdig1"; + regulator-always-on; + }; + + vdig2_reg: regulator@6 { + regulator-name = "ldo_vdig2"; + regulator-always-on; + }; + + vpll_reg: regulator@7 { + regulator-name = "ldo_vpll"; + regulator-always-on; + }; + + vdac_reg: regulator@8 { + regulator-name = "ldo_vdac"; + regulator-always-on; + }; + + vaux1_reg: regulator@9 { + regulator-name = "ldo_vaux1"; + regulator-always-on; + }; + + vaux2_reg: regulator@10 { + regulator-name = "ldo_vaux2"; + regulator-always-on; + }; + + vaux33_reg: regulator@11 { + regulator-name = "ldo_vaux33"; + regulator-always-on; + }; + + vmmc_reg: regulator@12 { + regulator-name = "ldo_vmmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vbb_reg: regulator@13 { + regulator-name = "bat_vbb"; + }; + }; +}; + +&mac { + pinctrl-names = "default"; + pinctrl-0 = <&cpsw_default>; + dual_emac; /* no switch, two distinct MACs */ + status = "okay"; +}; + +&davinci_mdio { + pinctrl-names = "default"; + pinctrl-0 = <&davinci_mdio_default>; + status = "okay"; +}; + +&cpsw_emac0 { + phy_id = <&davinci_mdio>, <0>; + phy-mode = "mii"; + dual_emac_res_vlan = <1>; +}; + +&cpsw_emac1 { + phy_id = <&davinci_mdio>, <1>; + phy-mode = "mii"; + dual_emac_res_vlan = <2>; +}; + +&tscadc { + status = "okay"; + tsc { + ti,wires = <4>; + ti,x-plate-resistance = <200>; + ti,coordinate-readouts = <5>; + ti,wire-config = <0x01 0x10 0x22 0x33>; + ti,charge-delay = <0x400>; + }; + + adc { + ti,adc-channels = <4 5 6 7>; + }; +}; + +&mmc1 { + status = "okay"; + vmmc-supply = <&vmmc_reg>; + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins>; + non-removable; +}; + +&mmc2 { + status = "okay"; + vmmc-supply = <&vmmc_reg>; + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc2_pins>; + cd-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>; +}; + +&sham { + status = "okay"; +}; + +&aes { + status = "okay"; +}; + +&dcan0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&dcan0_pins>; +}; -- cgit v1.3-14-g43fede From 6d7bdd328da42cd08bc98f6d1bcc402c68eed33e Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 11 Feb 2018 22:15:05 +0100 Subject: ARM: dts: omap4-droid4: update touchscreen Update the Droid 4 devicetree to properly describe the reset GPIO. Also rename the node to touchscreen instead of tsp, which seems to be commonly used for touchscreens. Signed-off-by: Sebastian Reichel Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/omap4-droid4-xt894.dts | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/arch/arm/boot/dts/omap4-droid4-xt894.dts b/arch/arm/boot/dts/omap4-droid4-xt894.dts index b21084da490b..65c2cc6a9a30 100644 --- a/arch/arm/boot/dts/omap4-droid4-xt894.dts +++ b/arch/arm/boot/dts/omap4-droid4-xt894.dts @@ -124,13 +124,6 @@ }; &gpio6 { - touchscreen_reset { - gpio-hog; - gpios = <13 0>; - output-high; - line-name = "touchscreen-reset"; - }; - pwm8: dmtimer-pwm-8 { pinctrl-names = "default"; pinctrl-0 = <&vibrator_direction_pin>; @@ -362,22 +355,18 @@ }; }; -/* - * REVISIT: Add gpio173 reset pin handling to the driver, see gpio-hog above. - * If the GPIO reset is used, we probably need to have /lib/firmware/maxtouch.fw - * available. See "mxt-app" and "droid4-touchscreen-firmware" tools for more - * information. - */ &i2c2 { - tsp@4a { + touchscreen@4a { compatible = "atmel,maxtouch"; reg = <0x4a>; pinctrl-names = "default"; pinctrl-0 = <&touchscreen_pins>; + reset-gpios = <&gpio6 13 GPIO_ACTIVE_HIGH>; /* gpio173 */ + /* gpio_183 with sys_nirq2 pad as wakeup */ - interrupts-extended = <&gpio6 23 IRQ_TYPE_EDGE_FALLING - &omap4_pmx_core 0x160>; + interrupts-extended = <&gpio6 23 IRQ_TYPE_EDGE_FALLING>, + <&omap4_pmx_core 0x160>; interrupt-names = "irq", "wakeup"; wakeup-source; }; @@ -435,6 +424,7 @@ touchscreen_pins: pinmux_touchscreen_pins { pinctrl-single,pins = < + OMAP4_IOPAD(0x180, PIN_OUTPUT | MUX_MODE3) OMAP4_IOPAD(0x1a0, PIN_INPUT_PULLUP | MUX_MODE3) >; }; -- cgit v1.3-14-g43fede From ef1e7b45c97398bdc8edcfe627b335c4a0dd3cb0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 9 Feb 2018 14:28:02 +0530 Subject: ARM: dts: omap: Remove "cooling-{min|max}-level" for CPU nodes The "cooling-min-level" and "cooling-max-level" properties are not parsed by any part of the kernel currently and the max cooling state of a CPU cooling device is found by referring to the cpufreq table instead. Remove the unused properties from the CPU nodes. Signed-off-by: Viresh Kumar Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/dra7.dtsi | 2 -- arch/arm/boot/dts/omap443x.dtsi | 2 -- arch/arm/boot/dts/omap4460.dtsi | 2 -- arch/arm/boot/dts/omap5.dtsi | 2 -- 4 files changed, 8 deletions(-) diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi index e4a420f16800..f4ddd86f2c77 100644 --- a/arch/arm/boot/dts/dra7.dtsi +++ b/arch/arm/boot/dts/dra7.dtsi @@ -92,8 +92,6 @@ clock-latency = <300000>; /* From omap-cpufreq driver */ /* cooling options */ - cooling-min-level = <0>; - cooling-max-level = <2>; #cooling-cells = <2>; /* min followed by max */ vbb-supply = <&abb_mpu>; diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi index 03c8ad91ddac..cbcdcb4e7d1c 100644 --- a/arch/arm/boot/dts/omap443x.dtsi +++ b/arch/arm/boot/dts/omap443x.dtsi @@ -24,8 +24,6 @@ clock-latency = <300000>; /* From legacy driver */ /* cooling options */ - cooling-min-level = <0>; - cooling-max-level = <3>; #cooling-cells = <2>; /* min followed by max */ }; }; diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi index c43f2a2d0a1e..ad97493e4e46 100644 --- a/arch/arm/boot/dts/omap4460.dtsi +++ b/arch/arm/boot/dts/omap4460.dtsi @@ -22,8 +22,6 @@ clock-latency = <300000>; /* From legacy driver */ /* cooling options */ - cooling-min-level = <0>; - cooling-max-level = <2>; #cooling-cells = <2>; /* min followed by max */ }; }; diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi index 35d4298da83d..511606c17a04 100644 --- a/arch/arm/boot/dts/omap5.dtsi +++ b/arch/arm/boot/dts/omap5.dtsi @@ -55,8 +55,6 @@ clock-latency = <300000>; /* From omap-cpufreq driver */ /* cooling options */ - cooling-min-level = <0>; - cooling-max-level = <2>; #cooling-cells = <2>; /* min followed by max */ }; cpu@1 { -- cgit v1.3-14-g43fede From ddd6a9d928931f2dfe61029e55faee21f7de2768 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 14 Feb 2018 09:35:20 -0800 Subject: ARM: OMAP2+: Include types.h directly for hwmod data This is needed in preparation for dropping some unused headers that indirectly include types.h. Tested-by: Suman Anna Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c | 2 ++ arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c | 2 ++ arch/arm/mach-omap2/omap_hwmod_81xx_data.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c index 4b094cb384cb..4f0a1d4dd7fa 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c @@ -9,6 +9,8 @@ * published by the Free Software Foundation. */ +#include + #include #include #include diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c index 4bcf9f3e1544..4161e369d216 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c @@ -14,6 +14,8 @@ * GNU General Public License for more details. */ +#include + #include #include #include diff --git a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c index 84f118280a0e..64c5a1299003 100644 --- a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c @@ -15,6 +15,8 @@ * */ +#include + #include #include #include -- cgit v1.3-14-g43fede From a0e37da2a542acb6069b9e10d8aba3be4e5204d7 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 12 Feb 2018 19:32:37 -0600 Subject: ARM: OMAP2+: Cleanup omap_gpio_dev_attr usage The omap_gpio_dev_attr data was used to supply instance-specific data for legacy non-DT devices. The GPIO legacy device support has been cleaned up in commit 14944934f8ac ("ARM: OMAP2+: Remove legacy gpio code") a while ago and this data is therefore no longer needed. So, cleanup the structure and all the associated data in various hwmod data files. Signed-off-by: Suman Anna Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/omap_hwmod_2430_data.c | 1 - arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c | 13 +------------ arch/arm/mach-omap2/omap_hwmod_33xx_43xx_common_data.h | 1 - arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c | 9 --------- arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 2 -- arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 13 ------------- arch/arm/mach-omap2/omap_hwmod_43xx_data.c | 4 ---- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 13 ------------- arch/arm/mach-omap2/omap_hwmod_54xx_data.c | 15 --------------- arch/arm/mach-omap2/omap_hwmod_7xx_data.c | 15 --------------- arch/arm/mach-omap2/omap_hwmod_81xx_data.c | 8 -------- arch/arm/mach-omap2/omap_hwmod_common_data.h | 1 - include/linux/platform_data/gpio-omap.h | 5 ----- 13 files changed, 1 insertion(+), 99 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c index 013b26b305d2..1f696bec9962 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -134,7 +134,6 @@ static struct omap_hwmod omap2430_gpio5_hwmod = { }, }, .class = &omap2xxx_gpio_hwmod_class, - .dev_attr = &omap2xxx_gpio_dev_attr, }; /* dma attributes */ diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c index 4f0a1d4dd7fa..e1a6ebe3a8ac 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c @@ -10,9 +10,8 @@ */ #include - -#include #include + #include #include @@ -570,12 +569,6 @@ struct omap_hwmod omap2xxx_dss_venc_hwmod = { .flags = HWMOD_NO_IDLEST, }; -/* gpio dev_attr */ -struct omap_gpio_dev_attr omap2xxx_gpio_dev_attr = { - .bank_width = 32, - .dbck_flag = false, -}; - /* gpio1 */ struct omap_hwmod omap2xxx_gpio1_hwmod = { .name = "gpio1", @@ -589,7 +582,6 @@ struct omap_hwmod omap2xxx_gpio1_hwmod = { }, }, .class = &omap2xxx_gpio_hwmod_class, - .dev_attr = &omap2xxx_gpio_dev_attr, }; /* gpio2 */ @@ -605,7 +597,6 @@ struct omap_hwmod omap2xxx_gpio2_hwmod = { }, }, .class = &omap2xxx_gpio_hwmod_class, - .dev_attr = &omap2xxx_gpio_dev_attr, }; /* gpio3 */ @@ -621,7 +612,6 @@ struct omap_hwmod omap2xxx_gpio3_hwmod = { }, }, .class = &omap2xxx_gpio_hwmod_class, - .dev_attr = &omap2xxx_gpio_dev_attr, }; /* gpio4 */ @@ -637,7 +627,6 @@ struct omap_hwmod omap2xxx_gpio4_hwmod = { }, }, .class = &omap2xxx_gpio_hwmod_class, - .dev_attr = &omap2xxx_gpio_dev_attr, }; /* mcspi1 */ diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_common_data.h b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_common_data.h index 434bd1a77229..bbda6887388b 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_common_data.h +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_common_data.h @@ -139,7 +139,6 @@ extern struct omap_hwmod_class am33xx_epwmss_hwmod_class; extern struct omap_hwmod_class am33xx_ehrpwm_hwmod_class; extern struct omap_hwmod_class am33xx_spi_hwmod_class; -extern struct omap_gpio_dev_attr gpio_dev_attr; extern struct omap2_mcspi_dev_attr mcspi_attrib; void omap_hwmod_am33xx_reg(void); diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c index 4161e369d216..db8cd550a5bd 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c @@ -16,7 +16,6 @@ #include -#include #include #include #include "omap_hwmod.h" @@ -539,11 +538,6 @@ struct omap_hwmod_class am33xx_gpio_hwmod_class = { .rev = 2, }; -struct omap_gpio_dev_attr gpio_dev_attr = { - .bank_width = 32, - .dbck_flag = true, -}; - /* gpio1 */ static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { { .role = "dbclk", .clk = "gpio1_dbclk" }, @@ -562,7 +556,6 @@ struct omap_hwmod am33xx_gpio1_hwmod = { }, .opt_clks = gpio1_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio1_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio2 */ @@ -583,7 +576,6 @@ struct omap_hwmod am33xx_gpio2_hwmod = { }, .opt_clks = gpio2_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio2_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio3 */ @@ -604,7 +596,6 @@ struct omap_hwmod am33xx_gpio3_hwmod = { }, .opt_clks = gpio3_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio3_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpmc */ diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 4d16b15bb0cf..232d03045c6d 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -17,7 +17,6 @@ #include #include "omap_hwmod.h" -#include #include #include "omap_hwmod_common_data.h" @@ -252,7 +251,6 @@ static struct omap_hwmod am33xx_gpio0_hwmod = { }, .opt_clks = gpio0_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio0_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* lcdc */ diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 1a2f2242e31b..c7ff7560f47a 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -17,7 +17,6 @@ #include #include -#include #include #include @@ -769,12 +768,6 @@ static struct omap_hwmod_class omap3xxx_gpio_hwmod_class = { .rev = 1, }; -/* gpio_dev_attr */ -static struct omap_gpio_dev_attr gpio_dev_attr = { - .bank_width = 32, - .dbck_flag = true, -}; - /* gpio1 */ static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { { .role = "dbclk", .clk = "gpio1_dbck", }, @@ -794,7 +787,6 @@ static struct omap_hwmod omap3xxx_gpio1_hwmod = { }, }, .class = &omap3xxx_gpio_hwmod_class, - .dev_attr = &gpio_dev_attr, }; /* gpio2 */ @@ -816,7 +808,6 @@ static struct omap_hwmod omap3xxx_gpio2_hwmod = { }, }, .class = &omap3xxx_gpio_hwmod_class, - .dev_attr = &gpio_dev_attr, }; /* gpio3 */ @@ -838,7 +829,6 @@ static struct omap_hwmod omap3xxx_gpio3_hwmod = { }, }, .class = &omap3xxx_gpio_hwmod_class, - .dev_attr = &gpio_dev_attr, }; /* gpio4 */ @@ -860,7 +850,6 @@ static struct omap_hwmod omap3xxx_gpio4_hwmod = { }, }, .class = &omap3xxx_gpio_hwmod_class, - .dev_attr = &gpio_dev_attr, }; /* gpio5 */ @@ -883,7 +872,6 @@ static struct omap_hwmod omap3xxx_gpio5_hwmod = { }, }, .class = &omap3xxx_gpio_hwmod_class, - .dev_attr = &gpio_dev_attr, }; /* gpio6 */ @@ -906,7 +894,6 @@ static struct omap_hwmod omap3xxx_gpio6_hwmod = { }, }, .class = &omap3xxx_gpio_hwmod_class, - .dev_attr = &gpio_dev_attr, }; /* dma attributes */ diff --git a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c index afbce1f6f641..4f31ce899869 100644 --- a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c @@ -14,7 +14,6 @@ * GNU General Public License for more details. */ -#include #include #include "omap_hwmod.h" #include "omap_hwmod_33xx_43xx_common_data.h" @@ -107,7 +106,6 @@ static struct omap_hwmod am43xx_gpio0_hwmod = { }, .opt_clks = gpio0_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio0_opt_clks), - .dev_attr = &gpio_dev_attr, }; static struct omap_hwmod_class_sysconfig am43xx_synctimer_sysc = { @@ -288,7 +286,6 @@ static struct omap_hwmod am43xx_gpio4_hwmod = { }, .opt_clks = gpio4_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio4_opt_clks), - .dev_attr = &gpio_dev_attr, }; static struct omap_hwmod_opt_clk gpio5_opt_clks[] = { @@ -309,7 +306,6 @@ static struct omap_hwmod am43xx_gpio5_hwmod = { }, .opt_clks = gpio5_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio5_opt_clks), - .dev_attr = &gpio_dev_attr, }; static struct omap_hwmod_class am43xx_ocp2scp_hwmod_class = { diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index a1901c22a0f0..3afb7333b800 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -21,7 +21,6 @@ */ #include -#include #include #include #include @@ -1083,12 +1082,6 @@ static struct omap_hwmod_class omap44xx_gpio_hwmod_class = { .rev = 2, }; -/* gpio dev_attr */ -static struct omap_gpio_dev_attr gpio_dev_attr = { - .bank_width = 32, - .dbck_flag = true, -}; - /* gpio1 */ static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { { .role = "dbclk", .clk = "gpio1_dbclk" }, @@ -1108,7 +1101,6 @@ static struct omap_hwmod omap44xx_gpio1_hwmod = { }, .opt_clks = gpio1_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio1_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio2 */ @@ -1131,7 +1123,6 @@ static struct omap_hwmod omap44xx_gpio2_hwmod = { }, .opt_clks = gpio2_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio2_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio3 */ @@ -1154,7 +1145,6 @@ static struct omap_hwmod omap44xx_gpio3_hwmod = { }, .opt_clks = gpio3_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio3_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio4 */ @@ -1177,7 +1167,6 @@ static struct omap_hwmod omap44xx_gpio4_hwmod = { }, .opt_clks = gpio4_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio4_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio5 */ @@ -1200,7 +1189,6 @@ static struct omap_hwmod omap44xx_gpio5_hwmod = { }, .opt_clks = gpio5_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio5_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio6 */ @@ -1223,7 +1211,6 @@ static struct omap_hwmod omap44xx_gpio6_hwmod = { }, .opt_clks = gpio6_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio6_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c index 988e7eaa1330..593b4bc92d99 100644 --- a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c @@ -18,7 +18,6 @@ */ #include -#include #include #include #include @@ -627,12 +626,6 @@ static struct omap_hwmod_class omap54xx_gpio_hwmod_class = { .rev = 2, }; -/* gpio dev_attr */ -static struct omap_gpio_dev_attr gpio_dev_attr = { - .bank_width = 32, - .dbck_flag = true, -}; - /* gpio1 */ static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { { .role = "dbclk", .clk = "gpio1_dbclk" }, @@ -652,7 +645,6 @@ static struct omap_hwmod omap54xx_gpio1_hwmod = { }, .opt_clks = gpio1_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio1_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio2 */ @@ -675,7 +667,6 @@ static struct omap_hwmod omap54xx_gpio2_hwmod = { }, .opt_clks = gpio2_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio2_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio3 */ @@ -698,7 +689,6 @@ static struct omap_hwmod omap54xx_gpio3_hwmod = { }, .opt_clks = gpio3_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio3_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio4 */ @@ -721,7 +711,6 @@ static struct omap_hwmod omap54xx_gpio4_hwmod = { }, .opt_clks = gpio4_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio4_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio5 */ @@ -744,7 +733,6 @@ static struct omap_hwmod omap54xx_gpio5_hwmod = { }, .opt_clks = gpio5_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio5_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio6 */ @@ -767,7 +755,6 @@ static struct omap_hwmod omap54xx_gpio6_hwmod = { }, .opt_clks = gpio6_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio6_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio7 */ @@ -790,7 +777,6 @@ static struct omap_hwmod omap54xx_gpio7_hwmod = { }, .opt_clks = gpio7_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio7_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio8 */ @@ -813,7 +799,6 @@ static struct omap_hwmod omap54xx_gpio8_hwmod = { }, .opt_clks = gpio8_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio8_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c index 4c2a05b1bd19..523e89498fd3 100644 --- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c @@ -18,7 +18,6 @@ */ #include -#include #include #include #include @@ -818,12 +817,6 @@ static struct omap_hwmod_class dra7xx_gpio_hwmod_class = { .rev = 2, }; -/* gpio dev_attr */ -static struct omap_gpio_dev_attr gpio_dev_attr = { - .bank_width = 32, - .dbck_flag = true, -}; - /* gpio1 */ static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { { .role = "dbclk", .clk = "gpio1_dbclk" }, @@ -844,7 +837,6 @@ static struct omap_hwmod dra7xx_gpio1_hwmod = { }, .opt_clks = gpio1_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio1_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio2 */ @@ -867,7 +859,6 @@ static struct omap_hwmod dra7xx_gpio2_hwmod = { }, .opt_clks = gpio2_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio2_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio3 */ @@ -890,7 +881,6 @@ static struct omap_hwmod dra7xx_gpio3_hwmod = { }, .opt_clks = gpio3_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio3_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio4 */ @@ -913,7 +903,6 @@ static struct omap_hwmod dra7xx_gpio4_hwmod = { }, .opt_clks = gpio4_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio4_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio5 */ @@ -936,7 +925,6 @@ static struct omap_hwmod dra7xx_gpio5_hwmod = { }, .opt_clks = gpio5_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio5_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio6 */ @@ -959,7 +947,6 @@ static struct omap_hwmod dra7xx_gpio6_hwmod = { }, .opt_clks = gpio6_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio6_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio7 */ @@ -982,7 +969,6 @@ static struct omap_hwmod dra7xx_gpio7_hwmod = { }, .opt_clks = gpio7_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio7_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* gpio8 */ @@ -1005,7 +991,6 @@ static struct omap_hwmod dra7xx_gpio8_hwmod = { }, .opt_clks = gpio8_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio8_opt_clks), - .dev_attr = &gpio_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c index 64c5a1299003..d1f4dc47a3ae 100644 --- a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c @@ -17,7 +17,6 @@ #include -#include #include #include #include @@ -490,11 +489,6 @@ static struct omap_hwmod_class dm81xx_gpio_hwmod_class = { .rev = 2, }; -static struct omap_gpio_dev_attr gpio_dev_attr = { - .bank_width = 32, - .dbck_flag = true, -}; - static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { { .role = "dbclk", .clk = "sysclk18_ck" }, }; @@ -512,7 +506,6 @@ static struct omap_hwmod dm81xx_gpio1_hwmod = { }, .opt_clks = gpio1_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio1_opt_clks), - .dev_attr = &gpio_dev_attr, }; static struct omap_hwmod_ocp_if dm81xx_l4_ls__gpio1 = { @@ -539,7 +532,6 @@ static struct omap_hwmod dm81xx_gpio2_hwmod = { }, .opt_clks = gpio2_opt_clks, .opt_clks_cnt = ARRAY_SIZE(gpio2_opt_clks), - .dev_attr = &gpio_dev_attr, }; static struct omap_hwmod_ocp_if dm81xx_l4_ls__gpio2 = { diff --git a/arch/arm/mach-omap2/omap_hwmod_common_data.h b/arch/arm/mach-omap2/omap_hwmod_common_data.h index 29a52df2de26..56dbaca9a728 100644 --- a/arch/arm/mach-omap2/omap_hwmod_common_data.h +++ b/arch/arm/mach-omap2/omap_hwmod_common_data.h @@ -19,7 +19,6 @@ #include "display.h" /* Common IP block data across OMAP2xxx */ -extern struct omap_gpio_dev_attr omap2xxx_gpio_dev_attr; extern struct omap_hwmod omap2xxx_l3_main_hwmod; extern struct omap_hwmod omap2xxx_l4_core_hwmod; extern struct omap_hwmod omap2xxx_l4_wkup_hwmod; diff --git a/include/linux/platform_data/gpio-omap.h b/include/linux/platform_data/gpio-omap.h index cb2618147c34..8612855691b2 100644 --- a/include/linux/platform_data/gpio-omap.h +++ b/include/linux/platform_data/gpio-omap.h @@ -157,11 +157,6 @@ #define OMAP_MPUIO(nr) (OMAP_MAX_GPIO_LINES + (nr)) #define OMAP_GPIO_IS_MPUIO(nr) ((nr) >= OMAP_MAX_GPIO_LINES) -struct omap_gpio_dev_attr { - int bank_width; /* GPIO bank width */ - bool dbck_flag; /* dbck required or not - True for OMAP3&4 */ -}; - struct omap_gpio_reg_offs { u16 revision; u16 direction; -- cgit v1.3-14-g43fede From 5297e1d7679560212355ecb88b700be6c0bc378b Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 12 Feb 2018 19:32:38 -0600 Subject: ARM: OMAP2+: Cleanup omap_i2c_dev_attr usage The omap_i2c_dev_attr data was used to supply instance-specific data for legacy non-DT devices. The I2C legacy device support has been cleaned up in commit 65fa3e719f36 ("ARM: OMAP2+: Remove legacy i2c.c platform init code") and this data is therefore no longer needed. So, cleanup the structure and all the associated data in various hwmod data files. The i2c-omap.h header is still needed because of the need for various OMAP_I2C_IP_VERSION_x macros. Signed-off-by: Suman Anna Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/i2c.h | 13 ------------- arch/arm/mach-omap2/omap_hwmod_2420_data.c | 9 --------- arch/arm/mach-omap2/omap_hwmod_2430_data.c | 8 -------- .../mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c | 7 ------- arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 20 -------------------- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 8 -------- arch/arm/mach-omap2/omap_hwmod_54xx_data.c | 10 ---------- arch/arm/mach-omap2/omap_hwmod_7xx_data.c | 10 ---------- 8 files changed, 85 deletions(-) diff --git a/arch/arm/mach-omap2/i2c.h b/arch/arm/mach-omap2/i2c.h index 42b6f2e7d190..f76ff790b435 100644 --- a/arch/arm/mach-omap2/i2c.h +++ b/arch/arm/mach-omap2/i2c.h @@ -24,19 +24,6 @@ #ifndef __MACH_OMAP2_I2C_H #define __MACH_OMAP2_I2C_H -/** - * i2c_dev_attr - OMAP I2C controller device attributes for omap_hwmod - * @fifo_depth: total controller FIFO size (in bytes) - * @flags: differences in hardware support capability - * - * @fifo_depth represents what exists on the hardware, not what is - * actually configured at runtime by the device driver. - */ -struct omap_i2c_dev_attr { - u8 fifo_depth; - u32 flags; -}; - int omap_i2c_reset(struct omap_hwmod *oh); #endif /* __MACH_OMAP2_I2C_H */ diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c index 0afb014b211f..1fdfd4568899 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c @@ -97,13 +97,6 @@ static struct omap_hwmod_class i2c_class = { .reset = &omap_i2c_reset, }; -static struct omap_i2c_dev_attr i2c_dev_attr = { - .flags = OMAP_I2C_FLAG_NO_FIFO | - OMAP_I2C_FLAG_SIMPLE_CLOCK | - OMAP_I2C_FLAG_16BIT_DATA_REG | - OMAP_I2C_FLAG_BUS_SHIFT_2, -}; - /* I2C1 */ static struct omap_hwmod omap2420_i2c1_hwmod = { .name = "i2c1", @@ -116,7 +109,6 @@ static struct omap_hwmod omap2420_i2c1_hwmod = { }, }, .class = &i2c_class, - .dev_attr = &i2c_dev_attr, /* * From mach-omap2/pm24xx.c: "Putting MPU into the WFI state * while a transfer is active seems to cause the I2C block to @@ -137,7 +129,6 @@ static struct omap_hwmod omap2420_i2c2_hwmod = { }, }, .class = &i2c_class, - .dev_attr = &i2c_dev_attr, .flags = HWMOD_16BIT_REG, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c index 1f696bec9962..9b5a18b35ff6 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -75,12 +75,6 @@ static struct omap_hwmod_class i2c_class = { .reset = &omap_i2c_reset, }; -static struct omap_i2c_dev_attr i2c_dev_attr = { - .fifo_depth = 8, /* bytes */ - .flags = OMAP_I2C_FLAG_BUS_SHIFT_2 | - OMAP_I2C_FLAG_FORCE_19200_INT_CLK, -}; - /* I2C1 */ static struct omap_hwmod omap2430_i2c1_hwmod = { .name = "i2c1", @@ -102,7 +96,6 @@ static struct omap_hwmod omap2430_i2c1_hwmod = { }, }, .class = &i2c_class, - .dev_attr = &i2c_dev_attr, }; /* I2C2 */ @@ -118,7 +111,6 @@ static struct omap_hwmod omap2430_i2c2_hwmod = { }, }, .class = &i2c_class, - .dev_attr = &i2c_dev_attr, }; /* gpio5 */ diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c index db8cd550a5bd..b1118b1124d9 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c @@ -647,10 +647,6 @@ static struct omap_hwmod_class i2c_class = { .reset = &omap_i2c_reset, }; -static struct omap_i2c_dev_attr i2c_dev_attr = { - .flags = OMAP_I2C_FLAG_BUS_SHIFT_NONE, -}; - /* i2c1 */ struct omap_hwmod am33xx_i2c1_hwmod = { .name = "i2c1", @@ -663,7 +659,6 @@ struct omap_hwmod am33xx_i2c1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c1 */ @@ -678,7 +673,6 @@ struct omap_hwmod am33xx_i2c2_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c3 */ @@ -693,7 +687,6 @@ struct omap_hwmod am33xx_i2c3_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index c7ff7560f47a..1bb33d8bd2ac 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -682,11 +682,6 @@ static struct omap_hwmod omap3xxx_dss_venc_hwmod = { }; /* I2C1 */ -static struct omap_i2c_dev_attr i2c1_dev_attr = { - .fifo_depth = 8, /* bytes */ - .flags = OMAP_I2C_FLAG_BUS_SHIFT_2, -}; - static struct omap_hwmod omap3xxx_i2c1_hwmod = { .name = "i2c1", .flags = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT, @@ -699,15 +694,9 @@ static struct omap_hwmod omap3xxx_i2c1_hwmod = { }, }, .class = &i2c_class, - .dev_attr = &i2c1_dev_attr, }; /* I2C2 */ -static struct omap_i2c_dev_attr i2c2_dev_attr = { - .fifo_depth = 8, /* bytes */ - .flags = OMAP_I2C_FLAG_BUS_SHIFT_2, -}; - static struct omap_hwmod omap3xxx_i2c2_hwmod = { .name = "i2c2", .flags = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT, @@ -720,17 +709,9 @@ static struct omap_hwmod omap3xxx_i2c2_hwmod = { }, }, .class = &i2c_class, - .dev_attr = &i2c2_dev_attr, }; /* I2C3 */ -static struct omap_i2c_dev_attr i2c3_dev_attr = { - .fifo_depth = 64, /* bytes */ - .flags = OMAP_I2C_FLAG_BUS_SHIFT_2, -}; - - - static struct omap_hwmod omap3xxx_i2c3_hwmod = { .name = "i2c3", .flags = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT, @@ -743,7 +724,6 @@ static struct omap_hwmod omap3xxx_i2c3_hwmod = { }, }, .class = &i2c_class, - .dev_attr = &i2c3_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 3afb7333b800..cb39901d611b 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -1381,10 +1381,6 @@ static struct omap_hwmod_class omap44xx_i2c_hwmod_class = { .reset = &omap_i2c_reset, }; -static struct omap_i2c_dev_attr i2c_dev_attr = { - .flags = OMAP_I2C_FLAG_BUS_SHIFT_NONE, -}; - /* i2c1 */ static struct omap_hwmod omap44xx_i2c1_hwmod = { .name = "i2c1", @@ -1399,7 +1395,6 @@ static struct omap_hwmod omap44xx_i2c1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c2 */ @@ -1416,7 +1411,6 @@ static struct omap_hwmod omap44xx_i2c2_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c3 */ @@ -1433,7 +1427,6 @@ static struct omap_hwmod omap44xx_i2c3_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c4 */ @@ -1450,7 +1443,6 @@ static struct omap_hwmod omap44xx_i2c4_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c index 593b4bc92d99..250524fed202 100644 --- a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c @@ -824,11 +824,6 @@ static struct omap_hwmod_class omap54xx_i2c_hwmod_class = { .rev = OMAP_I2C_IP_VERSION_2, }; -/* i2c dev_attr */ -static struct omap_i2c_dev_attr i2c_dev_attr = { - .flags = OMAP_I2C_FLAG_BUS_SHIFT_NONE, -}; - /* i2c1 */ static struct omap_hwmod omap54xx_i2c1_hwmod = { .name = "i2c1", @@ -843,7 +838,6 @@ static struct omap_hwmod omap54xx_i2c1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c2 */ @@ -860,7 +854,6 @@ static struct omap_hwmod omap54xx_i2c2_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c3 */ @@ -877,7 +870,6 @@ static struct omap_hwmod omap54xx_i2c3_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c4 */ @@ -894,7 +886,6 @@ static struct omap_hwmod omap54xx_i2c4_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c5 */ @@ -911,7 +902,6 @@ static struct omap_hwmod omap54xx_i2c5_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c index 523e89498fd3..a78f9285cbe8 100644 --- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c @@ -1090,11 +1090,6 @@ static struct omap_hwmod_class dra7xx_i2c_hwmod_class = { .rev = OMAP_I2C_IP_VERSION_2, }; -/* i2c dev_attr */ -static struct omap_i2c_dev_attr i2c_dev_attr = { - .flags = OMAP_I2C_FLAG_BUS_SHIFT_NONE, -}; - /* i2c1 */ static struct omap_hwmod dra7xx_i2c1_hwmod = { .name = "i2c1", @@ -1109,7 +1104,6 @@ static struct omap_hwmod dra7xx_i2c1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c2 */ @@ -1126,7 +1120,6 @@ static struct omap_hwmod dra7xx_i2c2_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c3 */ @@ -1143,7 +1136,6 @@ static struct omap_hwmod dra7xx_i2c3_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c4 */ @@ -1160,7 +1152,6 @@ static struct omap_hwmod dra7xx_i2c4_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* i2c5 */ @@ -1177,7 +1168,6 @@ static struct omap_hwmod dra7xx_i2c5_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &i2c_dev_attr, }; /* -- cgit v1.3-14-g43fede From cc7e3fb6413a79e22a144f48361ec4be0b888614 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 12 Feb 2018 19:32:39 -0600 Subject: ARM: OMAP2+: Cleanup omap_timer_capability_dev_attr usage The omap_timer_capability_dev_attr data was used to supply instance specific capabilities (like always-on, PWM functionality or ability to interrupt DSP cores) for legacy non-DT devices. These capabilities are now provided through device-tree properties. The legacy device support has been cleaned up in commit 8d39ff3d1696 ("ARM: OMAP2+: Remove unused legacy code for timer") and this data is therefore no longer needed. So, cleanup the structure and all the associated data in various hwmod data files. While at this, remove the stale header in hwmod data files that already do not have any timer capability data. Cc: Keerthy Signed-off-by: Suman Anna Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/omap_hwmod_2420_data.c | 1 - arch/arm/mach-omap2/omap_hwmod_2430_data.c | 1 - arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c | 37 ---------------------- arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 36 --------------------- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 29 ----------------- arch/arm/mach-omap2/omap_hwmod_54xx_data.c | 1 - arch/arm/mach-omap2/omap_hwmod_7xx_data.c | 1 - arch/arm/mach-omap2/omap_hwmod_81xx_data.c | 14 -------- arch/arm/plat-omap/include/plat/dmtimer.h | 4 --- 9 files changed, 124 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c index 1fdfd4568899..9f16b1b8d882 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c @@ -16,7 +16,6 @@ #include #include #include -#include #include "omap_hwmod.h" #include "l3_2xxx.h" diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c index 9b5a18b35ff6..409f0e634707 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -18,7 +18,6 @@ #include #include #include -#include #include "omap_hwmod.h" #include "l3_2xxx.h" diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c index e1a6ebe3a8ac..00a5ae5df82d 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c @@ -12,7 +12,6 @@ #include #include -#include #include #include "omap_hwmod.h" @@ -221,23 +220,7 @@ struct omap_hwmod omap2xxx_iva_hwmod = { .class = &iva_hwmod_class, }; -/* always-on timers dev attribute */ -static struct omap_timer_capability_dev_attr capability_alwon_dev_attr = { - .timer_capability = OMAP_TIMER_ALWON, -}; - -/* pwm timers dev attribute */ -static struct omap_timer_capability_dev_attr capability_pwm_dev_attr = { - .timer_capability = OMAP_TIMER_HAS_PWM, -}; - -/* timers with DSP interrupt dev attribute */ -static struct omap_timer_capability_dev_attr capability_dsp_dev_attr = { - .timer_capability = OMAP_TIMER_HAS_DSP_IRQ, -}; - /* timer1 */ - struct omap_hwmod omap2xxx_timer1_hwmod = { .name = "timer1", .main_clk = "gpt1_fck", @@ -248,13 +231,11 @@ struct omap_hwmod omap2xxx_timer1_hwmod = { .idlest_idle_bit = OMAP24XX_ST_GPT1_SHIFT, }, }, - .dev_attr = &capability_alwon_dev_attr, .class = &omap2xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; /* timer2 */ - struct omap_hwmod omap2xxx_timer2_hwmod = { .name = "timer2", .main_clk = "gpt2_fck", @@ -270,7 +251,6 @@ struct omap_hwmod omap2xxx_timer2_hwmod = { }; /* timer3 */ - struct omap_hwmod omap2xxx_timer3_hwmod = { .name = "timer3", .main_clk = "gpt3_fck", @@ -286,7 +266,6 @@ struct omap_hwmod omap2xxx_timer3_hwmod = { }; /* timer4 */ - struct omap_hwmod omap2xxx_timer4_hwmod = { .name = "timer4", .main_clk = "gpt4_fck", @@ -302,7 +281,6 @@ struct omap_hwmod omap2xxx_timer4_hwmod = { }; /* timer5 */ - struct omap_hwmod omap2xxx_timer5_hwmod = { .name = "timer5", .main_clk = "gpt5_fck", @@ -313,13 +291,11 @@ struct omap_hwmod omap2xxx_timer5_hwmod = { .idlest_idle_bit = OMAP24XX_ST_GPT5_SHIFT, }, }, - .dev_attr = &capability_dsp_dev_attr, .class = &omap2xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; /* timer6 */ - struct omap_hwmod omap2xxx_timer6_hwmod = { .name = "timer6", .main_clk = "gpt6_fck", @@ -330,13 +306,11 @@ struct omap_hwmod omap2xxx_timer6_hwmod = { .idlest_idle_bit = OMAP24XX_ST_GPT6_SHIFT, }, }, - .dev_attr = &capability_dsp_dev_attr, .class = &omap2xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; /* timer7 */ - struct omap_hwmod omap2xxx_timer7_hwmod = { .name = "timer7", .main_clk = "gpt7_fck", @@ -347,13 +321,11 @@ struct omap_hwmod omap2xxx_timer7_hwmod = { .idlest_idle_bit = OMAP24XX_ST_GPT7_SHIFT, }, }, - .dev_attr = &capability_dsp_dev_attr, .class = &omap2xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; /* timer8 */ - struct omap_hwmod omap2xxx_timer8_hwmod = { .name = "timer8", .main_clk = "gpt8_fck", @@ -364,13 +336,11 @@ struct omap_hwmod omap2xxx_timer8_hwmod = { .idlest_idle_bit = OMAP24XX_ST_GPT8_SHIFT, }, }, - .dev_attr = &capability_dsp_dev_attr, .class = &omap2xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; /* timer9 */ - struct omap_hwmod omap2xxx_timer9_hwmod = { .name = "timer9", .main_clk = "gpt9_fck", @@ -381,13 +351,11 @@ struct omap_hwmod omap2xxx_timer9_hwmod = { .idlest_idle_bit = OMAP24XX_ST_GPT9_SHIFT, }, }, - .dev_attr = &capability_pwm_dev_attr, .class = &omap2xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; /* timer10 */ - struct omap_hwmod omap2xxx_timer10_hwmod = { .name = "timer10", .main_clk = "gpt10_fck", @@ -398,13 +366,11 @@ struct omap_hwmod omap2xxx_timer10_hwmod = { .idlest_idle_bit = OMAP24XX_ST_GPT10_SHIFT, }, }, - .dev_attr = &capability_pwm_dev_attr, .class = &omap2xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; /* timer11 */ - struct omap_hwmod omap2xxx_timer11_hwmod = { .name = "timer11", .main_clk = "gpt11_fck", @@ -415,13 +381,11 @@ struct omap_hwmod omap2xxx_timer11_hwmod = { .idlest_idle_bit = OMAP24XX_ST_GPT11_SHIFT, }, }, - .dev_attr = &capability_pwm_dev_attr, .class = &omap2xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; /* timer12 */ - struct omap_hwmod omap2xxx_timer12_hwmod = { .name = "timer12", .main_clk = "gpt12_fck", @@ -432,7 +396,6 @@ struct omap_hwmod omap2xxx_timer12_hwmod = { .idlest_idle_bit = OMAP24XX_ST_GPT12_SHIFT, }, }, - .dev_attr = &capability_pwm_dev_attr, .class = &omap2xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 1bb33d8bd2ac..7515119cab64 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -24,7 +24,6 @@ #include "l4_3xxx.h" #include #include -#include #include "soc.h" #include "omap_hwmod.h" @@ -154,31 +153,6 @@ static struct omap_hwmod_class omap3xxx_timer_hwmod_class = { .sysc = &omap3xxx_timer_sysc, }; -/* secure timers dev attribute */ -static struct omap_timer_capability_dev_attr capability_secure_dev_attr = { - .timer_capability = OMAP_TIMER_ALWON | OMAP_TIMER_SECURE, -}; - -/* always-on timers dev attribute */ -static struct omap_timer_capability_dev_attr capability_alwon_dev_attr = { - .timer_capability = OMAP_TIMER_ALWON, -}; - -/* pwm timers dev attribute */ -static struct omap_timer_capability_dev_attr capability_pwm_dev_attr = { - .timer_capability = OMAP_TIMER_HAS_PWM, -}; - -/* timers with DSP interrupt dev attribute */ -static struct omap_timer_capability_dev_attr capability_dsp_dev_attr = { - .timer_capability = OMAP_TIMER_HAS_DSP_IRQ, -}; - -/* pwm timers with DSP interrupt dev attribute */ -static struct omap_timer_capability_dev_attr capability_dsp_pwm_dev_attr = { - .timer_capability = OMAP_TIMER_HAS_DSP_IRQ | OMAP_TIMER_HAS_PWM, -}; - /* timer1 */ static struct omap_hwmod omap3xxx_timer1_hwmod = { .name = "timer1", @@ -190,7 +164,6 @@ static struct omap_hwmod omap3xxx_timer1_hwmod = { .idlest_idle_bit = OMAP3430_ST_GPT1_SHIFT, }, }, - .dev_attr = &capability_alwon_dev_attr, .class = &omap3xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; @@ -251,7 +224,6 @@ static struct omap_hwmod omap3xxx_timer5_hwmod = { .idlest_idle_bit = OMAP3430_ST_GPT5_SHIFT, }, }, - .dev_attr = &capability_dsp_dev_attr, .class = &omap3xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; @@ -267,7 +239,6 @@ static struct omap_hwmod omap3xxx_timer6_hwmod = { .idlest_idle_bit = OMAP3430_ST_GPT6_SHIFT, }, }, - .dev_attr = &capability_dsp_dev_attr, .class = &omap3xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; @@ -283,7 +254,6 @@ static struct omap_hwmod omap3xxx_timer7_hwmod = { .idlest_idle_bit = OMAP3430_ST_GPT7_SHIFT, }, }, - .dev_attr = &capability_dsp_dev_attr, .class = &omap3xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; @@ -299,7 +269,6 @@ static struct omap_hwmod omap3xxx_timer8_hwmod = { .idlest_idle_bit = OMAP3430_ST_GPT8_SHIFT, }, }, - .dev_attr = &capability_dsp_pwm_dev_attr, .class = &omap3xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; @@ -315,7 +284,6 @@ static struct omap_hwmod omap3xxx_timer9_hwmod = { .idlest_idle_bit = OMAP3430_ST_GPT9_SHIFT, }, }, - .dev_attr = &capability_pwm_dev_attr, .class = &omap3xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; @@ -331,7 +299,6 @@ static struct omap_hwmod omap3xxx_timer10_hwmod = { .idlest_idle_bit = OMAP3430_ST_GPT10_SHIFT, }, }, - .dev_attr = &capability_pwm_dev_attr, .class = &omap3xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; @@ -347,13 +314,11 @@ static struct omap_hwmod omap3xxx_timer11_hwmod = { .idlest_idle_bit = OMAP3430_ST_GPT11_SHIFT, }, }, - .dev_attr = &capability_pwm_dev_attr, .class = &omap3xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; /* timer12 */ - static struct omap_hwmod omap3xxx_timer12_hwmod = { .name = "timer12", .main_clk = "gpt12_fck", @@ -364,7 +329,6 @@ static struct omap_hwmod omap3xxx_timer12_hwmod = { .idlest_idle_bit = OMAP3430_ST_GPT12_SHIFT, }, }, - .dev_attr = &capability_secure_dev_attr, .class = &omap3xxx_timer_hwmod_class, .flags = HWMOD_SET_DEFAULT_CLOCKACT, }; diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index cb39901d611b..70eb826d5f65 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -29,7 +29,6 @@ #include #include -#include #include "omap_hwmod.h" #include "omap_hwmod_common_data.h" @@ -2526,26 +2525,6 @@ static struct omap_hwmod_class omap44xx_timer_hwmod_class = { .sysc = &omap44xx_timer_sysc, }; -/* always-on timers dev attribute */ -static struct omap_timer_capability_dev_attr capability_alwon_dev_attr = { - .timer_capability = OMAP_TIMER_ALWON, -}; - -/* pwm timers dev attribute */ -static struct omap_timer_capability_dev_attr capability_pwm_dev_attr = { - .timer_capability = OMAP_TIMER_HAS_PWM, -}; - -/* timers with DSP interrupt dev attribute */ -static struct omap_timer_capability_dev_attr capability_dsp_dev_attr = { - .timer_capability = OMAP_TIMER_HAS_DSP_IRQ, -}; - -/* pwm timers with DSP interrupt dev attribute */ -static struct omap_timer_capability_dev_attr capability_dsp_pwm_dev_attr = { - .timer_capability = OMAP_TIMER_HAS_DSP_IRQ | OMAP_TIMER_HAS_PWM, -}; - /* timer1 */ static struct omap_hwmod omap44xx_timer1_hwmod = { .name = "timer1", @@ -2560,7 +2539,6 @@ static struct omap_hwmod omap44xx_timer1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_alwon_dev_attr, }; /* timer2 */ @@ -2622,7 +2600,6 @@ static struct omap_hwmod omap44xx_timer5_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_dsp_dev_attr, }; /* timer6 */ @@ -2638,7 +2615,6 @@ static struct omap_hwmod omap44xx_timer6_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_dsp_dev_attr, }; /* timer7 */ @@ -2654,7 +2630,6 @@ static struct omap_hwmod omap44xx_timer7_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_dsp_dev_attr, }; /* timer8 */ @@ -2670,7 +2645,6 @@ static struct omap_hwmod omap44xx_timer8_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_dsp_pwm_dev_attr, }; /* timer9 */ @@ -2686,7 +2660,6 @@ static struct omap_hwmod omap44xx_timer9_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_pwm_dev_attr, }; /* timer10 */ @@ -2703,7 +2676,6 @@ static struct omap_hwmod omap44xx_timer10_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_pwm_dev_attr, }; /* timer11 */ @@ -2719,7 +2691,6 @@ static struct omap_hwmod omap44xx_timer11_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_pwm_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c index 250524fed202..2275789854dc 100644 --- a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c @@ -25,7 +25,6 @@ #include #include #include -#include #include "omap_hwmod.h" #include "omap_hwmod_common_data.h" diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c index a78f9285cbe8..d0f1fd65d01f 100644 --- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c @@ -25,7 +25,6 @@ #include #include #include -#include #include "omap_hwmod.h" #include "omap_hwmod_common_data.h" diff --git a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c index d1f4dc47a3ae..333a896c0c9a 100644 --- a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c @@ -19,7 +19,6 @@ #include #include -#include #include "omap_hwmod_common_data.h" #include "cm81xx.h" @@ -648,15 +647,10 @@ static struct omap_hwmod_class dm816x_timer_hwmod_class = { .sysc = &dm816x_timer_sysc, }; -static struct omap_timer_capability_dev_attr capability_alwon_dev_attr = { - .timer_capability = OMAP_TIMER_ALWON, -}; - static struct omap_hwmod dm814x_timer1_hwmod = { .name = "timer1", .clkdm_name = "alwon_l3s_clkdm", .main_clk = "timer1_fck", - .dev_attr = &capability_alwon_dev_attr, .class = &dm816x_timer_hwmod_class, .flags = HWMOD_NO_IDLEST, }; @@ -678,7 +672,6 @@ static struct omap_hwmod dm816x_timer1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_alwon_dev_attr, .class = &dm816x_timer_hwmod_class, }; @@ -693,7 +686,6 @@ static struct omap_hwmod dm814x_timer2_hwmod = { .name = "timer2", .clkdm_name = "alwon_l3s_clkdm", .main_clk = "timer2_fck", - .dev_attr = &capability_alwon_dev_attr, .class = &dm816x_timer_hwmod_class, .flags = HWMOD_NO_IDLEST, }; @@ -715,7 +707,6 @@ static struct omap_hwmod dm816x_timer2_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_alwon_dev_attr, .class = &dm816x_timer_hwmod_class, }; @@ -736,7 +727,6 @@ static struct omap_hwmod dm816x_timer3_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_alwon_dev_attr, .class = &dm816x_timer_hwmod_class, }; @@ -757,7 +747,6 @@ static struct omap_hwmod dm816x_timer4_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_alwon_dev_attr, .class = &dm816x_timer_hwmod_class, }; @@ -778,7 +767,6 @@ static struct omap_hwmod dm816x_timer5_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_alwon_dev_attr, .class = &dm816x_timer_hwmod_class, }; @@ -799,7 +787,6 @@ static struct omap_hwmod dm816x_timer6_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_alwon_dev_attr, .class = &dm816x_timer_hwmod_class, }; @@ -820,7 +807,6 @@ static struct omap_hwmod dm816x_timer7_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &capability_alwon_dev_attr, .class = &dm816x_timer_hwmod_class, }; diff --git a/arch/arm/plat-omap/include/plat/dmtimer.h b/arch/arm/plat-omap/include/plat/dmtimer.h index dd79f3005cdf..8b7d8a6e02cc 100644 --- a/arch/arm/plat-omap/include/plat/dmtimer.h +++ b/arch/arm/plat-omap/include/plat/dmtimer.h @@ -75,10 +75,6 @@ */ #define OMAP_TIMER_ERRATA_I103_I767 0x80000000 -struct omap_timer_capability_dev_attr { - u32 timer_capability; -}; - struct timer_regs { u32 tidr; u32 tier; -- cgit v1.3-14-g43fede From 1cddc364584e76c16354d34326c671aac2a23e4f Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 12 Feb 2018 19:32:40 -0600 Subject: ARM: OMAP2+: Cleanup omap2_spi_dev_attr and other legacy data The omap2_spi_dev_attr data was used to supply instance-specific data for legacy non-DT devices. The SPI legacy device support including the usage of the hwmod class revision data has been dropped in commit 6f3ab009a178 ("ARM: OMAP2+: Remove unused legacy code for device init") and this data is therefore no longer needed. So, cleanup the structure and all the associated data in various hwmod data files. Signed-off-by: Suman Anna Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/omap_hwmod_2420_data.c | 1 - arch/arm/mach-omap2/omap_hwmod_2430_data.c | 6 ----- arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c | 13 ----------- .../mach-omap2/omap_hwmod_33xx_43xx_common_data.h | 2 -- .../mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c | 7 ------ arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 2 -- arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 26 ---------------------- arch/arm/mach-omap2/omap_hwmod_43xx_data.c | 4 ---- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 22 ------------------ arch/arm/mach-omap2/omap_hwmod_54xx_data.c | 26 ---------------------- arch/arm/mach-omap2/omap_hwmod_7xx_data.c | 26 ---------------------- arch/arm/mach-omap2/omap_hwmod_81xx_data.c | 7 ------ include/linux/platform_data/spi-omap2-mcspi.h | 8 ------- 13 files changed, 150 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c index 9f16b1b8d882..fe66cf247874 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c @@ -14,7 +14,6 @@ */ #include -#include #include #include "omap_hwmod.h" diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c index 409f0e634707..cdbd09b21168 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include "omap_hwmod.h" @@ -157,10 +156,6 @@ static struct omap_hwmod omap2430_mailbox_hwmod = { }; /* mcspi3 */ -static struct omap2_mcspi_dev_attr omap_mcspi3_dev_attr = { - .num_chipselect = 2, -}; - static struct omap_hwmod omap2430_mcspi3_hwmod = { .name = "mcspi3", .main_clk = "mcspi3_fck", @@ -172,7 +167,6 @@ static struct omap_hwmod omap2430_mcspi3_hwmod = { }, }, .class = &omap2xxx_mcspi_class, - .dev_attr = &omap_mcspi3_dev_attr, }; /* usbhsotg */ diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c index 00a5ae5df82d..5345919a81f8 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c @@ -12,8 +12,6 @@ #include #include -#include - #include "omap_hwmod.h" #include "omap_hwmod_common_data.h" #include "cm-regbits-24xx.h" @@ -159,7 +157,6 @@ static struct omap_hwmod_class_sysconfig omap2xxx_mcspi_sysc = { struct omap_hwmod_class omap2xxx_mcspi_class = { .name = "mcspi", .sysc = &omap2xxx_mcspi_sysc, - .rev = OMAP2_MCSPI_REV, }; /* @@ -593,10 +590,6 @@ struct omap_hwmod omap2xxx_gpio4_hwmod = { }; /* mcspi1 */ -static struct omap2_mcspi_dev_attr omap_mcspi1_dev_attr = { - .num_chipselect = 4, -}; - struct omap_hwmod omap2xxx_mcspi1_hwmod = { .name = "mcspi1", .main_clk = "mcspi1_fck", @@ -608,14 +601,9 @@ struct omap_hwmod omap2xxx_mcspi1_hwmod = { }, }, .class = &omap2xxx_mcspi_class, - .dev_attr = &omap_mcspi1_dev_attr, }; /* mcspi2 */ -static struct omap2_mcspi_dev_attr omap_mcspi2_dev_attr = { - .num_chipselect = 2, -}; - struct omap_hwmod omap2xxx_mcspi2_hwmod = { .name = "mcspi2", .main_clk = "mcspi2_fck", @@ -627,7 +615,6 @@ struct omap_hwmod omap2xxx_mcspi2_hwmod = { }, }, .class = &omap2xxx_mcspi_class, - .dev_attr = &omap_mcspi2_dev_attr, }; static struct omap_hwmod_class omap2xxx_counter_hwmod_class = { diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_common_data.h b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_common_data.h index bbda6887388b..6f81d7a4fec1 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_common_data.h +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_common_data.h @@ -139,8 +139,6 @@ extern struct omap_hwmod_class am33xx_epwmss_hwmod_class; extern struct omap_hwmod_class am33xx_ehrpwm_hwmod_class; extern struct omap_hwmod_class am33xx_spi_hwmod_class; -extern struct omap2_mcspi_dev_attr mcspi_attrib; - void omap_hwmod_am33xx_reg(void); void omap_hwmod_am43xx_reg(void); diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c index b1118b1124d9..5efe91c6e95b 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c @@ -17,7 +17,6 @@ #include #include -#include #include "omap_hwmod.h" #include "i2c.h" #include "wd_timer.h" @@ -879,13 +878,9 @@ static struct omap_hwmod_class_sysconfig am33xx_mcspi_sysc = { struct omap_hwmod_class am33xx_spi_hwmod_class = { .name = "mcspi", .sysc = &am33xx_mcspi_sysc, - .rev = OMAP4_MCSPI_REV, }; /* spi0 */ -struct omap2_mcspi_dev_attr mcspi_attrib = { - .num_chipselect = 2, -}; struct omap_hwmod am33xx_spi0_hwmod = { .name = "spi0", .class = &am33xx_spi_hwmod_class, @@ -896,7 +891,6 @@ struct omap_hwmod am33xx_spi0_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi_attrib, }; /* spi1 */ @@ -910,7 +904,6 @@ struct omap_hwmod am33xx_spi1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi_attrib, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 232d03045c6d..53e1ac3724f2 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -17,8 +17,6 @@ #include #include "omap_hwmod.h" -#include - #include "omap_hwmod_common_data.h" #include "control.h" diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 7515119cab64..23008cb35140 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -23,7 +23,6 @@ #include "l3_3xxx.h" #include "l4_3xxx.h" #include -#include #include "soc.h" #include "omap_hwmod.h" @@ -1189,14 +1188,9 @@ static struct omap_hwmod_class_sysconfig omap34xx_mcspi_sysc = { static struct omap_hwmod_class omap34xx_mcspi_class = { .name = "mcspi", .sysc = &omap34xx_mcspi_sysc, - .rev = OMAP3_MCSPI_REV, }; /* mcspi1 */ -static struct omap2_mcspi_dev_attr omap_mcspi1_dev_attr = { - .num_chipselect = 4, -}; - static struct omap_hwmod omap34xx_mcspi1 = { .name = "mcspi1", .main_clk = "mcspi1_fck", @@ -1208,14 +1202,9 @@ static struct omap_hwmod omap34xx_mcspi1 = { }, }, .class = &omap34xx_mcspi_class, - .dev_attr = &omap_mcspi1_dev_attr, }; /* mcspi2 */ -static struct omap2_mcspi_dev_attr omap_mcspi2_dev_attr = { - .num_chipselect = 2, -}; - static struct omap_hwmod omap34xx_mcspi2 = { .name = "mcspi2", .main_clk = "mcspi2_fck", @@ -1227,16 +1216,9 @@ static struct omap_hwmod omap34xx_mcspi2 = { }, }, .class = &omap34xx_mcspi_class, - .dev_attr = &omap_mcspi2_dev_attr, }; /* mcspi3 */ - - -static struct omap2_mcspi_dev_attr omap_mcspi3_dev_attr = { - .num_chipselect = 2, -}; - static struct omap_hwmod omap34xx_mcspi3 = { .name = "mcspi3", .main_clk = "mcspi3_fck", @@ -1248,16 +1230,9 @@ static struct omap_hwmod omap34xx_mcspi3 = { }, }, .class = &omap34xx_mcspi_class, - .dev_attr = &omap_mcspi3_dev_attr, }; /* mcspi4 */ - - -static struct omap2_mcspi_dev_attr omap_mcspi4_dev_attr = { - .num_chipselect = 1, -}; - static struct omap_hwmod omap34xx_mcspi4 = { .name = "mcspi4", .main_clk = "mcspi4_fck", @@ -1269,7 +1244,6 @@ static struct omap_hwmod omap34xx_mcspi4 = { }, }, .class = &omap34xx_mcspi_class, - .dev_attr = &omap_mcspi4_dev_attr, }; /* usbhsotg */ diff --git a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c index 4f31ce899869..5f73b730d4fc 100644 --- a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c @@ -14,7 +14,6 @@ * GNU General Public License for more details. */ -#include #include "omap_hwmod.h" #include "omap_hwmod_33xx_43xx_common_data.h" #include "prcm43xx.h" @@ -237,7 +236,6 @@ static struct omap_hwmod am43xx_spi2_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi_attrib, }; static struct omap_hwmod am43xx_spi3_hwmod = { @@ -251,7 +249,6 @@ static struct omap_hwmod am43xx_spi3_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi_attrib, }; static struct omap_hwmod am43xx_spi4_hwmod = { @@ -265,7 +262,6 @@ static struct omap_hwmod am43xx_spi4_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi_attrib, }; static struct omap_hwmod_opt_clk gpio4_opt_clks[] = { diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 70eb826d5f65..5a313483b3b8 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -27,7 +27,6 @@ #include -#include #include #include "omap_hwmod.h" @@ -1838,14 +1837,9 @@ static struct omap_hwmod_class_sysconfig omap44xx_mcspi_sysc = { static struct omap_hwmod_class omap44xx_mcspi_hwmod_class = { .name = "mcspi", .sysc = &omap44xx_mcspi_sysc, - .rev = OMAP4_MCSPI_REV, }; /* mcspi1 */ -static struct omap2_mcspi_dev_attr mcspi1_dev_attr = { - .num_chipselect = 4, -}; - static struct omap_hwmod omap44xx_mcspi1_hwmod = { .name = "mcspi1", .class = &omap44xx_mcspi_hwmod_class, @@ -1858,14 +1852,9 @@ static struct omap_hwmod omap44xx_mcspi1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi1_dev_attr, }; /* mcspi2 */ -static struct omap2_mcspi_dev_attr mcspi2_dev_attr = { - .num_chipselect = 2, -}; - static struct omap_hwmod omap44xx_mcspi2_hwmod = { .name = "mcspi2", .class = &omap44xx_mcspi_hwmod_class, @@ -1878,14 +1867,9 @@ static struct omap_hwmod omap44xx_mcspi2_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi2_dev_attr, }; /* mcspi3 */ -static struct omap2_mcspi_dev_attr mcspi3_dev_attr = { - .num_chipselect = 2, -}; - static struct omap_hwmod omap44xx_mcspi3_hwmod = { .name = "mcspi3", .class = &omap44xx_mcspi_hwmod_class, @@ -1898,14 +1882,9 @@ static struct omap_hwmod omap44xx_mcspi3_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi3_dev_attr, }; /* mcspi4 */ -static struct omap2_mcspi_dev_attr mcspi4_dev_attr = { - .num_chipselect = 1, -}; - static struct omap_hwmod omap44xx_mcspi4_hwmod = { .name = "mcspi4", .class = &omap44xx_mcspi_hwmod_class, @@ -1918,7 +1897,6 @@ static struct omap_hwmod omap44xx_mcspi4_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi4_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c index 2275789854dc..f901b17bd73a 100644 --- a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c @@ -23,7 +23,6 @@ #include #include -#include #include #include "omap_hwmod.h" @@ -1123,15 +1122,9 @@ static struct omap_hwmod_class_sysconfig omap54xx_mcspi_sysc = { static struct omap_hwmod_class omap54xx_mcspi_hwmod_class = { .name = "mcspi", .sysc = &omap54xx_mcspi_sysc, - .rev = OMAP4_MCSPI_REV, }; /* mcspi1 */ -/* mcspi1 dev_attr */ -static struct omap2_mcspi_dev_attr mcspi1_dev_attr = { - .num_chipselect = 4, -}; - static struct omap_hwmod omap54xx_mcspi1_hwmod = { .name = "mcspi1", .class = &omap54xx_mcspi_hwmod_class, @@ -1144,15 +1137,9 @@ static struct omap_hwmod omap54xx_mcspi1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi1_dev_attr, }; /* mcspi2 */ -/* mcspi2 dev_attr */ -static struct omap2_mcspi_dev_attr mcspi2_dev_attr = { - .num_chipselect = 2, -}; - static struct omap_hwmod omap54xx_mcspi2_hwmod = { .name = "mcspi2", .class = &omap54xx_mcspi_hwmod_class, @@ -1165,15 +1152,9 @@ static struct omap_hwmod omap54xx_mcspi2_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi2_dev_attr, }; /* mcspi3 */ -/* mcspi3 dev_attr */ -static struct omap2_mcspi_dev_attr mcspi3_dev_attr = { - .num_chipselect = 2, -}; - static struct omap_hwmod omap54xx_mcspi3_hwmod = { .name = "mcspi3", .class = &omap54xx_mcspi_hwmod_class, @@ -1186,15 +1167,9 @@ static struct omap_hwmod omap54xx_mcspi3_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi3_dev_attr, }; /* mcspi4 */ -/* mcspi4 dev_attr */ -static struct omap2_mcspi_dev_attr mcspi4_dev_attr = { - .num_chipselect = 1, -}; - static struct omap_hwmod omap54xx_mcspi4_hwmod = { .name = "mcspi4", .class = &omap54xx_mcspi_hwmod_class, @@ -1207,7 +1182,6 @@ static struct omap_hwmod omap54xx_mcspi4_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi4_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c index d0f1fd65d01f..d66dc806425d 100644 --- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c @@ -23,7 +23,6 @@ #include #include -#include #include #include "omap_hwmod.h" @@ -1375,15 +1374,9 @@ static struct omap_hwmod_class_sysconfig dra7xx_mcspi_sysc = { static struct omap_hwmod_class dra7xx_mcspi_hwmod_class = { .name = "mcspi", .sysc = &dra7xx_mcspi_sysc, - .rev = OMAP4_MCSPI_REV, }; /* mcspi1 */ -/* mcspi1 dev_attr */ -static struct omap2_mcspi_dev_attr mcspi1_dev_attr = { - .num_chipselect = 4, -}; - static struct omap_hwmod dra7xx_mcspi1_hwmod = { .name = "mcspi1", .class = &dra7xx_mcspi_hwmod_class, @@ -1396,15 +1389,9 @@ static struct omap_hwmod dra7xx_mcspi1_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi1_dev_attr, }; /* mcspi2 */ -/* mcspi2 dev_attr */ -static struct omap2_mcspi_dev_attr mcspi2_dev_attr = { - .num_chipselect = 2, -}; - static struct omap_hwmod dra7xx_mcspi2_hwmod = { .name = "mcspi2", .class = &dra7xx_mcspi_hwmod_class, @@ -1417,15 +1404,9 @@ static struct omap_hwmod dra7xx_mcspi2_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi2_dev_attr, }; /* mcspi3 */ -/* mcspi3 dev_attr */ -static struct omap2_mcspi_dev_attr mcspi3_dev_attr = { - .num_chipselect = 2, -}; - static struct omap_hwmod dra7xx_mcspi3_hwmod = { .name = "mcspi3", .class = &dra7xx_mcspi_hwmod_class, @@ -1438,15 +1419,9 @@ static struct omap_hwmod dra7xx_mcspi3_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi3_dev_attr, }; /* mcspi4 */ -/* mcspi4 dev_attr */ -static struct omap2_mcspi_dev_attr mcspi4_dev_attr = { - .num_chipselect = 1, -}; - static struct omap_hwmod dra7xx_mcspi4_hwmod = { .name = "mcspi4", .class = &dra7xx_mcspi_hwmod_class, @@ -1459,7 +1434,6 @@ static struct omap_hwmod dra7xx_mcspi4_hwmod = { .modulemode = MODULEMODE_SWCTRL, }, }, - .dev_attr = &mcspi4_dev_attr, }; /* diff --git a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c index 333a896c0c9a..686655f884c1 100644 --- a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c @@ -18,7 +18,6 @@ #include #include -#include #include "omap_hwmod_common_data.h" #include "cm81xx.h" @@ -1118,11 +1117,6 @@ static struct omap_hwmod_class_sysconfig dm816x_mcspi_sysc = { static struct omap_hwmod_class dm816x_mcspi_class = { .name = "mcspi", .sysc = &dm816x_mcspi_sysc, - .rev = OMAP3_MCSPI_REV, -}; - -static struct omap2_mcspi_dev_attr dm816x_mcspi1_dev_attr = { - .num_chipselect = 4, }; static struct omap_hwmod dm81xx_mcspi1_hwmod = { @@ -1136,7 +1130,6 @@ static struct omap_hwmod dm81xx_mcspi1_hwmod = { }, }, .class = &dm816x_mcspi_class, - .dev_attr = &dm816x_mcspi1_dev_attr, }; static struct omap_hwmod_ocp_if dm81xx_l4_ls__mcspi1 = { diff --git a/include/linux/platform_data/spi-omap2-mcspi.h b/include/linux/platform_data/spi-omap2-mcspi.h index 13c83a25958a..0bf9fddb8306 100644 --- a/include/linux/platform_data/spi-omap2-mcspi.h +++ b/include/linux/platform_data/spi-omap2-mcspi.h @@ -2,10 +2,6 @@ #ifndef _OMAP2_MCSPI_H #define _OMAP2_MCSPI_H -#define OMAP2_MCSPI_REV 0 -#define OMAP3_MCSPI_REV 1 -#define OMAP4_MCSPI_REV 2 - #define OMAP4_MCSPI_REG_OFFSET 0x100 #define MCSPI_PINDIR_D0_IN_D1_OUT 0 @@ -17,10 +13,6 @@ struct omap2_mcspi_platform_config { unsigned int pin_dir:1; }; -struct omap2_mcspi_dev_attr { - unsigned short num_chipselect; -}; - struct omap2_mcspi_device_config { unsigned turbo_mode:1; -- cgit v1.3-14-g43fede From 0693036ca800ab471e8f28caeb3a9ac4d77af810 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 12 Feb 2018 19:32:41 -0600 Subject: ARM: OMAP2+: Cleanup omap_mcbsp_dev_attr and other legacy data The omap_mcbsp_dev_attr data was used to supply instance-specific data for legacy non-DT devices. The legacy McBSP device support including the usage of the hwmod class revision data has been dropped in commit 48f6693790aa ("ARM: OMAP2+: Remove unused legacy code for McBSP") and this data is therefore no longer needed. So, cleanup the structure and all the associated data in various hwmod data files. Cc: Peter Ujfalusi Signed-off-by: Suman Anna Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/omap_hwmod_2430_data.c | 2 -- arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 21 --------------------- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 3 --- arch/arm/mach-omap2/omap_hwmod_54xx_data.c | 2 -- arch/arm/mach-omap2/omap_hwmod_7xx_data.c | 1 - include/linux/platform_data/asoc-ti-mcbsp.h | 12 ------------ 6 files changed, 41 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c index cdbd09b21168..74eefd30518c 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -14,7 +14,6 @@ */ #include -#include #include #include @@ -223,7 +222,6 @@ static struct omap_hwmod_class_sysconfig omap2430_mcbsp_sysc = { static struct omap_hwmod_class omap2430_mcbsp_hwmod_class = { .name = "mcbsp", .sysc = &omap2430_mcbsp_sysc, - .rev = MCBSP_CONFIG_TYPE2, }; static struct omap_hwmod_opt_clk mcbsp_opt_clks[] = { diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 23008cb35140..23336b6c7125 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -22,7 +22,6 @@ #include #include "l3_3xxx.h" #include "l4_3xxx.h" -#include #include "soc.h" #include "omap_hwmod.h" @@ -896,7 +895,6 @@ static struct omap_hwmod_class_sysconfig omap3xxx_mcbsp_sysc = { static struct omap_hwmod_class omap3xxx_mcbsp_hwmod_class = { .name = "mcbsp", .sysc = &omap3xxx_mcbsp_sysc, - .rev = MCBSP_CONFIG_TYPE3, }; /* McBSP functional clock mapping */ @@ -911,7 +909,6 @@ static struct omap_hwmod_opt_clk mcbsp234_opt_clks[] = { }; /* mcbsp1 */ - static struct omap_hwmod omap3xxx_mcbsp1_hwmod = { .name = "mcbsp1", .class = &omap3xxx_mcbsp_hwmod_class, @@ -928,11 +925,6 @@ static struct omap_hwmod omap3xxx_mcbsp1_hwmod = { }; /* mcbsp2 */ - -static struct omap_mcbsp_dev_attr omap34xx_mcbsp2_dev_attr = { - .sidetone = "mcbsp2_sidetone", -}; - static struct omap_hwmod omap3xxx_mcbsp2_hwmod = { .name = "mcbsp2", .class = &omap3xxx_mcbsp_hwmod_class, @@ -946,15 +938,9 @@ static struct omap_hwmod omap3xxx_mcbsp2_hwmod = { }, .opt_clks = mcbsp234_opt_clks, .opt_clks_cnt = ARRAY_SIZE(mcbsp234_opt_clks), - .dev_attr = &omap34xx_mcbsp2_dev_attr, }; /* mcbsp3 */ - -static struct omap_mcbsp_dev_attr omap34xx_mcbsp3_dev_attr = { - .sidetone = "mcbsp3_sidetone", -}; - static struct omap_hwmod omap3xxx_mcbsp3_hwmod = { .name = "mcbsp3", .class = &omap3xxx_mcbsp_hwmod_class, @@ -968,12 +954,9 @@ static struct omap_hwmod omap3xxx_mcbsp3_hwmod = { }, .opt_clks = mcbsp234_opt_clks, .opt_clks_cnt = ARRAY_SIZE(mcbsp234_opt_clks), - .dev_attr = &omap34xx_mcbsp3_dev_attr, }; /* mcbsp4 */ - - static struct omap_hwmod omap3xxx_mcbsp4_hwmod = { .name = "mcbsp4", .class = &omap3xxx_mcbsp_hwmod_class, @@ -990,8 +973,6 @@ static struct omap_hwmod omap3xxx_mcbsp4_hwmod = { }; /* mcbsp5 */ - - static struct omap_hwmod omap3xxx_mcbsp5_hwmod = { .name = "mcbsp5", .class = &omap3xxx_mcbsp_hwmod_class, @@ -1020,7 +1001,6 @@ static struct omap_hwmod_class omap3xxx_mcbsp_sidetone_hwmod_class = { }; /* mcbsp2_sidetone */ - static struct omap_hwmod omap3xxx_mcbsp2_sidetone_hwmod = { .name = "mcbsp2_sidetone", .class = &omap3xxx_mcbsp_sidetone_hwmod_class, @@ -1029,7 +1009,6 @@ static struct omap_hwmod omap3xxx_mcbsp2_sidetone_hwmod = { }; /* mcbsp3_sidetone */ - static struct omap_hwmod omap3xxx_mcbsp3_sidetone_hwmod = { .name = "mcbsp3_sidetone", .class = &omap3xxx_mcbsp_sidetone_hwmod_class, diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 5a313483b3b8..e4f8ae9cd637 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -27,8 +27,6 @@ #include -#include - #include "omap_hwmod.h" #include "omap_hwmod_common_data.h" #include "cm1_44xx.h" @@ -1679,7 +1677,6 @@ static struct omap_hwmod_class_sysconfig omap44xx_mcbsp_sysc = { static struct omap_hwmod_class omap44xx_mcbsp_hwmod_class = { .name = "mcbsp", .sysc = &omap44xx_mcbsp_sysc, - .rev = MCBSP_CONFIG_TYPE4, }; /* mcbsp1 */ diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c index f901b17bd73a..c72cd84b07ec 100644 --- a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c @@ -23,7 +23,6 @@ #include #include -#include #include "omap_hwmod.h" #include "omap_hwmod_common_data.h" @@ -985,7 +984,6 @@ static struct omap_hwmod_class_sysconfig omap54xx_mcbsp_sysc = { static struct omap_hwmod_class omap54xx_mcbsp_hwmod_class = { .name = "mcbsp", .sysc = &omap54xx_mcbsp_sysc, - .rev = MCBSP_CONFIG_TYPE4, }; /* mcbsp1 */ diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c index d66dc806425d..62352d1e6361 100644 --- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c @@ -23,7 +23,6 @@ #include #include -#include #include "omap_hwmod.h" #include "omap_hwmod_common_data.h" diff --git a/include/linux/platform_data/asoc-ti-mcbsp.h b/include/linux/platform_data/asoc-ti-mcbsp.h index e684543254f3..e319d0a2ec82 100644 --- a/include/linux/platform_data/asoc-ti-mcbsp.h +++ b/include/linux/platform_data/asoc-ti-mcbsp.h @@ -25,10 +25,6 @@ #include #include -#define MCBSP_CONFIG_TYPE2 0x2 -#define MCBSP_CONFIG_TYPE3 0x3 -#define MCBSP_CONFIG_TYPE4 0x4 - /* Platform specific configuration */ struct omap_mcbsp_ops { void (*request)(unsigned int); @@ -47,14 +43,6 @@ struct omap_mcbsp_platform_data { int (*force_ick_on)(struct clk *clk, bool force_on); }; -/** - * omap_mcbsp_dev_attr - OMAP McBSP device attributes for omap_hwmod - * @sidetone: name of the sidetone device - */ -struct omap_mcbsp_dev_attr { - const char *sidetone; -}; - void omap3_mcbsp_init_pdata_callback(struct omap_mcbsp_platform_data *pdata); #endif -- cgit v1.3-14-g43fede From 9138f89e7bb4b83ea0722f9912b96e236ece54cb Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 12 Feb 2018 19:32:42 -0600 Subject: ARM: OMAP: Move plat/i2c.h into mach-omap1 folder The current contents of plat/i2c.h are only relevant for OMAP1 platforms, as all the equivalent functions for OMAP2 has been dropped in commit 65fa3e719f36 ("ARM: OMAP2+: Remove legacy i2c.c platform init code"), and which has also moved the left-over code from plat-omap into mach-omap1. The hwmod is a concept only applicable for OMAP2 platforms, and the omap_i2c_reset() is already declared in mach-omap2/i2c.h. So, move the current plat/i2c.h header file into the mach-omap1 folder, and adjust the header usage accordingly. Signed-off-by: Suman Anna Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/common.h | 3 +- arch/arm/mach-omap1/i2c.c | 2 -- arch/arm/mach-omap1/i2c.h | 50 +++++++++++++++++++++++++++++++++ arch/arm/mach-omap2/i2c.h | 4 +-- arch/arm/plat-omap/include/plat/i2c.h | 53 ----------------------------------- 5 files changed, 53 insertions(+), 59 deletions(-) create mode 100644 arch/arm/mach-omap1/i2c.h delete mode 100644 arch/arm/plat-omap/include/plat/i2c.h diff --git a/arch/arm/mach-omap1/common.h b/arch/arm/mach-omap1/common.h index 65bb6e8085de..d83ff257eaa8 100644 --- a/arch/arm/mach-omap1/common.h +++ b/arch/arm/mach-omap1/common.h @@ -32,11 +32,10 @@ #include -#include - #include #include "soc.h" +#include "i2c.h" #if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) void omap7xx_map_io(void); diff --git a/arch/arm/mach-omap1/i2c.c b/arch/arm/mach-omap1/i2c.c index 32f6c53367bf..5bdf3c4190f9 100644 --- a/arch/arm/mach-omap1/i2c.c +++ b/arch/arm/mach-omap1/i2c.c @@ -24,8 +24,6 @@ #include #include "soc.h" -#include - #define OMAP_I2C_SIZE 0x3f #define OMAP1_I2C_BASE 0xfffb3800 diff --git a/arch/arm/mach-omap1/i2c.h b/arch/arm/mach-omap1/i2c.h new file mode 100644 index 000000000000..54a2bce7879e --- /dev/null +++ b/arch/arm/mach-omap1/i2c.h @@ -0,0 +1,50 @@ +/* + * Helper module for board specific I2C bus registration + * + * Copyright (C) 2009 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __ARCH_ARM_MACH_OMAP1_I2C_H +#define __ARCH_ARM_MACH_OMAP1_I2C_H + +struct i2c_board_info; +struct omap_i2c_bus_platform_data; + +int omap_i2c_add_bus(struct omap_i2c_bus_platform_data *i2c_pdata, + int bus_id); + +#if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE) +extern int omap_register_i2c_bus(int bus_id, u32 clkrate, + struct i2c_board_info const *info, + unsigned len); +extern int omap_register_i2c_bus_cmdline(void); +#else +static inline int omap_register_i2c_bus(int bus_id, u32 clkrate, + struct i2c_board_info const *info, + unsigned len) +{ + return 0; +} + +static inline int omap_register_i2c_bus_cmdline(void) +{ + return 0; +} +#endif + +#endif /* __ARCH_ARM_MACH_OMAP1_I2C_H */ diff --git a/arch/arm/mach-omap2/i2c.h b/arch/arm/mach-omap2/i2c.h index f76ff790b435..4d085c7ad425 100644 --- a/arch/arm/mach-omap2/i2c.h +++ b/arch/arm/mach-omap2/i2c.h @@ -19,11 +19,11 @@ * */ -#include - #ifndef __MACH_OMAP2_I2C_H #define __MACH_OMAP2_I2C_H +struct omap_hwmod; + int omap_i2c_reset(struct omap_hwmod *oh); #endif /* __MACH_OMAP2_I2C_H */ diff --git a/arch/arm/plat-omap/include/plat/i2c.h b/arch/arm/plat-omap/include/plat/i2c.h deleted file mode 100644 index 810629d79668..000000000000 --- a/arch/arm/plat-omap/include/plat/i2c.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Helper module for board specific I2C bus registration - * - * Copyright (C) 2009 Nokia Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __PLAT_OMAP_I2C_H -#define __PLAT_OMAP_I2C_H - -struct i2c_board_info; -struct omap_i2c_bus_platform_data; - -int omap_i2c_add_bus(struct omap_i2c_bus_platform_data *i2c_pdata, - int bus_id); - -#if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE) -extern int omap_register_i2c_bus(int bus_id, u32 clkrate, - struct i2c_board_info const *info, - unsigned len); -extern int omap_register_i2c_bus_cmdline(void); -#else -static inline int omap_register_i2c_bus(int bus_id, u32 clkrate, - struct i2c_board_info const *info, - unsigned len) -{ - return 0; -} - -static inline int omap_register_i2c_bus_cmdline(void) -{ - return 0; -} -#endif - -struct omap_hwmod; -int omap_i2c_reset(struct omap_hwmod *oh); - -#endif /* __PLAT_OMAP_I2C_H */ -- cgit v1.3-14-g43fede From 1ff4cb677dcac953514473971866a791400bbc5a Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 12 Feb 2018 19:32:43 -0600 Subject: ARM: OMAP2+: Remove unused gpio header file references Drop stale references to the generic and OMAP gpio header files from couple of files which no longer invoke any gpio functions. Signed-off-by: Suman Anna Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/devices.c | 2 +- arch/arm/mach-omap2/hsmmc.c | 2 -- arch/arm/mach-omap2/msdi.c | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 93057fb65f44..ed6f074ea672 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -8,7 +8,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ -#include + #include #include #include diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index 6d28aa20a7d3..b064066d431c 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -13,9 +13,7 @@ #include #include #include -#include #include -#include #include #include "soc.h" diff --git a/arch/arm/mach-omap2/msdi.c b/arch/arm/mach-omap2/msdi.c index 5a3bc3de58d0..978fba722b82 100644 --- a/arch/arm/mach-omap2/msdi.c +++ b/arch/arm/mach-omap2/msdi.c @@ -23,7 +23,6 @@ #include #include -#include #include "prm.h" #include "common.h" -- cgit v1.3-14-g43fede From 213d7f94775322ba44e0bbb55ec6946e9de88cea Mon Sep 17 00:00:00 2001 From: Richard Haines Date: Mon, 13 Nov 2017 20:54:22 +0000 Subject: netlabel: If PF_INET6, check sk_buff ip header version When resolving a fallback label, check the sk_buff version as it is possible (e.g. SCTP) to have family = PF_INET6 while receiving ip_hdr(skb)->version = 4. Signed-off-by: Richard Haines Acked-by: Paul Moore Signed-off-by: Paul Moore --- net/netlabel/netlabel_unlabeled.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 22dc1b9d6362..c070dfc0190a 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -1472,6 +1472,16 @@ int netlbl_unlabel_getattr(const struct sk_buff *skb, iface = rcu_dereference(netlbl_unlhsh_def); if (iface == NULL || !iface->valid) goto unlabel_getattr_nolabel; + +#if IS_ENABLED(CONFIG_IPV6) + /* When resolving a fallback label, check the sk_buff version as + * it is possible (e.g. SCTP) to have family = PF_INET6 while + * receiving ip_hdr(skb)->version = 4. + */ + if (family == PF_INET6 && ip_hdr(skb)->version == 4) + family = PF_INET; +#endif /* IPv6 */ + switch (family) { case PF_INET: { struct iphdr *hdr4; -- cgit v1.3-14-g43fede From fb3bff5b407e58493fa6bc44f24217f649aecd25 Mon Sep 17 00:00:00 2001 From: KOBAYASHI Yoshitake Date: Thu, 15 Feb 2018 00:35:06 +0900 Subject: mtd: nand: toshiba: Retrieve ECC requirements from extended ID This patch enables support to read the ECC strength and size from the NAND flash using Toshiba Memory SLC NAND extended-ID. This patch is based on the information of the 6th ID byte of the Toshiba Memory SLC NAND. Signed-off-by: KOBAYASHI Yoshitake Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_toshiba.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/nand_toshiba.c index 57df857074e6..ab43f027cd23 100644 --- a/drivers/mtd/nand/nand_toshiba.c +++ b/drivers/mtd/nand/nand_toshiba.c @@ -35,6 +35,32 @@ static void toshiba_nand_decode_id(struct nand_chip *chip) (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ && !(chip->id.data[4] & 0x80) /* !BENAND */) mtd->oobsize = 32 * mtd->writesize >> 9; + + /* + * Extract ECC requirements from 6th id byte. + * For Toshiba SLC, ecc requrements are as follows: + * - 43nm: 1 bit ECC for each 512Byte is required. + * - 32nm: 4 bit ECC for each 512Byte is required. + * - 24nm: 8 bit ECC for each 512Byte is required. + */ + if (chip->id.len >= 6 && nand_is_slc(chip)) { + chip->ecc_step_ds = 512; + switch (chip->id.data[5] & 0x7) { + case 0x4: + chip->ecc_strength_ds = 1; + break; + case 0x5: + chip->ecc_strength_ds = 4; + break; + case 0x6: + chip->ecc_strength_ds = 8; + break; + default: + WARN(1, "Could not get ECC info"); + chip->ecc_step_ds = 0; + break; + } + } } static int toshiba_nand_init(struct nand_chip *chip) -- cgit v1.3-14-g43fede From 7b21db9184a21b3ba479030dcfecaf3f2fa94b1f Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 12 Feb 2018 23:47:15 +0100 Subject: rtc: documentation: correct nvmem date and version The nvmem interface appaeared in linux 4.13 Signed-off-by: Alexandre Belloni --- Documentation/ABI/testing/sysfs-class-rtc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-rtc b/Documentation/ABI/testing/sysfs-class-rtc index cf60412882f0..65270d95873c 100644 --- a/Documentation/ABI/testing/sysfs-class-rtc +++ b/Documentation/ABI/testing/sysfs-class-rtc @@ -58,8 +58,8 @@ Description: (RO) RTC-provided time in 24-hour notation (hh:mm:ss) What: /sys/class/rtc/rtcX/*/nvmem -Date: February 2016 -KernelVersion: 4.6 +Date: July 2017 +KernelVersion: 4.13 Contact: linux-rtc@vger.kernel.org Description: (RW) The non volatile storage exported as a raw file, as -- cgit v1.3-14-g43fede From 4cce9d3988ae33eb53742d9648ecc59046196e6f Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 12 Feb 2018 23:47:16 +0100 Subject: rtc: nvmem: pass nvmem_config to rtc_nvmem_register() To be able to remove nvmem_config from struct rtc_device, pass it as a parameter to rtc_nvmem_register. Signed-off-by: Alexandre Belloni --- drivers/rtc/class.c | 2 +- drivers/rtc/nvmem.c | 17 +++++++++-------- drivers/rtc/rtc-core.h | 6 ++++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 722d683e0b0f..47c4aeed5106 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -453,7 +453,7 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc) rtc_proc_add_device(rtc); - rtc_nvmem_register(rtc); + rtc_nvmem_register(rtc, rtc->nvmem_config); rtc->registered = true; dev_info(rtc->dev.parent, "registered as %s\n", diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c index 8567b4ed9ac6..0a3522bcdd25 100644 --- a/drivers/rtc/nvmem.c +++ b/drivers/rtc/nvmem.c @@ -46,7 +46,7 @@ rtc_nvram_write(struct file *filp, struct kobject *kobj, return nvmem_device_write(rtc->nvmem, off, count, buf); } -static int rtc_nvram_register(struct rtc_device *rtc) +static int rtc_nvram_register(struct rtc_device *rtc, size_t size) { int err; @@ -64,7 +64,7 @@ static int rtc_nvram_register(struct rtc_device *rtc) rtc->nvram->read = rtc_nvram_read; rtc->nvram->write = rtc_nvram_write; - rtc->nvram->size = rtc->nvmem_config->size; + rtc->nvram->size = size; err = sysfs_create_bin_file(&rtc->dev.parent->kobj, rtc->nvram); @@ -84,20 +84,21 @@ static void rtc_nvram_unregister(struct rtc_device *rtc) /* * New ABI, uses nvmem */ -void rtc_nvmem_register(struct rtc_device *rtc) +void rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config) { - if (!rtc->nvmem_config) + if (!nvmem_config) return; - rtc->nvmem_config->dev = &rtc->dev; - rtc->nvmem_config->owner = rtc->owner; - rtc->nvmem = nvmem_register(rtc->nvmem_config); + nvmem_config->dev = &rtc->dev; + nvmem_config->owner = rtc->owner; + rtc->nvmem = nvmem_register(nvmem_config); if (IS_ERR_OR_NULL(rtc->nvmem)) return; /* Register the old ABI */ if (rtc->nvram_old_abi) - rtc_nvram_register(rtc); + rtc_nvram_register(rtc, nvmem_config->size); } void rtc_nvmem_unregister(struct rtc_device *rtc) diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h index 513b9bedd2c8..5f60e3b11cde 100644 --- a/drivers/rtc/rtc-core.h +++ b/drivers/rtc/rtc-core.h @@ -48,9 +48,11 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void) #endif #ifdef CONFIG_RTC_NVMEM -void rtc_nvmem_register(struct rtc_device *rtc); +void rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config); void rtc_nvmem_unregister(struct rtc_device *rtc); #else -static inline void rtc_nvmem_register(struct rtc_device *rtc) {} +static inline void rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config) {} static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {} #endif -- cgit v1.3-14-g43fede From 2cc8212198820487ec3fdcc7b98133701fc9dfc3 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 12 Feb 2018 23:47:17 +0100 Subject: rtc: nvmem: return error values In case of error, make rtc_nvmem_register() able to return an error value to its caller. Signed-off-by: Alexandre Belloni --- drivers/rtc/nvmem.c | 10 ++++++---- drivers/rtc/rtc-core.h | 11 +++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c index 0a3522bcdd25..293df6db7c42 100644 --- a/drivers/rtc/nvmem.c +++ b/drivers/rtc/nvmem.c @@ -84,21 +84,23 @@ static void rtc_nvram_unregister(struct rtc_device *rtc) /* * New ABI, uses nvmem */ -void rtc_nvmem_register(struct rtc_device *rtc, - struct nvmem_config *nvmem_config) +int rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config) { if (!nvmem_config) - return; + return -ENODEV; nvmem_config->dev = &rtc->dev; nvmem_config->owner = rtc->owner; rtc->nvmem = nvmem_register(nvmem_config); if (IS_ERR_OR_NULL(rtc->nvmem)) - return; + return PTR_ERR(rtc->nvmem); /* Register the old ABI */ if (rtc->nvram_old_abi) rtc_nvram_register(rtc, nvmem_config->size); + + return 0; } void rtc_nvmem_unregister(struct rtc_device *rtc) diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h index 5f60e3b11cde..05a67837fd76 100644 --- a/drivers/rtc/rtc-core.h +++ b/drivers/rtc/rtc-core.h @@ -48,11 +48,14 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void) #endif #ifdef CONFIG_RTC_NVMEM -void rtc_nvmem_register(struct rtc_device *rtc, - struct nvmem_config *nvmem_config); +int rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config); void rtc_nvmem_unregister(struct rtc_device *rtc); #else -static inline void rtc_nvmem_register(struct rtc_device *rtc, - struct nvmem_config *nvmem_config) {} +static inline int rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config) +{ + return -ENODEV; +} static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {} #endif -- cgit v1.3-14-g43fede From ab3ea36eda76069ba52e9b6aef5a9370b0b462bf Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 12 Feb 2018 23:47:18 +0100 Subject: rtc: nvmem: disallow registering nvmem more than once Make rtc_nvmem_register return -EBUSY when an nvmem is already registered for that RTC. Signed-off-by: Alexandre Belloni --- drivers/rtc/nvmem.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c index 293df6db7c42..fd1c39a5ade5 100644 --- a/drivers/rtc/nvmem.c +++ b/drivers/rtc/nvmem.c @@ -87,6 +87,9 @@ static void rtc_nvram_unregister(struct rtc_device *rtc) int rtc_nvmem_register(struct rtc_device *rtc, struct nvmem_config *nvmem_config) { + if (!IS_ERR_OR_NULL(rtc->nvmem)) + return -EBUSY; + if (!nvmem_config) return -ENODEV; -- cgit v1.3-14-g43fede From d590dca62bb8a315a2356159444de07278a3ad83 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Sat, 3 Feb 2018 00:33:11 -0500 Subject: audit: update bugtracker and source URIs Since the Linux Audit project has transitioned completely over to github, update the MAINTAINERS file and the primary audit source file to reflect that reality. Signed-off-by: Richard Guy Briggs Signed-off-by: Paul Moore --- MAINTAINERS | 1 - kernel/audit.c | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 845fc25812f1..fba48756e495 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2479,7 +2479,6 @@ M: Paul Moore M: Eric Paris L: linux-audit@redhat.com (moderated for non-subscribers) W: https://github.com/linux-audit -W: https://people.redhat.com/sgrubb/audit T: git git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit.git S: Supported F: include/linux/audit.h diff --git a/kernel/audit.c b/kernel/audit.c index 227db99b0f19..5c2544984375 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -38,7 +38,8 @@ * 6) Support low-overhead kernel-based filtering to minimize the * information that must be passed to user-space. * - * Example user-space utilities: http://people.redhat.com/sgrubb/audit/ + * Audit userspace, documentation, tests, and bug/issue trackers: + * https://github.com/linux-audit */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -- cgit v1.3-14-g43fede From 6387440e15db1c9ee58028433cd87291cae488e7 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Mon, 12 Feb 2018 05:04:53 -0500 Subject: audit: session ID should not set arch quick field pointer A bug was introduced in 8fae47705685fcaa75a1fe4c8c3e18300a702979 ("audit: add support for session ID user filter") See: https://github.com/linux-audit/audit-kernel/issues/4 When setting a session ID filter, the session ID filter field overwrote the quick pointer reference to the arch field, potentially causing the arch field to be misinterpreted. Passes audit-testsuite. Signed-off-by: Richard Guy Briggs Signed-off-by: Paul Moore --- kernel/auditfilter.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 4a1758adb222..739a6d2d4df8 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -496,7 +496,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, if (!gid_valid(f->gid)) goto exit_free; break; - case AUDIT_SESSIONID: case AUDIT_ARCH: entry->rule.arch_f = f; break; -- cgit v1.3-14-g43fede From fc968aee5e984c9cc2417147b9b5cd1da7ab43f3 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 6 Feb 2018 20:37:31 +0000 Subject: IB/cxgb3: remove cxio_dbg.c cxio_dbg.c is uncompiled since commit 2b540355cd2f ("RDMA/cxgb3: cleanups") 10 years after, we could remove it. Signed-off-by: Corentin Labbe Acked-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb3/Kconfig | 9 -- drivers/infiniband/hw/cxgb3/Makefile | 2 - drivers/infiniband/hw/cxgb3/cxio_dbg.c | 206 --------------------------------- drivers/infiniband/hw/cxgb3/cxio_hal.h | 9 -- drivers/infiniband/hw/cxgb3/iwch_cq.c | 6 - 5 files changed, 232 deletions(-) delete mode 100644 drivers/infiniband/hw/cxgb3/cxio_dbg.c diff --git a/drivers/infiniband/hw/cxgb3/Kconfig b/drivers/infiniband/hw/cxgb3/Kconfig index 431be733fbbe..a7b77cb3d5d5 100644 --- a/drivers/infiniband/hw/cxgb3/Kconfig +++ b/drivers/infiniband/hw/cxgb3/Kconfig @@ -16,12 +16,3 @@ config INFINIBAND_CXGB3 To compile this driver as a module, choose M here: the module will be called iw_cxgb3. - -config INFINIBAND_CXGB3_DEBUG - bool "Verbose debugging output" - depends on INFINIBAND_CXGB3 - default n - ---help--- - This option causes the Chelsio RDMA driver to produce copious - amounts of debug messages. Select this if you are developing - the driver or trying to diagnose a problem. diff --git a/drivers/infiniband/hw/cxgb3/Makefile b/drivers/infiniband/hw/cxgb3/Makefile index 2c66d35d19bd..66fe0917aba0 100644 --- a/drivers/infiniband/hw/cxgb3/Makefile +++ b/drivers/infiniband/hw/cxgb3/Makefile @@ -5,5 +5,3 @@ obj-$(CONFIG_INFINIBAND_CXGB3) += iw_cxgb3.o iw_cxgb3-y := iwch_cm.o iwch_ev.o iwch_cq.o iwch_qp.o iwch_mem.o \ iwch_provider.o iwch.o cxio_hal.o cxio_resource.o - -ccflags-$(CONFIG_INFINIBAND_CXGB3_DEBUG) += -DDEBUG diff --git a/drivers/infiniband/hw/cxgb3/cxio_dbg.c b/drivers/infiniband/hw/cxgb3/cxio_dbg.c deleted file mode 100644 index 97dbe728520a..000000000000 --- a/drivers/infiniband/hw/cxgb3/cxio_dbg.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifdef DEBUG -#include -#include -#include "common.h" -#include "cxgb3_ioctl.h" -#include "cxio_hal.h" -#include "cxio_wr.h" - -void cxio_dump_tpt(struct cxio_rdev *rdev, u32 stag) -{ - struct ch_mem_range *m; - u64 *data; - int rc; - int size = 32; - - m = kmalloc(sizeof(*m) + size, GFP_ATOMIC); - if (!m) - return; - - m->mem_id = MEM_PMRX; - m->addr = (stag>>8) * 32 + rdev->rnic_info.tpt_base; - m->len = size; - pr_debug("%s TPT addr 0x%x len %d\n", __func__, m->addr, m->len); - rc = rdev->t3cdev_p->ctl(rdev->t3cdev_p, RDMA_GET_MEM, m); - if (rc) { - pr_debug("%s toectl returned error %d\n", __func__, rc); - kfree(m); - return; - } - - data = (u64 *)m->buf; - while (size > 0) { - pr_debug("TPT %08x: %016llx\n", - m->addr, (unsigned long long)*data); - size -= 8; - data++; - m->addr += 8; - } - kfree(m); -} - -void cxio_dump_pbl(struct cxio_rdev *rdev, u32 pbl_addr, uint len, u8 shift) -{ - struct ch_mem_range *m; - u64 *data; - int rc; - int size, npages; - - shift += 12; - npages = (len + (1ULL << shift) - 1) >> shift; - size = npages * sizeof(u64); - - m = kmalloc(sizeof(*m) + size, GFP_ATOMIC); - if (!m) - return; - - m->mem_id = MEM_PMRX; - m->addr = pbl_addr; - m->len = size; - pr_debug("%s PBL addr 0x%x len %d depth %d\n", - __func__, m->addr, m->len, npages); - rc = rdev->t3cdev_p->ctl(rdev->t3cdev_p, RDMA_GET_MEM, m); - if (rc) { - pr_debug("%s toectl returned error %d\n", __func__, rc); - kfree(m); - return; - } - - data = (u64 *)m->buf; - while (size > 0) { - pr_debug("PBL %08x: %016llx\n", - m->addr, (unsigned long long)*data); - size -= 8; - data++; - m->addr += 8; - } - kfree(m); -} - -void cxio_dump_wqe(union t3_wr *wqe) -{ - __be64 *data = (__be64 *)wqe; - uint size = (uint)(be64_to_cpu(*data) & 0xff); - - if (size == 0) - size = 8; - while (size > 0) { - pr_debug("WQE %p: %016llx\n", - data, (unsigned long long)be64_to_cpu(*data)); - size--; - data++; - } -} - -void cxio_dump_wce(struct t3_cqe *wce) -{ - __be64 *data = (__be64 *)wce; - int size = sizeof(*wce); - - while (size > 0) { - pr_debug("WCE %p: %016llx\n", - data, (unsigned long long)be64_to_cpu(*data)); - size -= 8; - data++; - } -} - -void cxio_dump_rqt(struct cxio_rdev *rdev, u32 hwtid, int nents) -{ - struct ch_mem_range *m; - int size = nents * 64; - u64 *data; - int rc; - - m = kmalloc(sizeof(*m) + size, GFP_ATOMIC); - if (!m) - return; - - m->mem_id = MEM_PMRX; - m->addr = ((hwtid)<<10) + rdev->rnic_info.rqt_base; - m->len = size; - pr_debug("%s RQT addr 0x%x len %d\n", __func__, m->addr, m->len); - rc = rdev->t3cdev_p->ctl(rdev->t3cdev_p, RDMA_GET_MEM, m); - if (rc) { - pr_debug("%s toectl returned error %d\n", __func__, rc); - kfree(m); - return; - } - - data = (u64 *)m->buf; - while (size > 0) { - pr_debug("RQT %08x: %016llx\n", - m->addr, (unsigned long long)*data); - size -= 8; - data++; - m->addr += 8; - } - kfree(m); -} - -void cxio_dump_tcb(struct cxio_rdev *rdev, u32 hwtid) -{ - struct ch_mem_range *m; - int size = TCB_SIZE; - u32 *data; - int rc; - - m = kmalloc(sizeof(*m) + size, GFP_ATOMIC); - if (!m) - return; - - m->mem_id = MEM_CM; - m->addr = hwtid * size; - m->len = size; - pr_debug("%s TCB %d len %d\n", __func__, m->addr, m->len); - rc = rdev->t3cdev_p->ctl(rdev->t3cdev_p, RDMA_GET_MEM, m); - if (rc) { - pr_debug("%s toectl returned error %d\n", __func__, rc); - kfree(m); - return; - } - - data = (u32 *)m->buf; - while (size > 0) { - printk("%2u: %08x %08x %08x %08x %08x %08x %08x %08x\n", - m->addr, - *(data+2), *(data+3), *(data),*(data+1), - *(data+6), *(data+7), *(data+4), *(data+5)); - size -= 32; - data += 8; - m->addr += 32; - } - kfree(m); -} -#endif diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.h b/drivers/infiniband/hw/cxgb3/cxio_hal.h index 7e70c5492262..c64e50b5a548 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_hal.h +++ b/drivers/infiniband/hw/cxgb3/cxio_hal.h @@ -202,13 +202,4 @@ int iwch_cxgb3_ofld_send(struct t3cdev *tdev, struct sk_buff *skb); #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#ifdef DEBUG -void cxio_dump_tpt(struct cxio_rdev *rev, u32 stag); -void cxio_dump_pbl(struct cxio_rdev *rev, u32 pbl_addr, uint len, u8 shift); -void cxio_dump_wqe(union t3_wr *wqe); -void cxio_dump_wce(struct t3_cqe *wce); -void cxio_dump_rqt(struct cxio_rdev *rdev, u32 hwtid, int nents); -void cxio_dump_tcb(struct cxio_rdev *rdev, u32 hwtid); -#endif - #endif diff --git a/drivers/infiniband/hw/cxgb3/iwch_cq.c b/drivers/infiniband/hw/cxgb3/iwch_cq.c index dd5348e48806..0a8542c20804 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cq.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cq.c @@ -200,9 +200,6 @@ int iwch_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc) spin_lock_irqsave(&chp->lock, flags); for (npolled = 0; npolled < num_entries; ++npolled) { -#ifdef DEBUG - int i=0; -#endif /* * Because T3 can post CQEs that are _not_ associated @@ -211,9 +208,6 @@ int iwch_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc) */ do { err = iwch_poll_cq_one(rhp, chp, wc + npolled); -#ifdef DEBUG - BUG_ON(++i > 1000); -#endif } while (err == -EAGAIN); if (err <= 0) break; -- cgit v1.3-14-g43fede From 173bc6be96c733ff7e6418eece9d64d03e7465b3 Mon Sep 17 00:00:00 2001 From: oulijun Date: Wed, 7 Feb 2018 17:49:28 +0800 Subject: RDMA/hns: Fix a bug with modifying mac address When modifying mac address, it will trigger hns_roce_del_gid function and can't delete the default gid matched the index because the attribute of gid is null. Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index eb9a69fc7bec..8255bb9021b0 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -100,6 +100,7 @@ static int hns_roce_del_gid(struct ib_device *device, u8 port_num, unsigned int index, void **context) { struct hns_roce_dev *hr_dev = to_hr_dev(device); + struct ib_gid_attr zattr = { }; union ib_gid zgid = { {0} }; u8 port = port_num - 1; unsigned long flags; @@ -110,7 +111,7 @@ static int hns_roce_del_gid(struct ib_device *device, u8 port_num, spin_lock_irqsave(&hr_dev->iboe.lock, flags); - ret = hr_dev->hw->set_gid(hr_dev, port, index, &zgid, NULL); + ret = hr_dev->hw->set_gid(hr_dev, port, index, &zgid, &zattr); spin_unlock_irqrestore(&hr_dev->iboe.lock, flags); -- cgit v1.3-14-g43fede From ced07769dc8484a9221aa616b3e935e748e8db03 Mon Sep 17 00:00:00 2001 From: Yixian Liu Date: Wed, 7 Feb 2018 17:49:29 +0800 Subject: RDMA/hns: Fix QP state judgement before receiving work requests The QP can accept receive work requests only when the QP is in the states that allow them to be submitted. This patch updates the QP state judgement based on the specification. Signed-off-by: Yixian Liu Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index db2ff352d75f..0aa748304ab5 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -509,7 +509,7 @@ static int hns_roce_v2_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr, spin_lock_irqsave(&hr_qp->rq.lock, flags); ind = hr_qp->rq.head & (hr_qp->rq.wqe_cnt - 1); - if (hr_qp->state == IB_QPS_RESET || hr_qp->state == IB_QPS_ERR) { + if (hr_qp->state == IB_QPS_RESET) { spin_unlock_irqrestore(&hr_qp->rq.lock, flags); *bad_wr = wr; return -EINVAL; -- cgit v1.3-14-g43fede From d480bb50d294e23a6773c507b8017e6bc45efc67 Mon Sep 17 00:00:00 2001 From: oulijun Date: Wed, 7 Feb 2018 17:49:30 +0800 Subject: RDMA/hns: Use free_pages function instead of free_page It need to use free_pages function for free the memory allocated by __get_free_pages function. Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_mr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index da86a8117bd5..f7256d88d38f 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -933,7 +933,7 @@ int hns_roce_ib_umem_write_mtt(struct hns_roce_dev *hr_dev, ret = hns_roce_write_mtt(hr_dev, mtt, n, i, pages); out: - free_page((unsigned long) pages); + free_pages((unsigned long) pages, order); return ret; } -- cgit v1.3-14-g43fede From 5229f87efcc5a0c800e7f3b49264af984ea4aba9 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 7 Feb 2018 16:45:51 -0700 Subject: RDMA: Do not used __packed in uapi headers __packed is not available in linux/types.h, so we cannot use it in the uapi headers. The construction struct ABC {} __packed; may still compile even if __packed is not defined, however it simply creates a variable called __packed, and doesn't set the alignment. All these uses of packed are on structs that already have aligned members. While use in hfi may indicate the struct itself is unaligned, the use in ocrdma is on a UHW struct which should never be unaligned, so just delete it there. Signed-off-by: Jason Gunthorpe --- include/uapi/rdma/hfi/hfi1_user.h | 6 +++--- include/uapi/rdma/ocrdma-abi.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/uapi/rdma/hfi/hfi1_user.h b/include/uapi/rdma/hfi/hfi1_user.h index 791bea2f8297..43b46bf6f8bb 100644 --- a/include/uapi/rdma/hfi/hfi1_user.h +++ b/include/uapi/rdma/hfi/hfi1_user.h @@ -219,7 +219,7 @@ struct sdma_req_info { * in charge of managing its own ring. */ __u16 comp_idx; -} __packed; +} __attribute__((__packed__)); /* * SW KDETH header. @@ -230,7 +230,7 @@ struct hfi1_kdeth_header { __le16 jkey; __le16 hcrc; __le32 swdata[7]; -} __packed; +} __attribute__((__packed__)); /* * Structure describing the headers that User space uses. The @@ -241,7 +241,7 @@ struct hfi1_pkt_header { __be16 lrh[4]; __be32 bth[3]; struct hfi1_kdeth_header kdeth; -} __packed; +} __attribute__((__packed__)); /* diff --git a/include/uapi/rdma/ocrdma-abi.h b/include/uapi/rdma/ocrdma-abi.h index ad64a3cea1cd..e0475d59cdf0 100644 --- a/include/uapi/rdma/ocrdma-abi.h +++ b/include/uapi/rdma/ocrdma-abi.h @@ -127,7 +127,7 @@ struct ocrdma_create_qp_uresp { __u32 db_rq_offset; __u32 db_shift; __u64 rsvd[11]; -} __packed; +}; struct ocrdma_create_srq_uresp { __u16 rq_dbid; -- cgit v1.3-14-g43fede From 7061f28d8a2faf8131ac3a8ceb1af9850313e22c Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 7 Feb 2018 16:49:10 -0700 Subject: rxe: Do not use 'struct sockaddr' in a uapi header Linux has two 'linux/socket.h' files - and only the one in the kernel defines struct sockaddr - the user space one does not. Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rxe/rxe_av.c | 5 +++-- include/uapi/rdma/rdma_user_rxe.h | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_av.c b/drivers/infiniband/sw/rxe/rxe_av.c index 7522d1af3ae2..7f1ae364088a 100644 --- a/drivers/infiniband/sw/rxe/rxe_av.c +++ b/drivers/infiniband/sw/rxe/rxe_av.c @@ -74,8 +74,9 @@ void rxe_av_fill_ip_info(struct rxe_av *av, struct ib_gid_attr *sgid_attr, union ib_gid *sgid) { - rdma_gid2ip(&av->sgid_addr._sockaddr, sgid); - rdma_gid2ip(&av->dgid_addr._sockaddr, &rdma_ah_read_grh(attr)->dgid); + rdma_gid2ip((struct sockaddr *)&av->sgid_addr, sgid); + rdma_gid2ip((struct sockaddr *)&av->dgid_addr, + &rdma_ah_read_grh(attr)->dgid); av->network_type = ib_gid_to_network_type(sgid_attr->gid_type, sgid); } diff --git a/include/uapi/rdma/rdma_user_rxe.h b/include/uapi/rdma/rdma_user_rxe.h index bdeea948b2f3..e3e6852b58eb 100644 --- a/include/uapi/rdma/rdma_user_rxe.h +++ b/include/uapi/rdma/rdma_user_rxe.h @@ -35,6 +35,9 @@ #define RDMA_USER_RXE_H #include +#include +#include +#include union rxe_gid { __u8 raw[16]; @@ -57,7 +60,6 @@ struct rxe_av { __u8 network_type; struct rxe_global_route grh; union { - struct sockaddr _sockaddr; struct sockaddr_in _sockaddr_in; struct sockaddr_in6 _sockaddr_in6; } sgid_addr, dgid_addr; -- cgit v1.3-14-g43fede From 71591d1280e5ef02c2af2ffb9801d0c842973be9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 14 Feb 2018 20:11:17 +0200 Subject: RDMA/hns: Replace __raw_write*(cpu_to_le*()) with LE write*() There is no need to repeat the semantics of writel() and similar. Moreover sparse complains about this: drivers/infiniband/hw/hns/hns_roce_hw_v1.c:1690:22: expected unsigned long long val drivers/infiniband/hw/hns/hns_roce_hw_v1.c:1690:22: got restricted __le64 Fixing this by replacing __raw_write*(cpu_to_le*()) calls by plain write*() ones. Note, write*() accessors are little endian by definition. Reported-by: kbuild test robot Signed-off-by: Andy Shevchenko Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v1.c | 8 ++++---- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c index da13bd7c3ca9..47e1b6ac1e1a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c @@ -1687,13 +1687,13 @@ static int hns_roce_v1_post_mbox(struct hns_roce_dev *hr_dev, u64 in_param, roce_set_field(val, ROCEE_MB6_ROCEE_MB_TOKEN_M, ROCEE_MB6_ROCEE_MB_TOKEN_S, token); - __raw_writeq(cpu_to_le64(in_param), hcr + 0); - __raw_writeq(cpu_to_le64(out_param), hcr + 2); - __raw_writel(cpu_to_le32(in_modifier), hcr + 4); + writeq(in_param, hcr + 0); + writeq(out_param, hcr + 2); + writel(in_modifier, hcr + 4); /* Memory barrier */ wmb(); - __raw_writel(cpu_to_le32(val), hcr + 5); + writel(val, hcr + 5); mmiowb(); diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 0aa748304ab5..016bca1923ec 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -1228,14 +1228,14 @@ static int hns_roce_v2_post_mbox(struct hns_roce_dev *hr_dev, u64 in_param, roce_set_field(val1, HNS_ROCE_VF_MB5_TOKEN_MASK, HNS_ROCE_VF_MB5_TOKEN_SHIFT, token); - __raw_writeq(cpu_to_le64(in_param), hcr + 0); - __raw_writeq(cpu_to_le64(out_param), hcr + 2); + writeq(in_param, hcr + 0); + writeq(out_param, hcr + 2); /* Memory barrier */ wmb(); - __raw_writel(cpu_to_le32(val0), hcr + 4); - __raw_writel(cpu_to_le32(val1), hcr + 5); + writel(val0, hcr + 4); + writel(val1, hcr + 5); mmiowb(); -- cgit v1.3-14-g43fede From e607b605be46a44f50e3b48597188477e52eeff8 Mon Sep 17 00:00:00 2001 From: Joonas Kylmälä Date: Thu, 8 Feb 2018 14:01:40 +0200 Subject: ARM: dts: sunxi: h3-h5: Move pinctrl of mmc1 from dts to dtsi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the boards use the mmc1 pins and their attributes defined in mmc1_pins_a. Let's default to that by moving the pinctrl attributes to the dtsi file. This makes it easier to modify device trees in the future as there is only one place to change the pinctrl attributes. Signed-off-by: Joonas Kylmälä Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts | 2 -- arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts | 2 -- arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts | 2 -- arch/arm/boot/dts/sun8i-h3-beelink-x2.dts | 2 -- arch/arm/boot/dts/sun8i-h3-nanopi-m1-plus.dts | 2 -- arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts | 2 -- arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 2 -- arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts | 2 -- arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts | 2 -- arch/arm/boot/dts/sunxi-h3-h5.dtsi | 2 ++ arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts | 2 -- arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts | 2 -- arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts | 2 -- 13 files changed, 2 insertions(+), 24 deletions(-) diff --git a/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts index 29a85eb14927..7d01f9322658 100644 --- a/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts +++ b/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts @@ -72,8 +72,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; vqmmc-supply = <®_vcc3v3>; mmc-pwrseq = <&wifi_pwrseq>; diff --git a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts index f3b066ff63cb..73978853bb4c 100644 --- a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts +++ b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts @@ -119,8 +119,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc_wifi>; mmc-pwrseq = <&wifi_pwrseq>; bus-width = <4>; diff --git a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts index bea49ed89cc7..9c1bc472fb1c 100644 --- a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts +++ b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts @@ -143,8 +143,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; vqmmc-supply = <®_vcc3v3>; mmc-pwrseq = <&wifi_pwrseq>; diff --git a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts index 9fc07593e907..870aabcbb2d8 100644 --- a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts +++ b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts @@ -122,8 +122,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; non-removable; diff --git a/arch/arm/boot/dts/sun8i-h3-nanopi-m1-plus.dts b/arch/arm/boot/dts/sun8i-h3-nanopi-m1-plus.dts index a6e61915d648..07b51a2f14c6 100644 --- a/arch/arm/boot/dts/sun8i-h3-nanopi-m1-plus.dts +++ b/arch/arm/boot/dts/sun8i-h3-nanopi-m1-plus.dts @@ -101,8 +101,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; vqmmc-supply = <®_vcc3v3>; mmc-pwrseq = <&wifi_pwrseq>; diff --git a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts index 07e2e6180792..6246d3eff39d 100644 --- a/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts +++ b/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts @@ -87,8 +87,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; vqmmc-supply = <®_vcc3v3>; mmc-pwrseq = <&wifi_pwrseq>; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts index ac6f52f3fa62..7569bd05e249 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts @@ -139,8 +139,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; mmc-pwrseq = <&wifi_pwrseq>; bus-width = <4>; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts index 82ab5b6b730b..2e59fd296717 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-lite.dts @@ -113,8 +113,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; non-removable; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts index a10281b455f5..71fb73208939 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts @@ -59,8 +59,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; bus-width = <4>; non-removable; diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi index 3a0854a96a04..b4a4b3cb6c4e 100644 --- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi +++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi @@ -154,6 +154,8 @@ mmc1: mmc@1c10000 { /* compatible and clocks are in per SoC .dtsi file */ reg = <0x01c10000 0x1000>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins_a>; resets = <&ccu RST_BUS_MMC1>; reset-names = "ahb"; interrupts = ; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts index 1ef70cb4c9bc..506e25ba028a 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo-plus2.dts @@ -158,8 +158,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; vqmmc-supply = <®_vcc3v3>; mmc-pwrseq = <&wifi_pwrseq>; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts index 586ec67f4101..803566608ed8 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-prime.dts @@ -174,8 +174,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; mmc-pwrseq = <&wifi_pwrseq>; bus-width = <4>; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts index feee125a4e9a..eda24d813282 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-zero-plus2.dts @@ -81,8 +81,6 @@ }; &mmc1 { - pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; vmmc-supply = <®_vcc3v3>; vqmmc-supply = <®_vcc3v3>; mmc-pwrseq = <&wifi_pwrseq>; -- cgit v1.3-14-g43fede From 8566df712b5bb7b35d292f3b6b9216f511607e1f Mon Sep 17 00:00:00 2001 From: Joonas Kylmälä Date: Thu, 8 Feb 2018 14:01:41 +0200 Subject: ARM: dts: sunxi: h3-h5: rename mmc0_pins_a and mmc1_pins_a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is only one pinctrl configuration for mmc0 and mmc1 so let's drop the _a suffix from both of them. Signed-off-by: Joonas Kylmälä Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts | 2 +- arch/arm/boot/dts/sunxi-h3-h5.dtsi | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts index 73978853bb4c..f868cf197c8e 100644 --- a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts +++ b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts @@ -134,7 +134,7 @@ }; }; -&mmc1_pins_a { +&mmc1_pins { bias-pull-up; }; diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi index b4a4b3cb6c4e..7741166d34d8 100644 --- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi +++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi @@ -142,7 +142,7 @@ /* compatible and clocks are in per SoC .dtsi file */ reg = <0x01c0f000 0x1000>; pinctrl-names = "default"; - pinctrl-0 = <&mmc0_pins_a>; + pinctrl-0 = <&mmc0_pins>; resets = <&ccu RST_BUS_MMC0>; reset-names = "ahb"; interrupts = ; @@ -155,7 +155,7 @@ /* compatible and clocks are in per SoC .dtsi file */ reg = <0x01c10000 0x1000>; pinctrl-names = "default"; - pinctrl-0 = <&mmc1_pins_a>; + pinctrl-0 = <&mmc1_pins>; resets = <&ccu RST_BUS_MMC1>; reset-names = "ahb"; interrupts = ; @@ -352,7 +352,7 @@ function = "i2c2"; }; - mmc0_pins_a: mmc0 { + mmc0_pins: mmc0 { pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; function = "mmc0"; @@ -360,7 +360,7 @@ bias-pull-up; }; - mmc1_pins_a: mmc1 { + mmc1_pins: mmc1 { pins = "PG0", "PG1", "PG2", "PG3", "PG4", "PG5"; function = "mmc1"; -- cgit v1.3-14-g43fede From 5f49ddb1a7bc265fdade9317842da7e7a9a6bcac Mon Sep 17 00:00:00 2001 From: Joonas Kylmälä Date: Mon, 12 Feb 2018 18:29:45 +0200 Subject: ARM: dts: sun8i: h2-plus: remove unnecessary mmc1_pins node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mmc1_pins node with bias-pull-up attribute is already defined in the sunxi-h3-h5.dtsi file. Thus, we can remove it from here. Signed-off-by: Joonas Kylmälä Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts index f868cf197c8e..0bc031fe4c56 100644 --- a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts +++ b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts @@ -134,10 +134,6 @@ }; }; -&mmc1_pins { - bias-pull-up; -}; - &ohci0 { status = "okay"; }; -- cgit v1.3-14-g43fede From 6f58e1b34881a9446a82f60644b200dc16f774b8 Mon Sep 17 00:00:00 2001 From: Wadim Egorov Date: Tue, 13 Feb 2018 10:44:33 +0100 Subject: ARM: dts: rockchip: Fix supply node for card's power on phycore som The card is powered by the RK818 switch vdd_sd which is supplied by VCC9 (VDD_3V3_IO). Signed-off-by: Wadim Egorov Signed-off-by: Daniel Schultz Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3288-phycore-rdk.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/rk3288-phycore-rdk.dts b/arch/arm/boot/dts/rk3288-phycore-rdk.dts index 1241cbcfc16f..ecb26f0f6ffa 100644 --- a/arch/arm/boot/dts/rk3288-phycore-rdk.dts +++ b/arch/arm/boot/dts/rk3288-phycore-rdk.dts @@ -265,7 +265,7 @@ disable-wp; pinctrl-names = "default"; pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; - vmmc-supply = <&vdd_io_sd>; + vmmc-supply = <&vdd_sd>; vqmmc-supply = <&vdd_io_sd>; status = "okay"; }; -- cgit v1.3-14-g43fede From 83173662bdbb012c9afe02571bbedfa67354dd66 Mon Sep 17 00:00:00 2001 From: Wadim Egorov Date: Tue, 13 Feb 2018 10:44:34 +0100 Subject: ARM: dts: rockchip: Support UHS mode for SD card on phyCORE-RK3288 RDK The phyCORE-RK3288 RDK could enable SD3.0 card but need vdd_io_sd to support the voltage range from 1.8V to 3.3V. And we have also to add relevant UHS modes to the sdmmc node. Let's also turn off the vdd_io_sd regulator in suspend state. Signed-off-by: Wadim Egorov Signed-off-by: Daniel Schultz Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3288-phycore-rdk.dts | 4 ++++ arch/arm/boot/dts/rk3288-phycore-som.dtsi | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/rk3288-phycore-rdk.dts b/arch/arm/boot/dts/rk3288-phycore-rdk.dts index ecb26f0f6ffa..985743fa134c 100644 --- a/arch/arm/boot/dts/rk3288-phycore-rdk.dts +++ b/arch/arm/boot/dts/rk3288-phycore-rdk.dts @@ -265,6 +265,10 @@ disable-wp; pinctrl-names = "default"; pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; vmmc-supply = <&vdd_sd>; vqmmc-supply = <&vdd_io_sd>; status = "okay"; diff --git a/arch/arm/boot/dts/rk3288-phycore-som.dtsi b/arch/arm/boot/dts/rk3288-phycore-som.dtsi index 99cfae875e12..e9c9cad688cb 100644 --- a/arch/arm/boot/dts/rk3288-phycore-som.dtsi +++ b/arch/arm/boot/dts/rk3288-phycore-som.dtsi @@ -356,11 +356,10 @@ regulator-name = "vdd_io_sd"; regulator-always-on; regulator-boot-on; - regulator-min-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <3300000>; + regulator-off-in-suspend; }; }; }; -- cgit v1.3-14-g43fede From b1ca80e4a23b91fc90545919dda90a32ec558663 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 9 Feb 2018 11:53:40 +0000 Subject: platform/x86: GPD pocket fan: fix spelling mistake: "Mill-celcius" -> "millicelsius" Trivial fix to spelling mistake in MODULE_PARM_DESC text and remove unnecessary hyphen. Signed-off-by: Colin Ian King Signed-off-by: Andy Shevchenko --- drivers/platform/x86/gpd-pocket-fan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/gpd-pocket-fan.c b/drivers/platform/x86/gpd-pocket-fan.c index 2d645c505f81..be85ed966bf3 100644 --- a/drivers/platform/x86/gpd-pocket-fan.c +++ b/drivers/platform/x86/gpd-pocket-fan.c @@ -19,12 +19,12 @@ static int temp_limits[3] = { 55000, 60000, 65000 }; module_param_array(temp_limits, int, NULL, 0444); MODULE_PARM_DESC(temp_limits, - "Milli-celcius values above which the fan speed increases"); + "Millicelsius values above which the fan speed increases"); static int hysteresis = 3000; module_param(hysteresis, int, 0444); MODULE_PARM_DESC(hysteresis, - "Hysteresis in milli-celcius before lowering the fan speed"); + "Hysteresis in millicelsius before lowering the fan speed"); static int speed_on_ac = 2; module_param(speed_on_ac, int, 0444); -- cgit v1.3-14-g43fede From 04e4e888558ebe5d9701e31bba61d4e256ab71a8 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 12 Feb 2018 11:50:47 -0800 Subject: platform/x86: intel_turbo_max_3: Remove restriction for HWP platforms On systems supporting HWP (Hardware P-States) mode, we expected to enumerate core priority via ACPI-CPPC tables. Unfortunately deployment of TURBO 3.0 didn't use this method to show core priority. So users are not able to utilize this feature in HWP mode. So remove the loading restriction of this driver for HWP enabled systems. Even if there are some systems, which are providing the core priority via ACPI CPPC, this shouldn't cause any conflict as the source of priority definition is same. Signed-off-by: Srinivas Pandruvada Reported-and-tested-and-reviewed-by: Arjan van de Ven Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_turbo_max_3.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c index d4ea01805879..a6d5aa0c3c47 100644 --- a/drivers/platform/x86/intel_turbo_max_3.c +++ b/drivers/platform/x86/intel_turbo_max_3.c @@ -138,9 +138,6 @@ static int __init itmt_legacy_init(void) if (!id) return -ENODEV; - if (boot_cpu_has(X86_FEATURE_HWP)) - return -ENODEV; - ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "platform/x86/turbo_max_3:online", itmt_legacy_cpu_online, NULL); -- cgit v1.3-14-g43fede From df42a04b15f19a842393dc98a84cbc52b1f8ed49 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 14 Feb 2018 17:36:28 -0600 Subject: iommu/amd: Avoid locking get_irq_table() from atomic context get_irq_table() previously acquired amd_iommu_devtable_lock which is not a raw lock, and thus cannot be acquired from atomic context on PREEMPT_RT. Many calls to modify_irte*() come from atomic context due to the IRQ desc->lock, as does amd_iommu_update_ga() due to the preemption disabling in vcpu_load/put(). The only difference between calling get_irq_table() and reading from irq_lookup_table[] directly, other than the lock acquisition and amd_iommu_rlookup_table[] check, is if the table entry is unpopulated, which should never happen when looking up a devid that came from an irq_2_irte struct, as get_irq_table() would have already been called on that devid during irq_remapping_alloc(). The lock acquisition is not needed in these cases because entries in irq_lookup_table[] never change once non-NULL -- nor would the amd_iommu_devtable_lock usage in get_irq_table() provide meaningful protection if they did, since it's released before using the looked up table in the get_irq_table() caller. Rename the old get_irq_table() to alloc_irq_table(), and create a new lockless get_irq_table() to be used in non-allocating contexts that WARNs if it doesn't find what it's looking for. Signed-off-by: Scott Wood Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index d666246ac30b..14efeb306a9f 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3595,7 +3595,22 @@ static void set_dte_irq_entry(u16 devid, struct irq_remap_table *table) amd_iommu_dev_table[devid].data[2] = dte; } -static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic) +static struct irq_remap_table *get_irq_table(u16 devid) +{ + struct irq_remap_table *table; + + if (WARN_ONCE(!amd_iommu_rlookup_table[devid], + "%s: no iommu for devid %x\n", __func__, devid)) + return NULL; + + table = irq_lookup_table[devid]; + if (WARN_ONCE(!table, "%s: no table for devid %x\n", __func__, devid)) + return NULL; + + return table; +} + +static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic) { struct irq_remap_table *table = NULL; struct amd_iommu *iommu; @@ -3682,7 +3697,7 @@ static int alloc_irq_index(u16 devid, int count, bool align) if (!iommu) return -ENODEV; - table = get_irq_table(devid, false); + table = alloc_irq_table(devid, false); if (!table) return -ENODEV; @@ -3733,7 +3748,7 @@ static int modify_irte_ga(u16 devid, int index, struct irte_ga *irte, if (iommu == NULL) return -EINVAL; - table = get_irq_table(devid, false); + table = get_irq_table(devid); if (!table) return -ENOMEM; @@ -3766,7 +3781,7 @@ static int modify_irte(u16 devid, int index, union irte *irte) if (iommu == NULL) return -EINVAL; - table = get_irq_table(devid, false); + table = get_irq_table(devid); if (!table) return -ENOMEM; @@ -3790,7 +3805,7 @@ static void free_irte(u16 devid, int index) if (iommu == NULL) return; - table = get_irq_table(devid, false); + table = get_irq_table(devid); if (!table) return; @@ -4108,7 +4123,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq, return ret; if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC) { - if (get_irq_table(devid, true)) + if (alloc_irq_table(devid, true)) index = info->ioapic_pin; else ret = -ENOMEM; @@ -4391,7 +4406,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data) if (!iommu) return -ENODEV; - irt = get_irq_table(devid, false); + irt = get_irq_table(devid); if (!irt) return -ENODEV; -- cgit v1.3-14-g43fede From d897ef56faf9bdb36e931251d0b70b10ebb03a14 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Wed, 14 Feb 2018 21:08:55 +0100 Subject: clk: sunxi-ng: Mask nkmp factors when setting register Currently, if one of the factors isn't present, bit 0 gets always set to 1. For example, A83T has NMP PLLs modelled as NKMP PLL without K. Since K is not specified, it's offset, width and shift is 0. Driver assumes that lowest value possible is 1, otherwise we would get division by 0. That situation causes that bit 0 is always set, which may change wanted clock rate. Fix that by masking every factor according to it's specified width. Factors with width set to 0 won't have any influence to final register value. Acked-by: Maxime Ripard Signed-off-by: Jernej Skrabec Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu_nkmp.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c index e58c95787f94..a99068a08315 100644 --- a/drivers/clk/sunxi-ng/ccu_nkmp.c +++ b/drivers/clk/sunxi-ng/ccu_nkmp.c @@ -134,6 +134,7 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); + u32 n_mask, k_mask, m_mask, p_mask; struct _ccu_nkmp _nkmp; unsigned long flags; u32 reg; @@ -149,18 +150,20 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, ccu_nkmp_find_best(parent_rate, rate, &_nkmp); + n_mask = GENMASK(nkmp->n.width + nkmp->n.shift - 1, nkmp->n.shift); + k_mask = GENMASK(nkmp->k.width + nkmp->k.shift - 1, nkmp->k.shift); + m_mask = GENMASK(nkmp->m.width + nkmp->m.shift - 1, nkmp->m.shift); + p_mask = GENMASK(nkmp->p.width + nkmp->p.shift - 1, nkmp->p.shift); + spin_lock_irqsave(nkmp->common.lock, flags); reg = readl(nkmp->common.base + nkmp->common.reg); - reg &= ~GENMASK(nkmp->n.width + nkmp->n.shift - 1, nkmp->n.shift); - reg &= ~GENMASK(nkmp->k.width + nkmp->k.shift - 1, nkmp->k.shift); - reg &= ~GENMASK(nkmp->m.width + nkmp->m.shift - 1, nkmp->m.shift); - reg &= ~GENMASK(nkmp->p.width + nkmp->p.shift - 1, nkmp->p.shift); - - reg |= (_nkmp.n - nkmp->n.offset) << nkmp->n.shift; - reg |= (_nkmp.k - nkmp->k.offset) << nkmp->k.shift; - reg |= (_nkmp.m - nkmp->m.offset) << nkmp->m.shift; - reg |= ilog2(_nkmp.p) << nkmp->p.shift; + reg &= ~(n_mask | k_mask | m_mask | p_mask); + + reg |= ((_nkmp.n - nkmp->n.offset) << nkmp->n.shift) & n_mask; + reg |= ((_nkmp.k - nkmp->k.offset) << nkmp->k.shift) & k_mask; + reg |= ((_nkmp.m - nkmp->m.offset) << nkmp->m.shift) & m_mask; + reg |= (ilog2(_nkmp.p) << nkmp->p.shift) & p_mask; writel(reg, nkmp->common.base + nkmp->common.reg); -- cgit v1.3-14-g43fede From a5ebc3368ef7c77da5a03ad52834520a634a36ef Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Wed, 14 Feb 2018 21:08:56 +0100 Subject: clk: sunxi-ng: Use u64 for calculation of nkmp rate When parent rate is 24MHz and multiplier N >= 180, intermediate clock rate doesn't fit in 32 bit variable anymore. Because of that, introduce function for calculating clock rate which uses 64 bit variable for intermediate result. Acked-by: Maxime Ripard Signed-off-by: Jernej Skrabec Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu_nkmp.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c index a99068a08315..c3f6fe7be565 100644 --- a/drivers/clk/sunxi-ng/ccu_nkmp.c +++ b/drivers/clk/sunxi-ng/ccu_nkmp.c @@ -20,6 +20,18 @@ struct _ccu_nkmp { unsigned long p, min_p, max_p; }; +static unsigned long ccu_nkmp_calc_rate(unsigned long parent, + unsigned long n, unsigned long k, + unsigned long m, unsigned long p) +{ + u64 rate = parent; + + rate *= n * k; + do_div(rate, m * p); + + return rate; +} + static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate, struct _ccu_nkmp *nkmp) { @@ -33,7 +45,9 @@ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate, for (_p = nkmp->min_p; _p <= nkmp->max_p; _p <<= 1) { unsigned long tmp_rate; - tmp_rate = parent * _n * _k / (_m * _p); + tmp_rate = ccu_nkmp_calc_rate(parent, + _n, _k, + _m, _p); if (tmp_rate > rate) continue; @@ -107,7 +121,7 @@ static unsigned long ccu_nkmp_recalc_rate(struct clk_hw *hw, p = reg >> nkmp->p.shift; p &= (1 << nkmp->p.width) - 1; - return (parent_rate * n * k >> p) / m; + return ccu_nkmp_calc_rate(parent_rate, n, k, m, 1 << p); } static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate, @@ -127,7 +141,8 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate, ccu_nkmp_find_best(*parent_rate, rate, &_nkmp); - return *parent_rate * _nkmp.n * _nkmp.k / (_nkmp.m * _nkmp.p); + return ccu_nkmp_calc_rate(*parent_rate, _nkmp.n, _nkmp.k, + _nkmp.m, _nkmp.p); } static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, -- cgit v1.3-14-g43fede From da557d738466247f9437aaba2f2cd2afafa411ba Mon Sep 17 00:00:00 2001 From: Giulio Benetti Date: Thu, 15 Feb 2018 14:46:02 +0100 Subject: ARM: dts: sun7i: Add Mali node The A20 has an ARM Mali 400 GPU, so add binding to our DT. Signed-off-by: Giulio Benetti Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun7i-a20.dtsi | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 1d95885d9cf9..e529e4ff2174 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -1215,6 +1215,31 @@ #size-cells = <0>; }; + mali: gpu@1c40000 { + compatible = "allwinner,sun7i-a20-mali", "arm,mali-400"; + reg = <0x01c40000 0x10000>; + interrupts = , + , + , + , + , + , + ; + interrupt-names = "gp", + "gpmmu", + "pp0", + "ppmmu0", + "pp1", + "ppmmu1", + "pmu"; + clocks = <&ccu CLK_AHB_GPU>, <&ccu CLK_GPU>; + clock-names = "bus", "core"; + resets = <&ccu RST_GPU>; + + assigned-clocks = <&ccu CLK_GPU>; + assigned-clock-rates = <384000000>; + }; + gmac: ethernet@1c50000 { compatible = "allwinner,sun7i-a20-gmac"; reg = <0x01c50000 0x10000>; -- cgit v1.3-14-g43fede From 9618b2cbcf22fa3c206cac3984288413ef4d7194 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 13 Feb 2018 14:22:52 +0300 Subject: arm64: dts: renesas: r8a77970: add GPIO support Describe all 6 GPIO controllers in the R8A77970 device tree. Based on the original (and large) patch by Daisuke Matsushita . Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77970.dtsi | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77970.dtsi b/arch/arm64/boot/dts/renesas/r8a77970.dtsi index 8eccfec83c9c..31eeca1531f6 100644 --- a/arch/arm64/boot/dts/renesas/r8a77970.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77970.dtsi @@ -171,6 +171,96 @@ reg = <0 0xe6060000 0 0x504>; }; + gpio0: gpio@e6050000 { + compatible = "renesas,gpio-r8a77970", + "renesas,rcar-gen3-gpio"; + reg = <0 0xe6050000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 0 22>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 912>; + power-domains = <&sysc R8A77970_PD_ALWAYS_ON>; + resets = <&cpg 912>; + }; + + gpio1: gpio@e6051000 { + compatible = "renesas,gpio-r8a77970", + "renesas,rcar-gen3-gpio"; + reg = <0 0xe6051000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 32 28>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 911>; + power-domains = <&sysc R8A77970_PD_ALWAYS_ON>; + resets = <&cpg 911>; + }; + + gpio2: gpio@e6052000 { + compatible = "renesas,gpio-r8a77970", + "renesas,rcar-gen3-gpio"; + reg = <0 0xe6052000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 64 17>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 910>; + power-domains = <&sysc R8A77970_PD_ALWAYS_ON>; + resets = <&cpg 910>; + }; + + gpio3: gpio@e6053000 { + compatible = "renesas,gpio-r8a77970", + "renesas,rcar-gen3-gpio"; + reg = <0 0xe6053000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 96 17>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 909>; + power-domains = <&sysc R8A77970_PD_ALWAYS_ON>; + resets = <&cpg 909>; + }; + + gpio4: gpio@e6054000 { + compatible = "renesas,gpio-r8a77970", + "renesas,rcar-gen3-gpio"; + reg = <0 0xe6054000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 128 6>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 908>; + power-domains = <&sysc R8A77970_PD_ALWAYS_ON>; + resets = <&cpg 908>; + }; + + gpio5: gpio@e6055000 { + compatible = "renesas,gpio-r8a77970", + "renesas,rcar-gen3-gpio"; + reg = <0 0xe6055000 0 0x50>; + interrupts = ; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pfc 0 160 15>; + #interrupt-cells = <2>; + interrupt-controller; + clocks = <&cpg CPG_MOD 907>; + power-domains = <&sysc R8A77970_PD_ALWAYS_ON>; + resets = <&cpg 907>; + }; + intc_ex: interrupt-controller@e61c0000 { compatible = "renesas,intc-ex-r8a77970", "renesas,irqc"; #interrupt-cells = <2>; -- cgit v1.3-14-g43fede From 51671b265b2915846826da407a4ab7d4ba52f404 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 13 Feb 2018 14:24:14 +0300 Subject: arm64: dts: renesas: eagle: specify EtherAVB PHY IRQ Specify EtherAVB PHY IRQ in the Eagle board's device tree, now that we have the GPIO support (previously phylib had to resort to polling). Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77970-eagle.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts b/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts index f174103d2206..cb4bd40584cf 100644 --- a/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts +++ b/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts @@ -41,6 +41,8 @@ phy0: ethernet-phy@0 { rxc-skew-ps = <1500>; reg = <0>; + interrupt-parent = <&gpio1>; + interrupts = <17 IRQ_TYPE_LEVEL_LOW>; }; }; -- cgit v1.3-14-g43fede From 69e0d1b8db8f8cc319f966ec3eb2fffce28c4f28 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Tue, 13 Feb 2018 13:02:44 +0000 Subject: soc: renesas: rcar-rst: Enable watchdog as reset trigger for Gen2 This patch allows for platform specific quirks as some of the SoC need further customization for the watchdog to work properly, like for R-Car Gen2 and for RZ/G. Signed-off-by: Fabrizio Castro Signed-off-by: Ramesh Shanmugasundaram Signed-off-by: Simon Horman --- drivers/soc/renesas/rcar-rst.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index e2340eb9ea9c..34136664ece4 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -13,8 +13,18 @@ #include #include +#define WDTRSTCR_RESET 0xA55A0002 +#define WDTRSTCR 0x0054 + +static int rcar_rst_enable_wdt_reset(void __iomem *base) +{ + iowrite32(WDTRSTCR_RESET, base + WDTRSTCR); + return 0; +} + struct rst_config { - unsigned int modemr; /* Mode Monitoring Register Offset */ + unsigned int modemr; /* Mode Monitoring Register Offset */ + int (*configure)(void *base); /* Platform specific configuration */ }; static const struct rst_config rcar_rst_gen1 __initconst = { @@ -23,6 +33,11 @@ static const struct rst_config rcar_rst_gen1 __initconst = { static const struct rst_config rcar_rst_gen2 __initconst = { .modemr = 0x60, + .configure = rcar_rst_enable_wdt_reset, +}; + +static const struct rst_config rcar_rst_gen3 __initconst = { + .modemr = 0x60, }; static const struct of_device_id rcar_rst_matches[] __initconst = { @@ -38,12 +53,12 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { .compatible = "renesas,r8a7792-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7793-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7794-rst", .data = &rcar_rst_gen2 }, - /* R-Car Gen3 is handled like R-Car Gen2 */ - { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen2 }, - { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen2 }, - { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen2 }, - { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen2 }, - { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen2 }, + /* R-Car Gen3 */ + { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen3 }, { /* sentinel */ } }; @@ -72,6 +87,14 @@ static int __init rcar_rst_init(void) rcar_rst_base = base; cfg = match->data; saved_mode = ioread32(base + cfg->modemr); + if (cfg->configure) { + error = cfg->configure(base); + if (error) { + pr_warn("%pOF: Cannot run SoC specific configuration\n", + np); + goto out_put; + } + } pr_debug("%pOF: MODE = 0x%08x\n", np, saved_mode); -- cgit v1.3-14-g43fede From d7ef367be983185f336ac6e310ac9e469f53cd4a Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 14 Feb 2018 09:55:04 +0000 Subject: arm64: dts: renesas: r8a77995: add FCPV nodes The FCPVB handles the interface between the VSPB and memory, while the FCPVD handles the interface between the VSPD and memory. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995.dtsi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi index cd3c6a30fc47..196a917afea6 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi @@ -691,6 +691,33 @@ #phy-cells = <0>; status = "disabled"; }; + + fcpvb0: fcp@fe96f000 { + compatible = "renesas,fcpv"; + reg = <0 0xfe96f000 0 0x200>; + clocks = <&cpg CPG_MOD 607>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 607>; + iommus = <&ipmmu_vp0 5>; + }; + + fcpvd0: fcp@fea27000 { + compatible = "renesas,fcpv"; + reg = <0 0xfea27000 0 0x200>; + clocks = <&cpg CPG_MOD 603>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 603>; + iommus = <&ipmmu_vi0 8>; + }; + + fcpvd1: fcp@fea2f000 { + compatible = "renesas,fcpv"; + reg = <0 0xfea2f000 0 0x200>; + clocks = <&cpg CPG_MOD 602>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 602>; + iommus = <&ipmmu_vi0 9>; + }; }; timer { -- cgit v1.3-14-g43fede From 295952a183d3d10d4f532c623c51c30e25d6a421 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 14 Feb 2018 09:55:05 +0000 Subject: arm64: dts: renesas: r8a77995: add VSP instances The r8a77995 has a VSPBS to support image processing such as blending of two input images, and has two VSPDs to handle display pipelines with a DU. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart [simon: updated base address of vsp node to fea28000] Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995.dtsi | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi index 196a917afea6..621cf30e521d 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi @@ -692,6 +692,16 @@ status = "disabled"; }; + vspbs: vsp@fe960000 { + compatible = "renesas,vsp2"; + reg = <0 0xfe960000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 627>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 627>; + renesas,fcp = <&fcpvb0>; + }; + fcpvb0: fcp@fe96f000 { compatible = "renesas,fcpv"; reg = <0 0xfe96f000 0 0x200>; @@ -701,6 +711,16 @@ iommus = <&ipmmu_vp0 5>; }; + vspd0: vsp@fea20000 { + compatible = "renesas,vsp2"; + reg = <0 0xfea20000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 623>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 623>; + renesas,fcp = <&fcpvd0>; + }; + fcpvd0: fcp@fea27000 { compatible = "renesas,fcpv"; reg = <0 0xfea27000 0 0x200>; @@ -710,6 +730,16 @@ iommus = <&ipmmu_vi0 8>; }; + vspd1: vsp@fea28000 { + compatible = "renesas,vsp2"; + reg = <0 0xfea28000 0 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 622>; + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>; + resets = <&cpg 622>; + renesas,fcp = <&fcpvd1>; + }; + fcpvd1: fcp@fea2f000 { compatible = "renesas,fcpv"; reg = <0 0xfea2f000 0 0x200>; -- cgit v1.3-14-g43fede From 9d9505a2f4eb6aa29639c90b8ad596e62314faec Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Wed, 29 Nov 2017 17:06:46 +0100 Subject: arm64: dts: renesas: draak: enable SDHI2 The single SDHI controller is connected to eMMC. Signed-off-by: Ulrich Hecht Acked-by: Wolfram Sang Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995-draak.dts | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts index af07da240be0..7ea6709d706d 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts +++ b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts @@ -32,6 +32,24 @@ /* first 128MB is reserved for secure area. */ reg = <0x0 0x48000000 0x0 0x18000000>; }; + + reg_1p8v: regulator0 { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: regulator1 { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; }; &extal_clk { @@ -71,6 +89,18 @@ function = "scif2"; }; + sdhi2_pins: sd2 { + groups = "mmc_data8", "mmc_ctrl"; + function = "mmc"; + power-source = <1800>; + }; + + sdhi2_pins_uhs: sd2_uhs { + groups = "mmc_data8", "mmc_ctrl"; + function = "mmc"; + power-source = <1800>; + }; + usb0_pins: usb0 { groups = "usb0"; function = "usb0"; @@ -125,6 +155,20 @@ status = "okay"; }; +&sdhi2 { + /* used for on-board eMMC */ + pinctrl-0 = <&sdhi2_pins>; + pinctrl-1 = <&sdhi2_pins_uhs>; + pinctrl-names = "default", "state_uhs"; + + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_1p8v>; + bus-width = <8>; + mmc-hs200-1_8v; + non-removable; + status = "okay"; +}; + &usb2_phy0 { pinctrl-0 = <&usb0_pins>; pinctrl-names = "default"; -- cgit v1.3-14-g43fede From 18f1a773e3f9e6d1eb5549d98bae6f2959edecf2 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Thu, 15 Feb 2018 08:38:19 +0000 Subject: arm64: dts: renesas: r8a77995: add DU support Define the generic r8a77995 part of the DU device node. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995.dtsi | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi index 621cf30e521d..bcf737a20636 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi @@ -748,6 +748,41 @@ resets = <&cpg 602>; iommus = <&ipmmu_vi0 9>; }; + + du: display@feb00000 { + compatible = "renesas,du-r8a77995"; + reg = <0 0xfeb00000 0 0x80000>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 724>, + <&cpg CPG_MOD 723>; + clock-names = "du.0", "du.1"; + vsps = <&vspd0 0 &vspd1 0>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb: endpoint { + }; + }; + + port@1 { + reg = <1>; + du_out_lvds0: endpoint { + }; + }; + + port@2 { + reg = <2>; + du_out_lvds1: endpoint { + }; + }; + }; + }; }; timer { -- cgit v1.3-14-g43fede From 37b3c6a6404f00ed14f72ada07af58bf9b2c0bca Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 15 Feb 2018 13:11:48 -0500 Subject: [poll] annotate SAA6588_CMD_POLL users Signed-off-by: Al Viro --- drivers/media/i2c/saa6588.c | 4 ++-- drivers/media/pci/bt8xx/bttv-driver.c | 4 ++-- drivers/media/pci/saa7134/saa7134-video.c | 4 ++-- include/media/i2c/saa6588.h | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index c3089bd34df2..33d2987f9555 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -411,9 +411,9 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) break; /* --- poll() for /dev/radio --- */ case SAA6588_CMD_POLL: - a->result = 0; + a->poll_mask = 0; if (s->data_available_for_read) - a->result |= EPOLLIN | EPOLLRDNORM; + a->poll_mask |= EPOLLIN | EPOLLRDNORM; poll_wait(a->instance, &s->read_queue, a->event_list); break; diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index f697698fe38d..707f57a9f940 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3344,10 +3344,10 @@ static __poll_t radio_poll(struct file *file, poll_table *wait) radio_enable(btv); cmd.instance = file; cmd.event_list = wait; - cmd.result = res; + cmd.poll_mask = res; bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd); - return cmd.result; + return cmd.poll_mask; } static const struct v4l2_file_operations radio_fops = diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 1ca6a32ad10e..d5a8b24abfba 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1235,12 +1235,12 @@ static __poll_t radio_poll(struct file *file, poll_table *wait) cmd.instance = file; cmd.event_list = wait; - cmd.result = 0; + cmd.poll_mask = 0; mutex_lock(&dev->lock); saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd); mutex_unlock(&dev->lock); - return rc | cmd.result; + return rc | cmd.poll_mask; } /* ------------------------------------------------------------------ */ diff --git a/include/media/i2c/saa6588.h b/include/media/i2c/saa6588.h index b5ec1aa60ed5..a0825f532f71 100644 --- a/include/media/i2c/saa6588.h +++ b/include/media/i2c/saa6588.h @@ -32,6 +32,7 @@ struct saa6588_command { unsigned char __user *buffer; struct file *instance; poll_table *event_list; + __poll_t poll_mask; }; /* These ioctls are internal to the kernel */ -- cgit v1.3-14-g43fede From d8d579c316e826416a25120250fcde72d69a9f02 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 12 Feb 2018 17:31:45 +0100 Subject: ARM: dts: exynos: Add I2S1 device node to exynos5433 Add DT node for the second I2S controller available on Exynos 5433 SoC. Signed-off-by: Sylwester Nawrocki Signed-off-by: Krzysztof Kozlowski --- arch/arm64/boot/dts/exynos/exynos5433.dtsi | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arch/arm64/boot/dts/exynos/exynos5433.dtsi b/arch/arm64/boot/dts/exynos/exynos5433.dtsi index 62f276970174..3845a8de7a42 100644 --- a/arch/arm64/boot/dts/exynos/exynos5433.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos5433.dtsi @@ -1311,6 +1311,25 @@ status = "disabled"; }; + i2s1: i2s@14d60000 { + compatible = "samsung,exynos7-i2s"; + reg = <0x14d60000 0x100>; + dmas = <&pdma0 31 &pdma0 30>; + dma-names = "tx", "rx"; + interrupts = ; + clocks = <&cmu_peric CLK_PCLK_I2S1>, + <&cmu_peric CLK_PCLK_I2S1>, + <&cmu_peric CLK_SCLK_I2S1>; + clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; + #clock-cells = <1>; + samsung,supports-6ch; + samsung,supports-rstclr; + samsung,supports-tdm; + samsung,supports-low-rfs; + #sound-dai-cells = <1>; + status = "disabled"; + }; + pwm: pwm@14dd0000 { compatible = "samsung,exynos4210-pwm"; reg = <0x14dd0000 0x100>; -- cgit v1.3-14-g43fede From ac2af0fd83087258074e2e684aaeca2acd7daf5e Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 12 Feb 2018 17:31:46 +0100 Subject: ARM: dts: exynos: Update I2S0 device node in exynos5433 The i2s0 node name is changed to a more generic "i2s" and missing (optional) properties are added. The #sound-dai-cells property is required for HDMI audio support on TM2 board. Signed-off-by: Sylwester Nawrocki Signed-off-by: Krzysztof Kozlowski --- arch/arm64/boot/dts/exynos/exynos5433.dtsi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/exynos/exynos5433.dtsi b/arch/arm64/boot/dts/exynos/exynos5433.dtsi index 3845a8de7a42..2b73bd86bc56 100644 --- a/arch/arm64/boot/dts/exynos/exynos5433.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos5433.dtsi @@ -1658,7 +1658,7 @@ power-domains = <&pd_aud>; }; - i2s0: i2s0@11440000 { + i2s0: i2s@11440000 { compatible = "samsung,exynos7-i2s"; reg = <0x11440000 0x100>; dmas = <&adma 0 &adma 2>; @@ -1670,9 +1670,11 @@ <&cmu_aud CLK_SCLK_AUD_I2S>, <&cmu_aud CLK_SCLK_I2S_BCLK>; clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; + #clock-cells = <1>; pinctrl-names = "default"; pinctrl-0 = <&i2s0_bus>; power-domains = <&pd_aud>; + #sound-dai-cells = <1>; status = "disabled"; }; -- cgit v1.3-14-g43fede From cf2ad8c025402ae98a3785c133fd2ee189b3f64f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 12 Feb 2018 17:31:47 +0100 Subject: ARM: dts: exynos: Add support for HDMI audio on Exynos 5433 TM2 board This patch updates the sound node of the exynos5433-tm2 board and adds clock tree configuration in order to support HDMI sound. Signed-off-by: Sylwester Nawrocki Signed-off-by: Krzysztof Kozlowski --- .../boot/dts/exynos/exynos5433-tm2-common.dtsi | 52 ++++++++++++++++++++-- arch/arm64/boot/dts/exynos/exynos5433.dtsi | 1 + 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi b/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi index a77462da4a36..b73c389e1a8b 100644 --- a/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos5433-tm2-common.dtsi @@ -14,6 +14,7 @@ #include #include #include +#include / { aliases { @@ -112,8 +113,8 @@ sound { compatible = "samsung,tm2-audio"; - audio-codec = <&wm5110>; - i2s-controller = <&i2s0>; + audio-codec = <&wm5110>, <&hdmi>; + i2s-controller = <&i2s0 0>, <&i2s1 0>; audio-amplifier = <&max98504>; mic-bias-gpios = <&gpr3 2 GPIO_ACTIVE_HIGH>; model = "wm5110"; @@ -217,8 +218,40 @@ }; &cmu_aud { - assigned-clocks = <&cmu_aud CLK_MOUT_AUD_PLL_USER>; - assigned-clock-parents = <&cmu_top CLK_FOUT_AUD_PLL>; + assigned-clocks = <&cmu_aud CLK_MOUT_AUD_PLL_USER>, + <&cmu_aud CLK_MOUT_SCLK_AUD_I2S>, + <&cmu_aud CLK_MOUT_SCLK_AUD_PCM>, + <&cmu_top CLK_MOUT_AUD_PLL>, + <&cmu_top CLK_MOUT_AUD_PLL_USER_T>, + <&cmu_top CLK_MOUT_SCLK_AUDIO0>, + <&cmu_top CLK_MOUT_SCLK_AUDIO1>, + <&cmu_top CLK_MOUT_SCLK_SPDIF>, + + <&cmu_aud CLK_DIV_AUD_CA5>, + <&cmu_aud CLK_DIV_ACLK_AUD>, + <&cmu_aud CLK_DIV_PCLK_DBG_AUD>, + <&cmu_aud CLK_DIV_SCLK_AUD_I2S>, + <&cmu_aud CLK_DIV_SCLK_AUD_PCM>, + <&cmu_aud CLK_DIV_SCLK_AUD_SLIMBUS>, + <&cmu_aud CLK_DIV_SCLK_AUD_UART>, + <&cmu_top CLK_DIV_SCLK_AUDIO0>, + <&cmu_top CLK_DIV_SCLK_AUDIO1>, + <&cmu_top CLK_DIV_SCLK_PCM1>, + <&cmu_top CLK_DIV_SCLK_I2S1>; + + assigned-clock-parents = <&cmu_top CLK_FOUT_AUD_PLL>, + <&cmu_aud CLK_MOUT_AUD_PLL_USER>, + <&cmu_aud CLK_MOUT_AUD_PLL_USER>, + <&cmu_top CLK_FOUT_AUD_PLL>, + <&cmu_top CLK_MOUT_AUD_PLL>, + <&cmu_top CLK_MOUT_AUD_PLL_USER_T>, + <&cmu_top CLK_MOUT_AUD_PLL_USER_T>, + <&cmu_top CLK_SCLK_AUDIO0>; + + assigned-clock-rates = <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, + <196608001>, <65536001>, <32768001>, <49152001>, + <2048001>, <24576001>, <196608001>, + <24576001>, <98304001>, <2048001>, <49152001>; }; &cmu_fsys { @@ -267,6 +300,11 @@ <&cmu_top CLK_MOUT_BUS_PLL_USER>; }; +&cmu_top { + assigned-clocks = <&cmu_top CLK_FOUT_AUD_PLL>; + assigned-clock-rates = <196608001>; +}; + &cpu0 { cpu-supply = <&buck3_reg>; }; @@ -838,6 +876,12 @@ status = "okay"; }; +&i2s1 { + assigned-clocks = <&i2s1 CLK_I2S_RCLK_SRC>; + assigned-clock-parents = <&cmu_peric CLK_SCLK_I2S1>; + status = "okay"; +}; + &mshc_0 { status = "okay"; mmc-hs200-1_8v; diff --git a/arch/arm64/boot/dts/exynos/exynos5433.dtsi b/arch/arm64/boot/dts/exynos/exynos5433.dtsi index 2b73bd86bc56..c0231d077fa6 100644 --- a/arch/arm64/boot/dts/exynos/exynos5433.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos5433.dtsi @@ -969,6 +969,7 @@ ddc = <&hsi2c_11>; samsung,syscon-phandle = <&pmu_system_controller>; samsung,sysreg-phandle = <&syscon_disp>; + #sound-dai-cells = <0>; status = "disabled"; }; -- cgit v1.3-14-g43fede From 5260ecc2e0480cc7e184901ab4c3721d0c2765e3 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Wed, 14 Feb 2018 21:47:43 -0500 Subject: audit: deprecate the AUDIT_FILTER_ENTRY filter The audit entry filter has been long deprecated with userspace support finally removed in audit-v2.6.7 and plans to remove kernel support have existed since kernel-v2.6.31. Remove it. Since removing the audit entry filter, test for early return before setting up any context state. Passes audit-testsuite. See: https://github.com/linux-audit/audit-kernel/issues/6 Signed-off-by: Richard Guy Briggs Signed-off-by: Paul Moore --- kernel/auditfilter.c | 4 ++-- kernel/auditsc.c | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 739a6d2d4df8..d7a807e81451 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -258,8 +258,8 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data * goto exit_err; #ifdef CONFIG_AUDITSYSCALL case AUDIT_FILTER_ENTRY: - if (rule->action == AUDIT_ALWAYS) - goto exit_err; + pr_err("AUDIT_FILTER_ENTRY is deprecated\n"); + goto exit_err; case AUDIT_FILTER_EXIT: case AUDIT_FILTER_TASK: #endif diff --git a/kernel/auditsc.c b/kernel/auditsc.c index e80459f7e132..bc534bfb49a4 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1519,22 +1519,23 @@ void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2, if (!audit_enabled) return; - context->arch = syscall_get_arch(); - context->major = major; - context->argv[0] = a1; - context->argv[1] = a2; - context->argv[2] = a3; - context->argv[3] = a4; - state = context->state; + if (state == AUDIT_DISABLED) + return; + context->dummy = !audit_n_rules; if (!context->dummy && state == AUDIT_BUILD_CONTEXT) { context->prio = 0; - state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]); + if (auditd_test_task(tsk)) + return; } - if (state == AUDIT_DISABLED) - return; + context->arch = syscall_get_arch(); + context->major = major; + context->argv[0] = a1; + context->argv[1] = a2; + context->argv[2] = a3; + context->argv[3] = a4; context->serial = 0; context->ctime = current_kernel_time64(); context->in_syscall = 1; -- cgit v1.3-14-g43fede From 94d14e3e7b41d99f0d62a41fd856183057e1e474 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Wed, 14 Feb 2018 21:47:44 -0500 Subject: audit: bail before bug check if audit disabled If audit is disabled, who cares if there is a bug indicating syscall in process or names already recorded. Bail immediately on audit disabled. Signed-off-by: Richard Guy Briggs Signed-off-by: Paul Moore --- kernel/auditsc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index bc534bfb49a4..4e0a4ac803db 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1511,14 +1511,11 @@ void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2, struct audit_context *context = tsk->audit_context; enum audit_state state; - if (!context) + if (!audit_enabled || !context) return; BUG_ON(context->in_syscall || context->name_count); - if (!audit_enabled) - return; - state = context->state; if (state == AUDIT_DISABLED) return; -- cgit v1.3-14-g43fede From b4a304489ef95b6b921d1faa46438d9ea89cfd4b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 20 Apr 2017 22:04:48 -0700 Subject: sparc/PCI: Support arbitrary host bridge address offset Add support for arbitrary bus address offset. Previously we ignored the child (PCI) address in the "ranges" property and assumed it was always zero. That means every host bridge window mapped to PCI bus address zero, e.g., pci_bus 0000:00: root bus resource [mem 0x2000000000000-0x200007fffffff] (bus address [0x00000000-0x7fffffff]) But some systems have host bridge windows with non-zero child addresses, so parse the child address and compute the offset between the parent (CPU) and child (PCI) addresses. This allows windows like these: /pci@305: PCI MEM [mem 0x2000000100000-0x200007effffff] offset 2000000000000 pci_sun4v f02ae7f8: PCI host bridge to bus 0000:00 pci_bus 0000:00: root bus resource [mem 0x2000000100000-0x200007effffff] (bus address [0x00100000-0x7effffff]) [bhelgaas: changelog] Tested-by: Khalid Aziz Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas Acked-by: David S. Miller Cc: sparclinux@vger.kernel.org --- arch/sparc/kernel/pci.c | 6 +++--- arch/sparc/kernel/pci_common.c | 34 ++++++++++++++++++++++++++-------- arch/sparc/kernel/pci_impl.h | 4 ++++ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 220d0f36560a..41b20edb427d 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -664,12 +664,12 @@ struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm, printk("PCI: Scanning PBM %s\n", node->full_name); pci_add_resource_offset(&resources, &pbm->io_space, - pbm->io_space.start); + pbm->io_offset); pci_add_resource_offset(&resources, &pbm->mem_space, - pbm->mem_space.start); + pbm->mem_offset); if (pbm->mem64_space.flags) pci_add_resource_offset(&resources, &pbm->mem64_space, - pbm->mem_space.start); + pbm->mem64_offset); pbm->busn.start = pbm->pci_first_busno; pbm->busn.end = pbm->pci_last_busno; pbm->busn.flags = IORESOURCE_BUS; diff --git a/arch/sparc/kernel/pci_common.c b/arch/sparc/kernel/pci_common.c index 1e10fb26fa88..14fa74f90fdb 100644 --- a/arch/sparc/kernel/pci_common.c +++ b/arch/sparc/kernel/pci_common.c @@ -397,6 +397,8 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) int i, saw_mem, saw_io; int num_pbm_ranges; + /* Corresponding generic code in of_pci_get_host_bridge_resources() */ + saw_mem = saw_io = 0; pbm_ranges = of_get_property(pbm->op->dev.of_node, "ranges", &i); if (!pbm_ranges) { @@ -411,13 +413,16 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) for (i = 0; i < num_pbm_ranges; i++) { const struct linux_prom_pci_ranges *pr = &pbm_ranges[i]; - unsigned long a, size; + unsigned long a, size, region_a; u32 parent_phys_hi, parent_phys_lo; + u32 child_phys_mid, child_phys_lo; u32 size_hi, size_lo; int type; parent_phys_hi = pr->parent_phys_hi; parent_phys_lo = pr->parent_phys_lo; + child_phys_mid = pr->child_phys_mid; + child_phys_lo = pr->child_phys_lo; if (tlb_type == hypervisor) parent_phys_hi &= 0x0fffffff; @@ -427,6 +432,8 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) type = (pr->child_phys_hi >> 24) & 0x3; a = (((unsigned long)parent_phys_hi << 32UL) | ((unsigned long)parent_phys_lo << 0UL)); + region_a = (((unsigned long)child_phys_mid << 32UL) | + ((unsigned long)child_phys_lo << 0UL)); size = (((unsigned long)size_hi << 32UL) | ((unsigned long)size_lo << 0UL)); @@ -441,6 +448,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) pbm->io_space.start = a; pbm->io_space.end = a + size - 1UL; pbm->io_space.flags = IORESOURCE_IO; + pbm->io_offset = a - region_a; saw_io = 1; break; @@ -449,6 +457,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) pbm->mem_space.start = a; pbm->mem_space.end = a + size - 1UL; pbm->mem_space.flags = IORESOURCE_MEM; + pbm->mem_offset = a - region_a; saw_mem = 1; break; @@ -457,6 +466,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) pbm->mem64_space.start = a; pbm->mem64_space.end = a + size - 1UL; pbm->mem64_space.flags = IORESOURCE_MEM; + pbm->mem64_offset = a - region_a; saw_mem = 1; break; @@ -472,14 +482,22 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) prom_halt(); } - printk("%s: PCI IO[%llx] MEM[%llx]", - pbm->name, - pbm->io_space.start, - pbm->mem_space.start); + if (pbm->io_space.flags) + printk("%s: PCI IO %pR offset %llx\n", + pbm->name, &pbm->io_space, pbm->io_offset); + if (pbm->mem_space.flags) + printk("%s: PCI MEM %pR offset %llx\n", + pbm->name, &pbm->mem_space, pbm->mem_offset); + if (pbm->mem64_space.flags && pbm->mem_space.flags) { + if (pbm->mem64_space.start <= pbm->mem_space.end) + pbm->mem64_space.start = pbm->mem_space.end + 1; + if (pbm->mem64_space.start > pbm->mem64_space.end) + pbm->mem64_space.flags = 0; + } + if (pbm->mem64_space.flags) - printk(" MEM64[%llx]", - pbm->mem64_space.start); - printk("\n"); + printk("%s: PCI MEM64 %pR offset %llx\n", + pbm->name, &pbm->mem64_space, pbm->mem64_offset); pbm->io_space.name = pbm->mem_space.name = pbm->name; pbm->mem64_space.name = pbm->name; diff --git a/arch/sparc/kernel/pci_impl.h b/arch/sparc/kernel/pci_impl.h index ac172961d276..4e3d15189fa9 100644 --- a/arch/sparc/kernel/pci_impl.h +++ b/arch/sparc/kernel/pci_impl.h @@ -100,6 +100,10 @@ struct pci_pbm_info { struct resource mem_space; struct resource mem64_space; struct resource busn; + /* offset */ + resource_size_t io_offset; + resource_size_t mem_offset; + resource_size_t mem64_offset; /* Base of PCI Config space, can be per-PBM or shared. */ unsigned long config_space; -- cgit v1.3-14-g43fede From 3a148896b24adf8688dc0c59af54531931677a40 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 12 Feb 2018 09:50:25 -0800 Subject: IB/srp: Fix completion vector assignment algorithm Ensure that cv_end is equal to ibdev->num_comp_vectors for the NUMA node with the highest index. This patch improves spreading of RDMA channels over completion vectors and thereby improves performance, especially on systems with only a single NUMA node. This patch drops support for the comp_vector login parameter by ignoring the value of that parameter since I have not found a good way to combine support for that parameter and automatic spreading of RDMA channels over completion vectors. Fixes: d92c0da71a35 ("IB/srp: Add multichannel support") Reported-by: Alexander Schmid Signed-off-by: Bart Van Assche Cc: Alexander Schmid Cc: stable@vger.kernel.org Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/srp/ib_srp.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index b48843833d69..241c0e72dce3 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -3871,12 +3871,10 @@ static ssize_t srp_create_target(struct device *dev, num_online_nodes()); const int ch_end = ((node_idx + 1) * target->ch_count / num_online_nodes()); - const int cv_start = (node_idx * ibdev->num_comp_vectors / - num_online_nodes() + target->comp_vector) - % ibdev->num_comp_vectors; - const int cv_end = ((node_idx + 1) * ibdev->num_comp_vectors / - num_online_nodes() + target->comp_vector) - % ibdev->num_comp_vectors; + const int cv_start = node_idx * ibdev->num_comp_vectors / + num_online_nodes(); + const int cv_end = (node_idx + 1) * ibdev->num_comp_vectors / + num_online_nodes(); int cpu_idx = 0; for_each_online_cpu(cpu) { -- cgit v1.3-14-g43fede From c62f40bfb26356bb289267f3b802ec0434fdc118 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 14 Feb 2018 22:35:43 +0000 Subject: scsi: scsi_transport_spi: make two const arrays static, shrinks object size Don't populate the const read-only arrays spi_test_unit_ready and spi_test_unit_ready on the stack but instead make them static. Makes the object code smaller by over 100 bytes: Before: text data bss dec hex filename 40171 12832 128 53131 cf8b drivers/scsi/scsi_transport_spi.o After: text data bss dec hex filename 39922 12976 128 53026 cf22 drivers/scsi/scsi_transport_spi.o (gcc version 7.2.0 x86_64) Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c index 871ea582029e..2ca150b16764 100644 --- a/drivers/scsi/scsi_transport_spi.c +++ b/drivers/scsi/scsi_transport_spi.c @@ -822,11 +822,11 @@ spi_dv_device_get_echo_buffer(struct scsi_device *sdev, u8 *buffer) * fails, the device won't let us write to the echo buffer * so just return failure */ - const char spi_test_unit_ready[] = { + static const char spi_test_unit_ready[] = { TEST_UNIT_READY, 0, 0, 0, 0, 0 }; - const char spi_read_buffer_descriptor[] = { + static const char spi_read_buffer_descriptor[] = { READ_BUFFER, 0x0b, 0, 0, 0, 0, 0, 0, 4, 0 }; -- cgit v1.3-14-g43fede From f66b85171a0ebd55a7efc3e82575ae187d62322c Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 14 Feb 2018 11:05:57 +0100 Subject: scsi: scsi_debug: call resp_*() function after setting host_scribble Error injection in scsi_debug (e.g. opts=16, SDEBUG_OPT_TRANSPORT_ERR) currently doesn't work correctly because the test for sqcp in resp_read_dt0() and similar resp_*() functions always fails. sqcp is set from cmnd->host_scribble, which is set in schedule_resp(), which is called from scsi_debug_queuecommand() after calling the resp_* function. Defer calling resp_*() until after cmnd->host_scribble is set in schedule_resp(). Fixes: c483739430f1 "scsi_debug: add multiple queue support" Signed-off-by: Martin Wilck Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 53 +++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 905501075ec6..26ce022dd6f4 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -4315,7 +4315,10 @@ static void setup_inject(struct sdebug_queue *sqp, * SCSI_MLQUEUE_HOST_BUSY if temporarily out of resources. */ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip, - int scsi_result, int delta_jiff, int ndelay) + int scsi_result, + int (*pfp)(struct scsi_cmnd *, + struct sdebug_dev_info *), + int delta_jiff, int ndelay) { unsigned long iflags; int k, num_in_q, qdepth, inject; @@ -4331,9 +4334,6 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip, } sdp = cmnd->device; - if (unlikely(sdebug_verbose && scsi_result)) - sdev_printk(KERN_INFO, sdp, "%s: non-zero result=0x%x\n", - __func__, scsi_result); if (delta_jiff == 0) goto respond_in_thread; @@ -4388,7 +4388,6 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip, sqcp = &sqp->qc_arr[k]; sqcp->a_cmnd = cmnd; cmnd->host_scribble = (unsigned char *)sqcp; - cmnd->result = scsi_result; sd_dp = sqcp->sd_dp; spin_unlock_irqrestore(&sqp->qc_lock, iflags); if (unlikely(sdebug_every_nth && sdebug_any_injecting_opt)) @@ -4398,6 +4397,22 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip, if (sd_dp == NULL) return SCSI_MLQUEUE_HOST_BUSY; } + + cmnd->result = pfp != NULL ? pfp(cmnd, devip) : 0; + if (cmnd->result & SDEG_RES_IMMED_MASK) { + /* + * This is the F_DELAY_OVERR case. No delay. + */ + cmnd->result &= ~SDEG_RES_IMMED_MASK; + delta_jiff = ndelay = 0; + } + if (cmnd->result == 0 && scsi_result != 0) + cmnd->result = scsi_result; + + if (unlikely(sdebug_verbose && cmnd->result)) + sdev_printk(KERN_INFO, sdp, "%s: non-zero result=0x%x\n", + __func__, cmnd->result); + if (delta_jiff > 0 || ndelay > 0) { ktime_t kt; @@ -4440,7 +4455,10 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip, return 0; respond_in_thread: /* call back to mid-layer using invocation thread */ - cmnd->result = scsi_result; + cmnd->result = pfp != NULL ? pfp(cmnd, devip) : 0; + cmnd->result &= ~SDEG_RES_IMMED_MASK; + if (cmnd->result == 0 && scsi_result != 0) + cmnd->result = scsi_result; cmnd->scsi_done(cmnd); return 0; } @@ -5628,6 +5646,7 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost, struct sdebug_dev_info *devip; u8 *cmd = scp->cmnd; int (*r_pfp)(struct scsi_cmnd *, struct sdebug_dev_info *); + int (*pfp)(struct scsi_cmnd *, struct sdebug_dev_info *) = NULL; int k, na; int errsts = 0; u32 flags; @@ -5749,19 +5768,13 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost, return 0; /* ignore command: make trouble */ } if (likely(oip->pfp)) - errsts = oip->pfp(scp, devip); /* calls a resp_* function */ - else if (r_pfp) /* if leaf function ptr NULL, try the root's */ - errsts = r_pfp(scp, devip); - if (errsts & SDEG_RES_IMMED_MASK) { - errsts &= ~SDEG_RES_IMMED_MASK; - flags |= F_DELAY_OVERR; - flags &= ~F_LONG_DELAY; - } - + pfp = oip->pfp; /* calls a resp_* function */ + else + pfp = r_pfp; /* if leaf function ptr NULL, try the root's */ fini: if (F_DELAY_OVERR & flags) - return schedule_resp(scp, devip, errsts, 0, 0); + return schedule_resp(scp, devip, errsts, pfp, 0, 0); else if ((sdebug_jdelay || sdebug_ndelay) && (flags & F_LONG_DELAY)) { /* * If any delay is active, want F_LONG_DELAY to be at least 1 @@ -5771,14 +5784,14 @@ fini: int jdelay = (sdebug_jdelay < 2) ? 1 : sdebug_jdelay; jdelay = mult_frac(USER_HZ * jdelay, HZ, USER_HZ); - return schedule_resp(scp, devip, errsts, jdelay, 0); + return schedule_resp(scp, devip, errsts, pfp, jdelay, 0); } else - return schedule_resp(scp, devip, errsts, sdebug_jdelay, + return schedule_resp(scp, devip, errsts, pfp, sdebug_jdelay, sdebug_ndelay); check_cond: - return schedule_resp(scp, devip, check_condition_result, 0, 0); + return schedule_resp(scp, devip, check_condition_result, NULL, 0, 0); err_out: - return schedule_resp(scp, NULL, DID_NO_CONNECT << 16, 0, 0); + return schedule_resp(scp, NULL, DID_NO_CONNECT << 16, NULL, 0, 0); } static struct scsi_host_template sdebug_driver_template = { -- cgit v1.3-14-g43fede From cbb6813ee771abc17a713432f31820581eafd4ae Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:01 +0200 Subject: scsi: ufs: sysfs: attribute group for existing sysfs entries. This patch introduces attribute group to show existing sysfs entries. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/Makefile | 3 +- drivers/scsi/ufs/ufs-sysfs.c | 156 +++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.h | 14 ++++ drivers/scsi/ufs/ufshcd.c | 156 ++----------------------------------------- drivers/scsi/ufs/ufshcd.h | 2 + 5 files changed, 178 insertions(+), 153 deletions(-) create mode 100644 drivers/scsi/ufs/ufs-sysfs.c create mode 100644 drivers/scsi/ufs/ufs-sysfs.h diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile index 9310c6c83041..918f5791202d 100644 --- a/drivers/scsi/ufs/Makefile +++ b/drivers/scsi/ufs/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o -obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o +obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o +ufshcd-core-objs := ufshcd.o ufs-sysfs.o obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c new file mode 100644 index 000000000000..77624d0adee6 --- /dev/null +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Western Digital Corporation + +#include +#include + +#include "ufs-sysfs.h" + +static const char *ufschd_uic_link_state_to_string( + enum uic_link_state state) +{ + switch (state) { + case UIC_LINK_OFF_STATE: return "OFF"; + case UIC_LINK_ACTIVE_STATE: return "ACTIVE"; + case UIC_LINK_HIBERN8_STATE: return "HIBERN8"; + default: return "UNKNOWN"; + } +} + +static const char *ufschd_ufs_dev_pwr_mode_to_string( + enum ufs_dev_pwr_mode state) +{ + switch (state) { + case UFS_ACTIVE_PWR_MODE: return "ACTIVE"; + case UFS_SLEEP_PWR_MODE: return "SLEEP"; + case UFS_POWERDOWN_PWR_MODE: return "POWERDOWN"; + default: return "UNKNOWN"; + } +} + +static inline ssize_t ufs_sysfs_pm_lvl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count, + bool rpm) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + unsigned long flags, value; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + if (value >= UFS_PM_LVL_MAX) + return -EINVAL; + + spin_lock_irqsave(hba->host->host_lock, flags); + if (rpm) + hba->rpm_lvl = value; + else + hba->spm_lvl = value; + spin_unlock_irqrestore(hba->host->host_lock, flags); + return count; +} + +static ssize_t rpm_lvl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + int curr_len; + u8 lvl; + + curr_len = snprintf(buf, PAGE_SIZE, + "\nCurrent Runtime PM level [%d] => dev_state [%s] link_state [%s]\n", + hba->rpm_lvl, + ufschd_ufs_dev_pwr_mode_to_string( + ufs_pm_lvl_states[hba->rpm_lvl].dev_state), + ufschd_uic_link_state_to_string( + ufs_pm_lvl_states[hba->rpm_lvl].link_state)); + + curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len), + "\nAll available Runtime PM levels info:\n"); + for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++) + curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len), + "\tRuntime PM level [%d] => dev_state [%s] link_state [%s]\n", + lvl, + ufschd_ufs_dev_pwr_mode_to_string( + ufs_pm_lvl_states[lvl].dev_state), + ufschd_uic_link_state_to_string( + ufs_pm_lvl_states[lvl].link_state)); + + return curr_len; +} + +static ssize_t rpm_lvl_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return ufs_sysfs_pm_lvl_store(dev, attr, buf, count, true); +} + +static ssize_t spm_lvl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + int curr_len; + u8 lvl; + + curr_len = snprintf(buf, PAGE_SIZE, + "\nCurrent System PM level [%d] => dev_state [%s] link_state [%s]\n", + hba->spm_lvl, + ufschd_ufs_dev_pwr_mode_to_string( + ufs_pm_lvl_states[hba->spm_lvl].dev_state), + ufschd_uic_link_state_to_string( + ufs_pm_lvl_states[hba->spm_lvl].link_state)); + + curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len), + "\nAll available System PM levels info:\n"); + for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++) + curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len), + "\tSystem PM level [%d] => dev_state [%s] link_state [%s]\n", + lvl, + ufschd_ufs_dev_pwr_mode_to_string( + ufs_pm_lvl_states[lvl].dev_state), + ufschd_uic_link_state_to_string( + ufs_pm_lvl_states[lvl].link_state)); + + return curr_len; +} + +static ssize_t spm_lvl_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return ufs_sysfs_pm_lvl_store(dev, attr, buf, count, false); +} + +static DEVICE_ATTR_RW(rpm_lvl); +static DEVICE_ATTR_RW(spm_lvl); + +static struct attribute *ufs_sysfs_ufshcd_attrs[] = { + &dev_attr_rpm_lvl.attr, + &dev_attr_spm_lvl.attr, + NULL +}; + +static const struct attribute_group ufs_sysfs_default_group = { + .attrs = ufs_sysfs_ufshcd_attrs, +}; + +static const struct attribute_group *ufs_sysfs_groups[] = { + &ufs_sysfs_default_group, + NULL, +}; + +void ufs_sysfs_add_nodes(struct device *dev) +{ + int ret; + + ret = sysfs_create_groups(&dev->kobj, ufs_sysfs_groups); + if (ret) + dev_err(dev, + "%s: sysfs groups creation failed (err = %d)\n", + __func__, ret); +} + +void ufs_sysfs_remove_nodes(struct device *dev) +{ + sysfs_remove_groups(&dev->kobj, ufs_sysfs_groups); +} diff --git a/drivers/scsi/ufs/ufs-sysfs.h b/drivers/scsi/ufs/ufs-sysfs.h new file mode 100644 index 000000000000..ce58861f548a --- /dev/null +++ b/drivers/scsi/ufs/ufs-sysfs.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Copyright (C) 2018 Western Digital Corporation + */ + +#ifndef __UFS_SYSFS_H__ +#define __UFS_SYSFS_H__ + +#include + +#include "ufshcd.h" + +void ufs_sysfs_add_nodes(struct device *dev); +void ufs_sysfs_remove_nodes(struct device *dev); +#endif diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index a355d989b414..e7621a0a54b8 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -44,6 +44,7 @@ #include "ufshcd.h" #include "ufs_quirks.h" #include "unipro.h" +#include "ufs-sysfs.h" #define CREATE_TRACE_POINTS #include @@ -150,7 +151,7 @@ enum { #define ufshcd_is_ufs_dev_poweroff(h) \ ((h)->curr_dev_pwr_mode == UFS_POWERDOWN_PWR_MODE) -static struct ufs_pm_lvl_states ufs_pm_lvl_states[] = { +struct ufs_pm_lvl_states ufs_pm_lvl_states[] = { {UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE}, {UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE}, {UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE}, @@ -813,28 +814,6 @@ static inline bool ufshcd_is_hba_active(struct ufs_hba *hba) ? false : true; } -static const char *ufschd_uic_link_state_to_string( - enum uic_link_state state) -{ - switch (state) { - case UIC_LINK_OFF_STATE: return "OFF"; - case UIC_LINK_ACTIVE_STATE: return "ACTIVE"; - case UIC_LINK_HIBERN8_STATE: return "HIBERN8"; - default: return "UNKNOWN"; - } -} - -static const char *ufschd_ufs_dev_pwr_mode_to_string( - enum ufs_dev_pwr_mode state) -{ - switch (state) { - case UFS_ACTIVE_PWR_MODE: return "ACTIVE"; - case UFS_SLEEP_PWR_MODE: return "SLEEP"; - case UFS_POWERDOWN_PWR_MODE: return "POWERDOWN"; - default: return "UNKNOWN"; - } -} - u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba) { /* HCI version 1.0 and 1.1 supports UniPro 1.41 */ @@ -7585,133 +7564,6 @@ int ufshcd_runtime_idle(struct ufs_hba *hba) } EXPORT_SYMBOL(ufshcd_runtime_idle); -static inline ssize_t ufshcd_pm_lvl_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count, - bool rpm) -{ - struct ufs_hba *hba = dev_get_drvdata(dev); - unsigned long flags, value; - - if (kstrtoul(buf, 0, &value)) - return -EINVAL; - - if (value >= UFS_PM_LVL_MAX) - return -EINVAL; - - spin_lock_irqsave(hba->host->host_lock, flags); - if (rpm) - hba->rpm_lvl = value; - else - hba->spm_lvl = value; - spin_unlock_irqrestore(hba->host->host_lock, flags); - return count; -} - -static ssize_t ufshcd_rpm_lvl_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ufs_hba *hba = dev_get_drvdata(dev); - int curr_len; - u8 lvl; - - curr_len = snprintf(buf, PAGE_SIZE, - "\nCurrent Runtime PM level [%d] => dev_state [%s] link_state [%s]\n", - hba->rpm_lvl, - ufschd_ufs_dev_pwr_mode_to_string( - ufs_pm_lvl_states[hba->rpm_lvl].dev_state), - ufschd_uic_link_state_to_string( - ufs_pm_lvl_states[hba->rpm_lvl].link_state)); - - curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len), - "\nAll available Runtime PM levels info:\n"); - for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++) - curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len), - "\tRuntime PM level [%d] => dev_state [%s] link_state [%s]\n", - lvl, - ufschd_ufs_dev_pwr_mode_to_string( - ufs_pm_lvl_states[lvl].dev_state), - ufschd_uic_link_state_to_string( - ufs_pm_lvl_states[lvl].link_state)); - - return curr_len; -} - -static ssize_t ufshcd_rpm_lvl_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - return ufshcd_pm_lvl_store(dev, attr, buf, count, true); -} - -static void ufshcd_add_rpm_lvl_sysfs_nodes(struct ufs_hba *hba) -{ - hba->rpm_lvl_attr.show = ufshcd_rpm_lvl_show; - hba->rpm_lvl_attr.store = ufshcd_rpm_lvl_store; - sysfs_attr_init(&hba->rpm_lvl_attr.attr); - hba->rpm_lvl_attr.attr.name = "rpm_lvl"; - hba->rpm_lvl_attr.attr.mode = 0644; - if (device_create_file(hba->dev, &hba->rpm_lvl_attr)) - dev_err(hba->dev, "Failed to create sysfs for rpm_lvl\n"); -} - -static ssize_t ufshcd_spm_lvl_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ufs_hba *hba = dev_get_drvdata(dev); - int curr_len; - u8 lvl; - - curr_len = snprintf(buf, PAGE_SIZE, - "\nCurrent System PM level [%d] => dev_state [%s] link_state [%s]\n", - hba->spm_lvl, - ufschd_ufs_dev_pwr_mode_to_string( - ufs_pm_lvl_states[hba->spm_lvl].dev_state), - ufschd_uic_link_state_to_string( - ufs_pm_lvl_states[hba->spm_lvl].link_state)); - - curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len), - "\nAll available System PM levels info:\n"); - for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++) - curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len), - "\tSystem PM level [%d] => dev_state [%s] link_state [%s]\n", - lvl, - ufschd_ufs_dev_pwr_mode_to_string( - ufs_pm_lvl_states[lvl].dev_state), - ufschd_uic_link_state_to_string( - ufs_pm_lvl_states[lvl].link_state)); - - return curr_len; -} - -static ssize_t ufshcd_spm_lvl_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - return ufshcd_pm_lvl_store(dev, attr, buf, count, false); -} - -static void ufshcd_add_spm_lvl_sysfs_nodes(struct ufs_hba *hba) -{ - hba->spm_lvl_attr.show = ufshcd_spm_lvl_show; - hba->spm_lvl_attr.store = ufshcd_spm_lvl_store; - sysfs_attr_init(&hba->spm_lvl_attr.attr); - hba->spm_lvl_attr.attr.name = "spm_lvl"; - hba->spm_lvl_attr.attr.mode = 0644; - if (device_create_file(hba->dev, &hba->spm_lvl_attr)) - dev_err(hba->dev, "Failed to create sysfs for spm_lvl\n"); -} - -static inline void ufshcd_add_sysfs_nodes(struct ufs_hba *hba) -{ - ufshcd_add_rpm_lvl_sysfs_nodes(hba); - ufshcd_add_spm_lvl_sysfs_nodes(hba); -} - -static inline void ufshcd_remove_sysfs_nodes(struct ufs_hba *hba) -{ - device_remove_file(hba->dev, &hba->rpm_lvl_attr); - device_remove_file(hba->dev, &hba->spm_lvl_attr); -} - /** * ufshcd_shutdown - shutdown routine * @hba: per adapter instance @@ -7749,7 +7601,7 @@ EXPORT_SYMBOL(ufshcd_shutdown); */ void ufshcd_remove(struct ufs_hba *hba) { - ufshcd_remove_sysfs_nodes(hba); + ufs_sysfs_remove_nodes(hba->dev); scsi_remove_host(hba->host); /* disable interrupts */ ufshcd_disable_intr(hba, hba->intr_mask); @@ -7996,7 +7848,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) ufshcd_set_ufs_dev_active(hba); async_schedule(ufshcd_async_scan, hba); - ufshcd_add_sysfs_nodes(hba); + ufs_sysfs_add_nodes(hba->dev); return 0; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 1332e544da92..53e2779f9d06 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -985,4 +985,6 @@ static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba) hba->vops->dbg_register_dump(hba); } +extern struct ufs_pm_lvl_states ufs_pm_lvl_states[]; + #endif /* End of Header */ -- cgit v1.3-14-g43fede From 45bced87e79316ecd868aee8f187284025792c5f Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:02 +0200 Subject: scsi: ufs: sysfs: device descriptor This patch introduces a sysfs group entry for the UFS device descriptor parameters. The group adds "device_descriptor" folder under the UFS driver sysfs entry (/sys/bus/platform/drivers/ufshcd/*). The parameters are shown as hexadecimal numbers. The full information about the parameters could be found at UFS specifications 2.1. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- Documentation/ABI/testing/sysfs-driver-ufs | 223 +++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.c | 116 +++++++++++++++ drivers/scsi/ufs/ufs.h | 8 ++ drivers/scsi/ufs/ufshcd.c | 12 +- drivers/scsi/ufs/ufshcd.h | 6 + 5 files changed, 359 insertions(+), 6 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-ufs diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs new file mode 100644 index 000000000000..8da7b8448f13 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -0,0 +1,223 @@ +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/device_type +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the device type. This is one of the UFS + device descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/device_class +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the device class. This is one of the UFS + device descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/device_sub_class +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the UFS storage subclass. This is one of + the UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/protocol +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the protocol supported by an UFS device. + This is one of the UFS device descriptor parameters. + The full information about the descriptor could be found + at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/number_of_luns +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows number of logical units. This is one of + the UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/number_of_wluns +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows number of well known logical units. + This is one of the UFS device descriptor parameters. + The full information about the descriptor could be found + at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/boot_enable +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows value that indicates whether the device is + enabled for boot. This is one of the UFS device descriptor + parameters. The full information about the descriptor could + be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/descriptor_access_enable +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows value that indicates whether the device + descriptor could be read after partial initialization phase + of the boot sequence. This is one of the UFS device descriptor + parameters. The full information about the descriptor could + be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/initial_power_mode +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows value that defines the power mode after + device initialization or hardware reset. This is one of + the UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/high_priority_lun +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the high priority lun. This is one of + the UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/secure_removal_type +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the secure removal type. This is one of + the UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/support_security_lun +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows whether the security lun is supported. + This is one of the UFS device descriptor parameters. + The full information about the descriptor could be found + at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/bkops_termination_latency +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the background operations termination + latency. This is one of the UFS device descriptor parameters. + The full information about the descriptor could be found + at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/initial_active_icc_level +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the initial active ICC level. This is one + of the UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/specification_version +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the specification version. This is one + of the UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/manufacturing_date +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the manufacturing date in BCD format. + This is one of the UFS device descriptor parameters. + The full information about the descriptor could be found + at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/manufacturer_id +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the manufacturee ID. This is one of the + UFS device descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/rtt_capability +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the maximum number of outstanding RTTs + supported by the device. This is one of the UFS device + descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/rtc_update +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the frequency and method of the realtime + clock update. This is one of the UFS device descriptor + parameters. The full information about the descriptor + could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/ufs_features +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows which features are supported by the device. + This is one of the UFS device descriptor parameters. + The full information about the descriptor could be + found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/ffu_timeout +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the FFU timeout. This is one of the + UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/queue_depth +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the device queue depth. This is one of the + UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/device_version +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the device version. This is one of the + UFS device descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/number_of_secure_wpa +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows number of secure write protect areas + supported by the device. This is one of the UFS device + descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/psa_max_data_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the maximum amount of data that may be + written during the pre-soldering phase of the PSA flow. + This is one of the UFS device descriptor parameters. + The full information about the descriptor could be found + at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/psa_state_timeout +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the command maximum timeout for a change + in PSA state. This is one of the UFS device descriptor + parameters. The full information about the descriptor could + be found at UFS specifications 2.1. + The file is read only. diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index 77624d0adee6..440b94e453bf 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -3,7 +3,9 @@ #include #include +#include +#include "ufs.h" #include "ufs-sysfs.h" static const char *ufschd_uic_link_state_to_string( @@ -134,8 +136,122 @@ static const struct attribute_group ufs_sysfs_default_group = { .attrs = ufs_sysfs_ufshcd_attrs, }; +static ssize_t ufs_sysfs_read_desc_param(struct ufs_hba *hba, + enum desc_idn desc_id, + u8 desc_index, + u8 param_offset, + u8 *sysfs_buf, + u8 param_size) +{ + u8 desc_buf[8] = {0}; + int ret; + + if (param_size > 8) + return -EINVAL; + + ret = ufshcd_read_desc_param(hba, desc_id, desc_index, + param_offset, desc_buf, param_size); + if (ret) + return -EINVAL; + switch (param_size) { + case 1: + ret = sprintf(sysfs_buf, "0x%02X\n", *desc_buf); + break; + case 2: + ret = sprintf(sysfs_buf, "0x%04X\n", + get_unaligned_be16(desc_buf)); + break; + case 4: + ret = sprintf(sysfs_buf, "0x%08X\n", + get_unaligned_be32(desc_buf)); + break; + case 8: + ret = sprintf(sysfs_buf, "0x%016llX\n", + get_unaligned_be64(desc_buf)); + break; + } + + return ret; +} + +#define UFS_DESC_PARAM(_name, _puname, _duname, _size) \ +static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct ufs_hba *hba = dev_get_drvdata(dev); \ + return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_##_duname, \ + 0, _duname##_DESC_PARAM##_puname, buf, _size); \ +} \ +static DEVICE_ATTR_RO(_name) + +#define UFS_DEVICE_DESC_PARAM(_name, _uname, _size) \ + UFS_DESC_PARAM(_name, _uname, DEVICE, _size) + +UFS_DEVICE_DESC_PARAM(device_type, _DEVICE_TYPE, 1); +UFS_DEVICE_DESC_PARAM(device_class, _DEVICE_CLASS, 1); +UFS_DEVICE_DESC_PARAM(device_sub_class, _DEVICE_SUB_CLASS, 1); +UFS_DEVICE_DESC_PARAM(protocol, _PRTCL, 1); +UFS_DEVICE_DESC_PARAM(number_of_luns, _NUM_LU, 1); +UFS_DEVICE_DESC_PARAM(number_of_wluns, _NUM_WLU, 1); +UFS_DEVICE_DESC_PARAM(boot_enable, _BOOT_ENBL, 1); +UFS_DEVICE_DESC_PARAM(descriptor_access_enable, _DESC_ACCSS_ENBL, 1); +UFS_DEVICE_DESC_PARAM(initial_power_mode, _INIT_PWR_MODE, 1); +UFS_DEVICE_DESC_PARAM(high_priority_lun, _HIGH_PR_LUN, 1); +UFS_DEVICE_DESC_PARAM(secure_removal_type, _SEC_RMV_TYPE, 1); +UFS_DEVICE_DESC_PARAM(support_security_lun, _SEC_LU, 1); +UFS_DEVICE_DESC_PARAM(bkops_termination_latency, _BKOP_TERM_LT, 1); +UFS_DEVICE_DESC_PARAM(initial_active_icc_level, _ACTVE_ICC_LVL, 1); +UFS_DEVICE_DESC_PARAM(specification_version, _SPEC_VER, 2); +UFS_DEVICE_DESC_PARAM(manufacturing_date, _MANF_DATE, 2); +UFS_DEVICE_DESC_PARAM(manufacturer_id, _MANF_ID, 2); +UFS_DEVICE_DESC_PARAM(rtt_capability, _RTT_CAP, 1); +UFS_DEVICE_DESC_PARAM(rtc_update, _FRQ_RTC, 2); +UFS_DEVICE_DESC_PARAM(ufs_features, _UFS_FEAT, 1); +UFS_DEVICE_DESC_PARAM(ffu_timeout, _FFU_TMT, 1); +UFS_DEVICE_DESC_PARAM(queue_depth, _Q_DPTH, 1); +UFS_DEVICE_DESC_PARAM(device_version, _DEV_VER, 2); +UFS_DEVICE_DESC_PARAM(number_of_secure_wpa, _NUM_SEC_WPA, 1); +UFS_DEVICE_DESC_PARAM(psa_max_data_size, _PSA_MAX_DATA, 4); +UFS_DEVICE_DESC_PARAM(psa_state_timeout, _PSA_TMT, 1); + +static struct attribute *ufs_sysfs_device_descriptor[] = { + &dev_attr_device_type.attr, + &dev_attr_device_class.attr, + &dev_attr_device_sub_class.attr, + &dev_attr_protocol.attr, + &dev_attr_number_of_luns.attr, + &dev_attr_number_of_wluns.attr, + &dev_attr_boot_enable.attr, + &dev_attr_descriptor_access_enable.attr, + &dev_attr_initial_power_mode.attr, + &dev_attr_high_priority_lun.attr, + &dev_attr_secure_removal_type.attr, + &dev_attr_support_security_lun.attr, + &dev_attr_bkops_termination_latency.attr, + &dev_attr_initial_active_icc_level.attr, + &dev_attr_specification_version.attr, + &dev_attr_manufacturing_date.attr, + &dev_attr_manufacturer_id.attr, + &dev_attr_rtt_capability.attr, + &dev_attr_rtc_update.attr, + &dev_attr_ufs_features.attr, + &dev_attr_ffu_timeout.attr, + &dev_attr_queue_depth.attr, + &dev_attr_device_version.attr, + &dev_attr_number_of_secure_wpa.attr, + &dev_attr_psa_max_data_size.attr, + &dev_attr_psa_state_timeout.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_device_descriptor_group = { + .name = "device_descriptor", + .attrs = ufs_sysfs_device_descriptor, +}; + static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_default_group, + &ufs_sysfs_device_descriptor_group, NULL, }; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 54deeb754db5..6ae1e08329cc 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -220,6 +220,14 @@ enum device_desc_param { DEVICE_DESC_PARAM_UD_LEN = 0x1B, DEVICE_DESC_PARAM_RTT_CAP = 0x1C, DEVICE_DESC_PARAM_FRQ_RTC = 0x1D, + DEVICE_DESC_PARAM_UFS_FEAT = 0x1F, + DEVICE_DESC_PARAM_FFU_TMT = 0x20, + DEVICE_DESC_PARAM_Q_DPTH = 0x21, + DEVICE_DESC_PARAM_DEV_VER = 0x22, + DEVICE_DESC_PARAM_NUM_SEC_WPA = 0x24, + DEVICE_DESC_PARAM_PSA_MAX_DATA = 0x25, + DEVICE_DESC_PARAM_PSA_TMT = 0x29, + DEVICE_DESC_PARAM_PRDCT_REV = 0x2A, }; /* diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index e7621a0a54b8..540a431e38ce 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2989,12 +2989,12 @@ EXPORT_SYMBOL(ufshcd_map_desc_id_to_length); * * Return 0 in case of success, non-zero otherwise */ -static int ufshcd_read_desc_param(struct ufs_hba *hba, - enum desc_idn desc_id, - int desc_index, - u8 param_offset, - u8 *param_read_buf, - u8 param_size) +int ufshcd_read_desc_param(struct ufs_hba *hba, + enum desc_idn desc_id, + int desc_index, + u8 param_offset, + u8 *param_read_buf, + u8 param_size) { int ret; u8 *desc_buf; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 53e2779f9d06..38c307d053c7 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -841,6 +841,12 @@ static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info) } /* Expose Query-Request API */ +int ufshcd_read_desc_param(struct ufs_hba *hba, + enum desc_idn desc_id, + int desc_index, + u8 param_offset, + u8 *param_read_buf, + u8 param_size); int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, enum flag_idn idn, bool *flag_res); int ufshcd_hold(struct ufs_hba *hba, bool async); -- cgit v1.3-14-g43fede From 8c2582bfbd9abe84b9cf435996aa720a84f908c4 Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:03 +0200 Subject: scsi: ufs: sysfs: interconnect descriptor This patch introduces a sysfs group entry for the UFS interconnect descriptor parameters. The group adds "interconnect_descriptor" folder under the UFS driver sysfs entry (/sys/bus/platform/drivers/ufshcd/*). The parameters are shown as hexadecimal numbers. The full information about the parameters could be found at UFS specifications 2.1. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- Documentation/ABI/testing/sysfs-driver-ufs | 19 +++++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.c | 18 ++++++++++++++++++ drivers/scsi/ufs/ufs.h | 8 ++++++++ 3 files changed, 45 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index 8da7b8448f13..099e6fa0e301 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -221,3 +221,22 @@ Description: This file shows the command maximum timeout for a change parameters. The full information about the descriptor could be found at UFS specifications 2.1. The file is read only. + + +What: /sys/bus/platform/drivers/ufshcd/*/interconnect_descriptor/unipro_version +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the MIPI UniPro version number in BCD format. + This is one of the UFS interconnect descriptor parameters. + The full information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/interconnect_descriptor/mphy_version +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the MIPI M-PHY version number in BCD format. + This is one of the UFS interconnect descriptor parameters. + The full information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index 440b94e453bf..ba229d9f326b 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -249,9 +249,27 @@ static const struct attribute_group ufs_sysfs_device_descriptor_group = { .attrs = ufs_sysfs_device_descriptor, }; +#define UFS_INTERCONNECT_DESC_PARAM(_name, _uname, _size) \ + UFS_DESC_PARAM(_name, _uname, INTERCONNECT, _size) + +UFS_INTERCONNECT_DESC_PARAM(unipro_version, _UNIPRO_VER, 2); +UFS_INTERCONNECT_DESC_PARAM(mphy_version, _MPHY_VER, 2); + +static struct attribute *ufs_sysfs_interconnect_descriptor[] = { + &dev_attr_unipro_version.attr, + &dev_attr_mphy_version.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_interconnect_descriptor_group = { + .name = "interconnect_descriptor", + .attrs = ufs_sysfs_interconnect_descriptor, +}; + static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_default_group, &ufs_sysfs_device_descriptor_group, + &ufs_sysfs_interconnect_descriptor_group, NULL, }; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 6ae1e08329cc..773c0495c542 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -230,6 +230,14 @@ enum device_desc_param { DEVICE_DESC_PARAM_PRDCT_REV = 0x2A, }; +/* Interconnect descriptor parameters offsets in bytes*/ +enum interconnect_desc_param { + INTERCONNECT_DESC_PARAM_LEN = 0x0, + INTERCONNECT_DESC_PARAM_TYPE = 0x1, + INTERCONNECT_DESC_PARAM_UNIPRO_VER = 0x2, + INTERCONNECT_DESC_PARAM_MPHY_VER = 0x4, +}; + /* * Logical Unit Write Protect * 00h: LU not write protected -- cgit v1.3-14-g43fede From c720c091222e26d46c0fd9c70144a0f916bf3993 Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:04 +0200 Subject: scsi: ufs: sysfs: geometry descriptor This patch introduces a sysfs group entry for the UFS geometry descriptor parameters. The group adds "geometry_descriptor" folder under the UFS driver sysfs entry (/sys/bus/platform/drivers/ufshcd/*). The parameters are shown as hexadecimal numbers. The full information about the parameters could be found at UFS specifications 2.1. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- Documentation/ABI/testing/sysfs-driver-ufs | 173 +++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.c | 84 ++++++++++++++ drivers/scsi/ufs/ufs.h | 36 ++++++ 3 files changed, 293 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index 099e6fa0e301..6ea7613a3f3e 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -240,3 +240,176 @@ Description: This file shows the MIPI M-PHY version number in BCD format. The full information about the descriptor could be found at UFS specifications 2.1. The file is read only. + + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/raw_device_capacity +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the total memory quantity available to + the user to configure the device logical units. This is one + of the UFS geometry descriptor parameters. The full + information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/max_number_of_luns +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the maximum number of logical units + supported by the UFS device. This is one of the UFS + geometry descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/segment_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the segment size. This is one of the UFS + geometry descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/allocation_unit_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the allocation unit size. This is one of + the UFS geometry descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/min_addressable_block_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the minimum addressable block size. This + is one of the UFS geometry descriptor parameters. The full + information about the descriptor could be found at UFS + specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/optimal_read_block_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the optimal read block size. This is one + of the UFS geometry descriptor parameters. The full + information about the descriptor could be found at UFS + specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/optimal_write_block_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the optimal write block size. This is one + of the UFS geometry descriptor parameters. The full + information about the descriptor could be found at UFS + specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/max_in_buffer_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the maximum data-in buffer size. This + is one of the UFS geometry descriptor parameters. The full + information about the descriptor could be found at UFS + specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/max_out_buffer_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the maximum data-out buffer size. This + is one of the UFS geometry descriptor parameters. The full + information about the descriptor could be found at UFS + specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/rpmb_rw_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the maximum number of RPMB frames allowed + in Security Protocol In/Out. This is one of the UFS geometry + descriptor parameters. The full information about the + descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/dyn_capacity_resource_policy +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the dynamic capacity resource policy. This + is one of the UFS geometry descriptor parameters. The full + information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/data_ordering +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows support for out-of-order data transfer. + This is one of the UFS geometry descriptor parameters. + The full information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/max_number_of_contexts +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows maximum available number of contexts which + are supported by the device. This is one of the UFS geometry + descriptor parameters. The full information about the + descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/sys_data_tag_unit_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows system data tag unit size. This is one of + the UFS geometry descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/sys_data_tag_resource_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows maximum storage area size allocated by + the device to handle system data by the tagging mechanism. + This is one of the UFS geometry descriptor parameters. + The full information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/secure_removal_types +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows supported secure removal types. This is + one of the UFS geometry descriptor parameters. The full + information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/memory_types +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows supported memory types. This is one of + the UFS geometry descriptor parameters. The full + information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/*_memory_max_alloc_units +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the maximum number of allocation units for + different memory types (system code, non persistent, + enhanced type 1-4). This is one of the UFS geometry + descriptor parameters. The full information about the + descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/*_memory_capacity_adjustment_factor +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the memory capacity adjustment factor for + different memory types (system code, non persistent, + enhanced type 1-4). This is one of the UFS geometry + descriptor parameters. The full information about the + descriptor could be found at UFS specifications 2.1. + The file is read only. diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index ba229d9f326b..ecd3dcb05f67 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -266,10 +266,94 @@ static const struct attribute_group ufs_sysfs_interconnect_descriptor_group = { .attrs = ufs_sysfs_interconnect_descriptor, }; +#define UFS_GEOMETRY_DESC_PARAM(_name, _uname, _size) \ + UFS_DESC_PARAM(_name, _uname, GEOMETRY, _size) + +UFS_GEOMETRY_DESC_PARAM(raw_device_capacity, _DEV_CAP, 8); +UFS_GEOMETRY_DESC_PARAM(max_number_of_luns, _MAX_NUM_LUN, 1); +UFS_GEOMETRY_DESC_PARAM(segment_size, _SEG_SIZE, 4); +UFS_GEOMETRY_DESC_PARAM(allocation_unit_size, _ALLOC_UNIT_SIZE, 1); +UFS_GEOMETRY_DESC_PARAM(min_addressable_block_size, _MIN_BLK_SIZE, 1); +UFS_GEOMETRY_DESC_PARAM(optimal_read_block_size, _OPT_RD_BLK_SIZE, 1); +UFS_GEOMETRY_DESC_PARAM(optimal_write_block_size, _OPT_WR_BLK_SIZE, 1); +UFS_GEOMETRY_DESC_PARAM(max_in_buffer_size, _MAX_IN_BUF_SIZE, 1); +UFS_GEOMETRY_DESC_PARAM(max_out_buffer_size, _MAX_OUT_BUF_SIZE, 1); +UFS_GEOMETRY_DESC_PARAM(rpmb_rw_size, _RPMB_RW_SIZE, 1); +UFS_GEOMETRY_DESC_PARAM(dyn_capacity_resource_policy, _DYN_CAP_RSRC_PLC, 1); +UFS_GEOMETRY_DESC_PARAM(data_ordering, _DATA_ORDER, 1); +UFS_GEOMETRY_DESC_PARAM(max_number_of_contexts, _MAX_NUM_CTX, 1); +UFS_GEOMETRY_DESC_PARAM(sys_data_tag_unit_size, _TAG_UNIT_SIZE, 1); +UFS_GEOMETRY_DESC_PARAM(sys_data_tag_resource_size, _TAG_RSRC_SIZE, 1); +UFS_GEOMETRY_DESC_PARAM(secure_removal_types, _SEC_RM_TYPES, 1); +UFS_GEOMETRY_DESC_PARAM(memory_types, _MEM_TYPES, 2); +UFS_GEOMETRY_DESC_PARAM(sys_code_memory_max_alloc_units, + _SCM_MAX_NUM_UNITS, 4); +UFS_GEOMETRY_DESC_PARAM(sys_code_memory_capacity_adjustment_factor, + _SCM_CAP_ADJ_FCTR, 2); +UFS_GEOMETRY_DESC_PARAM(non_persist_memory_max_alloc_units, + _NPM_MAX_NUM_UNITS, 4); +UFS_GEOMETRY_DESC_PARAM(non_persist_memory_capacity_adjustment_factor, + _NPM_CAP_ADJ_FCTR, 2); +UFS_GEOMETRY_DESC_PARAM(enh1_memory_max_alloc_units, + _ENM1_MAX_NUM_UNITS, 4); +UFS_GEOMETRY_DESC_PARAM(enh1_memory_capacity_adjustment_factor, + _ENM1_CAP_ADJ_FCTR, 2); +UFS_GEOMETRY_DESC_PARAM(enh2_memory_max_alloc_units, + _ENM2_MAX_NUM_UNITS, 4); +UFS_GEOMETRY_DESC_PARAM(enh2_memory_capacity_adjustment_factor, + _ENM2_CAP_ADJ_FCTR, 2); +UFS_GEOMETRY_DESC_PARAM(enh3_memory_max_alloc_units, + _ENM3_MAX_NUM_UNITS, 4); +UFS_GEOMETRY_DESC_PARAM(enh3_memory_capacity_adjustment_factor, + _ENM3_CAP_ADJ_FCTR, 2); +UFS_GEOMETRY_DESC_PARAM(enh4_memory_max_alloc_units, + _ENM4_MAX_NUM_UNITS, 4); +UFS_GEOMETRY_DESC_PARAM(enh4_memory_capacity_adjustment_factor, + _ENM4_CAP_ADJ_FCTR, 2); + +static struct attribute *ufs_sysfs_geometry_descriptor[] = { + &dev_attr_raw_device_capacity.attr, + &dev_attr_max_number_of_luns.attr, + &dev_attr_segment_size.attr, + &dev_attr_allocation_unit_size.attr, + &dev_attr_min_addressable_block_size.attr, + &dev_attr_optimal_read_block_size.attr, + &dev_attr_optimal_write_block_size.attr, + &dev_attr_max_in_buffer_size.attr, + &dev_attr_max_out_buffer_size.attr, + &dev_attr_rpmb_rw_size.attr, + &dev_attr_dyn_capacity_resource_policy.attr, + &dev_attr_data_ordering.attr, + &dev_attr_max_number_of_contexts.attr, + &dev_attr_sys_data_tag_unit_size.attr, + &dev_attr_sys_data_tag_resource_size.attr, + &dev_attr_secure_removal_types.attr, + &dev_attr_memory_types.attr, + &dev_attr_sys_code_memory_max_alloc_units.attr, + &dev_attr_sys_code_memory_capacity_adjustment_factor.attr, + &dev_attr_non_persist_memory_max_alloc_units.attr, + &dev_attr_non_persist_memory_capacity_adjustment_factor.attr, + &dev_attr_enh1_memory_max_alloc_units.attr, + &dev_attr_enh1_memory_capacity_adjustment_factor.attr, + &dev_attr_enh2_memory_max_alloc_units.attr, + &dev_attr_enh2_memory_capacity_adjustment_factor.attr, + &dev_attr_enh3_memory_max_alloc_units.attr, + &dev_attr_enh3_memory_capacity_adjustment_factor.attr, + &dev_attr_enh4_memory_max_alloc_units.attr, + &dev_attr_enh4_memory_capacity_adjustment_factor.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_geometry_descriptor_group = { + .name = "geometry_descriptor", + .attrs = ufs_sysfs_geometry_descriptor, +}; + static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_default_group, &ufs_sysfs_device_descriptor_group, &ufs_sysfs_interconnect_descriptor_group, + &ufs_sysfs_geometry_descriptor_group, NULL, }; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 773c0495c542..04d41c8d3dc1 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -238,6 +238,42 @@ enum interconnect_desc_param { INTERCONNECT_DESC_PARAM_MPHY_VER = 0x4, }; +/* Geometry descriptor parameters offsets in bytes*/ +enum geometry_desc_param { + GEOMETRY_DESC_PARAM_LEN = 0x0, + GEOMETRY_DESC_PARAM_TYPE = 0x1, + GEOMETRY_DESC_PARAM_DEV_CAP = 0x4, + GEOMETRY_DESC_PARAM_MAX_NUM_LUN = 0xC, + GEOMETRY_DESC_PARAM_SEG_SIZE = 0xD, + GEOMETRY_DESC_PARAM_ALLOC_UNIT_SIZE = 0x11, + GEOMETRY_DESC_PARAM_MIN_BLK_SIZE = 0x12, + GEOMETRY_DESC_PARAM_OPT_RD_BLK_SIZE = 0x13, + GEOMETRY_DESC_PARAM_OPT_WR_BLK_SIZE = 0x14, + GEOMETRY_DESC_PARAM_MAX_IN_BUF_SIZE = 0x15, + GEOMETRY_DESC_PARAM_MAX_OUT_BUF_SIZE = 0x16, + GEOMETRY_DESC_PARAM_RPMB_RW_SIZE = 0x17, + GEOMETRY_DESC_PARAM_DYN_CAP_RSRC_PLC = 0x18, + GEOMETRY_DESC_PARAM_DATA_ORDER = 0x19, + GEOMETRY_DESC_PARAM_MAX_NUM_CTX = 0x1A, + GEOMETRY_DESC_PARAM_TAG_UNIT_SIZE = 0x1B, + GEOMETRY_DESC_PARAM_TAG_RSRC_SIZE = 0x1C, + GEOMETRY_DESC_PARAM_SEC_RM_TYPES = 0x1D, + GEOMETRY_DESC_PARAM_MEM_TYPES = 0x1E, + GEOMETRY_DESC_PARAM_SCM_MAX_NUM_UNITS = 0x20, + GEOMETRY_DESC_PARAM_SCM_CAP_ADJ_FCTR = 0x24, + GEOMETRY_DESC_PARAM_NPM_MAX_NUM_UNITS = 0x26, + GEOMETRY_DESC_PARAM_NPM_CAP_ADJ_FCTR = 0x2A, + GEOMETRY_DESC_PARAM_ENM1_MAX_NUM_UNITS = 0x2C, + GEOMETRY_DESC_PARAM_ENM1_CAP_ADJ_FCTR = 0x30, + GEOMETRY_DESC_PARAM_ENM2_MAX_NUM_UNITS = 0x32, + GEOMETRY_DESC_PARAM_ENM2_CAP_ADJ_FCTR = 0x36, + GEOMETRY_DESC_PARAM_ENM3_MAX_NUM_UNITS = 0x38, + GEOMETRY_DESC_PARAM_ENM3_CAP_ADJ_FCTR = 0x3C, + GEOMETRY_DESC_PARAM_ENM4_MAX_NUM_UNITS = 0x3E, + GEOMETRY_DESC_PARAM_ENM4_CAP_ADJ_FCTR = 0x42, + GEOMETRY_DESC_PARAM_OPT_LOG_BLK_SIZE = 0x44, +}; + /* * Logical Unit Write Protect * 00h: LU not write protected -- cgit v1.3-14-g43fede From c648c2d27f168ae4faeb43f8c3074226aae3862c Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:05 +0200 Subject: scsi: ufs: sysfs: health descriptor This patch introduces a sysfs group entry for the UFS health descriptor parameters. The group adds "health_descriptor" folder under the UFS driver sysfs entry (/sys/bus/platform/drivers/ufshcd/*). The parameters are shown as hexadecimal numbers. The full information about the parameters could be found at UFS specifications 2.1. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- Documentation/ABI/testing/sysfs-driver-ufs | 28 ++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.c | 20 ++++++++++++++++++++ drivers/scsi/ufs/ufs.h | 11 +++++++++++ drivers/scsi/ufs/ufshcd.c | 8 ++++++++ drivers/scsi/ufs/ufshcd.h | 1 + 5 files changed, 68 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index 6ea7613a3f3e..ddb012b05f0d 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -413,3 +413,31 @@ Description: This file shows the memory capacity adjustment factor for descriptor parameters. The full information about the descriptor could be found at UFS specifications 2.1. The file is read only. + + +What: /sys/bus/platform/drivers/ufshcd/*/health_descriptor/eol_info +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows preend of life information. This is one + of the UFS health descriptor parameters. The full + information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/health_descriptor/life_time_estimation_a +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows indication of the device life time + (method a). This is one of the UFS health descriptor + parameters. The full information about the descriptor + could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/health_descriptor/life_time_estimation_b +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows indication of the device life time + (method b). This is one of the UFS health descriptor + parameters. The full information about the descriptor + could be found at UFS specifications 2.1. + The file is read only. diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index ecd3dcb05f67..c4f18b0970db 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -349,11 +349,31 @@ static const struct attribute_group ufs_sysfs_geometry_descriptor_group = { .attrs = ufs_sysfs_geometry_descriptor, }; +#define UFS_HEALTH_DESC_PARAM(_name, _uname, _size) \ + UFS_DESC_PARAM(_name, _uname, HEALTH, _size) + +UFS_HEALTH_DESC_PARAM(eol_info, _EOL_INFO, 1); +UFS_HEALTH_DESC_PARAM(life_time_estimation_a, _LIFE_TIME_EST_A, 1); +UFS_HEALTH_DESC_PARAM(life_time_estimation_b, _LIFE_TIME_EST_B, 1); + +static struct attribute *ufs_sysfs_health_descriptor[] = { + &dev_attr_eol_info.attr, + &dev_attr_life_time_estimation_a.attr, + &dev_attr_life_time_estimation_b.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_health_descriptor_group = { + .name = "health_descriptor", + .attrs = ufs_sysfs_health_descriptor, +}; + static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_default_group, &ufs_sysfs_device_descriptor_group, &ufs_sysfs_interconnect_descriptor_group, &ufs_sysfs_geometry_descriptor_group, + &ufs_sysfs_health_descriptor_group, NULL, }; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 04d41c8d3dc1..6bfeedb934f1 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -154,6 +154,7 @@ enum desc_idn { QUERY_DESC_IDN_RFU_1 = 0x6, QUERY_DESC_IDN_GEOMETRY = 0x7, QUERY_DESC_IDN_POWER = 0x8, + QUERY_DESC_IDN_HEALTH = 0x9, QUERY_DESC_IDN_MAX, }; @@ -169,6 +170,7 @@ enum ufs_desc_def_size { QUERY_DESC_INTERCONNECT_DEF_SIZE = 0x06, QUERY_DESC_GEOMETRY_DEF_SIZE = 0x44, QUERY_DESC_POWER_DEF_SIZE = 0x62, + QUERY_DESC_HEALTH_DEF_SIZE = 0x25, }; /* Unit descriptor parameters offsets in bytes*/ @@ -274,6 +276,15 @@ enum geometry_desc_param { GEOMETRY_DESC_PARAM_OPT_LOG_BLK_SIZE = 0x44, }; +/* Health descriptor parameters offsets in bytes*/ +enum health_desc_param { + HEALTH_DESC_PARAM_LEN = 0x0, + HEALTH_DESC_PARAM_TYPE = 0x1, + HEALTH_DESC_PARAM_EOL_INFO = 0x2, + HEALTH_DESC_PARAM_LIFE_TIME_EST_A = 0x3, + HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4, +}; + /* * Logical Unit Write Protect * 00h: LU not write protected diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 540a431e38ce..b88fcf7be47b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2966,6 +2966,9 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, case QUERY_DESC_IDN_STRING: *desc_len = QUERY_DESC_MAX_SIZE; break; + case QUERY_DESC_IDN_HEALTH: + *desc_len = hba->desc_size.hlth_desc; + break; case QUERY_DESC_IDN_RFU_0: case QUERY_DESC_IDN_RFU_1: *desc_len = 0; @@ -6277,6 +6280,10 @@ static void ufshcd_init_desc_sizes(struct ufs_hba *hba) &hba->desc_size.geom_desc); if (err) hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE; + err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_HEALTH, 0, + &hba->desc_size.hlth_desc); + if (err) + hba->desc_size.hlth_desc = QUERY_DESC_HEALTH_DEF_SIZE; } static void ufshcd_def_desc_sizes(struct ufs_hba *hba) @@ -6287,6 +6294,7 @@ static void ufshcd_def_desc_sizes(struct ufs_hba *hba) hba->desc_size.conf_desc = QUERY_DESC_CONFIGURATION_DEF_SIZE; hba->desc_size.unit_desc = QUERY_DESC_UNIT_DEF_SIZE; hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE; + hba->desc_size.hlth_desc = QUERY_DESC_HEALTH_DEF_SIZE; } /** diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 38c307d053c7..515c6e6244be 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -229,6 +229,7 @@ struct ufs_desc_size { int interc_desc; int unit_desc; int conf_desc; + int hlth_desc; }; /** -- cgit v1.3-14-g43fede From c2e6e283c52f3a2c4a99774b36bcc88e7cbd5eb7 Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:06 +0200 Subject: scsi: ufs: sysfs: power descriptor This patch introduces a sysfs group entry for the UFS power descriptor parameters. The group adds "power_descriptor" folder under the UFS driver sysfs entry (/sys/bus/platform/drivers/ufshcd/*). The parameters are shown as hexadecimal numbers. The full information about the parameters could be found at UFS specifications 2.1. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- Documentation/ABI/testing/sysfs-driver-ufs | 10 +++ drivers/scsi/ufs/ufs-sysfs.c | 117 +++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index ddb012b05f0d..7460566f557d 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -441,3 +441,13 @@ Description: This file shows indication of the device life time parameters. The full information about the descriptor could be found at UFS specifications 2.1. The file is read only. + + +What: /sys/bus/platform/drivers/ufshcd/*/power_descriptor/active_icc_levels_vcc* +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows maximum VCC, VCCQ and VCCQ2 value for + active ICC levels from 0 to 15. This is one of the UFS + power descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index c4f18b0970db..16e357a95b6b 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -368,12 +368,129 @@ static const struct attribute_group ufs_sysfs_health_descriptor_group = { .attrs = ufs_sysfs_health_descriptor, }; +#define UFS_POWER_DESC_PARAM(_name, _uname, _index) \ +static ssize_t _name##_index##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct ufs_hba *hba = dev_get_drvdata(dev); \ + return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_POWER, 0, \ + PWR_DESC##_uname##_0 + _index * 2, buf, 2); \ +} \ +static DEVICE_ATTR_RO(_name##_index) + +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 0); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 1); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 2); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 3); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 4); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 5); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 6); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 7); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 8); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 9); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 10); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 11); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 12); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 13); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 14); +UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 15); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 0); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 1); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 2); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 3); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 4); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 5); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 6); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 7); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 8); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 9); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 10); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 11); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 12); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 13); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 14); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 15); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 0); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 1); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 2); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 3); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 4); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 5); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 6); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 7); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 8); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 9); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 10); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 11); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 12); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 13); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 14); +UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 15); + +static struct attribute *ufs_sysfs_power_descriptor[] = { + &dev_attr_active_icc_levels_vcc0.attr, + &dev_attr_active_icc_levels_vcc1.attr, + &dev_attr_active_icc_levels_vcc2.attr, + &dev_attr_active_icc_levels_vcc3.attr, + &dev_attr_active_icc_levels_vcc4.attr, + &dev_attr_active_icc_levels_vcc5.attr, + &dev_attr_active_icc_levels_vcc6.attr, + &dev_attr_active_icc_levels_vcc7.attr, + &dev_attr_active_icc_levels_vcc8.attr, + &dev_attr_active_icc_levels_vcc9.attr, + &dev_attr_active_icc_levels_vcc10.attr, + &dev_attr_active_icc_levels_vcc11.attr, + &dev_attr_active_icc_levels_vcc12.attr, + &dev_attr_active_icc_levels_vcc13.attr, + &dev_attr_active_icc_levels_vcc14.attr, + &dev_attr_active_icc_levels_vcc15.attr, + &dev_attr_active_icc_levels_vccq0.attr, + &dev_attr_active_icc_levels_vccq1.attr, + &dev_attr_active_icc_levels_vccq2.attr, + &dev_attr_active_icc_levels_vccq3.attr, + &dev_attr_active_icc_levels_vccq4.attr, + &dev_attr_active_icc_levels_vccq5.attr, + &dev_attr_active_icc_levels_vccq6.attr, + &dev_attr_active_icc_levels_vccq7.attr, + &dev_attr_active_icc_levels_vccq8.attr, + &dev_attr_active_icc_levels_vccq9.attr, + &dev_attr_active_icc_levels_vccq10.attr, + &dev_attr_active_icc_levels_vccq11.attr, + &dev_attr_active_icc_levels_vccq12.attr, + &dev_attr_active_icc_levels_vccq13.attr, + &dev_attr_active_icc_levels_vccq14.attr, + &dev_attr_active_icc_levels_vccq15.attr, + &dev_attr_active_icc_levels_vccq20.attr, + &dev_attr_active_icc_levels_vccq21.attr, + &dev_attr_active_icc_levels_vccq22.attr, + &dev_attr_active_icc_levels_vccq23.attr, + &dev_attr_active_icc_levels_vccq24.attr, + &dev_attr_active_icc_levels_vccq25.attr, + &dev_attr_active_icc_levels_vccq26.attr, + &dev_attr_active_icc_levels_vccq27.attr, + &dev_attr_active_icc_levels_vccq28.attr, + &dev_attr_active_icc_levels_vccq29.attr, + &dev_attr_active_icc_levels_vccq210.attr, + &dev_attr_active_icc_levels_vccq211.attr, + &dev_attr_active_icc_levels_vccq212.attr, + &dev_attr_active_icc_levels_vccq213.attr, + &dev_attr_active_icc_levels_vccq214.attr, + &dev_attr_active_icc_levels_vccq215.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_power_descriptor_group = { + .name = "power_descriptor", + .attrs = ufs_sysfs_power_descriptor, +}; + static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_default_group, &ufs_sysfs_device_descriptor_group, &ufs_sysfs_interconnect_descriptor_group, &ufs_sysfs_geometry_descriptor_group, &ufs_sysfs_health_descriptor_group, + &ufs_sysfs_power_descriptor_group, NULL, }; -- cgit v1.3-14-g43fede From 2238d31cdeabc585de5869efd11856815aa4dd13 Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:07 +0200 Subject: scsi: ufs: sysfs: string descriptors This patch introduces a sysfs group entry for the UFS string descriptors. The group adds "string_descriptors" folder under the UFS driver sysfs entry (/sys/bus/platform/drivers/ufshcd/*). The folder will contain 5 files that will show string values defined by the UFS spec: a manufacturer name, a product name, an OEM id, a serial number and a product revision. The full information about the string descriptors could be found at UFS specifications 2.1. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- Documentation/ABI/testing/sysfs-driver-ufs | 39 +++++++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.c | 56 ++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd.c | 14 ++++---- drivers/scsi/ufs/ufshcd.h | 9 +++++ 4 files changed, 111 insertions(+), 7 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index 7460566f557d..c17a9685048b 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -451,3 +451,42 @@ Description: This file shows maximum VCC, VCCQ and VCCQ2 value for power descriptor parameters. The full information about the descriptor could be found at UFS specifications 2.1. The file is read only. + + +What: /sys/bus/platform/drivers/ufshcd/*/string_descriptors/manufacturer_name +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file contains a device manufactureer name string. + The full information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/string_descriptors/product_name +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file contains a product name string. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/string_descriptors/oem_id +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file contains a OEM ID string. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/string_descriptors/serial_number +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file contains a device serial number string. The full + information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/string_descriptors/product_revision +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file contains a product revision string. The full + information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index 16e357a95b6b..c1aeb8aa3160 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -484,6 +484,61 @@ static const struct attribute_group ufs_sysfs_power_descriptor_group = { .attrs = ufs_sysfs_power_descriptor, }; +#define UFS_STRING_DESCRIPTOR(_name, _pname) \ +static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + u8 index; \ + struct ufs_hba *hba = dev_get_drvdata(dev); \ + int ret; \ + int desc_len = QUERY_DESC_MAX_SIZE; \ + u8 *desc_buf; \ + desc_buf = kzalloc(QUERY_DESC_MAX_SIZE, GFP_ATOMIC); \ + if (!desc_buf) \ + return -ENOMEM; \ + ret = ufshcd_query_descriptor_retry(hba, \ + UPIU_QUERY_OPCODE_READ_DESC, QUERY_DESC_IDN_DEVICE, \ + 0, 0, desc_buf, &desc_len); \ + if (ret) { \ + ret = -EINVAL; \ + goto out; \ + } \ + index = desc_buf[DEVICE_DESC_PARAM##_pname]; \ + memset(desc_buf, 0, QUERY_DESC_MAX_SIZE); \ + if (ufshcd_read_string_desc(hba, index, desc_buf, \ + QUERY_DESC_MAX_SIZE, true)) { \ + ret = -EINVAL; \ + goto out; \ + } \ + ret = snprintf(buf, PAGE_SIZE, "%s\n", \ + desc_buf + QUERY_DESC_HDR_SIZE); \ +out: \ + kfree(desc_buf); \ + return ret; \ +} \ +static DEVICE_ATTR_RO(_name) + +UFS_STRING_DESCRIPTOR(manufacturer_name, _MANF_NAME); +UFS_STRING_DESCRIPTOR(product_name, _PRDCT_NAME); +UFS_STRING_DESCRIPTOR(oem_id, _OEM_ID); +UFS_STRING_DESCRIPTOR(serial_number, _SN); +UFS_STRING_DESCRIPTOR(product_revision, _PRDCT_REV); + +static struct attribute *ufs_sysfs_string_descriptors[] = { + &dev_attr_manufacturer_name.attr, + &dev_attr_product_name.attr, + &dev_attr_oem_id.attr, + &dev_attr_serial_number.attr, + &dev_attr_product_revision.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_string_descriptors_group = { + .name = "string_descriptors", + .attrs = ufs_sysfs_string_descriptors, +}; + + static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_default_group, &ufs_sysfs_device_descriptor_group, @@ -491,6 +546,7 @@ static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_geometry_descriptor_group, &ufs_sysfs_health_descriptor_group, &ufs_sysfs_power_descriptor_group, + &ufs_sysfs_string_descriptors_group, NULL, }; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b88fcf7be47b..9cfdd8ed198a 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2873,11 +2873,11 @@ out: * The buf_len parameter will contain, on return, the length parameter * received on the response. */ -static int ufshcd_query_descriptor_retry(struct ufs_hba *hba, - enum query_opcode opcode, - enum desc_idn idn, u8 index, - u8 selector, - u8 *desc_buf, int *buf_len) +int ufshcd_query_descriptor_retry(struct ufs_hba *hba, + enum query_opcode opcode, + enum desc_idn idn, u8 index, + u8 selector, + u8 *desc_buf, int *buf_len) { int err; int retries; @@ -3093,8 +3093,8 @@ static int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size) * Return 0 in case of success, non-zero otherwise */ #define ASCII_STD true -static int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, - u8 *buf, u32 size, bool ascii) +int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, + u8 *buf, u32 size, bool ascii) { int err = 0; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 515c6e6244be..2dad9dada86a 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -842,6 +842,12 @@ static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info) } /* Expose Query-Request API */ +int ufshcd_query_descriptor_retry(struct ufs_hba *hba, + enum query_opcode opcode, + enum desc_idn idn, u8 index, + u8 selector, + u8 *desc_buf, int *buf_len); + int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, int desc_index, @@ -850,6 +856,9 @@ int ufshcd_read_desc_param(struct ufs_hba *hba, u8 param_size); int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, enum flag_idn idn, bool *flag_res); +int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, + u8 *buf, u32 size, bool ascii); + int ufshcd_hold(struct ufs_hba *hba, bool async); void ufshcd_release(struct ufs_hba *hba); -- cgit v1.3-14-g43fede From 86b87cde0b5581cdb1a7babeb9c4c387761f151b Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:08 +0200 Subject: scsi: core: host template attribute groups The patch introduces an additional field in the scsi_host_template structure - struct attribute_group **sdev_group. This field allows to define groups of attributes. It will provide an ability to use binary attributes as well as device attributes and to group them under subfolders if necessary. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_sysfs.c | 11 +++++++++++ include/scsi/scsi_host.h | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 91b90f672d23..e56a4ac990c0 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1310,6 +1310,13 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) } } + if (sdev->host->hostt->sdev_groups) { + error = sysfs_create_groups(&sdev->sdev_gendev.kobj, + sdev->host->hostt->sdev_groups); + if (error) + return error; + } + scsi_autopm_put_device(sdev); return error; } @@ -1349,6 +1356,10 @@ void __scsi_remove_device(struct scsi_device *sdev) if (res != 0) return; + if (sdev->host->hostt->sdev_groups) + sysfs_remove_groups(&sdev->sdev_gendev.kobj, + sdev->host->hostt->sdev_groups); + bsg_unregister_queue(sdev->request_queue); device_unregister(&sdev->sdev_dev); transport_remove_device(dev); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 1a1df0d21ee3..19317585ae48 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -476,6 +476,12 @@ struct scsi_host_template { */ struct device_attribute **sdev_attrs; + /* + * Pointer to the SCSI device attribute groups for this host, + * NULL terminated. + */ + const struct attribute_group **sdev_groups; + /* * List of hosts per template. * -- cgit v1.3-14-g43fede From d829fc8a1058851f1058b4a29ea02da125c1684a Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:09 +0200 Subject: scsi: ufs: sysfs: unit descriptor This patch introduces a sysfs group entry for the UFS unit descriptor parameters. The group adds "unit_descriptor" folder under the corresponding SCSI device sysfs entry (/sys/class/scsi_device/*/device/). The parameters are shown as hexadecimal numbers. The full information about the parameters could be found at UFS specifications 2.1. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- Documentation/ABI/testing/sysfs-driver-ufs | 108 +++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.c | 53 ++++++++++++++ drivers/scsi/ufs/ufs-sysfs.h | 2 + drivers/scsi/ufs/ufs.h | 11 +++ drivers/scsi/ufs/ufshcd.c | 23 ++---- drivers/scsi/ufs/ufshcd.h | 15 ++++ 6 files changed, 196 insertions(+), 16 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index c17a9685048b..57c6a9028108 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -490,3 +490,111 @@ Description: This file contains a product revision string. The full information about the descriptor could be found at UFS specifications 2.1. The file is read only. + + +What: /sys/class/scsi_device/*/device/unit_descriptor/boot_lun_id +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows boot LUN information. This is one of + the UFS unit descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/lun_write_protect +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows LUN write protection status. This is one of + the UFS unit descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/lun_queue_depth +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows LUN queue depth. This is one of the UFS + unit descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/psa_sensitive +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows PSA sensitivity. This is one of the UFS + unit descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/lun_memory_type +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows LUN memory type. This is one of the UFS + unit descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/data_reliability +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file defines the device behavior when a power failure + occurs during a write operation. This is one of the UFS + unit descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/logical_block_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the size of addressable logical blocks + (calculated as an exponent with base 2). This is one of + the UFS unit descriptor parameters. The full information about + the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/logical_block_count +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows total number of addressable logical blocks. + This is one of the UFS unit descriptor parameters. The full + information about the descriptor could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/erase_block_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the erase block size. This is one of + the UFS unit descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/provisioning_type +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the thin provisioning type. This is one of + the UFS unit descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/physical_memory_resourse_count +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the total physical memory resources. This is + one of the UFS unit descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/context_capabilities +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the context capabilities. This is one of + the UFS unit descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/class/scsi_device/*/device/unit_descriptor/large_unit_granularity +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the granularity of the LUN. This is one of + the UFS unit descriptor parameters. The full information + about the descriptor could be found at UFS specifications 2.1. + The file is read only. diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index c1aeb8aa3160..cd4d9d039ee9 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -550,6 +550,59 @@ static const struct attribute_group *ufs_sysfs_groups[] = { NULL, }; +#define UFS_LUN_DESC_PARAM(_pname, _puname, _duname, _size) \ +static ssize_t _pname##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct scsi_device *sdev = to_scsi_device(dev); \ + struct ufs_hba *hba = shost_priv(sdev->host); \ + u8 lun = ufshcd_scsi_to_upiu_lun(sdev->lun); \ + if (!ufs_is_valid_unit_desc_lun(lun)) \ + return -EINVAL; \ + return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_##_duname, \ + lun, _duname##_DESC_PARAM##_puname, buf, _size); \ +} \ +static DEVICE_ATTR_RO(_pname) + +#define UFS_UNIT_DESC_PARAM(_name, _uname, _size) \ + UFS_LUN_DESC_PARAM(_name, _uname, UNIT, _size) + +UFS_UNIT_DESC_PARAM(boot_lun_id, _BOOT_LUN_ID, 1); +UFS_UNIT_DESC_PARAM(lun_write_protect, _LU_WR_PROTECT, 1); +UFS_UNIT_DESC_PARAM(lun_queue_depth, _LU_Q_DEPTH, 1); +UFS_UNIT_DESC_PARAM(psa_sensitive, _PSA_SENSITIVE, 1); +UFS_UNIT_DESC_PARAM(lun_memory_type, _MEM_TYPE, 1); +UFS_UNIT_DESC_PARAM(data_reliability, _DATA_RELIABILITY, 1); +UFS_UNIT_DESC_PARAM(logical_block_size, _LOGICAL_BLK_SIZE, 1); +UFS_UNIT_DESC_PARAM(logical_block_count, _LOGICAL_BLK_COUNT, 8); +UFS_UNIT_DESC_PARAM(erase_block_size, _ERASE_BLK_SIZE, 4); +UFS_UNIT_DESC_PARAM(provisioning_type, _PROVISIONING_TYPE, 1); +UFS_UNIT_DESC_PARAM(physical_memory_resourse_count, _PHY_MEM_RSRC_CNT, 8); +UFS_UNIT_DESC_PARAM(context_capabilities, _CTX_CAPABILITIES, 2); +UFS_UNIT_DESC_PARAM(large_unit_granularity, _LARGE_UNIT_SIZE_M1, 1); + +static struct attribute *ufs_sysfs_unit_descriptor[] = { + &dev_attr_boot_lun_id.attr, + &dev_attr_lun_write_protect.attr, + &dev_attr_lun_queue_depth.attr, + &dev_attr_psa_sensitive.attr, + &dev_attr_lun_memory_type.attr, + &dev_attr_data_reliability.attr, + &dev_attr_logical_block_size.attr, + &dev_attr_logical_block_count.attr, + &dev_attr_erase_block_size.attr, + &dev_attr_provisioning_type.attr, + &dev_attr_physical_memory_resourse_count.attr, + &dev_attr_context_capabilities.attr, + &dev_attr_large_unit_granularity.attr, + NULL, +}; + +const struct attribute_group ufs_sysfs_unit_descriptor_group = { + .name = "unit_descriptor", + .attrs = ufs_sysfs_unit_descriptor, +}; + void ufs_sysfs_add_nodes(struct device *dev) { int ret; diff --git a/drivers/scsi/ufs/ufs-sysfs.h b/drivers/scsi/ufs/ufs-sysfs.h index ce58861f548a..2afce169a9f0 100644 --- a/drivers/scsi/ufs/ufs-sysfs.h +++ b/drivers/scsi/ufs/ufs-sysfs.h @@ -11,4 +11,6 @@ void ufs_sysfs_add_nodes(struct device *dev); void ufs_sysfs_remove_nodes(struct device *dev); + +extern const struct attribute_group ufs_sysfs_unit_descriptor_group; #endif diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 6bfeedb934f1..73870597e34c 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -182,6 +182,7 @@ enum unit_desc_param { UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4, UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5, UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6, + UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7, UNIT_DESC_PARAM_MEM_TYPE = 0x8, UNIT_DESC_PARAM_DATA_RELIABILITY = 0x9, UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA, @@ -592,4 +593,14 @@ struct ufs_dev_desc { char model[MAX_MODEL_LEN + 1]; }; +/** + * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor + * @lun: LU number to check + * @return: true if the lun has a matching unit descriptor, false otherwise + */ +static inline bool ufs_is_valid_unit_desc_lun(u8 lun) +{ + return lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN); +} + #endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 9cfdd8ed198a..ace3acdc9a5b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2222,21 +2222,6 @@ static int ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) return ret; } -/* - * ufshcd_scsi_to_upiu_lun - maps scsi LUN to UPIU LUN - * @scsi_lun: scsi LUN id - * - * Returns UPIU LUN id - */ -static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun) -{ - if (scsi_is_wlun(scsi_lun)) - return (scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID) - | UFS_UPIU_WLUN_ID; - else - return scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID; -} - /** * ufshcd_upiu_wlun_to_scsi_wlun - maps UPIU W-LUN id to SCSI W-LUN ID * @scsi_lun: UPIU W-LUN id @@ -3171,7 +3156,7 @@ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba, * Unit descriptors are only available for general purpose LUs (LUN id * from 0 to 7) and RPMB Well known LU. */ - if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN)) + if (!ufs_is_valid_unit_desc_lun(lun)) return -EOPNOTSUPP; return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun, @@ -6481,6 +6466,11 @@ static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd) return found ? BLK_EH_NOT_HANDLED : BLK_EH_RESET_TIMER; } +static const struct attribute_group *ufshcd_driver_groups[] = { + &ufs_sysfs_unit_descriptor_group, + NULL, +}; + static struct scsi_host_template ufshcd_driver_template = { .module = THIS_MODULE, .name = UFSHCD, @@ -6500,6 +6490,7 @@ static struct scsi_host_template ufshcd_driver_template = { .can_queue = UFSHCD_CAN_QUEUE, .max_host_blocked = 1, .track_queue_depth = 1, + .sdev_groups = ufshcd_driver_groups, }; static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg, diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 2dad9dada86a..f4cb31b23728 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -1003,4 +1003,19 @@ static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba) extern struct ufs_pm_lvl_states ufs_pm_lvl_states[]; +/* + * ufshcd_scsi_to_upiu_lun - maps scsi LUN to UPIU LUN + * @scsi_lun: scsi LUN id + * + * Returns UPIU LUN id + */ +static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun) +{ + if (scsi_is_wlun(scsi_lun)) + return (scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID) + | UFS_UPIU_WLUN_ID; + else + return scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID; +} + #endif /* End of Header */ -- cgit v1.3-14-g43fede From d10b2a8ea8fd0d6c8a667dc1950c8c061bfbbcdd Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:10 +0200 Subject: scsi: ufs: sysfs: flags This patch introduces a sysfs group entry for the UFS flags. The group adds "flags" folder under the UFS driver sysfs entry (/sys/bus/platform/drivers/ufshcd/*). The flags are shown as boolean value ("true" or "false"). The full information about the UFS flags could be found at UFS specifications 2.1. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- Documentation/ABI/testing/sysfs-driver-ufs | 65 ++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.c | 39 ++++++++++++++++++ drivers/scsi/ufs/ufs.h | 14 +++++-- 3 files changed, 115 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index 57c6a9028108..f4f49e29e225 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -598,3 +598,68 @@ Description: This file shows the granularity of the LUN. This is one of the UFS unit descriptor parameters. The full information about the descriptor could be found at UFS specifications 2.1. The file is read only. + + +What: /sys/bus/platform/drivers/ufshcd/*/flags/device_init +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the device init status. The full information + about the flag could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/flags/permanent_wpe +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows whether permanent write protection is enabled. + The full information about the flag could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/flags/power_on_wpe +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows whether write protection is enabled on all + logical units configured as power on write protected. The + full information about the flag could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/flags/bkops_enable +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows whether the device background operations are + enabled. The full information about the flag could be + found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/flags/life_span_mode_enable +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows whether the device life span mode is enabled. + The full information about the flag could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/flags/phy_resource_removal +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows whether physical resource removal is enable. + The full information about the flag could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/flags/busy_rtc +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows whether the device is executing internal + operation related to real time clock. The full information + about the flag could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/flags/disable_fw_update +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows whether the device FW update is permanently + disabled. The full information about the flag could be found + at UFS specifications 2.1. + The file is read only. diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index cd4d9d039ee9..a09a8a2ed46c 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -538,6 +538,44 @@ static const struct attribute_group ufs_sysfs_string_descriptors_group = { .attrs = ufs_sysfs_string_descriptors, }; +#define UFS_FLAG(_name, _uname) \ +static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + bool flag; \ + struct ufs_hba *hba = dev_get_drvdata(dev); \ + if (ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG, \ + QUERY_FLAG_IDN##_uname, &flag)) \ + return -EINVAL; \ + return sprintf(buf, "%s\n", flag ? "true" : "false"); \ +} \ +static DEVICE_ATTR_RO(_name) + +UFS_FLAG(device_init, _FDEVICEINIT); +UFS_FLAG(permanent_wpe, _PERMANENT_WPE); +UFS_FLAG(power_on_wpe, _PWR_ON_WPE); +UFS_FLAG(bkops_enable, _BKOPS_EN); +UFS_FLAG(life_span_mode_enable, _LIFE_SPAN_MODE_ENABLE); +UFS_FLAG(phy_resource_removal, _FPHYRESOURCEREMOVAL); +UFS_FLAG(busy_rtc, _BUSY_RTC); +UFS_FLAG(disable_fw_update, _PERMANENTLY_DISABLE_FW_UPDATE); + +static struct attribute *ufs_sysfs_device_flags[] = { + &dev_attr_device_init.attr, + &dev_attr_permanent_wpe.attr, + &dev_attr_power_on_wpe.attr, + &dev_attr_bkops_enable.attr, + &dev_attr_life_span_mode_enable.attr, + &dev_attr_phy_resource_removal.attr, + &dev_attr_busy_rtc.attr, + &dev_attr_disable_fw_update.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_flags_group = { + .name = "flags", + .attrs = ufs_sysfs_device_flags, +}; static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_default_group, @@ -547,6 +585,7 @@ static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_health_descriptor_group, &ufs_sysfs_power_descriptor_group, &ufs_sysfs_string_descriptors_group, + &ufs_sysfs_flags_group, NULL, }; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 73870597e34c..df5e73e7de3e 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -130,9 +130,17 @@ enum { /* Flag idn for Query Requests*/ enum flag_idn { - QUERY_FLAG_IDN_FDEVICEINIT = 0x01, - QUERY_FLAG_IDN_PWR_ON_WPE = 0x03, - QUERY_FLAG_IDN_BKOPS_EN = 0x04, + QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_PERMANENT_WPE = 0x02, + QUERY_FLAG_IDN_PWR_ON_WPE = 0x03, + QUERY_FLAG_IDN_BKOPS_EN = 0x04, + QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE = 0x05, + QUERY_FLAG_IDN_PURGE_ENABLE = 0x06, + QUERY_FLAG_IDN_RESERVED2 = 0x07, + QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08, + QUERY_FLAG_IDN_BUSY_RTC = 0x09, + QUERY_FLAG_IDN_RESERVED3 = 0x0A, + QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE = 0x0B, }; /* Attribute idn for Query requests */ -- cgit v1.3-14-g43fede From ec92b59cc03c3d5757449c1c9344524e088df2bd Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:11 +0200 Subject: scsi: ufs: sysfs: attributes This patch introduces a sysfs group entry for the UFS attributes. The group adds "attributes" folder under the UFS driver sysfs entry (/sys/bus/platform/drivers/ufshcd/*). The attributes are shown as hexadecimal numbers. The full information about the attributes could be found at UFS specifications 2.1. Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- Documentation/ABI/testing/sysfs-driver-ufs | 139 +++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.c | 80 +++++++++++++++++ drivers/scsi/ufs/ufs-sysfs.h | 1 + drivers/scsi/ufs/ufs.h | 27 +++++- drivers/scsi/ufs/ufshcd.c | 5 +- drivers/scsi/ufs/ufshcd.h | 3 +- 6 files changed, 248 insertions(+), 7 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index f4f49e29e225..07f1c2f8dbfc 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -663,3 +663,142 @@ Description: This file shows whether the device FW update is permanently disabled. The full information about the flag could be found at UFS specifications 2.1. The file is read only. + + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/boot_lun_enabled +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the boot lun enabled UFS device attribute. + The full information about the attribute could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/current_power_mode +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the current power mode UFS device attribute. + The full information about the attribute could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/active_icc_level +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the active icc level UFS device attribute. + The full information about the attribute could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/ooo_data_enabled +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the out of order data transfer enabled UFS + device attribute. The full information about the attribute + could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/bkops_status +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the background operations status UFS device + attribute. The full information about the attribute could + be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/purge_status +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the purge operation status UFS device + attribute. The full information about the attribute could + be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/max_data_in_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the maximum data size in a DATA IN + UPIU. The full information about the attribute could + be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/max_data_out_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the maximum number of bytes that can be + requested with a READY TO TRANSFER UPIU. The full information + about the attribute could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/reference_clock_frequency +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the reference clock frequency UFS device + attribute. The full information about the attribute could + be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/configuration_descriptor_lock +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows whether the configuration descriptor is locked. + The full information about the attribute could be found at + UFS specifications 2.1. The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/max_number_of_rtt +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the maximum current number of + outstanding RTTs in device that is allowed. The full + information about the attribute could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/exception_event_control +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the exception event control UFS device + attribute. The full information about the attribute could + be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/exception_event_status +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the exception event status UFS device + attribute. The full information about the attribute could + be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/ffu_status +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file provides the ffu status UFS device attribute. + The full information about the attribute could be found at + UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/psa_state +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file show the PSA feature status. The full information + about the attribute could be found at UFS specifications 2.1. + The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/attributes/psa_data_size +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the amount of data that the host plans to + load to all logical units in pre-soldering state. + The full information about the attribute could be found at + UFS specifications 2.1. + The file is read only. + + +What: /sys/class/scsi_device/*/device/dyn_cap_needed +Date: February 2018 +Contact: Stanislav Nijnikov +Description: This file shows the The amount of physical memory needed + to be removed from the physical memory resources pool of + the particular logical unit. The full information about + the attribute could be found at UFS specifications 2.1. + The file is read only. diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index a09a8a2ed46c..cd7174d2d225 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -577,6 +577,61 @@ static const struct attribute_group ufs_sysfs_flags_group = { .attrs = ufs_sysfs_device_flags, }; +#define UFS_ATTRIBUTE(_name, _uname) \ +static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct ufs_hba *hba = dev_get_drvdata(dev); \ + u32 value; \ + if (ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, \ + QUERY_ATTR_IDN##_uname, 0, 0, &value)) \ + return -EINVAL; \ + return sprintf(buf, "0x%08X\n", value); \ +} \ +static DEVICE_ATTR_RO(_name) + +UFS_ATTRIBUTE(boot_lun_enabled, _BOOT_LU_EN); +UFS_ATTRIBUTE(current_power_mode, _POWER_MODE); +UFS_ATTRIBUTE(active_icc_level, _ACTIVE_ICC_LVL); +UFS_ATTRIBUTE(ooo_data_enabled, _OOO_DATA_EN); +UFS_ATTRIBUTE(bkops_status, _BKOPS_STATUS); +UFS_ATTRIBUTE(purge_status, _PURGE_STATUS); +UFS_ATTRIBUTE(max_data_in_size, _MAX_DATA_IN); +UFS_ATTRIBUTE(max_data_out_size, _MAX_DATA_OUT); +UFS_ATTRIBUTE(reference_clock_frequency, _REF_CLK_FREQ); +UFS_ATTRIBUTE(configuration_descriptor_lock, _CONF_DESC_LOCK); +UFS_ATTRIBUTE(max_number_of_rtt, _MAX_NUM_OF_RTT); +UFS_ATTRIBUTE(exception_event_control, _EE_CONTROL); +UFS_ATTRIBUTE(exception_event_status, _EE_STATUS); +UFS_ATTRIBUTE(ffu_status, _FFU_STATUS); +UFS_ATTRIBUTE(psa_state, _PSA_STATE); +UFS_ATTRIBUTE(psa_data_size, _PSA_DATA_SIZE); + +static struct attribute *ufs_sysfs_attributes[] = { + &dev_attr_boot_lun_enabled.attr, + &dev_attr_current_power_mode.attr, + &dev_attr_active_icc_level.attr, + &dev_attr_ooo_data_enabled.attr, + &dev_attr_bkops_status.attr, + &dev_attr_purge_status.attr, + &dev_attr_max_data_in_size.attr, + &dev_attr_max_data_out_size.attr, + &dev_attr_reference_clock_frequency.attr, + &dev_attr_configuration_descriptor_lock.attr, + &dev_attr_max_number_of_rtt.attr, + &dev_attr_exception_event_control.attr, + &dev_attr_exception_event_status.attr, + &dev_attr_ffu_status.attr, + &dev_attr_psa_state.attr, + &dev_attr_psa_data_size.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_attributes_group = { + .name = "attributes", + .attrs = ufs_sysfs_attributes, +}; + static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_default_group, &ufs_sysfs_device_descriptor_group, @@ -586,6 +641,7 @@ static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_power_descriptor_group, &ufs_sysfs_string_descriptors_group, &ufs_sysfs_flags_group, + &ufs_sysfs_attributes_group, NULL, }; @@ -642,6 +698,30 @@ const struct attribute_group ufs_sysfs_unit_descriptor_group = { .attrs = ufs_sysfs_unit_descriptor, }; +static ssize_t dyn_cap_needed_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 value; + struct scsi_device *sdev = to_scsi_device(dev); + struct ufs_hba *hba = shost_priv(sdev->host); + u8 lun = ufshcd_scsi_to_upiu_lun(sdev->lun); + + if (ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_DYN_CAP_NEEDED, lun, 0, &value)) + return -EINVAL; + return sprintf(buf, "0x%08X\n", value); +} +static DEVICE_ATTR_RO(dyn_cap_needed_attribute); + +static struct attribute *ufs_sysfs_lun_attributes[] = { + &dev_attr_dyn_cap_needed_attribute.attr, + NULL, +}; + +const struct attribute_group ufs_sysfs_lun_attributes_group = { + .attrs = ufs_sysfs_lun_attributes, +}; + void ufs_sysfs_add_nodes(struct device *dev) { int ret; diff --git a/drivers/scsi/ufs/ufs-sysfs.h b/drivers/scsi/ufs/ufs-sysfs.h index 2afce169a9f0..e5621e59a432 100644 --- a/drivers/scsi/ufs/ufs-sysfs.h +++ b/drivers/scsi/ufs/ufs-sysfs.h @@ -13,4 +13,5 @@ void ufs_sysfs_add_nodes(struct device *dev); void ufs_sysfs_remove_nodes(struct device *dev); extern const struct attribute_group ufs_sysfs_unit_descriptor_group; +extern const struct attribute_group ufs_sysfs_lun_attributes_group; #endif diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index df5e73e7de3e..14e5bf7af0bb 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -145,10 +145,29 @@ enum flag_idn { /* Attribute idn for Query requests */ enum attr_idn { - QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03, - QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, - QUERY_ATTR_IDN_EE_CONTROL = 0x0D, - QUERY_ATTR_IDN_EE_STATUS = 0x0E, + QUERY_ATTR_IDN_BOOT_LU_EN = 0x00, + QUERY_ATTR_IDN_RESERVED = 0x01, + QUERY_ATTR_IDN_POWER_MODE = 0x02, + QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03, + QUERY_ATTR_IDN_OOO_DATA_EN = 0x04, + QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, + QUERY_ATTR_IDN_PURGE_STATUS = 0x06, + QUERY_ATTR_IDN_MAX_DATA_IN = 0x07, + QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08, + QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09, + QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A, + QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B, + QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C, + QUERY_ATTR_IDN_EE_CONTROL = 0x0D, + QUERY_ATTR_IDN_EE_STATUS = 0x0E, + QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F, + QUERY_ATTR_IDN_CNTX_CONF = 0x10, + QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11, + QUERY_ATTR_IDN_RESERVED2 = 0x12, + QUERY_ATTR_IDN_RESERVED3 = 0x13, + QUERY_ATTR_IDN_FFU_STATUS = 0x14, + QUERY_ATTR_IDN_PSA_STATE = 0x15, + QUERY_ATTR_IDN_PSA_DATA_SIZE = 0x16, }; /* Descriptor idn for Query requests */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index ace3acdc9a5b..1fbb17bb175b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2689,8 +2689,8 @@ out_unlock: * * Returns 0 for success, non-zero in case of failure */ -static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, - enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) +int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) { struct ufs_query_req *request = NULL; struct ufs_query_res *response = NULL; @@ -6468,6 +6468,7 @@ static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd) static const struct attribute_group *ufshcd_driver_groups[] = { &ufs_sysfs_unit_descriptor_group, + &ufs_sysfs_lun_attributes_group, NULL, }; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index f4cb31b23728..deb3c5d382e9 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -847,13 +847,14 @@ int ufshcd_query_descriptor_retry(struct ufs_hba *hba, enum desc_idn idn, u8 index, u8 selector, u8 *desc_buf, int *buf_len); - int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, int desc_index, u8 param_offset, u8 *param_read_buf, u8 param_size); +int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 index, u8 selector, u32 *attr_val); int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, enum flag_idn idn, bool *flag_res); int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, -- cgit v1.3-14-g43fede From 7babfa5b4bf0a9574c9b30f657b1d87abc5a84cf Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 15 Dec 2017 16:15:12 -0600 Subject: rapidio/tsi721: use PCI_EXP_DEVCTL2_COMP_TIMEOUT macro Use the existing PCI_EXP_DEVCTL2_COMP_TIMEOUT macro instead of hard-coding the PCIe Completion Timeout Value mask. No functional change intended. Signed-off-by: Bjorn Helgaas Acked-by: Alexandre Bounine --- drivers/rapidio/devices/tsi721.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index 9a68914100ad..bb655854713d 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -2880,8 +2880,9 @@ static int tsi721_probe(struct pci_dev *pdev, "Invalid MRRS override value %d", pcie_mrrs); } - /* Adjust PCIe completion timeout. */ - pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2, 0xf, 0x2); + /* Set PCIe completion timeout to 1-10ms */ + pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0x2); /* * FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block -- cgit v1.3-14-g43fede From f0b55841cc602f9a40f26ce1b0c6ae83c6bfa92f Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 17 Jan 2018 16:46:48 +0800 Subject: ARM: dts: sun9i: Add CCI-400 device nodes for A80 The A80 includes an ARM CCI-400 interconnect to support multi-cluster CPU caches. Also add the maximum clock frequency for the CPUs, as listed in the A80 Optimus Board FEX file. Acked-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- arch/arm/boot/dts/sun9i-a80.dtsi | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/arch/arm/boot/dts/sun9i-a80.dtsi b/arch/arm/boot/dts/sun9i-a80.dtsi index 90eac0b2a193..85fb800af8ab 100644 --- a/arch/arm/boot/dts/sun9i-a80.dtsi +++ b/arch/arm/boot/dts/sun9i-a80.dtsi @@ -63,48 +63,64 @@ cpu0: cpu@0 { compatible = "arm,cortex-a7"; device_type = "cpu"; + cci-control-port = <&cci_control0>; + clock-frequency = <12000000>; reg = <0x0>; }; cpu1: cpu@1 { compatible = "arm,cortex-a7"; device_type = "cpu"; + cci-control-port = <&cci_control0>; + clock-frequency = <12000000>; reg = <0x1>; }; cpu2: cpu@2 { compatible = "arm,cortex-a7"; device_type = "cpu"; + cci-control-port = <&cci_control0>; + clock-frequency = <12000000>; reg = <0x2>; }; cpu3: cpu@3 { compatible = "arm,cortex-a7"; device_type = "cpu"; + cci-control-port = <&cci_control0>; + clock-frequency = <12000000>; reg = <0x3>; }; cpu4: cpu@100 { compatible = "arm,cortex-a15"; device_type = "cpu"; + cci-control-port = <&cci_control1>; + clock-frequency = <18000000>; reg = <0x100>; }; cpu5: cpu@101 { compatible = "arm,cortex-a15"; device_type = "cpu"; + cci-control-port = <&cci_control1>; + clock-frequency = <18000000>; reg = <0x101>; }; cpu6: cpu@102 { compatible = "arm,cortex-a15"; device_type = "cpu"; + cci-control-port = <&cci_control1>; + clock-frequency = <18000000>; reg = <0x102>; }; cpu7: cpu@103 { compatible = "arm,cortex-a15"; device_type = "cpu"; + cci-control-port = <&cci_control1>; + clock-frequency = <18000000>; reg = <0x103>; }; }; @@ -431,6 +447,36 @@ interrupts = ; }; + cci: cci@1c90000 { + compatible = "arm,cci-400"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x01c90000 0x1000>; + ranges = <0x0 0x01c90000 0x10000>; + + cci_control0: slave-if@4000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x4000 0x1000>; + }; + + cci_control1: slave-if@5000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x5000 0x1000>; + }; + + pmu@9000 { + compatible = "arm,cci-400-pmu,r1"; + reg = <0x9000 0x5000>; + interrupts = , + , + , + , + ; + }; + }; + de_clocks: clock@3000000 { compatible = "allwinner,sun9i-a80-de-clks"; reg = <0x03000000 0x30>; -- cgit v1.3-14-g43fede From 61cf3ed092c68c9652271320b3808ecf4f5ed12f Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 17 Jan 2018 16:46:49 +0800 Subject: ARM: dts: sun9i: Add CPUCFG device node for A80 dtsi CPUCFG is a collection of registers that are mapped to the SoC's signals from each individual processor core and associated peripherals, such as resets for processors, L1/L2 cache and other things. These registers are used for SMP bringup and CPU hotplugging. Acked-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- arch/arm/boot/dts/sun9i-a80.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/sun9i-a80.dtsi b/arch/arm/boot/dts/sun9i-a80.dtsi index 85fb800af8ab..85ecb4d64cfd 100644 --- a/arch/arm/boot/dts/sun9i-a80.dtsi +++ b/arch/arm/boot/dts/sun9i-a80.dtsi @@ -363,6 +363,11 @@ #reset-cells = <1>; }; + cpucfg@1700000 { + compatible = "allwinner,sun9i-a80-cpucfg"; + reg = <0x01700000 0x100>; + }; + mmc0: mmc@1c0f000 { compatible = "allwinner,sun9i-a80-mmc"; reg = <0x01c0f000 0x1000>; -- cgit v1.3-14-g43fede From fd4b0c334040bb39087454d700155e5f25227ead Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 17 Jan 2018 16:46:50 +0800 Subject: ARM: dts: sun9i: Add PRCM device node for the A80 dtsi The PRCM is a collection of clock controls, reset controls, and various power switches/gates. Some of these can be independently listed and supported, while a number of CPU related ones are used in tandem with CPUCFG for SMP bringup and CPU hotplugging. Acked-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- arch/arm/boot/dts/sun9i-a80.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/sun9i-a80.dtsi b/arch/arm/boot/dts/sun9i-a80.dtsi index 85ecb4d64cfd..bf4d40e8359f 100644 --- a/arch/arm/boot/dts/sun9i-a80.dtsi +++ b/arch/arm/boot/dts/sun9i-a80.dtsi @@ -709,6 +709,11 @@ interrupts = ; }; + prcm@8001400 { + compatible = "allwinner,sun9i-a80-prcm"; + reg = <0x08001400 0x200>; + }; + apbs_rst: reset@80014b0 { reg = <0x080014b0 0x4>; compatible = "allwinner,sun6i-a31-clock-reset"; -- cgit v1.3-14-g43fede From 43f624aa4f145497d2b1814916440a285f55a959 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 17 Jan 2018 16:46:54 +0800 Subject: ARM: dts: sun9i: Add secure SRAM node used for SMP hotplug The A80 stores some magic flags in a portion of the secure SRAM. The BROM jumps directly to the software entry point set by the SMP code if the flags are set. This is required for CPU0 hotplugging. Acked-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- arch/arm/boot/dts/sun9i-a80.dtsi | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arch/arm/boot/dts/sun9i-a80.dtsi b/arch/arm/boot/dts/sun9i-a80.dtsi index bf4d40e8359f..b1c86b76ac3c 100644 --- a/arch/arm/boot/dts/sun9i-a80.dtsi +++ b/arch/arm/boot/dts/sun9i-a80.dtsi @@ -250,6 +250,25 @@ */ ranges = <0 0 0 0x20000000>; + sram_b: sram@20000 { + /* 256 KiB secure SRAM at 0x20000 */ + compatible = "mmio-sram"; + reg = <0x00020000 0x40000>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x00020000 0x40000>; + + smp-sram@1000 { + /* + * This is checked by BROM to determine if + * cpu0 should jump to SMP entry vector + */ + compatible = "allwinner,sun9i-a80-smp-sram"; + reg = <0x1000 0x8>; + }; + }; + ehci0: usb@a00000 { compatible = "allwinner,sun9i-a80-ehci", "generic-ehci"; reg = <0x00a00000 0x100>; -- cgit v1.3-14-g43fede From 28aff8c2050112f45a26baa2569366ac797b698a Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Wed, 14 Feb 2018 21:09:05 +0100 Subject: ARM: dts: sun8i: a83t: Add HDMI display pipeline This commit adds all bits necessary for HDMI on A83T - mixer1, tcon1, hdmi, hdmi phy and hdmi pinctrl entries. Signed-off-by: Jernej Skrabec Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-a83t.dtsi | 108 +++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi index 7f4955a5fab7..46ae4faa5894 100644 --- a/arch/arm/boot/dts/sun8i-a83t.dtsi +++ b/arch/arm/boot/dts/sun8i-a83t.dtsi @@ -155,7 +155,7 @@ de: display-engine { compatible = "allwinner,sun8i-a83t-display-engine"; - allwinner,pipelines = <&mixer0>; + allwinner,pipelines = <&mixer0>, <&mixer1>; status = "disabled"; }; @@ -208,6 +208,29 @@ }; }; + mixer1: mixer@1200000 { + compatible = "allwinner,sun8i-a83t-de2-mixer-1"; + reg = <0x01200000 0x100000>; + clocks = <&display_clocks CLK_BUS_MIXER1>, + <&display_clocks CLK_MIXER1>; + clock-names = "bus", + "mod"; + resets = <&display_clocks RST_WB>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + mixer1_out: port@1 { + reg = <1>; + + mixer1_out_tcon1: endpoint { + remote-endpoint = <&tcon1_in_mixer1>; + }; + }; + }; + }; + syscon: syscon@1c00000 { compatible = "allwinner,sun8i-a83t-system-controller", "syscon"; @@ -256,6 +279,40 @@ }; }; + tcon1: lcd-controller@1c0d000 { + compatible = "allwinner,sun8i-a83t-tcon-tv"; + reg = <0x01c0d000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_TCON1>, <&ccu CLK_TCON1>; + clock-names = "ahb", "tcon-ch1"; + resets = <&ccu RST_BUS_TCON1>; + reset-names = "lcd"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + tcon1_in: port@0 { + reg = <0>; + + tcon1_in_mixer1: endpoint { + remote-endpoint = <&mixer1_out_tcon1>; + }; + }; + + tcon1_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + tcon1_out_hdmi: endpoint@1 { + reg = <1>; + remote-endpoint = <&hdmi_in_tcon1>; + }; + }; + }; + }; + mmc0: mmc@1c0f000 { compatible = "allwinner,sun8i-a83t-mmc", "allwinner,sun7i-a20-mmc"; @@ -427,6 +484,11 @@ drive-strength = <40>; }; + hdmi_pins: hdmi-pins { + pins = "PH6", "PH7", "PH8"; + function = "hdmi"; + }; + i2c0_pins: i2c0-pins { pins = "PH0", "PH1"; function = "i2c0"; @@ -685,6 +747,50 @@ interrupts = ; }; + hdmi: hdmi@1ee0000 { + compatible = "allwinner,sun8i-a83t-dw-hdmi"; + reg = <0x01ee0000 0x10000>; + reg-io-width = <1>; + interrupts = ; + clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI_SLOW>, + <&ccu CLK_HDMI>; + clock-names = "iahb", "isfr", "tmds"; + resets = <&ccu RST_BUS_HDMI1>; + reset-names = "ctrl"; + phys = <&hdmi_phy>; + phy-names = "hdmi-phy"; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_pins>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + hdmi_in: port@0 { + reg = <0>; + + hdmi_in_tcon1: endpoint { + remote-endpoint = <&tcon1_out_hdmi>; + }; + }; + + hdmi_out: port@1 { + reg = <1>; + }; + }; + }; + + hdmi_phy: hdmi-phy@1ef0000 { + compatible = "allwinner,sun8i-a83t-hdmi-phy"; + reg = <0x01ef0000 0x10000>; + clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI_SLOW>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_HDMI0>; + reset-names = "phy"; + #phy-cells = <0>; + }; + r_intc: interrupt-controller@1f00c00 { compatible = "allwinner,sun8i-a83t-r-intc", "allwinner,sun6i-a31-r-intc"; -- cgit v1.3-14-g43fede From f978b05efd4e8570a442bc7173ba2b75d8a2e382 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Wed, 14 Feb 2018 21:09:06 +0100 Subject: ARM: dts: sun8i: a83t: Enable HDMI on BananaPi M3 BananaPi M3 includes HDMI connector, so add support for it. Signed-off-by: Jernej Skrabec Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts index 78fe61d97790..d4ec104f84a3 100644 --- a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts +++ b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts @@ -60,6 +60,17 @@ stdout-path = "serial0:115200n8"; }; + connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + leds { compatible = "gpio-leds"; @@ -96,6 +107,10 @@ }; }; +&de { + status = "okay"; +}; + &ehci0 { /* Terminus Tech FE 1.1s 4-port USB 2.0 hub here */ status = "okay"; @@ -114,6 +129,16 @@ status = "okay"; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &mdio { rgmii_phy: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; -- cgit v1.3-14-g43fede From 1255fe0340592a44c666b64f9d5c3650cc69bb54 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Thu, 15 Feb 2018 13:46:50 +0000 Subject: arm64: dts: rockchip: Fix RK3328 UART DMAs Using a serial console on RK3328 provokes an error from of_dma_request_slave_channel() since the UART nodes have a "dmas" property but are missing the mandatory "dma-names" to go with it. Replace the bogus "#dma-cells" - these UARTs are DMA channel consumers, not providers - with the appropriate names instead. DMA still doesn't actually work, since the PL330 driver doesn't quite implement everything the 8250 driver demands, but at least it makes the DT correct. Signed-off-by: Robin Murphy Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3328.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi index a037ee56fead..822488bdf364 100644 --- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi @@ -318,7 +318,7 @@ clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>; clock-names = "baudclk", "apb_pclk"; dmas = <&dmac 2>, <&dmac 3>; - #dma-cells = <2>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; reg-io-width = <4>; @@ -333,7 +333,7 @@ clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>; clock-names = "sclk_uart", "pclk_uart"; dmas = <&dmac 4>, <&dmac 5>; - #dma-cells = <2>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&uart1_xfer &uart1_cts &uart1_rts>; reg-io-width = <4>; @@ -348,7 +348,7 @@ clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>; clock-names = "baudclk", "apb_pclk"; dmas = <&dmac 6>, <&dmac 7>; - #dma-cells = <2>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&uart2m1_xfer>; reg-io-width = <4>; -- cgit v1.3-14-g43fede From 43a0a45abc4ab386f3ba978c877a2b68a0cad448 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 5 Feb 2018 23:01:59 +0100 Subject: mtd: nand: Get rid of comments giving the file path inside the file itself Some files add a comment giving the path of the file inside the Linux tree, which is pretty useless since the reader had to find the file to open it. Getting rid of these comments will also allow us to easily move these files around when needed. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/Makefile | 3 --- drivers/mtd/nand/ams-delta.c | 2 -- drivers/mtd/nand/au1550nd.c | 2 -- drivers/mtd/nand/bf5xx_nand.c | 3 +-- drivers/mtd/nand/cmx270_nand.c | 2 -- drivers/mtd/nand/cs553x_nand.c | 2 -- drivers/mtd/nand/diskonchip.c | 2 -- drivers/mtd/nand/fsmc_nand.c | 2 -- drivers/mtd/nand/gpio.c | 2 -- drivers/mtd/nand/nand_ecc.c | 2 -- drivers/mtd/nand/orion_nand.c | 2 -- drivers/mtd/nand/pxa3xx_nand.c | 2 -- drivers/mtd/nand/s3c2410.c | 3 +-- drivers/mtd/nand/sharpsl.c | 2 -- drivers/mtd/nand/socrates_nand.c | 2 -- include/linux/mtd/bbm.h | 2 -- include/linux/mtd/nand_ecc.h | 2 -- include/linux/mtd/ndfc.h | 2 -- 18 files changed, 2 insertions(+), 37 deletions(-) diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 921634ba400c..4e0982476267 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,7 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -# -# linux/drivers/nand/Makefile -# obj-$(CONFIG_MTD_NAND) += nand.o obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index d60ada45c549..e15991d81a20 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/ams-delta.c - * * Copyright (C) 2006 Jonathan McDowell * * Derived from drivers/mtd/toto.c diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 8ab827edf94e..df0ef1f1e2f5 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/au1550nd.c - * * Copyright (C) 2004 Embedded Edge, LLC * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 87bbd177b3e5..4a5f56f76efd 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -1,5 +1,4 @@ -/* linux/drivers/mtd/nand/bf5xx_nand.c - * +/* * Copyright 2006-2008 Analog Devices Inc. * http://blackfin.uclinux.org/ * Bryan Wu diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index b01c9804590e..66749ade9654 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -1,6 +1,4 @@ /* - * linux/drivers/mtd/nand/cmx270-nand.c - * * Copyright (C) 2006 Compulab, Ltd. * Mike Rapoport * diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index d48877540f14..be1f28fc7363 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/cs553x_nand.c - * * (C) 2005, 2006 Red Hat Inc. * * Author: David Woodhouse diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 6bc93ea66f50..1af77f798fe5 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/diskonchip.c - * * (C) 2003 Red Hat, Inc. * (C) 2004 Dan Brown * (C) 2004 Kalev Lember diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index f49ed46fa770..e763161a7c82 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/fsmc_nand.c - * * ST Microelectronics * Flexible Static Memory Controller (FSMC) * Driver for NAND portions diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index a8bde6665c24..2780af26d9ab 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/gpio.c - * * Updated, and converted to generic GPIO based driver by Russell King. * * Written by Ben Dooks diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 7613a0388044..3630f0fe8fa4 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -2,8 +2,6 @@ * This file contains an ECC algorithm that detects and corrects 1 bit * errors in a 256 byte block of data. * - * drivers/mtd/nand/nand_ecc.c - * * Copyright © 2008 Koninklijke Philips Electronics NV. * Author: Frans Meulenbroeks * diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 5a5aa1f07d07..7825fd3ce66b 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/orion_nand.c - * * NAND support for Marvell Orion SoC platforms * * Tzachi Perelstein diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index d1979c7dbe7e..d75f30263d21 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/pxa3xx_nand.c - * * Copyright © 2005 Intel Corporation * Copyright © 2006 Marvell International Ltd. * diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 4c383eeec6f6..b5bc5f106c09 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -1,5 +1,4 @@ -/* linux/drivers/mtd/nand/s3c2410.c - * +/* * Copyright © 2004-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index f59c455d9f51..e93df02c825e 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/sharpsl.c - * * Copyright (C) 2004 Richard Purdie * Copyright (C) 2008 Dmitry Baryshkov * diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index 575997d0ef8a..9824a9923583 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/socrates_nand.c - * * Copyright © 2008 Ilya Yanok, Emcraft Systems * * diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 3bf8f954b642..3102bd754d18 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -1,6 +1,4 @@ /* - * linux/include/linux/mtd/bbm.h - * * NAND family Bad Block Management (BBM) header file * - Bad Block Table (BBT) implementation * diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index 4d8406c81652..8a2decf7462c 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand_ecc.h - * * Copyright (C) 2000-2010 Steven J. Hill * David Woodhouse * Thomas Gleixner diff --git a/include/linux/mtd/ndfc.h b/include/linux/mtd/ndfc.h index d0558a982628..357e88b3263a 100644 --- a/include/linux/mtd/ndfc.h +++ b/include/linux/mtd/ndfc.h @@ -1,6 +1,4 @@ /* - * linux/include/linux/mtd/ndfc.h - * * Copyright (c) 2006 Thomas Gleixner * * This program is free software; you can redistribute it and/or modify -- cgit v1.3-14-g43fede From 93cbd6f354f178b6e0a7d80482981520be6347b4 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 5 Feb 2018 23:02:00 +0100 Subject: mtd: nand: Stop using full path when referring to files placed in the same dir Some NAND drivers are derived from other NAND drivers and state it in their license header. Using full path to point to other driver files sitting in the same directory is not such a good idea, since the NAND drivers might be moved to a different directory at some point, and we don't to patch all license/copyright headers everytime this happen. The only exception where we keep full path is when the referred source files no longer exist in the tree. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/ams-delta.c | 2 +- drivers/mtd/nand/bf5xx_nand.c | 4 ++-- drivers/mtd/nand/mpc5121_nfc.c | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index e15991d81a20..f3eaea9df90b 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -3,7 +3,7 @@ * * Derived from drivers/mtd/toto.c * Converted to platform driver by Janusz Krzysztofik - * Partially stolen from drivers/mtd/nand/plat_nand.c + * Partially stolen from plat_nand.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 4a5f56f76efd..9a1d8d104570 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -5,10 +5,10 @@ * * Blackfin BF5xx on-chip NAND flash controller driver * - * Derived from drivers/mtd/nand/s3c2410.c + * Derived from s3c2410.c * Copyright (c) 2007 Ben Dooks * - * Derived from drivers/mtd/nand/cafe.c + * Derived from cafe.c * Copyright © 2006 Red Hat, Inc. * Copyright © 2006 David Woodhouse * diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index b6b97cc9fba6..913b9d1225c6 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -6,9 +6,8 @@ * by OSADL membership fees in 2009; for details see www.osadl.org. * * Based on original driver from Freescale Semiconductor - * written by John Rigby on basis - * of drivers/mtd/nand/mxc_nand.c. Reworked and extended - * Piotr Ziecik . + * written by John Rigby on basis of mxc_nand.c. + * Reworked and extended by Piotr Ziecik . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License -- cgit v1.3-14-g43fede From e36f2eb6ca707853b17fea63e222e86f914617cc Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 5 Feb 2018 23:02:01 +0100 Subject: mtd: nand: ams-delta: Fix path to toto.c source file Last known location of toto.c was drivers/mtd/nand/toto.c. Fix the path so that one can use git log to find when the driver was deleted and possibly checkout the source code. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/ams-delta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index f3eaea9df90b..25a92474fa59 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2006 Jonathan McDowell * - * Derived from drivers/mtd/toto.c + * Derived from drivers/mtd/nand/toto.c * Converted to platform driver by Janusz Krzysztofik * Partially stolen from plat_nand.c * -- cgit v1.3-14-g43fede From 187c54488ead35d19082d3ee75afdbe94d3d4026 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 5 Feb 2018 23:02:02 +0100 Subject: mtd: nand: State when references to other drivers are no longer valid A lot of NAND drivers have been derived from other old NAND drivers which have since then been removed from the Linux tree. When this is the case, specify when the file the header is referring to has been removed so that people can find the original implementation more easily. Signed-off-by: Boris Brezillon --- arch/cris/arch-v32/drivers/mach-a3/nandflash.c | 2 +- arch/cris/arch-v32/drivers/mach-fs/nandflash.c | 2 +- drivers/mtd/nand/ams-delta.c | 2 +- drivers/mtd/nand/atmel/nand-controller.c | 4 ++-- drivers/mtd/nand/atmel/pmecc.c | 4 ++-- drivers/mtd/nand/atmel/pmecc.h | 4 ++-- drivers/mtd/nand/cmx270_nand.c | 2 +- drivers/mtd/nand/fsmc_nand.c | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/cris/arch-v32/drivers/mach-a3/nandflash.c b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c index 925a98eb6d68..7ec29d2d3661 100644 --- a/arch/cris/arch-v32/drivers/mach-a3/nandflash.c +++ b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 * - * Derived from drivers/mtd/nand/spia.c + * Derived from drivers/mtd/nand/spia.c (removed in v3.8) * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * * This program is free software; you can redistribute it and/or modify diff --git a/arch/cris/arch-v32/drivers/mach-fs/nandflash.c b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c index 53b56a429dde..7ce72906039a 100644 --- a/arch/cris/arch-v32/drivers/mach-fs/nandflash.c +++ b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c @@ -3,7 +3,7 @@ * * Copyright (c) 2004 * - * Derived from drivers/mtd/nand/spia.c + * Derived from drivers/mtd/nand/spia.c (removed in v3.8) * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 25a92474fa59..24173a0a7875 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2006 Jonathan McDowell * - * Derived from drivers/mtd/nand/toto.c + * Derived from drivers/mtd/nand/toto.c (removed in v2.6.28) * Converted to platform driver by Janusz Krzysztofik * Partially stolen from plat_nand.c * diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c index b2f00b398490..12f6753d47ae 100644 --- a/drivers/mtd/nand/atmel/nand-controller.c +++ b/drivers/mtd/nand/atmel/nand-controller.c @@ -9,10 +9,10 @@ * * Copyright 2003 Rick Bronson * - * Derived from drivers/mtd/nand/autcpu12.c + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) * - * Derived from drivers/mtd/spia.c + * Derived from drivers/mtd/spia.c (removed in v3.8) * Copyright 2000 Steven J. Hill (sjhill@cotw.com) * * diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c index fcbe4fd6e684..9de29c9afb0c 100644 --- a/drivers/mtd/nand/atmel/pmecc.c +++ b/drivers/mtd/nand/atmel/pmecc.c @@ -9,10 +9,10 @@ * * Copyright 2003 Rick Bronson * - * Derived from drivers/mtd/nand/autcpu12.c + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) * - * Derived from drivers/mtd/spia.c + * Derived from drivers/mtd/spia.c (removed in v3.8) * Copyright 2000 Steven J. Hill (sjhill@cotw.com) * * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 diff --git a/drivers/mtd/nand/atmel/pmecc.h b/drivers/mtd/nand/atmel/pmecc.h index 817e0dd9fd15..808f1be0d6ad 100644 --- a/drivers/mtd/nand/atmel/pmecc.h +++ b/drivers/mtd/nand/atmel/pmecc.h @@ -9,10 +9,10 @@ * * Copyright © 2003 Rick Bronson * - * Derived from drivers/mtd/nand/autcpu12.c + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de) * - * Derived from drivers/mtd/spia.c + * Derived from drivers/mtd/spia.c (removed in v3.8) * Copyright © 2000 Steven J. Hill (sjhill@cotw.com) * * diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 66749ade9654..02d6751e9efe 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -2,7 +2,7 @@ * Copyright (C) 2006 Compulab, Ltd. * Mike Rapoport * - * Derived from drivers/mtd/nand/h1910.c + * Derived from drivers/mtd/nand/h1910.c (removed in v3.10) * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index e763161a7c82..a4a5da318eed 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -7,7 +7,7 @@ * Vipin Kumar * Ashish Priyadarshi * - * Based on drivers/mtd/nand/nomadik_nand.c + * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8) * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any -- cgit v1.3-14-g43fede From 7b6afee7291802aa8c02aa918782033992caf641 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 5 Feb 2018 23:02:03 +0100 Subject: mtd: nand: Add missing copyright information Some drivers have been derived from others which have then been removed from the source tree. When this is the case, add mention the copyright of the source file(s) they've been derived from. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/ams-delta.c | 3 +++ drivers/mtd/nand/fsmc_nand.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 24173a0a7875..35f80523e52e 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -2,6 +2,9 @@ * Copyright (C) 2006 Jonathan McDowell * * Derived from drivers/mtd/nand/toto.c (removed in v2.6.28) + * Copyright (c) 2003 Texas Instruments + * Copyright (c) 2002 Thomas Gleixner + * * Converted to platform driver by Janusz Krzysztofik * Partially stolen from plat_nand.c * diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index a4a5da318eed..7e66268f8154 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -8,6 +8,8 @@ * Ashish Priyadarshi * * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8) + * Copyright © 2007 STMicroelectronics Pvt. Ltd. + * Copyright © 2009 Alessandro Rubini * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any -- cgit v1.3-14-g43fede From 93db446a424cee9387b532995e6b516667079555 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 5 Feb 2018 23:02:04 +0100 Subject: mtd: nand: move raw NAND related code to the raw/ subdir As part of the process of sharing more code between different NAND based devices, we need to move all raw NAND related code to the raw/ subdirectory. Signed-off-by: Boris Brezillon --- Documentation/arm/Samsung-S3C24XX/S3C2412.txt | 2 +- Documentation/driver-api/mtdnand.rst | 8 +- Documentation/gpio/drivers-on-gpio.txt | 4 +- MAINTAINERS | 22 +- drivers/mtd/nand/Kconfig | 581 +- drivers/mtd/nand/Makefile | 67 +- drivers/mtd/nand/ams-delta.c | 291 - drivers/mtd/nand/atmel/Makefile | 4 - drivers/mtd/nand/atmel/nand-controller.c | 2565 -------- drivers/mtd/nand/atmel/pmecc.c | 1012 --- drivers/mtd/nand/atmel/pmecc.h | 74 - drivers/mtd/nand/au1550nd.c | 515 -- drivers/mtd/nand/bcm47xxnflash/Makefile | 4 - drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h | 26 - drivers/mtd/nand/bcm47xxnflash/main.c | 81 - drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c | 456 -- drivers/mtd/nand/bf5xx_nand.c | 861 --- drivers/mtd/nand/brcmnand/Makefile | 8 - drivers/mtd/nand/brcmnand/bcm63138_nand.c | 109 - drivers/mtd/nand/brcmnand/bcm6368_nand.c | 142 - drivers/mtd/nand/brcmnand/brcmnand.c | 2620 -------- drivers/mtd/nand/brcmnand/brcmnand.h | 74 - drivers/mtd/nand/brcmnand/brcmstb_nand.c | 44 - drivers/mtd/nand/brcmnand/iproc_nand.c | 160 - drivers/mtd/nand/cafe_nand.c | 871 --- drivers/mtd/nand/cmx270_nand.c | 244 - drivers/mtd/nand/cs553x_nand.c | 355 -- drivers/mtd/nand/davinci_nand.c | 879 --- drivers/mtd/nand/denali.c | 1408 ----- drivers/mtd/nand/denali.h | 335 - drivers/mtd/nand/denali_dt.c | 163 - drivers/mtd/nand/denali_pci.c | 131 - drivers/mtd/nand/diskonchip.c | 1709 ----- drivers/mtd/nand/docg4.c | 1421 ----- drivers/mtd/nand/fsl_elbc_nand.c | 979 --- drivers/mtd/nand/fsl_ifc_nand.c | 1117 ---- drivers/mtd/nand/fsl_upm.c | 363 -- drivers/mtd/nand/fsmc_nand.c | 1175 ---- drivers/mtd/nand/gpio.c | 327 - drivers/mtd/nand/gpmi-nand/Makefile | 3 - drivers/mtd/nand/gpmi-nand/bch-regs.h | 128 - drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 1510 ----- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 2182 ------- drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 315 - drivers/mtd/nand/gpmi-nand/gpmi-regs.h | 187 - drivers/mtd/nand/hisi504_nand.c | 896 --- drivers/mtd/nand/jz4740_nand.c | 536 -- drivers/mtd/nand/jz4780_bch.c | 380 -- drivers/mtd/nand/jz4780_bch.h | 43 - drivers/mtd/nand/jz4780_nand.c | 416 -- drivers/mtd/nand/lpc32xx_mlc.c | 909 --- drivers/mtd/nand/lpc32xx_slc.c | 1032 --- drivers/mtd/nand/marvell_nand.c | 2915 --------- drivers/mtd/nand/mpc5121_nfc.c | 856 --- drivers/mtd/nand/mtk_ecc.c | 608 -- drivers/mtd/nand/mtk_ecc.h | 49 - drivers/mtd/nand/mtk_nand.c | 1599 ----- drivers/mtd/nand/mxc_nand.c | 1966 ------ drivers/mtd/nand/nand_amd.c | 51 - drivers/mtd/nand/nand_base.c | 6582 -------------------- drivers/mtd/nand/nand_bbt.c | 1452 ----- drivers/mtd/nand/nand_bch.c | 234 - drivers/mtd/nand/nand_ecc.c | 531 -- drivers/mtd/nand/nand_hynix.c | 676 -- drivers/mtd/nand/nand_ids.c | 207 - drivers/mtd/nand/nand_macronix.c | 30 - drivers/mtd/nand/nand_micron.c | 289 - drivers/mtd/nand/nand_samsung.c | 134 - drivers/mtd/nand/nand_timings.c | 324 - drivers/mtd/nand/nand_toshiba.c | 77 - drivers/mtd/nand/nandsim.c | 2392 ------- drivers/mtd/nand/ndfc.c | 286 - drivers/mtd/nand/nuc900_nand.c | 306 - drivers/mtd/nand/omap2.c | 2316 ------- drivers/mtd/nand/omap_elm.c | 578 -- drivers/mtd/nand/orion_nand.c | 232 - drivers/mtd/nand/oxnas_nand.c | 206 - drivers/mtd/nand/pasemi_nand.c | 232 - drivers/mtd/nand/plat_nand.c | 144 - drivers/mtd/nand/pxa3xx_nand.c | 2103 ------- drivers/mtd/nand/qcom_nandc.c | 2921 --------- drivers/mtd/nand/r852.c | 1079 ---- drivers/mtd/nand/r852.h | 160 - drivers/mtd/nand/raw/Kconfig | 580 ++ drivers/mtd/nand/raw/Makefile | 68 + drivers/mtd/nand/raw/ams-delta.c | 291 + drivers/mtd/nand/raw/atmel/Makefile | 4 + drivers/mtd/nand/raw/atmel/nand-controller.c | 2565 ++++++++ drivers/mtd/nand/raw/atmel/pmecc.c | 1012 +++ drivers/mtd/nand/raw/atmel/pmecc.h | 74 + drivers/mtd/nand/raw/au1550nd.c | 515 ++ drivers/mtd/nand/raw/bcm47xxnflash/Makefile | 4 + drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h | 26 + drivers/mtd/nand/raw/bcm47xxnflash/main.c | 81 + drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c | 456 ++ drivers/mtd/nand/raw/bf5xx_nand.c | 861 +++ drivers/mtd/nand/raw/brcmnand/Makefile | 8 + drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c | 109 + drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c | 142 + drivers/mtd/nand/raw/brcmnand/brcmnand.c | 2620 ++++++++ drivers/mtd/nand/raw/brcmnand/brcmnand.h | 74 + drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c | 44 + drivers/mtd/nand/raw/brcmnand/iproc_nand.c | 160 + drivers/mtd/nand/raw/cafe_nand.c | 871 +++ drivers/mtd/nand/raw/cmx270_nand.c | 244 + drivers/mtd/nand/raw/cs553x_nand.c | 355 ++ drivers/mtd/nand/raw/davinci_nand.c | 879 +++ drivers/mtd/nand/raw/denali.c | 1408 +++++ drivers/mtd/nand/raw/denali.h | 335 + drivers/mtd/nand/raw/denali_dt.c | 163 + drivers/mtd/nand/raw/denali_pci.c | 131 + drivers/mtd/nand/raw/diskonchip.c | 1709 +++++ drivers/mtd/nand/raw/docg4.c | 1421 +++++ drivers/mtd/nand/raw/fsl_elbc_nand.c | 979 +++ drivers/mtd/nand/raw/fsl_ifc_nand.c | 1117 ++++ drivers/mtd/nand/raw/fsl_upm.c | 363 ++ drivers/mtd/nand/raw/fsmc_nand.c | 1175 ++++ drivers/mtd/nand/raw/gpio.c | 327 + drivers/mtd/nand/raw/gpmi-nand/Makefile | 3 + drivers/mtd/nand/raw/gpmi-nand/bch-regs.h | 128 + drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 1510 +++++ drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 2182 +++++++ drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 315 + drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h | 187 + drivers/mtd/nand/raw/hisi504_nand.c | 896 +++ drivers/mtd/nand/raw/jz4740_nand.c | 536 ++ drivers/mtd/nand/raw/jz4780_bch.c | 380 ++ drivers/mtd/nand/raw/jz4780_bch.h | 43 + drivers/mtd/nand/raw/jz4780_nand.c | 416 ++ drivers/mtd/nand/raw/lpc32xx_mlc.c | 909 +++ drivers/mtd/nand/raw/lpc32xx_slc.c | 1032 +++ drivers/mtd/nand/raw/marvell_nand.c | 2915 +++++++++ drivers/mtd/nand/raw/mpc5121_nfc.c | 856 +++ drivers/mtd/nand/raw/mtk_ecc.c | 608 ++ drivers/mtd/nand/raw/mtk_ecc.h | 49 + drivers/mtd/nand/raw/mtk_nand.c | 1599 +++++ drivers/mtd/nand/raw/mxc_nand.c | 1966 ++++++ drivers/mtd/nand/raw/nand_amd.c | 51 + drivers/mtd/nand/raw/nand_base.c | 6582 ++++++++++++++++++++ drivers/mtd/nand/raw/nand_bbt.c | 1452 +++++ drivers/mtd/nand/raw/nand_bch.c | 234 + drivers/mtd/nand/raw/nand_ecc.c | 531 ++ drivers/mtd/nand/raw/nand_hynix.c | 676 ++ drivers/mtd/nand/raw/nand_ids.c | 207 + drivers/mtd/nand/raw/nand_macronix.c | 30 + drivers/mtd/nand/raw/nand_micron.c | 289 + drivers/mtd/nand/raw/nand_samsung.c | 134 + drivers/mtd/nand/raw/nand_timings.c | 324 + drivers/mtd/nand/raw/nand_toshiba.c | 77 + drivers/mtd/nand/raw/nandsim.c | 2392 +++++++ drivers/mtd/nand/raw/ndfc.c | 286 + drivers/mtd/nand/raw/nuc900_nand.c | 306 + drivers/mtd/nand/raw/omap2.c | 2316 +++++++ drivers/mtd/nand/raw/omap_elm.c | 578 ++ drivers/mtd/nand/raw/orion_nand.c | 232 + drivers/mtd/nand/raw/oxnas_nand.c | 206 + drivers/mtd/nand/raw/pasemi_nand.c | 232 + drivers/mtd/nand/raw/plat_nand.c | 144 + drivers/mtd/nand/raw/pxa3xx_nand.c | 2103 +++++++ drivers/mtd/nand/raw/qcom_nandc.c | 2921 +++++++++ drivers/mtd/nand/raw/r852.c | 1079 ++++ drivers/mtd/nand/raw/r852.h | 160 + drivers/mtd/nand/raw/s3c2410.c | 1295 ++++ drivers/mtd/nand/raw/sh_flctl.c | 1250 ++++ drivers/mtd/nand/raw/sharpsl.c | 233 + drivers/mtd/nand/raw/sm_common.c | 202 + drivers/mtd/nand/raw/sm_common.h | 61 + drivers/mtd/nand/raw/socrates_nand.c | 241 + drivers/mtd/nand/raw/sunxi_nand.c | 2321 +++++++ drivers/mtd/nand/raw/tango_nand.c | 688 ++ drivers/mtd/nand/raw/tmio_nand.c | 513 ++ drivers/mtd/nand/raw/txx9ndfmc.c | 423 ++ drivers/mtd/nand/raw/vf610_nfc.c | 835 +++ drivers/mtd/nand/raw/xway_nand.c | 245 + drivers/mtd/nand/s3c2410.c | 1295 ---- drivers/mtd/nand/sh_flctl.c | 1250 ---- drivers/mtd/nand/sharpsl.c | 233 - drivers/mtd/nand/sm_common.c | 202 - drivers/mtd/nand/sm_common.h | 61 - drivers/mtd/nand/socrates_nand.c | 241 - drivers/mtd/nand/sunxi_nand.c | 2321 ------- drivers/mtd/nand/tango_nand.c | 688 -- drivers/mtd/nand/tmio_nand.c | 513 -- drivers/mtd/nand/txx9ndfmc.c | 423 -- drivers/mtd/nand/vf610_nfc.c | 835 --- drivers/mtd/nand/xway_nand.c | 245 - drivers/mtd/sm_ftl.c | 2 +- 187 files changed, 69571 insertions(+), 69567 deletions(-) delete mode 100644 drivers/mtd/nand/ams-delta.c delete mode 100644 drivers/mtd/nand/atmel/Makefile delete mode 100644 drivers/mtd/nand/atmel/nand-controller.c delete mode 100644 drivers/mtd/nand/atmel/pmecc.c delete mode 100644 drivers/mtd/nand/atmel/pmecc.h delete mode 100644 drivers/mtd/nand/au1550nd.c delete mode 100644 drivers/mtd/nand/bcm47xxnflash/Makefile delete mode 100644 drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h delete mode 100644 drivers/mtd/nand/bcm47xxnflash/main.c delete mode 100644 drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c delete mode 100644 drivers/mtd/nand/bf5xx_nand.c delete mode 100644 drivers/mtd/nand/brcmnand/Makefile delete mode 100644 drivers/mtd/nand/brcmnand/bcm63138_nand.c delete mode 100644 drivers/mtd/nand/brcmnand/bcm6368_nand.c delete mode 100644 drivers/mtd/nand/brcmnand/brcmnand.c delete mode 100644 drivers/mtd/nand/brcmnand/brcmnand.h delete mode 100644 drivers/mtd/nand/brcmnand/brcmstb_nand.c delete mode 100644 drivers/mtd/nand/brcmnand/iproc_nand.c delete mode 100644 drivers/mtd/nand/cafe_nand.c delete mode 100644 drivers/mtd/nand/cmx270_nand.c delete mode 100644 drivers/mtd/nand/cs553x_nand.c delete mode 100644 drivers/mtd/nand/davinci_nand.c delete mode 100644 drivers/mtd/nand/denali.c delete mode 100644 drivers/mtd/nand/denali.h delete mode 100644 drivers/mtd/nand/denali_dt.c delete mode 100644 drivers/mtd/nand/denali_pci.c delete mode 100644 drivers/mtd/nand/diskonchip.c delete mode 100644 drivers/mtd/nand/docg4.c delete mode 100644 drivers/mtd/nand/fsl_elbc_nand.c delete mode 100644 drivers/mtd/nand/fsl_ifc_nand.c delete mode 100644 drivers/mtd/nand/fsl_upm.c delete mode 100644 drivers/mtd/nand/fsmc_nand.c delete mode 100644 drivers/mtd/nand/gpio.c delete mode 100644 drivers/mtd/nand/gpmi-nand/Makefile delete mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h delete mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c delete mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.c delete mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.h delete mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h delete mode 100644 drivers/mtd/nand/hisi504_nand.c delete mode 100644 drivers/mtd/nand/jz4740_nand.c delete mode 100644 drivers/mtd/nand/jz4780_bch.c delete mode 100644 drivers/mtd/nand/jz4780_bch.h delete mode 100644 drivers/mtd/nand/jz4780_nand.c delete mode 100644 drivers/mtd/nand/lpc32xx_mlc.c delete mode 100644 drivers/mtd/nand/lpc32xx_slc.c delete mode 100644 drivers/mtd/nand/marvell_nand.c delete mode 100644 drivers/mtd/nand/mpc5121_nfc.c delete mode 100644 drivers/mtd/nand/mtk_ecc.c delete mode 100644 drivers/mtd/nand/mtk_ecc.h delete mode 100644 drivers/mtd/nand/mtk_nand.c delete mode 100644 drivers/mtd/nand/mxc_nand.c delete mode 100644 drivers/mtd/nand/nand_amd.c delete mode 100644 drivers/mtd/nand/nand_base.c delete mode 100644 drivers/mtd/nand/nand_bbt.c delete mode 100644 drivers/mtd/nand/nand_bch.c delete mode 100644 drivers/mtd/nand/nand_ecc.c delete mode 100644 drivers/mtd/nand/nand_hynix.c delete mode 100644 drivers/mtd/nand/nand_ids.c delete mode 100644 drivers/mtd/nand/nand_macronix.c delete mode 100644 drivers/mtd/nand/nand_micron.c delete mode 100644 drivers/mtd/nand/nand_samsung.c delete mode 100644 drivers/mtd/nand/nand_timings.c delete mode 100644 drivers/mtd/nand/nand_toshiba.c delete mode 100644 drivers/mtd/nand/nandsim.c delete mode 100644 drivers/mtd/nand/ndfc.c delete mode 100644 drivers/mtd/nand/nuc900_nand.c delete mode 100644 drivers/mtd/nand/omap2.c delete mode 100644 drivers/mtd/nand/omap_elm.c delete mode 100644 drivers/mtd/nand/orion_nand.c delete mode 100644 drivers/mtd/nand/oxnas_nand.c delete mode 100644 drivers/mtd/nand/pasemi_nand.c delete mode 100644 drivers/mtd/nand/plat_nand.c delete mode 100644 drivers/mtd/nand/pxa3xx_nand.c delete mode 100644 drivers/mtd/nand/qcom_nandc.c delete mode 100644 drivers/mtd/nand/r852.c delete mode 100644 drivers/mtd/nand/r852.h create mode 100644 drivers/mtd/nand/raw/Kconfig create mode 100644 drivers/mtd/nand/raw/Makefile create mode 100644 drivers/mtd/nand/raw/ams-delta.c create mode 100644 drivers/mtd/nand/raw/atmel/Makefile create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.c create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.h create mode 100644 drivers/mtd/nand/raw/au1550nd.c create mode 100644 drivers/mtd/nand/raw/bcm47xxnflash/Makefile create mode 100644 drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h create mode 100644 drivers/mtd/nand/raw/bcm47xxnflash/main.c create mode 100644 drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c create mode 100644 drivers/mtd/nand/raw/bf5xx_nand.c create mode 100644 drivers/mtd/nand/raw/brcmnand/Makefile create mode 100644 drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c create mode 100644 drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c create mode 100644 drivers/mtd/nand/raw/brcmnand/brcmnand.c create mode 100644 drivers/mtd/nand/raw/brcmnand/brcmnand.h create mode 100644 drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c create mode 100644 drivers/mtd/nand/raw/brcmnand/iproc_nand.c create mode 100644 drivers/mtd/nand/raw/cafe_nand.c create mode 100644 drivers/mtd/nand/raw/cmx270_nand.c create mode 100644 drivers/mtd/nand/raw/cs553x_nand.c create mode 100644 drivers/mtd/nand/raw/davinci_nand.c create mode 100644 drivers/mtd/nand/raw/denali.c create mode 100644 drivers/mtd/nand/raw/denali.h create mode 100644 drivers/mtd/nand/raw/denali_dt.c create mode 100644 drivers/mtd/nand/raw/denali_pci.c create mode 100644 drivers/mtd/nand/raw/diskonchip.c create mode 100644 drivers/mtd/nand/raw/docg4.c create mode 100644 drivers/mtd/nand/raw/fsl_elbc_nand.c create mode 100644 drivers/mtd/nand/raw/fsl_ifc_nand.c create mode 100644 drivers/mtd/nand/raw/fsl_upm.c create mode 100644 drivers/mtd/nand/raw/fsmc_nand.c create mode 100644 drivers/mtd/nand/raw/gpio.c create mode 100644 drivers/mtd/nand/raw/gpmi-nand/Makefile create mode 100644 drivers/mtd/nand/raw/gpmi-nand/bch-regs.h create mode 100644 drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c create mode 100644 drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c create mode 100644 drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h create mode 100644 drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h create mode 100644 drivers/mtd/nand/raw/hisi504_nand.c create mode 100644 drivers/mtd/nand/raw/jz4740_nand.c create mode 100644 drivers/mtd/nand/raw/jz4780_bch.c create mode 100644 drivers/mtd/nand/raw/jz4780_bch.h create mode 100644 drivers/mtd/nand/raw/jz4780_nand.c create mode 100644 drivers/mtd/nand/raw/lpc32xx_mlc.c create mode 100644 drivers/mtd/nand/raw/lpc32xx_slc.c create mode 100644 drivers/mtd/nand/raw/marvell_nand.c create mode 100644 drivers/mtd/nand/raw/mpc5121_nfc.c create mode 100644 drivers/mtd/nand/raw/mtk_ecc.c create mode 100644 drivers/mtd/nand/raw/mtk_ecc.h create mode 100644 drivers/mtd/nand/raw/mtk_nand.c create mode 100644 drivers/mtd/nand/raw/mxc_nand.c create mode 100644 drivers/mtd/nand/raw/nand_amd.c create mode 100644 drivers/mtd/nand/raw/nand_base.c create mode 100644 drivers/mtd/nand/raw/nand_bbt.c create mode 100644 drivers/mtd/nand/raw/nand_bch.c create mode 100644 drivers/mtd/nand/raw/nand_ecc.c create mode 100644 drivers/mtd/nand/raw/nand_hynix.c create mode 100644 drivers/mtd/nand/raw/nand_ids.c create mode 100644 drivers/mtd/nand/raw/nand_macronix.c create mode 100644 drivers/mtd/nand/raw/nand_micron.c create mode 100644 drivers/mtd/nand/raw/nand_samsung.c create mode 100644 drivers/mtd/nand/raw/nand_timings.c create mode 100644 drivers/mtd/nand/raw/nand_toshiba.c create mode 100644 drivers/mtd/nand/raw/nandsim.c create mode 100644 drivers/mtd/nand/raw/ndfc.c create mode 100644 drivers/mtd/nand/raw/nuc900_nand.c create mode 100644 drivers/mtd/nand/raw/omap2.c create mode 100644 drivers/mtd/nand/raw/omap_elm.c create mode 100644 drivers/mtd/nand/raw/orion_nand.c create mode 100644 drivers/mtd/nand/raw/oxnas_nand.c create mode 100644 drivers/mtd/nand/raw/pasemi_nand.c create mode 100644 drivers/mtd/nand/raw/plat_nand.c create mode 100644 drivers/mtd/nand/raw/pxa3xx_nand.c create mode 100644 drivers/mtd/nand/raw/qcom_nandc.c create mode 100644 drivers/mtd/nand/raw/r852.c create mode 100644 drivers/mtd/nand/raw/r852.h create mode 100644 drivers/mtd/nand/raw/s3c2410.c create mode 100644 drivers/mtd/nand/raw/sh_flctl.c create mode 100644 drivers/mtd/nand/raw/sharpsl.c create mode 100644 drivers/mtd/nand/raw/sm_common.c create mode 100644 drivers/mtd/nand/raw/sm_common.h create mode 100644 drivers/mtd/nand/raw/socrates_nand.c create mode 100644 drivers/mtd/nand/raw/sunxi_nand.c create mode 100644 drivers/mtd/nand/raw/tango_nand.c create mode 100644 drivers/mtd/nand/raw/tmio_nand.c create mode 100644 drivers/mtd/nand/raw/txx9ndfmc.c create mode 100644 drivers/mtd/nand/raw/vf610_nfc.c create mode 100644 drivers/mtd/nand/raw/xway_nand.c delete mode 100644 drivers/mtd/nand/s3c2410.c delete mode 100644 drivers/mtd/nand/sh_flctl.c delete mode 100644 drivers/mtd/nand/sharpsl.c delete mode 100644 drivers/mtd/nand/sm_common.c delete mode 100644 drivers/mtd/nand/sm_common.h delete mode 100644 drivers/mtd/nand/socrates_nand.c delete mode 100644 drivers/mtd/nand/sunxi_nand.c delete mode 100644 drivers/mtd/nand/tango_nand.c delete mode 100644 drivers/mtd/nand/tmio_nand.c delete mode 100644 drivers/mtd/nand/txx9ndfmc.c delete mode 100644 drivers/mtd/nand/vf610_nfc.c delete mode 100644 drivers/mtd/nand/xway_nand.c diff --git a/Documentation/arm/Samsung-S3C24XX/S3C2412.txt b/Documentation/arm/Samsung-S3C24XX/S3C2412.txt index f057876b920b..dc1fd362d3c1 100644 --- a/Documentation/arm/Samsung-S3C24XX/S3C2412.txt +++ b/Documentation/arm/Samsung-S3C24XX/S3C2412.txt @@ -46,7 +46,7 @@ NAND ---- The NAND hardware is similar to the S3C2440, and is supported by the - s3c2410 driver in the drivers/mtd/nand directory. + s3c2410 driver in the drivers/mtd/nand/raw directory. USB Host diff --git a/Documentation/driver-api/mtdnand.rst b/Documentation/driver-api/mtdnand.rst index 2a5191b6d445..dcd63599f700 100644 --- a/Documentation/driver-api/mtdnand.rst +++ b/Documentation/driver-api/mtdnand.rst @@ -967,10 +967,10 @@ API functions which are exported. Each function has a short description which is marked with an [XXX] identifier. See the chapter "Documentation hints" for an explanation. -.. kernel-doc:: drivers/mtd/nand/nand_base.c +.. kernel-doc:: drivers/mtd/nand/raw/nand_base.c :export: -.. kernel-doc:: drivers/mtd/nand/nand_ecc.c +.. kernel-doc:: drivers/mtd/nand/raw/nand_ecc.c :export: Internal Functions Provided @@ -982,10 +982,10 @@ marked with an [XXX] identifier. See the chapter "Documentation hints" for an explanation. The functions marked with [DEFAULT] might be relevant for a board driver developer. -.. kernel-doc:: drivers/mtd/nand/nand_base.c +.. kernel-doc:: drivers/mtd/nand/raw/nand_base.c :internal: -.. kernel-doc:: drivers/mtd/nand/nand_bbt.c +.. kernel-doc:: drivers/mtd/nand/raw/nand_bbt.c :internal: Credits diff --git a/Documentation/gpio/drivers-on-gpio.txt b/Documentation/gpio/drivers-on-gpio.txt index a2ccbab12eb7..a3e612f55bc7 100644 --- a/Documentation/gpio/drivers-on-gpio.txt +++ b/Documentation/gpio/drivers-on-gpio.txt @@ -74,8 +74,8 @@ hardware descriptions such as device tree or ACPI: it from 1-to-0-to-1. If that hardware does not receive its "ping" periodically, it will reset the system. -- gpio-nand: drivers/mtd/nand/gpio.c is used to connect a NAND flash chip to - a set of simple GPIO lines: RDY, NCE, ALE, CLE, NWP. It interacts with the +- gpio-nand: drivers/mtd/nand/raw/gpio.c is used to connect a NAND flash chip + to a set of simple GPIO lines: RDY, NCE, ALE, CLE, NWP. It interacts with the NAND flash MTD subsystem and provides chip access and partition parsing like any other NAND driving hardware. diff --git a/MAINTAINERS b/MAINTAINERS index 3bdc260e36b7..34b2e9ed6d3d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1710,7 +1710,7 @@ F: drivers/input/keyboard/w90p910_keypad.c F: drivers/input/touchscreen/w90p910_ts.c F: drivers/watchdog/nuc900_wdt.c F: drivers/net/ethernet/nuvoton/w90p910_ether.c -F: drivers/mtd/nand/nuc900_nand.c +F: drivers/mtd/nand/raw/nuc900_nand.c F: drivers/rtc/rtc-nuc900.c F: drivers/spi/spi-nuc900.c F: drivers/usb/host/ehci-w90x900.c @@ -3014,7 +3014,7 @@ M: Kamal Dasu L: linux-mtd@lists.infradead.org L: bcm-kernel-feedback-list@broadcom.com S: Maintained -F: drivers/mtd/nand/brcmnand/ +F: drivers/mtd/nand/raw/brcmnand/ BROADCOM STB DPFE DRIVER M: Markus Mayer @@ -4116,7 +4116,7 @@ DENALI NAND DRIVER M: Masahiro Yamada L: linux-mtd@lists.infradead.org S: Supported -F: drivers/mtd/nand/denali* +F: drivers/mtd/nand/raw/denali* DESIGNWARE USB2 DRD IP DRIVER M: John Youn @@ -5646,7 +5646,7 @@ FREESCALE GPMI NAND DRIVER M: Han Xu L: linux-mtd@lists.infradead.org S: Maintained -F: drivers/mtd/nand/gpmi-nand/* +F: drivers/mtd/nand/raw/gpmi-nand/* FREESCALE I2C CPM DRIVER M: Jochen Friedrich @@ -6955,7 +6955,7 @@ INGENIC JZ4780 NAND DRIVER M: Harvey Hunt L: linux-mtd@lists.infradead.org S: Maintained -F: drivers/mtd/nand/jz4780_* +F: drivers/mtd/nand/raw/jz4780_* INOTIFY M: Jan Kara @@ -8474,7 +8474,7 @@ MARVELL NAND CONTROLLER DRIVER M: Miquel Raynal L: linux-mtd@lists.infradead.org S: Maintained -F: drivers/mtd/nand/marvell_nand.c +F: drivers/mtd/nand/raw/marvell_nand.c F: Documentation/devicetree/bindings/mtd/marvell-nand.txt MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER @@ -9136,7 +9136,7 @@ M: Wenyou Yang M: Josh Wu L: linux-mtd@lists.infradead.org S: Supported -F: drivers/mtd/nand/atmel/* +F: drivers/mtd/nand/raw/atmel/* F: Documentation/devicetree/bindings/mtd/atmel-nand.txt MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER @@ -11331,7 +11331,7 @@ PXA3xx NAND FLASH DRIVER M: Ezequiel Garcia L: linux-mtd@lists.infradead.org S: Maintained -F: drivers/mtd/nand/pxa3xx_nand.c +F: drivers/mtd/nand/raw/pxa3xx_nand.c QAT DRIVER M: Giovanni Cabiddu @@ -11815,8 +11815,8 @@ F: drivers/memstick/host/r592.* RICOH SMARTMEDIA/XD DRIVER M: Maxim Levitsky S: Maintained -F: drivers/mtd/nand/r852.c -F: drivers/mtd/nand/r852.h +F: drivers/mtd/nand/raw/r852.c +F: drivers/mtd/nand/raw/r852.h RISC-V ARCHITECTURE M: Palmer Dabbelt @@ -14635,7 +14635,7 @@ VF610 NAND DRIVER M: Stefan Agner L: linux-mtd@lists.infradead.org S: Supported -F: drivers/mtd/nand/vf610_nfc.c +F: drivers/mtd/nand/raw/vf610_nfc.c VFAT/FAT/MSDOS FILESYSTEM M: OGAWA Hirofumi diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index e6b8c59f2c0d..6d5373471809 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,580 +1 @@ -config MTD_NAND_ECC - tristate - -config MTD_NAND_ECC_SMC - bool "NAND ECC Smart Media byte order" - depends on MTD_NAND_ECC - default n - help - Software ECC according to the Smart Media Specification. - The original Linux implementation had byte 0 and 1 swapped. - - -menuconfig MTD_NAND - tristate "NAND Device Support" - depends on MTD - select MTD_NAND_ECC - help - This enables support for accessing all type of NAND flash - devices. For further information see - . - -if MTD_NAND - -config MTD_NAND_BCH - tristate - select BCH - depends on MTD_NAND_ECC_BCH - default MTD_NAND - -config MTD_NAND_ECC_BCH - bool "Support software BCH ECC" - default n - help - This enables support for software BCH error correction. Binary BCH - codes are more powerful and cpu intensive than traditional Hamming - ECC codes. They are used with NAND devices requiring more than 1 bit - of error correction. - -config MTD_SM_COMMON - tristate - default n - -config MTD_NAND_DENALI - tristate - -config MTD_NAND_DENALI_PCI - tristate "Support Denali NAND controller on Intel Moorestown" - select MTD_NAND_DENALI - depends on HAS_DMA && PCI - help - Enable the driver for NAND flash on Intel Moorestown, using the - Denali NAND controller core. - -config MTD_NAND_DENALI_DT - tristate "Support Denali NAND controller as a DT device" - select MTD_NAND_DENALI - depends on HAS_DMA && HAVE_CLK && OF - help - Enable the driver for NAND flash on platforms using a Denali NAND - controller as a DT device. - -config MTD_NAND_GPIO - tristate "GPIO assisted NAND Flash driver" - depends on GPIOLIB || COMPILE_TEST - depends on HAS_IOMEM - help - This enables a NAND flash driver where control signals are - connected to GPIO pins, and commands and data are communicated - via a memory mapped interface. - -config MTD_NAND_AMS_DELTA - tristate "NAND Flash device on Amstrad E3" - depends on MACH_AMS_DELTA - default y - help - Support for NAND flash on Amstrad E3 (Delta). - -config MTD_NAND_OMAP2 - tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone" - depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE) - help - Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4 - and Keystone platforms. - -config MTD_NAND_OMAP_BCH - depends on MTD_NAND_OMAP2 - bool "Support hardware based BCH error correction" - default n - select BCH - help - This config enables the ELM hardware engine, which can be used to - locate and correct errors when using BCH ECC scheme. This offloads - the cpu from doing ECC error searching and correction. However some - legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine - so this is optional for them. - -config MTD_NAND_OMAP_BCH_BUILD - def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH - -config MTD_NAND_RICOH - tristate "Ricoh xD card reader" - default n - depends on PCI - select MTD_SM_COMMON - help - Enable support for Ricoh R5C852 xD card reader - You also need to enable ether - NAND SSFDC (SmartMedia) read only translation layer' or new - expermental, readwrite - 'SmartMedia/xD new translation layer' - -config MTD_NAND_AU1550 - tristate "Au1550/1200 NAND support" - depends on MIPS_ALCHEMY - help - This enables the driver for the NAND flash controller on the - AMD/Alchemy 1550 SOC. - -config MTD_NAND_BF5XX - tristate "Blackfin on-chip NAND Flash Controller driver" - depends on BF54x || BF52x - help - This enables the Blackfin on-chip NAND flash controller - - No board specific support is done by this driver, each board - must advertise a platform_device for the driver to attach. - - This driver can also be built as a module. If so, the module - will be called bf5xx-nand. - -config MTD_NAND_BF5XX_HWECC - bool "BF5XX NAND Hardware ECC" - default y - depends on MTD_NAND_BF5XX - help - Enable the use of the BF5XX's internal ECC generator when - using NAND. - -config MTD_NAND_BF5XX_BOOTROM_ECC - bool "Use Blackfin BootROM ECC Layout" - default n - depends on MTD_NAND_BF5XX_HWECC - help - If you wish to modify NAND pages and allow the Blackfin on-chip - BootROM to boot from them, say Y here. This is only necessary - if you are booting U-Boot out of NAND and you wish to update - U-Boot from Linux' userspace. Otherwise, you should say N here. - - If unsure, say N. - -config MTD_NAND_S3C2410 - tristate "NAND Flash support for Samsung S3C SoCs" - depends on ARCH_S3C24XX || ARCH_S3C64XX - help - This enables the NAND flash controller on the S3C24xx and S3C64xx - SoCs - - No board specific support is done by this driver, each board - must advertise a platform_device for the driver to attach. - -config MTD_NAND_S3C2410_DEBUG - bool "Samsung S3C NAND driver debug" - depends on MTD_NAND_S3C2410 - help - Enable debugging of the S3C NAND driver - -config MTD_NAND_NDFC - tristate "NDFC NanD Flash Controller" - depends on 4xx - select MTD_NAND_ECC_SMC - help - NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs - -config MTD_NAND_S3C2410_CLKSTOP - bool "Samsung S3C NAND IDLE clock stop" - depends on MTD_NAND_S3C2410 - default n - help - Stop the clock to the NAND controller when there is no chip - selected to save power. This will mean there is a small delay - when the is NAND chip selected or released, but will save - approximately 5mA of power when there is nothing happening. - -config MTD_NAND_TANGO - tristate "NAND Flash support for Tango chips" - depends on ARCH_TANGO || COMPILE_TEST - depends on HAS_DMA - help - Enables the NAND Flash controller on Tango chips. - -config MTD_NAND_DISKONCHIP - tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)" - depends on HAS_IOMEM - select REED_SOLOMON - select REED_SOLOMON_DEC16 - help - This is a reimplementation of M-Systems DiskOnChip 2000, - Millennium and Millennium Plus as a standard NAND device driver, - as opposed to the earlier self-contained MTD device drivers. - This should enable, among other things, proper JFFS2 operation on - these devices. - -config MTD_NAND_DISKONCHIP_PROBE_ADVANCED - bool "Advanced detection options for DiskOnChip" - depends on MTD_NAND_DISKONCHIP - help - This option allows you to specify nonstandard address at which to - probe for a DiskOnChip, or to change the detection options. You - are unlikely to need any of this unless you are using LinuxBIOS. - Say 'N'. - -config MTD_NAND_DISKONCHIP_PROBE_ADDRESS - hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED - depends on MTD_NAND_DISKONCHIP - default "0" - ---help--- - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option allows you to specify a single address at which to probe - for the device, which is useful if you have other devices in that - range which get upset when they are probed. - - (Note that on PowerPC, the normal probe will only check at - 0xE4000000.) - - Normally, you should leave this set to zero, to allow the probe at - the normal addresses. - -config MTD_NAND_DISKONCHIP_PROBE_HIGH - bool "Probe high addresses" - depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED - help - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option changes to make it probe between 0xFFFC8000 and - 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be - useful to you. Say 'N'. - -config MTD_NAND_DISKONCHIP_BBTWRITE - bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP" - depends on MTD_NAND_DISKONCHIP - help - On DiskOnChip devices shipped with the INFTL filesystem (Millennium - and 2000 TSOP/Alon), Linux reserves some space at the end of the - device for the Bad Block Table (BBT). If you have existing INFTL - data on your device (created by non-Linux tools such as M-Systems' - DOS drivers), your data might overlap the area Linux wants to use for - the BBT. If this is a concern for you, leave this option disabled and - Linux will not write BBT data into this area. - The downside of leaving this option disabled is that if bad blocks - are detected by Linux, they will not be recorded in the BBT, which - could cause future problems. - Once you enable this option, new filesystems (INFTL or others, created - in Linux or other operating systems) will not use the reserved area. - The only reason not to enable this option is to prevent damage to - preexisting filesystems. - Even if you leave this disabled, you can enable BBT writes at module - load time (assuming you build diskonchip as a module) with the module - parameter "inftl_bbt_write=1". - -config MTD_NAND_DOCG4 - tristate "Support for DiskOnChip G4" - depends on HAS_IOMEM - select BCH - select BITREVERSE - help - Support for diskonchip G4 nand flash, found in various smartphones and - PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba - Portege G900, Asus P526, and O2 XDA Zinc. - - With this driver you will be able to use UBI and create a ubifs on the - device, so you may wish to consider enabling UBI and UBIFS as well. - - These devices ship with the Mys/Sandisk SAFTL formatting, for which - there is currently no mtd parser, so you may want to use command line - partitioning to segregate write-protected blocks. On the Treo680, the - first five erase blocks (256KiB each) are write-protected, followed - by the block containing the saftl partition table. This is probably - typical. - -config MTD_NAND_SHARPSL - tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" - depends on ARCH_PXA - -config MTD_NAND_CAFE - tristate "NAND support for OLPC CAFÉ chip" - depends on PCI - select REED_SOLOMON - select REED_SOLOMON_DEC16 - help - Use NAND flash attached to the CAFÉ chip designed for the OLPC - laptop. - -config MTD_NAND_CS553X - tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" - depends on X86_32 - depends on !UML && HAS_IOMEM - help - The CS553x companion chips for the AMD Geode processor - include NAND flash controllers with built-in hardware ECC - capabilities; enabling this option will allow you to use - these. The driver will check the MSRs to verify that the - controller is enabled for NAND, and currently requires that - the controller be in MMIO mode. - - If you say "m", the module will be called cs553x_nand. - -config MTD_NAND_ATMEL - tristate "Support for NAND Flash / SmartMedia on AT91" - depends on ARCH_AT91 - select MFD_ATMEL_SMC - help - Enables support for NAND Flash / Smart Media Card interface - on Atmel AT91 processors. - -config MTD_NAND_PXA3xx - tristate "NAND support on PXA3xx and Armada 370/XP" - depends on !MTD_NAND_MARVELL - depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU - help - - This enables the driver for the NAND flash device found on - PXA3xx processors (NFCv1) and also on 32-bit Armada - platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada - platforms (7K, 8K) (NFCv2). - -config MTD_NAND_MARVELL - tristate "NAND controller support on Marvell boards" - depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \ - COMPILE_TEST - depends on HAS_IOMEM - help - This enables the NAND flash controller driver for Marvell boards, - including: - - PXA3xx processors (NFCv1) - - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2) - - 64-bit Aramda platforms (7k, 8k) (NFCv2) - -config MTD_NAND_SLC_LPC32XX - tristate "NXP LPC32xx SLC Controller" - depends on ARCH_LPC32XX - help - Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell - chips) NAND controller. This is the default for the PHYTEC 3250 - reference board which contains a NAND256R3A2CZA6 chip. - - Please check the actual NAND chip connected and its support - by the SLC NAND controller. - -config MTD_NAND_MLC_LPC32XX - tristate "NXP LPC32xx MLC Controller" - depends on ARCH_LPC32XX - help - Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND - controller. This is the default for the WORK92105 controller - board. - - Please check the actual NAND chip connected and its support - by the MLC NAND controller. - -config MTD_NAND_CM_X270 - tristate "Support for NAND Flash on CM-X270 modules" - depends on MACH_ARMCORE - -config MTD_NAND_PASEMI - tristate "NAND support for PA Semi PWRficient" - depends on PPC_PASEMI - help - Enables support for NAND Flash interface on PA Semi PWRficient - based boards - -config MTD_NAND_TMIO - tristate "NAND Flash device on Toshiba Mobile IO Controller" - depends on MFD_TMIO - help - Support for NAND flash connected to a Toshiba Mobile IO - Controller in some PDAs, including the Sharp SL6000x. - -config MTD_NAND_NANDSIM - tristate "Support for NAND Flash Simulator" - help - The simulator may simulate various NAND flash chips for the - MTD nand layer. - -config MTD_NAND_GPMI_NAND - tristate "GPMI NAND Flash Controller driver" - depends on MTD_NAND && MXS_DMA - help - Enables NAND Flash support for IMX23, IMX28 or IMX6. - The GPMI controller is very powerful, with the help of BCH - module, it can do the hardware ECC. The GPMI supports several - NAND flashs at the same time. - -config MTD_NAND_BRCMNAND - tristate "Broadcom STB NAND controller" - depends on ARM || ARM64 || MIPS - help - Enables the Broadcom NAND controller driver. The controller was - originally designed for Set-Top Box but is used on various BCM7xxx, - BCM3xxx, BCM63xxx, iProc/Cygnus and more. - -config MTD_NAND_BCM47XXNFLASH - tristate "Support for NAND flash on BCM4706 BCMA bus" - depends on BCMA_NFLASH - help - BCMA bus can have various flash memories attached, they are - registered by bcma as platform devices. This enables driver for - NAND flash memories. For now only BCM4706 is supported. - -config MTD_NAND_PLATFORM - tristate "Support for generic platform NAND driver" - depends on HAS_IOMEM - help - This implements a generic NAND driver for on-SOC platform - devices. You will need to provide platform-specific functions - via platform_data. - -config MTD_NAND_ORION - tristate "NAND Flash support for Marvell Orion SoC" - depends on PLAT_ORION - help - This enables the NAND flash controller on Orion machines. - - No board specific support is done by this driver, each board - must advertise a platform_device for the driver to attach. - -config MTD_NAND_OXNAS - tristate "NAND Flash support for Oxford Semiconductor SoC" - depends on ARCH_OXNAS || COMPILE_TEST - depends on HAS_IOMEM - help - This enables the NAND flash controller on Oxford Semiconductor SoCs. - -config MTD_NAND_FSL_ELBC - tristate "NAND support for Freescale eLBC controllers" - depends on FSL_SOC - select FSL_LBC - help - Various Freescale chips, including the 8313, include a NAND Flash - Controller Module with built-in hardware ECC capabilities. - Enabling this option will enable you to use this to control - external NAND devices. - -config MTD_NAND_FSL_IFC - tristate "NAND support for Freescale IFC controller" - depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A - select FSL_IFC - select MEMORY - help - Various Freescale chips e.g P1010, include a NAND Flash machine - with built-in hardware ECC capabilities. - Enabling this option will enable you to use this to control - external NAND devices. - -config MTD_NAND_FSL_UPM - tristate "Support for NAND on Freescale UPM" - depends on PPC_83xx || PPC_85xx - select FSL_LBC - help - Enables support for NAND Flash chips wired onto Freescale PowerPC - processor localbus with User-Programmable Machine support. - -config MTD_NAND_MPC5121_NFC - tristate "MPC5121 built-in NAND Flash Controller support" - depends on PPC_MPC512x - help - This enables the driver for the NAND flash controller on the - MPC5121 SoC. - -config MTD_NAND_VF610_NFC - tristate "Support for Freescale NFC for VF610/MPC5125" - depends on (SOC_VF610 || COMPILE_TEST) - depends on HAS_IOMEM - help - Enables support for NAND Flash Controller on some Freescale - processors like the VF610, MPC5125, MCF54418 or Kinetis K70. - The driver supports a maximum 2k page size. With 2k pages and - 64 bytes or more of OOB, hardware ECC with up to 32-bit error - correction is supported. Hardware ECC is only enabled through - device tree. - -config MTD_NAND_MXC - tristate "MXC NAND support" - depends on ARCH_MXC - help - This enables the driver for the NAND flash controller on the - MXC processors. - -config MTD_NAND_SH_FLCTL - tristate "Support for NAND on Renesas SuperH FLCTL" - depends on SUPERH || COMPILE_TEST - depends on HAS_IOMEM - depends on HAS_DMA - help - Several Renesas SuperH CPU has FLCTL. This option enables support - for NAND Flash using FLCTL. - -config MTD_NAND_DAVINCI - tristate "Support NAND on DaVinci/Keystone SoC" - depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) - help - Enable the driver for NAND flash chips on Texas Instruments - DaVinci/Keystone processors. - -config MTD_NAND_TXX9NDFMC - tristate "NAND Flash support for TXx9 SoC" - depends on SOC_TX4938 || SOC_TX4939 - help - This enables the NAND flash controller on the TXx9 SoCs. - -config MTD_NAND_SOCRATES - tristate "Support for NAND on Socrates board" - depends on SOCRATES - help - Enables support for NAND Flash chips wired onto Socrates board. - -config MTD_NAND_NUC900 - tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." - depends on ARCH_W90X900 - help - This enables the driver for the NAND Flash on evaluation board based - on w90p910 / NUC9xx. - -config MTD_NAND_JZ4740 - tristate "Support for JZ4740 SoC NAND controller" - depends on MACH_JZ4740 - help - Enables support for NAND Flash on JZ4740 SoC based boards. - -config MTD_NAND_JZ4780 - tristate "Support for NAND on JZ4780 SoC" - depends on MACH_JZ4780 && JZ4780_NEMC - help - Enables support for NAND Flash connected to the NEMC on JZ4780 SoC - based boards, using the BCH controller for hardware error correction. - -config MTD_NAND_FSMC - tristate "Support for NAND on ST Micros FSMC" - depends on OF - depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 - help - Enables support for NAND Flash chips on the ST Microelectronics - Flexible Static Memory Controller (FSMC) - -config MTD_NAND_XWAY - bool "Support for NAND on Lantiq XWAY SoC" - depends on LANTIQ && SOC_TYPE_XWAY - help - Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached - to the External Bus Unit (EBU). - -config MTD_NAND_SUNXI - tristate "Support for NAND on Allwinner SoCs" - depends on ARCH_SUNXI - help - Enables support for NAND Flash chips on Allwinner SoCs. - -config MTD_NAND_HISI504 - tristate "Support for NAND controller on Hisilicon SoC Hip04" - depends on ARCH_HISI || COMPILE_TEST - depends on HAS_DMA - help - Enables support for NAND controller on Hisilicon SoC Hip04. - -config MTD_NAND_QCOM - tristate "Support for NAND on QCOM SoCs" - depends on ARCH_QCOM - help - Enables support for NAND flash chips on SoCs containing the EBI2 NAND - controller. This controller is found on IPQ806x SoC. - -config MTD_NAND_MTK - tristate "Support for NAND controller on MTK SoCs" - depends on ARCH_MEDIATEK || COMPILE_TEST - depends on HAS_DMA - help - Enables support for NAND controller on MTK SoCs. - This controller is found on mt27xx, mt81xx, mt65xx SoCs. - -endif # MTD_NAND +source "drivers/mtd/nand/raw/Kconfig" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 4e0982476267..32af7168c5ba 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,68 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_MTD_NAND) += nand.o -obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o -obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o -obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o - -obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o -obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o -obj-$(CONFIG_MTD_NAND_DENALI) += denali.o -obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o -obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o -obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o -obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o -obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o -obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o -obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o -obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o -obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o -obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o -obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o -obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o -obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o -obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o -obj-$(CONFIG_MTD_NAND_ATMEL) += atmel/ -obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o -omap2_nand-objs := omap2.o -obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o -obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o -obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o -obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o -obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o -obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o -obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o -obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o -obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o -obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o -obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o -obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o -obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o -obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o -obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o -obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o -obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o -obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o -obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o -obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o -obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o -obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o -obj-$(CONFIG_MTD_NAND_RICOH) += r852.o -obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o -obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o -obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ -obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o -obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ -obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o -obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o -obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ -obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o -obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o - -nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o -nand-objs += nand_amd.o -nand-objs += nand_hynix.o -nand-objs += nand_macronix.o -nand-objs += nand_micron.o -nand-objs += nand_samsung.o -nand-objs += nand_toshiba.o +obj-y += raw/ diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c deleted file mode 100644 index 35f80523e52e..000000000000 --- a/drivers/mtd/nand/ams-delta.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2006 Jonathan McDowell - * - * Derived from drivers/mtd/nand/toto.c (removed in v2.6.28) - * Copyright (c) 2003 Texas Instruments - * Copyright (c) 2002 Thomas Gleixner - * - * Converted to platform driver by Janusz Krzysztofik - * Partially stolen from plat_nand.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * Amstrad E3 (Delta). - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -/* - * MTD structure for E3 (Delta) - */ -static struct mtd_info *ams_delta_mtd = NULL; - -/* - * Define partitions for flash devices - */ - -static const struct mtd_partition partition_info[] = { - { .name = "Kernel", - .offset = 0, - .size = 3 * SZ_1M + SZ_512K }, - { .name = "u-boot", - .offset = 3 * SZ_1M + SZ_512K, - .size = SZ_256K }, - { .name = "u-boot params", - .offset = 3 * SZ_1M + SZ_512K + SZ_256K, - .size = SZ_256K }, - { .name = "Amstrad LDR", - .offset = 4 * SZ_1M, - .size = SZ_256K }, - { .name = "File system", - .offset = 4 * SZ_1M + 1 * SZ_256K, - .size = 27 * SZ_1M }, - { .name = "PBL reserved", - .offset = 32 * SZ_1M - 3 * SZ_256K, - .size = 3 * SZ_256K }, -}; - -static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte) -{ - struct nand_chip *this = mtd_to_nand(mtd); - void __iomem *io_base = (void __iomem *)nand_get_controller_data(this); - - writew(0, io_base + OMAP_MPUIO_IO_CNTL); - writew(byte, this->IO_ADDR_W); - gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0); - ndelay(40); - gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1); -} - -static u_char ams_delta_read_byte(struct mtd_info *mtd) -{ - u_char res; - struct nand_chip *this = mtd_to_nand(mtd); - void __iomem *io_base = (void __iomem *)nand_get_controller_data(this); - - gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0); - ndelay(40); - writew(~0, io_base + OMAP_MPUIO_IO_CNTL); - res = readw(this->IO_ADDR_R); - gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1); - - return res; -} - -static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf, - int len) -{ - int i; - - for (i=0; i bit 2 - * NAND_CLE: bit 1 -> bit 7 - * NAND_ALE: bit 2 -> bit 6 - */ -static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - - if (ctrl & NAND_CTRL_CHANGE) { - gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE, - (ctrl & NAND_NCE) == 0); - gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE, - (ctrl & NAND_CLE) != 0); - gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE, - (ctrl & NAND_ALE) != 0); - } - - if (cmd != NAND_CMD_NONE) - ams_delta_write_byte(mtd, cmd); -} - -static int ams_delta_nand_ready(struct mtd_info *mtd) -{ - return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB); -} - -static const struct gpio _mandatory_gpio[] = { - { - .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE, - .flags = GPIOF_OUT_INIT_HIGH, - .label = "nand_nce", - }, - { - .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE, - .flags = GPIOF_OUT_INIT_HIGH, - .label = "nand_nre", - }, - { - .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP, - .flags = GPIOF_OUT_INIT_HIGH, - .label = "nand_nwp", - }, - { - .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE, - .flags = GPIOF_OUT_INIT_HIGH, - .label = "nand_nwe", - }, - { - .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE, - .flags = GPIOF_OUT_INIT_LOW, - .label = "nand_ale", - }, - { - .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE, - .flags = GPIOF_OUT_INIT_LOW, - .label = "nand_cle", - }, -}; - -/* - * Main initialization routine - */ -static int ams_delta_init(struct platform_device *pdev) -{ - struct nand_chip *this; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - void __iomem *io_base; - int err = 0; - - if (!res) - return -ENXIO; - - /* Allocate memory for MTD device structure and private data */ - this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); - if (!this) { - printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n"); - err = -ENOMEM; - goto out; - } - - ams_delta_mtd = nand_to_mtd(this); - ams_delta_mtd->owner = THIS_MODULE; - - /* - * Don't try to request the memory region from here, - * it should have been already requested from the - * gpio-omap driver and requesting it again would fail. - */ - - io_base = ioremap(res->start, resource_size(res)); - if (io_base == NULL) { - dev_err(&pdev->dev, "ioremap failed\n"); - err = -EIO; - goto out_free; - } - - nand_set_controller_data(this, (void *)io_base); - - /* Set address of NAND IO lines */ - this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH; - this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT; - this->read_byte = ams_delta_read_byte; - this->write_buf = ams_delta_write_buf; - this->read_buf = ams_delta_read_buf; - this->cmd_ctrl = ams_delta_hwcontrol; - if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) { - this->dev_ready = ams_delta_nand_ready; - } else { - this->dev_ready = NULL; - printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n"); - } - /* 25 us command delay time */ - this->chip_delay = 30; - this->ecc.mode = NAND_ECC_SOFT; - this->ecc.algo = NAND_ECC_HAMMING; - - platform_set_drvdata(pdev, io_base); - - /* Set chip enabled, but */ - err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); - if (err) - goto out_gpio; - - /* Scan to find existence of the device */ - err = nand_scan(ams_delta_mtd, 1); - if (err) - goto out_mtd; - - /* Register the partitions */ - mtd_device_register(ams_delta_mtd, partition_info, - ARRAY_SIZE(partition_info)); - - goto out; - - out_mtd: - gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); -out_gpio: - gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); - iounmap(io_base); -out_free: - kfree(this); - out: - return err; -} - -/* - * Clean up routine - */ -static int ams_delta_cleanup(struct platform_device *pdev) -{ - void __iomem *io_base = platform_get_drvdata(pdev); - - /* Release resources, unregister device */ - nand_release(ams_delta_mtd); - - gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); - gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); - iounmap(io_base); - - /* Free the MTD device structure */ - kfree(mtd_to_nand(ams_delta_mtd)); - - return 0; -} - -static struct platform_driver ams_delta_nand_driver = { - .probe = ams_delta_init, - .remove = ams_delta_cleanup, - .driver = { - .name = "ams-delta-nand", - }, -}; - -module_platform_driver(ams_delta_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jonathan McDowell "); -MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)"); diff --git a/drivers/mtd/nand/atmel/Makefile b/drivers/mtd/nand/atmel/Makefile deleted file mode 100644 index 288db4f38a8f..000000000000 --- a/drivers/mtd/nand/atmel/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -obj-$(CONFIG_MTD_NAND_ATMEL) += atmel-nand-controller.o atmel-pmecc.o - -atmel-nand-controller-objs := nand-controller.o -atmel-pmecc-objs := pmecc.o diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c deleted file mode 100644 index 12f6753d47ae..000000000000 --- a/drivers/mtd/nand/atmel/nand-controller.c +++ /dev/null @@ -1,2565 +0,0 @@ -/* - * Copyright 2017 ATMEL - * Copyright 2017 Free Electrons - * - * Author: Boris Brezillon - * - * Derived from the atmel_nand.c driver which contained the following - * copyrights: - * - * Copyright 2003 Rick Bronson - * - * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) - * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) - * - * Derived from drivers/mtd/spia.c (removed in v3.8) - * Copyright 2000 Steven J. Hill (sjhill@cotw.com) - * - * - * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 - * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 - * - * Derived from Das U-Boot source code - * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) - * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas - * - * Add Programmable Multibit ECC support for various AT91 SoC - * Copyright 2012 ATMEL, Hong Xu - * - * Add Nand Flash Controller support for SAMA5 SoC - * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * A few words about the naming convention in this file. This convention - * applies to structure and function names. - * - * Prefixes: - * - * - atmel_nand_: all generic structures/functions - * - atmel_smc_nand_: all structures/functions specific to the SMC interface - * (at91sam9 and avr32 SoCs) - * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface - * (sama5 SoCs and later) - * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block - * that is available in the HSMC block - * - _nand_: all SoC specific structures/functions - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pmecc.h" - -#define ATMEL_HSMC_NFC_CFG 0x0 -#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24) -#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24) -#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20)) -#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16) -#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13) -#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12) -#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9) -#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8) -#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0) -#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1) - -#define ATMEL_HSMC_NFC_CTRL 0x4 -#define ATMEL_HSMC_NFC_CTRL_EN BIT(0) -#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1) - -#define ATMEL_HSMC_NFC_SR 0x8 -#define ATMEL_HSMC_NFC_IER 0xc -#define ATMEL_HSMC_NFC_IDR 0x10 -#define ATMEL_HSMC_NFC_IMR 0x14 -#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1) -#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4) -#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5) -#define ATMEL_HSMC_NFC_SR_BUSY BIT(8) -#define ATMEL_HSMC_NFC_SR_WR BIT(11) -#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12) -#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16) -#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17) -#define ATMEL_HSMC_NFC_SR_DTOE BIT(20) -#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21) -#define ATMEL_HSMC_NFC_SR_AWB BIT(22) -#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23) -#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \ - ATMEL_HSMC_NFC_SR_UNDEF | \ - ATMEL_HSMC_NFC_SR_AWB | \ - ATMEL_HSMC_NFC_SR_NFCASE) -#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24) - -#define ATMEL_HSMC_NFC_ADDR 0x18 -#define ATMEL_HSMC_NFC_BANK 0x1c - -#define ATMEL_NFC_MAX_RB_ID 7 - -#define ATMEL_NFC_SRAM_SIZE 0x2400 - -#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2)) -#define ATMEL_NFC_VCMD2 BIT(18) -#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19) -#define ATMEL_NFC_CSID(cs) ((cs) << 22) -#define ATMEL_NFC_DATAEN BIT(25) -#define ATMEL_NFC_NFCWR BIT(26) - -#define ATMEL_NFC_MAX_ADDR_CYCLES 5 - -#define ATMEL_NAND_ALE_OFFSET BIT(21) -#define ATMEL_NAND_CLE_OFFSET BIT(22) - -#define DEFAULT_TIMEOUT_MS 1000 -#define MIN_DMA_LEN 128 - -enum atmel_nand_rb_type { - ATMEL_NAND_NO_RB, - ATMEL_NAND_NATIVE_RB, - ATMEL_NAND_GPIO_RB, -}; - -struct atmel_nand_rb { - enum atmel_nand_rb_type type; - union { - struct gpio_desc *gpio; - int id; - }; -}; - -struct atmel_nand_cs { - int id; - struct atmel_nand_rb rb; - struct gpio_desc *csgpio; - struct { - void __iomem *virt; - dma_addr_t dma; - } io; - - struct atmel_smc_cs_conf smcconf; -}; - -struct atmel_nand { - struct list_head node; - struct device *dev; - struct nand_chip base; - struct atmel_nand_cs *activecs; - struct atmel_pmecc_user *pmecc; - struct gpio_desc *cdgpio; - int numcs; - struct atmel_nand_cs cs[]; -}; - -static inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip) -{ - return container_of(chip, struct atmel_nand, base); -} - -enum atmel_nfc_data_xfer { - ATMEL_NFC_NO_DATA, - ATMEL_NFC_READ_DATA, - ATMEL_NFC_WRITE_DATA, -}; - -struct atmel_nfc_op { - u8 cs; - u8 ncmds; - u8 cmds[2]; - u8 naddrs; - u8 addrs[5]; - enum atmel_nfc_data_xfer data; - u32 wait; - u32 errors; -}; - -struct atmel_nand_controller; -struct atmel_nand_controller_caps; - -struct atmel_nand_controller_ops { - int (*probe)(struct platform_device *pdev, - const struct atmel_nand_controller_caps *caps); - int (*remove)(struct atmel_nand_controller *nc); - void (*nand_init)(struct atmel_nand_controller *nc, - struct atmel_nand *nand); - int (*ecc_init)(struct atmel_nand *nand); - int (*setup_data_interface)(struct atmel_nand *nand, int csline, - const struct nand_data_interface *conf); -}; - -struct atmel_nand_controller_caps { - bool has_dma; - bool legacy_of_bindings; - u32 ale_offs; - u32 cle_offs; - const struct atmel_nand_controller_ops *ops; -}; - -struct atmel_nand_controller { - struct nand_hw_control base; - const struct atmel_nand_controller_caps *caps; - struct device *dev; - struct regmap *smc; - struct dma_chan *dmac; - struct atmel_pmecc *pmecc; - struct list_head chips; - struct clk *mck; -}; - -static inline struct atmel_nand_controller * -to_nand_controller(struct nand_hw_control *ctl) -{ - return container_of(ctl, struct atmel_nand_controller, base); -} - -struct atmel_smc_nand_controller { - struct atmel_nand_controller base; - struct regmap *matrix; - unsigned int ebi_csa_offs; -}; - -static inline struct atmel_smc_nand_controller * -to_smc_nand_controller(struct nand_hw_control *ctl) -{ - return container_of(to_nand_controller(ctl), - struct atmel_smc_nand_controller, base); -} - -struct atmel_hsmc_nand_controller { - struct atmel_nand_controller base; - struct { - struct gen_pool *pool; - void __iomem *virt; - dma_addr_t dma; - } sram; - const struct atmel_hsmc_reg_layout *hsmc_layout; - struct regmap *io; - struct atmel_nfc_op op; - struct completion complete; - int irq; - - /* Only used when instantiating from legacy DT bindings. */ - struct clk *clk; -}; - -static inline struct atmel_hsmc_nand_controller * -to_hsmc_nand_controller(struct nand_hw_control *ctl) -{ - return container_of(to_nand_controller(ctl), - struct atmel_hsmc_nand_controller, base); -} - -static bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status) -{ - op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS; - op->wait ^= status & op->wait; - - return !op->wait || op->errors; -} - -static irqreturn_t atmel_nfc_interrupt(int irq, void *data) -{ - struct atmel_hsmc_nand_controller *nc = data; - u32 sr, rcvd; - bool done; - - regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr); - - rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); - done = atmel_nfc_op_done(&nc->op, sr); - - if (rcvd) - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd); - - if (done) - complete(&nc->complete); - - return rcvd ? IRQ_HANDLED : IRQ_NONE; -} - -static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll, - unsigned int timeout_ms) -{ - int ret; - - if (!timeout_ms) - timeout_ms = DEFAULT_TIMEOUT_MS; - - if (poll) { - u32 status; - - ret = regmap_read_poll_timeout(nc->base.smc, - ATMEL_HSMC_NFC_SR, status, - atmel_nfc_op_done(&nc->op, - status), - 0, timeout_ms * 1000); - } else { - init_completion(&nc->complete); - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER, - nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); - ret = wait_for_completion_timeout(&nc->complete, - msecs_to_jiffies(timeout_ms)); - if (!ret) - ret = -ETIMEDOUT; - else - ret = 0; - - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); - } - - if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) { - dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n"); - ret = -ETIMEDOUT; - } - - if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) { - dev_err(nc->base.dev, "Access to an undefined area\n"); - ret = -EIO; - } - - if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) { - dev_err(nc->base.dev, "Access while busy\n"); - ret = -EIO; - } - - if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) { - dev_err(nc->base.dev, "Wrong access size\n"); - ret = -EIO; - } - - return ret; -} - -static void atmel_nand_dma_transfer_finished(void *data) -{ - struct completion *finished = data; - - complete(finished); -} - -static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc, - void *buf, dma_addr_t dev_dma, size_t len, - enum dma_data_direction dir) -{ - DECLARE_COMPLETION_ONSTACK(finished); - dma_addr_t src_dma, dst_dma, buf_dma; - struct dma_async_tx_descriptor *tx; - dma_cookie_t cookie; - - buf_dma = dma_map_single(nc->dev, buf, len, dir); - if (dma_mapping_error(nc->dev, dev_dma)) { - dev_err(nc->dev, - "Failed to prepare a buffer for DMA access\n"); - goto err; - } - - if (dir == DMA_FROM_DEVICE) { - src_dma = dev_dma; - dst_dma = buf_dma; - } else { - src_dma = buf_dma; - dst_dma = dev_dma; - } - - tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len, - DMA_CTRL_ACK | DMA_PREP_INTERRUPT); - if (!tx) { - dev_err(nc->dev, "Failed to prepare DMA memcpy\n"); - goto err_unmap; - } - - tx->callback = atmel_nand_dma_transfer_finished; - tx->callback_param = &finished; - - cookie = dmaengine_submit(tx); - if (dma_submit_error(cookie)) { - dev_err(nc->dev, "Failed to do DMA tx_submit\n"); - goto err_unmap; - } - - dma_async_issue_pending(nc->dmac); - wait_for_completion(&finished); - - return 0; - -err_unmap: - dma_unmap_single(nc->dev, buf_dma, len, dir); - -err: - dev_dbg(nc->dev, "Fall back to CPU I/O\n"); - - return -EIO; -} - -static u8 atmel_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - - return ioread8(nand->activecs->io.virt); -} - -static u16 atmel_nand_read_word(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - - return ioread16(nand->activecs->io.virt); -} - -static void atmel_nand_write_byte(struct mtd_info *mtd, u8 byte) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - - if (chip->options & NAND_BUSWIDTH_16) - iowrite16(byte | (byte << 8), nand->activecs->io.virt); - else - iowrite8(byte, nand->activecs->io.virt); -} - -static void atmel_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_nand_controller *nc; - - nc = to_nand_controller(chip->controller); - - /* - * If the controller supports DMA, the buffer address is DMA-able and - * len is long enough to make DMA transfers profitable, let's trigger - * a DMA transfer. If it fails, fallback to PIO mode. - */ - if (nc->dmac && virt_addr_valid(buf) && - len >= MIN_DMA_LEN && - !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len, - DMA_FROM_DEVICE)) - return; - - if (chip->options & NAND_BUSWIDTH_16) - ioread16_rep(nand->activecs->io.virt, buf, len / 2); - else - ioread8_rep(nand->activecs->io.virt, buf, len); -} - -static void atmel_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_nand_controller *nc; - - nc = to_nand_controller(chip->controller); - - /* - * If the controller supports DMA, the buffer address is DMA-able and - * len is long enough to make DMA transfers profitable, let's trigger - * a DMA transfer. If it fails, fallback to PIO mode. - */ - if (nc->dmac && virt_addr_valid(buf) && - len >= MIN_DMA_LEN && - !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma, - len, DMA_TO_DEVICE)) - return; - - if (chip->options & NAND_BUSWIDTH_16) - iowrite16_rep(nand->activecs->io.virt, buf, len / 2); - else - iowrite8_rep(nand->activecs->io.virt, buf, len); -} - -static int atmel_nand_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - - return gpiod_get_value(nand->activecs->rb.gpio); -} - -static void atmel_nand_select_chip(struct mtd_info *mtd, int cs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - - if (cs < 0 || cs >= nand->numcs) { - nand->activecs = NULL; - chip->dev_ready = NULL; - return; - } - - nand->activecs = &nand->cs[cs]; - - if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB) - chip->dev_ready = atmel_nand_dev_ready; -} - -static int atmel_hsmc_nand_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_hsmc_nand_controller *nc; - u32 status; - - nc = to_hsmc_nand_controller(chip->controller); - - regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status); - - return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id); -} - -static void atmel_hsmc_nand_select_chip(struct mtd_info *mtd, int cs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_hsmc_nand_controller *nc; - - nc = to_hsmc_nand_controller(chip->controller); - - atmel_nand_select_chip(mtd, cs); - - if (!nand->activecs) { - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, - ATMEL_HSMC_NFC_CTRL_DIS); - return; - } - - if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB) - chip->dev_ready = atmel_hsmc_nand_dev_ready; - - regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG, - ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK | - ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK | - ATMEL_HSMC_NFC_CFG_RSPARE | - ATMEL_HSMC_NFC_CFG_WSPARE, - ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) | - ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) | - ATMEL_HSMC_NFC_CFG_RSPARE); - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, - ATMEL_HSMC_NFC_CTRL_EN); -} - -static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll) -{ - u8 *addrs = nc->op.addrs; - unsigned int op = 0; - u32 addr, val; - int i, ret; - - nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE; - - for (i = 0; i < nc->op.ncmds; i++) - op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]); - - if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++); - - op |= ATMEL_NFC_CSID(nc->op.cs) | - ATMEL_NFC_ACYCLE(nc->op.naddrs); - - if (nc->op.ncmds > 1) - op |= ATMEL_NFC_VCMD2; - - addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) | - (addrs[3] << 24); - - if (nc->op.data != ATMEL_NFC_NO_DATA) { - op |= ATMEL_NFC_DATAEN; - nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE; - - if (nc->op.data == ATMEL_NFC_WRITE_DATA) - op |= ATMEL_NFC_NFCWR; - } - - /* Clear all flags. */ - regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val); - - /* Send the command. */ - regmap_write(nc->io, op, addr); - - ret = atmel_nfc_wait(nc, poll, 0); - if (ret) - dev_err(nc->base.dev, - "Failed to send NAND command (err = %d)!", - ret); - - /* Reset the op state. */ - memset(&nc->op, 0, sizeof(nc->op)); - - return ret; -} - -static void atmel_hsmc_nand_cmd_ctrl(struct mtd_info *mtd, int dat, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_hsmc_nand_controller *nc; - - nc = to_hsmc_nand_controller(chip->controller); - - if (ctrl & NAND_ALE) { - if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) - return; - - nc->op.addrs[nc->op.naddrs++] = dat; - } else if (ctrl & NAND_CLE) { - if (nc->op.ncmds > 1) - return; - - nc->op.cmds[nc->op.ncmds++] = dat; - } - - if (dat == NAND_CMD_NONE) { - nc->op.cs = nand->activecs->id; - atmel_nfc_exec_op(nc, true); - } -} - -static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_nand_controller *nc; - - nc = to_nand_controller(chip->controller); - - if ((ctrl & NAND_CTRL_CHANGE) && nand->activecs->csgpio) { - if (ctrl & NAND_NCE) - gpiod_set_value(nand->activecs->csgpio, 0); - else - gpiod_set_value(nand->activecs->csgpio, 1); - } - - if (ctrl & NAND_ALE) - writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs); - else if (ctrl & NAND_CLE) - writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs); -} - -static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf, - bool oob_required) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_hsmc_nand_controller *nc; - int ret = -EIO; - - nc = to_hsmc_nand_controller(chip->controller); - - if (nc->base.dmac) - ret = atmel_nand_dma_transfer(&nc->base, (void *)buf, - nc->sram.dma, mtd->writesize, - DMA_TO_DEVICE); - - /* Falling back to CPU copy. */ - if (ret) - memcpy_toio(nc->sram.virt, buf, mtd->writesize); - - if (oob_required) - memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi, - mtd->oobsize); -} - -static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf, - bool oob_required) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_hsmc_nand_controller *nc; - int ret = -EIO; - - nc = to_hsmc_nand_controller(chip->controller); - - if (nc->base.dmac) - ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma, - mtd->writesize, DMA_FROM_DEVICE); - - /* Falling back to CPU copy. */ - if (ret) - memcpy_fromio(buf, nc->sram.virt, mtd->writesize); - - if (oob_required) - memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize, - mtd->oobsize); -} - -static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_hsmc_nand_controller *nc; - - nc = to_hsmc_nand_controller(chip->controller); - - if (column >= 0) { - nc->op.addrs[nc->op.naddrs++] = column; - - /* - * 2 address cycles for the column offset on large page NANDs. - */ - if (mtd->writesize > 512) - nc->op.addrs[nc->op.naddrs++] = column >> 8; - } - - if (page >= 0) { - nc->op.addrs[nc->op.naddrs++] = page; - nc->op.addrs[nc->op.naddrs++] = page >> 8; - - if (chip->options & NAND_ROW_ADDR_3) - nc->op.addrs[nc->op.naddrs++] = page >> 16; - } -} - -static int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw) -{ - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_nand_controller *nc; - int ret; - - nc = to_nand_controller(chip->controller); - - if (raw) - return 0; - - ret = atmel_pmecc_enable(nand->pmecc, op); - if (ret) - dev_err(nc->dev, - "Failed to enable ECC engine (err = %d)\n", ret); - - return ret; -} - -static void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw) -{ - struct atmel_nand *nand = to_atmel_nand(chip); - - if (!raw) - atmel_pmecc_disable(nand->pmecc); -} - -static int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw) -{ - struct atmel_nand *nand = to_atmel_nand(chip); - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand_controller *nc; - struct mtd_oob_region oobregion; - void *eccbuf; - int ret, i; - - nc = to_nand_controller(chip->controller); - - if (raw) - return 0; - - ret = atmel_pmecc_wait_rdy(nand->pmecc); - if (ret) { - dev_err(nc->dev, - "Failed to transfer NAND page data (err = %d)\n", - ret); - return ret; - } - - mtd_ooblayout_ecc(mtd, 0, &oobregion); - eccbuf = chip->oob_poi + oobregion.offset; - - for (i = 0; i < chip->ecc.steps; i++) { - atmel_pmecc_get_generated_eccbytes(nand->pmecc, i, - eccbuf); - eccbuf += chip->ecc.bytes; - } - - return 0; -} - -static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf, - bool raw) -{ - struct atmel_nand *nand = to_atmel_nand(chip); - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand_controller *nc; - struct mtd_oob_region oobregion; - int ret, i, max_bitflips = 0; - void *databuf, *eccbuf; - - nc = to_nand_controller(chip->controller); - - if (raw) - return 0; - - ret = atmel_pmecc_wait_rdy(nand->pmecc); - if (ret) { - dev_err(nc->dev, - "Failed to read NAND page data (err = %d)\n", - ret); - return ret; - } - - mtd_ooblayout_ecc(mtd, 0, &oobregion); - eccbuf = chip->oob_poi + oobregion.offset; - databuf = buf; - - for (i = 0; i < chip->ecc.steps; i++) { - ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf, - eccbuf); - if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc)) - ret = nand_check_erased_ecc_chunk(databuf, - chip->ecc.size, - eccbuf, - chip->ecc.bytes, - NULL, 0, - chip->ecc.strength); - - if (ret >= 0) - max_bitflips = max(ret, max_bitflips); - else - mtd->ecc_stats.failed++; - - databuf += chip->ecc.size; - eccbuf += chip->ecc.bytes; - } - - return max_bitflips; -} - -static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, - bool oob_required, int page, bool raw) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand *nand = to_atmel_nand(chip); - int ret; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); - if (ret) - return ret; - - atmel_nand_write_buf(mtd, buf, mtd->writesize); - - ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); - if (ret) { - atmel_pmecc_disable(nand->pmecc); - return ret; - } - - atmel_nand_pmecc_disable(chip, raw); - - atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, const u8 *buf, - int oob_required, int page) -{ - return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false); -} - -static int atmel_nand_pmecc_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, - const u8 *buf, int oob_required, - int page) -{ - return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true); -} - -static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, - bool oob_required, int page, bool raw) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - nand_read_page_op(chip, page, 0, NULL, 0); - - ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); - if (ret) - return ret; - - atmel_nand_read_buf(mtd, buf, mtd->writesize); - atmel_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - ret = atmel_nand_pmecc_correct_data(chip, buf, raw); - - atmel_nand_pmecc_disable(chip, raw); - - return ret; -} - -static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, - int oob_required, int page) -{ - return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false); -} - -static int atmel_nand_pmecc_read_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, - int oob_required, int page) -{ - return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true); -} - -static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip, - const u8 *buf, bool oob_required, - int page, bool raw) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_hsmc_nand_controller *nc; - int ret, status; - - nc = to_hsmc_nand_controller(chip->controller); - - atmel_nfc_copy_to_sram(chip, buf, false); - - nc->op.cmds[0] = NAND_CMD_SEQIN; - nc->op.ncmds = 1; - atmel_nfc_set_op_addr(chip, page, 0x0); - nc->op.cs = nand->activecs->id; - nc->op.data = ATMEL_NFC_WRITE_DATA; - - ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); - if (ret) - return ret; - - ret = atmel_nfc_exec_op(nc, false); - if (ret) { - atmel_nand_pmecc_disable(chip, raw); - dev_err(nc->base.dev, - "Failed to transfer NAND page data (err = %d)\n", - ret); - return ret; - } - - ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); - - atmel_nand_pmecc_disable(chip, raw); - - if (ret) - return ret; - - atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - nc->op.cmds[0] = NAND_CMD_PAGEPROG; - nc->op.ncmds = 1; - nc->op.cs = nand->activecs->id; - ret = atmel_nfc_exec_op(nc, false); - if (ret) - dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n", - ret); - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - return -EIO; - - return ret; -} - -static int atmel_hsmc_nand_pmecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const u8 *buf, int oob_required, - int page) -{ - return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, - false); -} - -static int atmel_hsmc_nand_pmecc_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, - const u8 *buf, - int oob_required, int page) -{ - return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, - true); -} - -static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, - bool oob_required, int page, - bool raw) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_hsmc_nand_controller *nc; - int ret; - - nc = to_hsmc_nand_controller(chip->controller); - - /* - * Optimized read page accessors only work when the NAND R/B pin is - * connected to a native SoC R/B pin. If that's not the case, fallback - * to the non-optimized one. - */ - if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) { - nand_read_page_op(chip, page, 0, NULL, 0); - - return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, - raw); - } - - nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0; - - if (mtd->writesize > 512) - nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART; - - atmel_nfc_set_op_addr(chip, page, 0x0); - nc->op.cs = nand->activecs->id; - nc->op.data = ATMEL_NFC_READ_DATA; - - ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); - if (ret) - return ret; - - ret = atmel_nfc_exec_op(nc, false); - if (ret) { - atmel_nand_pmecc_disable(chip, raw); - dev_err(nc->base.dev, - "Failed to load NAND page data (err = %d)\n", - ret); - return ret; - } - - atmel_nfc_copy_from_sram(chip, buf, true); - - ret = atmel_nand_pmecc_correct_data(chip, buf, raw); - - atmel_nand_pmecc_disable(chip, raw); - - return ret; -} - -static int atmel_hsmc_nand_pmecc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, - int oob_required, int page) -{ - return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, - false); -} - -static int atmel_hsmc_nand_pmecc_read_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, - u8 *buf, int oob_required, - int page) -{ - return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, - true); -} - -static int atmel_nand_pmecc_init(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_nand_controller *nc; - struct atmel_pmecc_user_req req; - - nc = to_nand_controller(chip->controller); - - if (!nc->pmecc) { - dev_err(nc->dev, "HW ECC not supported\n"); - return -ENOTSUPP; - } - - if (nc->caps->legacy_of_bindings) { - u32 val; - - if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap", - &val)) - chip->ecc.strength = val; - - if (!of_property_read_u32(nc->dev->of_node, - "atmel,pmecc-sector-size", - &val)) - chip->ecc.size = val; - } - - if (chip->ecc.options & NAND_ECC_MAXIMIZE) - req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; - else if (chip->ecc.strength) - req.ecc.strength = chip->ecc.strength; - else if (chip->ecc_strength_ds) - req.ecc.strength = chip->ecc_strength_ds; - else - req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; - - if (chip->ecc.size) - req.ecc.sectorsize = chip->ecc.size; - else if (chip->ecc_step_ds) - req.ecc.sectorsize = chip->ecc_step_ds; - else - req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO; - - req.pagesize = mtd->writesize; - req.oobsize = mtd->oobsize; - - if (mtd->writesize <= 512) { - req.ecc.bytes = 4; - req.ecc.ooboffset = 0; - } else { - req.ecc.bytes = mtd->oobsize - 2; - req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO; - } - - nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req); - if (IS_ERR(nand->pmecc)) - return PTR_ERR(nand->pmecc); - - chip->ecc.algo = NAND_ECC_BCH; - chip->ecc.size = req.ecc.sectorsize; - chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors; - chip->ecc.strength = req.ecc.strength; - - chip->options |= NAND_NO_SUBPAGE_WRITE; - - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); - - return 0; -} - -static int atmel_nand_ecc_init(struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - struct atmel_nand_controller *nc; - int ret; - - nc = to_nand_controller(chip->controller); - - switch (chip->ecc.mode) { - case NAND_ECC_NONE: - case NAND_ECC_SOFT: - /* - * Nothing to do, the core will initialize everything for us. - */ - break; - - case NAND_ECC_HW: - ret = atmel_nand_pmecc_init(chip); - if (ret) - return ret; - - chip->ecc.read_page = atmel_nand_pmecc_read_page; - chip->ecc.write_page = atmel_nand_pmecc_write_page; - chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw; - chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw; - break; - - default: - /* Other modes are not supported. */ - dev_err(nc->dev, "Unsupported ECC mode: %d\n", - chip->ecc.mode); - return -ENOTSUPP; - } - - return 0; -} - -static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - int ret; - - ret = atmel_nand_ecc_init(nand); - if (ret) - return ret; - - if (chip->ecc.mode != NAND_ECC_HW) - return 0; - - /* Adjust the ECC operations for the HSMC IP. */ - chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page; - chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page; - chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw; - chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw; - - return 0; -} - -static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, - const struct nand_data_interface *conf, - struct atmel_smc_cs_conf *smcconf) -{ - u32 ncycles, totalcycles, timeps, mckperiodps; - struct atmel_nand_controller *nc; - int ret; - - nc = to_nand_controller(nand->base.controller); - - /* DDR interface not supported. */ - if (conf->type != NAND_SDR_IFACE) - return -ENOTSUPP; - - /* - * tRC < 30ns implies EDO mode. This controller does not support this - * mode. - */ - if (conf->timings.sdr.tRC_min < 30000) - return -ENOTSUPP; - - atmel_smc_cs_conf_init(smcconf); - - mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck); - mckperiodps *= 1000; - - /* - * Set write pulse timing. This one is easy to extract: - * - * NWE_PULSE = tWP - */ - ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps); - totalcycles = ncycles; - ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT, - ncycles); - if (ret) - return ret; - - /* - * The write setup timing depends on the operation done on the NAND. - * All operations goes through the same data bus, but the operation - * type depends on the address we are writing to (ALE/CLE address - * lines). - * Since we have no way to differentiate the different operations at - * the SMC level, we must consider the worst case (the biggest setup - * time among all operation types): - * - * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE - */ - timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min, - conf->timings.sdr.tALS_min); - timeps = max(timeps, conf->timings.sdr.tDS_min); - ncycles = DIV_ROUND_UP(timeps, mckperiodps); - ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0; - totalcycles += ncycles; - ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT, - ncycles); - if (ret) - return ret; - - /* - * As for the write setup timing, the write hold timing depends on the - * operation done on the NAND: - * - * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH) - */ - timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min, - conf->timings.sdr.tALH_min); - timeps = max3(timeps, conf->timings.sdr.tDH_min, - conf->timings.sdr.tWH_min); - ncycles = DIV_ROUND_UP(timeps, mckperiodps); - totalcycles += ncycles; - - /* - * The write cycle timing is directly matching tWC, but is also - * dependent on the other timings on the setup and hold timings we - * calculated earlier, which gives: - * - * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD) - */ - ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps); - ncycles = max(totalcycles, ncycles); - ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT, - ncycles); - if (ret) - return ret; - - /* - * We don't want the CS line to be toggled between each byte/word - * transfer to the NAND. The only way to guarantee that is to have the - * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: - * - * NCS_WR_PULSE = NWE_CYCLE - */ - ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT, - ncycles); - if (ret) - return ret; - - /* - * As for the write setup timing, the read hold timing depends on the - * operation done on the NAND: - * - * NRD_HOLD = max(tREH, tRHOH) - */ - timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min); - ncycles = DIV_ROUND_UP(timeps, mckperiodps); - totalcycles = ncycles; - - /* - * TDF = tRHZ - NRD_HOLD - */ - ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps); - ncycles -= totalcycles; - - /* - * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and - * we might end up with a config that does not fit in the TDF field. - * Just take the max value in this case and hope that the NAND is more - * tolerant than advertised. - */ - if (ncycles > ATMEL_SMC_MODE_TDF_MAX) - ncycles = ATMEL_SMC_MODE_TDF_MAX; - else if (ncycles < ATMEL_SMC_MODE_TDF_MIN) - ncycles = ATMEL_SMC_MODE_TDF_MIN; - - smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) | - ATMEL_SMC_MODE_TDFMODE_OPTIMIZED; - - /* - * Read pulse timing directly matches tRP: - * - * NRD_PULSE = tRP - */ - ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps); - totalcycles += ncycles; - ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT, - ncycles); - if (ret) - return ret; - - /* - * The write cycle timing is directly matching tWC, but is also - * dependent on the setup and hold timings we calculated earlier, - * which gives: - * - * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD) - * - * NRD_SETUP is always 0. - */ - ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps); - ncycles = max(totalcycles, ncycles); - ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT, - ncycles); - if (ret) - return ret; - - /* - * We don't want the CS line to be toggled between each byte/word - * transfer from the NAND. The only way to guarantee that is to have - * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: - * - * NCS_RD_PULSE = NRD_CYCLE - */ - ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT, - ncycles); - if (ret) - return ret; - - /* Txxx timings are directly matching tXXX ones. */ - ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps); - ret = atmel_smc_cs_conf_set_timing(smcconf, - ATMEL_HSMC_TIMINGS_TCLR_SHIFT, - ncycles); - if (ret) - return ret; - - ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps); - ret = atmel_smc_cs_conf_set_timing(smcconf, - ATMEL_HSMC_TIMINGS_TADL_SHIFT, - ncycles); - /* - * Version 4 of the ONFI spec mandates that tADL be at least 400 - * nanoseconds, but, depending on the master clock rate, 400 ns may not - * fit in the tADL field of the SMC reg. We need to relax the check and - * accept the -ERANGE return code. - * - * Note that previous versions of the ONFI spec had a lower tADL_min - * (100 or 200 ns). It's not clear why this timing constraint got - * increased but it seems most NANDs are fine with values lower than - * 400ns, so we should be safe. - */ - if (ret && ret != -ERANGE) - return ret; - - ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps); - ret = atmel_smc_cs_conf_set_timing(smcconf, - ATMEL_HSMC_TIMINGS_TAR_SHIFT, - ncycles); - if (ret) - return ret; - - ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps); - ret = atmel_smc_cs_conf_set_timing(smcconf, - ATMEL_HSMC_TIMINGS_TRR_SHIFT, - ncycles); - if (ret) - return ret; - - ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps); - ret = atmel_smc_cs_conf_set_timing(smcconf, - ATMEL_HSMC_TIMINGS_TWB_SHIFT, - ncycles); - if (ret) - return ret; - - /* Attach the CS line to the NFC logic. */ - smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL; - - /* Set the appropriate data bus width. */ - if (nand->base.options & NAND_BUSWIDTH_16) - smcconf->mode |= ATMEL_SMC_MODE_DBW_16; - - /* Operate in NRD/NWE READ/WRITEMODE. */ - smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD | - ATMEL_SMC_MODE_WRITEMODE_NWE; - - return 0; -} - -static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand, - int csline, - const struct nand_data_interface *conf) -{ - struct atmel_nand_controller *nc; - struct atmel_smc_cs_conf smcconf; - struct atmel_nand_cs *cs; - int ret; - - nc = to_nand_controller(nand->base.controller); - - ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); - if (ret) - return ret; - - if (csline == NAND_DATA_IFACE_CHECK_ONLY) - return 0; - - cs = &nand->cs[csline]; - cs->smcconf = smcconf; - atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf); - - return 0; -} - -static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand, - int csline, - const struct nand_data_interface *conf) -{ - struct atmel_hsmc_nand_controller *nc; - struct atmel_smc_cs_conf smcconf; - struct atmel_nand_cs *cs; - int ret; - - nc = to_hsmc_nand_controller(nand->base.controller); - - ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); - if (ret) - return ret; - - if (csline == NAND_DATA_IFACE_CHECK_ONLY) - return 0; - - cs = &nand->cs[csline]; - cs->smcconf = smcconf; - - if (cs->rb.type == ATMEL_NAND_NATIVE_RB) - cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id); - - atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id, - &cs->smcconf); - - return 0; -} - -static int atmel_nand_setup_data_interface(struct mtd_info *mtd, int csline, - const struct nand_data_interface *conf) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_nand_controller *nc; - - nc = to_nand_controller(nand->base.controller); - - if (csline >= nand->numcs || - (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY)) - return -EINVAL; - - return nc->caps->ops->setup_data_interface(nand, csline, conf); -} - -static void atmel_nand_init(struct atmel_nand_controller *nc, - struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - struct mtd_info *mtd = nand_to_mtd(chip); - - mtd->dev.parent = nc->dev; - nand->base.controller = &nc->base; - - chip->cmd_ctrl = atmel_nand_cmd_ctrl; - chip->read_byte = atmel_nand_read_byte; - chip->read_word = atmel_nand_read_word; - chip->write_byte = atmel_nand_write_byte; - chip->read_buf = atmel_nand_read_buf; - chip->write_buf = atmel_nand_write_buf; - chip->select_chip = atmel_nand_select_chip; - - if (nc->mck && nc->caps->ops->setup_data_interface) - chip->setup_data_interface = atmel_nand_setup_data_interface; - - /* Some NANDs require a longer delay than the default one (20us). */ - chip->chip_delay = 40; - - /* - * Use a bounce buffer when the buffer passed by the MTD user is not - * suitable for DMA. - */ - if (nc->dmac) - chip->options |= NAND_USE_BOUNCE_BUFFER; - - /* Default to HW ECC if pmecc is available. */ - if (nc->pmecc) - chip->ecc.mode = NAND_ECC_HW; -} - -static void atmel_smc_nand_init(struct atmel_nand_controller *nc, - struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - struct atmel_smc_nand_controller *smc_nc; - int i; - - atmel_nand_init(nc, nand); - - smc_nc = to_smc_nand_controller(chip->controller); - if (!smc_nc->matrix) - return; - - /* Attach the CS to the NAND Flash logic. */ - for (i = 0; i < nand->numcs; i++) - regmap_update_bits(smc_nc->matrix, smc_nc->ebi_csa_offs, - BIT(nand->cs[i].id), BIT(nand->cs[i].id)); -} - -static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc, - struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - - atmel_nand_init(nc, nand); - - /* Overload some methods for the HSMC controller. */ - chip->cmd_ctrl = atmel_hsmc_nand_cmd_ctrl; - chip->select_chip = atmel_hsmc_nand_select_chip; -} - -static int atmel_nand_detect(struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand_controller *nc; - int ret; - - nc = to_nand_controller(chip->controller); - - ret = nand_scan_ident(mtd, nand->numcs, NULL); - if (ret) - dev_err(nc->dev, "nand_scan_ident() failed: %d\n", ret); - - return ret; -} - -static int atmel_nand_unregister(struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - ret = mtd_device_unregister(mtd); - if (ret) - return ret; - - nand_cleanup(chip); - list_del(&nand->node); - - return 0; -} - -static int atmel_nand_register(struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand_controller *nc; - int ret; - - nc = to_nand_controller(chip->controller); - - if (nc->caps->legacy_of_bindings || !nc->dev->of_node) { - /* - * We keep the MTD name unchanged to avoid breaking platforms - * where the MTD cmdline parser is used and the bootloader - * has not been updated to use the new naming scheme. - */ - mtd->name = "atmel_nand"; - } else if (!mtd->name) { - /* - * If the new bindings are used and the bootloader has not been - * updated to pass a new mtdparts parameter on the cmdline, you - * should define the following property in your nand node: - * - * label = "atmel_nand"; - * - * This way, mtd->name will be set by the core when - * nand_set_flash_node() is called. - */ - mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL, - "%s:nand.%d", dev_name(nc->dev), - nand->cs[0].id); - if (!mtd->name) { - dev_err(nc->dev, "Failed to allocate mtd->name\n"); - return -ENOMEM; - } - } - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret); - return ret; - } - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - dev_err(nc->dev, "Failed to register mtd device: %d\n", ret); - nand_cleanup(chip); - return ret; - } - - list_add_tail(&nand->node, &nc->chips); - - return 0; -} - -static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc, - struct device_node *np, - int reg_cells) -{ - struct atmel_nand *nand; - struct gpio_desc *gpio; - int numcs, ret, i; - - numcs = of_property_count_elems_of_size(np, "reg", - reg_cells * sizeof(u32)); - if (numcs < 1) { - dev_err(nc->dev, "Missing or invalid reg property\n"); - return ERR_PTR(-EINVAL); - } - - nand = devm_kzalloc(nc->dev, - sizeof(*nand) + (numcs * sizeof(*nand->cs)), - GFP_KERNEL); - if (!nand) { - dev_err(nc->dev, "Failed to allocate NAND object\n"); - return ERR_PTR(-ENOMEM); - } - - nand->numcs = numcs; - - gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "det", 0, - &np->fwnode, GPIOD_IN, - "nand-det"); - if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { - dev_err(nc->dev, - "Failed to get detect gpio (err = %ld)\n", - PTR_ERR(gpio)); - return ERR_CAST(gpio); - } - - if (!IS_ERR(gpio)) - nand->cdgpio = gpio; - - for (i = 0; i < numcs; i++) { - struct resource res; - u32 val; - - ret = of_address_to_resource(np, 0, &res); - if (ret) { - dev_err(nc->dev, "Invalid reg property (err = %d)\n", - ret); - return ERR_PTR(ret); - } - - ret = of_property_read_u32_index(np, "reg", i * reg_cells, - &val); - if (ret) { - dev_err(nc->dev, "Invalid reg property (err = %d)\n", - ret); - return ERR_PTR(ret); - } - - nand->cs[i].id = val; - - nand->cs[i].io.dma = res.start; - nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res); - if (IS_ERR(nand->cs[i].io.virt)) - return ERR_CAST(nand->cs[i].io.virt); - - if (!of_property_read_u32(np, "atmel,rb", &val)) { - if (val > ATMEL_NFC_MAX_RB_ID) - return ERR_PTR(-EINVAL); - - nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB; - nand->cs[i].rb.id = val; - } else { - gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, - "rb", i, &np->fwnode, - GPIOD_IN, "nand-rb"); - if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { - dev_err(nc->dev, - "Failed to get R/B gpio (err = %ld)\n", - PTR_ERR(gpio)); - return ERR_CAST(gpio); - } - - if (!IS_ERR(gpio)) { - nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB; - nand->cs[i].rb.gpio = gpio; - } - } - - gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "cs", - i, &np->fwnode, - GPIOD_OUT_HIGH, - "nand-cs"); - if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { - dev_err(nc->dev, - "Failed to get CS gpio (err = %ld)\n", - PTR_ERR(gpio)); - return ERR_CAST(gpio); - } - - if (!IS_ERR(gpio)) - nand->cs[i].csgpio = gpio; - } - - nand_set_flash_node(&nand->base, np); - - return nand; -} - -static int -atmel_nand_controller_add_nand(struct atmel_nand_controller *nc, - struct atmel_nand *nand) -{ - int ret; - - /* No card inserted, skip this NAND. */ - if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) { - dev_info(nc->dev, "No SmartMedia card inserted.\n"); - return 0; - } - - nc->caps->ops->nand_init(nc, nand); - - ret = atmel_nand_detect(nand); - if (ret) - return ret; - - ret = nc->caps->ops->ecc_init(nand); - if (ret) - return ret; - - return atmel_nand_register(nand); -} - -static int -atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc) -{ - struct atmel_nand *nand, *tmp; - int ret; - - list_for_each_entry_safe(nand, tmp, &nc->chips, node) { - ret = atmel_nand_unregister(nand); - if (ret) - return ret; - } - - return 0; -} - -static int -atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc) -{ - struct device *dev = nc->dev; - struct platform_device *pdev = to_platform_device(dev); - struct atmel_nand *nand; - struct gpio_desc *gpio; - struct resource *res; - - /* - * Legacy bindings only allow connecting a single NAND with a unique CS - * line to the controller. - */ - nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs), - GFP_KERNEL); - if (!nand) - return -ENOMEM; - - nand->numcs = 1; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nand->cs[0].io.virt = devm_ioremap_resource(dev, res); - if (IS_ERR(nand->cs[0].io.virt)) - return PTR_ERR(nand->cs[0].io.virt); - - nand->cs[0].io.dma = res->start; - - /* - * The old driver was hardcoding the CS id to 3 for all sama5 - * controllers. Since this id is only meaningful for the sama5 - * controller we can safely assign this id to 3 no matter the - * controller. - * If one wants to connect a NAND to a different CS line, he will - * have to use the new bindings. - */ - nand->cs[0].id = 3; - - /* R/B GPIO. */ - gpio = devm_gpiod_get_index_optional(dev, NULL, 0, GPIOD_IN); - if (IS_ERR(gpio)) { - dev_err(dev, "Failed to get R/B gpio (err = %ld)\n", - PTR_ERR(gpio)); - return PTR_ERR(gpio); - } - - if (gpio) { - nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB; - nand->cs[0].rb.gpio = gpio; - } - - /* CS GPIO. */ - gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH); - if (IS_ERR(gpio)) { - dev_err(dev, "Failed to get CS gpio (err = %ld)\n", - PTR_ERR(gpio)); - return PTR_ERR(gpio); - } - - nand->cs[0].csgpio = gpio; - - /* Card detect GPIO. */ - gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN); - if (IS_ERR(gpio)) { - dev_err(dev, - "Failed to get detect gpio (err = %ld)\n", - PTR_ERR(gpio)); - return PTR_ERR(gpio); - } - - nand->cdgpio = gpio; - - nand_set_flash_node(&nand->base, nc->dev->of_node); - - return atmel_nand_controller_add_nand(nc, nand); -} - -static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc) -{ - struct device_node *np, *nand_np; - struct device *dev = nc->dev; - int ret, reg_cells; - u32 val; - - /* We do not retrieve the SMC syscon when parsing old DTs. */ - if (nc->caps->legacy_of_bindings) - return atmel_nand_controller_legacy_add_nands(nc); - - np = dev->of_node; - - ret = of_property_read_u32(np, "#address-cells", &val); - if (ret) { - dev_err(dev, "missing #address-cells property\n"); - return ret; - } - - reg_cells = val; - - ret = of_property_read_u32(np, "#size-cells", &val); - if (ret) { - dev_err(dev, "missing #address-cells property\n"); - return ret; - } - - reg_cells += val; - - for_each_child_of_node(np, nand_np) { - struct atmel_nand *nand; - - nand = atmel_nand_create(nc, nand_np, reg_cells); - if (IS_ERR(nand)) { - ret = PTR_ERR(nand); - goto err; - } - - ret = atmel_nand_controller_add_nand(nc, nand); - if (ret) - goto err; - } - - return 0; - -err: - atmel_nand_controller_remove_nands(nc); - - return ret; -} - -static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc) -{ - if (nc->dmac) - dma_release_channel(nc->dmac); - - clk_put(nc->mck); -} - -static const struct of_device_id atmel_matrix_of_ids[] = { - { - .compatible = "atmel,at91sam9260-matrix", - .data = (void *)AT91SAM9260_MATRIX_EBICSA, - }, - { - .compatible = "atmel,at91sam9261-matrix", - .data = (void *)AT91SAM9261_MATRIX_EBICSA, - }, - { - .compatible = "atmel,at91sam9263-matrix", - .data = (void *)AT91SAM9263_MATRIX_EBI0CSA, - }, - { - .compatible = "atmel,at91sam9rl-matrix", - .data = (void *)AT91SAM9RL_MATRIX_EBICSA, - }, - { - .compatible = "atmel,at91sam9g45-matrix", - .data = (void *)AT91SAM9G45_MATRIX_EBICSA, - }, - { - .compatible = "atmel,at91sam9n12-matrix", - .data = (void *)AT91SAM9N12_MATRIX_EBICSA, - }, - { - .compatible = "atmel,at91sam9x5-matrix", - .data = (void *)AT91SAM9X5_MATRIX_EBICSA, - }, - { /* sentinel */ }, -}; - -static int atmel_nand_controller_init(struct atmel_nand_controller *nc, - struct platform_device *pdev, - const struct atmel_nand_controller_caps *caps) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - int ret; - - nand_hw_control_init(&nc->base); - INIT_LIST_HEAD(&nc->chips); - nc->dev = dev; - nc->caps = caps; - - platform_set_drvdata(pdev, nc); - - nc->pmecc = devm_atmel_pmecc_get(dev); - if (IS_ERR(nc->pmecc)) { - ret = PTR_ERR(nc->pmecc); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Could not get PMECC object (err = %d)\n", - ret); - return ret; - } - - if (nc->caps->has_dma) { - dma_cap_mask_t mask; - - dma_cap_zero(mask); - dma_cap_set(DMA_MEMCPY, mask); - - nc->dmac = dma_request_channel(mask, NULL, NULL); - if (!nc->dmac) - dev_err(nc->dev, "Failed to request DMA channel\n"); - } - - /* We do not retrieve the SMC syscon when parsing old DTs. */ - if (nc->caps->legacy_of_bindings) - return 0; - - nc->mck = of_clk_get(dev->parent->of_node, 0); - if (IS_ERR(nc->mck)) { - dev_err(dev, "Failed to retrieve MCK clk\n"); - return PTR_ERR(nc->mck); - } - - np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); - if (!np) { - dev_err(dev, "Missing or invalid atmel,smc property\n"); - return -EINVAL; - } - - nc->smc = syscon_node_to_regmap(np); - of_node_put(np); - if (IS_ERR(nc->smc)) { - ret = PTR_ERR(nc->smc); - dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret); - return ret; - } - - return 0; -} - -static int -atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc) -{ - struct device *dev = nc->base.dev; - const struct of_device_id *match; - struct device_node *np; - int ret; - - /* We do not retrieve the matrix syscon when parsing old DTs. */ - if (nc->base.caps->legacy_of_bindings) - return 0; - - np = of_parse_phandle(dev->parent->of_node, "atmel,matrix", 0); - if (!np) - return 0; - - match = of_match_node(atmel_matrix_of_ids, np); - if (!match) { - of_node_put(np); - return 0; - } - - nc->matrix = syscon_node_to_regmap(np); - of_node_put(np); - if (IS_ERR(nc->matrix)) { - ret = PTR_ERR(nc->matrix); - dev_err(dev, "Could not get Matrix regmap (err = %d)\n", ret); - return ret; - } - - nc->ebi_csa_offs = (unsigned int)match->data; - - /* - * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1 - * add 4 to ->ebi_csa_offs. - */ - if (of_device_is_compatible(dev->parent->of_node, - "atmel,at91sam9263-ebi1")) - nc->ebi_csa_offs += 4; - - return 0; -} - -static int -atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc) -{ - struct regmap_config regmap_conf = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - }; - - struct device *dev = nc->base.dev; - struct device_node *nand_np, *nfc_np; - void __iomem *iomem; - struct resource res; - int ret; - - nand_np = dev->of_node; - nfc_np = of_find_compatible_node(dev->of_node, NULL, - "atmel,sama5d3-nfc"); - - nc->clk = of_clk_get(nfc_np, 0); - if (IS_ERR(nc->clk)) { - ret = PTR_ERR(nc->clk); - dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n", - ret); - goto out; - } - - ret = clk_prepare_enable(nc->clk); - if (ret) { - dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n", - ret); - goto out; - } - - nc->irq = of_irq_get(nand_np, 0); - if (nc->irq <= 0) { - ret = nc->irq ?: -ENXIO; - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get IRQ number (err = %d)\n", - ret); - goto out; - } - - ret = of_address_to_resource(nfc_np, 0, &res); - if (ret) { - dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n", - ret); - goto out; - } - - iomem = devm_ioremap_resource(dev, &res); - if (IS_ERR(iomem)) { - ret = PTR_ERR(iomem); - goto out; - } - - regmap_conf.name = "nfc-io"; - regmap_conf.max_register = resource_size(&res) - 4; - nc->io = devm_regmap_init_mmio(dev, iomem, ®map_conf); - if (IS_ERR(nc->io)) { - ret = PTR_ERR(nc->io); - dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", - ret); - goto out; - } - - ret = of_address_to_resource(nfc_np, 1, &res); - if (ret) { - dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n", - ret); - goto out; - } - - iomem = devm_ioremap_resource(dev, &res); - if (IS_ERR(iomem)) { - ret = PTR_ERR(iomem); - goto out; - } - - regmap_conf.name = "smc"; - regmap_conf.max_register = resource_size(&res) - 4; - nc->base.smc = devm_regmap_init_mmio(dev, iomem, ®map_conf); - if (IS_ERR(nc->base.smc)) { - ret = PTR_ERR(nc->base.smc); - dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", - ret); - goto out; - } - - ret = of_address_to_resource(nfc_np, 2, &res); - if (ret) { - dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n", - ret); - goto out; - } - - nc->sram.virt = devm_ioremap_resource(dev, &res); - if (IS_ERR(nc->sram.virt)) { - ret = PTR_ERR(nc->sram.virt); - goto out; - } - - nc->sram.dma = res.start; - -out: - of_node_put(nfc_np); - - return ret; -} - -static int -atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc) -{ - struct device *dev = nc->base.dev; - struct device_node *np; - int ret; - - np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); - if (!np) { - dev_err(dev, "Missing or invalid atmel,smc property\n"); - return -EINVAL; - } - - nc->hsmc_layout = atmel_hsmc_get_reg_layout(np); - - nc->irq = of_irq_get(np, 0); - of_node_put(np); - if (nc->irq <= 0) { - ret = nc->irq ?: -ENXIO; - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get IRQ number (err = %d)\n", - ret); - return ret; - } - - np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0); - if (!np) { - dev_err(dev, "Missing or invalid atmel,nfc-io property\n"); - return -EINVAL; - } - - nc->io = syscon_node_to_regmap(np); - of_node_put(np); - if (IS_ERR(nc->io)) { - ret = PTR_ERR(nc->io); - dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret); - return ret; - } - - nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node, - "atmel,nfc-sram", 0); - if (!nc->sram.pool) { - dev_err(nc->base.dev, "Missing SRAM\n"); - return -ENOMEM; - } - - nc->sram.virt = gen_pool_dma_alloc(nc->sram.pool, - ATMEL_NFC_SRAM_SIZE, - &nc->sram.dma); - if (!nc->sram.virt) { - dev_err(nc->base.dev, - "Could not allocate memory from the NFC SRAM pool\n"); - return -ENOMEM; - } - - return 0; -} - -static int -atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc) -{ - struct atmel_hsmc_nand_controller *hsmc_nc; - int ret; - - ret = atmel_nand_controller_remove_nands(nc); - if (ret) - return ret; - - hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base); - if (hsmc_nc->sram.pool) - gen_pool_free(hsmc_nc->sram.pool, - (unsigned long)hsmc_nc->sram.virt, - ATMEL_NFC_SRAM_SIZE); - - if (hsmc_nc->clk) { - clk_disable_unprepare(hsmc_nc->clk); - clk_put(hsmc_nc->clk); - } - - atmel_nand_controller_cleanup(nc); - - return 0; -} - -static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev, - const struct atmel_nand_controller_caps *caps) -{ - struct device *dev = &pdev->dev; - struct atmel_hsmc_nand_controller *nc; - int ret; - - nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); - if (!nc) - return -ENOMEM; - - ret = atmel_nand_controller_init(&nc->base, pdev, caps); - if (ret) - return ret; - - if (caps->legacy_of_bindings) - ret = atmel_hsmc_nand_controller_legacy_init(nc); - else - ret = atmel_hsmc_nand_controller_init(nc); - - if (ret) - return ret; - - /* Make sure all irqs are masked before registering our IRQ handler. */ - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); - ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt, - IRQF_SHARED, "nfc", nc); - if (ret) { - dev_err(dev, - "Could not get register NFC interrupt handler (err = %d)\n", - ret); - goto err; - } - - /* Initial NFC configuration. */ - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG, - ATMEL_HSMC_NFC_CFG_DTO_MAX); - - ret = atmel_nand_controller_add_nands(&nc->base); - if (ret) - goto err; - - return 0; - -err: - atmel_hsmc_nand_controller_remove(&nc->base); - - return ret; -} - -static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = { - .probe = atmel_hsmc_nand_controller_probe, - .remove = atmel_hsmc_nand_controller_remove, - .ecc_init = atmel_hsmc_nand_ecc_init, - .nand_init = atmel_hsmc_nand_init, - .setup_data_interface = atmel_hsmc_nand_setup_data_interface, -}; - -static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = { - .has_dma = true, - .ale_offs = BIT(21), - .cle_offs = BIT(22), - .ops = &atmel_hsmc_nc_ops, -}; - -/* Only used to parse old bindings. */ -static const struct atmel_nand_controller_caps atmel_sama5_nand_caps = { - .has_dma = true, - .ale_offs = BIT(21), - .cle_offs = BIT(22), - .ops = &atmel_hsmc_nc_ops, - .legacy_of_bindings = true, -}; - -static int atmel_smc_nand_controller_probe(struct platform_device *pdev, - const struct atmel_nand_controller_caps *caps) -{ - struct device *dev = &pdev->dev; - struct atmel_smc_nand_controller *nc; - int ret; - - nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); - if (!nc) - return -ENOMEM; - - ret = atmel_nand_controller_init(&nc->base, pdev, caps); - if (ret) - return ret; - - ret = atmel_smc_nand_controller_init(nc); - if (ret) - return ret; - - return atmel_nand_controller_add_nands(&nc->base); -} - -static int -atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc) -{ - int ret; - - ret = atmel_nand_controller_remove_nands(nc); - if (ret) - return ret; - - atmel_nand_controller_cleanup(nc); - - return 0; -} - -/* - * The SMC reg layout of at91rm9200 is completely different which prevents us - * from re-using atmel_smc_nand_setup_data_interface() for the - * ->setup_data_interface() hook. - * At this point, there's no support for the at91rm9200 SMC IP, so we leave - * ->setup_data_interface() unassigned. - */ -static const struct atmel_nand_controller_ops at91rm9200_nc_ops = { - .probe = atmel_smc_nand_controller_probe, - .remove = atmel_smc_nand_controller_remove, - .ecc_init = atmel_nand_ecc_init, - .nand_init = atmel_smc_nand_init, -}; - -static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = { - .ale_offs = BIT(21), - .cle_offs = BIT(22), - .ops = &at91rm9200_nc_ops, -}; - -static const struct atmel_nand_controller_ops atmel_smc_nc_ops = { - .probe = atmel_smc_nand_controller_probe, - .remove = atmel_smc_nand_controller_remove, - .ecc_init = atmel_nand_ecc_init, - .nand_init = atmel_smc_nand_init, - .setup_data_interface = atmel_smc_nand_setup_data_interface, -}; - -static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = { - .ale_offs = BIT(21), - .cle_offs = BIT(22), - .ops = &atmel_smc_nc_ops, -}; - -static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = { - .ale_offs = BIT(22), - .cle_offs = BIT(21), - .ops = &atmel_smc_nc_ops, -}; - -static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = { - .has_dma = true, - .ale_offs = BIT(21), - .cle_offs = BIT(22), - .ops = &atmel_smc_nc_ops, -}; - -/* Only used to parse old bindings. */ -static const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = { - .ale_offs = BIT(21), - .cle_offs = BIT(22), - .ops = &atmel_smc_nc_ops, - .legacy_of_bindings = true, -}; - -static const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = { - .ale_offs = BIT(22), - .cle_offs = BIT(21), - .ops = &atmel_smc_nc_ops, - .legacy_of_bindings = true, -}; - -static const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = { - .has_dma = true, - .ale_offs = BIT(21), - .cle_offs = BIT(22), - .ops = &atmel_smc_nc_ops, - .legacy_of_bindings = true, -}; - -static const struct of_device_id atmel_nand_controller_of_ids[] = { - { - .compatible = "atmel,at91rm9200-nand-controller", - .data = &atmel_rm9200_nc_caps, - }, - { - .compatible = "atmel,at91sam9260-nand-controller", - .data = &atmel_sam9260_nc_caps, - }, - { - .compatible = "atmel,at91sam9261-nand-controller", - .data = &atmel_sam9261_nc_caps, - }, - { - .compatible = "atmel,at91sam9g45-nand-controller", - .data = &atmel_sam9g45_nc_caps, - }, - { - .compatible = "atmel,sama5d3-nand-controller", - .data = &atmel_sama5_nc_caps, - }, - /* Support for old/deprecated bindings: */ - { - .compatible = "atmel,at91rm9200-nand", - .data = &atmel_rm9200_nand_caps, - }, - { - .compatible = "atmel,sama5d4-nand", - .data = &atmel_rm9200_nand_caps, - }, - { - .compatible = "atmel,sama5d2-nand", - .data = &atmel_rm9200_nand_caps, - }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids); - -static int atmel_nand_controller_probe(struct platform_device *pdev) -{ - const struct atmel_nand_controller_caps *caps; - - if (pdev->id_entry) - caps = (void *)pdev->id_entry->driver_data; - else - caps = of_device_get_match_data(&pdev->dev); - - if (!caps) { - dev_err(&pdev->dev, "Could not retrieve NFC caps\n"); - return -EINVAL; - } - - if (caps->legacy_of_bindings) { - u32 ale_offs = 21; - - /* - * If we are parsing legacy DT props and the DT contains a - * valid NFC node, forward the request to the sama5 logic. - */ - if (of_find_compatible_node(pdev->dev.of_node, NULL, - "atmel,sama5d3-nfc")) - caps = &atmel_sama5_nand_caps; - - /* - * Even if the compatible says we are dealing with an - * at91rm9200 controller, the atmel,nand-has-dma specify that - * this controller supports DMA, which means we are in fact - * dealing with an at91sam9g45+ controller. - */ - if (!caps->has_dma && - of_property_read_bool(pdev->dev.of_node, - "atmel,nand-has-dma")) - caps = &atmel_sam9g45_nand_caps; - - /* - * All SoCs except the at91sam9261 are assigning ALE to A21 and - * CLE to A22. If atmel,nand-addr-offset != 21 this means we're - * actually dealing with an at91sam9261 controller. - */ - of_property_read_u32(pdev->dev.of_node, - "atmel,nand-addr-offset", &ale_offs); - if (ale_offs != 21) - caps = &atmel_sam9261_nand_caps; - } - - return caps->ops->probe(pdev, caps); -} - -static int atmel_nand_controller_remove(struct platform_device *pdev) -{ - struct atmel_nand_controller *nc = platform_get_drvdata(pdev); - - return nc->caps->ops->remove(nc); -} - -static __maybe_unused int atmel_nand_controller_resume(struct device *dev) -{ - struct atmel_nand_controller *nc = dev_get_drvdata(dev); - struct atmel_nand *nand; - - if (nc->pmecc) - atmel_pmecc_reset(nc->pmecc); - - list_for_each_entry(nand, &nc->chips, node) { - int i; - - for (i = 0; i < nand->numcs; i++) - nand_reset(&nand->base, i); - } - - return 0; -} - -static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL, - atmel_nand_controller_resume); - -static struct platform_driver atmel_nand_controller_driver = { - .driver = { - .name = "atmel-nand-controller", - .of_match_table = of_match_ptr(atmel_nand_controller_of_ids), - .pm = &atmel_nand_controller_pm_ops, - }, - .probe = atmel_nand_controller_probe, - .remove = atmel_nand_controller_remove, -}; -module_platform_driver(atmel_nand_controller_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Boris Brezillon "); -MODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs"); -MODULE_ALIAS("platform:atmel-nand-controller"); diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c deleted file mode 100644 index 9de29c9afb0c..000000000000 --- a/drivers/mtd/nand/atmel/pmecc.c +++ /dev/null @@ -1,1012 +0,0 @@ -/* - * Copyright 2017 ATMEL - * Copyright 2017 Free Electrons - * - * Author: Boris Brezillon - * - * Derived from the atmel_nand.c driver which contained the following - * copyrights: - * - * Copyright 2003 Rick Bronson - * - * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) - * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) - * - * Derived from drivers/mtd/spia.c (removed in v3.8) - * Copyright 2000 Steven J. Hill (sjhill@cotw.com) - * - * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 - * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 - * - * Derived from Das U-Boot source code - * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) - * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas - * - * Add Programmable Multibit ECC support for various AT91 SoC - * Copyright 2012 ATMEL, Hong Xu - * - * Add Nand Flash Controller support for SAMA5 SoC - * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * The PMECC is an hardware assisted BCH engine, which means part of the - * ECC algorithm is left to the software. The hardware/software repartition - * is explained in the "PMECC Controller Functional Description" chapter in - * Atmel datasheets, and some of the functions in this file are directly - * implementing the algorithms described in the "Software Implementation" - * sub-section. - * - * TODO: it seems that the software BCH implementation in lib/bch.c is already - * providing some of the logic we are implementing here. It would be smart - * to expose the needed lib/bch.c helpers/functions and re-use them here. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pmecc.h" - -/* Galois field dimension */ -#define PMECC_GF_DIMENSION_13 13 -#define PMECC_GF_DIMENSION_14 14 - -/* Primitive Polynomial used by PMECC */ -#define PMECC_GF_13_PRIMITIVE_POLY 0x201b -#define PMECC_GF_14_PRIMITIVE_POLY 0x4443 - -#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000 -#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000 - -/* Time out value for reading PMECC status register */ -#define PMECC_MAX_TIMEOUT_MS 100 - -/* PMECC Register Definitions */ -#define ATMEL_PMECC_CFG 0x0 -#define PMECC_CFG_BCH_STRENGTH(x) (x) -#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0) -#define PMECC_CFG_SECTOR512 (0 << 4) -#define PMECC_CFG_SECTOR1024 (1 << 4) -#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8) -#define PMECC_CFG_READ_OP (0 << 12) -#define PMECC_CFG_WRITE_OP (1 << 12) -#define PMECC_CFG_SPARE_ENABLE BIT(16) -#define PMECC_CFG_AUTO_ENABLE BIT(20) - -#define ATMEL_PMECC_SAREA 0x4 -#define ATMEL_PMECC_SADDR 0x8 -#define ATMEL_PMECC_EADDR 0xc - -#define ATMEL_PMECC_CLK 0x10 -#define PMECC_CLK_133MHZ (2 << 0) - -#define ATMEL_PMECC_CTRL 0x14 -#define PMECC_CTRL_RST BIT(0) -#define PMECC_CTRL_DATA BIT(1) -#define PMECC_CTRL_USER BIT(2) -#define PMECC_CTRL_ENABLE BIT(4) -#define PMECC_CTRL_DISABLE BIT(5) - -#define ATMEL_PMECC_SR 0x18 -#define PMECC_SR_BUSY BIT(0) -#define PMECC_SR_ENABLE BIT(4) - -#define ATMEL_PMECC_IER 0x1c -#define ATMEL_PMECC_IDR 0x20 -#define ATMEL_PMECC_IMR 0x24 -#define ATMEL_PMECC_ISR 0x28 -#define PMECC_ERROR_INT BIT(0) - -#define ATMEL_PMECC_ECC(sector, n) \ - ((((sector) + 1) * 0x40) + (n)) - -#define ATMEL_PMECC_REM(sector, n) \ - ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200) - -/* PMERRLOC Register Definitions */ -#define ATMEL_PMERRLOC_ELCFG 0x0 -#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0) -#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0) -#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16) - -#define ATMEL_PMERRLOC_ELPRIM 0x4 -#define ATMEL_PMERRLOC_ELEN 0x8 -#define ATMEL_PMERRLOC_ELDIS 0xc -#define PMERRLOC_DISABLE BIT(0) - -#define ATMEL_PMERRLOC_ELSR 0x10 -#define PMERRLOC_ELSR_BUSY BIT(0) - -#define ATMEL_PMERRLOC_ELIER 0x14 -#define ATMEL_PMERRLOC_ELIDR 0x18 -#define ATMEL_PMERRLOC_ELIMR 0x1c -#define ATMEL_PMERRLOC_ELISR 0x20 -#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8) -#define PMERRLOC_CALC_DONE BIT(0) - -#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28) - -#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs)) - -struct atmel_pmecc_gf_tables { - u16 *alpha_to; - u16 *index_of; -}; - -struct atmel_pmecc_caps { - const int *strengths; - int nstrengths; - int el_offset; - bool correct_erased_chunks; -}; - -struct atmel_pmecc { - struct device *dev; - const struct atmel_pmecc_caps *caps; - - struct { - void __iomem *base; - void __iomem *errloc; - } regs; - - struct mutex lock; -}; - -struct atmel_pmecc_user_conf_cache { - u32 cfg; - u32 sarea; - u32 saddr; - u32 eaddr; -}; - -struct atmel_pmecc_user { - struct atmel_pmecc_user_conf_cache cache; - struct atmel_pmecc *pmecc; - const struct atmel_pmecc_gf_tables *gf_tables; - int eccbytes; - s16 *partial_syn; - s16 *si; - s16 *lmu; - s16 *smu; - s32 *mu; - s32 *dmu; - s32 *delta; - u32 isr; -}; - -static DEFINE_MUTEX(pmecc_gf_tables_lock); -static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512; -static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024; - -static inline int deg(unsigned int poly) -{ - /* polynomial degree is the most-significant bit index */ - return fls(poly) - 1; -} - -static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly, - struct atmel_pmecc_gf_tables *gf_tables) -{ - unsigned int i, x = 1; - const unsigned int k = BIT(deg(poly)); - unsigned int nn = BIT(mm) - 1; - - /* primitive polynomial must be of degree m */ - if (k != (1u << mm)) - return -EINVAL; - - for (i = 0; i < nn; i++) { - gf_tables->alpha_to[i] = x; - gf_tables->index_of[x] = i; - if (i && (x == 1)) - /* polynomial is not primitive (a^i=1 with 0alpha_to[nn] = 1; - gf_tables->index_of[0] = 0; - - return 0; -} - -static const struct atmel_pmecc_gf_tables * -atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req) -{ - struct atmel_pmecc_gf_tables *gf_tables; - unsigned int poly, degree, table_size; - int ret; - - if (req->ecc.sectorsize == 512) { - degree = PMECC_GF_DIMENSION_13; - poly = PMECC_GF_13_PRIMITIVE_POLY; - table_size = PMECC_LOOKUP_TABLE_SIZE_512; - } else { - degree = PMECC_GF_DIMENSION_14; - poly = PMECC_GF_14_PRIMITIVE_POLY; - table_size = PMECC_LOOKUP_TABLE_SIZE_1024; - } - - gf_tables = kzalloc(sizeof(*gf_tables) + - (2 * table_size * sizeof(u16)), - GFP_KERNEL); - if (!gf_tables) - return ERR_PTR(-ENOMEM); - - gf_tables->alpha_to = (void *)(gf_tables + 1); - gf_tables->index_of = gf_tables->alpha_to + table_size; - - ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables); - if (ret) { - kfree(gf_tables); - return ERR_PTR(ret); - } - - return gf_tables; -} - -static const struct atmel_pmecc_gf_tables * -atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req) -{ - const struct atmel_pmecc_gf_tables **gf_tables, *ret; - - mutex_lock(&pmecc_gf_tables_lock); - if (req->ecc.sectorsize == 512) - gf_tables = &pmecc_gf_tables_512; - else - gf_tables = &pmecc_gf_tables_1024; - - ret = *gf_tables; - - if (!ret) { - ret = atmel_pmecc_create_gf_tables(req); - if (!IS_ERR(ret)) - *gf_tables = ret; - } - mutex_unlock(&pmecc_gf_tables_lock); - - return ret; -} - -static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc, - struct atmel_pmecc_user_req *req) -{ - int i, max_eccbytes, eccbytes = 0, eccstrength = 0; - - if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0) - return -EINVAL; - - if (req->ecc.ooboffset >= 0 && - req->ecc.ooboffset + req->ecc.bytes > req->oobsize) - return -EINVAL; - - if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) { - if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) - return -EINVAL; - - if (req->pagesize > 512) - req->ecc.sectorsize = 1024; - else - req->ecc.sectorsize = 512; - } - - if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024) - return -EINVAL; - - if (req->pagesize % req->ecc.sectorsize) - return -EINVAL; - - req->ecc.nsectors = req->pagesize / req->ecc.sectorsize; - - max_eccbytes = req->ecc.bytes; - - for (i = 0; i < pmecc->caps->nstrengths; i++) { - int nbytes, strength = pmecc->caps->strengths[i]; - - if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH && - strength < req->ecc.strength) - continue; - - nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize), - 8); - nbytes *= req->ecc.nsectors; - - if (nbytes > max_eccbytes) - break; - - eccstrength = strength; - eccbytes = nbytes; - - if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) - break; - } - - if (!eccstrength) - return -EINVAL; - - req->ecc.bytes = eccbytes; - req->ecc.strength = eccstrength; - - if (req->ecc.ooboffset < 0) - req->ecc.ooboffset = req->oobsize - eccbytes; - - return 0; -} - -struct atmel_pmecc_user * -atmel_pmecc_create_user(struct atmel_pmecc *pmecc, - struct atmel_pmecc_user_req *req) -{ - struct atmel_pmecc_user *user; - const struct atmel_pmecc_gf_tables *gf_tables; - int strength, size, ret; - - ret = atmel_pmecc_prepare_user_req(pmecc, req); - if (ret) - return ERR_PTR(ret); - - size = sizeof(*user); - size = ALIGN(size, sizeof(u16)); - /* Reserve space for partial_syn, si and smu */ - size += ((2 * req->ecc.strength) + 1) * sizeof(u16) * - (2 + req->ecc.strength + 2); - /* Reserve space for lmu. */ - size += (req->ecc.strength + 1) * sizeof(u16); - /* Reserve space for mu, dmu and delta. */ - size = ALIGN(size, sizeof(s32)); - size += (req->ecc.strength + 1) * sizeof(s32) * 3; - - user = kzalloc(size, GFP_KERNEL); - if (!user) - return ERR_PTR(-ENOMEM); - - user->pmecc = pmecc; - - user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16)); - user->si = user->partial_syn + ((2 * req->ecc.strength) + 1); - user->lmu = user->si + ((2 * req->ecc.strength) + 1); - user->smu = user->lmu + (req->ecc.strength + 1); - user->mu = (s32 *)PTR_ALIGN(user->smu + - (((2 * req->ecc.strength) + 1) * - (req->ecc.strength + 2)), - sizeof(s32)); - user->dmu = user->mu + req->ecc.strength + 1; - user->delta = user->dmu + req->ecc.strength + 1; - - gf_tables = atmel_pmecc_get_gf_tables(req); - if (IS_ERR(gf_tables)) { - kfree(user); - return ERR_CAST(gf_tables); - } - - user->gf_tables = gf_tables; - - user->eccbytes = req->ecc.bytes / req->ecc.nsectors; - - for (strength = 0; strength < pmecc->caps->nstrengths; strength++) { - if (pmecc->caps->strengths[strength] == req->ecc.strength) - break; - } - - user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) | - PMECC_CFG_NSECTORS(req->ecc.nsectors); - - if (req->ecc.sectorsize == 1024) - user->cache.cfg |= PMECC_CFG_SECTOR1024; - - user->cache.sarea = req->oobsize - 1; - user->cache.saddr = req->ecc.ooboffset; - user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1; - - return user; -} -EXPORT_SYMBOL_GPL(atmel_pmecc_create_user); - -void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user) -{ - kfree(user); -} -EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user); - -static int get_strength(struct atmel_pmecc_user *user) -{ - const int *strengths = user->pmecc->caps->strengths; - - return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK]; -} - -static int get_sectorsize(struct atmel_pmecc_user *user) -{ - return user->cache.cfg & PMECC_LOOKUP_TABLE_SIZE_1024 ? 1024 : 512; -} - -static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector) -{ - int strength = get_strength(user); - u32 value; - int i; - - /* Fill odd syndromes */ - for (i = 0; i < strength; i++) { - value = readl_relaxed(user->pmecc->regs.base + - ATMEL_PMECC_REM(sector, i / 2)); - if (i & 1) - value >>= 16; - - user->partial_syn[(2 * i) + 1] = value; - } -} - -static void atmel_pmecc_substitute(struct atmel_pmecc_user *user) -{ - int degree = get_sectorsize(user) == 512 ? 13 : 14; - int cw_len = BIT(degree) - 1; - int strength = get_strength(user); - s16 *alpha_to = user->gf_tables->alpha_to; - s16 *index_of = user->gf_tables->index_of; - s16 *partial_syn = user->partial_syn; - s16 *si; - int i, j; - - /* - * si[] is a table that holds the current syndrome value, - * an element of that table belongs to the field - */ - si = user->si; - - memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1)); - - /* Computation 2t syndromes based on S(x) */ - /* Odd syndromes */ - for (i = 1; i < 2 * strength; i += 2) { - for (j = 0; j < degree; j++) { - if (partial_syn[i] & BIT(j)) - si[i] = alpha_to[i * j] ^ si[i]; - } - } - /* Even syndrome = (Odd syndrome) ** 2 */ - for (i = 2, j = 1; j <= strength; i = ++j << 1) { - if (si[j] == 0) { - si[i] = 0; - } else { - s16 tmp; - - tmp = index_of[si[j]]; - tmp = (tmp * 2) % cw_len; - si[i] = alpha_to[tmp]; - } - } -} - -static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user) -{ - s16 *lmu = user->lmu; - s16 *si = user->si; - s32 *mu = user->mu; - s32 *dmu = user->dmu; - s32 *delta = user->delta; - int degree = get_sectorsize(user) == 512 ? 13 : 14; - int cw_len = BIT(degree) - 1; - int strength = get_strength(user); - int num = 2 * strength + 1; - s16 *index_of = user->gf_tables->index_of; - s16 *alpha_to = user->gf_tables->alpha_to; - int i, j, k; - u32 dmu_0_count, tmp; - s16 *smu = user->smu; - - /* index of largest delta */ - int ro; - int largest; - int diff; - - dmu_0_count = 0; - - /* First Row */ - - /* Mu */ - mu[0] = -1; - - memset(smu, 0, sizeof(s16) * num); - smu[0] = 1; - - /* discrepancy set to 1 */ - dmu[0] = 1; - /* polynom order set to 0 */ - lmu[0] = 0; - delta[0] = (mu[0] * 2 - lmu[0]) >> 1; - - /* Second Row */ - - /* Mu */ - mu[1] = 0; - /* Sigma(x) set to 1 */ - memset(&smu[num], 0, sizeof(s16) * num); - smu[num] = 1; - - /* discrepancy set to S1 */ - dmu[1] = si[1]; - - /* polynom order set to 0 */ - lmu[1] = 0; - - delta[1] = (mu[1] * 2 - lmu[1]) >> 1; - - /* Init the Sigma(x) last row */ - memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num); - - for (i = 1; i <= strength; i++) { - mu[i + 1] = i << 1; - /* Begin Computing Sigma (Mu+1) and L(mu) */ - /* check if discrepancy is set to 0 */ - if (dmu[i] == 0) { - dmu_0_count++; - - tmp = ((strength - (lmu[i] >> 1) - 1) / 2); - if ((strength - (lmu[i] >> 1) - 1) & 0x1) - tmp += 2; - else - tmp += 1; - - if (dmu_0_count == tmp) { - for (j = 0; j <= (lmu[i] >> 1) + 1; j++) - smu[(strength + 1) * num + j] = - smu[i * num + j]; - - lmu[strength + 1] = lmu[i]; - return; - } - - /* copy polynom */ - for (j = 0; j <= lmu[i] >> 1; j++) - smu[(i + 1) * num + j] = smu[i * num + j]; - - /* copy previous polynom order to the next */ - lmu[i + 1] = lmu[i]; - } else { - ro = 0; - largest = -1; - /* find largest delta with dmu != 0 */ - for (j = 0; j < i; j++) { - if ((dmu[j]) && (delta[j] > largest)) { - largest = delta[j]; - ro = j; - } - } - - /* compute difference */ - diff = (mu[i] - mu[ro]); - - /* Compute degree of the new smu polynomial */ - if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) - lmu[i + 1] = lmu[i]; - else - lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; - - /* Init smu[i+1] with 0 */ - for (k = 0; k < num; k++) - smu[(i + 1) * num + k] = 0; - - /* Compute smu[i+1] */ - for (k = 0; k <= lmu[ro] >> 1; k++) { - s16 a, b, c; - - if (!(smu[ro * num + k] && dmu[i])) - continue; - - a = index_of[dmu[i]]; - b = index_of[dmu[ro]]; - c = index_of[smu[ro * num + k]]; - tmp = a + (cw_len - b) + c; - a = alpha_to[tmp % cw_len]; - smu[(i + 1) * num + (k + diff)] = a; - } - - for (k = 0; k <= lmu[i] >> 1; k++) - smu[(i + 1) * num + k] ^= smu[i * num + k]; - } - - /* End Computing Sigma (Mu+1) and L(mu) */ - /* In either case compute delta */ - delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; - - /* Do not compute discrepancy for the last iteration */ - if (i >= strength) - continue; - - for (k = 0; k <= (lmu[i + 1] >> 1); k++) { - tmp = 2 * (i - 1); - if (k == 0) { - dmu[i + 1] = si[tmp + 3]; - } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { - s16 a, b, c; - - a = index_of[smu[(i + 1) * num + k]]; - b = si[2 * (i - 1) + 3 - k]; - c = index_of[b]; - tmp = a + c; - tmp %= cw_len; - dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1]; - } - } - } -} - -static int atmel_pmecc_err_location(struct atmel_pmecc_user *user) -{ - int sector_size = get_sectorsize(user); - int degree = sector_size == 512 ? 13 : 14; - struct atmel_pmecc *pmecc = user->pmecc; - int strength = get_strength(user); - int ret, roots_nbr, i, err_nbr = 0; - int num = (2 * strength) + 1; - s16 *smu = user->smu; - u32 val; - - writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS); - - for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) { - writel_relaxed(smu[(strength + 1) * num + i], - pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i)); - err_nbr++; - } - - val = (err_nbr - 1) << 16; - if (sector_size == 1024) - val |= 1; - - writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG); - writel((sector_size * 8) + (degree * strength), - pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN); - - ret = readl_relaxed_poll_timeout(pmecc->regs.errloc + - ATMEL_PMERRLOC_ELISR, - val, val & PMERRLOC_CALC_DONE, 0, - PMECC_MAX_TIMEOUT_MS * 1000); - if (ret) { - dev_err(pmecc->dev, - "PMECC: Timeout to calculate error location.\n"); - return ret; - } - - roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8; - /* Number of roots == degree of smu hence <= cap */ - if (roots_nbr == user->lmu[strength + 1] >> 1) - return err_nbr - 1; - - /* - * Number of roots does not match the degree of smu - * unable to correct error. - */ - return -EBADMSG; -} - -int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector, - void *data, void *ecc) -{ - struct atmel_pmecc *pmecc = user->pmecc; - int sectorsize = get_sectorsize(user); - int eccbytes = user->eccbytes; - int i, nerrors; - - if (!(user->isr & BIT(sector))) - return 0; - - atmel_pmecc_gen_syndrome(user, sector); - atmel_pmecc_substitute(user); - atmel_pmecc_get_sigma(user); - - nerrors = atmel_pmecc_err_location(user); - if (nerrors < 0) - return nerrors; - - for (i = 0; i < nerrors; i++) { - const char *area; - int byte, bit; - u32 errpos; - u8 *ptr; - - errpos = readl_relaxed(pmecc->regs.errloc + - ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i)); - errpos--; - - byte = errpos / 8; - bit = errpos % 8; - - if (byte < sectorsize) { - ptr = data + byte; - area = "data"; - } else if (byte < sectorsize + eccbytes) { - ptr = ecc + byte - sectorsize; - area = "ECC"; - } else { - dev_dbg(pmecc->dev, - "Invalid errpos value (%d, max is %d)\n", - errpos, (sectorsize + eccbytes) * 8); - return -EINVAL; - } - - dev_dbg(pmecc->dev, - "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n", - area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit))); - - *ptr ^= BIT(bit); - } - - return nerrors; -} -EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector); - -bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user) -{ - return user->pmecc->caps->correct_erased_chunks; -} -EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks); - -void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, - int sector, void *ecc) -{ - struct atmel_pmecc *pmecc = user->pmecc; - u8 *ptr = ecc; - int i; - - for (i = 0; i < user->eccbytes; i++) - ptr[i] = readb_relaxed(pmecc->regs.base + - ATMEL_PMECC_ECC(sector, i)); -} -EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes); - -void atmel_pmecc_reset(struct atmel_pmecc *pmecc) -{ - writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); - writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); -} -EXPORT_SYMBOL_GPL(atmel_pmecc_reset); - -int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op) -{ - struct atmel_pmecc *pmecc = user->pmecc; - u32 cfg; - - if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) { - dev_err(pmecc->dev, "Bad ECC operation!"); - return -EINVAL; - } - - mutex_lock(&user->pmecc->lock); - - cfg = user->cache.cfg; - if (op == NAND_ECC_WRITE) - cfg |= PMECC_CFG_WRITE_OP; - else - cfg |= PMECC_CFG_AUTO_ENABLE; - - writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG); - writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA); - writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR); - writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR); - - writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); - writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL); - - return 0; -} -EXPORT_SYMBOL_GPL(atmel_pmecc_enable); - -void atmel_pmecc_disable(struct atmel_pmecc_user *user) -{ - atmel_pmecc_reset(user->pmecc); - mutex_unlock(&user->pmecc->lock); -} -EXPORT_SYMBOL_GPL(atmel_pmecc_disable); - -int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user) -{ - struct atmel_pmecc *pmecc = user->pmecc; - u32 status; - int ret; - - ret = readl_relaxed_poll_timeout(pmecc->regs.base + - ATMEL_PMECC_SR, - status, !(status & PMECC_SR_BUSY), 0, - PMECC_MAX_TIMEOUT_MS * 1000); - if (ret) { - dev_err(pmecc->dev, - "Timeout while waiting for PMECC ready.\n"); - return ret; - } - - user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR); - - return 0; -} -EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy); - -static struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev, - const struct atmel_pmecc_caps *caps, - int pmecc_res_idx, int errloc_res_idx) -{ - struct device *dev = &pdev->dev; - struct atmel_pmecc *pmecc; - struct resource *res; - - pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL); - if (!pmecc) - return ERR_PTR(-ENOMEM); - - pmecc->caps = caps; - pmecc->dev = dev; - mutex_init(&pmecc->lock); - - res = platform_get_resource(pdev, IORESOURCE_MEM, pmecc_res_idx); - pmecc->regs.base = devm_ioremap_resource(dev, res); - if (IS_ERR(pmecc->regs.base)) - return ERR_CAST(pmecc->regs.base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, errloc_res_idx); - pmecc->regs.errloc = devm_ioremap_resource(dev, res); - if (IS_ERR(pmecc->regs.errloc)) - return ERR_CAST(pmecc->regs.errloc); - - /* Disable all interrupts before registering the PMECC handler. */ - writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR); - atmel_pmecc_reset(pmecc); - - return pmecc; -} - -static void devm_atmel_pmecc_put(struct device *dev, void *res) -{ - struct atmel_pmecc **pmecc = res; - - put_device((*pmecc)->dev); -} - -static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev, - struct device_node *np) -{ - struct platform_device *pdev; - struct atmel_pmecc *pmecc, **ptr; - - pdev = of_find_device_by_node(np); - if (!pdev || !platform_get_drvdata(pdev)) - return ERR_PTR(-EPROBE_DEFER); - - ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - get_device(&pdev->dev); - pmecc = platform_get_drvdata(pdev); - - *ptr = pmecc; - - devres_add(userdev, ptr); - - return pmecc; -} - -static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 }; - -static struct atmel_pmecc_caps at91sam9g45_caps = { - .strengths = atmel_pmecc_strengths, - .nstrengths = 5, - .el_offset = 0x8c, -}; - -static struct atmel_pmecc_caps sama5d4_caps = { - .strengths = atmel_pmecc_strengths, - .nstrengths = 5, - .el_offset = 0x8c, - .correct_erased_chunks = true, -}; - -static struct atmel_pmecc_caps sama5d2_caps = { - .strengths = atmel_pmecc_strengths, - .nstrengths = 6, - .el_offset = 0xac, - .correct_erased_chunks = true, -}; - -static const struct of_device_id atmel_pmecc_legacy_match[] = { - { .compatible = "atmel,sama5d4-nand", &sama5d4_caps }, - { .compatible = "atmel,sama5d2-nand", &sama5d2_caps }, - { /* sentinel */ } -}; - -struct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev) -{ - struct atmel_pmecc *pmecc; - struct device_node *np; - - if (!userdev) - return ERR_PTR(-EINVAL); - - if (!userdev->of_node) - return NULL; - - np = of_parse_phandle(userdev->of_node, "ecc-engine", 0); - if (np) { - pmecc = atmel_pmecc_get_by_node(userdev, np); - of_node_put(np); - } else { - /* - * Support old DT bindings: in this case the PMECC iomem - * resources are directly defined in the user pdev at position - * 1 and 2. Extract all relevant information from there. - */ - struct platform_device *pdev = to_platform_device(userdev); - const struct atmel_pmecc_caps *caps; - const struct of_device_id *match; - - /* No PMECC engine available. */ - if (!of_property_read_bool(userdev->of_node, - "atmel,has-pmecc")) - return NULL; - - caps = &at91sam9g45_caps; - - /* Find the caps associated to the NAND dev node. */ - match = of_match_node(atmel_pmecc_legacy_match, - userdev->of_node); - if (match && match->data) - caps = match->data; - - pmecc = atmel_pmecc_create(pdev, caps, 1, 2); - } - - return pmecc; -} -EXPORT_SYMBOL(devm_atmel_pmecc_get); - -static const struct of_device_id atmel_pmecc_match[] = { - { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps }, - { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps }, - { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, atmel_pmecc_match); - -static int atmel_pmecc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - const struct atmel_pmecc_caps *caps; - struct atmel_pmecc *pmecc; - - caps = of_device_get_match_data(&pdev->dev); - if (!caps) { - dev_err(dev, "Invalid caps\n"); - return -EINVAL; - } - - pmecc = atmel_pmecc_create(pdev, caps, 0, 1); - if (IS_ERR(pmecc)) - return PTR_ERR(pmecc); - - platform_set_drvdata(pdev, pmecc); - - return 0; -} - -static struct platform_driver atmel_pmecc_driver = { - .driver = { - .name = "atmel-pmecc", - .of_match_table = of_match_ptr(atmel_pmecc_match), - }, - .probe = atmel_pmecc_probe, -}; -module_platform_driver(atmel_pmecc_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Boris Brezillon "); -MODULE_DESCRIPTION("PMECC engine driver"); -MODULE_ALIAS("platform:atmel_pmecc"); diff --git a/drivers/mtd/nand/atmel/pmecc.h b/drivers/mtd/nand/atmel/pmecc.h deleted file mode 100644 index 808f1be0d6ad..000000000000 --- a/drivers/mtd/nand/atmel/pmecc.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * © Copyright 2016 ATMEL - * © Copyright 2016 Free Electrons - * - * Author: Boris Brezillon - * - * Derived from the atmel_nand.c driver which contained the following - * copyrights: - * - * Copyright © 2003 Rick Bronson - * - * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) - * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de) - * - * Derived from drivers/mtd/spia.c (removed in v3.8) - * Copyright © 2000 Steven J. Hill (sjhill@cotw.com) - * - * - * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 - * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007 - * - * Derived from Das U-Boot source code - * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) - * © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas - * - * Add Programmable Multibit ECC support for various AT91 SoC - * © Copyright 2012 ATMEL, Hong Xu - * - * Add Nand Flash Controller support for SAMA5 SoC - * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#ifndef ATMEL_PMECC_H -#define ATMEL_PMECC_H - -#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0 -#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0 -#define ATMEL_PMECC_OOBOFFSET_AUTO -1 - -struct atmel_pmecc_user_req { - int pagesize; - int oobsize; - struct { - int strength; - int bytes; - int sectorsize; - int nsectors; - int ooboffset; - } ecc; -}; - -struct atmel_pmecc *devm_atmel_pmecc_get(struct device *dev); - -struct atmel_pmecc_user * -atmel_pmecc_create_user(struct atmel_pmecc *pmecc, - struct atmel_pmecc_user_req *req); -void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user); - -void atmel_pmecc_reset(struct atmel_pmecc *pmecc); -int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op); -void atmel_pmecc_disable(struct atmel_pmecc_user *user); -int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user); -int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector, - void *data, void *ecc); -bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user); -void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, - int sector, void *ecc); - -#endif /* ATMEL_PMECC_H */ diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c deleted file mode 100644 index df0ef1f1e2f5..000000000000 --- a/drivers/mtd/nand/au1550nd.c +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright (C) 2004 Embedded Edge, LLC - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -struct au1550nd_ctx { - struct nand_chip chip; - - int cs; - void __iomem *base; - void (*write_byte)(struct mtd_info *, u_char); -}; - -/** - * au_read_byte - read one byte from the chip - * @mtd: MTD device structure - * - * read function for 8bit buswidth - */ -static u_char au_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u_char ret = readb(this->IO_ADDR_R); - wmb(); /* drain writebuffer */ - return ret; -} - -/** - * au_write_byte - write one byte to the chip - * @mtd: MTD device structure - * @byte: pointer to data byte to write - * - * write function for 8it buswidth - */ -static void au_write_byte(struct mtd_info *mtd, u_char byte) -{ - struct nand_chip *this = mtd_to_nand(mtd); - writeb(byte, this->IO_ADDR_W); - wmb(); /* drain writebuffer */ -} - -/** - * au_read_byte16 - read one byte endianness aware from the chip - * @mtd: MTD device structure - * - * read function for 16bit buswidth with endianness conversion - */ -static u_char au_read_byte16(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); - wmb(); /* drain writebuffer */ - return ret; -} - -/** - * au_write_byte16 - write one byte endianness aware to the chip - * @mtd: MTD device structure - * @byte: pointer to data byte to write - * - * write function for 16bit buswidth with endianness conversion - */ -static void au_write_byte16(struct mtd_info *mtd, u_char byte) -{ - struct nand_chip *this = mtd_to_nand(mtd); - writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); - wmb(); /* drain writebuffer */ -} - -/** - * au_read_word - read one word from the chip - * @mtd: MTD device structure - * - * read function for 16bit buswidth without endianness conversion - */ -static u16 au_read_word(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u16 ret = readw(this->IO_ADDR_R); - wmb(); /* drain writebuffer */ - return ret; -} - -/** - * au_write_buf - write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - * - * write function for 8bit buswidth - */ -static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd_to_nand(mtd); - - for (i = 0; i < len; i++) { - writeb(buf[i], this->IO_ADDR_W); - wmb(); /* drain writebuffer */ - } -} - -/** - * au_read_buf - read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - * - * read function for 8bit buswidth - */ -static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd_to_nand(mtd); - - for (i = 0; i < len; i++) { - buf[i] = readb(this->IO_ADDR_R); - wmb(); /* drain writebuffer */ - } -} - -/** - * au_write_buf16 - write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - * - * write function for 16bit buswidth - */ -static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd_to_nand(mtd); - u16 *p = (u16 *) buf; - len >>= 1; - - for (i = 0; i < len; i++) { - writew(p[i], this->IO_ADDR_W); - wmb(); /* drain writebuffer */ - } - -} - -/** - * au_read_buf16 - read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - * - * read function for 16bit buswidth - */ -static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd_to_nand(mtd); - u16 *p = (u16 *) buf; - len >>= 1; - - for (i = 0; i < len; i++) { - p[i] = readw(this->IO_ADDR_R); - wmb(); /* drain writebuffer */ - } -} - -/* Select the chip by setting nCE to low */ -#define NAND_CTL_SETNCE 1 -/* Deselect the chip by setting nCE to high */ -#define NAND_CTL_CLRNCE 2 -/* Select the command latch by setting CLE to high */ -#define NAND_CTL_SETCLE 3 -/* Deselect the command latch by setting CLE to low */ -#define NAND_CTL_CLRCLE 4 -/* Select the address latch by setting ALE to high */ -#define NAND_CTL_SETALE 5 -/* Deselect the address latch by setting ALE to low */ -#define NAND_CTL_CLRALE 6 - -static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx, - chip); - - switch (cmd) { - - case NAND_CTL_SETCLE: - this->IO_ADDR_W = ctx->base + MEM_STNAND_CMD; - break; - - case NAND_CTL_CLRCLE: - this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA; - break; - - case NAND_CTL_SETALE: - this->IO_ADDR_W = ctx->base + MEM_STNAND_ADDR; - break; - - case NAND_CTL_CLRALE: - this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA; - /* FIXME: Nobody knows why this is necessary, - * but it works only that way */ - udelay(1); - break; - - case NAND_CTL_SETNCE: - /* assert (force assert) chip enable */ - alchemy_wrsmem((1 << (4 + ctx->cs)), AU1000_MEM_STNDCTL); - break; - - case NAND_CTL_CLRNCE: - /* deassert chip enable */ - alchemy_wrsmem(0, AU1000_MEM_STNDCTL); - break; - } - - this->IO_ADDR_R = this->IO_ADDR_W; - - wmb(); /* Drain the writebuffer */ -} - -int au1550_device_ready(struct mtd_info *mtd) -{ - return (alchemy_rdsmem(AU1000_MEM_STSTAT) & 0x1) ? 1 : 0; -} - -/** - * au1550_select_chip - control -CE line - * Forbid driving -CE manually permitting the NAND controller to do this. - * Keeping -CE asserted during the whole sector reads interferes with the - * NOR flash and PCMCIA drivers as it causes contention on the static bus. - * We only have to hold -CE low for the NAND read commands since the flash - * chip needs it to be asserted during chip not ready time but the NAND - * controller keeps it released. - * - * @mtd: MTD device structure - * @chip: chipnumber to select, -1 for deselect - */ -static void au1550_select_chip(struct mtd_info *mtd, int chip) -{ -} - -/** - * au1550_command - Send command to NAND device - * @mtd: MTD device structure - * @command: the command to be sent - * @column: the column address for this command, -1 if none - * @page_addr: the page address for this command, -1 if none - */ -static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx, - chip); - int ce_override = 0, i; - unsigned long flags = 0; - - /* Begin command latch cycle */ - au1550_hwcontrol(mtd, NAND_CTL_SETCLE); - /* - * Write out the command to the device. - */ - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->writesize) { - /* OOB area */ - column -= mtd->writesize; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - ctx->write_byte(mtd, readcmd); - } - ctx->write_byte(mtd, command); - - /* Set ALE and clear CLE to start address cycle */ - au1550_hwcontrol(mtd, NAND_CTL_CLRCLE); - - if (column != -1 || page_addr != -1) { - au1550_hwcontrol(mtd, NAND_CTL_SETALE); - - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - ctx->write_byte(mtd, column); - } - if (page_addr != -1) { - ctx->write_byte(mtd, (u8)(page_addr & 0xff)); - - if (command == NAND_CMD_READ0 || - command == NAND_CMD_READ1 || - command == NAND_CMD_READOOB) { - /* - * NAND controller will release -CE after - * the last address byte is written, so we'll - * have to forcibly assert it. No interrupts - * are allowed while we do this as we don't - * want the NOR flash or PCMCIA drivers to - * steal our precious bytes of data... - */ - ce_override = 1; - local_irq_save(flags); - au1550_hwcontrol(mtd, NAND_CTL_SETNCE); - } - - ctx->write_byte(mtd, (u8)(page_addr >> 8)); - - if (this->options & NAND_ROW_ADDR_3) - ctx->write_byte(mtd, - ((page_addr >> 16) & 0x0f)); - } - /* Latch in address */ - au1550_hwcontrol(mtd, NAND_CTL_CLRALE); - } - - /* - * Program and erase have their own busy handlers. - * Status and sequential in need no delay. - */ - switch (command) { - - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_STATUS: - return; - - case NAND_CMD_RESET: - break; - - case NAND_CMD_READ0: - case NAND_CMD_READ1: - case NAND_CMD_READOOB: - /* Check if we're really driving -CE low (just in case) */ - if (unlikely(!ce_override)) - break; - - /* Apply a short delay always to ensure that we do wait tWB. */ - ndelay(100); - /* Wait for a chip to become ready... */ - for (i = this->chip_delay; !this->dev_ready(mtd) && i > 0; --i) - udelay(1); - - /* Release -CE and re-enable interrupts. */ - au1550_hwcontrol(mtd, NAND_CTL_CLRNCE); - local_irq_restore(flags); - return; - } - /* Apply this short delay always to ensure that we do wait tWB. */ - ndelay(100); - - while(!this->dev_ready(mtd)); -} - -static int find_nand_cs(unsigned long nand_base) -{ - void __iomem *base = - (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR); - unsigned long addr, staddr, start, mask, end; - int i; - - for (i = 0; i < 4; i++) { - addr = 0x1000 + (i * 0x10); /* CSx */ - staddr = __raw_readl(base + addr + 0x08); /* STADDRx */ - /* figure out the decoded range of this CS */ - start = (staddr << 4) & 0xfffc0000; - mask = (staddr << 18) & 0xfffc0000; - end = (start | (start - 1)) & ~(start ^ mask); - if ((nand_base >= start) && (nand_base < end)) - return i; - } - - return -ENODEV; -} - -static int au1550nd_probe(struct platform_device *pdev) -{ - struct au1550nd_platdata *pd; - struct au1550nd_ctx *ctx; - struct nand_chip *this; - struct mtd_info *mtd; - struct resource *r; - int ret, cs; - - pd = dev_get_platdata(&pdev->dev); - if (!pd) { - dev_err(&pdev->dev, "missing platform data\n"); - return -ENODEV; - } - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - dev_err(&pdev->dev, "no NAND memory resource\n"); - ret = -ENODEV; - goto out1; - } - if (request_mem_region(r->start, resource_size(r), "au1550-nand")) { - dev_err(&pdev->dev, "cannot claim NAND memory area\n"); - ret = -ENOMEM; - goto out1; - } - - ctx->base = ioremap_nocache(r->start, 0x1000); - if (!ctx->base) { - dev_err(&pdev->dev, "cannot remap NAND memory area\n"); - ret = -ENODEV; - goto out2; - } - - this = &ctx->chip; - mtd = nand_to_mtd(this); - mtd->dev.parent = &pdev->dev; - - /* figure out which CS# r->start belongs to */ - cs = find_nand_cs(r->start); - if (cs < 0) { - dev_err(&pdev->dev, "cannot detect NAND chipselect\n"); - ret = -ENODEV; - goto out3; - } - ctx->cs = cs; - - this->dev_ready = au1550_device_ready; - this->select_chip = au1550_select_chip; - this->cmdfunc = au1550_command; - - /* 30 us command delay time */ - this->chip_delay = 30; - this->ecc.mode = NAND_ECC_SOFT; - this->ecc.algo = NAND_ECC_HAMMING; - - if (pd->devwidth) - this->options |= NAND_BUSWIDTH_16; - - this->read_byte = (pd->devwidth) ? au_read_byte16 : au_read_byte; - ctx->write_byte = (pd->devwidth) ? au_write_byte16 : au_write_byte; - this->read_word = au_read_word; - this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf; - this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf; - - ret = nand_scan(mtd, 1); - if (ret) { - dev_err(&pdev->dev, "NAND scan failed with %d\n", ret); - goto out3; - } - - mtd_device_register(mtd, pd->parts, pd->num_parts); - - platform_set_drvdata(pdev, ctx); - - return 0; - -out3: - iounmap(ctx->base); -out2: - release_mem_region(r->start, resource_size(r)); -out1: - kfree(ctx); - return ret; -} - -static int au1550nd_remove(struct platform_device *pdev) -{ - struct au1550nd_ctx *ctx = platform_get_drvdata(pdev); - struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - nand_release(nand_to_mtd(&ctx->chip)); - iounmap(ctx->base); - release_mem_region(r->start, 0x1000); - kfree(ctx); - return 0; -} - -static struct platform_driver au1550nd_driver = { - .driver = { - .name = "au1550-nand", - }, - .probe = au1550nd_probe, - .remove = au1550nd_remove, -}; - -module_platform_driver(au1550nd_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Embedded Edge, LLC"); -MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board"); diff --git a/drivers/mtd/nand/bcm47xxnflash/Makefile b/drivers/mtd/nand/bcm47xxnflash/Makefile deleted file mode 100644 index f05b119e134b..000000000000 --- a/drivers/mtd/nand/bcm47xxnflash/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -bcm47xxnflash-y += main.o -bcm47xxnflash-y += ops_bcm4706.o - -obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash.o diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h deleted file mode 100644 index 201b9baa52a0..000000000000 --- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __BCM47XXNFLASH_H -#define __BCM47XXNFLASH_H - -#ifndef pr_fmt -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#endif - -#include -#include - -struct bcm47xxnflash { - struct bcma_drv_cc *cc; - - struct nand_chip nand_chip; - - unsigned curr_command; - int curr_page_addr; - int curr_column; - - u8 id_data[8]; -}; - -int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n); - -#endif /* BCM47XXNFLASH */ diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/bcm47xxnflash/main.c deleted file mode 100644 index fb31429b70a9..000000000000 --- a/drivers/mtd/nand/bcm47xxnflash/main.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * BCM47XX NAND flash driver - * - * Copyright (C) 2012 Rafał Miłecki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include "bcm47xxnflash.h" - -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("NAND flash driver for BCMA bus"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Rafał Miłecki"); - -static const char *probes[] = { "bcm47xxpart", NULL }; - -static int bcm47xxnflash_probe(struct platform_device *pdev) -{ - struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev); - struct bcm47xxnflash *b47n; - struct mtd_info *mtd; - int err = 0; - - b47n = devm_kzalloc(&pdev->dev, sizeof(*b47n), GFP_KERNEL); - if (!b47n) - return -ENOMEM; - - nand_set_controller_data(&b47n->nand_chip, b47n); - mtd = nand_to_mtd(&b47n->nand_chip); - mtd->dev.parent = &pdev->dev; - b47n->cc = container_of(nflash, struct bcma_drv_cc, nflash); - - if (b47n->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { - err = bcm47xxnflash_ops_bcm4706_init(b47n); - } else { - pr_err("Device not supported\n"); - err = -ENOTSUPP; - } - if (err) { - pr_err("Initialization failed: %d\n", err); - return err; - } - - platform_set_drvdata(pdev, b47n); - - err = mtd_device_parse_register(mtd, probes, NULL, NULL, 0); - if (err) { - pr_err("Failed to register MTD device: %d\n", err); - return err; - } - - return 0; -} - -static int bcm47xxnflash_remove(struct platform_device *pdev) -{ - struct bcm47xxnflash *nflash = platform_get_drvdata(pdev); - - nand_release(nand_to_mtd(&nflash->nand_chip)); - - return 0; -} - -static struct platform_driver bcm47xxnflash_driver = { - .probe = bcm47xxnflash_probe, - .remove = bcm47xxnflash_remove, - .driver = { - .name = "bcma_nflash", - }, -}; - -module_platform_driver(bcm47xxnflash_driver); diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c deleted file mode 100644 index 54bac5b73f0a..000000000000 --- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * BCM47XX NAND flash driver - * - * Copyright (C) 2012 Rafał Miłecki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include "bcm47xxnflash.h" - -#include -#include -#include -#include -#include - -/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has - * shown ~1000 retries as maxiumum. */ -#define NFLASH_READY_RETRIES 10000 - -#define NFLASH_SECTOR_SIZE 512 - -#define NCTL_CMD0 0x00010000 -#define NCTL_COL 0x00020000 /* Update column with value from BCMA_CC_NFLASH_COL_ADDR */ -#define NCTL_ROW 0x00040000 /* Update row (page) with value from BCMA_CC_NFLASH_ROW_ADDR */ -#define NCTL_CMD1W 0x00080000 -#define NCTL_READ 0x00100000 -#define NCTL_WRITE 0x00200000 -#define NCTL_SPECADDR 0x01000000 -#define NCTL_READY 0x04000000 -#define NCTL_ERR 0x08000000 -#define NCTL_CSA 0x40000000 -#define NCTL_START 0x80000000 - -/************************************************** - * Various helpers - **************************************************/ - -static inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock) -{ - return ((ns * 1000 * clock) / 1000000) + 1; -} - -static int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code) -{ - int i = 0; - - bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, NCTL_START | code); - for (i = 0; i < NFLASH_READY_RETRIES; i++) { - if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_START)) { - i = 0; - break; - } - } - if (i) { - pr_err("NFLASH control command not ready!\n"); - return -EBUSY; - } - return 0; -} - -static int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc) -{ - int i; - - for (i = 0; i < NFLASH_READY_RETRIES; i++) { - if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_READY) { - if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & - BCMA_CC_NFLASH_CTL_ERR) { - pr_err("Error on polling\n"); - return -EBUSY; - } else { - return 0; - } - } - } - - pr_err("Polling timeout!\n"); - return -EBUSY; -} - -/************************************************** - * R/W - **************************************************/ - -static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf, - int len) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); - - u32 ctlcode; - u32 *dest = (u32 *)buf; - int i; - int toread; - - BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); - /* Don't validate column using nand_chip->page_shift, it may be bigger - * when accessing OOB */ - - while (len) { - /* We can read maximum of 0x200 bytes at once */ - toread = min(len, 0x200); - - /* Set page and column */ - bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR, - b47n->curr_column); - bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR, - b47n->curr_page_addr); - - /* Prepare to read */ - ctlcode = NCTL_CSA | NCTL_CMD1W | NCTL_ROW | NCTL_COL | - NCTL_CMD0; - ctlcode |= NAND_CMD_READSTART << 8; - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) - return; - if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc)) - return; - - /* Eventually read some data :) */ - for (i = 0; i < toread; i += 4, dest++) { - ctlcode = NCTL_CSA | 0x30000000 | NCTL_READ; - if (i == toread - 4) /* Last read goes without that */ - ctlcode &= ~NCTL_CSA; - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, - ctlcode)) - return; - *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA); - } - - b47n->curr_column += toread; - len -= toread; - } -} - -static void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); - struct bcma_drv_cc *cc = b47n->cc; - - u32 ctlcode; - const u32 *data = (u32 *)buf; - int i; - - BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); - /* Don't validate column using nand_chip->page_shift, it may be bigger - * when accessing OOB */ - - for (i = 0; i < len; i += 4, data++) { - bcma_cc_write32(cc, BCMA_CC_NFLASH_DATA, *data); - - ctlcode = NCTL_CSA | 0x30000000 | NCTL_WRITE; - if (i == len - 4) /* Last read goes without that */ - ctlcode &= ~NCTL_CSA; - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) { - pr_err("%s ctl_cmd didn't work!\n", __func__); - return; - } - } - - b47n->curr_column += len; -} - -/************************************************** - * NAND chip ops - **************************************************/ - -static void bcm47xxnflash_ops_bcm4706_cmd_ctrl(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); - u32 code = 0; - - if (cmd == NAND_CMD_NONE) - return; - - if (cmd & NAND_CTRL_CLE) - code = cmd | NCTL_CMD0; - - /* nCS is not needed for reset command */ - if (cmd != NAND_CMD_RESET) - code |= NCTL_CSA; - - bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, code); -} - -/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */ -static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd, - int chip) -{ - return; -} - -static int bcm47xxnflash_ops_bcm4706_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); - - return !!(bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_CTL) & NCTL_READY); -} - -/* - * Default nand_command and nand_command_lp don't match BCM4706 hardware layout. - * For example, reading chip id is performed in a non-standard way. - * Setting column and page is also handled differently, we use a special - * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert - * standard commands would be much more complicated. - */ -static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd, - unsigned command, int column, - int page_addr) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); - struct bcma_drv_cc *cc = b47n->cc; - u32 ctlcode; - int i; - - if (column != -1) - b47n->curr_column = column; - if (page_addr != -1) - b47n->curr_page_addr = page_addr; - - switch (command) { - case NAND_CMD_RESET: - nand_chip->cmd_ctrl(mtd, command, NAND_CTRL_CLE); - - ndelay(100); - nand_wait_ready(mtd); - break; - case NAND_CMD_READID: - ctlcode = NCTL_CSA | 0x01000000 | NCTL_CMD1W | NCTL_CMD0; - ctlcode |= NAND_CMD_READID; - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) { - pr_err("READID error\n"); - break; - } - - /* - * Reading is specific, last one has to go without NCTL_CSA - * bit. We don't know how many reads NAND subsystem is going - * to perform, so cache everything. - */ - for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) { - ctlcode = NCTL_CSA | NCTL_READ; - if (i == ARRAY_SIZE(b47n->id_data) - 1) - ctlcode &= ~NCTL_CSA; - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, - ctlcode)) { - pr_err("READID error\n"); - break; - } - b47n->id_data[i] = - bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA) - & 0xFF; - } - - break; - case NAND_CMD_STATUS: - ctlcode = NCTL_CSA | NCTL_CMD0 | NAND_CMD_STATUS; - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) - pr_err("STATUS command error\n"); - break; - case NAND_CMD_READ0: - break; - case NAND_CMD_READOOB: - if (page_addr != -1) - b47n->curr_column += mtd->writesize; - break; - case NAND_CMD_ERASE1: - bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, - b47n->curr_page_addr); - ctlcode = NCTL_ROW | NCTL_CMD1W | NCTL_CMD0 | - NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8); - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) - pr_err("ERASE1 failed\n"); - break; - case NAND_CMD_ERASE2: - break; - case NAND_CMD_SEQIN: - /* Set page and column */ - bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR, - b47n->curr_column); - bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, - b47n->curr_page_addr); - - /* Prepare to write */ - ctlcode = 0x40000000 | NCTL_ROW | NCTL_COL | NCTL_CMD0; - ctlcode |= NAND_CMD_SEQIN; - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) - pr_err("SEQIN failed\n"); - break; - case NAND_CMD_PAGEPROG: - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_CMD0 | - NAND_CMD_PAGEPROG)) - pr_err("PAGEPROG failed\n"); - if (bcm47xxnflash_ops_bcm4706_poll(cc)) - pr_err("PAGEPROG not ready\n"); - break; - default: - pr_err("Command 0x%X unsupported\n", command); - break; - } - b47n->curr_command = command; -} - -static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); - struct bcma_drv_cc *cc = b47n->cc; - u32 tmp = 0; - - switch (b47n->curr_command) { - case NAND_CMD_READID: - if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) { - pr_err("Requested invalid id_data: %d\n", - b47n->curr_column); - return 0; - } - return b47n->id_data[b47n->curr_column++]; - case NAND_CMD_STATUS: - if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_READ)) - return 0; - return bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xff; - case NAND_CMD_READOOB: - bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4); - return tmp & 0xFF; - } - - pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command); - return 0; -} - -static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd, - uint8_t *buf, int len) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); - - switch (b47n->curr_command) { - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - bcm47xxnflash_ops_bcm4706_read(mtd, buf, len); - return; - } - - pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command); -} - -static void bcm47xxnflash_ops_bcm4706_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); - - switch (b47n->curr_command) { - case NAND_CMD_SEQIN: - bcm47xxnflash_ops_bcm4706_write(mtd, buf, len); - return; - } - - pr_err("Invalid command for buf write: 0x%X\n", b47n->curr_command); -} - -/************************************************** - * Init - **************************************************/ - -int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) -{ - struct nand_chip *nand_chip = (struct nand_chip *)&b47n->nand_chip; - int err; - u32 freq; - u16 clock; - u8 w0, w1, w2, w3, w4; - - unsigned long chipsize; /* MiB */ - u8 tbits, col_bits, col_size, row_bits, row_bsize; - u32 val; - - b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; - nand_chip->cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl; - nand_chip->dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready; - b47n->nand_chip.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc; - b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte; - b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf; - b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf; - b47n->nand_chip.onfi_set_features = nand_onfi_get_set_features_notsupp; - b47n->nand_chip.onfi_get_features = nand_onfi_get_set_features_notsupp; - - nand_chip->chip_delay = 50; - b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; - b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */ - - /* Enable NAND flash access */ - bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG, - BCMA_CC_4706_FLASHSCFG_NF1); - - /* Configure wait counters */ - if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) { - /* 400 MHz */ - freq = 400000000 / 4; - } else { - freq = bcma_chipco_pll_read(b47n->cc, 4); - freq = (freq & 0xFFF) >> 3; - /* Fixed reference clock 25 MHz and m = 2 */ - freq = (freq * 25000000 / 2) / 4; - } - clock = freq / 1000000; - w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock); - w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock); - w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); - w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); - w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock); - bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0, - (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0)); - - /* Scan NAND */ - err = nand_scan(nand_to_mtd(&b47n->nand_chip), 1); - if (err) { - pr_err("Could not scan NAND flash: %d\n", err); - goto exit; - } - - /* Configure FLASH */ - chipsize = b47n->nand_chip.chipsize >> 20; - tbits = ffs(chipsize); /* find first bit set */ - if (!tbits || tbits != fls(chipsize)) { - pr_err("Invalid flash size: 0x%lX\n", chipsize); - err = -ENOTSUPP; - goto exit; - } - tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */ - - col_bits = b47n->nand_chip.page_shift + 1; - col_size = (col_bits + 7) / 8; - - row_bits = tbits - col_bits + 1; - row_bsize = (row_bits + 7) / 8; - - val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2; - bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val); - -exit: - if (err) - bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG, - ~BCMA_CC_4706_FLASHSCFG_NF1); - return err; -} diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c deleted file mode 100644 index 9a1d8d104570..000000000000 --- a/drivers/mtd/nand/bf5xx_nand.c +++ /dev/null @@ -1,861 +0,0 @@ -/* - * Copyright 2006-2008 Analog Devices Inc. - * http://blackfin.uclinux.org/ - * Bryan Wu - * - * Blackfin BF5xx on-chip NAND flash controller driver - * - * Derived from s3c2410.c - * Copyright (c) 2007 Ben Dooks - * - * Derived from cafe.c - * Copyright © 2006 Red Hat, Inc. - * Copyright © 2006 David Woodhouse - * - * Changelog: - * 12-Jun-2007 Bryan Wu: Initial version - * 18-Jul-2007 Bryan Wu: - * - ECC_HW and ECC_SW supported - * - DMA supported in ECC_HW - * - YAFFS tested as rootfs in both ECC_HW and ECC_SW - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define DRV_NAME "bf5xx-nand" -#define DRV_VERSION "1.2" -#define DRV_AUTHOR "Bryan Wu " -#define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver" - -/* NFC_STAT Masks */ -#define NBUSY 0x01 /* Not Busy */ -#define WB_FULL 0x02 /* Write Buffer Full */ -#define PG_WR_STAT 0x04 /* Page Write Pending */ -#define PG_RD_STAT 0x08 /* Page Read Pending */ -#define WB_EMPTY 0x10 /* Write Buffer Empty */ - -/* NFC_IRQSTAT Masks */ -#define NBUSYIRQ 0x01 /* Not Busy IRQ */ -#define WB_OVF 0x02 /* Write Buffer Overflow */ -#define WB_EDGE 0x04 /* Write Buffer Edge Detect */ -#define RD_RDY 0x08 /* Read Data Ready */ -#define WR_DONE 0x10 /* Page Write Done */ - -/* NFC_RST Masks */ -#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */ - -/* NFC_PGCTL Masks */ -#define PG_RD_START 0x01 /* Page Read Start */ -#define PG_WR_START 0x02 /* Page Write Start */ - -#ifdef CONFIG_MTD_NAND_BF5XX_HWECC -static int hardware_ecc = 1; -#else -static int hardware_ecc; -#endif - -static const unsigned short bfin_nfc_pin_req[] = - {P_NAND_CE, - P_NAND_RB, - P_NAND_D0, - P_NAND_D1, - P_NAND_D2, - P_NAND_D3, - P_NAND_D4, - P_NAND_D5, - P_NAND_D6, - P_NAND_D7, - P_NAND_WE, - P_NAND_RE, - P_NAND_CLE, - P_NAND_ALE, - 0}; - -#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC -static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 7) - return -ERANGE; - - oobregion->offset = section * 8; - oobregion->length = 3; - - return 0; -} - -static int bootrom_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 7) - return -ERANGE; - - oobregion->offset = (section * 8) + 3; - oobregion->length = 5; - - return 0; -} - -static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = { - .ecc = bootrom_ooblayout_ecc, - .free = bootrom_ooblayout_free, -}; -#endif - -/* - * Data structures for bf5xx nand flash controller driver - */ - -/* bf5xx nand info */ -struct bf5xx_nand_info { - /* mtd info */ - struct nand_hw_control controller; - struct nand_chip chip; - - /* platform info */ - struct bf5xx_nand_platform *platform; - - /* device info */ - struct device *device; - - /* DMA stuff */ - struct completion dma_completion; -}; - -/* - * Conversion functions - */ -static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct bf5xx_nand_info, - chip); -} - -static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev) -{ - return platform_get_drvdata(pdev); -} - -static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev) -{ - return dev_get_platdata(&pdev->dev); -} - -/* - * struct nand_chip interface function pointers - */ - -/* - * bf5xx_nand_hwcontrol - * - * Issue command and address cycles to the chip - */ -static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - if (cmd == NAND_CMD_NONE) - return; - - while (bfin_read_NFC_STAT() & WB_FULL) - cpu_relax(); - - if (ctrl & NAND_CLE) - bfin_write_NFC_CMD(cmd); - else if (ctrl & NAND_ALE) - bfin_write_NFC_ADDR(cmd); - SSYNC(); -} - -/* - * bf5xx_nand_devready() - * - * returns 0 if the nand is busy, 1 if it is ready - */ -static int bf5xx_nand_devready(struct mtd_info *mtd) -{ - unsigned short val = bfin_read_NFC_STAT(); - - if ((val & NBUSY) == NBUSY) - return 1; - else - return 0; -} - -/* - * ECC functions - * These allow the bf5xx to use the controller's ECC - * generator block to ECC the data as it passes through - */ - -/* - * ECC error correction function - */ -static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - u32 syndrome[5]; - u32 calced, stored; - int i; - unsigned short failing_bit, failing_byte; - u_char data; - - calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16); - stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16); - - syndrome[0] = (calced ^ stored); - - /* - * syndrome 0: all zero - * No error in data - * No action - */ - if (!syndrome[0] || !calced || !stored) - return 0; - - /* - * sysdrome 0: only one bit is one - * ECC data was incorrect - * No action - */ - if (hweight32(syndrome[0]) == 1) { - dev_err(info->device, "ECC data was incorrect!\n"); - return -EBADMSG; - } - - syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF); - syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF); - syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF); - syndrome[4] = syndrome[2] ^ syndrome[3]; - - for (i = 0; i < 5; i++) - dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]); - - dev_info(info->device, - "calced[0x%08x], stored[0x%08x]\n", - calced, stored); - - /* - * sysdrome 0: exactly 11 bits are one, each parity - * and parity' pair is 1 & 0 or 0 & 1. - * 1-bit correctable error - * Correct the error - */ - if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) { - dev_info(info->device, - "1-bit correctable error, correct it.\n"); - dev_info(info->device, - "syndrome[1] 0x%08x\n", syndrome[1]); - - failing_bit = syndrome[1] & 0x7; - failing_byte = syndrome[1] >> 0x3; - data = *(dat + failing_byte); - data = data ^ (0x1 << failing_bit); - *(dat + failing_byte) = data; - - return 1; - } - - /* - * sysdrome 0: random data - * More than 1-bit error, non-correctable error - * Discard data, mark bad block - */ - dev_err(info->device, - "More than 1-bit error, non-correctable error.\n"); - dev_err(info->device, - "Please discard data, mark bad block\n"); - - return -EBADMSG; -} - -static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int ret, bitflips = 0; - - ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); - if (ret < 0) - return ret; - - bitflips = ret; - - /* If ecc size is 512, correct second 256 bytes */ - if (chip->ecc.size == 512) { - dat += 256; - read_ecc += 3; - calc_ecc += 3; - ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); - if (ret < 0) - return ret; - - bitflips += ret; - } - - return bitflips; -} - -static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ - return; -} - -static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, - const u_char *dat, u_char *ecc_code) -{ - struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - u16 ecc0, ecc1; - u32 code[2]; - u8 *p; - - /* first 3 bytes ECC code for 256 page size */ - ecc0 = bfin_read_NFC_ECC0(); - ecc1 = bfin_read_NFC_ECC1(); - - code[0] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11); - - dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]); - - p = (u8 *) code; - memcpy(ecc_code, p, 3); - - /* second 3 bytes ECC code for 512 ecc size */ - if (chip->ecc.size == 512) { - ecc0 = bfin_read_NFC_ECC2(); - ecc1 = bfin_read_NFC_ECC3(); - code[1] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11); - - /* second 3 bytes in ecc_code for second 256 - * bytes of 512 page size - */ - p = (u8 *) (code + 1); - memcpy((ecc_code + 3), p, 3); - dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]); - } - - return 0; -} - -/* - * PIO mode for buffer writing and reading - */ -static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - int i; - unsigned short val; - - /* - * Data reads are requested by first writing to NFC_DATA_RD - * and then reading back from NFC_READ. - */ - for (i = 0; i < len; i++) { - while (bfin_read_NFC_STAT() & WB_FULL) - cpu_relax(); - - /* Contents do not matter */ - bfin_write_NFC_DATA_RD(0x0000); - SSYNC(); - - while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY) - cpu_relax(); - - buf[i] = bfin_read_NFC_READ(); - - val = bfin_read_NFC_IRQSTAT(); - val |= RD_RDY; - bfin_write_NFC_IRQSTAT(val); - SSYNC(); - } -} - -static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd) -{ - uint8_t val; - - bf5xx_nand_read_buf(mtd, &val, 1); - - return val; -} - -static void bf5xx_nand_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) { - while (bfin_read_NFC_STAT() & WB_FULL) - cpu_relax(); - - bfin_write_NFC_DATA_WR(buf[i]); - SSYNC(); - } -} - -static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) -{ - int i; - u16 *p = (u16 *) buf; - len >>= 1; - - /* - * Data reads are requested by first writing to NFC_DATA_RD - * and then reading back from NFC_READ. - */ - bfin_write_NFC_DATA_RD(0x5555); - - SSYNC(); - - for (i = 0; i < len; i++) - p[i] = bfin_read_NFC_READ(); -} - -static void bf5xx_nand_write_buf16(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - int i; - u16 *p = (u16 *) buf; - len >>= 1; - - for (i = 0; i < len; i++) - bfin_write_NFC_DATA_WR(p[i]); - - SSYNC(); -} - -/* - * DMA functions for buffer writing and reading - */ -static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id) -{ - struct bf5xx_nand_info *info = dev_id; - - clear_dma_irqstat(CH_NFC); - disable_dma(CH_NFC); - complete(&info->dma_completion); - - return IRQ_HANDLED; -} - -static void bf5xx_nand_dma_rw(struct mtd_info *mtd, - uint8_t *buf, int is_read) -{ - struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - unsigned short val; - - dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n", - mtd, buf, is_read); - - /* - * Before starting a dma transfer, be sure to invalidate/flush - * the cache over the address range of your DMA buffer to - * prevent cache coherency problems. Otherwise very subtle bugs - * can be introduced to your driver. - */ - if (is_read) - invalidate_dcache_range((unsigned int)buf, - (unsigned int)(buf + chip->ecc.size)); - else - flush_dcache_range((unsigned int)buf, - (unsigned int)(buf + chip->ecc.size)); - - /* - * This register must be written before each page is - * transferred to generate the correct ECC register - * values. - */ - bfin_write_NFC_RST(ECC_RST); - SSYNC(); - while (bfin_read_NFC_RST() & ECC_RST) - cpu_relax(); - - disable_dma(CH_NFC); - clear_dma_irqstat(CH_NFC); - - /* setup DMA register with Blackfin DMA API */ - set_dma_config(CH_NFC, 0x0); - set_dma_start_addr(CH_NFC, (unsigned long) buf); - - /* The DMAs have different size on BF52x and BF54x */ -#ifdef CONFIG_BF52x - set_dma_x_count(CH_NFC, (chip->ecc.size >> 1)); - set_dma_x_modify(CH_NFC, 2); - val = DI_EN | WDSIZE_16; -#endif - -#ifdef CONFIG_BF54x - set_dma_x_count(CH_NFC, (chip->ecc.size >> 2)); - set_dma_x_modify(CH_NFC, 4); - val = DI_EN | WDSIZE_32; -#endif - /* setup write or read operation */ - if (is_read) - val |= WNR; - set_dma_config(CH_NFC, val); - enable_dma(CH_NFC); - - /* Start PAGE read/write operation */ - if (is_read) - bfin_write_NFC_PGCTL(PG_RD_START); - else - bfin_write_NFC_PGCTL(PG_WR_START); - wait_for_completion(&info->dma_completion); -} - -static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, - uint8_t *buf, int len) -{ - struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - - dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len); - - if (len == chip->ecc.size) - bf5xx_nand_dma_rw(mtd, buf, 1); - else - bf5xx_nand_read_buf(mtd, buf, len); -} - -static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - - dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len); - - if (len == chip->ecc.size) - bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0); - else - bf5xx_nand_write_buf(mtd, buf, len); -} - -static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - nand_read_page_op(chip, page, 0, NULL, 0); - - bf5xx_nand_read_buf(mtd, buf, mtd->writesize); - bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - -static int bf5xx_nand_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required, - int page) -{ - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -/* - * System initialization functions - */ -static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) -{ - int ret; - - /* Do not use dma */ - if (!hardware_ecc) - return 0; - - init_completion(&info->dma_completion); - - /* Request NFC DMA channel */ - ret = request_dma(CH_NFC, "BF5XX NFC driver"); - if (ret < 0) { - dev_err(info->device, " unable to get DMA channel\n"); - return ret; - } - -#ifdef CONFIG_BF54x - /* Setup DMAC1 channel mux for NFC which shared with SDH */ - bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1); - SSYNC(); -#endif - - set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info); - - /* Turn off the DMA channel first */ - disable_dma(CH_NFC); - return 0; -} - -static void bf5xx_nand_dma_remove(struct bf5xx_nand_info *info) -{ - /* Free NFC DMA channel */ - if (hardware_ecc) - free_dma(CH_NFC); -} - -/* - * BF5XX NFC hardware initialization - * - pin mux setup - * - clear interrupt status - */ -static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) -{ - int err = 0; - unsigned short val; - struct bf5xx_nand_platform *plat = info->platform; - - /* setup NFC_CTL register */ - dev_info(info->device, - "data_width=%d, wr_dly=%d, rd_dly=%d\n", - (plat->data_width ? 16 : 8), - plat->wr_dly, plat->rd_dly); - - val = (1 << NFC_PG_SIZE_OFFSET) | - (plat->data_width << NFC_NWIDTH_OFFSET) | - (plat->rd_dly << NFC_RDDLY_OFFSET) | - (plat->wr_dly << NFC_WRDLY_OFFSET); - dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val); - - bfin_write_NFC_CTL(val); - SSYNC(); - - /* clear interrupt status */ - bfin_write_NFC_IRQMASK(0x0); - SSYNC(); - val = bfin_read_NFC_IRQSTAT(); - bfin_write_NFC_IRQSTAT(val); - SSYNC(); - - /* DMA initialization */ - if (bf5xx_nand_dma_init(info)) - err = -ENXIO; - - return err; -} - -/* - * Device management interface - */ -static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info) -{ - struct mtd_info *mtd = nand_to_mtd(&info->chip); - struct mtd_partition *parts = info->platform->partitions; - int nr = info->platform->nr_partitions; - - return mtd_device_register(mtd, parts, nr); -} - -static int bf5xx_nand_remove(struct platform_device *pdev) -{ - struct bf5xx_nand_info *info = to_nand_info(pdev); - - /* first thing we need to do is release all our mtds - * and their partitions, then go through freeing the - * resources used - */ - nand_release(nand_to_mtd(&info->chip)); - - peripheral_free_list(bfin_nfc_pin_req); - bf5xx_nand_dma_remove(info); - - return 0; -} - -static int bf5xx_nand_scan(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - if (hardware_ecc) { - /* - * for nand with page size > 512B, think it as several sections with 512B - */ - if (likely(mtd->writesize >= 512)) { - chip->ecc.size = 512; - chip->ecc.bytes = 6; - chip->ecc.strength = 2; - } else { - chip->ecc.size = 256; - chip->ecc.bytes = 3; - chip->ecc.strength = 1; - bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET)); - SSYNC(); - } - } - - return nand_scan_tail(mtd); -} - -/* - * bf5xx_nand_probe - * - * called by device layer when it finds a device matching - * one our driver can handled. This code checks to see if - * it can allocate all necessary resources then calls the - * nand layer to look for devices - */ -static int bf5xx_nand_probe(struct platform_device *pdev) -{ - struct bf5xx_nand_platform *plat = to_nand_plat(pdev); - struct bf5xx_nand_info *info = NULL; - struct nand_chip *chip = NULL; - struct mtd_info *mtd = NULL; - int err = 0; - - dev_dbg(&pdev->dev, "(%p)\n", pdev); - - if (!plat) { - dev_err(&pdev->dev, "no platform specific information\n"); - return -EINVAL; - } - - if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) { - dev_err(&pdev->dev, "requesting Peripherals failed\n"); - return -EFAULT; - } - - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); - if (info == NULL) { - err = -ENOMEM; - goto out_err; - } - - platform_set_drvdata(pdev, info); - - nand_hw_control_init(&info->controller); - - info->device = &pdev->dev; - info->platform = plat; - - /* initialise chip data struct */ - chip = &info->chip; - mtd = nand_to_mtd(&info->chip); - - if (plat->data_width) - chip->options |= NAND_BUSWIDTH_16; - - chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN; - - chip->read_buf = (plat->data_width) ? - bf5xx_nand_read_buf16 : bf5xx_nand_read_buf; - chip->write_buf = (plat->data_width) ? - bf5xx_nand_write_buf16 : bf5xx_nand_write_buf; - - chip->read_byte = bf5xx_nand_read_byte; - - chip->cmd_ctrl = bf5xx_nand_hwcontrol; - chip->dev_ready = bf5xx_nand_devready; - - nand_set_controller_data(chip, mtd); - chip->controller = &info->controller; - - chip->IO_ADDR_R = (void __iomem *) NFC_READ; - chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR; - - chip->chip_delay = 0; - - /* initialise mtd info data struct */ - mtd->dev.parent = &pdev->dev; - - /* initialise the hardware */ - err = bf5xx_nand_hw_init(info); - if (err) - goto out_err; - - /* setup hardware ECC data struct */ - if (hardware_ecc) { -#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC - mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops); -#endif - chip->read_buf = bf5xx_nand_dma_read_buf; - chip->write_buf = bf5xx_nand_dma_write_buf; - chip->ecc.calculate = bf5xx_nand_calculate_ecc; - chip->ecc.correct = bf5xx_nand_correct_data; - chip->ecc.mode = NAND_ECC_HW; - chip->ecc.hwctl = bf5xx_nand_enable_hwecc; - chip->ecc.read_page_raw = bf5xx_nand_read_page_raw; - chip->ecc.write_page_raw = bf5xx_nand_write_page_raw; - } else { - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - } - - /* scan hardware nand chip and setup mtd info data struct */ - if (bf5xx_nand_scan(mtd)) { - err = -ENXIO; - goto out_err_nand_scan; - } - -#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC - chip->badblockpos = 63; -#endif - - /* add NAND partition */ - bf5xx_nand_add_partition(info); - - dev_dbg(&pdev->dev, "initialised ok\n"); - return 0; - -out_err_nand_scan: - bf5xx_nand_dma_remove(info); -out_err: - peripheral_free_list(bfin_nfc_pin_req); - - return err; -} - -/* driver device registration */ -static struct platform_driver bf5xx_nand_driver = { - .probe = bf5xx_nand_probe, - .remove = bf5xx_nand_remove, - .driver = { - .name = DRV_NAME, - }, -}; - -module_platform_driver(bf5xx_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR(DRV_AUTHOR); -MODULE_DESCRIPTION(DRV_DESC); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/mtd/nand/brcmnand/Makefile b/drivers/mtd/nand/brcmnand/Makefile deleted file mode 100644 index 195b845e48b8..000000000000 --- a/drivers/mtd/nand/brcmnand/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# link order matters; don't link the more generic brcmstb_nand.o before the -# more specific iproc_nand.o, for instance -obj-$(CONFIG_MTD_NAND_BRCMNAND) += iproc_nand.o -obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63138_nand.o -obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm6368_nand.o -obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o -obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o diff --git a/drivers/mtd/nand/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/brcmnand/bcm63138_nand.c deleted file mode 100644 index 59444b3a697d..000000000000 --- a/drivers/mtd/nand/brcmnand/bcm63138_nand.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright © 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "brcmnand.h" - -struct bcm63138_nand_soc { - struct brcmnand_soc soc; - void __iomem *base; -}; - -#define BCM63138_NAND_INT_STATUS 0x00 -#define BCM63138_NAND_INT_EN 0x04 - -enum { - BCM63138_CTLRDY = BIT(4), -}; - -static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc) -{ - struct bcm63138_nand_soc *priv = - container_of(soc, struct bcm63138_nand_soc, soc); - void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS; - u32 val = brcmnand_readl(mmio); - - if (val & BCM63138_CTLRDY) { - brcmnand_writel(val & ~BCM63138_CTLRDY, mmio); - return true; - } - - return false; -} - -static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en) -{ - struct bcm63138_nand_soc *priv = - container_of(soc, struct bcm63138_nand_soc, soc); - void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN; - u32 val = brcmnand_readl(mmio); - - if (en) - val |= BCM63138_CTLRDY; - else - val &= ~BCM63138_CTLRDY; - - brcmnand_writel(val, mmio); -} - -static int bcm63138_nand_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct bcm63138_nand_soc *priv; - struct brcmnand_soc *soc; - struct resource *res; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - soc = &priv->soc; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base"); - priv->base = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - soc->ctlrdy_ack = bcm63138_nand_intc_ack; - soc->ctlrdy_set_enabled = bcm63138_nand_intc_set; - - return brcmnand_probe(pdev, soc); -} - -static const struct of_device_id bcm63138_nand_of_match[] = { - { .compatible = "brcm,nand-bcm63138" }, - {}, -}; -MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match); - -static struct platform_driver bcm63138_nand_driver = { - .probe = bcm63138_nand_probe, - .remove = brcmnand_remove, - .driver = { - .name = "bcm63138_nand", - .pm = &brcmnand_pm_ops, - .of_match_table = bcm63138_nand_of_match, - } -}; -module_platform_driver(bcm63138_nand_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Brian Norris"); -MODULE_DESCRIPTION("NAND driver for BCM63138"); diff --git a/drivers/mtd/nand/brcmnand/bcm6368_nand.c b/drivers/mtd/nand/brcmnand/bcm6368_nand.c deleted file mode 100644 index 34c91b0e1e69..000000000000 --- a/drivers/mtd/nand/brcmnand/bcm6368_nand.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2015 Simon Arlott - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Derived from bcm63138_nand.c: - * Copyright © 2015 Broadcom Corporation - * - * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/include/bcm963xx/63268_map_part.h: - * Copyright 2000-2010 Broadcom Corporation - * - * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/flash/nandflash.c: - * Copyright 2000-2010 Broadcom Corporation - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "brcmnand.h" - -struct bcm6368_nand_soc { - struct brcmnand_soc soc; - void __iomem *base; -}; - -#define BCM6368_NAND_INT 0x00 -#define BCM6368_NAND_STATUS_SHIFT 0 -#define BCM6368_NAND_STATUS_MASK (0xfff << BCM6368_NAND_STATUS_SHIFT) -#define BCM6368_NAND_ENABLE_SHIFT 16 -#define BCM6368_NAND_ENABLE_MASK (0xffff << BCM6368_NAND_ENABLE_SHIFT) -#define BCM6368_NAND_BASE_ADDR0 0x04 -#define BCM6368_NAND_BASE_ADDR1 0x0c - -enum { - BCM6368_NP_READ = BIT(0), - BCM6368_BLOCK_ERASE = BIT(1), - BCM6368_COPY_BACK = BIT(2), - BCM6368_PAGE_PGM = BIT(3), - BCM6368_CTRL_READY = BIT(4), - BCM6368_DEV_RBPIN = BIT(5), - BCM6368_ECC_ERR_UNC = BIT(6), - BCM6368_ECC_ERR_CORR = BIT(7), -}; - -static bool bcm6368_nand_intc_ack(struct brcmnand_soc *soc) -{ - struct bcm6368_nand_soc *priv = - container_of(soc, struct bcm6368_nand_soc, soc); - void __iomem *mmio = priv->base + BCM6368_NAND_INT; - u32 val = brcmnand_readl(mmio); - - if (val & (BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT)) { - /* Ack interrupt */ - val &= ~BCM6368_NAND_STATUS_MASK; - val |= BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT; - brcmnand_writel(val, mmio); - return true; - } - - return false; -} - -static void bcm6368_nand_intc_set(struct brcmnand_soc *soc, bool en) -{ - struct bcm6368_nand_soc *priv = - container_of(soc, struct bcm6368_nand_soc, soc); - void __iomem *mmio = priv->base + BCM6368_NAND_INT; - u32 val = brcmnand_readl(mmio); - - /* Don't ack any interrupts */ - val &= ~BCM6368_NAND_STATUS_MASK; - - if (en) - val |= BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT; - else - val &= ~(BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT); - - brcmnand_writel(val, mmio); -} - -static int bcm6368_nand_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct bcm6368_nand_soc *priv; - struct brcmnand_soc *soc; - struct resource *res; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - soc = &priv->soc; - - res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, "nand-int-base"); - priv->base = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - soc->ctlrdy_ack = bcm6368_nand_intc_ack; - soc->ctlrdy_set_enabled = bcm6368_nand_intc_set; - - /* Disable and ack all interrupts */ - brcmnand_writel(0, priv->base + BCM6368_NAND_INT); - brcmnand_writel(BCM6368_NAND_STATUS_MASK, - priv->base + BCM6368_NAND_INT); - - return brcmnand_probe(pdev, soc); -} - -static const struct of_device_id bcm6368_nand_of_match[] = { - { .compatible = "brcm,nand-bcm6368" }, - {}, -}; -MODULE_DEVICE_TABLE(of, bcm6368_nand_of_match); - -static struct platform_driver bcm6368_nand_driver = { - .probe = bcm6368_nand_probe, - .remove = brcmnand_remove, - .driver = { - .name = "bcm6368_nand", - .pm = &brcmnand_pm_ops, - .of_match_table = bcm6368_nand_of_match, - } -}; -module_platform_driver(bcm6368_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Simon Arlott"); -MODULE_DESCRIPTION("NAND driver for BCM6368"); diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c deleted file mode 100644 index c28fd2bc1a84..000000000000 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ /dev/null @@ -1,2620 +0,0 @@ -/* - * Copyright © 2010-2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "brcmnand.h" - -/* - * This flag controls if WP stays on between erase/write commands to mitigate - * flash corruption due to power glitches. Values: - * 0: NAND_WP is not used or not available - * 1: NAND_WP is set by default, cleared for erase/write operations - * 2: NAND_WP is always cleared - */ -static int wp_on = 1; -module_param(wp_on, int, 0444); - -/*********************************************************************** - * Definitions - ***********************************************************************/ - -#define DRV_NAME "brcmnand" - -#define CMD_NULL 0x00 -#define CMD_PAGE_READ 0x01 -#define CMD_SPARE_AREA_READ 0x02 -#define CMD_STATUS_READ 0x03 -#define CMD_PROGRAM_PAGE 0x04 -#define CMD_PROGRAM_SPARE_AREA 0x05 -#define CMD_COPY_BACK 0x06 -#define CMD_DEVICE_ID_READ 0x07 -#define CMD_BLOCK_ERASE 0x08 -#define CMD_FLASH_RESET 0x09 -#define CMD_BLOCKS_LOCK 0x0a -#define CMD_BLOCKS_LOCK_DOWN 0x0b -#define CMD_BLOCKS_UNLOCK 0x0c -#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d -#define CMD_PARAMETER_READ 0x0e -#define CMD_PARAMETER_CHANGE_COL 0x0f -#define CMD_LOW_LEVEL_OP 0x10 - -struct brcm_nand_dma_desc { - u32 next_desc; - u32 next_desc_ext; - u32 cmd_irq; - u32 dram_addr; - u32 dram_addr_ext; - u32 tfr_len; - u32 total_len; - u32 flash_addr; - u32 flash_addr_ext; - u32 cs; - u32 pad2[5]; - u32 status_valid; -} __packed; - -/* Bitfields for brcm_nand_dma_desc::status_valid */ -#define FLASH_DMA_ECC_ERROR (1 << 8) -#define FLASH_DMA_CORR_ERROR (1 << 9) - -/* 512B flash cache in the NAND controller HW */ -#define FC_SHIFT 9U -#define FC_BYTES 512U -#define FC_WORDS (FC_BYTES >> 2) - -#define BRCMNAND_MIN_PAGESIZE 512 -#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024) -#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024) - -#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY) -#define NAND_POLL_STATUS_TIMEOUT_MS 100 - -/* Controller feature flags */ -enum { - BRCMNAND_HAS_1K_SECTORS = BIT(0), - BRCMNAND_HAS_PREFETCH = BIT(1), - BRCMNAND_HAS_CACHE_MODE = BIT(2), - BRCMNAND_HAS_WP = BIT(3), -}; - -struct brcmnand_controller { - struct device *dev; - struct nand_hw_control controller; - void __iomem *nand_base; - void __iomem *nand_fc; /* flash cache */ - void __iomem *flash_dma_base; - unsigned int irq; - unsigned int dma_irq; - int nand_version; - - /* Some SoCs provide custom interrupt status register(s) */ - struct brcmnand_soc *soc; - - /* Some SoCs have a gateable clock for the controller */ - struct clk *clk; - - int cmd_pending; - bool dma_pending; - struct completion done; - struct completion dma_done; - - /* List of NAND hosts (one for each chip-select) */ - struct list_head host_list; - - struct brcm_nand_dma_desc *dma_desc; - dma_addr_t dma_pa; - - /* in-memory cache of the FLASH_CACHE, used only for some commands */ - u8 flash_cache[FC_BYTES]; - - /* Controller revision details */ - const u16 *reg_offsets; - unsigned int reg_spacing; /* between CS1, CS2, ... regs */ - const u8 *cs_offsets; /* within each chip-select */ - const u8 *cs0_offsets; /* within CS0, if different */ - unsigned int max_block_size; - const unsigned int *block_sizes; - unsigned int max_page_size; - const unsigned int *page_sizes; - unsigned int max_oob; - u32 features; - - /* for low-power standby/resume only */ - u32 nand_cs_nand_select; - u32 nand_cs_nand_xor; - u32 corr_stat_threshold; - u32 flash_dma_mode; -}; - -struct brcmnand_cfg { - u64 device_size; - unsigned int block_size; - unsigned int page_size; - unsigned int spare_area_size; - unsigned int device_width; - unsigned int col_adr_bytes; - unsigned int blk_adr_bytes; - unsigned int ful_adr_bytes; - unsigned int sector_size_1k; - unsigned int ecc_level; - /* use for low-power standby/resume only */ - u32 acc_control; - u32 config; - u32 config_ext; - u32 timing_1; - u32 timing_2; -}; - -struct brcmnand_host { - struct list_head node; - - struct nand_chip chip; - struct platform_device *pdev; - int cs; - - unsigned int last_cmd; - unsigned int last_byte; - u64 last_addr; - struct brcmnand_cfg hwcfg; - struct brcmnand_controller *ctrl; -}; - -enum brcmnand_reg { - BRCMNAND_CMD_START = 0, - BRCMNAND_CMD_EXT_ADDRESS, - BRCMNAND_CMD_ADDRESS, - BRCMNAND_INTFC_STATUS, - BRCMNAND_CS_SELECT, - BRCMNAND_CS_XOR, - BRCMNAND_LL_OP, - BRCMNAND_CS0_BASE, - BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */ - BRCMNAND_CORR_THRESHOLD, - BRCMNAND_CORR_THRESHOLD_EXT, - BRCMNAND_UNCORR_COUNT, - BRCMNAND_CORR_COUNT, - BRCMNAND_CORR_EXT_ADDR, - BRCMNAND_CORR_ADDR, - BRCMNAND_UNCORR_EXT_ADDR, - BRCMNAND_UNCORR_ADDR, - BRCMNAND_SEMAPHORE, - BRCMNAND_ID, - BRCMNAND_ID_EXT, - BRCMNAND_LL_RDATA, - BRCMNAND_OOB_READ_BASE, - BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */ - BRCMNAND_OOB_WRITE_BASE, - BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */ - BRCMNAND_FC_BASE, -}; - -/* BRCMNAND v4.0 */ -static const u16 brcmnand_regs_v40[] = { - [BRCMNAND_CMD_START] = 0x04, - [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, - [BRCMNAND_CMD_ADDRESS] = 0x0c, - [BRCMNAND_INTFC_STATUS] = 0x6c, - [BRCMNAND_CS_SELECT] = 0x14, - [BRCMNAND_CS_XOR] = 0x18, - [BRCMNAND_LL_OP] = 0x178, - [BRCMNAND_CS0_BASE] = 0x40, - [BRCMNAND_CS1_BASE] = 0xd0, - [BRCMNAND_CORR_THRESHOLD] = 0x84, - [BRCMNAND_CORR_THRESHOLD_EXT] = 0, - [BRCMNAND_UNCORR_COUNT] = 0, - [BRCMNAND_CORR_COUNT] = 0, - [BRCMNAND_CORR_EXT_ADDR] = 0x70, - [BRCMNAND_CORR_ADDR] = 0x74, - [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, - [BRCMNAND_UNCORR_ADDR] = 0x7c, - [BRCMNAND_SEMAPHORE] = 0x58, - [BRCMNAND_ID] = 0x60, - [BRCMNAND_ID_EXT] = 0x64, - [BRCMNAND_LL_RDATA] = 0x17c, - [BRCMNAND_OOB_READ_BASE] = 0x20, - [BRCMNAND_OOB_READ_10_BASE] = 0x130, - [BRCMNAND_OOB_WRITE_BASE] = 0x30, - [BRCMNAND_OOB_WRITE_10_BASE] = 0, - [BRCMNAND_FC_BASE] = 0x200, -}; - -/* BRCMNAND v5.0 */ -static const u16 brcmnand_regs_v50[] = { - [BRCMNAND_CMD_START] = 0x04, - [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, - [BRCMNAND_CMD_ADDRESS] = 0x0c, - [BRCMNAND_INTFC_STATUS] = 0x6c, - [BRCMNAND_CS_SELECT] = 0x14, - [BRCMNAND_CS_XOR] = 0x18, - [BRCMNAND_LL_OP] = 0x178, - [BRCMNAND_CS0_BASE] = 0x40, - [BRCMNAND_CS1_BASE] = 0xd0, - [BRCMNAND_CORR_THRESHOLD] = 0x84, - [BRCMNAND_CORR_THRESHOLD_EXT] = 0, - [BRCMNAND_UNCORR_COUNT] = 0, - [BRCMNAND_CORR_COUNT] = 0, - [BRCMNAND_CORR_EXT_ADDR] = 0x70, - [BRCMNAND_CORR_ADDR] = 0x74, - [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, - [BRCMNAND_UNCORR_ADDR] = 0x7c, - [BRCMNAND_SEMAPHORE] = 0x58, - [BRCMNAND_ID] = 0x60, - [BRCMNAND_ID_EXT] = 0x64, - [BRCMNAND_LL_RDATA] = 0x17c, - [BRCMNAND_OOB_READ_BASE] = 0x20, - [BRCMNAND_OOB_READ_10_BASE] = 0x130, - [BRCMNAND_OOB_WRITE_BASE] = 0x30, - [BRCMNAND_OOB_WRITE_10_BASE] = 0x140, - [BRCMNAND_FC_BASE] = 0x200, -}; - -/* BRCMNAND v6.0 - v7.1 */ -static const u16 brcmnand_regs_v60[] = { - [BRCMNAND_CMD_START] = 0x04, - [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, - [BRCMNAND_CMD_ADDRESS] = 0x0c, - [BRCMNAND_INTFC_STATUS] = 0x14, - [BRCMNAND_CS_SELECT] = 0x18, - [BRCMNAND_CS_XOR] = 0x1c, - [BRCMNAND_LL_OP] = 0x20, - [BRCMNAND_CS0_BASE] = 0x50, - [BRCMNAND_CS1_BASE] = 0, - [BRCMNAND_CORR_THRESHOLD] = 0xc0, - [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4, - [BRCMNAND_UNCORR_COUNT] = 0xfc, - [BRCMNAND_CORR_COUNT] = 0x100, - [BRCMNAND_CORR_EXT_ADDR] = 0x10c, - [BRCMNAND_CORR_ADDR] = 0x110, - [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, - [BRCMNAND_UNCORR_ADDR] = 0x118, - [BRCMNAND_SEMAPHORE] = 0x150, - [BRCMNAND_ID] = 0x194, - [BRCMNAND_ID_EXT] = 0x198, - [BRCMNAND_LL_RDATA] = 0x19c, - [BRCMNAND_OOB_READ_BASE] = 0x200, - [BRCMNAND_OOB_READ_10_BASE] = 0, - [BRCMNAND_OOB_WRITE_BASE] = 0x280, - [BRCMNAND_OOB_WRITE_10_BASE] = 0, - [BRCMNAND_FC_BASE] = 0x400, -}; - -/* BRCMNAND v7.1 */ -static const u16 brcmnand_regs_v71[] = { - [BRCMNAND_CMD_START] = 0x04, - [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, - [BRCMNAND_CMD_ADDRESS] = 0x0c, - [BRCMNAND_INTFC_STATUS] = 0x14, - [BRCMNAND_CS_SELECT] = 0x18, - [BRCMNAND_CS_XOR] = 0x1c, - [BRCMNAND_LL_OP] = 0x20, - [BRCMNAND_CS0_BASE] = 0x50, - [BRCMNAND_CS1_BASE] = 0, - [BRCMNAND_CORR_THRESHOLD] = 0xdc, - [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, - [BRCMNAND_UNCORR_COUNT] = 0xfc, - [BRCMNAND_CORR_COUNT] = 0x100, - [BRCMNAND_CORR_EXT_ADDR] = 0x10c, - [BRCMNAND_CORR_ADDR] = 0x110, - [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, - [BRCMNAND_UNCORR_ADDR] = 0x118, - [BRCMNAND_SEMAPHORE] = 0x150, - [BRCMNAND_ID] = 0x194, - [BRCMNAND_ID_EXT] = 0x198, - [BRCMNAND_LL_RDATA] = 0x19c, - [BRCMNAND_OOB_READ_BASE] = 0x200, - [BRCMNAND_OOB_READ_10_BASE] = 0, - [BRCMNAND_OOB_WRITE_BASE] = 0x280, - [BRCMNAND_OOB_WRITE_10_BASE] = 0, - [BRCMNAND_FC_BASE] = 0x400, -}; - -/* BRCMNAND v7.2 */ -static const u16 brcmnand_regs_v72[] = { - [BRCMNAND_CMD_START] = 0x04, - [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, - [BRCMNAND_CMD_ADDRESS] = 0x0c, - [BRCMNAND_INTFC_STATUS] = 0x14, - [BRCMNAND_CS_SELECT] = 0x18, - [BRCMNAND_CS_XOR] = 0x1c, - [BRCMNAND_LL_OP] = 0x20, - [BRCMNAND_CS0_BASE] = 0x50, - [BRCMNAND_CS1_BASE] = 0, - [BRCMNAND_CORR_THRESHOLD] = 0xdc, - [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, - [BRCMNAND_UNCORR_COUNT] = 0xfc, - [BRCMNAND_CORR_COUNT] = 0x100, - [BRCMNAND_CORR_EXT_ADDR] = 0x10c, - [BRCMNAND_CORR_ADDR] = 0x110, - [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, - [BRCMNAND_UNCORR_ADDR] = 0x118, - [BRCMNAND_SEMAPHORE] = 0x150, - [BRCMNAND_ID] = 0x194, - [BRCMNAND_ID_EXT] = 0x198, - [BRCMNAND_LL_RDATA] = 0x19c, - [BRCMNAND_OOB_READ_BASE] = 0x200, - [BRCMNAND_OOB_READ_10_BASE] = 0, - [BRCMNAND_OOB_WRITE_BASE] = 0x400, - [BRCMNAND_OOB_WRITE_10_BASE] = 0, - [BRCMNAND_FC_BASE] = 0x600, -}; - -enum brcmnand_cs_reg { - BRCMNAND_CS_CFG_EXT = 0, - BRCMNAND_CS_CFG, - BRCMNAND_CS_ACC_CONTROL, - BRCMNAND_CS_TIMING1, - BRCMNAND_CS_TIMING2, -}; - -/* Per chip-select offsets for v7.1 */ -static const u8 brcmnand_cs_offsets_v71[] = { - [BRCMNAND_CS_ACC_CONTROL] = 0x00, - [BRCMNAND_CS_CFG_EXT] = 0x04, - [BRCMNAND_CS_CFG] = 0x08, - [BRCMNAND_CS_TIMING1] = 0x0c, - [BRCMNAND_CS_TIMING2] = 0x10, -}; - -/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */ -static const u8 brcmnand_cs_offsets[] = { - [BRCMNAND_CS_ACC_CONTROL] = 0x00, - [BRCMNAND_CS_CFG_EXT] = 0x04, - [BRCMNAND_CS_CFG] = 0x04, - [BRCMNAND_CS_TIMING1] = 0x08, - [BRCMNAND_CS_TIMING2] = 0x0c, -}; - -/* Per chip-select offset for <= v5.0 on CS0 only */ -static const u8 brcmnand_cs_offsets_cs0[] = { - [BRCMNAND_CS_ACC_CONTROL] = 0x00, - [BRCMNAND_CS_CFG_EXT] = 0x08, - [BRCMNAND_CS_CFG] = 0x08, - [BRCMNAND_CS_TIMING1] = 0x10, - [BRCMNAND_CS_TIMING2] = 0x14, -}; - -/* - * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had - * one config register, but once the bitfields overflowed, newer controllers - * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around. - */ -enum { - CFG_BLK_ADR_BYTES_SHIFT = 8, - CFG_COL_ADR_BYTES_SHIFT = 12, - CFG_FUL_ADR_BYTES_SHIFT = 16, - CFG_BUS_WIDTH_SHIFT = 23, - CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT), - CFG_DEVICE_SIZE_SHIFT = 24, - - /* Only for pre-v7.1 (with no CFG_EXT register) */ - CFG_PAGE_SIZE_SHIFT = 20, - CFG_BLK_SIZE_SHIFT = 28, - - /* Only for v7.1+ (with CFG_EXT register) */ - CFG_EXT_PAGE_SIZE_SHIFT = 0, - CFG_EXT_BLK_SIZE_SHIFT = 4, -}; - -/* BRCMNAND_INTFC_STATUS */ -enum { - INTFC_FLASH_STATUS = GENMASK(7, 0), - - INTFC_ERASED = BIT(27), - INTFC_OOB_VALID = BIT(28), - INTFC_CACHE_VALID = BIT(29), - INTFC_FLASH_READY = BIT(30), - INTFC_CTLR_READY = BIT(31), -}; - -static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) -{ - return brcmnand_readl(ctrl->nand_base + offs); -} - -static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs, - u32 val) -{ - brcmnand_writel(val, ctrl->nand_base + offs); -} - -static int brcmnand_revision_init(struct brcmnand_controller *ctrl) -{ - static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 }; - static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 }; - static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 }; - - ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff; - - /* Only support v4.0+? */ - if (ctrl->nand_version < 0x0400) { - dev_err(ctrl->dev, "version %#x not supported\n", - ctrl->nand_version); - return -ENODEV; - } - - /* Register offsets */ - if (ctrl->nand_version >= 0x0702) - ctrl->reg_offsets = brcmnand_regs_v72; - else if (ctrl->nand_version >= 0x0701) - ctrl->reg_offsets = brcmnand_regs_v71; - else if (ctrl->nand_version >= 0x0600) - ctrl->reg_offsets = brcmnand_regs_v60; - else if (ctrl->nand_version >= 0x0500) - ctrl->reg_offsets = brcmnand_regs_v50; - else if (ctrl->nand_version >= 0x0400) - ctrl->reg_offsets = brcmnand_regs_v40; - - /* Chip-select stride */ - if (ctrl->nand_version >= 0x0701) - ctrl->reg_spacing = 0x14; - else - ctrl->reg_spacing = 0x10; - - /* Per chip-select registers */ - if (ctrl->nand_version >= 0x0701) { - ctrl->cs_offsets = brcmnand_cs_offsets_v71; - } else { - ctrl->cs_offsets = brcmnand_cs_offsets; - - /* v5.0 and earlier has a different CS0 offset layout */ - if (ctrl->nand_version <= 0x0500) - ctrl->cs0_offsets = brcmnand_cs_offsets_cs0; - } - - /* Page / block sizes */ - if (ctrl->nand_version >= 0x0701) { - /* >= v7.1 use nice power-of-2 values! */ - ctrl->max_page_size = 16 * 1024; - ctrl->max_block_size = 2 * 1024 * 1024; - } else { - ctrl->page_sizes = page_sizes; - if (ctrl->nand_version >= 0x0600) - ctrl->block_sizes = block_sizes_v6; - else - ctrl->block_sizes = block_sizes_v4; - - if (ctrl->nand_version < 0x0400) { - ctrl->max_page_size = 4096; - ctrl->max_block_size = 512 * 1024; - } - } - - /* Maximum spare area sector size (per 512B) */ - if (ctrl->nand_version >= 0x0702) - ctrl->max_oob = 128; - else if (ctrl->nand_version >= 0x0600) - ctrl->max_oob = 64; - else if (ctrl->nand_version >= 0x0500) - ctrl->max_oob = 32; - else - ctrl->max_oob = 16; - - /* v6.0 and newer (except v6.1) have prefetch support */ - if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601) - ctrl->features |= BRCMNAND_HAS_PREFETCH; - - /* - * v6.x has cache mode, but it's implemented differently. Ignore it for - * now. - */ - if (ctrl->nand_version >= 0x0700) - ctrl->features |= BRCMNAND_HAS_CACHE_MODE; - - if (ctrl->nand_version >= 0x0500) - ctrl->features |= BRCMNAND_HAS_1K_SECTORS; - - if (ctrl->nand_version >= 0x0700) - ctrl->features |= BRCMNAND_HAS_WP; - else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp")) - ctrl->features |= BRCMNAND_HAS_WP; - - return 0; -} - -static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl, - enum brcmnand_reg reg) -{ - u16 offs = ctrl->reg_offsets[reg]; - - if (offs) - return nand_readreg(ctrl, offs); - else - return 0; -} - -static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl, - enum brcmnand_reg reg, u32 val) -{ - u16 offs = ctrl->reg_offsets[reg]; - - if (offs) - nand_writereg(ctrl, offs, val); -} - -static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl, - enum brcmnand_reg reg, u32 mask, unsigned - int shift, u32 val) -{ - u32 tmp = brcmnand_read_reg(ctrl, reg); - - tmp &= ~mask; - tmp |= val << shift; - brcmnand_write_reg(ctrl, reg, tmp); -} - -static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word) -{ - return __raw_readl(ctrl->nand_fc + word * 4); -} - -static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, - int word, u32 val) -{ - __raw_writel(val, ctrl->nand_fc + word * 4); -} - -static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, - enum brcmnand_cs_reg reg) -{ - u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE]; - u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE]; - u8 cs_offs; - - if (cs == 0 && ctrl->cs0_offsets) - cs_offs = ctrl->cs0_offsets[reg]; - else - cs_offs = ctrl->cs_offsets[reg]; - - if (cs && offs_cs1) - return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs; - - return offs_cs0 + cs * ctrl->reg_spacing + cs_offs; -} - -static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl) -{ - if (ctrl->nand_version < 0x0600) - return 1; - return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT); -} - -static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) -{ - struct brcmnand_controller *ctrl = host->ctrl; - unsigned int shift = 0, bits; - enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; - int cs = host->cs; - - if (ctrl->nand_version >= 0x0702) - bits = 7; - else if (ctrl->nand_version >= 0x0600) - bits = 6; - else if (ctrl->nand_version >= 0x0500) - bits = 5; - else - bits = 4; - - if (ctrl->nand_version >= 0x0702) { - if (cs >= 4) - reg = BRCMNAND_CORR_THRESHOLD_EXT; - shift = (cs % 4) * bits; - } else if (ctrl->nand_version >= 0x0600) { - if (cs >= 5) - reg = BRCMNAND_CORR_THRESHOLD_EXT; - shift = (cs % 5) * bits; - } - brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val); -} - -static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) -{ - if (ctrl->nand_version < 0x0602) - return 24; - return 0; -} - -/*********************************************************************** - * NAND ACC CONTROL bitfield - * - * Some bits have remained constant throughout hardware revision, while - * others have shifted around. - ***********************************************************************/ - -/* Constant for all versions (where supported) */ -enum { - /* See BRCMNAND_HAS_CACHE_MODE */ - ACC_CONTROL_CACHE_MODE = BIT(22), - - /* See BRCMNAND_HAS_PREFETCH */ - ACC_CONTROL_PREFETCH = BIT(23), - - ACC_CONTROL_PAGE_HIT = BIT(24), - ACC_CONTROL_WR_PREEMPT = BIT(25), - ACC_CONTROL_PARTIAL_PAGE = BIT(26), - ACC_CONTROL_RD_ERASED = BIT(27), - ACC_CONTROL_FAST_PGM_RDIN = BIT(28), - ACC_CONTROL_WR_ECC = BIT(30), - ACC_CONTROL_RD_ECC = BIT(31), -}; - -static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) -{ - if (ctrl->nand_version >= 0x0702) - return GENMASK(7, 0); - else if (ctrl->nand_version >= 0x0600) - return GENMASK(6, 0); - else - return GENMASK(5, 0); -} - -#define NAND_ACC_CONTROL_ECC_SHIFT 16 -#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13 - -static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl) -{ - u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f; - - mask <<= NAND_ACC_CONTROL_ECC_SHIFT; - - /* v7.2 includes additional ECC levels */ - if (ctrl->nand_version >= 0x0702) - mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT; - - return mask; -} - -static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) -{ - struct brcmnand_controller *ctrl = host->ctrl; - u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); - u32 acc_control = nand_readreg(ctrl, offs); - u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC; - - if (en) { - acc_control |= ecc_flags; /* enable RD/WR ECC */ - acc_control |= host->hwcfg.ecc_level - << NAND_ACC_CONTROL_ECC_SHIFT; - } else { - acc_control &= ~ecc_flags; /* disable RD/WR ECC */ - acc_control &= ~brcmnand_ecc_level_mask(ctrl); - } - - nand_writereg(ctrl, offs, acc_control); -} - -static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl) -{ - if (ctrl->nand_version >= 0x0702) - return 9; - else if (ctrl->nand_version >= 0x0600) - return 7; - else if (ctrl->nand_version >= 0x0500) - return 6; - else - return -1; -} - -static int brcmnand_get_sector_size_1k(struct brcmnand_host *host) -{ - struct brcmnand_controller *ctrl = host->ctrl; - int shift = brcmnand_sector_1k_shift(ctrl); - u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, - BRCMNAND_CS_ACC_CONTROL); - - if (shift < 0) - return 0; - - return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1; -} - -static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val) -{ - struct brcmnand_controller *ctrl = host->ctrl; - int shift = brcmnand_sector_1k_shift(ctrl); - u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, - BRCMNAND_CS_ACC_CONTROL); - u32 tmp; - - if (shift < 0) - return; - - tmp = nand_readreg(ctrl, acc_control_offs); - tmp &= ~(1 << shift); - tmp |= (!!val) << shift; - nand_writereg(ctrl, acc_control_offs, tmp); -} - -/*********************************************************************** - * CS_NAND_SELECT - ***********************************************************************/ - -enum { - CS_SELECT_NAND_WP = BIT(29), - CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30), -}; - -static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl, - u32 mask, u32 expected_val, - unsigned long timeout_ms) -{ - unsigned long limit; - u32 val; - - if (!timeout_ms) - timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS; - - limit = jiffies + msecs_to_jiffies(timeout_ms); - do { - val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); - if ((val & mask) == expected_val) - return 0; - - cpu_relax(); - } while (time_after(limit, jiffies)); - - dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n", - expected_val, val & mask); - - return -ETIMEDOUT; -} - -static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) -{ - u32 val = en ? CS_SELECT_NAND_WP : 0; - - brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val); -} - -/*********************************************************************** - * Flash DMA - ***********************************************************************/ - -enum flash_dma_reg { - FLASH_DMA_REVISION = 0x00, - FLASH_DMA_FIRST_DESC = 0x04, - FLASH_DMA_FIRST_DESC_EXT = 0x08, - FLASH_DMA_CTRL = 0x0c, - FLASH_DMA_MODE = 0x10, - FLASH_DMA_STATUS = 0x14, - FLASH_DMA_INTERRUPT_DESC = 0x18, - FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c, - FLASH_DMA_ERROR_STATUS = 0x20, - FLASH_DMA_CURRENT_DESC = 0x24, - FLASH_DMA_CURRENT_DESC_EXT = 0x28, -}; - -static inline bool has_flash_dma(struct brcmnand_controller *ctrl) -{ - return ctrl->flash_dma_base; -} - -static inline bool flash_dma_buf_ok(const void *buf) -{ - return buf && !is_vmalloc_addr(buf) && - likely(IS_ALIGNED((uintptr_t)buf, 4)); -} - -static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs, - u32 val) -{ - brcmnand_writel(val, ctrl->flash_dma_base + offs); -} - -static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs) -{ - return brcmnand_readl(ctrl->flash_dma_base + offs); -} - -/* Low-level operation types: command, address, write, or read */ -enum brcmnand_llop_type { - LL_OP_CMD, - LL_OP_ADDR, - LL_OP_WR, - LL_OP_RD, -}; - -/*********************************************************************** - * Internal support functions - ***********************************************************************/ - -static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl, - struct brcmnand_cfg *cfg) -{ - if (ctrl->nand_version <= 0x0701) - return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 && - cfg->ecc_level == 15; - else - return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 && - cfg->ecc_level == 15) || - (cfg->spare_area_size == 28 && cfg->ecc_level == 16)); -} - -/* - * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given - * the layout/configuration. - * Returns -ERRCODE on failure. - */ -static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_cfg *cfg = &host->hwcfg; - int sas = cfg->spare_area_size << cfg->sector_size_1k; - int sectors = cfg->page_size / (512 << cfg->sector_size_1k); - - if (section >= sectors) - return -ERANGE; - - oobregion->offset = (section * sas) + 6; - oobregion->length = 3; - - return 0; -} - -static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_cfg *cfg = &host->hwcfg; - int sas = cfg->spare_area_size << cfg->sector_size_1k; - int sectors = cfg->page_size / (512 << cfg->sector_size_1k); - - if (section >= sectors * 2) - return -ERANGE; - - oobregion->offset = (section / 2) * sas; - - if (section & 1) { - oobregion->offset += 9; - oobregion->length = 7; - } else { - oobregion->length = 6; - - /* First sector of each page may have BBI */ - if (!section) { - /* - * Small-page NAND use byte 6 for BBI while large-page - * NAND use byte 0. - */ - if (cfg->page_size > 512) - oobregion->offset++; - oobregion->length--; - } - } - - return 0; -} - -static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = { - .ecc = brcmnand_hamming_ooblayout_ecc, - .free = brcmnand_hamming_ooblayout_free, -}; - -static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_cfg *cfg = &host->hwcfg; - int sas = cfg->spare_area_size << cfg->sector_size_1k; - int sectors = cfg->page_size / (512 << cfg->sector_size_1k); - - if (section >= sectors) - return -ERANGE; - - oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; - oobregion->length = chip->ecc.bytes; - - return 0; -} - -static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_cfg *cfg = &host->hwcfg; - int sas = cfg->spare_area_size << cfg->sector_size_1k; - int sectors = cfg->page_size / (512 << cfg->sector_size_1k); - - if (section >= sectors) - return -ERANGE; - - if (sas <= chip->ecc.bytes) - return 0; - - oobregion->offset = section * sas; - oobregion->length = sas - chip->ecc.bytes; - - if (!section) { - oobregion->offset++; - oobregion->length--; - } - - return 0; -} - -static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_cfg *cfg = &host->hwcfg; - int sas = cfg->spare_area_size << cfg->sector_size_1k; - - if (section > 1 || sas - chip->ecc.bytes < 6 || - (section && sas - chip->ecc.bytes == 6)) - return -ERANGE; - - if (!section) { - oobregion->offset = 0; - oobregion->length = 5; - } else { - oobregion->offset = 6; - oobregion->length = sas - chip->ecc.bytes - 6; - } - - return 0; -} - -static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = { - .ecc = brcmnand_bch_ooblayout_ecc, - .free = brcmnand_bch_ooblayout_free_lp, -}; - -static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = { - .ecc = brcmnand_bch_ooblayout_ecc, - .free = brcmnand_bch_ooblayout_free_sp, -}; - -static int brcmstb_choose_ecc_layout(struct brcmnand_host *host) -{ - struct brcmnand_cfg *p = &host->hwcfg; - struct mtd_info *mtd = nand_to_mtd(&host->chip); - struct nand_ecc_ctrl *ecc = &host->chip.ecc; - unsigned int ecc_level = p->ecc_level; - int sas = p->spare_area_size << p->sector_size_1k; - int sectors = p->page_size / (512 << p->sector_size_1k); - - if (p->sector_size_1k) - ecc_level <<= 1; - - if (is_hamming_ecc(host->ctrl, p)) { - ecc->bytes = 3 * sectors; - mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops); - return 0; - } - - /* - * CONTROLLER_VERSION: - * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) - * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) - * But we will just be conservative. - */ - ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8); - if (p->page_size == 512) - mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops); - else - mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops); - - if (ecc->bytes >= sas) { - dev_err(&host->pdev->dev, - "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", - ecc->bytes, sas); - return -EINVAL; - } - - return 0; -} - -static void brcmnand_wp(struct mtd_info *mtd, int wp) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_controller *ctrl = host->ctrl; - - if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) { - static int old_wp = -1; - int ret; - - if (old_wp != wp) { - dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off"); - old_wp = wp; - } - - /* - * make sure ctrl/flash ready before and after - * changing state of #WP pin - */ - ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY | - NAND_STATUS_READY, - NAND_CTRL_RDY | - NAND_STATUS_READY, 0); - if (ret) - return; - - brcmnand_set_wp(ctrl, wp); - nand_status_op(chip, NULL); - /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */ - ret = bcmnand_ctrl_poll_status(ctrl, - NAND_CTRL_RDY | - NAND_STATUS_READY | - NAND_STATUS_WP, - NAND_CTRL_RDY | - NAND_STATUS_READY | - (wp ? 0 : NAND_STATUS_WP), 0); - - if (ret) - dev_err_ratelimited(&host->pdev->dev, - "nand #WP expected %s\n", - wp ? "on" : "off"); - } -} - -/* Helper functions for reading and writing OOB registers */ -static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs) -{ - u16 offset0, offset10, reg_offs; - - offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE]; - offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE]; - - if (offs >= ctrl->max_oob) - return 0x77; - - if (offs >= 16 && offset10) - reg_offs = offset10 + ((offs - 0x10) & ~0x03); - else - reg_offs = offset0 + (offs & ~0x03); - - return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3)); -} - -static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs, - u32 data) -{ - u16 offset0, offset10, reg_offs; - - offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE]; - offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE]; - - if (offs >= ctrl->max_oob) - return; - - if (offs >= 16 && offset10) - reg_offs = offset10 + ((offs - 0x10) & ~0x03); - else - reg_offs = offset0 + (offs & ~0x03); - - nand_writereg(ctrl, reg_offs, data); -} - -/* - * read_oob_from_regs - read data from OOB registers - * @ctrl: NAND controller - * @i: sub-page sector index - * @oob: buffer to read to - * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) - * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal - */ -static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob, - int sas, int sector_1k) -{ - int tbytes = sas << sector_1k; - int j; - - /* Adjust OOB values for 1K sector size */ - if (sector_1k && (i & 0x01)) - tbytes = max(0, tbytes - (int)ctrl->max_oob); - tbytes = min_t(int, tbytes, ctrl->max_oob); - - for (j = 0; j < tbytes; j++) - oob[j] = oob_reg_read(ctrl, j); - return tbytes; -} - -/* - * write_oob_to_regs - write data to OOB registers - * @i: sub-page sector index - * @oob: buffer to write from - * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) - * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal - */ -static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, - const u8 *oob, int sas, int sector_1k) -{ - int tbytes = sas << sector_1k; - int j; - - /* Adjust OOB values for 1K sector size */ - if (sector_1k && (i & 0x01)) - tbytes = max(0, tbytes - (int)ctrl->max_oob); - tbytes = min_t(int, tbytes, ctrl->max_oob); - - for (j = 0; j < tbytes; j += 4) - oob_reg_write(ctrl, j, - (oob[j + 0] << 24) | - (oob[j + 1] << 16) | - (oob[j + 2] << 8) | - (oob[j + 3] << 0)); - return tbytes; -} - -static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) -{ - struct brcmnand_controller *ctrl = data; - - /* Discard all NAND_CTLRDY interrupts during DMA */ - if (ctrl->dma_pending) - return IRQ_HANDLED; - - complete(&ctrl->done); - return IRQ_HANDLED; -} - -/* Handle SoC-specific interrupt hardware */ -static irqreturn_t brcmnand_irq(int irq, void *data) -{ - struct brcmnand_controller *ctrl = data; - - if (ctrl->soc->ctlrdy_ack(ctrl->soc)) - return brcmnand_ctlrdy_irq(irq, data); - - return IRQ_NONE; -} - -static irqreturn_t brcmnand_dma_irq(int irq, void *data) -{ - struct brcmnand_controller *ctrl = data; - - complete(&ctrl->dma_done); - - return IRQ_HANDLED; -} - -static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) -{ - struct brcmnand_controller *ctrl = host->ctrl; - int ret; - - dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, - brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); - BUG_ON(ctrl->cmd_pending != 0); - ctrl->cmd_pending = cmd; - - ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); - WARN_ON(ret); - - mb(); /* flush previous writes */ - brcmnand_write_reg(ctrl, BRCMNAND_CMD_START, - cmd << brcmnand_cmd_shift(ctrl)); -} - -/*********************************************************************** - * NAND MTD API: read/program/erase - ***********************************************************************/ - -static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat, - unsigned int ctrl) -{ - /* intentionally left blank */ -} - -static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_controller *ctrl = host->ctrl; - unsigned long timeo = msecs_to_jiffies(100); - - dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); - if (ctrl->cmd_pending && - wait_for_completion_timeout(&ctrl->done, timeo) <= 0) { - u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) - >> brcmnand_cmd_shift(ctrl); - - dev_err_ratelimited(ctrl->dev, - "timeout waiting for command %#02x\n", cmd); - dev_err_ratelimited(ctrl->dev, "intfc status %08x\n", - brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)); - } - ctrl->cmd_pending = 0; - return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & - INTFC_FLASH_STATUS; -} - -enum { - LLOP_RE = BIT(16), - LLOP_WE = BIT(17), - LLOP_ALE = BIT(18), - LLOP_CLE = BIT(19), - LLOP_RETURN_IDLE = BIT(31), - - LLOP_DATA_MASK = GENMASK(15, 0), -}; - -static int brcmnand_low_level_op(struct brcmnand_host *host, - enum brcmnand_llop_type type, u32 data, - bool last_op) -{ - struct mtd_info *mtd = nand_to_mtd(&host->chip); - struct nand_chip *chip = &host->chip; - struct brcmnand_controller *ctrl = host->ctrl; - u32 tmp; - - tmp = data & LLOP_DATA_MASK; - switch (type) { - case LL_OP_CMD: - tmp |= LLOP_WE | LLOP_CLE; - break; - case LL_OP_ADDR: - /* WE | ALE */ - tmp |= LLOP_WE | LLOP_ALE; - break; - case LL_OP_WR: - /* WE */ - tmp |= LLOP_WE; - break; - case LL_OP_RD: - /* RE */ - tmp |= LLOP_RE; - break; - } - if (last_op) - /* RETURN_IDLE */ - tmp |= LLOP_RETURN_IDLE; - - dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp); - - brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp); - (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP); - - brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP); - return brcmnand_waitfunc(mtd, chip); -} - -static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_controller *ctrl = host->ctrl; - u64 addr = (u64)page_addr << chip->page_shift; - int native_cmd = 0; - - if (command == NAND_CMD_READID || command == NAND_CMD_PARAM || - command == NAND_CMD_RNDOUT) - addr = (u64)column; - /* Avoid propagating a negative, don't-care address */ - else if (page_addr < 0) - addr = 0; - - dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command, - (unsigned long long)addr); - - host->last_cmd = command; - host->last_byte = 0; - host->last_addr = addr; - - switch (command) { - case NAND_CMD_RESET: - native_cmd = CMD_FLASH_RESET; - break; - case NAND_CMD_STATUS: - native_cmd = CMD_STATUS_READ; - break; - case NAND_CMD_READID: - native_cmd = CMD_DEVICE_ID_READ; - break; - case NAND_CMD_READOOB: - native_cmd = CMD_SPARE_AREA_READ; - break; - case NAND_CMD_ERASE1: - native_cmd = CMD_BLOCK_ERASE; - brcmnand_wp(mtd, 0); - break; - case NAND_CMD_PARAM: - native_cmd = CMD_PARAMETER_READ; - break; - case NAND_CMD_SET_FEATURES: - case NAND_CMD_GET_FEATURES: - brcmnand_low_level_op(host, LL_OP_CMD, command, false); - brcmnand_low_level_op(host, LL_OP_ADDR, column, false); - break; - case NAND_CMD_RNDOUT: - native_cmd = CMD_PARAMETER_CHANGE_COL; - addr &= ~((u64)(FC_BYTES - 1)); - /* - * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0 - * NB: hwcfg.sector_size_1k may not be initialized yet - */ - if (brcmnand_get_sector_size_1k(host)) { - host->hwcfg.sector_size_1k = - brcmnand_get_sector_size_1k(host); - brcmnand_set_sector_size_1k(host, 0); - } - break; - } - - if (!native_cmd) - return; - - brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, - (host->cs << 16) | ((addr >> 32) & 0xffff)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); - brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); - - brcmnand_send_cmd(host, native_cmd); - brcmnand_waitfunc(mtd, chip); - - if (native_cmd == CMD_PARAMETER_READ || - native_cmd == CMD_PARAMETER_CHANGE_COL) { - /* Copy flash cache word-wise */ - u32 *flash_cache = (u32 *)ctrl->flash_cache; - int i; - - brcmnand_soc_data_bus_prepare(ctrl->soc, true); - - /* - * Must cache the FLASH_CACHE now, since changes in - * SECTOR_SIZE_1K may invalidate it - */ - for (i = 0; i < FC_WORDS; i++) - /* - * Flash cache is big endian for parameter pages, at - * least on STB SoCs - */ - flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i)); - - brcmnand_soc_data_bus_unprepare(ctrl->soc, true); - - /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ - if (host->hwcfg.sector_size_1k) - brcmnand_set_sector_size_1k(host, - host->hwcfg.sector_size_1k); - } - - /* Re-enable protection is necessary only after erase */ - if (command == NAND_CMD_ERASE1) - brcmnand_wp(mtd, 1); -} - -static uint8_t brcmnand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_controller *ctrl = host->ctrl; - uint8_t ret = 0; - int addr, offs; - - switch (host->last_cmd) { - case NAND_CMD_READID: - if (host->last_byte < 4) - ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >> - (24 - (host->last_byte << 3)); - else if (host->last_byte < 8) - ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >> - (56 - (host->last_byte << 3)); - break; - - case NAND_CMD_READOOB: - ret = oob_reg_read(ctrl, host->last_byte); - break; - - case NAND_CMD_STATUS: - ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & - INTFC_FLASH_STATUS; - if (wp_on) /* hide WP status */ - ret |= NAND_STATUS_WP; - break; - - case NAND_CMD_PARAM: - case NAND_CMD_RNDOUT: - addr = host->last_addr + host->last_byte; - offs = addr & (FC_BYTES - 1); - - /* At FC_BYTES boundary, switch to next column */ - if (host->last_byte > 0 && offs == 0) - nand_change_read_column_op(chip, addr, NULL, 0, false); - - ret = ctrl->flash_cache[offs]; - break; - case NAND_CMD_GET_FEATURES: - if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) { - ret = 0; - } else { - bool last = host->last_byte == - ONFI_SUBFEATURE_PARAM_LEN - 1; - brcmnand_low_level_op(host, LL_OP_RD, 0, last); - ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff; - } - } - - dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret); - host->last_byte++; - - return ret; -} - -static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++, buf++) - *buf = brcmnand_read_byte(mtd); -} - -static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - int i; - struct nand_chip *chip = mtd_to_nand(mtd); - struct brcmnand_host *host = nand_get_controller_data(chip); - - switch (host->last_cmd) { - case NAND_CMD_SET_FEATURES: - for (i = 0; i < len; i++) - brcmnand_low_level_op(host, LL_OP_WR, buf[i], - (i + 1) == len); - break; - default: - BUG(); - break; - } -} - -/** - * Construct a FLASH_DMA descriptor as part of a linked list. You must know the - * following ahead of time: - * - Is this descriptor the beginning or end of a linked list? - * - What is the (DMA) address of the next descriptor in the linked list? - */ -static int brcmnand_fill_dma_desc(struct brcmnand_host *host, - struct brcm_nand_dma_desc *desc, u64 addr, - dma_addr_t buf, u32 len, u8 dma_cmd, - bool begin, bool end, - dma_addr_t next_desc) -{ - memset(desc, 0, sizeof(*desc)); - /* Descriptors are written in native byte order (wordwise) */ - desc->next_desc = lower_32_bits(next_desc); - desc->next_desc_ext = upper_32_bits(next_desc); - desc->cmd_irq = (dma_cmd << 24) | - (end ? (0x03 << 8) : 0) | /* IRQ | STOP */ - (!!begin) | ((!!end) << 1); /* head, tail */ -#ifdef CONFIG_CPU_BIG_ENDIAN - desc->cmd_irq |= 0x01 << 12; -#endif - desc->dram_addr = lower_32_bits(buf); - desc->dram_addr_ext = upper_32_bits(buf); - desc->tfr_len = len; - desc->total_len = len; - desc->flash_addr = lower_32_bits(addr); - desc->flash_addr_ext = upper_32_bits(addr); - desc->cs = host->cs; - desc->status_valid = 0x01; - return 0; -} - -/** - * Kick the FLASH_DMA engine, with a given DMA descriptor - */ -static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) -{ - struct brcmnand_controller *ctrl = host->ctrl; - unsigned long timeo = msecs_to_jiffies(100); - - flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); - (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); - flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); - (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); - - /* Start FLASH_DMA engine */ - ctrl->dma_pending = true; - mb(); /* flush previous writes */ - flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */ - - if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) { - dev_err(ctrl->dev, - "timeout waiting for DMA; status %#x, error status %#x\n", - flash_dma_readl(ctrl, FLASH_DMA_STATUS), - flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS)); - } - ctrl->dma_pending = false; - flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */ -} - -static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf, - u32 len, u8 dma_cmd) -{ - struct brcmnand_controller *ctrl = host->ctrl; - dma_addr_t buf_pa; - int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - - buf_pa = dma_map_single(ctrl->dev, buf, len, dir); - if (dma_mapping_error(ctrl->dev, buf_pa)) { - dev_err(ctrl->dev, "unable to map buffer for DMA\n"); - return -ENOMEM; - } - - brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len, - dma_cmd, true, true, 0); - - brcmnand_dma_run(host, ctrl->dma_pa); - - dma_unmap_single(ctrl->dev, buf_pa, len, dir); - - if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR) - return -EBADMSG; - else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR) - return -EUCLEAN; - - return 0; -} - -/* - * Assumes proper CS is already set - */ -static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, - u64 addr, unsigned int trans, u32 *buf, - u8 *oob, u64 *err_addr) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_controller *ctrl = host->ctrl; - int i, j, ret = 0; - - /* Clear error addresses */ - brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); - brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); - brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0); - brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0); - - brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, - (host->cs << 16) | ((addr >> 32) & 0xffff)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); - - for (i = 0; i < trans; i++, addr += FC_BYTES) { - brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, - lower_32_bits(addr)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); - /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ - brcmnand_send_cmd(host, CMD_PAGE_READ); - brcmnand_waitfunc(mtd, chip); - - if (likely(buf)) { - brcmnand_soc_data_bus_prepare(ctrl->soc, false); - - for (j = 0; j < FC_WORDS; j++, buf++) - *buf = brcmnand_read_fc(ctrl, j); - - brcmnand_soc_data_bus_unprepare(ctrl->soc, false); - } - - if (oob) - oob += read_oob_from_regs(ctrl, i, oob, - mtd->oobsize / trans, - host->hwcfg.sector_size_1k); - - if (!ret) { - *err_addr = brcmnand_read_reg(ctrl, - BRCMNAND_UNCORR_ADDR) | - ((u64)(brcmnand_read_reg(ctrl, - BRCMNAND_UNCORR_EXT_ADDR) - & 0xffff) << 32); - if (*err_addr) - ret = -EBADMSG; - } - - if (!ret) { - *err_addr = brcmnand_read_reg(ctrl, - BRCMNAND_CORR_ADDR) | - ((u64)(brcmnand_read_reg(ctrl, - BRCMNAND_CORR_EXT_ADDR) - & 0xffff) << 32); - if (*err_addr) - ret = -EUCLEAN; - } - } - - return ret; -} - -/* - * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC - * error - * - * Because the HW ECC signals an ECC error if an erase paged has even a single - * bitflip, we must check each ECC error to see if it is actually an erased - * page with bitflips, not a truly corrupted page. - * - * On a real error, return a negative error code (-EBADMSG for ECC error), and - * buf will contain raw data. - * Otherwise, buf gets filled with 0xffs and return the maximum number of - * bitflips-per-ECC-sector to the caller. - * - */ -static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, - struct nand_chip *chip, void *buf, u64 addr) -{ - int i, sas; - void *oob = chip->oob_poi; - int bitflips = 0; - int page = addr >> chip->page_shift; - int ret; - - if (!buf) { - buf = chip->data_buf; - /* Invalidate page cache */ - chip->pagebuf = -1; - } - - sas = mtd->oobsize / chip->ecc.steps; - - /* read without ecc for verification */ - ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page); - if (ret) - return ret; - - for (i = 0; i < chip->ecc.steps; i++, oob += sas) { - ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, - oob, sas, NULL, 0, - chip->ecc.strength); - if (ret < 0) - return ret; - - bitflips = max(bitflips, ret); - } - - return bitflips; -} - -static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, - u64 addr, unsigned int trans, u32 *buf, u8 *oob) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_controller *ctrl = host->ctrl; - u64 err_addr = 0; - int err; - bool retry = true; - - dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); - -try_dmaread: - brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0); - - if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { - err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, - CMD_PAGE_READ); - if (err) { - if (mtd_is_bitflip_or_eccerr(err)) - err_addr = addr; - else - return -EIO; - } - } else { - if (oob) - memset(oob, 0x99, mtd->oobsize); - - err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, - oob, &err_addr); - } - - if (mtd_is_eccerr(err)) { - /* - * On controller version and 7.0, 7.1 , DMA read after a - * prior PIO read that reported uncorrectable error, - * the DMA engine captures this error following DMA read - * cleared only on subsequent DMA read, so just retry once - * to clear a possible false error reported for current DMA - * read - */ - if ((ctrl->nand_version == 0x0700) || - (ctrl->nand_version == 0x0701)) { - if (retry) { - retry = false; - goto try_dmaread; - } - } - - /* - * Controller version 7.2 has hw encoder to detect erased page - * bitflips, apply sw verification for older controllers only - */ - if (ctrl->nand_version < 0x0702) { - err = brcmstb_nand_verify_erased_page(mtd, chip, buf, - addr); - /* erased page bitflips corrected */ - if (err >= 0) - return err; - } - - dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n", - (unsigned long long)err_addr); - mtd->ecc_stats.failed++; - /* NAND layer expects zero on ECC errors */ - return 0; - } - - if (mtd_is_bitflip(err)) { - unsigned int corrected = brcmnand_count_corrected(ctrl); - - dev_dbg(ctrl->dev, "corrected error at 0x%llx\n", - (unsigned long long)err_addr); - mtd->ecc_stats.corrected += corrected; - /* Always exceed the software-imposed threshold */ - return max(mtd->bitflip_threshold, corrected); - } - - return 0; -} - -static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; - - nand_read_page_op(chip, page, 0, NULL, 0); - - return brcmnand_read(mtd, chip, host->last_addr, - mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); -} - -static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; - int ret; - - nand_read_page_op(chip, page, 0, NULL, 0); - - brcmnand_set_ecc_enabled(host, 0); - ret = brcmnand_read(mtd, chip, host->last_addr, - mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); - brcmnand_set_ecc_enabled(host, 1); - return ret; -} - -static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - return brcmnand_read(mtd, chip, (u64)page << chip->page_shift, - mtd->writesize >> FC_SHIFT, - NULL, (u8 *)chip->oob_poi); -} - -static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - - brcmnand_set_ecc_enabled(host, 0); - brcmnand_read(mtd, chip, (u64)page << chip->page_shift, - mtd->writesize >> FC_SHIFT, - NULL, (u8 *)chip->oob_poi); - brcmnand_set_ecc_enabled(host, 1); - return 0; -} - -static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, - u64 addr, const u32 *buf, u8 *oob) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_controller *ctrl = host->ctrl; - unsigned int i, j, trans = mtd->writesize >> FC_SHIFT; - int status, ret = 0; - - dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf); - - if (unlikely((unsigned long)buf & 0x03)) { - dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf); - buf = (u32 *)((unsigned long)buf & ~0x03); - } - - brcmnand_wp(mtd, 0); - - for (i = 0; i < ctrl->max_oob; i += 4) - oob_reg_write(ctrl, i, 0xffffffff); - - if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { - if (brcmnand_dma_trans(host, addr, (u32 *)buf, - mtd->writesize, CMD_PROGRAM_PAGE)) - ret = -EIO; - goto out; - } - - brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, - (host->cs << 16) | ((addr >> 32) & 0xffff)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); - - for (i = 0; i < trans; i++, addr += FC_BYTES) { - /* full address MUST be set before populating FC */ - brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, - lower_32_bits(addr)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); - - if (buf) { - brcmnand_soc_data_bus_prepare(ctrl->soc, false); - - for (j = 0; j < FC_WORDS; j++, buf++) - brcmnand_write_fc(ctrl, j, *buf); - - brcmnand_soc_data_bus_unprepare(ctrl->soc, false); - } else if (oob) { - for (j = 0; j < FC_WORDS; j++) - brcmnand_write_fc(ctrl, j, 0xffffffff); - } - - if (oob) { - oob += write_oob_to_regs(ctrl, i, oob, - mtd->oobsize / trans, - host->hwcfg.sector_size_1k); - } - - /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */ - brcmnand_send_cmd(host, CMD_PROGRAM_PAGE); - status = brcmnand_waitfunc(mtd, chip); - - if (status & NAND_STATUS_FAIL) { - dev_info(ctrl->dev, "program failed at %llx\n", - (unsigned long long)addr); - ret = -EIO; - goto out; - } - } -out: - brcmnand_wp(mtd, 1); - return ret; -} - -static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - void *oob = oob_required ? chip->oob_poi : NULL; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); - - return nand_prog_page_end_op(chip); -} - -static int brcmnand_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, - int oob_required, int page) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - void *oob = oob_required ? chip->oob_poi : NULL; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - brcmnand_set_ecc_enabled(host, 0); - brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); - brcmnand_set_ecc_enabled(host, 1); - - return nand_prog_page_end_op(chip); -} - -static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - return brcmnand_write(mtd, chip, (u64)page << chip->page_shift, - NULL, chip->oob_poi); -} - -static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - int ret; - - brcmnand_set_ecc_enabled(host, 0); - ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL, - (u8 *)chip->oob_poi); - brcmnand_set_ecc_enabled(host, 1); - - return ret; -} - -/*********************************************************************** - * Per-CS setup (1 NAND device) - ***********************************************************************/ - -static int brcmnand_set_cfg(struct brcmnand_host *host, - struct brcmnand_cfg *cfg) -{ - struct brcmnand_controller *ctrl = host->ctrl; - struct nand_chip *chip = &host->chip; - u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); - u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, - BRCMNAND_CS_CFG_EXT); - u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, - BRCMNAND_CS_ACC_CONTROL); - u8 block_size = 0, page_size = 0, device_size = 0; - u32 tmp; - - if (ctrl->block_sizes) { - int i, found; - - for (i = 0, found = 0; ctrl->block_sizes[i]; i++) - if (ctrl->block_sizes[i] * 1024 == cfg->block_size) { - block_size = i; - found = 1; - } - if (!found) { - dev_warn(ctrl->dev, "invalid block size %u\n", - cfg->block_size); - return -EINVAL; - } - } else { - block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE); - } - - if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size && - cfg->block_size > ctrl->max_block_size)) { - dev_warn(ctrl->dev, "invalid block size %u\n", - cfg->block_size); - block_size = 0; - } - - if (ctrl->page_sizes) { - int i, found; - - for (i = 0, found = 0; ctrl->page_sizes[i]; i++) - if (ctrl->page_sizes[i] == cfg->page_size) { - page_size = i; - found = 1; - } - if (!found) { - dev_warn(ctrl->dev, "invalid page size %u\n", - cfg->page_size); - return -EINVAL; - } - } else { - page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE); - } - - if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size && - cfg->page_size > ctrl->max_page_size)) { - dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size); - return -EINVAL; - } - - if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) { - dev_warn(ctrl->dev, "invalid device size 0x%llx\n", - (unsigned long long)cfg->device_size); - return -EINVAL; - } - device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE); - - tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) | - (cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) | - (cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) | - (!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) | - (device_size << CFG_DEVICE_SIZE_SHIFT); - if (cfg_offs == cfg_ext_offs) { - tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) | - (block_size << CFG_BLK_SIZE_SHIFT); - nand_writereg(ctrl, cfg_offs, tmp); - } else { - nand_writereg(ctrl, cfg_offs, tmp); - tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) | - (block_size << CFG_EXT_BLK_SIZE_SHIFT); - nand_writereg(ctrl, cfg_ext_offs, tmp); - } - - tmp = nand_readreg(ctrl, acc_control_offs); - tmp &= ~brcmnand_ecc_level_mask(ctrl); - tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; - tmp &= ~brcmnand_spare_area_mask(ctrl); - tmp |= cfg->spare_area_size; - nand_writereg(ctrl, acc_control_offs, tmp); - - brcmnand_set_sector_size_1k(host, cfg->sector_size_1k); - - /* threshold = ceil(BCH-level * 0.75) */ - brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4)); - - return 0; -} - -static void brcmnand_print_cfg(struct brcmnand_host *host, - char *buf, struct brcmnand_cfg *cfg) -{ - buf += sprintf(buf, - "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit", - (unsigned long long)cfg->device_size >> 20, - cfg->block_size >> 10, - cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size, - cfg->page_size >= 1024 ? "KiB" : "B", - cfg->spare_area_size, cfg->device_width); - - /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */ - if (is_hamming_ecc(host->ctrl, cfg)) - sprintf(buf, ", Hamming ECC"); - else if (cfg->sector_size_1k) - sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1); - else - sprintf(buf, ", BCH-%u", cfg->ecc_level); -} - -/* - * Minimum number of bytes to address a page. Calculated as: - * roundup(log2(size / page-size) / 8) - * - * NB: the following does not "round up" for non-power-of-2 'size'; but this is - * OK because many other things will break if 'size' is irregular... - */ -static inline int get_blk_adr_bytes(u64 size, u32 writesize) -{ - return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3; -} - -static int brcmnand_setup_dev(struct brcmnand_host *host) -{ - struct mtd_info *mtd = nand_to_mtd(&host->chip); - struct nand_chip *chip = &host->chip; - struct brcmnand_controller *ctrl = host->ctrl; - struct brcmnand_cfg *cfg = &host->hwcfg; - char msg[128]; - u32 offs, tmp, oob_sector; - int ret; - - memset(cfg, 0, sizeof(*cfg)); - - ret = of_property_read_u32(nand_get_flash_node(chip), - "brcm,nand-oob-sector-size", - &oob_sector); - if (ret) { - /* Use detected size */ - cfg->spare_area_size = mtd->oobsize / - (mtd->writesize >> FC_SHIFT); - } else { - cfg->spare_area_size = oob_sector; - } - if (cfg->spare_area_size > ctrl->max_oob) - cfg->spare_area_size = ctrl->max_oob; - /* - * Set oobsize to be consistent with controller's spare_area_size, as - * the rest is inaccessible. - */ - mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT); - - cfg->device_size = mtd->size; - cfg->block_size = mtd->erasesize; - cfg->page_size = mtd->writesize; - cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8; - cfg->col_adr_bytes = 2; - cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize); - - if (chip->ecc.mode != NAND_ECC_HW) { - dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n", - chip->ecc.mode); - return -EINVAL; - } - - if (chip->ecc.algo == NAND_ECC_UNKNOWN) { - if (chip->ecc.strength == 1 && chip->ecc.size == 512) - /* Default to Hamming for 1-bit ECC, if unspecified */ - chip->ecc.algo = NAND_ECC_HAMMING; - else - /* Otherwise, BCH */ - chip->ecc.algo = NAND_ECC_BCH; - } - - if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 || - chip->ecc.size != 512)) { - dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n", - chip->ecc.strength, chip->ecc.size); - return -EINVAL; - } - - switch (chip->ecc.size) { - case 512: - if (chip->ecc.algo == NAND_ECC_HAMMING) - cfg->ecc_level = 15; - else - cfg->ecc_level = chip->ecc.strength; - cfg->sector_size_1k = 0; - break; - case 1024: - if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) { - dev_err(ctrl->dev, "1KB sectors not supported\n"); - return -EINVAL; - } - if (chip->ecc.strength & 0x1) { - dev_err(ctrl->dev, - "odd ECC not supported with 1KB sectors\n"); - return -EINVAL; - } - - cfg->ecc_level = chip->ecc.strength >> 1; - cfg->sector_size_1k = 1; - break; - default: - dev_err(ctrl->dev, "unsupported ECC size: %d\n", - chip->ecc.size); - return -EINVAL; - } - - cfg->ful_adr_bytes = cfg->blk_adr_bytes; - if (mtd->writesize > 512) - cfg->ful_adr_bytes += cfg->col_adr_bytes; - else - cfg->ful_adr_bytes += 1; - - ret = brcmnand_set_cfg(host, cfg); - if (ret) - return ret; - - brcmnand_set_ecc_enabled(host, 1); - - brcmnand_print_cfg(host, msg, cfg); - dev_info(ctrl->dev, "detected %s\n", msg); - - /* Configure ACC_CONTROL */ - offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); - tmp = nand_readreg(ctrl, offs); - tmp &= ~ACC_CONTROL_PARTIAL_PAGE; - tmp &= ~ACC_CONTROL_RD_ERASED; - - /* We need to turn on Read from erased paged protected by ECC */ - if (ctrl->nand_version >= 0x0702) - tmp |= ACC_CONTROL_RD_ERASED; - tmp &= ~ACC_CONTROL_FAST_PGM_RDIN; - if (ctrl->features & BRCMNAND_HAS_PREFETCH) - tmp &= ~ACC_CONTROL_PREFETCH; - - nand_writereg(ctrl, offs, tmp); - - return 0; -} - -static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) -{ - struct brcmnand_controller *ctrl = host->ctrl; - struct platform_device *pdev = host->pdev; - struct mtd_info *mtd; - struct nand_chip *chip; - int ret; - u16 cfg_offs; - - ret = of_property_read_u32(dn, "reg", &host->cs); - if (ret) { - dev_err(&pdev->dev, "can't get chip-select\n"); - return -ENXIO; - } - - mtd = nand_to_mtd(&host->chip); - chip = &host->chip; - - nand_set_flash_node(chip, dn); - nand_set_controller_data(chip, host); - mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d", - host->cs); - if (!mtd->name) - return -ENOMEM; - - mtd->owner = THIS_MODULE; - mtd->dev.parent = &pdev->dev; - - chip->IO_ADDR_R = (void __iomem *)0xdeadbeef; - chip->IO_ADDR_W = (void __iomem *)0xdeadbeef; - - chip->cmd_ctrl = brcmnand_cmd_ctrl; - chip->cmdfunc = brcmnand_cmdfunc; - chip->waitfunc = brcmnand_waitfunc; - chip->read_byte = brcmnand_read_byte; - chip->read_buf = brcmnand_read_buf; - chip->write_buf = brcmnand_write_buf; - - chip->ecc.mode = NAND_ECC_HW; - chip->ecc.read_page = brcmnand_read_page; - chip->ecc.write_page = brcmnand_write_page; - chip->ecc.read_page_raw = brcmnand_read_page_raw; - chip->ecc.write_page_raw = brcmnand_write_page_raw; - chip->ecc.write_oob_raw = brcmnand_write_oob_raw; - chip->ecc.read_oob_raw = brcmnand_read_oob_raw; - chip->ecc.read_oob = brcmnand_read_oob; - chip->ecc.write_oob = brcmnand_write_oob; - - chip->controller = &ctrl->controller; - - /* - * The bootloader might have configured 16bit mode but - * NAND READID command only works in 8bit mode. We force - * 8bit mode here to ensure that NAND READID commands works. - */ - cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); - nand_writereg(ctrl, cfg_offs, - nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH); - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - chip->options |= NAND_NO_SUBPAGE_WRITE; - /* - * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA - * to/from, and have nand_base pass us a bounce buffer instead, as - * needed. - */ - chip->options |= NAND_USE_BOUNCE_BUFFER; - - if (chip->bbt_options & NAND_BBT_USE_FLASH) - chip->bbt_options |= NAND_BBT_NO_OOB; - - if (brcmnand_setup_dev(host)) - return -ENXIO; - - chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; - /* only use our internal HW threshold */ - mtd->bitflip_threshold = 1; - - ret = brcmstb_choose_ecc_layout(host); - if (ret) - return ret; - - ret = nand_scan_tail(mtd); - if (ret) - return ret; - - return mtd_device_register(mtd, NULL, 0); -} - -static void brcmnand_save_restore_cs_config(struct brcmnand_host *host, - int restore) -{ - struct brcmnand_controller *ctrl = host->ctrl; - u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); - u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, - BRCMNAND_CS_CFG_EXT); - u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, - BRCMNAND_CS_ACC_CONTROL); - u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1); - u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2); - - if (restore) { - nand_writereg(ctrl, cfg_offs, host->hwcfg.config); - if (cfg_offs != cfg_ext_offs) - nand_writereg(ctrl, cfg_ext_offs, - host->hwcfg.config_ext); - nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control); - nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1); - nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2); - } else { - host->hwcfg.config = nand_readreg(ctrl, cfg_offs); - if (cfg_offs != cfg_ext_offs) - host->hwcfg.config_ext = - nand_readreg(ctrl, cfg_ext_offs); - host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs); - host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs); - host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs); - } -} - -static int brcmnand_suspend(struct device *dev) -{ - struct brcmnand_controller *ctrl = dev_get_drvdata(dev); - struct brcmnand_host *host; - - list_for_each_entry(host, &ctrl->host_list, node) - brcmnand_save_restore_cs_config(host, 0); - - ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT); - ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR); - ctrl->corr_stat_threshold = - brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD); - - if (has_flash_dma(ctrl)) - ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE); - - return 0; -} - -static int brcmnand_resume(struct device *dev) -{ - struct brcmnand_controller *ctrl = dev_get_drvdata(dev); - struct brcmnand_host *host; - - if (has_flash_dma(ctrl)) { - flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode); - flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); - } - - brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select); - brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); - brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, - ctrl->corr_stat_threshold); - if (ctrl->soc) { - /* Clear/re-enable interrupt */ - ctrl->soc->ctlrdy_ack(ctrl->soc); - ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); - } - - list_for_each_entry(host, &ctrl->host_list, node) { - struct nand_chip *chip = &host->chip; - - brcmnand_save_restore_cs_config(host, 1); - - /* Reset the chip, required by some chips after power-up */ - nand_reset_op(chip); - } - - return 0; -} - -const struct dev_pm_ops brcmnand_pm_ops = { - .suspend = brcmnand_suspend, - .resume = brcmnand_resume, -}; -EXPORT_SYMBOL_GPL(brcmnand_pm_ops); - -static const struct of_device_id brcmnand_of_match[] = { - { .compatible = "brcm,brcmnand-v4.0" }, - { .compatible = "brcm,brcmnand-v5.0" }, - { .compatible = "brcm,brcmnand-v6.0" }, - { .compatible = "brcm,brcmnand-v6.1" }, - { .compatible = "brcm,brcmnand-v6.2" }, - { .compatible = "brcm,brcmnand-v7.0" }, - { .compatible = "brcm,brcmnand-v7.1" }, - { .compatible = "brcm,brcmnand-v7.2" }, - {}, -}; -MODULE_DEVICE_TABLE(of, brcmnand_of_match); - -/*********************************************************************** - * Platform driver setup (per controller) - ***********************************************************************/ - -int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) -{ - struct device *dev = &pdev->dev; - struct device_node *dn = dev->of_node, *child; - struct brcmnand_controller *ctrl; - struct resource *res; - int ret; - - /* We only support device-tree instantiation */ - if (!dn) - return -ENODEV; - - if (!of_match_node(brcmnand_of_match, dn)) - return -ENODEV; - - ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); - if (!ctrl) - return -ENOMEM; - - dev_set_drvdata(dev, ctrl); - ctrl->dev = dev; - - init_completion(&ctrl->done); - init_completion(&ctrl->dma_done); - nand_hw_control_init(&ctrl->controller); - INIT_LIST_HEAD(&ctrl->host_list); - - /* NAND register range */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctrl->nand_base = devm_ioremap_resource(dev, res); - if (IS_ERR(ctrl->nand_base)) - return PTR_ERR(ctrl->nand_base); - - /* Enable clock before using NAND registers */ - ctrl->clk = devm_clk_get(dev, "nand"); - if (!IS_ERR(ctrl->clk)) { - ret = clk_prepare_enable(ctrl->clk); - if (ret) - return ret; - } else { - ret = PTR_ERR(ctrl->clk); - if (ret == -EPROBE_DEFER) - return ret; - - ctrl->clk = NULL; - } - - /* Initialize NAND revision */ - ret = brcmnand_revision_init(ctrl); - if (ret) - goto err; - - /* - * Most chips have this cache at a fixed offset within 'nand' block. - * Some must specify this region separately. - */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache"); - if (res) { - ctrl->nand_fc = devm_ioremap_resource(dev, res); - if (IS_ERR(ctrl->nand_fc)) { - ret = PTR_ERR(ctrl->nand_fc); - goto err; - } - } else { - ctrl->nand_fc = ctrl->nand_base + - ctrl->reg_offsets[BRCMNAND_FC_BASE]; - } - - /* FLASH_DMA */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma"); - if (res) { - ctrl->flash_dma_base = devm_ioremap_resource(dev, res); - if (IS_ERR(ctrl->flash_dma_base)) { - ret = PTR_ERR(ctrl->flash_dma_base); - goto err; - } - - flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */ - flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); - - /* Allocate descriptor(s) */ - ctrl->dma_desc = dmam_alloc_coherent(dev, - sizeof(*ctrl->dma_desc), - &ctrl->dma_pa, GFP_KERNEL); - if (!ctrl->dma_desc) { - ret = -ENOMEM; - goto err; - } - - ctrl->dma_irq = platform_get_irq(pdev, 1); - if ((int)ctrl->dma_irq < 0) { - dev_err(dev, "missing FLASH_DMA IRQ\n"); - ret = -ENODEV; - goto err; - } - - ret = devm_request_irq(dev, ctrl->dma_irq, - brcmnand_dma_irq, 0, DRV_NAME, - ctrl); - if (ret < 0) { - dev_err(dev, "can't allocate IRQ %d: error %d\n", - ctrl->dma_irq, ret); - goto err; - } - - dev_info(dev, "enabling FLASH_DMA\n"); - } - - /* Disable automatic device ID config, direct addressing */ - brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, - CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0); - /* Disable XOR addressing */ - brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); - - if (ctrl->features & BRCMNAND_HAS_WP) { - /* Permanently disable write protection */ - if (wp_on == 2) - brcmnand_set_wp(ctrl, false); - } else { - wp_on = 0; - } - - /* IRQ */ - ctrl->irq = platform_get_irq(pdev, 0); - if ((int)ctrl->irq < 0) { - dev_err(dev, "no IRQ defined\n"); - ret = -ENODEV; - goto err; - } - - /* - * Some SoCs integrate this controller (e.g., its interrupt bits) in - * interesting ways - */ - if (soc) { - ctrl->soc = soc; - - ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0, - DRV_NAME, ctrl); - - /* Enable interrupt */ - ctrl->soc->ctlrdy_ack(ctrl->soc); - ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); - } else { - /* Use standard interrupt infrastructure */ - ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, - DRV_NAME, ctrl); - } - if (ret < 0) { - dev_err(dev, "can't allocate IRQ %d: error %d\n", - ctrl->irq, ret); - goto err; - } - - for_each_available_child_of_node(dn, child) { - if (of_device_is_compatible(child, "brcm,nandcs")) { - struct brcmnand_host *host; - - host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); - if (!host) { - of_node_put(child); - ret = -ENOMEM; - goto err; - } - host->pdev = pdev; - host->ctrl = ctrl; - - ret = brcmnand_init_cs(host, child); - if (ret) { - devm_kfree(dev, host); - continue; /* Try all chip-selects */ - } - - list_add_tail(&host->node, &ctrl->host_list); - } - } - - /* No chip-selects could initialize properly */ - if (list_empty(&ctrl->host_list)) { - ret = -ENODEV; - goto err; - } - - return 0; - -err: - clk_disable_unprepare(ctrl->clk); - return ret; - -} -EXPORT_SYMBOL_GPL(brcmnand_probe); - -int brcmnand_remove(struct platform_device *pdev) -{ - struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); - struct brcmnand_host *host; - - list_for_each_entry(host, &ctrl->host_list, node) - nand_release(nand_to_mtd(&host->chip)); - - clk_disable_unprepare(ctrl->clk); - - dev_set_drvdata(&pdev->dev, NULL); - - return 0; -} -EXPORT_SYMBOL_GPL(brcmnand_remove); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Kevin Cernekee"); -MODULE_AUTHOR("Brian Norris"); -MODULE_DESCRIPTION("NAND driver for Broadcom chips"); -MODULE_ALIAS("platform:brcmnand"); diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/brcmnand/brcmnand.h deleted file mode 100644 index 5c44cd4aba87..000000000000 --- a/drivers/mtd/nand/brcmnand/brcmnand.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright © 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __BRCMNAND_H__ -#define __BRCMNAND_H__ - -#include -#include - -struct platform_device; -struct dev_pm_ops; - -struct brcmnand_soc { - bool (*ctlrdy_ack)(struct brcmnand_soc *soc); - void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); - void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare, - bool is_param); -}; - -static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc, - bool is_param) -{ - if (soc && soc->prepare_data_bus) - soc->prepare_data_bus(soc, true, is_param); -} - -static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc, - bool is_param) -{ - if (soc && soc->prepare_data_bus) - soc->prepare_data_bus(soc, false, is_param); -} - -static inline u32 brcmnand_readl(void __iomem *addr) -{ - /* - * MIPS endianness is configured by boot strap, which also reverses all - * bus endianness (i.e., big-endian CPU + big endian bus ==> native - * endian I/O). - * - * Other architectures (e.g., ARM) either do not support big endian, or - * else leave I/O in little endian mode. - */ - if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) - return __raw_readl(addr); - else - return readl_relaxed(addr); -} - -static inline void brcmnand_writel(u32 val, void __iomem *addr) -{ - /* See brcmnand_readl() comments */ - if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) - __raw_writel(val, addr); - else - writel_relaxed(val, addr); -} - -int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc); -int brcmnand_remove(struct platform_device *pdev); - -extern const struct dev_pm_ops brcmnand_pm_ops; - -#endif /* __BRCMNAND_H__ */ diff --git a/drivers/mtd/nand/brcmnand/brcmstb_nand.c b/drivers/mtd/nand/brcmnand/brcmstb_nand.c deleted file mode 100644 index 5c271077ac87..000000000000 --- a/drivers/mtd/nand/brcmnand/brcmstb_nand.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include - -#include "brcmnand.h" - -static const struct of_device_id brcmstb_nand_of_match[] = { - { .compatible = "brcm,brcmnand" }, - {}, -}; -MODULE_DEVICE_TABLE(of, brcmstb_nand_of_match); - -static int brcmstb_nand_probe(struct platform_device *pdev) -{ - return brcmnand_probe(pdev, NULL); -} - -static struct platform_driver brcmstb_nand_driver = { - .probe = brcmstb_nand_probe, - .remove = brcmnand_remove, - .driver = { - .name = "brcmstb_nand", - .pm = &brcmnand_pm_ops, - .of_match_table = brcmstb_nand_of_match, - } -}; -module_platform_driver(brcmstb_nand_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Brian Norris"); -MODULE_DESCRIPTION("NAND driver for Broadcom STB chips"); diff --git a/drivers/mtd/nand/brcmnand/iproc_nand.c b/drivers/mtd/nand/brcmnand/iproc_nand.c deleted file mode 100644 index 4c6ae113664d..000000000000 --- a/drivers/mtd/nand/brcmnand/iproc_nand.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright © 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "brcmnand.h" - -struct iproc_nand_soc { - struct brcmnand_soc soc; - - void __iomem *idm_base; - void __iomem *ext_base; - spinlock_t idm_lock; -}; - -#define IPROC_NAND_CTLR_READY_OFFSET 0x10 -#define IPROC_NAND_CTLR_READY BIT(0) - -#define IPROC_NAND_IO_CTRL_OFFSET 0x00 -#define IPROC_NAND_APB_LE_MODE BIT(24) -#define IPROC_NAND_INT_CTRL_READ_ENABLE BIT(6) - -static bool iproc_nand_intc_ack(struct brcmnand_soc *soc) -{ - struct iproc_nand_soc *priv = - container_of(soc, struct iproc_nand_soc, soc); - void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET; - u32 val = brcmnand_readl(mmio); - - if (val & IPROC_NAND_CTLR_READY) { - brcmnand_writel(IPROC_NAND_CTLR_READY, mmio); - return true; - } - - return false; -} - -static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en) -{ - struct iproc_nand_soc *priv = - container_of(soc, struct iproc_nand_soc, soc); - void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; - u32 val; - unsigned long flags; - - spin_lock_irqsave(&priv->idm_lock, flags); - - val = brcmnand_readl(mmio); - - if (en) - val |= IPROC_NAND_INT_CTRL_READ_ENABLE; - else - val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE; - - brcmnand_writel(val, mmio); - - spin_unlock_irqrestore(&priv->idm_lock, flags); -} - -static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare, - bool is_param) -{ - struct iproc_nand_soc *priv = - container_of(soc, struct iproc_nand_soc, soc); - void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; - u32 val; - unsigned long flags; - - spin_lock_irqsave(&priv->idm_lock, flags); - - val = brcmnand_readl(mmio); - - /* - * In the case of BE or when dealing with NAND data, alway configure - * the APB bus to LE mode before accessing the FIFO and back to BE mode - * after the access is done - */ - if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) || !is_param) { - if (prepare) - val |= IPROC_NAND_APB_LE_MODE; - else - val &= ~IPROC_NAND_APB_LE_MODE; - } else { /* when in LE accessing the parameter page, keep APB in BE */ - val &= ~IPROC_NAND_APB_LE_MODE; - } - - brcmnand_writel(val, mmio); - - spin_unlock_irqrestore(&priv->idm_lock, flags); -} - -static int iproc_nand_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct iproc_nand_soc *priv; - struct brcmnand_soc *soc; - struct resource *res; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - soc = &priv->soc; - - spin_lock_init(&priv->idm_lock); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm"); - priv->idm_base = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->idm_base)) - return PTR_ERR(priv->idm_base); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext"); - priv->ext_base = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->ext_base)) - return PTR_ERR(priv->ext_base); - - soc->ctlrdy_ack = iproc_nand_intc_ack; - soc->ctlrdy_set_enabled = iproc_nand_intc_set; - soc->prepare_data_bus = iproc_nand_apb_access; - - return brcmnand_probe(pdev, soc); -} - -static const struct of_device_id iproc_nand_of_match[] = { - { .compatible = "brcm,nand-iproc" }, - {}, -}; -MODULE_DEVICE_TABLE(of, iproc_nand_of_match); - -static struct platform_driver iproc_nand_driver = { - .probe = iproc_nand_probe, - .remove = brcmnand_remove, - .driver = { - .name = "iproc_nand", - .pm = &brcmnand_pm_ops, - .of_match_table = iproc_nand_of_match, - } -}; -module_platform_driver(iproc_nand_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Brian Norris"); -MODULE_AUTHOR("Ray Jui"); -MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs"); diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c deleted file mode 100644 index 567ff972d5fc..000000000000 --- a/drivers/mtd/nand/cafe_nand.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 - * - * The data sheet for this device can be found at: - * http://wiki.laptop.org/go/Datasheets - * - * Copyright © 2006 Red Hat, Inc. - * Copyright © 2006 David Woodhouse - */ - -#define DEBUG - -#include -#undef DEBUG -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define CAFE_NAND_CTRL1 0x00 -#define CAFE_NAND_CTRL2 0x04 -#define CAFE_NAND_CTRL3 0x08 -#define CAFE_NAND_STATUS 0x0c -#define CAFE_NAND_IRQ 0x10 -#define CAFE_NAND_IRQ_MASK 0x14 -#define CAFE_NAND_DATA_LEN 0x18 -#define CAFE_NAND_ADDR1 0x1c -#define CAFE_NAND_ADDR2 0x20 -#define CAFE_NAND_TIMING1 0x24 -#define CAFE_NAND_TIMING2 0x28 -#define CAFE_NAND_TIMING3 0x2c -#define CAFE_NAND_NONMEM 0x30 -#define CAFE_NAND_ECC_RESULT 0x3C -#define CAFE_NAND_DMA_CTRL 0x40 -#define CAFE_NAND_DMA_ADDR0 0x44 -#define CAFE_NAND_DMA_ADDR1 0x48 -#define CAFE_NAND_ECC_SYN01 0x50 -#define CAFE_NAND_ECC_SYN23 0x54 -#define CAFE_NAND_ECC_SYN45 0x58 -#define CAFE_NAND_ECC_SYN67 0x5c -#define CAFE_NAND_READ_DATA 0x1000 -#define CAFE_NAND_WRITE_DATA 0x2000 - -#define CAFE_GLOBAL_CTRL 0x3004 -#define CAFE_GLOBAL_IRQ 0x3008 -#define CAFE_GLOBAL_IRQ_MASK 0x300c -#define CAFE_NAND_RESET 0x3034 - -/* Missing from the datasheet: bit 19 of CTRL1 sets CE0 vs. CE1 */ -#define CTRL1_CHIPSELECT (1<<19) - -struct cafe_priv { - struct nand_chip nand; - struct pci_dev *pdev; - void __iomem *mmio; - struct rs_control *rs; - uint32_t ctl1; - uint32_t ctl2; - int datalen; - int nr_data; - int data_pos; - int page_addr; - dma_addr_t dmaaddr; - unsigned char *dmabuf; -}; - -static int usedma = 1; -module_param(usedma, int, 0644); - -static int skipbbt = 0; -module_param(skipbbt, int, 0644); - -static int debug = 0; -module_param(debug, int, 0644); - -static int regdebug = 0; -module_param(regdebug, int, 0644); - -static int checkecc = 1; -module_param(checkecc, int, 0644); - -static unsigned int numtimings; -static int timing[3]; -module_param_array(timing, int, &numtimings, 0644); - -static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; - -/* Hrm. Why isn't this already conditional on something in the struct device? */ -#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) - -/* Make it easier to switch to PIO if we need to */ -#define cafe_readl(cafe, addr) readl((cafe)->mmio + CAFE_##addr) -#define cafe_writel(cafe, datum, addr) writel(datum, (cafe)->mmio + CAFE_##addr) - -static int cafe_device_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct cafe_priv *cafe = nand_get_controller_data(chip); - int result = !!(cafe_readl(cafe, NAND_STATUS) & 0x40000000); - uint32_t irqs = cafe_readl(cafe, NAND_IRQ); - - cafe_writel(cafe, irqs, NAND_IRQ); - - cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", - result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ), - cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK)); - - return result; -} - - -static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct cafe_priv *cafe = nand_get_controller_data(chip); - - if (usedma) - memcpy(cafe->dmabuf + cafe->datalen, buf, len); - else - memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); - - cafe->datalen += len; - - cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", - len, cafe->datalen); -} - -static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct cafe_priv *cafe = nand_get_controller_data(chip); - - if (usedma) - memcpy(buf, cafe->dmabuf + cafe->datalen, len); - else - memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); - - cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", - len, cafe->datalen); - cafe->datalen += len; -} - -static uint8_t cafe_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct cafe_priv *cafe = nand_get_controller_data(chip); - uint8_t d; - - cafe_read_buf(mtd, &d, 1); - cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); - - return d; -} - -static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct cafe_priv *cafe = nand_get_controller_data(chip); - int adrbytes = 0; - uint32_t ctl1; - uint32_t doneint = 0x80000000; - - cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", - command, column, page_addr); - - if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { - /* Second half of a command we already calculated */ - cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2); - ctl1 = cafe->ctl1; - cafe->ctl2 &= ~(1<<30); - cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", - cafe->ctl1, cafe->nr_data); - goto do_command; - } - /* Reset ECC engine */ - cafe_writel(cafe, 0, NAND_CTRL2); - - /* Emulate NAND_CMD_READOOB on large-page chips */ - if (mtd->writesize > 512 && - command == NAND_CMD_READOOB) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - /* FIXME: Do we need to send read command before sending data - for small-page chips, to position the buffer correctly? */ - - if (column != -1) { - cafe_writel(cafe, column, NAND_ADDR1); - adrbytes = 2; - if (page_addr != -1) - goto write_adr2; - } else if (page_addr != -1) { - cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1); - page_addr >>= 16; - write_adr2: - cafe_writel(cafe, page_addr, NAND_ADDR2); - adrbytes += 2; - if (mtd->size > mtd->writesize << 16) - adrbytes++; - } - - cafe->data_pos = cafe->datalen = 0; - - /* Set command valid bit, mask in the chip select bit */ - ctl1 = 0x80000000 | command | (cafe->ctl1 & CTRL1_CHIPSELECT); - - /* Set RD or WR bits as appropriate */ - if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { - ctl1 |= (1<<26); /* rd */ - /* Always 5 bytes, for now */ - cafe->datalen = 4; - /* And one address cycle -- even for STATUS, since the controller doesn't work without */ - adrbytes = 1; - } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || - command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { - ctl1 |= 1<<26; /* rd */ - /* For now, assume just read to end of page */ - cafe->datalen = mtd->writesize + mtd->oobsize - column; - } else if (command == NAND_CMD_SEQIN) - ctl1 |= 1<<25; /* wr */ - - /* Set number of address bytes */ - if (adrbytes) - ctl1 |= ((adrbytes-1)|8) << 27; - - if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { - /* Ignore the first command of a pair; the hardware - deals with them both at once, later */ - cafe->ctl1 = ctl1; - cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", - cafe->ctl1, cafe->datalen); - return; - } - /* RNDOUT and READ0 commands need a following byte */ - if (command == NAND_CMD_RNDOUT) - cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2); - else if (command == NAND_CMD_READ0 && mtd->writesize > 512) - cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2); - - do_command: - cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", - cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2)); - - /* NB: The datasheet lies -- we really should be subtracting 1 here */ - cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN); - cafe_writel(cafe, 0x90000000, NAND_IRQ); - if (usedma && (ctl1 & (3<<25))) { - uint32_t dmactl = 0xc0000000 + cafe->datalen; - /* If WR or RD bits set, set up DMA */ - if (ctl1 & (1<<26)) { - /* It's a read */ - dmactl |= (1<<29); - /* ... so it's done when the DMA is done, not just - the command. */ - doneint = 0x10000000; - } - cafe_writel(cafe, dmactl, NAND_DMA_CTRL); - } - cafe->datalen = 0; - - if (unlikely(regdebug)) { - int i; - printk("About to write command %08x to register 0\n", ctl1); - for (i=4; i< 0x5c; i+=4) - printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); - } - - cafe_writel(cafe, ctl1, NAND_CTRL1); - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ - ndelay(100); - - if (1) { - int c; - uint32_t irqs; - - for (c = 500000; c != 0; c--) { - irqs = cafe_readl(cafe, NAND_IRQ); - if (irqs & doneint) - break; - udelay(1); - if (!(c % 100000)) - cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); - cpu_relax(); - } - cafe_writel(cafe, doneint, NAND_IRQ); - cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", - command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ)); - } - - WARN_ON(cafe->ctl2 & (1<<30)); - - switch (command) { - - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: - case NAND_CMD_STATUS: - case NAND_CMD_RNDOUT: - cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); - return; - } - nand_wait_ready(mtd); - cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); -} - -static void cafe_select_chip(struct mtd_info *mtd, int chipnr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct cafe_priv *cafe = nand_get_controller_data(chip); - - cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); - - /* Mask the appropriate bit into the stored value of ctl1 - which will be used by cafe_nand_cmdfunc() */ - if (chipnr) - cafe->ctl1 |= CTRL1_CHIPSELECT; - else - cafe->ctl1 &= ~CTRL1_CHIPSELECT; -} - -static irqreturn_t cafe_nand_interrupt(int irq, void *id) -{ - struct mtd_info *mtd = id; - struct nand_chip *chip = mtd_to_nand(mtd); - struct cafe_priv *cafe = nand_get_controller_data(chip); - uint32_t irqs = cafe_readl(cafe, NAND_IRQ); - cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ); - if (!irqs) - return IRQ_NONE; - - cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ)); - return IRQ_HANDLED; -} - -static void cafe_nand_bug(struct mtd_info *mtd) -{ - BUG(); -} - -static int cafe_nand_write_oob(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, - mtd->oobsize); -} - -/* Don't use -- use nand_read_oob_std for now */ -static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); -} -/** - * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller expects OOB data read to chip->oob_poi - * - * The hw generator calculates the error syndrome automatically. Therefore - * we need a special oob layout and handling. - */ -static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct cafe_priv *cafe = nand_get_controller_data(chip); - unsigned int max_bitflips = 0; - - cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", - cafe_readl(cafe, NAND_ECC_RESULT), - cafe_readl(cafe, NAND_ECC_SYN01)); - - nand_read_page_op(chip, page, 0, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { - unsigned short syn[8], pat[4]; - int pos[4]; - u8 *oob = chip->oob_poi; - int i, n; - - for (i=0; i<8; i+=2) { - uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); - syn[i] = cafe->rs->index_of[tmp & 0xfff]; - syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff]; - } - - n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0, - pat); - - for (i = 0; i < n; i++) { - int p = pos[i]; - - /* The 12-bit symbols are mapped to bytes here */ - - if (p > 1374) { - /* out of range */ - n = -1374; - } else if (p == 0) { - /* high four bits do not correspond to data */ - if (pat[i] > 0xff) - n = -2048; - else - buf[0] ^= pat[i]; - } else if (p == 1365) { - buf[2047] ^= pat[i] >> 4; - oob[0] ^= pat[i] << 4; - } else if (p > 1365) { - if ((p & 1) == 1) { - oob[3*p/2 - 2048] ^= pat[i] >> 4; - oob[3*p/2 - 2047] ^= pat[i] << 4; - } else { - oob[3*p/2 - 2049] ^= pat[i] >> 8; - oob[3*p/2 - 2048] ^= pat[i]; - } - } else if ((p & 1) == 1) { - buf[3*p/2] ^= pat[i] >> 4; - buf[3*p/2 + 1] ^= pat[i] << 4; - } else { - buf[3*p/2 - 1] ^= pat[i] >> 8; - buf[3*p/2] ^= pat[i]; - } - } - - if (n < 0) { - dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", - cafe_readl(cafe, NAND_ADDR2) * 2048); - for (i = 0; i < 0x5c; i += 4) - printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); - mtd->ecc_stats.failed++; - } else { - dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); - mtd->ecc_stats.corrected += n; - max_bitflips = max_t(unsigned int, max_bitflips, n); - } - } - - return max_bitflips; -} - -static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section) - return -ERANGE; - - oobregion->offset = 0; - oobregion->length = chip->ecc.total; - - return 0; -} - -static int cafe_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section) - return -ERANGE; - - oobregion->offset = chip->ecc.total; - oobregion->length = mtd->oobsize - chip->ecc.total; - - return 0; -} - -static const struct mtd_ooblayout_ops cafe_ooblayout_ops = { - .ecc = cafe_ooblayout_ecc, - .free = cafe_ooblayout_free, -}; - -/* Ick. The BBT code really ought to be able to work this bit out - for itself from the above, at least for the 2KiB case */ -static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' }; -static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' }; - -static uint8_t cafe_bbt_pattern_512[] = { 0xBB }; -static uint8_t cafe_mirror_pattern_512[] = { 0xBC }; - - -static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 14, - .len = 4, - .veroffs = 18, - .maxblocks = 4, - .pattern = cafe_bbt_pattern_2048 -}; - -static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 14, - .len = 4, - .veroffs = 18, - .maxblocks = 4, - .pattern = cafe_mirror_pattern_2048 -}; - -static struct nand_bbt_descr cafe_bbt_main_descr_512 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 14, - .len = 1, - .veroffs = 15, - .maxblocks = 4, - .pattern = cafe_bbt_pattern_512 -}; - -static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 14, - .len = 1, - .veroffs = 15, - .maxblocks = 4, - .pattern = cafe_mirror_pattern_512 -}; - - -static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - struct cafe_priv *cafe = nand_get_controller_data(chip); - - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - /* Set up ECC autogeneration */ - cafe->ctl2 |= (1<<30); - - return nand_prog_page_end_op(chip); -} - -static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs) -{ - return 0; -} - -/* F_2[X]/(X**6+X+1) */ -static unsigned short gf64_mul(u8 a, u8 b) -{ - u8 c; - unsigned int i; - - c = 0; - for (i = 0; i < 6; i++) { - if (a & 1) - c ^= b; - a >>= 1; - b <<= 1; - if ((b & 0x40) != 0) - b ^= 0x43; - } - - return c; -} - -/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */ -static u16 gf4096_mul(u16 a, u16 b) -{ - u8 ah, al, bh, bl, ch, cl; - - ah = a >> 6; - al = a & 0x3f; - bh = b >> 6; - bl = b & 0x3f; - - ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl); - cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl); - - return (ch << 6) ^ cl; -} - -static int cafe_mul(int x) -{ - if (x == 0) - return 1; - return gf4096_mul(x, 0xe01); -} - -static int cafe_nand_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct mtd_info *mtd; - struct cafe_priv *cafe; - uint32_t ctrl; - int err = 0; - int old_dma; - - /* Very old versions shared the same PCI ident for all three - functions on the chip. Verify the class too... */ - if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH) - return -ENODEV; - - err = pci_enable_device(pdev); - if (err) - return err; - - pci_set_master(pdev); - - cafe = kzalloc(sizeof(*cafe), GFP_KERNEL); - if (!cafe) - return -ENOMEM; - - mtd = nand_to_mtd(&cafe->nand); - mtd->dev.parent = &pdev->dev; - nand_set_controller_data(&cafe->nand, cafe); - - cafe->pdev = pdev; - cafe->mmio = pci_iomap(pdev, 0, 0); - if (!cafe->mmio) { - dev_warn(&pdev->dev, "failed to iomap\n"); - err = -ENOMEM; - goto out_free_mtd; - } - - cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8); - if (!cafe->rs) { - err = -ENOMEM; - goto out_ior; - } - - cafe->nand.cmdfunc = cafe_nand_cmdfunc; - cafe->nand.dev_ready = cafe_device_ready; - cafe->nand.read_byte = cafe_read_byte; - cafe->nand.read_buf = cafe_read_buf; - cafe->nand.write_buf = cafe_write_buf; - cafe->nand.select_chip = cafe_select_chip; - cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp; - cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp; - - cafe->nand.chip_delay = 0; - - /* Enable the following for a flash based bad block table */ - cafe->nand.bbt_options = NAND_BBT_USE_FLASH; - - if (skipbbt) { - cafe->nand.options |= NAND_SKIP_BBTSCAN; - cafe->nand.block_bad = cafe_nand_block_bad; - } - - if (numtimings && numtimings != 3) { - dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); - } - - if (numtimings == 3) { - cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", - timing[0], timing[1], timing[2]); - } else { - timing[0] = cafe_readl(cafe, NAND_TIMING1); - timing[1] = cafe_readl(cafe, NAND_TIMING2); - timing[2] = cafe_readl(cafe, NAND_TIMING3); - - if (timing[0] | timing[1] | timing[2]) { - cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", - timing[0], timing[1], timing[2]); - } else { - dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); - timing[0] = timing[1] = timing[2] = 0xffffffff; - } - } - - /* Start off by resetting the NAND controller completely */ - cafe_writel(cafe, 1, NAND_RESET); - cafe_writel(cafe, 0, NAND_RESET); - - cafe_writel(cafe, timing[0], NAND_TIMING1); - cafe_writel(cafe, timing[1], NAND_TIMING2); - cafe_writel(cafe, timing[2], NAND_TIMING3); - - cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); - err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, - "CAFE NAND", mtd); - if (err) { - dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); - goto out_ior; - } - - /* Disable master reset, enable NAND clock */ - ctrl = cafe_readl(cafe, GLOBAL_CTRL); - ctrl &= 0xffffeff0; - ctrl |= 0x00007000; - cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); - cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); - cafe_writel(cafe, 0, NAND_DMA_CTRL); - - cafe_writel(cafe, 0x7006, GLOBAL_CTRL); - cafe_writel(cafe, 0x700a, GLOBAL_CTRL); - - /* Enable NAND IRQ in global IRQ mask register */ - cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); - cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", - cafe_readl(cafe, GLOBAL_CTRL), - cafe_readl(cafe, GLOBAL_IRQ_MASK)); - - /* Do not use the DMA for the nand_scan_ident() */ - old_dma = usedma; - usedma = 0; - - /* Scan to find existence of the device */ - err = nand_scan_ident(mtd, 2, NULL); - if (err) - goto out_irq; - - cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112, - &cafe->dmaaddr, GFP_KERNEL); - if (!cafe->dmabuf) { - err = -ENOMEM; - goto out_irq; - } - - /* Set up DMA address */ - cafe_writel(cafe, lower_32_bits(cafe->dmaaddr), NAND_DMA_ADDR0); - cafe_writel(cafe, upper_32_bits(cafe->dmaaddr), NAND_DMA_ADDR1); - - cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", - cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); - - /* Restore the DMA flag */ - usedma = old_dma; - - cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ - if (mtd->writesize == 2048) - cafe->ctl2 |= 1<<29; /* 2KiB page size */ - - /* Set up ECC according to the type of chip we found */ - mtd_set_ooblayout(mtd, &cafe_ooblayout_ops); - if (mtd->writesize == 2048) { - cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; - cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; - } else if (mtd->writesize == 512) { - cafe->nand.bbt_td = &cafe_bbt_main_descr_512; - cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; - } else { - printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", - mtd->writesize); - goto out_free_dma; - } - cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; - cafe->nand.ecc.size = mtd->writesize; - cafe->nand.ecc.bytes = 14; - cafe->nand.ecc.strength = 4; - cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; - cafe->nand.ecc.calculate = (void *)cafe_nand_bug; - cafe->nand.ecc.correct = (void *)cafe_nand_bug; - cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; - cafe->nand.ecc.write_oob = cafe_nand_write_oob; - cafe->nand.ecc.read_page = cafe_nand_read_page; - cafe->nand.ecc.read_oob = cafe_nand_read_oob; - - err = nand_scan_tail(mtd); - if (err) - goto out_free_dma; - - pci_set_drvdata(pdev, mtd); - - mtd->name = "cafe_nand"; - mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); - - goto out; - - out_free_dma: - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); - out_irq: - /* Disable NAND IRQ in global IRQ mask register */ - cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); - free_irq(pdev->irq, mtd); - out_ior: - pci_iounmap(pdev, cafe->mmio); - out_free_mtd: - kfree(cafe); - out: - return err; -} - -static void cafe_nand_remove(struct pci_dev *pdev) -{ - struct mtd_info *mtd = pci_get_drvdata(pdev); - struct nand_chip *chip = mtd_to_nand(mtd); - struct cafe_priv *cafe = nand_get_controller_data(chip); - - /* Disable NAND IRQ in global IRQ mask register */ - cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); - free_irq(pdev->irq, mtd); - nand_release(mtd); - free_rs(cafe->rs); - pci_iounmap(pdev, cafe->mmio); - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); - kfree(cafe); -} - -static const struct pci_device_id cafe_nand_tbl[] = { - { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, - PCI_ANY_ID, PCI_ANY_ID }, - { } -}; - -MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); - -static int cafe_nand_resume(struct pci_dev *pdev) -{ - uint32_t ctrl; - struct mtd_info *mtd = pci_get_drvdata(pdev); - struct nand_chip *chip = mtd_to_nand(mtd); - struct cafe_priv *cafe = nand_get_controller_data(chip); - - /* Start off by resetting the NAND controller completely */ - cafe_writel(cafe, 1, NAND_RESET); - cafe_writel(cafe, 0, NAND_RESET); - cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); - - /* Restore timing configuration */ - cafe_writel(cafe, timing[0], NAND_TIMING1); - cafe_writel(cafe, timing[1], NAND_TIMING2); - cafe_writel(cafe, timing[2], NAND_TIMING3); - - /* Disable master reset, enable NAND clock */ - ctrl = cafe_readl(cafe, GLOBAL_CTRL); - ctrl &= 0xffffeff0; - ctrl |= 0x00007000; - cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); - cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); - cafe_writel(cafe, 0, NAND_DMA_CTRL); - cafe_writel(cafe, 0x7006, GLOBAL_CTRL); - cafe_writel(cafe, 0x700a, GLOBAL_CTRL); - - /* Set up DMA address */ - cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); - if (sizeof(cafe->dmaaddr) > 4) - /* Shift in two parts to shut the compiler up */ - cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); - else - cafe_writel(cafe, 0, NAND_DMA_ADDR1); - - /* Enable NAND IRQ in global IRQ mask register */ - cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); - return 0; -} - -static struct pci_driver cafe_nand_pci_driver = { - .name = "CAFÉ NAND", - .id_table = cafe_nand_tbl, - .probe = cafe_nand_probe, - .remove = cafe_nand_remove, - .resume = cafe_nand_resume, -}; - -module_pci_driver(cafe_nand_pci_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse "); -MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c deleted file mode 100644 index 02d6751e9efe..000000000000 --- a/drivers/mtd/nand/cmx270_nand.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2006 Compulab, Ltd. - * Mike Rapoport - * - * Derived from drivers/mtd/nand/h1910.c (removed in v3.10) - * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) - * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * CM-X270 board. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#define GPIO_NAND_CS (11) -#define GPIO_NAND_RB (89) - -/* MTD structure for CM-X270 board */ -static struct mtd_info *cmx270_nand_mtd; - -/* remaped IO address of the device */ -static void __iomem *cmx270_nand_io; - -/* - * Define static partitions for flash device - */ -static const struct mtd_partition partition_info[] = { - [0] = { - .name = "cmx270-0", - .offset = 0, - .size = MTDPART_SIZ_FULL - } -}; -#define NUM_PARTITIONS (ARRAY_SIZE(partition_info)) - -static u_char cmx270_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - return (readl(this->IO_ADDR_R) >> 16); -} - -static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd_to_nand(mtd); - - for (i=0; iIO_ADDR_W); -} - -static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd_to_nand(mtd); - - for (i=0; iIO_ADDR_R) >> 16; -} - -static inline void nand_cs_on(void) -{ - gpio_set_value(GPIO_NAND_CS, 0); -} - -static void nand_cs_off(void) -{ - dsb(); - - gpio_set_value(GPIO_NAND_CS, 1); -} - -/* - * hardware specific access to control-lines - */ -static void cmx270_hwcontrol(struct mtd_info *mtd, int dat, - unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; - - dsb(); - - if (ctrl & NAND_CTRL_CHANGE) { - if ( ctrl & NAND_ALE ) - nandaddr |= (1 << 3); - else - nandaddr &= ~(1 << 3); - if ( ctrl & NAND_CLE ) - nandaddr |= (1 << 2); - else - nandaddr &= ~(1 << 2); - if ( ctrl & NAND_NCE ) - nand_cs_on(); - else - nand_cs_off(); - } - - dsb(); - this->IO_ADDR_W = (void __iomem*)nandaddr; - if (dat != NAND_CMD_NONE) - writel((dat << 16), this->IO_ADDR_W); - - dsb(); -} - -/* - * read device ready pin - */ -static int cmx270_device_ready(struct mtd_info *mtd) -{ - dsb(); - - return (gpio_get_value(GPIO_NAND_RB)); -} - -/* - * Main initialization routine - */ -static int __init cmx270_init(void) -{ - struct nand_chip *this; - int ret; - - if (!(machine_is_armcore() && cpu_is_pxa27x())) - return -ENODEV; - - ret = gpio_request(GPIO_NAND_CS, "NAND CS"); - if (ret) { - pr_warn("CM-X270: failed to request NAND CS gpio\n"); - return ret; - } - - gpio_direction_output(GPIO_NAND_CS, 1); - - ret = gpio_request(GPIO_NAND_RB, "NAND R/B"); - if (ret) { - pr_warn("CM-X270: failed to request NAND R/B gpio\n"); - goto err_gpio_request; - } - - gpio_direction_input(GPIO_NAND_RB); - - /* Allocate memory for MTD device structure and private data */ - this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); - if (!this) { - ret = -ENOMEM; - goto err_kzalloc; - } - - cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12); - if (!cmx270_nand_io) { - pr_debug("Unable to ioremap NAND device\n"); - ret = -EINVAL; - goto err_ioremap; - } - - cmx270_nand_mtd = nand_to_mtd(this); - - /* Link the private data with the MTD structure */ - cmx270_nand_mtd->owner = THIS_MODULE; - - /* insert callbacks */ - this->IO_ADDR_R = cmx270_nand_io; - this->IO_ADDR_W = cmx270_nand_io; - this->cmd_ctrl = cmx270_hwcontrol; - this->dev_ready = cmx270_device_ready; - - /* 15 us command delay time */ - this->chip_delay = 20; - this->ecc.mode = NAND_ECC_SOFT; - this->ecc.algo = NAND_ECC_HAMMING; - - /* read/write functions */ - this->read_byte = cmx270_read_byte; - this->read_buf = cmx270_read_buf; - this->write_buf = cmx270_write_buf; - - /* Scan to find existence of the device */ - ret = nand_scan(cmx270_nand_mtd, 1); - if (ret) { - pr_notice("No NAND device\n"); - goto err_scan; - } - - /* Register the partitions */ - ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL, - partition_info, NUM_PARTITIONS); - if (ret) - goto err_scan; - - /* Return happy */ - return 0; - -err_scan: - iounmap(cmx270_nand_io); -err_ioremap: - kfree(this); -err_kzalloc: - gpio_free(GPIO_NAND_RB); -err_gpio_request: - gpio_free(GPIO_NAND_CS); - - return ret; - -} -module_init(cmx270_init); - -/* - * Clean up routine - */ -static void __exit cmx270_cleanup(void) -{ - /* Release resources, unregister device */ - nand_release(cmx270_nand_mtd); - - gpio_free(GPIO_NAND_RB); - gpio_free(GPIO_NAND_CS); - - iounmap(cmx270_nand_io); - - kfree(mtd_to_nand(cmx270_nand_mtd)); -} -module_exit(cmx270_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mike Rapoport "); -MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module"); diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c deleted file mode 100644 index be1f28fc7363..000000000000 --- a/drivers/mtd/nand/cs553x_nand.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - * (C) 2005, 2006 Red Hat Inc. - * - * Author: David Woodhouse - * Tom Sylla - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash controller found on - * the AMD CS5535/CS5536 companion chipsets for the Geode processor. - * mtd-id for command line partitioning is cs553x_nand_cs[0-3] - * where 0-3 reflects the chip select for NAND. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define NR_CS553X_CONTROLLERS 4 - -#define MSR_DIVIL_GLD_CAP 0x51400000 /* DIVIL capabilitiies */ -#define CAP_CS5535 0x2df000ULL -#define CAP_CS5536 0x5df500ULL - -/* NAND Timing MSRs */ -#define MSR_NANDF_DATA 0x5140001b /* NAND Flash Data Timing MSR */ -#define MSR_NANDF_CTL 0x5140001c /* NAND Flash Control Timing */ -#define MSR_NANDF_RSVD 0x5140001d /* Reserved */ - -/* NAND BAR MSRs */ -#define MSR_DIVIL_LBAR_FLSH0 0x51400010 /* Flash Chip Select 0 */ -#define MSR_DIVIL_LBAR_FLSH1 0x51400011 /* Flash Chip Select 1 */ -#define MSR_DIVIL_LBAR_FLSH2 0x51400012 /* Flash Chip Select 2 */ -#define MSR_DIVIL_LBAR_FLSH3 0x51400013 /* Flash Chip Select 3 */ - /* Each made up of... */ -#define FLSH_LBAR_EN (1ULL<<32) -#define FLSH_NOR_NAND (1ULL<<33) /* 1 for NAND */ -#define FLSH_MEM_IO (1ULL<<34) /* 1 for MMIO */ - /* I/O BARs have BASE_ADDR in bits 15:4, IO_MASK in 47:36 */ - /* MMIO BARs have BASE_ADDR in bits 31:12, MEM_MASK in 63:44 */ - -/* Pin function selection MSR (IDE vs. flash on the IDE pins) */ -#define MSR_DIVIL_BALL_OPTS 0x51400015 -#define PIN_OPT_IDE (1<<0) /* 0 for flash, 1 for IDE */ - -/* Registers within the NAND flash controller BAR -- memory mapped */ -#define MM_NAND_DATA 0x00 /* 0 to 0x7ff, in fact */ -#define MM_NAND_CTL 0x800 /* Any even address 0x800-0x80e */ -#define MM_NAND_IO 0x801 /* Any odd address 0x801-0x80f */ -#define MM_NAND_STS 0x810 -#define MM_NAND_ECC_LSB 0x811 -#define MM_NAND_ECC_MSB 0x812 -#define MM_NAND_ECC_COL 0x813 -#define MM_NAND_LAC 0x814 -#define MM_NAND_ECC_CTL 0x815 - -/* Registers within the NAND flash controller BAR -- I/O mapped */ -#define IO_NAND_DATA 0x00 /* 0 to 3, in fact */ -#define IO_NAND_CTL 0x04 -#define IO_NAND_IO 0x05 -#define IO_NAND_STS 0x06 -#define IO_NAND_ECC_CTL 0x08 -#define IO_NAND_ECC_LSB 0x09 -#define IO_NAND_ECC_MSB 0x0a -#define IO_NAND_ECC_COL 0x0b -#define IO_NAND_LAC 0x0c - -#define CS_NAND_CTL_DIST_EN (1<<4) /* Enable NAND Distract interrupt */ -#define CS_NAND_CTL_RDY_INT_MASK (1<<3) /* Enable RDY/BUSY# interrupt */ -#define CS_NAND_CTL_ALE (1<<2) -#define CS_NAND_CTL_CLE (1<<1) -#define CS_NAND_CTL_CE (1<<0) /* Keep low; 1 to reset */ - -#define CS_NAND_STS_FLASH_RDY (1<<3) -#define CS_NAND_CTLR_BUSY (1<<2) -#define CS_NAND_CMD_COMP (1<<1) -#define CS_NAND_DIST_ST (1<<0) - -#define CS_NAND_ECC_PARITY (1<<2) -#define CS_NAND_ECC_CLRECC (1<<1) -#define CS_NAND_ECC_ENECC (1<<0) - -static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - while (unlikely(len > 0x800)) { - memcpy_fromio(buf, this->IO_ADDR_R, 0x800); - buf += 0x800; - len -= 0x800; - } - memcpy_fromio(buf, this->IO_ADDR_R, len); -} - -static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - while (unlikely(len > 0x800)) { - memcpy_toio(this->IO_ADDR_R, buf, 0x800); - buf += 0x800; - len -= 0x800; - } - memcpy_toio(this->IO_ADDR_R, buf, len); -} - -static unsigned char cs553x_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - return readb(this->IO_ADDR_R); -} - -static void cs553x_write_byte(struct mtd_info *mtd, u_char byte) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int i = 100000; - - while (i && readb(this->IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) { - udelay(1); - i--; - } - writeb(byte, this->IO_ADDR_W + 0x801); -} - -static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - void __iomem *mmio_base = this->IO_ADDR_R; - if (ctrl & NAND_CTRL_CHANGE) { - unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01; - writeb(ctl, mmio_base + MM_NAND_CTL); - } - if (cmd != NAND_CMD_NONE) - cs553x_write_byte(mtd, cmd); -} - -static int cs553x_device_ready(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - void __iomem *mmio_base = this->IO_ADDR_R; - unsigned char foo = readb(mmio_base + MM_NAND_STS); - - return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY); -} - -static void cs_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct nand_chip *this = mtd_to_nand(mtd); - void __iomem *mmio_base = this->IO_ADDR_R; - - writeb(0x07, mmio_base + MM_NAND_ECC_CTL); -} - -static int cs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) -{ - uint32_t ecc; - struct nand_chip *this = mtd_to_nand(mtd); - void __iomem *mmio_base = this->IO_ADDR_R; - - ecc = readl(mmio_base + MM_NAND_STS); - - ecc_code[1] = ecc >> 8; - ecc_code[0] = ecc >> 16; - ecc_code[2] = ecc >> 24; - return 0; -} - -static struct mtd_info *cs553x_mtd[4]; - -static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) -{ - int err = 0; - struct nand_chip *this; - struct mtd_info *new_mtd; - - printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr); - - if (!mmio) { - printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n"); - return -ENXIO; - } - - /* Allocate memory for MTD device structure and private data */ - this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); - if (!this) { - err = -ENOMEM; - goto out; - } - - new_mtd = nand_to_mtd(this); - - /* Link the private data with the MTD structure */ - new_mtd->owner = THIS_MODULE; - - /* map physical address */ - this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096); - if (!this->IO_ADDR_R) { - printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr); - err = -EIO; - goto out_mtd; - } - - this->cmd_ctrl = cs553x_hwcontrol; - this->dev_ready = cs553x_device_ready; - this->read_byte = cs553x_read_byte; - this->read_buf = cs553x_read_buf; - this->write_buf = cs553x_write_buf; - - this->chip_delay = 0; - - this->ecc.mode = NAND_ECC_HW; - this->ecc.size = 256; - this->ecc.bytes = 3; - this->ecc.hwctl = cs_enable_hwecc; - this->ecc.calculate = cs_calculate_ecc; - this->ecc.correct = nand_correct_data; - this->ecc.strength = 1; - - /* Enable the following for a flash based bad block table */ - this->bbt_options = NAND_BBT_USE_FLASH; - - new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs); - if (!new_mtd->name) { - err = -ENOMEM; - goto out_ior; - } - - /* Scan to find existence of the device */ - err = nand_scan(new_mtd, 1); - if (err) - goto out_free; - - cs553x_mtd[cs] = new_mtd; - goto out; - -out_free: - kfree(new_mtd->name); -out_ior: - iounmap(this->IO_ADDR_R); -out_mtd: - kfree(this); -out: - return err; -} - -static int is_geode(void) -{ - /* These are the CPUs which will have a CS553[56] companion chip */ - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && - boot_cpu_data.x86 == 5 && - boot_cpu_data.x86_model == 10) - return 1; /* Geode LX */ - - if ((boot_cpu_data.x86_vendor == X86_VENDOR_NSC || - boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX) && - boot_cpu_data.x86 == 5 && - boot_cpu_data.x86_model == 5) - return 1; /* Geode GX (née GX2) */ - - return 0; -} - -static int __init cs553x_init(void) -{ - int err = -ENXIO; - int i; - uint64_t val; - - /* If the CPU isn't a Geode GX or LX, abort */ - if (!is_geode()) - return -ENXIO; - - /* If it doesn't have the CS553[56], abort */ - rdmsrl(MSR_DIVIL_GLD_CAP, val); - val &= ~0xFFULL; - if (val != CAP_CS5535 && val != CAP_CS5536) - return -ENXIO; - - /* If it doesn't have the NAND controller enabled, abort */ - rdmsrl(MSR_DIVIL_BALL_OPTS, val); - if (val & PIN_OPT_IDE) { - printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n"); - return -ENXIO; - } - - for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { - rdmsrl(MSR_DIVIL_LBAR_FLSH0 + i, val); - - if ((val & (FLSH_LBAR_EN|FLSH_NOR_NAND)) == (FLSH_LBAR_EN|FLSH_NOR_NAND)) - err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF); - } - - /* Register all devices together here. This means we can easily hack it to - do mtdconcat etc. if we want to. */ - for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { - if (cs553x_mtd[i]) { - /* If any devices registered, return success. Else the last error. */ - mtd_device_parse_register(cs553x_mtd[i], NULL, NULL, - NULL, 0); - err = 0; - } - } - - return err; -} - -module_init(cs553x_init); - -static void __exit cs553x_cleanup(void) -{ - int i; - - for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { - struct mtd_info *mtd = cs553x_mtd[i]; - struct nand_chip *this; - void __iomem *mmio_base; - - if (!mtd) - continue; - - this = mtd_to_nand(mtd); - mmio_base = this->IO_ADDR_R; - - /* Release resources, unregister device */ - nand_release(mtd); - kfree(mtd->name); - cs553x_mtd[i] = NULL; - - /* unmap physical address */ - iounmap(mmio_base); - - /* Free the MTD device structure */ - kfree(this); - } -} - -module_exit(cs553x_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse "); -MODULE_DESCRIPTION("NAND controller driver for AMD CS5535/CS5536 companion chip"); diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c deleted file mode 100644 index ccc8c43abcff..000000000000 --- a/drivers/mtd/nand/davinci_nand.c +++ /dev/null @@ -1,879 +0,0 @@ -/* - * davinci_nand.c - NAND Flash Driver for DaVinci family chips - * - * Copyright © 2006 Texas Instruments. - * - * Port to 2.6.23 Copyright © 2008 by: - * Sander Huijsen - * Troy Kisky - * Dirk Behme - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* - * This is a device driver for the NAND flash controller found on the - * various DaVinci family chips. It handles up to four SoC chipselects, - * and some flavors of secondary chipselect (e.g. based on A12) as used - * with multichip packages. - * - * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC - * available on chips like the DM355 and OMAP-L137 and needed with the - * more error-prone MLC NAND chips. - * - * This driver assumes EM_WAIT connects all the NAND devices' RDY/nBUSY - * outputs in a "wire-AND" configuration, with no per-chip signals. - */ -struct davinci_nand_info { - struct nand_chip chip; - - struct device *dev; - struct clk *clk; - - bool is_readmode; - - void __iomem *base; - void __iomem *vaddr; - - uint32_t ioaddr; - uint32_t current_cs; - - uint32_t mask_chipsel; - uint32_t mask_ale; - uint32_t mask_cle; - - uint32_t core_chipsel; - - struct davinci_aemif_timing *timing; -}; - -static DEFINE_SPINLOCK(davinci_nand_lock); -static bool ecc4_busy; - -static inline struct davinci_nand_info *to_davinci_nand(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct davinci_nand_info, chip); -} - -static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info, - int offset) -{ - return __raw_readl(info->base + offset); -} - -static inline void davinci_nand_writel(struct davinci_nand_info *info, - int offset, unsigned long value) -{ - __raw_writel(value, info->base + offset); -} - -/*----------------------------------------------------------------------*/ - -/* - * Access to hardware control lines: ALE, CLE, secondary chipselect. - */ - -static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct davinci_nand_info *info = to_davinci_nand(mtd); - uint32_t addr = info->current_cs; - struct nand_chip *nand = mtd_to_nand(mtd); - - /* Did the control lines change? */ - if (ctrl & NAND_CTRL_CHANGE) { - if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE) - addr |= info->mask_cle; - else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE) - addr |= info->mask_ale; - - nand->IO_ADDR_W = (void __iomem __force *)addr; - } - - if (cmd != NAND_CMD_NONE) - iowrite8(cmd, nand->IO_ADDR_W); -} - -static void nand_davinci_select_chip(struct mtd_info *mtd, int chip) -{ - struct davinci_nand_info *info = to_davinci_nand(mtd); - uint32_t addr = info->ioaddr; - - /* maybe kick in a second chipselect */ - if (chip > 0) - addr |= info->mask_chipsel; - info->current_cs = addr; - - info->chip.IO_ADDR_W = (void __iomem __force *)addr; - info->chip.IO_ADDR_R = info->chip.IO_ADDR_W; -} - -/*----------------------------------------------------------------------*/ - -/* - * 1-bit hardware ECC ... context maintained for each core chipselect - */ - -static inline uint32_t nand_davinci_readecc_1bit(struct mtd_info *mtd) -{ - struct davinci_nand_info *info = to_davinci_nand(mtd); - - return davinci_nand_readl(info, NANDF1ECC_OFFSET - + 4 * info->core_chipsel); -} - -static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode) -{ - struct davinci_nand_info *info; - uint32_t nandcfr; - unsigned long flags; - - info = to_davinci_nand(mtd); - - /* Reset ECC hardware */ - nand_davinci_readecc_1bit(mtd); - - spin_lock_irqsave(&davinci_nand_lock, flags); - - /* Restart ECC hardware */ - nandcfr = davinci_nand_readl(info, NANDFCR_OFFSET); - nandcfr |= BIT(8 + info->core_chipsel); - davinci_nand_writel(info, NANDFCR_OFFSET, nandcfr); - - spin_unlock_irqrestore(&davinci_nand_lock, flags); -} - -/* - * Read hardware ECC value and pack into three bytes - */ -static int nand_davinci_calculate_1bit(struct mtd_info *mtd, - const u_char *dat, u_char *ecc_code) -{ - unsigned int ecc_val = nand_davinci_readecc_1bit(mtd); - unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4); - - /* invert so that erased block ecc is correct */ - ecc24 = ~ecc24; - ecc_code[0] = (u_char)(ecc24); - ecc_code[1] = (u_char)(ecc24 >> 8); - ecc_code[2] = (u_char)(ecc24 >> 16); - - return 0; -} - -static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) | - (read_ecc[2] << 16); - uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) | - (calc_ecc[2] << 16); - uint32_t diff = eccCalc ^ eccNand; - - if (diff) { - if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { - /* Correctable error */ - if ((diff >> (12 + 3)) < chip->ecc.size) { - dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7); - return 1; - } else { - return -EBADMSG; - } - } else if (!(diff & (diff - 1))) { - /* Single bit ECC error in the ECC itself, - * nothing to fix */ - return 1; - } else { - /* Uncorrectable error */ - return -EBADMSG; - } - - } - return 0; -} - -/*----------------------------------------------------------------------*/ - -/* - * 4-bit hardware ECC ... context maintained over entire AEMIF - * - * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME - * since that forces use of a problematic "infix OOB" layout. - * Among other things, it trashes manufacturer bad block markers. - * Also, and specific to this hardware, it ECC-protects the "prepad" - * in the OOB ... while having ECC protection for parts of OOB would - * seem useful, the current MTD stack sometimes wants to update the - * OOB without recomputing ECC. - */ - -static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode) -{ - struct davinci_nand_info *info = to_davinci_nand(mtd); - unsigned long flags; - u32 val; - - /* Reset ECC hardware */ - davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); - - spin_lock_irqsave(&davinci_nand_lock, flags); - - /* Start 4-bit ECC calculation for read/write */ - val = davinci_nand_readl(info, NANDFCR_OFFSET); - val &= ~(0x03 << 4); - val |= (info->core_chipsel << 4) | BIT(12); - davinci_nand_writel(info, NANDFCR_OFFSET, val); - - info->is_readmode = (mode == NAND_ECC_READ); - - spin_unlock_irqrestore(&davinci_nand_lock, flags); -} - -/* Read raw ECC code after writing to NAND. */ -static void -nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4]) -{ - const u32 mask = 0x03ff03ff; - - code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask; - code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask; - code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask; - code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask; -} - -/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */ -static int nand_davinci_calculate_4bit(struct mtd_info *mtd, - const u_char *dat, u_char *ecc_code) -{ - struct davinci_nand_info *info = to_davinci_nand(mtd); - u32 raw_ecc[4], *p; - unsigned i; - - /* After a read, terminate ECC calculation by a dummy read - * of some 4-bit ECC register. ECC covers everything that - * was read; correct() just uses the hardware state, so - * ecc_code is not needed. - */ - if (info->is_readmode) { - davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); - return 0; - } - - /* Pack eight raw 10-bit ecc values into ten bytes, making - * two passes which each convert four values (in upper and - * lower halves of two 32-bit words) into five bytes. The - * ROM boot loader uses this same packing scheme. - */ - nand_davinci_readecc_4bit(info, raw_ecc); - for (i = 0, p = raw_ecc; i < 2; i++, p += 2) { - *ecc_code++ = p[0] & 0xff; - *ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc); - *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0); - *ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0); - *ecc_code++ = (p[1] >> 18) & 0xff; - } - - return 0; -} - -/* Correct up to 4 bits in data we just read, using state left in the - * hardware plus the ecc_code computed when it was first written. - */ -static int nand_davinci_correct_4bit(struct mtd_info *mtd, - u_char *data, u_char *ecc_code, u_char *null) -{ - int i; - struct davinci_nand_info *info = to_davinci_nand(mtd); - unsigned short ecc10[8]; - unsigned short *ecc16; - u32 syndrome[4]; - u32 ecc_state; - unsigned num_errors, corrected; - unsigned long timeo; - - /* Unpack ten bytes into eight 10 bit values. We know we're - * little-endian, and use type punning for less shifting/masking. - */ - if (WARN_ON(0x01 & (unsigned) ecc_code)) - return -EINVAL; - ecc16 = (unsigned short *)ecc_code; - - ecc10[0] = (ecc16[0] >> 0) & 0x3ff; - ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0); - ecc10[2] = (ecc16[1] >> 4) & 0x3ff; - ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc); - ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300); - ecc10[5] = (ecc16[3] >> 2) & 0x3ff; - ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0); - ecc10[7] = (ecc16[4] >> 6) & 0x3ff; - - /* Tell ECC controller about the expected ECC codes. */ - for (i = 7; i >= 0; i--) - davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]); - - /* Allow time for syndrome calculation ... then read it. - * A syndrome of all zeroes 0 means no detected errors. - */ - davinci_nand_readl(info, NANDFSR_OFFSET); - nand_davinci_readecc_4bit(info, syndrome); - if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) - return 0; - - /* - * Clear any previous address calculation by doing a dummy read of an - * error address register. - */ - davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET); - - /* Start address calculation, and wait for it to complete. - * We _could_ start reading more data while this is working, - * to speed up the overall page read. - */ - davinci_nand_writel(info, NANDFCR_OFFSET, - davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13)); - - /* - * ECC_STATE field reads 0x3 (Error correction complete) immediately - * after setting the 4BITECC_ADD_CALC_START bit. So if you immediately - * begin trying to poll for the state, you may fall right out of your - * loop without any of the correction calculations having taken place. - * The recommendation from the hardware team is to initially delay as - * long as ECC_STATE reads less than 4. After that, ECC HW has entered - * correction state. - */ - timeo = jiffies + usecs_to_jiffies(100); - do { - ecc_state = (davinci_nand_readl(info, - NANDFSR_OFFSET) >> 8) & 0x0f; - cpu_relax(); - } while ((ecc_state < 4) && time_before(jiffies, timeo)); - - for (;;) { - u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET); - - switch ((fsr >> 8) & 0x0f) { - case 0: /* no error, should not happen */ - davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); - return 0; - case 1: /* five or more errors detected */ - davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); - return -EBADMSG; - case 2: /* error addresses computed */ - case 3: - num_errors = 1 + ((fsr >> 16) & 0x03); - goto correct; - default: /* still working on it */ - cpu_relax(); - continue; - } - } - -correct: - /* correct each error */ - for (i = 0, corrected = 0; i < num_errors; i++) { - int error_address, error_value; - - if (i > 1) { - error_address = davinci_nand_readl(info, - NAND_ERR_ADD2_OFFSET); - error_value = davinci_nand_readl(info, - NAND_ERR_ERRVAL2_OFFSET); - } else { - error_address = davinci_nand_readl(info, - NAND_ERR_ADD1_OFFSET); - error_value = davinci_nand_readl(info, - NAND_ERR_ERRVAL1_OFFSET); - } - - if (i & 1) { - error_address >>= 16; - error_value >>= 16; - } - error_address &= 0x3ff; - error_address = (512 + 7) - error_address; - - if (error_address < 512) { - data[error_address] ^= error_value; - corrected++; - } - } - - return corrected; -} - -/*----------------------------------------------------------------------*/ - -/* - * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's - * how these chips are normally wired. This translates to both 8 and 16 - * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4). - * - * For now we assume that configuration, or any other one which ignores - * the two LSBs for NAND access ... so we can issue 32-bit reads/writes - * and have that transparently morphed into multiple NAND operations. - */ -static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) - ioread32_rep(chip->IO_ADDR_R, buf, len >> 2); - else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) - ioread16_rep(chip->IO_ADDR_R, buf, len >> 1); - else - ioread8_rep(chip->IO_ADDR_R, buf, len); -} - -static void nand_davinci_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) - iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2); - else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) - iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1); - else - iowrite8_rep(chip->IO_ADDR_R, buf, len); -} - -/* - * Check hardware register for wait status. Returns 1 if device is ready, - * 0 if it is still busy. - */ -static int nand_davinci_dev_ready(struct mtd_info *mtd) -{ - struct davinci_nand_info *info = to_davinci_nand(mtd); - - return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0); -} - -/*----------------------------------------------------------------------*/ - -/* An ECC layout for using 4-bit ECC with small-page flash, storing - * ten ECC bytes plus the manufacturer's bad block marker byte, and - * and not overlapping the default BBT markers. - */ -static int hwecc4_ooblayout_small_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 2) - return -ERANGE; - - if (!section) { - oobregion->offset = 0; - oobregion->length = 5; - } else if (section == 1) { - oobregion->offset = 6; - oobregion->length = 2; - } else { - oobregion->offset = 13; - oobregion->length = 3; - } - - return 0; -} - -static int hwecc4_ooblayout_small_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 1) - return -ERANGE; - - if (!section) { - oobregion->offset = 8; - oobregion->length = 5; - } else { - oobregion->offset = 16; - oobregion->length = mtd->oobsize - 16; - } - - return 0; -} - -static const struct mtd_ooblayout_ops hwecc4_small_ooblayout_ops = { - .ecc = hwecc4_ooblayout_small_ecc, - .free = hwecc4_ooblayout_small_free, -}; - -#if defined(CONFIG_OF) -static const struct of_device_id davinci_nand_of_match[] = { - {.compatible = "ti,davinci-nand", }, - {.compatible = "ti,keystone-nand", }, - {}, -}; -MODULE_DEVICE_TABLE(of, davinci_nand_of_match); - -static struct davinci_nand_pdata - *nand_davinci_get_pdata(struct platform_device *pdev) -{ - if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) { - struct davinci_nand_pdata *pdata; - const char *mode; - u32 prop; - - pdata = devm_kzalloc(&pdev->dev, - sizeof(struct davinci_nand_pdata), - GFP_KERNEL); - pdev->dev.platform_data = pdata; - if (!pdata) - return ERR_PTR(-ENOMEM); - if (!of_property_read_u32(pdev->dev.of_node, - "ti,davinci-chipselect", &prop)) - pdev->id = prop; - else - return ERR_PTR(-EINVAL); - - if (!of_property_read_u32(pdev->dev.of_node, - "ti,davinci-mask-ale", &prop)) - pdata->mask_ale = prop; - if (!of_property_read_u32(pdev->dev.of_node, - "ti,davinci-mask-cle", &prop)) - pdata->mask_cle = prop; - if (!of_property_read_u32(pdev->dev.of_node, - "ti,davinci-mask-chipsel", &prop)) - pdata->mask_chipsel = prop; - if (!of_property_read_string(pdev->dev.of_node, - "ti,davinci-ecc-mode", &mode)) { - if (!strncmp("none", mode, 4)) - pdata->ecc_mode = NAND_ECC_NONE; - if (!strncmp("soft", mode, 4)) - pdata->ecc_mode = NAND_ECC_SOFT; - if (!strncmp("hw", mode, 2)) - pdata->ecc_mode = NAND_ECC_HW; - } - if (!of_property_read_u32(pdev->dev.of_node, - "ti,davinci-ecc-bits", &prop)) - pdata->ecc_bits = prop; - - if (!of_property_read_u32(pdev->dev.of_node, - "ti,davinci-nand-buswidth", &prop) && prop == 16) - pdata->options |= NAND_BUSWIDTH_16; - - if (of_property_read_bool(pdev->dev.of_node, - "ti,davinci-nand-use-bbt")) - pdata->bbt_options = NAND_BBT_USE_FLASH; - - /* - * Since kernel v4.8, this driver has been fixed to enable - * use of 4-bit hardware ECC with subpages and verified on - * TI's keystone EVMs (K2L, K2HK and K2E). - * However, in the interest of not breaking systems using - * existing UBI partitions, sub-page writes are not being - * (re)enabled. If you want to use subpage writes on Keystone - * platforms (i.e. do not have any existing UBI partitions), - * then use "ti,davinci-nand" as the compatible in your - * device-tree file. - */ - if (of_device_is_compatible(pdev->dev.of_node, - "ti,keystone-nand")) { - pdata->options |= NAND_NO_SUBPAGE_WRITE; - } - } - - return dev_get_platdata(&pdev->dev); -} -#else -static struct davinci_nand_pdata - *nand_davinci_get_pdata(struct platform_device *pdev) -{ - return dev_get_platdata(&pdev->dev); -} -#endif - -static int nand_davinci_probe(struct platform_device *pdev) -{ - struct davinci_nand_pdata *pdata; - struct davinci_nand_info *info; - struct resource *res1; - struct resource *res2; - void __iomem *vaddr; - void __iomem *base; - int ret; - uint32_t val; - struct mtd_info *mtd; - - pdata = nand_davinci_get_pdata(pdev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - - /* insist on board-specific configuration */ - if (!pdata) - return -ENODEV; - - /* which external chipselect will we be managing? */ - if (pdev->id < 0 || pdev->id > 3) - return -ENODEV; - - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - platform_set_drvdata(pdev, info); - - res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res1 || !res2) { - dev_err(&pdev->dev, "resource missing\n"); - return -EINVAL; - } - - vaddr = devm_ioremap_resource(&pdev->dev, res1); - if (IS_ERR(vaddr)) - return PTR_ERR(vaddr); - - /* - * This registers range is used to setup NAND settings. In case with - * TI AEMIF driver, the same memory address range is requested already - * by AEMIF, so we cannot request it twice, just ioremap. - * The AEMIF and NAND drivers not use the same registers in this range. - */ - base = devm_ioremap(&pdev->dev, res2->start, resource_size(res2)); - if (!base) { - dev_err(&pdev->dev, "ioremap failed for resource %pR\n", res2); - return -EADDRNOTAVAIL; - } - - info->dev = &pdev->dev; - info->base = base; - info->vaddr = vaddr; - - mtd = nand_to_mtd(&info->chip); - mtd->dev.parent = &pdev->dev; - nand_set_flash_node(&info->chip, pdev->dev.of_node); - - info->chip.IO_ADDR_R = vaddr; - info->chip.IO_ADDR_W = vaddr; - info->chip.chip_delay = 0; - info->chip.select_chip = nand_davinci_select_chip; - - /* options such as NAND_BBT_USE_FLASH */ - info->chip.bbt_options = pdata->bbt_options; - /* options such as 16-bit widths */ - info->chip.options = pdata->options; - info->chip.bbt_td = pdata->bbt_td; - info->chip.bbt_md = pdata->bbt_md; - info->timing = pdata->timing; - - info->ioaddr = (uint32_t __force) vaddr; - - info->current_cs = info->ioaddr; - info->core_chipsel = pdev->id; - info->mask_chipsel = pdata->mask_chipsel; - - /* use nandboot-capable ALE/CLE masks by default */ - info->mask_ale = pdata->mask_ale ? : MASK_ALE; - info->mask_cle = pdata->mask_cle ? : MASK_CLE; - - /* Set address of hardware control function */ - info->chip.cmd_ctrl = nand_davinci_hwcontrol; - info->chip.dev_ready = nand_davinci_dev_ready; - - /* Speed up buffer I/O */ - info->chip.read_buf = nand_davinci_read_buf; - info->chip.write_buf = nand_davinci_write_buf; - - /* Use board-specific ECC config */ - info->chip.ecc.mode = pdata->ecc_mode; - - ret = -EINVAL; - - info->clk = devm_clk_get(&pdev->dev, "aemif"); - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(info->clk); - if (ret < 0) { - dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n", - ret); - goto err_clk_enable; - } - - spin_lock_irq(&davinci_nand_lock); - - /* put CSxNAND into NAND mode */ - val = davinci_nand_readl(info, NANDFCR_OFFSET); - val |= BIT(info->core_chipsel); - davinci_nand_writel(info, NANDFCR_OFFSET, val); - - spin_unlock_irq(&davinci_nand_lock); - - /* Scan to find existence of the device(s) */ - ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL); - if (ret < 0) { - dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); - goto err; - } - - switch (info->chip.ecc.mode) { - case NAND_ECC_NONE: - pdata->ecc_bits = 0; - break; - case NAND_ECC_SOFT: - pdata->ecc_bits = 0; - /* - * This driver expects Hamming based ECC when ecc_mode is set - * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to - * avoid adding an extra ->ecc_algo field to - * davinci_nand_pdata. - */ - info->chip.ecc.algo = NAND_ECC_HAMMING; - break; - case NAND_ECC_HW: - if (pdata->ecc_bits == 4) { - /* No sanity checks: CPUs must support this, - * and the chips may not use NAND_BUSWIDTH_16. - */ - - /* No sharing 4-bit hardware between chipselects yet */ - spin_lock_irq(&davinci_nand_lock); - if (ecc4_busy) - ret = -EBUSY; - else - ecc4_busy = true; - spin_unlock_irq(&davinci_nand_lock); - - if (ret == -EBUSY) - return ret; - - info->chip.ecc.calculate = nand_davinci_calculate_4bit; - info->chip.ecc.correct = nand_davinci_correct_4bit; - info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; - info->chip.ecc.bytes = 10; - info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; - info->chip.ecc.algo = NAND_ECC_BCH; - } else { - /* 1bit ecc hamming */ - info->chip.ecc.calculate = nand_davinci_calculate_1bit; - info->chip.ecc.correct = nand_davinci_correct_1bit; - info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; - info->chip.ecc.bytes = 3; - info->chip.ecc.algo = NAND_ECC_HAMMING; - } - info->chip.ecc.size = 512; - info->chip.ecc.strength = pdata->ecc_bits; - break; - default: - return -EINVAL; - } - - /* Update ECC layout if needed ... for 1-bit HW ECC, the default - * is OK, but it allocates 6 bytes when only 3 are needed (for - * each 512 bytes). For the 4-bit HW ECC, that default is not - * usable: 10 bytes are needed, not 6. - */ - if (pdata->ecc_bits == 4) { - int chunks = mtd->writesize / 512; - - if (!chunks || mtd->oobsize < 16) { - dev_dbg(&pdev->dev, "too small\n"); - ret = -EINVAL; - goto err; - } - - /* For small page chips, preserve the manufacturer's - * badblock marking data ... and make sure a flash BBT - * table marker fits in the free bytes. - */ - if (chunks == 1) { - mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops); - } else if (chunks == 4 || chunks == 8) { - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); - info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; - } else { - ret = -EIO; - goto err; - } - } - - ret = nand_scan_tail(mtd); - if (ret < 0) - goto err; - - if (pdata->parts) - ret = mtd_device_parse_register(mtd, NULL, NULL, - pdata->parts, pdata->nr_parts); - else - ret = mtd_device_register(mtd, NULL, 0); - if (ret < 0) - goto err; - - val = davinci_nand_readl(info, NRCSR_OFFSET); - dev_info(&pdev->dev, "controller rev. %d.%d\n", - (val >> 8) & 0xff, val & 0xff); - - return 0; - -err: - clk_disable_unprepare(info->clk); - -err_clk_enable: - spin_lock_irq(&davinci_nand_lock); - if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) - ecc4_busy = false; - spin_unlock_irq(&davinci_nand_lock); - return ret; -} - -static int nand_davinci_remove(struct platform_device *pdev) -{ - struct davinci_nand_info *info = platform_get_drvdata(pdev); - - spin_lock_irq(&davinci_nand_lock); - if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) - ecc4_busy = false; - spin_unlock_irq(&davinci_nand_lock); - - nand_release(nand_to_mtd(&info->chip)); - - clk_disable_unprepare(info->clk); - - return 0; -} - -static struct platform_driver nand_davinci_driver = { - .probe = nand_davinci_probe, - .remove = nand_davinci_remove, - .driver = { - .name = "davinci_nand", - .of_match_table = of_match_ptr(davinci_nand_of_match), - }, -}; -MODULE_ALIAS("platform:davinci_nand"); - -module_platform_driver(nand_davinci_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Texas Instruments"); -MODULE_DESCRIPTION("Davinci NAND flash driver"); - diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c deleted file mode 100644 index 313c7f50621b..000000000000 --- a/drivers/mtd/nand/denali.c +++ /dev/null @@ -1,1408 +0,0 @@ -/* - * NAND Flash Controller Device Driver - * Copyright © 2009-2010, Intel Corporation and its suppliers. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "denali.h" - -MODULE_LICENSE("GPL"); - -#define DENALI_NAND_NAME "denali-nand" - -/* for Indexed Addressing */ -#define DENALI_INDEXED_CTRL 0x00 -#define DENALI_INDEXED_DATA 0x10 - -#define DENALI_MAP00 (0 << 26) /* direct access to buffer */ -#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */ -#define DENALI_MAP10 (2 << 26) /* high-level control plane */ -#define DENALI_MAP11 (3 << 26) /* direct controller access */ - -/* MAP11 access cycle type */ -#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */ -#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */ -#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */ - -/* MAP10 commands */ -#define DENALI_ERASE 0x01 - -#define DENALI_BANK(denali) ((denali)->active_bank << 24) - -#define DENALI_INVALID_BANK -1 -#define DENALI_NR_BANKS 4 - -/* - * The bus interface clock, clk_x, is phase aligned with the core clock. The - * clk_x is an integral multiple N of the core clk. The value N is configured - * at IP delivery time, and its available value is 4, 5, or 6. We need to align - * to the largest value to make it work with any possible configuration. - */ -#define DENALI_CLK_X_MULT 6 - -static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand); -} - -/* - * Direct Addressing - the slave address forms the control information (command - * type, bank, block, and page address). The slave data is the actual data to - * be transferred. This mode requires 28 bits of address region allocated. - */ -static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr) -{ - return ioread32(denali->host + addr); -} - -static void denali_direct_write(struct denali_nand_info *denali, u32 addr, - u32 data) -{ - iowrite32(data, denali->host + addr); -} - -/* - * Indexed Addressing - address translation module intervenes in passing the - * control information. This mode reduces the required address range. The - * control information and transferred data are latched by the registers in - * the translation module. - */ -static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr) -{ - iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); - return ioread32(denali->host + DENALI_INDEXED_DATA); -} - -static void denali_indexed_write(struct denali_nand_info *denali, u32 addr, - u32 data) -{ - iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); - iowrite32(data, denali->host + DENALI_INDEXED_DATA); -} - -/* - * Use the configuration feature register to determine the maximum number of - * banks that the hardware supports. - */ -static void denali_detect_max_banks(struct denali_nand_info *denali) -{ - uint32_t features = ioread32(denali->reg + FEATURES); - - denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features); - - /* the encoding changed from rev 5.0 to 5.1 */ - if (denali->revision < 0x0501) - denali->max_banks <<= 1; -} - -static void denali_enable_irq(struct denali_nand_info *denali) -{ - int i; - - for (i = 0; i < DENALI_NR_BANKS; i++) - iowrite32(U32_MAX, denali->reg + INTR_EN(i)); - iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE); -} - -static void denali_disable_irq(struct denali_nand_info *denali) -{ - int i; - - for (i = 0; i < DENALI_NR_BANKS; i++) - iowrite32(0, denali->reg + INTR_EN(i)); - iowrite32(0, denali->reg + GLOBAL_INT_ENABLE); -} - -static void denali_clear_irq(struct denali_nand_info *denali, - int bank, uint32_t irq_status) -{ - /* write one to clear bits */ - iowrite32(irq_status, denali->reg + INTR_STATUS(bank)); -} - -static void denali_clear_irq_all(struct denali_nand_info *denali) -{ - int i; - - for (i = 0; i < DENALI_NR_BANKS; i++) - denali_clear_irq(denali, i, U32_MAX); -} - -static irqreturn_t denali_isr(int irq, void *dev_id) -{ - struct denali_nand_info *denali = dev_id; - irqreturn_t ret = IRQ_NONE; - uint32_t irq_status; - int i; - - spin_lock(&denali->irq_lock); - - for (i = 0; i < DENALI_NR_BANKS; i++) { - irq_status = ioread32(denali->reg + INTR_STATUS(i)); - if (irq_status) - ret = IRQ_HANDLED; - - denali_clear_irq(denali, i, irq_status); - - if (i != denali->active_bank) - continue; - - denali->irq_status |= irq_status; - - if (denali->irq_status & denali->irq_mask) - complete(&denali->complete); - } - - spin_unlock(&denali->irq_lock); - - return ret; -} - -static void denali_reset_irq(struct denali_nand_info *denali) -{ - unsigned long flags; - - spin_lock_irqsave(&denali->irq_lock, flags); - denali->irq_status = 0; - denali->irq_mask = 0; - spin_unlock_irqrestore(&denali->irq_lock, flags); -} - -static uint32_t denali_wait_for_irq(struct denali_nand_info *denali, - uint32_t irq_mask) -{ - unsigned long time_left, flags; - uint32_t irq_status; - - spin_lock_irqsave(&denali->irq_lock, flags); - - irq_status = denali->irq_status; - - if (irq_mask & irq_status) { - /* return immediately if the IRQ has already happened. */ - spin_unlock_irqrestore(&denali->irq_lock, flags); - return irq_status; - } - - denali->irq_mask = irq_mask; - reinit_completion(&denali->complete); - spin_unlock_irqrestore(&denali->irq_lock, flags); - - time_left = wait_for_completion_timeout(&denali->complete, - msecs_to_jiffies(1000)); - if (!time_left) { - dev_err(denali->dev, "timeout while waiting for irq 0x%x\n", - irq_mask); - return 0; - } - - return denali->irq_status; -} - -static uint32_t denali_check_irq(struct denali_nand_info *denali) -{ - unsigned long flags; - uint32_t irq_status; - - spin_lock_irqsave(&denali->irq_lock, flags); - irq_status = denali->irq_status; - spin_unlock_irqrestore(&denali->irq_lock, flags); - - return irq_status; -} - -static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); - int i; - - for (i = 0; i < len; i++) - buf[i] = denali->host_read(denali, addr); -} - -static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); - int i; - - for (i = 0; i < len; i++) - denali->host_write(denali, addr, buf[i]); -} - -static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); - uint16_t *buf16 = (uint16_t *)buf; - int i; - - for (i = 0; i < len / 2; i++) - buf16[i] = denali->host_read(denali, addr); -} - -static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); - const uint16_t *buf16 = (const uint16_t *)buf; - int i; - - for (i = 0; i < len / 2; i++) - denali->host_write(denali, addr, buf16[i]); -} - -static uint8_t denali_read_byte(struct mtd_info *mtd) -{ - uint8_t byte; - - denali_read_buf(mtd, &byte, 1); - - return byte; -} - -static void denali_write_byte(struct mtd_info *mtd, uint8_t byte) -{ - denali_write_buf(mtd, &byte, 1); -} - -static uint16_t denali_read_word(struct mtd_info *mtd) -{ - uint16_t word; - - denali_read_buf16(mtd, (uint8_t *)&word, 2); - - return word; -} - -static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t type; - - if (ctrl & NAND_CLE) - type = DENALI_MAP11_CMD; - else if (ctrl & NAND_ALE) - type = DENALI_MAP11_ADDR; - else - return; - - /* - * Some commands are followed by chip->dev_ready or chip->waitfunc. - * irq_status must be cleared here to catch the R/B# interrupt later. - */ - if (ctrl & NAND_CTRL_CHANGE) - denali_reset_irq(denali); - - denali->host_write(denali, DENALI_BANK(denali) | type, dat); -} - -static int denali_dev_ready(struct mtd_info *mtd) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - return !!(denali_check_irq(denali) & INTR__INT_ACT); -} - -static int denali_check_erased_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, - unsigned long uncor_ecc_flags, - unsigned int max_bitflips) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - uint8_t *ecc_code = chip->oob_poi + denali->oob_skip_bytes; - int ecc_steps = chip->ecc.steps; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - int i, stat; - - for (i = 0; i < ecc_steps; i++) { - if (!(uncor_ecc_flags & BIT(i))) - continue; - - stat = nand_check_erased_ecc_chunk(buf, ecc_size, - ecc_code, ecc_bytes, - NULL, 0, - chip->ecc.strength); - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - - buf += ecc_size; - ecc_code += ecc_bytes; - } - - return max_bitflips; -} - -static int denali_hw_ecc_fixup(struct mtd_info *mtd, - struct denali_nand_info *denali, - unsigned long *uncor_ecc_flags) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int bank = denali->active_bank; - uint32_t ecc_cor; - unsigned int max_bitflips; - - ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank)); - ecc_cor >>= ECC_COR_INFO__SHIFT(bank); - - if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) { - /* - * This flag is set when uncorrectable error occurs at least in - * one ECC sector. We can not know "how many sectors", or - * "which sector(s)". We need erase-page check for all sectors. - */ - *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0); - return 0; - } - - max_bitflips = FIELD_GET(ECC_COR_INFO__MAX_ERRORS, ecc_cor); - - /* - * The register holds the maximum of per-sector corrected bitflips. - * This is suitable for the return value of the ->read_page() callback. - * Unfortunately, we can not know the total number of corrected bits in - * the page. Increase the stats by max_bitflips. (compromised solution) - */ - mtd->ecc_stats.corrected += max_bitflips; - - return max_bitflips; -} - -static int denali_sw_ecc_fixup(struct mtd_info *mtd, - struct denali_nand_info *denali, - unsigned long *uncor_ecc_flags, uint8_t *buf) -{ - unsigned int ecc_size = denali->nand.ecc.size; - unsigned int bitflips = 0; - unsigned int max_bitflips = 0; - uint32_t err_addr, err_cor_info; - unsigned int err_byte, err_sector, err_device; - uint8_t err_cor_value; - unsigned int prev_sector = 0; - uint32_t irq_status; - - denali_reset_irq(denali); - - do { - err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS); - err_sector = FIELD_GET(ECC_ERROR_ADDRESS__SECTOR, err_addr); - err_byte = FIELD_GET(ECC_ERROR_ADDRESS__OFFSET, err_addr); - - err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO); - err_cor_value = FIELD_GET(ERR_CORRECTION_INFO__BYTE, - err_cor_info); - err_device = FIELD_GET(ERR_CORRECTION_INFO__DEVICE, - err_cor_info); - - /* reset the bitflip counter when crossing ECC sector */ - if (err_sector != prev_sector) - bitflips = 0; - - if (err_cor_info & ERR_CORRECTION_INFO__UNCOR) { - /* - * Check later if this is a real ECC error, or - * an erased sector. - */ - *uncor_ecc_flags |= BIT(err_sector); - } else if (err_byte < ecc_size) { - /* - * If err_byte is larger than ecc_size, means error - * happened in OOB, so we ignore it. It's no need for - * us to correct it err_device is represented the NAND - * error bits are happened in if there are more than - * one NAND connected. - */ - int offset; - unsigned int flips_in_byte; - - offset = (err_sector * ecc_size + err_byte) * - denali->devs_per_cs + err_device; - - /* correct the ECC error */ - flips_in_byte = hweight8(buf[offset] ^ err_cor_value); - buf[offset] ^= err_cor_value; - mtd->ecc_stats.corrected += flips_in_byte; - bitflips += flips_in_byte; - - max_bitflips = max(max_bitflips, bitflips); - } - - prev_sector = err_sector; - } while (!(err_cor_info & ERR_CORRECTION_INFO__LAST_ERR)); - - /* - * Once handle all ECC errors, controller will trigger an - * ECC_TRANSACTION_DONE interrupt. - */ - irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE); - if (!(irq_status & INTR__ECC_TRANSACTION_DONE)) - return -EIO; - - return max_bitflips; -} - -static void denali_setup_dma64(struct denali_nand_info *denali, - dma_addr_t dma_addr, int page, int write) -{ - uint32_t mode; - const int page_count = 1; - - mode = DENALI_MAP10 | DENALI_BANK(denali) | page; - - /* DMA is a three step process */ - - /* - * 1. setup transfer type, interrupt when complete, - * burst len = 64 bytes, the number of pages - */ - denali->host_write(denali, mode, - 0x01002000 | (64 << 16) | (write << 8) | page_count); - - /* 2. set memory low address */ - denali->host_write(denali, mode, lower_32_bits(dma_addr)); - - /* 3. set memory high address */ - denali->host_write(denali, mode, upper_32_bits(dma_addr)); -} - -static void denali_setup_dma32(struct denali_nand_info *denali, - dma_addr_t dma_addr, int page, int write) -{ - uint32_t mode; - const int page_count = 1; - - mode = DENALI_MAP10 | DENALI_BANK(denali); - - /* DMA is a four step process */ - - /* 1. setup transfer type and # of pages */ - denali->host_write(denali, mode | page, - 0x2000 | (write << 8) | page_count); - - /* 2. set memory high address bits 23:8 */ - denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200); - - /* 3. set memory low address bits 23:8 */ - denali->host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300); - - /* 4. interrupt when complete, burst len = 64 bytes */ - denali->host_write(denali, mode | 0x14000, 0x2400); -} - -static int denali_pio_read(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw) -{ - u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; - uint32_t *buf32 = (uint32_t *)buf; - uint32_t irq_status, ecc_err_mask; - int i; - - if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) - ecc_err_mask = INTR__ECC_UNCOR_ERR; - else - ecc_err_mask = INTR__ECC_ERR; - - denali_reset_irq(denali); - - for (i = 0; i < size / 4; i++) - *buf32++ = denali->host_read(denali, addr); - - irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC); - if (!(irq_status & INTR__PAGE_XFER_INC)) - return -EIO; - - if (irq_status & INTR__ERASED_PAGE) - memset(buf, 0xff, size); - - return irq_status & ecc_err_mask ? -EBADMSG : 0; -} - -static int denali_pio_write(struct denali_nand_info *denali, - const void *buf, size_t size, int page, int raw) -{ - u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; - const uint32_t *buf32 = (uint32_t *)buf; - uint32_t irq_status; - int i; - - denali_reset_irq(denali); - - for (i = 0; i < size / 4; i++) - denali->host_write(denali, addr, *buf32++); - - irq_status = denali_wait_for_irq(denali, - INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL); - if (!(irq_status & INTR__PROGRAM_COMP)) - return -EIO; - - return 0; -} - -static int denali_pio_xfer(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw, int write) -{ - if (write) - return denali_pio_write(denali, buf, size, page, raw); - else - return denali_pio_read(denali, buf, size, page, raw); -} - -static int denali_dma_xfer(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw, int write) -{ - dma_addr_t dma_addr; - uint32_t irq_mask, irq_status, ecc_err_mask; - enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - int ret = 0; - - dma_addr = dma_map_single(denali->dev, buf, size, dir); - if (dma_mapping_error(denali->dev, dma_addr)) { - dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n"); - return denali_pio_xfer(denali, buf, size, page, raw, write); - } - - if (write) { - /* - * INTR__PROGRAM_COMP is never asserted for the DMA transfer. - * We can use INTR__DMA_CMD_COMP instead. This flag is asserted - * when the page program is completed. - */ - irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL; - ecc_err_mask = 0; - } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) { - irq_mask = INTR__DMA_CMD_COMP; - ecc_err_mask = INTR__ECC_UNCOR_ERR; - } else { - irq_mask = INTR__DMA_CMD_COMP; - ecc_err_mask = INTR__ECC_ERR; - } - - iowrite32(DMA_ENABLE__FLAG, denali->reg + DMA_ENABLE); - - denali_reset_irq(denali); - denali->setup_dma(denali, dma_addr, page, write); - - irq_status = denali_wait_for_irq(denali, irq_mask); - if (!(irq_status & INTR__DMA_CMD_COMP)) - ret = -EIO; - else if (irq_status & ecc_err_mask) - ret = -EBADMSG; - - iowrite32(0, denali->reg + DMA_ENABLE); - - dma_unmap_single(denali->dev, dma_addr, size, dir); - - if (irq_status & INTR__ERASED_PAGE) - memset(buf, 0xff, size); - - return ret; -} - -static int denali_data_xfer(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw, int write) -{ - iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE); - iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0, - denali->reg + TRANSFER_SPARE_REG); - - if (denali->dma_avail) - return denali_dma_xfer(denali, buf, size, page, raw, write); - else - return denali_pio_xfer(denali, buf, size, page, raw, write); -} - -static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, - int page, int write) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - int writesize = mtd->writesize; - int oobsize = mtd->oobsize; - uint8_t *bufpoi = chip->oob_poi; - int ecc_steps = chip->ecc.steps; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - int oob_skip = denali->oob_skip_bytes; - size_t size = writesize + oobsize; - int i, pos, len; - - /* BBM at the beginning of the OOB area */ - if (write) - nand_prog_page_begin_op(chip, page, writesize, bufpoi, - oob_skip); - else - nand_read_page_op(chip, page, writesize, bufpoi, oob_skip); - bufpoi += oob_skip; - - /* OOB ECC */ - for (i = 0; i < ecc_steps; i++) { - pos = ecc_size + i * (ecc_size + ecc_bytes); - len = ecc_bytes; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - if (write) - nand_change_write_column_op(chip, pos, bufpoi, len, - false); - else - nand_change_read_column_op(chip, pos, bufpoi, len, - false); - bufpoi += len; - if (len < ecc_bytes) { - len = ecc_bytes - len; - if (write) - nand_change_write_column_op(chip, writesize + - oob_skip, bufpoi, - len, false); - else - nand_change_read_column_op(chip, writesize + - oob_skip, bufpoi, - len, false); - bufpoi += len; - } - } - - /* OOB free */ - len = oobsize - (bufpoi - chip->oob_poi); - if (write) - nand_change_write_column_op(chip, size - len, bufpoi, len, - false); - else - nand_change_read_column_op(chip, size - len, bufpoi, len, - false); -} - -static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - int writesize = mtd->writesize; - int oobsize = mtd->oobsize; - int ecc_steps = chip->ecc.steps; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - void *tmp_buf = denali->buf; - int oob_skip = denali->oob_skip_bytes; - size_t size = writesize + oobsize; - int ret, i, pos, len; - - ret = denali_data_xfer(denali, tmp_buf, size, page, 1, 0); - if (ret) - return ret; - - /* Arrange the buffer for syndrome payload/ecc layout */ - if (buf) { - for (i = 0; i < ecc_steps; i++) { - pos = i * (ecc_size + ecc_bytes); - len = ecc_size; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - memcpy(buf, tmp_buf + pos, len); - buf += len; - if (len < ecc_size) { - len = ecc_size - len; - memcpy(buf, tmp_buf + writesize + oob_skip, - len); - buf += len; - } - } - } - - if (oob_required) { - uint8_t *oob = chip->oob_poi; - - /* BBM at the beginning of the OOB area */ - memcpy(oob, tmp_buf + writesize, oob_skip); - oob += oob_skip; - - /* OOB ECC */ - for (i = 0; i < ecc_steps; i++) { - pos = ecc_size + i * (ecc_size + ecc_bytes); - len = ecc_bytes; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - memcpy(oob, tmp_buf + pos, len); - oob += len; - if (len < ecc_bytes) { - len = ecc_bytes - len; - memcpy(oob, tmp_buf + writesize + oob_skip, - len); - oob += len; - } - } - - /* OOB free */ - len = oobsize - (oob - chip->oob_poi); - memcpy(oob, tmp_buf + size - len, len); - } - - return 0; -} - -static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - denali_oob_xfer(mtd, chip, page, 0); - - return 0; -} - -static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - denali_reset_irq(denali); - - denali_oob_xfer(mtd, chip, page, 1); - - return nand_prog_page_end_op(chip); -} - -static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - unsigned long uncor_ecc_flags = 0; - int stat = 0; - int ret; - - ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0); - if (ret && ret != -EBADMSG) - return ret; - - if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) - stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags); - else if (ret == -EBADMSG) - stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf); - - if (stat < 0) - return stat; - - if (uncor_ecc_flags) { - ret = denali_read_oob(mtd, chip, page); - if (ret) - return ret; - - stat = denali_check_erased_page(mtd, chip, buf, - uncor_ecc_flags, stat); - } - - return stat; -} - -static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - int writesize = mtd->writesize; - int oobsize = mtd->oobsize; - int ecc_steps = chip->ecc.steps; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - void *tmp_buf = denali->buf; - int oob_skip = denali->oob_skip_bytes; - size_t size = writesize + oobsize; - int i, pos, len; - - /* - * Fill the buffer with 0xff first except the full page transfer. - * This simplifies the logic. - */ - if (!buf || !oob_required) - memset(tmp_buf, 0xff, size); - - /* Arrange the buffer for syndrome payload/ecc layout */ - if (buf) { - for (i = 0; i < ecc_steps; i++) { - pos = i * (ecc_size + ecc_bytes); - len = ecc_size; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - memcpy(tmp_buf + pos, buf, len); - buf += len; - if (len < ecc_size) { - len = ecc_size - len; - memcpy(tmp_buf + writesize + oob_skip, buf, - len); - buf += len; - } - } - } - - if (oob_required) { - const uint8_t *oob = chip->oob_poi; - - /* BBM at the beginning of the OOB area */ - memcpy(tmp_buf + writesize, oob, oob_skip); - oob += oob_skip; - - /* OOB ECC */ - for (i = 0; i < ecc_steps; i++) { - pos = ecc_size + i * (ecc_size + ecc_bytes); - len = ecc_bytes; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - memcpy(tmp_buf + pos, oob, len); - oob += len; - if (len < ecc_bytes) { - len = ecc_bytes - len; - memcpy(tmp_buf + writesize + oob_skip, oob, - len); - oob += len; - } - } - - /* OOB free */ - len = oobsize - (oob - chip->oob_poi); - memcpy(tmp_buf + size - len, oob, len); - } - - return denali_data_xfer(denali, tmp_buf, size, page, 1, 1); -} - -static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - return denali_data_xfer(denali, (void *)buf, mtd->writesize, - page, 0, 1); -} - -static void denali_select_chip(struct mtd_info *mtd, int chip) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - denali->active_bank = chip; -} - -static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t irq_status; - - /* R/B# pin transitioned from low to high? */ - irq_status = denali_wait_for_irq(denali, INTR__INT_ACT); - - return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL; -} - -static int denali_erase(struct mtd_info *mtd, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t irq_status; - - denali_reset_irq(denali); - - denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page, - DENALI_ERASE); - - /* wait for erase to complete or failure to occur */ - irq_status = denali_wait_for_irq(denali, - INTR__ERASE_COMP | INTR__ERASE_FAIL); - - return irq_status & INTR__ERASE_COMP ? 0 : -EIO; -} - -static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, - const struct nand_data_interface *conf) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - const struct nand_sdr_timings *timings; - unsigned long t_clk; - int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data; - int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup; - int addr_2_data_mask; - uint32_t tmp; - - timings = nand_get_sdr_timings(conf); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - /* clk_x period in picoseconds */ - t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate); - if (!t_clk) - return -EINVAL; - - if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) - return 0; - - /* tREA -> ACC_CLKS */ - acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk); - acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE); - - tmp = ioread32(denali->reg + ACC_CLKS); - tmp &= ~ACC_CLKS__VALUE; - tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks); - iowrite32(tmp, denali->reg + ACC_CLKS); - - /* tRWH -> RE_2_WE */ - re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk); - re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE); - - tmp = ioread32(denali->reg + RE_2_WE); - tmp &= ~RE_2_WE__VALUE; - tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we); - iowrite32(tmp, denali->reg + RE_2_WE); - - /* tRHZ -> RE_2_RE */ - re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk); - re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE); - - tmp = ioread32(denali->reg + RE_2_RE); - tmp &= ~RE_2_RE__VALUE; - tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re); - iowrite32(tmp, denali->reg + RE_2_RE); - - /* - * tCCS, tWHR -> WE_2_RE - * - * With WE_2_RE properly set, the Denali controller automatically takes - * care of the delay; the driver need not set NAND_WAIT_TCCS. - */ - we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), - t_clk); - we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE); - - tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE); - tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE; - tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re); - iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE); - - /* tADL -> ADDR_2_DATA */ - - /* for older versions, ADDR_2_DATA is only 6 bit wide */ - addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; - if (denali->revision < 0x0501) - addr_2_data_mask >>= 1; - - addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk); - addr_2_data = min_t(int, addr_2_data, addr_2_data_mask); - - tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA); - tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; - tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data); - iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA); - - /* tREH, tWH -> RDWR_EN_HI_CNT */ - rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min), - t_clk); - rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE); - - tmp = ioread32(denali->reg + RDWR_EN_HI_CNT); - tmp &= ~RDWR_EN_HI_CNT__VALUE; - tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi); - iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT); - - /* tRP, tWP -> RDWR_EN_LO_CNT */ - rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), - t_clk); - rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min), - t_clk); - rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT); - rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi); - rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE); - - tmp = ioread32(denali->reg + RDWR_EN_LO_CNT); - tmp &= ~RDWR_EN_LO_CNT__VALUE; - tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo); - iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT); - - /* tCS, tCEA -> CS_SETUP_CNT */ - cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo, - (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks, - 0); - cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE); - - tmp = ioread32(denali->reg + CS_SETUP_CNT); - tmp &= ~CS_SETUP_CNT__VALUE; - tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup); - iowrite32(tmp, denali->reg + CS_SETUP_CNT); - - return 0; -} - -static void denali_reset_banks(struct denali_nand_info *denali) -{ - u32 irq_status; - int i; - - for (i = 0; i < denali->max_banks; i++) { - denali->active_bank = i; - - denali_reset_irq(denali); - - iowrite32(DEVICE_RESET__BANK(i), - denali->reg + DEVICE_RESET); - - irq_status = denali_wait_for_irq(denali, - INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT); - if (!(irq_status & INTR__INT_ACT)) - break; - } - - dev_dbg(denali->dev, "%d chips connected\n", i); - denali->max_banks = i; -} - -static void denali_hw_init(struct denali_nand_info *denali) -{ - /* - * The REVISION register may not be reliable. Platforms are allowed to - * override it. - */ - if (!denali->revision) - denali->revision = swab16(ioread32(denali->reg + REVISION)); - - /* - * tell driver how many bit controller will skip before - * writing ECC code in OOB, this register may be already - * set by firmware. So we read this value out. - * if this value is 0, just let it be. - */ - denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES); - denali_detect_max_banks(denali); - iowrite32(0x0F, denali->reg + RB_PIN_ENABLED); - iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE); - - iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER); -} - -int denali_calc_ecc_bytes(int step_size, int strength) -{ - /* BCH code. Denali requires ecc.bytes to be multiple of 2 */ - return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2; -} -EXPORT_SYMBOL(denali_calc_ecc_bytes); - -static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, - struct denali_nand_info *denali) -{ - int oobavail = mtd->oobsize - denali->oob_skip_bytes; - int ret; - - /* - * If .size and .strength are already set (usually by DT), - * check if they are supported by this controller. - */ - if (chip->ecc.size && chip->ecc.strength) - return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail); - - /* - * We want .size and .strength closest to the chip's requirement - * unless NAND_ECC_MAXIMIZE is requested. - */ - if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { - ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail); - if (!ret) - return 0; - } - - /* Max ECC strength is the last thing we can do */ - return nand_maximize_ecc(chip, denali->ecc_caps, oobavail); -} - -static int denali_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section) - return -ERANGE; - - oobregion->offset = denali->oob_skip_bytes; - oobregion->length = chip->ecc.total; - - return 0; -} - -static int denali_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section) - return -ERANGE; - - oobregion->offset = chip->ecc.total + denali->oob_skip_bytes; - oobregion->length = mtd->oobsize - oobregion->offset; - - return 0; -} - -static const struct mtd_ooblayout_ops denali_ooblayout_ops = { - .ecc = denali_ooblayout_ecc, - .free = denali_ooblayout_free, -}; - -static int denali_multidev_fixup(struct denali_nand_info *denali) -{ - struct nand_chip *chip = &denali->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - - /* - * Support for multi device: - * When the IP configuration is x16 capable and two x8 chips are - * connected in parallel, DEVICES_CONNECTED should be set to 2. - * In this case, the core framework knows nothing about this fact, - * so we should tell it the _logical_ pagesize and anything necessary. - */ - denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED); - - /* - * On some SoCs, DEVICES_CONNECTED is not auto-detected. - * For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case. - */ - if (denali->devs_per_cs == 0) { - denali->devs_per_cs = 1; - iowrite32(1, denali->reg + DEVICES_CONNECTED); - } - - if (denali->devs_per_cs == 1) - return 0; - - if (denali->devs_per_cs != 2) { - dev_err(denali->dev, "unsupported number of devices %d\n", - denali->devs_per_cs); - return -EINVAL; - } - - /* 2 chips in parallel */ - mtd->size <<= 1; - mtd->erasesize <<= 1; - mtd->writesize <<= 1; - mtd->oobsize <<= 1; - chip->chipsize <<= 1; - chip->page_shift += 1; - chip->phys_erase_shift += 1; - chip->bbt_erase_shift += 1; - chip->chip_shift += 1; - chip->pagemask <<= 1; - chip->ecc.size <<= 1; - chip->ecc.bytes <<= 1; - chip->ecc.strength <<= 1; - denali->oob_skip_bytes <<= 1; - - return 0; -} - -int denali_init(struct denali_nand_info *denali) -{ - struct nand_chip *chip = &denali->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - u32 features = ioread32(denali->reg + FEATURES); - int ret; - - mtd->dev.parent = denali->dev; - denali_hw_init(denali); - - init_completion(&denali->complete); - spin_lock_init(&denali->irq_lock); - - denali_clear_irq_all(denali); - - ret = devm_request_irq(denali->dev, denali->irq, denali_isr, - IRQF_SHARED, DENALI_NAND_NAME, denali); - if (ret) { - dev_err(denali->dev, "Unable to request IRQ\n"); - return ret; - } - - denali_enable_irq(denali); - denali_reset_banks(denali); - - denali->active_bank = DENALI_INVALID_BANK; - - nand_set_flash_node(chip, denali->dev->of_node); - /* Fallback to the default name if DT did not give "label" property */ - if (!mtd->name) - mtd->name = "denali-nand"; - - chip->select_chip = denali_select_chip; - chip->read_byte = denali_read_byte; - chip->write_byte = denali_write_byte; - chip->read_word = denali_read_word; - chip->cmd_ctrl = denali_cmd_ctrl; - chip->dev_ready = denali_dev_ready; - chip->waitfunc = denali_waitfunc; - - if (features & FEATURES__INDEX_ADDR) { - denali->host_read = denali_indexed_read; - denali->host_write = denali_indexed_write; - } else { - denali->host_read = denali_direct_read; - denali->host_write = denali_direct_write; - } - - /* clk rate info is needed for setup_data_interface */ - if (denali->clk_x_rate) - chip->setup_data_interface = denali_setup_data_interface; - - ret = nand_scan_ident(mtd, denali->max_banks, NULL); - if (ret) - goto disable_irq; - - if (ioread32(denali->reg + FEATURES) & FEATURES__DMA) - denali->dma_avail = 1; - - if (denali->dma_avail) { - int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32; - - ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit)); - if (ret) { - dev_info(denali->dev, - "Failed to set DMA mask. Disabling DMA.\n"); - denali->dma_avail = 0; - } - } - - if (denali->dma_avail) { - chip->options |= NAND_USE_BOUNCE_BUFFER; - chip->buf_align = 16; - if (denali->caps & DENALI_CAP_DMA_64BIT) - denali->setup_dma = denali_setup_dma64; - else - denali->setup_dma = denali_setup_dma32; - } - - chip->bbt_options |= NAND_BBT_USE_FLASH; - chip->bbt_options |= NAND_BBT_NO_OOB; - chip->ecc.mode = NAND_ECC_HW_SYNDROME; - chip->options |= NAND_NO_SUBPAGE_WRITE; - - ret = denali_ecc_setup(mtd, chip, denali); - if (ret) { - dev_err(denali->dev, "Failed to setup ECC settings.\n"); - goto disable_irq; - } - - dev_dbg(denali->dev, - "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", - chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); - - iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) | - FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength), - denali->reg + ECC_CORRECTION); - iowrite32(mtd->erasesize / mtd->writesize, - denali->reg + PAGES_PER_BLOCK); - iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0, - denali->reg + DEVICE_WIDTH); - iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG, - denali->reg + TWO_ROW_ADDR_CYCLES); - iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE); - iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE); - - iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE); - iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE); - /* chip->ecc.steps is set by nand_scan_tail(); not available here */ - iowrite32(mtd->writesize / chip->ecc.size, - denali->reg + CFG_NUM_DATA_BLOCKS); - - mtd_set_ooblayout(mtd, &denali_ooblayout_ops); - - if (chip->options & NAND_BUSWIDTH_16) { - chip->read_buf = denali_read_buf16; - chip->write_buf = denali_write_buf16; - } else { - chip->read_buf = denali_read_buf; - chip->write_buf = denali_write_buf; - } - chip->ecc.read_page = denali_read_page; - chip->ecc.read_page_raw = denali_read_page_raw; - chip->ecc.write_page = denali_write_page; - chip->ecc.write_page_raw = denali_write_page_raw; - chip->ecc.read_oob = denali_read_oob; - chip->ecc.write_oob = denali_write_oob; - chip->erase = denali_erase; - - ret = denali_multidev_fixup(denali); - if (ret) - goto disable_irq; - - /* - * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not - * use devm_kmalloc() because the memory allocated by devm_ does not - * guarantee DMA-safe alignment. - */ - denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); - if (!denali->buf) { - ret = -ENOMEM; - goto disable_irq; - } - - ret = nand_scan_tail(mtd); - if (ret) - goto free_buf; - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - dev_err(denali->dev, "Failed to register MTD: %d\n", ret); - goto free_buf; - } - return 0; - -free_buf: - kfree(denali->buf); -disable_irq: - denali_disable_irq(denali); - - return ret; -} -EXPORT_SYMBOL(denali_init); - -void denali_remove(struct denali_nand_info *denali) -{ - struct mtd_info *mtd = nand_to_mtd(&denali->nand); - - nand_release(mtd); - kfree(denali->buf); - denali_disable_irq(denali); -} -EXPORT_SYMBOL(denali_remove); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h deleted file mode 100644 index 9ad33d237378..000000000000 --- a/drivers/mtd/nand/denali.h +++ /dev/null @@ -1,335 +0,0 @@ -/* - * NAND Flash Controller Device Driver - * Copyright (c) 2009 - 2010, Intel Corporation and its suppliers. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#ifndef __DENALI_H__ -#define __DENALI_H__ - -#include -#include -#include -#include -#include - -#define DEVICE_RESET 0x0 -#define DEVICE_RESET__BANK(bank) BIT(bank) - -#define TRANSFER_SPARE_REG 0x10 -#define TRANSFER_SPARE_REG__FLAG BIT(0) - -#define LOAD_WAIT_CNT 0x20 -#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0) - -#define PROGRAM_WAIT_CNT 0x30 -#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0) - -#define ERASE_WAIT_CNT 0x40 -#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0) - -#define INT_MON_CYCCNT 0x50 -#define INT_MON_CYCCNT__VALUE GENMASK(15, 0) - -#define RB_PIN_ENABLED 0x60 -#define RB_PIN_ENABLED__BANK(bank) BIT(bank) - -#define MULTIPLANE_OPERATION 0x70 -#define MULTIPLANE_OPERATION__FLAG BIT(0) - -#define MULTIPLANE_READ_ENABLE 0x80 -#define MULTIPLANE_READ_ENABLE__FLAG BIT(0) - -#define COPYBACK_DISABLE 0x90 -#define COPYBACK_DISABLE__FLAG BIT(0) - -#define CACHE_WRITE_ENABLE 0xa0 -#define CACHE_WRITE_ENABLE__FLAG BIT(0) - -#define CACHE_READ_ENABLE 0xb0 -#define CACHE_READ_ENABLE__FLAG BIT(0) - -#define PREFETCH_MODE 0xc0 -#define PREFETCH_MODE__PREFETCH_EN BIT(0) -#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4) - -#define CHIP_ENABLE_DONT_CARE 0xd0 -#define CHIP_EN_DONT_CARE__FLAG BIT(0) - -#define ECC_ENABLE 0xe0 -#define ECC_ENABLE__FLAG BIT(0) - -#define GLOBAL_INT_ENABLE 0xf0 -#define GLOBAL_INT_EN_FLAG BIT(0) - -#define TWHR2_AND_WE_2_RE 0x100 -#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0) -#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8) - -#define TCWAW_AND_ADDR_2_DATA 0x110 -/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */ -#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0) -#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8) - -#define RE_2_WE 0x120 -#define RE_2_WE__VALUE GENMASK(5, 0) - -#define ACC_CLKS 0x130 -#define ACC_CLKS__VALUE GENMASK(3, 0) - -#define NUMBER_OF_PLANES 0x140 -#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0) - -#define PAGES_PER_BLOCK 0x150 -#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0) - -#define DEVICE_WIDTH 0x160 -#define DEVICE_WIDTH__VALUE GENMASK(1, 0) - -#define DEVICE_MAIN_AREA_SIZE 0x170 -#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0) - -#define DEVICE_SPARE_AREA_SIZE 0x180 -#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0) - -#define TWO_ROW_ADDR_CYCLES 0x190 -#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0) - -#define MULTIPLANE_ADDR_RESTRICT 0x1a0 -#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0) - -#define ECC_CORRECTION 0x1b0 -#define ECC_CORRECTION__VALUE GENMASK(4, 0) -#define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16) - -#define READ_MODE 0x1c0 -#define READ_MODE__VALUE GENMASK(3, 0) - -#define WRITE_MODE 0x1d0 -#define WRITE_MODE__VALUE GENMASK(3, 0) - -#define COPYBACK_MODE 0x1e0 -#define COPYBACK_MODE__VALUE GENMASK(3, 0) - -#define RDWR_EN_LO_CNT 0x1f0 -#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0) - -#define RDWR_EN_HI_CNT 0x200 -#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0) - -#define MAX_RD_DELAY 0x210 -#define MAX_RD_DELAY__VALUE GENMASK(3, 0) - -#define CS_SETUP_CNT 0x220 -#define CS_SETUP_CNT__VALUE GENMASK(4, 0) -#define CS_SETUP_CNT__TWB GENMASK(17, 12) - -#define SPARE_AREA_SKIP_BYTES 0x230 -#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0) - -#define SPARE_AREA_MARKER 0x240 -#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0) - -#define DEVICES_CONNECTED 0x250 -#define DEVICES_CONNECTED__VALUE GENMASK(2, 0) - -#define DIE_MASK 0x260 -#define DIE_MASK__VALUE GENMASK(7, 0) - -#define FIRST_BLOCK_OF_NEXT_PLANE 0x270 -#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0) - -#define WRITE_PROTECT 0x280 -#define WRITE_PROTECT__FLAG BIT(0) - -#define RE_2_RE 0x290 -#define RE_2_RE__VALUE GENMASK(5, 0) - -#define MANUFACTURER_ID 0x300 -#define MANUFACTURER_ID__VALUE GENMASK(7, 0) - -#define DEVICE_ID 0x310 -#define DEVICE_ID__VALUE GENMASK(7, 0) - -#define DEVICE_PARAM_0 0x320 -#define DEVICE_PARAM_0__VALUE GENMASK(7, 0) - -#define DEVICE_PARAM_1 0x330 -#define DEVICE_PARAM_1__VALUE GENMASK(7, 0) - -#define DEVICE_PARAM_2 0x340 -#define DEVICE_PARAM_2__VALUE GENMASK(7, 0) - -#define LOGICAL_PAGE_DATA_SIZE 0x350 -#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0) - -#define LOGICAL_PAGE_SPARE_SIZE 0x360 -#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0) - -#define REVISION 0x370 -#define REVISION__VALUE GENMASK(15, 0) - -#define ONFI_DEVICE_FEATURES 0x380 -#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0) - -#define ONFI_OPTIONAL_COMMANDS 0x390 -#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0) - -#define ONFI_TIMING_MODE 0x3a0 -#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0) - -#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0 -#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0) - -#define ONFI_DEVICE_NO_OF_LUNS 0x3c0 -#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0) -#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8) - -#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0 -#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0) - -#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0 -#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0) - -#define FEATURES 0x3f0 -#define FEATURES__N_BANKS GENMASK(1, 0) -#define FEATURES__ECC_MAX_ERR GENMASK(5, 2) -#define FEATURES__DMA BIT(6) -#define FEATURES__CMD_DMA BIT(7) -#define FEATURES__PARTITION BIT(8) -#define FEATURES__XDMA_SIDEBAND BIT(9) -#define FEATURES__GPREG BIT(10) -#define FEATURES__INDEX_ADDR BIT(11) - -#define TRANSFER_MODE 0x400 -#define TRANSFER_MODE__VALUE GENMASK(1, 0) - -#define INTR_STATUS(bank) (0x410 + (bank) * 0x50) -#define INTR_EN(bank) (0x420 + (bank) * 0x50) -/* bit[1:0] is used differently depending on IP version */ -#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */ -#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */ -#define INTR__ECC_ERR BIT(1) /* old IP */ -#define INTR__DMA_CMD_COMP BIT(2) -#define INTR__TIME_OUT BIT(3) -#define INTR__PROGRAM_FAIL BIT(4) -#define INTR__ERASE_FAIL BIT(5) -#define INTR__LOAD_COMP BIT(6) -#define INTR__PROGRAM_COMP BIT(7) -#define INTR__ERASE_COMP BIT(8) -#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9) -#define INTR__LOCKED_BLK BIT(10) -#define INTR__UNSUP_CMD BIT(11) -#define INTR__INT_ACT BIT(12) -#define INTR__RST_COMP BIT(13) -#define INTR__PIPE_CMD_ERR BIT(14) -#define INTR__PAGE_XFER_INC BIT(15) -#define INTR__ERASED_PAGE BIT(16) - -#define PAGE_CNT(bank) (0x430 + (bank) * 0x50) -#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50) -#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50) - -#define ECC_THRESHOLD 0x600 -#define ECC_THRESHOLD__VALUE GENMASK(9, 0) - -#define ECC_ERROR_BLOCK_ADDRESS 0x610 -#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0) - -#define ECC_ERROR_PAGE_ADDRESS 0x620 -#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0) -#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12) - -#define ECC_ERROR_ADDRESS 0x630 -#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0) -#define ECC_ERROR_ADDRESS__SECTOR GENMASK(15, 12) - -#define ERR_CORRECTION_INFO 0x640 -#define ERR_CORRECTION_INFO__BYTE GENMASK(7, 0) -#define ERR_CORRECTION_INFO__DEVICE GENMASK(11, 8) -#define ERR_CORRECTION_INFO__UNCOR BIT(14) -#define ERR_CORRECTION_INFO__LAST_ERR BIT(15) - -#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10) -#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8) -#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0) -#define ECC_COR_INFO__UNCOR_ERR BIT(7) - -#define CFG_DATA_BLOCK_SIZE 0x6b0 - -#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0 - -#define CFG_NUM_DATA_BLOCKS 0x6d0 - -#define CFG_META_DATA_SIZE 0x6e0 - -#define DMA_ENABLE 0x700 -#define DMA_ENABLE__FLAG BIT(0) - -#define IGNORE_ECC_DONE 0x710 -#define IGNORE_ECC_DONE__FLAG BIT(0) - -#define DMA_INTR 0x720 -#define DMA_INTR_EN 0x730 -#define DMA_INTR__TARGET_ERROR BIT(0) -#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1) -#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2) -#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3) -#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4) -#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5) - -#define TARGET_ERR_ADDR_LO 0x740 -#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0) - -#define TARGET_ERR_ADDR_HI 0x750 -#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0) - -#define CHNL_ACTIVE 0x760 -#define CHNL_ACTIVE__CHANNEL0 BIT(0) -#define CHNL_ACTIVE__CHANNEL1 BIT(1) -#define CHNL_ACTIVE__CHANNEL2 BIT(2) -#define CHNL_ACTIVE__CHANNEL3 BIT(3) - -struct denali_nand_info { - struct nand_chip nand; - unsigned long clk_x_rate; /* bus interface clock rate */ - int active_bank; /* currently selected bank */ - struct device *dev; - void __iomem *reg; /* Register Interface */ - void __iomem *host; /* Host Data/Command Interface */ - struct completion complete; - spinlock_t irq_lock; /* protect irq_mask and irq_status */ - u32 irq_mask; /* interrupts we are waiting for */ - u32 irq_status; /* interrupts that have happened */ - int irq; - void *buf; /* for syndrome layout conversion */ - dma_addr_t dma_addr; - int dma_avail; /* can support DMA? */ - int devs_per_cs; /* devices connected in parallel */ - int oob_skip_bytes; /* number of bytes reserved for BBM */ - int max_banks; - unsigned int revision; /* IP revision */ - unsigned int caps; /* IP capability (or quirk) */ - const struct nand_ecc_caps *ecc_caps; - u32 (*host_read)(struct denali_nand_info *denali, u32 addr); - void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data); - void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr, - int page, int write); -}; - -#define DENALI_CAP_HW_ECC_FIXUP BIT(0) -#define DENALI_CAP_DMA_64BIT BIT(1) - -int denali_calc_ecc_bytes(int step_size, int strength); -int denali_init(struct denali_nand_info *denali); -void denali_remove(struct denali_nand_info *denali); - -#endif /* __DENALI_H__ */ diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c deleted file mode 100644 index cfd33e6ca77f..000000000000 --- a/drivers/mtd/nand/denali_dt.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * NAND Flash Controller Device Driver for DT - * - * Copyright © 2011, Picochip. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "denali.h" - -struct denali_dt { - struct denali_nand_info denali; - struct clk *clk; -}; - -struct denali_dt_data { - unsigned int revision; - unsigned int caps; - const struct nand_ecc_caps *ecc_caps; -}; - -NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes, - 512, 8, 15); -static const struct denali_dt_data denali_socfpga_data = { - .caps = DENALI_CAP_HW_ECC_FIXUP, - .ecc_caps = &denali_socfpga_ecc_caps, -}; - -NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes, - 1024, 8, 16, 24); -static const struct denali_dt_data denali_uniphier_v5a_data = { - .caps = DENALI_CAP_HW_ECC_FIXUP | - DENALI_CAP_DMA_64BIT, - .ecc_caps = &denali_uniphier_v5a_ecc_caps, -}; - -NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes, - 1024, 8, 16); -static const struct denali_dt_data denali_uniphier_v5b_data = { - .revision = 0x0501, - .caps = DENALI_CAP_HW_ECC_FIXUP | - DENALI_CAP_DMA_64BIT, - .ecc_caps = &denali_uniphier_v5b_ecc_caps, -}; - -static const struct of_device_id denali_nand_dt_ids[] = { - { - .compatible = "altr,socfpga-denali-nand", - .data = &denali_socfpga_data, - }, - { - .compatible = "socionext,uniphier-denali-nand-v5a", - .data = &denali_uniphier_v5a_data, - }, - { - .compatible = "socionext,uniphier-denali-nand-v5b", - .data = &denali_uniphier_v5b_data, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); - -static int denali_dt_probe(struct platform_device *pdev) -{ - struct resource *res; - struct denali_dt *dt; - const struct denali_dt_data *data; - struct denali_nand_info *denali; - int ret; - - dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL); - if (!dt) - return -ENOMEM; - denali = &dt->denali; - - data = of_device_get_match_data(&pdev->dev); - if (data) { - denali->revision = data->revision; - denali->caps = data->caps; - denali->ecc_caps = data->ecc_caps; - } - - denali->dev = &pdev->dev; - denali->irq = platform_get_irq(pdev, 0); - if (denali->irq < 0) { - dev_err(&pdev->dev, "no irq defined\n"); - return denali->irq; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg"); - denali->reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(denali->reg)) - return PTR_ERR(denali->reg); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - denali->host = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(denali->host)) - return PTR_ERR(denali->host); - - dt->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(dt->clk)) { - dev_err(&pdev->dev, "no clk available\n"); - return PTR_ERR(dt->clk); - } - ret = clk_prepare_enable(dt->clk); - if (ret) - return ret; - - denali->clk_x_rate = clk_get_rate(dt->clk); - - ret = denali_init(denali); - if (ret) - goto out_disable_clk; - - platform_set_drvdata(pdev, dt); - return 0; - -out_disable_clk: - clk_disable_unprepare(dt->clk); - - return ret; -} - -static int denali_dt_remove(struct platform_device *pdev) -{ - struct denali_dt *dt = platform_get_drvdata(pdev); - - denali_remove(&dt->denali); - clk_disable_unprepare(dt->clk); - - return 0; -} - -static struct platform_driver denali_dt_driver = { - .probe = denali_dt_probe, - .remove = denali_dt_remove, - .driver = { - .name = "denali-nand-dt", - .of_match_table = denali_nand_dt_ids, - }, -}; -module_platform_driver(denali_dt_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jamie Iles"); -MODULE_DESCRIPTION("DT driver for Denali NAND controller"); diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c deleted file mode 100644 index 49cb3e1f8bd0..000000000000 --- a/drivers/mtd/nand/denali_pci.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * NAND Flash Controller Device Driver - * Copyright © 2009-2010, Intel Corporation and its suppliers. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#include -#include -#include -#include -#include - -#include "denali.h" - -#define DENALI_NAND_NAME "denali-nand-pci" - -#define INTEL_CE4100 1 -#define INTEL_MRST 2 - -/* List of platforms this NAND controller has be integrated into */ -static const struct pci_device_id denali_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, - { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, - { /* end: all zeroes */ } -}; -MODULE_DEVICE_TABLE(pci, denali_pci_ids); - -NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15); - -static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - int ret; - resource_size_t csr_base, mem_base; - unsigned long csr_len, mem_len; - struct denali_nand_info *denali; - - denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL); - if (!denali) - return -ENOMEM; - - ret = pcim_enable_device(dev); - if (ret) { - dev_err(&dev->dev, "Spectra: pci_enable_device failed.\n"); - return ret; - } - - if (id->driver_data == INTEL_CE4100) { - mem_base = pci_resource_start(dev, 0); - mem_len = pci_resource_len(dev, 1); - csr_base = pci_resource_start(dev, 1); - csr_len = pci_resource_len(dev, 1); - } else { - csr_base = pci_resource_start(dev, 0); - csr_len = pci_resource_len(dev, 0); - mem_base = pci_resource_start(dev, 1); - mem_len = pci_resource_len(dev, 1); - if (!mem_len) { - mem_base = csr_base + csr_len; - mem_len = csr_len; - } - } - - pci_set_master(dev); - denali->dev = &dev->dev; - denali->irq = dev->irq; - denali->ecc_caps = &denali_pci_ecc_caps; - denali->nand.ecc.options |= NAND_ECC_MAXIMIZE; - denali->clk_x_rate = 200000000; /* 200 MHz */ - - ret = pci_request_regions(dev, DENALI_NAND_NAME); - if (ret) { - dev_err(&dev->dev, "Spectra: Unable to request memory regions\n"); - return ret; - } - - denali->reg = ioremap_nocache(csr_base, csr_len); - if (!denali->reg) { - dev_err(&dev->dev, "Spectra: Unable to remap memory region\n"); - return -ENOMEM; - } - - denali->host = ioremap_nocache(mem_base, mem_len); - if (!denali->host) { - dev_err(&dev->dev, "Spectra: ioremap_nocache failed!"); - ret = -ENOMEM; - goto failed_remap_reg; - } - - ret = denali_init(denali); - if (ret) - goto failed_remap_mem; - - pci_set_drvdata(dev, denali); - - return 0; - -failed_remap_mem: - iounmap(denali->host); -failed_remap_reg: - iounmap(denali->reg); - return ret; -} - -static void denali_pci_remove(struct pci_dev *dev) -{ - struct denali_nand_info *denali = pci_get_drvdata(dev); - - denali_remove(denali); - iounmap(denali->reg); - iounmap(denali->host); -} - -static struct pci_driver denali_pci_driver = { - .name = DENALI_NAND_NAME, - .id_table = denali_pci_ids, - .probe = denali_pci_probe, - .remove = denali_pci_remove, -}; -module_pci_driver(denali_pci_driver); - -MODULE_DESCRIPTION("PCI driver for Denali NAND controller"); -MODULE_AUTHOR("Intel Corporation and its suppliers"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c deleted file mode 100644 index 1af77f798fe5..000000000000 --- a/drivers/mtd/nand/diskonchip.c +++ /dev/null @@ -1,1709 +0,0 @@ -/* - * (C) 2003 Red Hat, Inc. - * (C) 2004 Dan Brown - * (C) 2004 Kalev Lember - * - * Author: David Woodhouse - * Additional Diskonchip 2000 and Millennium support by Dan Brown - * Diskonchip Millennium Plus support by Kalev Lember - * - * Error correction code lifted from the old docecc code - * Author: Fabrice Bellard (fabrice.bellard@netgem.com) - * Copyright (C) 2000 Netgem S.A. - * converted to the generic Reed-Solomon library by Thomas Gleixner - * - * Interface to generic NAND code for M-Systems DiskOnChip devices - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* Where to look for the devices? */ -#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS -#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 -#endif - -static unsigned long doc_locations[] __initdata = { -#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) -#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH - 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, - 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, - 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, - 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, - 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, -#else - 0xc8000, 0xca000, 0xcc000, 0xce000, - 0xd0000, 0xd2000, 0xd4000, 0xd6000, - 0xd8000, 0xda000, 0xdc000, 0xde000, - 0xe0000, 0xe2000, 0xe4000, 0xe6000, - 0xe8000, 0xea000, 0xec000, 0xee000, -#endif -#endif - 0xffffffff }; - -static struct mtd_info *doclist = NULL; - -struct doc_priv { - void __iomem *virtadr; - unsigned long physadr; - u_char ChipID; - u_char CDSNControl; - int chips_per_floor; /* The number of chips detected on each floor */ - int curfloor; - int curchip; - int mh0_page; - int mh1_page; - struct mtd_info *nextdoc; - - /* Handle the last stage of initialization (BBT scan, partitioning) */ - int (*late_init)(struct mtd_info *mtd); -}; - -/* This is the ecc value computed by the HW ecc generator upon writing an empty - page, one with all 0xff for data. */ -static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; - -#define INFTL_BBT_RESERVED_BLOCKS 4 - -#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32) -#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) -#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) - -static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int bitmask); -static void doc200x_select_chip(struct mtd_info *mtd, int chip); - -static int debug = 0; -module_param(debug, int, 0); - -static int try_dword = 1; -module_param(try_dword, int, 0); - -static int no_ecc_failures = 0; -module_param(no_ecc_failures, int, 0); - -static int no_autopart = 0; -module_param(no_autopart, int, 0); - -static int show_firmware_partition = 0; -module_param(show_firmware_partition, int, 0); - -#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE -static int inftl_bbt_write = 1; -#else -static int inftl_bbt_write = 0; -#endif -module_param(inftl_bbt_write, int, 0); - -static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; -module_param(doc_config_location, ulong, 0); -MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); - -/* Sector size for HW ECC */ -#define SECTOR_SIZE 512 -/* The sector bytes are packed into NB_DATA 10 bit words */ -#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) -/* Number of roots */ -#define NROOTS 4 -/* First consective root */ -#define FCR 510 -/* Number of symbols */ -#define NN 1023 - -/* the Reed Solomon control structure */ -static struct rs_control *rs_decoder; - -/* - * The HW decoder in the DoC ASIC's provides us a error syndrome, - * which we must convert to a standard syndrome usable by the generic - * Reed-Solomon library code. - * - * Fabrice Bellard figured this out in the old docecc code. I added - * some comments, improved a minor bit and converted it to make use - * of the generic Reed-Solomon library. tglx - */ -static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) -{ - int i, j, nerr, errpos[8]; - uint8_t parity; - uint16_t ds[4], s[5], tmp, errval[8], syn[4]; - - memset(syn, 0, sizeof(syn)); - /* Convert the ecc bytes into words */ - ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); - ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); - ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); - ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); - parity = ecc[1]; - - /* Initialize the syndrome buffer */ - for (i = 0; i < NROOTS; i++) - s[i] = ds[0]; - /* - * Evaluate - * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] - * where x = alpha^(FCR + i) - */ - for (j = 1; j < NROOTS; j++) { - if (ds[j] == 0) - continue; - tmp = rs->index_of[ds[j]]; - for (i = 0; i < NROOTS; i++) - s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; - } - - /* Calc syn[i] = s[i] / alpha^(v + i) */ - for (i = 0; i < NROOTS; i++) { - if (s[i]) - syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); - } - /* Call the decoder library */ - nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); - - /* Incorrectable errors ? */ - if (nerr < 0) - return nerr; - - /* - * Correct the errors. The bitpositions are a bit of magic, - * but they are given by the design of the de/encoder circuit - * in the DoC ASIC's. - */ - for (i = 0; i < nerr; i++) { - int index, bitpos, pos = 1015 - errpos[i]; - uint8_t val; - if (pos >= NB_DATA && pos < 1019) - continue; - if (pos < NB_DATA) { - /* extract bit position (MSB first) */ - pos = 10 * (NB_DATA - 1 - pos) - 6; - /* now correct the following 10 bits. At most two bytes - can be modified since pos is even */ - index = (pos >> 3) ^ 1; - bitpos = pos & 7; - if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { - val = (uint8_t) (errval[i] >> (2 + bitpos)); - parity ^= val; - if (index < SECTOR_SIZE) - data[index] ^= val; - } - index = ((pos >> 3) + 1) ^ 1; - bitpos = (bitpos + 10) & 7; - if (bitpos == 0) - bitpos = 8; - if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { - val = (uint8_t) (errval[i] << (8 - bitpos)); - parity ^= val; - if (index < SECTOR_SIZE) - data[index] ^= val; - } - } - } - /* If the parity is wrong, no rescue possible */ - return parity ? -EBADMSG : nerr; -} - -static void DoC_Delay(struct doc_priv *doc, unsigned short cycles) -{ - volatile char dummy; - int i; - - for (i = 0; i < cycles; i++) { - if (DoC_is_Millennium(doc)) - dummy = ReadDOC(doc->virtadr, NOP); - else if (DoC_is_MillenniumPlus(doc)) - dummy = ReadDOC(doc->virtadr, Mplus_NOP); - else - dummy = ReadDOC(doc->virtadr, DOCStatus); - } - -} - -#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) - -/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ -static int _DoC_WaitReady(struct doc_priv *doc) -{ - void __iomem *docptr = doc->virtadr; - unsigned long timeo = jiffies + (HZ * 10); - - if (debug) - printk("_DoC_WaitReady...\n"); - /* Out-of-line routine to wait for chip response */ - if (DoC_is_MillenniumPlus(doc)) { - while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { - if (time_after(jiffies, timeo)) { - printk("_DoC_WaitReady timed out.\n"); - return -EIO; - } - udelay(1); - cond_resched(); - } - } else { - while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { - if (time_after(jiffies, timeo)) { - printk("_DoC_WaitReady timed out.\n"); - return -EIO; - } - udelay(1); - cond_resched(); - } - } - - return 0; -} - -static inline int DoC_WaitReady(struct doc_priv *doc) -{ - void __iomem *docptr = doc->virtadr; - int ret = 0; - - if (DoC_is_MillenniumPlus(doc)) { - DoC_Delay(doc, 4); - - if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) - /* Call the out-of-line routine to wait */ - ret = _DoC_WaitReady(doc); - } else { - DoC_Delay(doc, 4); - - if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) - /* Call the out-of-line routine to wait */ - ret = _DoC_WaitReady(doc); - DoC_Delay(doc, 2); - } - - if (debug) - printk("DoC_WaitReady OK\n"); - return ret; -} - -static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - - if (debug) - printk("write_byte %02x\n", datum); - WriteDOC(datum, docptr, CDSNSlowIO); - WriteDOC(datum, docptr, 2k_CDSN_IO); -} - -static u_char doc2000_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - u_char ret; - - ReadDOC(docptr, CDSNSlowIO); - DoC_Delay(doc, 2); - ret = ReadDOC(docptr, 2k_CDSN_IO); - if (debug) - printk("read_byte returns %02x\n", ret); - return ret; -} - -static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int i; - if (debug) - printk("writebuf of %d bytes: ", len); - for (i = 0; i < len; i++) { - WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); - if (debug && i < 16) - printk("%02x ", buf[i]); - } - if (debug) - printk("\n"); -} - -static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int i; - - if (debug) - printk("readbuf of %d bytes: ", len); - - for (i = 0; i < len; i++) { - buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); - } -} - -static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int i; - - if (debug) - printk("readbuf_dword of %d bytes: ", len); - - if (unlikely((((unsigned long)buf) | len) & 3)) { - for (i = 0; i < len; i++) { - *(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); - } - } else { - for (i = 0; i < len; i += 4) { - *(uint32_t *) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); - } - } -} - -static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - uint16_t ret; - - doc200x_select_chip(mtd, nr); - doc200x_hwcontrol(mtd, NAND_CMD_READID, - NAND_CTRL_CLE | NAND_CTRL_CHANGE); - doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); - doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - /* We can't use dev_ready here, but at least we wait for the - * command to complete - */ - udelay(50); - - ret = this->read_byte(mtd) << 8; - ret |= this->read_byte(mtd); - - if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { - /* First chip probe. See if we get same results by 32-bit access */ - union { - uint32_t dword; - uint8_t byte[4]; - } ident; - void __iomem *docptr = doc->virtadr; - - doc200x_hwcontrol(mtd, NAND_CMD_READID, - NAND_CTRL_CLE | NAND_CTRL_CHANGE); - doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); - doc200x_hwcontrol(mtd, NAND_CMD_NONE, - NAND_NCE | NAND_CTRL_CHANGE); - - udelay(50); - - ident.dword = readl(docptr + DoC_2k_CDSN_IO); - if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { - printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); - this->read_buf = &doc2000_readbuf_dword; - } - } - - return ret; -} - -static void __init doc2000_count_chips(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - uint16_t mfrid; - int i; - - /* Max 4 chips per floor on DiskOnChip 2000 */ - doc->chips_per_floor = 4; - - /* Find out what the first chip is */ - mfrid = doc200x_ident_chip(mtd, 0); - - /* Find how many chips in each floor. */ - for (i = 1; i < 4; i++) { - if (doc200x_ident_chip(mtd, i) != mfrid) - break; - } - doc->chips_per_floor = i; - printk(KERN_DEBUG "Detected %d chips per floor.\n", i); -} - -static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this) -{ - struct doc_priv *doc = nand_get_controller_data(this); - - int status; - - DoC_WaitReady(doc); - nand_status_op(this, NULL); - DoC_WaitReady(doc); - status = (int)this->read_byte(mtd); - - return status; -} - -static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - - WriteDOC(datum, docptr, CDSNSlowIO); - WriteDOC(datum, docptr, Mil_CDSN_IO); - WriteDOC(datum, docptr, WritePipeTerm); -} - -static u_char doc2001_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - - //ReadDOC(docptr, CDSNSlowIO); - /* 11.4.5 -- delay twice to allow extended length cycle */ - DoC_Delay(doc, 2); - ReadDOC(docptr, ReadPipeInit); - //return ReadDOC(docptr, Mil_CDSN_IO); - return ReadDOC(docptr, LastDataRead); -} - -static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int i; - - for (i = 0; i < len; i++) - WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); - /* Terminate write pipeline */ - WriteDOC(0x00, docptr, WritePipeTerm); -} - -static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int i; - - /* Start read pipeline */ - ReadDOC(docptr, ReadPipeInit); - - for (i = 0; i < len - 1; i++) - buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); - - /* Terminate read pipeline */ - buf[i] = ReadDOC(docptr, LastDataRead); -} - -static u_char doc2001plus_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - u_char ret; - - ReadDOC(docptr, Mplus_ReadPipeInit); - ReadDOC(docptr, Mplus_ReadPipeInit); - ret = ReadDOC(docptr, Mplus_LastDataRead); - if (debug) - printk("read_byte returns %02x\n", ret); - return ret; -} - -static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int i; - - if (debug) - printk("writebuf of %d bytes: ", len); - for (i = 0; i < len; i++) { - WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); - if (debug && i < 16) - printk("%02x ", buf[i]); - } - if (debug) - printk("\n"); -} - -static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int i; - - if (debug) - printk("readbuf of %d bytes: ", len); - - /* Start read pipeline */ - ReadDOC(docptr, Mplus_ReadPipeInit); - ReadDOC(docptr, Mplus_ReadPipeInit); - - for (i = 0; i < len - 2; i++) { - buf[i] = ReadDOC(docptr, Mil_CDSN_IO); - if (debug && i < 16) - printk("%02x ", buf[i]); - } - - /* Terminate read pipeline */ - buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead); - if (debug && i < 16) - printk("%02x ", buf[len - 2]); - buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead); - if (debug && i < 16) - printk("%02x ", buf[len - 1]); - if (debug) - printk("\n"); -} - -static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int floor = 0; - - if (debug) - printk("select chip (%d)\n", chip); - - if (chip == -1) { - /* Disable flash internally */ - WriteDOC(0, docptr, Mplus_FlashSelect); - return; - } - - floor = chip / doc->chips_per_floor; - chip -= (floor * doc->chips_per_floor); - - /* Assert ChipEnable and deassert WriteProtect */ - WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); - nand_reset_op(this); - - doc->curchip = chip; - doc->curfloor = floor; -} - -static void doc200x_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int floor = 0; - - if (debug) - printk("select chip (%d)\n", chip); - - if (chip == -1) - return; - - floor = chip / doc->chips_per_floor; - chip -= (floor * doc->chips_per_floor); - - /* 11.4.4 -- deassert CE before changing chip */ - doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); - - WriteDOC(floor, docptr, FloorSelect); - WriteDOC(chip, docptr, CDSNDeviceSelect); - - doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - doc->curchip = chip; - doc->curfloor = floor; -} - -#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE) - -static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - - if (ctrl & NAND_CTRL_CHANGE) { - doc->CDSNControl &= ~CDSN_CTRL_MSK; - doc->CDSNControl |= ctrl & CDSN_CTRL_MSK; - if (debug) - printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); - WriteDOC(doc->CDSNControl, docptr, CDSNControl); - /* 11.4.3 -- 4 NOPs after CSDNControl write */ - DoC_Delay(doc, 4); - } - if (cmd != NAND_CMD_NONE) { - if (DoC_is_2000(doc)) - doc2000_write_byte(mtd, cmd); - else - doc2001_write_byte(mtd, cmd); - } -} - -static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - - /* - * Must terminate write pipeline before sending any commands - * to the device. - */ - if (command == NAND_CMD_PAGEPROG) { - WriteDOC(0x00, docptr, Mplus_WritePipeTerm); - WriteDOC(0x00, docptr, Mplus_WritePipeTerm); - } - - /* - * Write out the command to the device. - */ - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->writesize) { - /* OOB area */ - column -= mtd->writesize; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - WriteDOC(readcmd, docptr, Mplus_FlashCmd); - } - WriteDOC(command, docptr, Mplus_FlashCmd); - WriteDOC(0, docptr, Mplus_WritePipeTerm); - WriteDOC(0, docptr, Mplus_WritePipeTerm); - - if (column != -1 || page_addr != -1) { - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - WriteDOC(column, docptr, Mplus_FlashAddress); - } - if (page_addr != -1) { - WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress); - WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); - if (this->options & NAND_ROW_ADDR_3) { - WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); - printk("high density\n"); - } - } - WriteDOC(0, docptr, Mplus_WritePipeTerm); - WriteDOC(0, docptr, Mplus_WritePipeTerm); - /* deassert ALE */ - if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || - command == NAND_CMD_READOOB || command == NAND_CMD_READID) - WriteDOC(0, docptr, Mplus_FlashControl); - } - - /* - * program and erase have their own busy handlers - * status and sequential in needs no delay - */ - switch (command) { - - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_STATUS: - return; - - case NAND_CMD_RESET: - if (this->dev_ready) - break; - udelay(this->chip_delay); - WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd); - WriteDOC(0, docptr, Mplus_WritePipeTerm); - WriteDOC(0, docptr, Mplus_WritePipeTerm); - while (!(this->read_byte(mtd) & 0x40)) ; - return; - - /* This applies to read commands */ - default: - /* - * If we don't have access to the busy pin, we apply the given - * command delay - */ - if (!this->dev_ready) { - udelay(this->chip_delay); - return; - } - } - - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ - ndelay(100); - /* wait until command is processed */ - while (!this->dev_ready(mtd)) ; -} - -static int doc200x_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - - if (DoC_is_MillenniumPlus(doc)) { - /* 11.4.2 -- must NOP four times before checking FR/B# */ - DoC_Delay(doc, 4); - if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { - if (debug) - printk("not ready\n"); - return 0; - } - if (debug) - printk("was ready\n"); - return 1; - } else { - /* 11.4.2 -- must NOP four times before checking FR/B# */ - DoC_Delay(doc, 4); - if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { - if (debug) - printk("not ready\n"); - return 0; - } - /* 11.4.2 -- Must NOP twice if it's ready */ - DoC_Delay(doc, 2); - if (debug) - printk("was ready\n"); - return 1; - } -} - -static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs) -{ - /* This is our last resort if we couldn't find or create a BBT. Just - pretend all blocks are good. */ - return 0; -} - -static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - - /* Prime the ECC engine */ - switch (mode) { - case NAND_ECC_READ: - WriteDOC(DOC_ECC_RESET, docptr, ECCConf); - WriteDOC(DOC_ECC_EN, docptr, ECCConf); - break; - case NAND_ECC_WRITE: - WriteDOC(DOC_ECC_RESET, docptr, ECCConf); - WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); - break; - } -} - -static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - - /* Prime the ECC engine */ - switch (mode) { - case NAND_ECC_READ: - WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); - WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); - break; - case NAND_ECC_WRITE: - WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); - WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); - break; - } -} - -/* This code is only called on write */ -static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - int i; - int emptymatch = 1; - - /* flush the pipeline */ - if (DoC_is_2000(doc)) { - WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl); - WriteDOC(0, docptr, 2k_CDSN_IO); - WriteDOC(0, docptr, 2k_CDSN_IO); - WriteDOC(0, docptr, 2k_CDSN_IO); - WriteDOC(doc->CDSNControl, docptr, CDSNControl); - } else if (DoC_is_MillenniumPlus(doc)) { - WriteDOC(0, docptr, Mplus_NOP); - WriteDOC(0, docptr, Mplus_NOP); - WriteDOC(0, docptr, Mplus_NOP); - } else { - WriteDOC(0, docptr, NOP); - WriteDOC(0, docptr, NOP); - WriteDOC(0, docptr, NOP); - } - - for (i = 0; i < 6; i++) { - if (DoC_is_MillenniumPlus(doc)) - ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); - else - ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); - if (ecc_code[i] != empty_write_ecc[i]) - emptymatch = 0; - } - if (DoC_is_MillenniumPlus(doc)) - WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); - else - WriteDOC(DOC_ECC_DIS, docptr, ECCConf); -#if 0 - /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ - if (emptymatch) { - /* Note: this somewhat expensive test should not be triggered - often. It could be optimized away by examining the data in - the writebuf routine, and remembering the result. */ - for (i = 0; i < 512; i++) { - if (dat[i] == 0xff) - continue; - emptymatch = 0; - break; - } - } - /* If emptymatch still =1, we do have an all-0xff data buffer. - Return all-0xff ecc value instead of the computed one, so - it'll look just like a freshly-erased page. */ - if (emptymatch) - memset(ecc_code, 0xff, 6); -#endif - return 0; -} - -static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *isnull) -{ - int i, ret = 0; - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - void __iomem *docptr = doc->virtadr; - uint8_t calc_ecc[6]; - volatile u_char dummy; - - /* flush the pipeline */ - if (DoC_is_2000(doc)) { - dummy = ReadDOC(docptr, 2k_ECCStatus); - dummy = ReadDOC(docptr, 2k_ECCStatus); - dummy = ReadDOC(docptr, 2k_ECCStatus); - } else if (DoC_is_MillenniumPlus(doc)) { - dummy = ReadDOC(docptr, Mplus_ECCConf); - dummy = ReadDOC(docptr, Mplus_ECCConf); - dummy = ReadDOC(docptr, Mplus_ECCConf); - } else { - dummy = ReadDOC(docptr, ECCConf); - dummy = ReadDOC(docptr, ECCConf); - dummy = ReadDOC(docptr, ECCConf); - } - - /* Error occurred ? */ - if (dummy & 0x80) { - for (i = 0; i < 6; i++) { - if (DoC_is_MillenniumPlus(doc)) - calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); - else - calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); - } - - ret = doc_ecc_decode(rs_decoder, dat, calc_ecc); - if (ret > 0) - printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); - } - if (DoC_is_MillenniumPlus(doc)) - WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); - else - WriteDOC(DOC_ECC_DIS, docptr, ECCConf); - if (no_ecc_failures && mtd_is_eccerr(ret)) { - printk(KERN_ERR "suppressing ECC failure\n"); - ret = 0; - } - return ret; -} - -//u_char mydatabuf[528]; - -static int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->offset = 0; - oobregion->length = 6; - - return 0; -} - -static int doc200x_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 1) - return -ERANGE; - - /* - * The strange out-of-order free bytes definition is a (possibly - * unneeded) attempt to retain compatibility. It used to read: - * .oobfree = { {8, 8} } - * Since that leaves two bytes unusable, it was changed. But the - * following scheme might affect existing jffs2 installs by moving the - * cleanmarker: - * .oobfree = { {6, 10} } - * jffs2 seems to handle the above gracefully, but the current scheme - * seems safer. The only problem with it is that any code retrieving - * free bytes position must be able to handle out-of-order segments. - */ - if (!section) { - oobregion->offset = 8; - oobregion->length = 8; - } else { - oobregion->offset = 6; - oobregion->length = 2; - } - - return 0; -} - -static const struct mtd_ooblayout_ops doc200x_ooblayout_ops = { - .ecc = doc200x_ooblayout_ecc, - .free = doc200x_ooblayout_free, -}; - -/* Find the (I)NFTL Media Header, and optionally also the mirror media header. - On successful return, buf will contain a copy of the media header for - further processing. id is the string to scan for, and will presumably be - either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media - header. The page #s of the found media headers are placed in mh0_page and - mh1_page in the DOC private structure. */ -static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - unsigned offs; - int ret; - size_t retlen; - - for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { - ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); - if (retlen != mtd->writesize) - continue; - if (ret) { - printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs); - } - if (memcmp(buf, id, 6)) - continue; - printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); - if (doc->mh0_page == -1) { - doc->mh0_page = offs >> this->page_shift; - if (!findmirror) - return 1; - continue; - } - doc->mh1_page = offs >> this->page_shift; - return 2; - } - if (doc->mh0_page == -1) { - printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id); - return 0; - } - /* Only one mediaheader was found. We want buf to contain a - mediaheader on return, so we'll have to re-read the one we found. */ - offs = doc->mh0_page << this->page_shift; - ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); - if (retlen != mtd->writesize) { - /* Insanity. Give up. */ - printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); - return 0; - } - return 1; -} - -static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - int ret = 0; - u_char *buf; - struct NFTLMediaHeader *mh; - const unsigned psize = 1 << this->page_shift; - int numparts = 0; - unsigned blocks, maxblocks; - int offs, numheaders; - - buf = kmalloc(mtd->writesize, GFP_KERNEL); - if (!buf) { - return 0; - } - if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1))) - goto out; - mh = (struct NFTLMediaHeader *)buf; - - le16_to_cpus(&mh->NumEraseUnits); - le16_to_cpus(&mh->FirstPhysicalEUN); - le32_to_cpus(&mh->FormattedSize); - - printk(KERN_INFO " DataOrgID = %s\n" - " NumEraseUnits = %d\n" - " FirstPhysicalEUN = %d\n" - " FormattedSize = %d\n" - " UnitSizeFactor = %d\n", - mh->DataOrgID, mh->NumEraseUnits, - mh->FirstPhysicalEUN, mh->FormattedSize, - mh->UnitSizeFactor); - - blocks = mtd->size >> this->phys_erase_shift; - maxblocks = min(32768U, mtd->erasesize - psize); - - if (mh->UnitSizeFactor == 0x00) { - /* Auto-determine UnitSizeFactor. The constraints are: - - There can be at most 32768 virtual blocks. - - There can be at most (virtual block size - page size) - virtual blocks (because MediaHeader+BBT must fit in 1). - */ - mh->UnitSizeFactor = 0xff; - while (blocks > maxblocks) { - blocks >>= 1; - maxblocks = min(32768U, (maxblocks << 1) + psize); - mh->UnitSizeFactor--; - } - printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); - } - - /* NOTE: The lines below modify internal variables of the NAND and MTD - layers; variables with have already been configured by nand_scan. - Unfortunately, we didn't know before this point what these values - should be. Thus, this code is somewhat dependent on the exact - implementation of the NAND layer. */ - if (mh->UnitSizeFactor != 0xff) { - this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); - mtd->erasesize <<= (0xff - mh->UnitSizeFactor); - printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize); - blocks = mtd->size >> this->bbt_erase_shift; - maxblocks = min(32768U, mtd->erasesize - psize); - } - - if (blocks > maxblocks) { - printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); - goto out; - } - - /* Skip past the media headers. */ - offs = max(doc->mh0_page, doc->mh1_page); - offs <<= this->page_shift; - offs += mtd->erasesize; - - if (show_firmware_partition == 1) { - parts[0].name = " DiskOnChip Firmware / Media Header partition"; - parts[0].offset = 0; - parts[0].size = offs; - numparts = 1; - } - - parts[numparts].name = " DiskOnChip BDTL partition"; - parts[numparts].offset = offs; - parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; - - offs += parts[numparts].size; - numparts++; - - if (offs < mtd->size) { - parts[numparts].name = " DiskOnChip Remainder partition"; - parts[numparts].offset = offs; - parts[numparts].size = mtd->size - offs; - numparts++; - } - - ret = numparts; - out: - kfree(buf); - return ret; -} - -/* This is a stripped-down copy of the code in inftlmount.c */ -static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - int ret = 0; - u_char *buf; - struct INFTLMediaHeader *mh; - struct INFTLPartition *ip; - int numparts = 0; - int blocks; - int vshift, lastvunit = 0; - int i; - int end = mtd->size; - - if (inftl_bbt_write) - end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); - - buf = kmalloc(mtd->writesize, GFP_KERNEL); - if (!buf) { - return 0; - } - - if (!find_media_headers(mtd, buf, "BNAND", 0)) - goto out; - doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); - mh = (struct INFTLMediaHeader *)buf; - - le32_to_cpus(&mh->NoOfBootImageBlocks); - le32_to_cpus(&mh->NoOfBinaryPartitions); - le32_to_cpus(&mh->NoOfBDTLPartitions); - le32_to_cpus(&mh->BlockMultiplierBits); - le32_to_cpus(&mh->FormatFlags); - le32_to_cpus(&mh->PercentUsed); - - printk(KERN_INFO " bootRecordID = %s\n" - " NoOfBootImageBlocks = %d\n" - " NoOfBinaryPartitions = %d\n" - " NoOfBDTLPartitions = %d\n" - " BlockMultiplerBits = %d\n" - " FormatFlgs = %d\n" - " OsakVersion = %d.%d.%d.%d\n" - " PercentUsed = %d\n", - mh->bootRecordID, mh->NoOfBootImageBlocks, - mh->NoOfBinaryPartitions, - mh->NoOfBDTLPartitions, - mh->BlockMultiplierBits, mh->FormatFlags, - ((unsigned char *) &mh->OsakVersion)[0] & 0xf, - ((unsigned char *) &mh->OsakVersion)[1] & 0xf, - ((unsigned char *) &mh->OsakVersion)[2] & 0xf, - ((unsigned char *) &mh->OsakVersion)[3] & 0xf, - mh->PercentUsed); - - vshift = this->phys_erase_shift + mh->BlockMultiplierBits; - - blocks = mtd->size >> vshift; - if (blocks > 32768) { - printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); - goto out; - } - - blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); - if (inftl_bbt_write && (blocks > mtd->erasesize)) { - printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); - goto out; - } - - /* Scan the partitions */ - for (i = 0; (i < 4); i++) { - ip = &(mh->Partitions[i]); - le32_to_cpus(&ip->virtualUnits); - le32_to_cpus(&ip->firstUnit); - le32_to_cpus(&ip->lastUnit); - le32_to_cpus(&ip->flags); - le32_to_cpus(&ip->spareUnits); - le32_to_cpus(&ip->Reserved0); - - printk(KERN_INFO " PARTITION[%d] ->\n" - " virtualUnits = %d\n" - " firstUnit = %d\n" - " lastUnit = %d\n" - " flags = 0x%x\n" - " spareUnits = %d\n", - i, ip->virtualUnits, ip->firstUnit, - ip->lastUnit, ip->flags, - ip->spareUnits); - - if ((show_firmware_partition == 1) && - (i == 0) && (ip->firstUnit > 0)) { - parts[0].name = " DiskOnChip IPL / Media Header partition"; - parts[0].offset = 0; - parts[0].size = mtd->erasesize * ip->firstUnit; - numparts = 1; - } - - if (ip->flags & INFTL_BINARY) - parts[numparts].name = " DiskOnChip BDK partition"; - else - parts[numparts].name = " DiskOnChip BDTL partition"; - parts[numparts].offset = ip->firstUnit << vshift; - parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; - numparts++; - if (ip->lastUnit > lastvunit) - lastvunit = ip->lastUnit; - if (ip->flags & INFTL_LAST) - break; - } - lastvunit++; - if ((lastvunit << vshift) < end) { - parts[numparts].name = " DiskOnChip Remainder partition"; - parts[numparts].offset = lastvunit << vshift; - parts[numparts].size = end - parts[numparts].offset; - numparts++; - } - ret = numparts; - out: - kfree(buf); - return ret; -} - -static int __init nftl_scan_bbt(struct mtd_info *mtd) -{ - int ret, numparts; - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - struct mtd_partition parts[2]; - - memset((char *)parts, 0, sizeof(parts)); - /* On NFTL, we have to find the media headers before we can read the - BBTs, since they're stored in the media header eraseblocks. */ - numparts = nftl_partscan(mtd, parts); - if (!numparts) - return -EIO; - this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | - NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | - NAND_BBT_VERSION; - this->bbt_td->veroffs = 7; - this->bbt_td->pages[0] = doc->mh0_page + 1; - if (doc->mh1_page != -1) { - this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | - NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | - NAND_BBT_VERSION; - this->bbt_md->veroffs = 7; - this->bbt_md->pages[0] = doc->mh1_page + 1; - } else { - this->bbt_md = NULL; - } - - ret = this->scan_bbt(mtd); - if (ret) - return ret; - - return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts); -} - -static int __init inftl_scan_bbt(struct mtd_info *mtd) -{ - int ret, numparts; - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - struct mtd_partition parts[5]; - - if (this->numchips > doc->chips_per_floor) { - printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n"); - return -EIO; - } - - if (DoC_is_MillenniumPlus(doc)) { - this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE; - if (inftl_bbt_write) - this->bbt_td->options |= NAND_BBT_WRITE; - this->bbt_td->pages[0] = 2; - this->bbt_md = NULL; - } else { - this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; - if (inftl_bbt_write) - this->bbt_td->options |= NAND_BBT_WRITE; - this->bbt_td->offs = 8; - this->bbt_td->len = 8; - this->bbt_td->veroffs = 7; - this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS; - this->bbt_td->reserved_block_code = 0x01; - this->bbt_td->pattern = "MSYS_BBT"; - - this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; - if (inftl_bbt_write) - this->bbt_md->options |= NAND_BBT_WRITE; - this->bbt_md->offs = 8; - this->bbt_md->len = 8; - this->bbt_md->veroffs = 7; - this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS; - this->bbt_md->reserved_block_code = 0x01; - this->bbt_md->pattern = "TBB_SYSM"; - } - - ret = this->scan_bbt(mtd); - if (ret) - return ret; - - memset((char *)parts, 0, sizeof(parts)); - numparts = inftl_partscan(mtd, parts); - /* At least for now, require the INFTL Media Header. We could probably - do without it for non-INFTL use, since all it gives us is - autopartitioning, but I want to give it more thought. */ - if (!numparts) - return -EIO; - return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts); -} - -static inline int __init doc2000_init(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - - this->read_byte = doc2000_read_byte; - this->write_buf = doc2000_writebuf; - this->read_buf = doc2000_readbuf; - doc->late_init = nftl_scan_bbt; - - doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; - doc2000_count_chips(mtd); - mtd->name = "DiskOnChip 2000 (NFTL Model)"; - return (4 * doc->chips_per_floor); -} - -static inline int __init doc2001_init(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - - this->read_byte = doc2001_read_byte; - this->write_buf = doc2001_writebuf; - this->read_buf = doc2001_readbuf; - - ReadDOC(doc->virtadr, ChipID); - ReadDOC(doc->virtadr, ChipID); - ReadDOC(doc->virtadr, ChipID); - if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { - /* It's not a Millennium; it's one of the newer - DiskOnChip 2000 units with a similar ASIC. - Treat it like a Millennium, except that it - can have multiple chips. */ - doc2000_count_chips(mtd); - mtd->name = "DiskOnChip 2000 (INFTL Model)"; - doc->late_init = inftl_scan_bbt; - return (4 * doc->chips_per_floor); - } else { - /* Bog-standard Millennium */ - doc->chips_per_floor = 1; - mtd->name = "DiskOnChip Millennium"; - doc->late_init = nftl_scan_bbt; - return 1; - } -} - -static inline int __init doc2001plus_init(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct doc_priv *doc = nand_get_controller_data(this); - - this->read_byte = doc2001plus_read_byte; - this->write_buf = doc2001plus_writebuf; - this->read_buf = doc2001plus_readbuf; - doc->late_init = inftl_scan_bbt; - this->cmd_ctrl = NULL; - this->select_chip = doc2001plus_select_chip; - this->cmdfunc = doc2001plus_command; - this->ecc.hwctl = doc2001plus_enable_hwecc; - - doc->chips_per_floor = 1; - mtd->name = "DiskOnChip Millennium Plus"; - - return 1; -} - -static int __init doc_probe(unsigned long physadr) -{ - unsigned char ChipID; - struct mtd_info *mtd; - struct nand_chip *nand; - struct doc_priv *doc; - void __iomem *virtadr; - unsigned char save_control; - unsigned char tmp, tmpb, tmpc; - int reg, len, numchips; - int ret = 0; - - if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip")) - return -EBUSY; - virtadr = ioremap(physadr, DOC_IOREMAP_LEN); - if (!virtadr) { - printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); - ret = -EIO; - goto error_ioremap; - } - - /* It's not possible to cleanly detect the DiskOnChip - the - * bootup procedure will put the device into reset mode, and - * it's not possible to talk to it without actually writing - * to the DOCControl register. So we store the current contents - * of the DOCControl register's location, in case we later decide - * that it's not a DiskOnChip, and want to put it back how we - * found it. - */ - save_control = ReadDOC(virtadr, DOCControl); - - /* Reset the DiskOnChip ASIC */ - WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); - WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); - - /* Enable the DiskOnChip ASIC */ - WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); - WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); - - ChipID = ReadDOC(virtadr, ChipID); - - switch (ChipID) { - case DOC_ChipID_Doc2k: - reg = DoC_2k_ECCStatus; - break; - case DOC_ChipID_DocMil: - reg = DoC_ECCConf; - break; - case DOC_ChipID_DocMilPlus16: - case DOC_ChipID_DocMilPlus32: - case 0: - /* Possible Millennium Plus, need to do more checks */ - /* Possibly release from power down mode */ - for (tmp = 0; (tmp < 4); tmp++) - ReadDOC(virtadr, Mplus_Power); - - /* Reset the Millennium Plus ASIC */ - tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; - WriteDOC(tmp, virtadr, Mplus_DOCControl); - WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); - - mdelay(1); - /* Enable the Millennium Plus ASIC */ - tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; - WriteDOC(tmp, virtadr, Mplus_DOCControl); - WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); - mdelay(1); - - ChipID = ReadDOC(virtadr, ChipID); - - switch (ChipID) { - case DOC_ChipID_DocMilPlus16: - reg = DoC_Mplus_Toggle; - break; - case DOC_ChipID_DocMilPlus32: - printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); - default: - ret = -ENODEV; - goto notfound; - } - break; - - default: - ret = -ENODEV; - goto notfound; - } - /* Check the TOGGLE bit in the ECC register */ - tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; - tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; - tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; - if ((tmp == tmpb) || (tmp != tmpc)) { - printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); - ret = -ENODEV; - goto notfound; - } - - for (mtd = doclist; mtd; mtd = doc->nextdoc) { - unsigned char oldval; - unsigned char newval; - nand = mtd_to_nand(mtd); - doc = nand_get_controller_data(nand); - /* Use the alias resolution register to determine if this is - in fact the same DOC aliased to a new address. If writes - to one chip's alias resolution register change the value on - the other chip, they're the same chip. */ - if (ChipID == DOC_ChipID_DocMilPlus16) { - oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); - newval = ReadDOC(virtadr, Mplus_AliasResolution); - } else { - oldval = ReadDOC(doc->virtadr, AliasResolution); - newval = ReadDOC(virtadr, AliasResolution); - } - if (oldval != newval) - continue; - if (ChipID == DOC_ChipID_DocMilPlus16) { - WriteDOC(~newval, virtadr, Mplus_AliasResolution); - oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); - WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it - } else { - WriteDOC(~newval, virtadr, AliasResolution); - oldval = ReadDOC(doc->virtadr, AliasResolution); - WriteDOC(newval, virtadr, AliasResolution); // restore it - } - newval = ~newval; - if (oldval == newval) { - printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr); - goto notfound; - } - } - - printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); - - len = sizeof(struct nand_chip) + sizeof(struct doc_priv) + - (2 * sizeof(struct nand_bbt_descr)); - nand = kzalloc(len, GFP_KERNEL); - if (!nand) { - ret = -ENOMEM; - goto fail; - } - - mtd = nand_to_mtd(nand); - doc = (struct doc_priv *) (nand + 1); - nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); - nand->bbt_md = nand->bbt_td + 1; - - mtd->owner = THIS_MODULE; - mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops); - - nand_set_controller_data(nand, doc); - nand->select_chip = doc200x_select_chip; - nand->cmd_ctrl = doc200x_hwcontrol; - nand->dev_ready = doc200x_dev_ready; - nand->waitfunc = doc200x_wait; - nand->block_bad = doc200x_block_bad; - nand->ecc.hwctl = doc200x_enable_hwecc; - nand->ecc.calculate = doc200x_calculate_ecc; - nand->ecc.correct = doc200x_correct_data; - - nand->ecc.mode = NAND_ECC_HW_SYNDROME; - nand->ecc.size = 512; - nand->ecc.bytes = 6; - nand->ecc.strength = 2; - nand->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; - nand->bbt_options = NAND_BBT_USE_FLASH; - /* Skip the automatic BBT scan so we can run it manually */ - nand->options |= NAND_SKIP_BBTSCAN; - - doc->physadr = physadr; - doc->virtadr = virtadr; - doc->ChipID = ChipID; - doc->curfloor = -1; - doc->curchip = -1; - doc->mh0_page = -1; - doc->mh1_page = -1; - doc->nextdoc = doclist; - - if (ChipID == DOC_ChipID_Doc2k) - numchips = doc2000_init(mtd); - else if (ChipID == DOC_ChipID_DocMilPlus16) - numchips = doc2001plus_init(mtd); - else - numchips = doc2001_init(mtd); - - if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) { - /* DBB note: i believe nand_release is necessary here, as - buffers may have been allocated in nand_base. Check with - Thomas. FIX ME! */ - /* nand_release will call mtd_device_unregister, but we - haven't yet added it. This is handled without incident by - mtd_device_unregister, as far as I can tell. */ - nand_release(mtd); - kfree(nand); - goto fail; - } - - /* Success! */ - doclist = mtd; - return 0; - - notfound: - /* Put back the contents of the DOCControl register, in case it's not - actually a DiskOnChip. */ - WriteDOC(save_control, virtadr, DOCControl); - fail: - iounmap(virtadr); - -error_ioremap: - release_mem_region(physadr, DOC_IOREMAP_LEN); - - return ret; -} - -static void release_nanddoc(void) -{ - struct mtd_info *mtd, *nextmtd; - struct nand_chip *nand; - struct doc_priv *doc; - - for (mtd = doclist; mtd; mtd = nextmtd) { - nand = mtd_to_nand(mtd); - doc = nand_get_controller_data(nand); - - nextmtd = doc->nextdoc; - nand_release(mtd); - iounmap(doc->virtadr); - release_mem_region(doc->physadr, DOC_IOREMAP_LEN); - kfree(nand); - } -} - -static int __init init_nanddoc(void) -{ - int i, ret = 0; - - /* We could create the decoder on demand, if memory is a concern. - * This way we have it handy, if an error happens - * - * Symbolsize is 10 (bits) - * Primitve polynomial is x^10+x^3+1 - * first consecutive root is 510 - * primitve element to generate roots = 1 - * generator polinomial degree = 4 - */ - rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); - if (!rs_decoder) { - printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); - return -ENOMEM; - } - - if (doc_config_location) { - printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); - ret = doc_probe(doc_config_location); - if (ret < 0) - goto outerr; - } else { - for (i = 0; (doc_locations[i] != 0xffffffff); i++) { - doc_probe(doc_locations[i]); - } - } - /* No banner message any more. Print a message if no DiskOnChip - found, so the user knows we at least tried. */ - if (!doclist) { - printk(KERN_INFO "No valid DiskOnChip devices found\n"); - ret = -ENODEV; - goto outerr; - } - return 0; - outerr: - free_rs(rs_decoder); - return ret; -} - -static void __exit cleanup_nanddoc(void) -{ - /* Cleanup the nand/DoC resources */ - release_nanddoc(); - - /* Free the reed solomon resources */ - if (rs_decoder) { - free_rs(rs_decoder); - } -} - -module_init(init_nanddoc); -module_exit(cleanup_nanddoc); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse "); -MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver"); diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c deleted file mode 100644 index 72f1327c4430..000000000000 --- a/drivers/mtd/nand/docg4.c +++ /dev/null @@ -1,1421 +0,0 @@ -/* - * Copyright © 2012 Mike Dunn - * - * mtd nand driver for M-Systems DiskOnChip G4 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus - * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others. - * Should work on these as well. Let me know! - * - * TODO: - * - * Mechanism for management of password-protected areas - * - * Hamming ecc when reading oob only - * - * According to the M-Sys documentation, this device is also available in a - * "dual-die" configuration having a 256MB capacity, but no mechanism for - * detecting this variant is documented. Currently this driver assumes 128MB - * capacity. - * - * Support for multiple cascaded devices ("floors"). Not sure which gadgets - * contain multiple G4s in a cascaded configuration, if any. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * In "reliable mode" consecutive 2k pages are used in parallel (in some - * fashion) to store the same data. The data can be read back from the - * even-numbered pages in the normal manner; odd-numbered pages will appear to - * contain junk. Systems that boot from the docg4 typically write the secondary - * program loader (SPL) code in this mode. The SPL is loaded by the initial - * program loader (IPL, stored in the docg4's 2k NOR-like region that is mapped - * to the reset vector address). This module parameter enables you to use this - * driver to write the SPL. When in this mode, no more than 2k of data can be - * written at a time, because the addresses do not increment in the normal - * manner, and the starting offset must be within an even-numbered 2k region; - * i.e., invalid starting offsets are 0x800, 0xa00, 0xc00, 0xe00, 0x1800, - * 0x1a00, ... Reliable mode is a special case and should not be used unless - * you know what you're doing. - */ -static bool reliable_mode; -module_param(reliable_mode, bool, 0); -MODULE_PARM_DESC(reliable_mode, "pages are programmed in reliable mode"); - -/* - * You'll want to ignore badblocks if you're reading a partition that contains - * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since - * it does not use mtd nand's method for marking bad blocks (using oob area). - * This will also skip the check of the "page written" flag. - */ -static bool ignore_badblocks; -module_param(ignore_badblocks, bool, 0); -MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed"); - -struct docg4_priv { - struct mtd_info *mtd; - struct device *dev; - void __iomem *virtadr; - int status; - struct { - unsigned int command; - int column; - int page; - } last_command; - uint8_t oob_buf[16]; - uint8_t ecc_buf[7]; - int oob_page; - struct bch_control *bch; -}; - -/* - * Defines prefixed with DOCG4 are unique to the diskonchip G4. All others are - * shared with other diskonchip devices (P3, G3 at least). - * - * Functions with names prefixed with docg4_ are mtd / nand interface functions - * (though they may also be called internally). All others are internal. - */ - -#define DOC_IOSPACE_DATA 0x0800 - -/* register offsets */ -#define DOC_CHIPID 0x1000 -#define DOC_DEVICESELECT 0x100a -#define DOC_ASICMODE 0x100c -#define DOC_DATAEND 0x101e -#define DOC_NOP 0x103e - -#define DOC_FLASHSEQUENCE 0x1032 -#define DOC_FLASHCOMMAND 0x1034 -#define DOC_FLASHADDRESS 0x1036 -#define DOC_FLASHCONTROL 0x1038 -#define DOC_ECCCONF0 0x1040 -#define DOC_ECCCONF1 0x1042 -#define DOC_HAMMINGPARITY 0x1046 -#define DOC_BCH_SYNDROM(idx) (0x1048 + idx) - -#define DOC_ASICMODECONFIRM 0x1072 -#define DOC_CHIPID_INV 0x1074 -#define DOC_POWERMODE 0x107c - -#define DOCG4_MYSTERY_REG 0x1050 - -/* apparently used only to write oob bytes 6 and 7 */ -#define DOCG4_OOB_6_7 0x1052 - -/* DOC_FLASHSEQUENCE register commands */ -#define DOC_SEQ_RESET 0x00 -#define DOCG4_SEQ_PAGE_READ 0x03 -#define DOCG4_SEQ_FLUSH 0x29 -#define DOCG4_SEQ_PAGEWRITE 0x16 -#define DOCG4_SEQ_PAGEPROG 0x1e -#define DOCG4_SEQ_BLOCKERASE 0x24 -#define DOCG4_SEQ_SETMODE 0x45 - -/* DOC_FLASHCOMMAND register commands */ -#define DOCG4_CMD_PAGE_READ 0x00 -#define DOC_CMD_ERASECYCLE2 0xd0 -#define DOCG4_CMD_FLUSH 0x70 -#define DOCG4_CMD_READ2 0x30 -#define DOC_CMD_PROG_BLOCK_ADDR 0x60 -#define DOCG4_CMD_PAGEWRITE 0x80 -#define DOC_CMD_PROG_CYCLE2 0x10 -#define DOCG4_CMD_FAST_MODE 0xa3 /* functionality guessed */ -#define DOC_CMD_RELIABLE_MODE 0x22 -#define DOC_CMD_RESET 0xff - -/* DOC_POWERMODE register bits */ -#define DOC_POWERDOWN_READY 0x80 - -/* DOC_FLASHCONTROL register bits */ -#define DOC_CTRL_CE 0x10 -#define DOC_CTRL_UNKNOWN 0x40 -#define DOC_CTRL_FLASHREADY 0x01 - -/* DOC_ECCCONF0 register bits */ -#define DOC_ECCCONF0_READ_MODE 0x8000 -#define DOC_ECCCONF0_UNKNOWN 0x2000 -#define DOC_ECCCONF0_ECC_ENABLE 0x1000 -#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff - -/* DOC_ECCCONF1 register bits */ -#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80 -#define DOC_ECCCONF1_ECC_ENABLE 0x07 -#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20 - -/* DOC_ASICMODE register bits */ -#define DOC_ASICMODE_RESET 0x00 -#define DOC_ASICMODE_NORMAL 0x01 -#define DOC_ASICMODE_POWERDOWN 0x02 -#define DOC_ASICMODE_MDWREN 0x04 -#define DOC_ASICMODE_BDETCT_RESET 0x08 -#define DOC_ASICMODE_RSTIN_RESET 0x10 -#define DOC_ASICMODE_RAM_WE 0x20 - -/* good status values read after read/write/erase operations */ -#define DOCG4_PROGSTATUS_GOOD 0x51 -#define DOCG4_PROGSTATUS_GOOD_2 0xe0 - -/* - * On read operations (page and oob-only), the first byte read from I/O reg is a - * status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read - * after reset only) or 0x51, so bit 1 is presumed to be an error indicator. - */ -#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */ - -/* anatomy of the device */ -#define DOCG4_CHIP_SIZE 0x8000000 -#define DOCG4_PAGE_SIZE 0x200 -#define DOCG4_PAGES_PER_BLOCK 0x200 -#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE) -#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE) -#define DOCG4_OOB_SIZE 0x10 -#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */ -#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */ -#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */ - -/* all but the last byte is included in ecc calculation */ -#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1) - -#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */ - -/* expected values from the ID registers */ -#define DOCG4_IDREG1_VALUE 0x0400 -#define DOCG4_IDREG2_VALUE 0xfbff - -/* primitive polynomial used to build the Galois field used by hw ecc gen */ -#define DOCG4_PRIMITIVE_POLY 0x4443 - -#define DOCG4_M 14 /* Galois field is of order 2^14 */ -#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */ - -#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */ -#define DOCG4_REDUNDANT_BBT_PAGE 24 /* page where redundant factory bbt lives */ - -/* - * Bytes 0, 1 are used as badblock marker. - * Bytes 2 - 6 are available to the user. - * Byte 7 is hamming ecc for first 7 oob bytes only. - * Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14. - * Byte 15 (the last) is used by the driver as a "page written" flag. - */ -static int docg4_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->offset = 7; - oobregion->length = 9; - - return 0; -} - -static int docg4_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->offset = 2; - oobregion->length = 5; - - return 0; -} - -static const struct mtd_ooblayout_ops docg4_ooblayout_ops = { - .ecc = docg4_ooblayout_ecc, - .free = docg4_ooblayout_free, -}; - -/* - * The device has a nop register which M-Sys claims is for the purpose of - * inserting precise delays. But beware; at least some operations fail if the - * nop writes are replaced with a generic delay! - */ -static inline void write_nop(void __iomem *docptr) -{ - writew(0, docptr + DOC_NOP); -} - -static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - int i; - struct nand_chip *nand = mtd_to_nand(mtd); - uint16_t *p = (uint16_t *) buf; - len >>= 1; - - for (i = 0; i < len; i++) - p[i] = readw(nand->IO_ADDR_R); -} - -static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - int i; - struct nand_chip *nand = mtd_to_nand(mtd); - uint16_t *p = (uint16_t *) buf; - len >>= 1; - - for (i = 0; i < len; i++) - writew(p[i], nand->IO_ADDR_W); -} - -static int poll_status(struct docg4_priv *doc) -{ - /* - * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL - * register. Operations known to take a long time (e.g., block erase) - * should sleep for a while before calling this. - */ - - uint16_t flash_status; - unsigned long timeo; - void __iomem *docptr = doc->virtadr; - - dev_dbg(doc->dev, "%s...\n", __func__); - - /* hardware quirk requires reading twice initially */ - flash_status = readw(docptr + DOC_FLASHCONTROL); - - timeo = jiffies + msecs_to_jiffies(200); /* generous timeout */ - do { - cpu_relax(); - flash_status = readb(docptr + DOC_FLASHCONTROL); - } while (!(flash_status & DOC_CTRL_FLASHREADY) && - time_before(jiffies, timeo)); - - if (unlikely(!(flash_status & DOC_CTRL_FLASHREADY))) { - dev_err(doc->dev, "%s: timed out!\n", __func__); - return NAND_STATUS_FAIL; - } - - return 0; -} - - -static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand) -{ - - struct docg4_priv *doc = nand_get_controller_data(nand); - int status = NAND_STATUS_WP; /* inverse logic?? */ - dev_dbg(doc->dev, "%s...\n", __func__); - - /* report any previously unreported error */ - if (doc->status) { - status |= doc->status; - doc->status = 0; - return status; - } - - status |= poll_status(doc); - return status; -} - -static void docg4_select_chip(struct mtd_info *mtd, int chip) -{ - /* - * Select among multiple cascaded chips ("floors"). Multiple floors are - * not yet supported, so the only valid non-negative value is 0. - */ - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - - dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip); - - if (chip < 0) - return; /* deselected */ - - if (chip > 0) - dev_warn(doc->dev, "multiple floors currently unsupported\n"); - - writew(0, docptr + DOC_DEVICESELECT); -} - -static void reset(struct mtd_info *mtd) -{ - /* full device reset */ - - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - - writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN, - docptr + DOC_ASICMODE); - writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN), - docptr + DOC_ASICMODECONFIRM); - write_nop(docptr); - - writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN, - docptr + DOC_ASICMODE); - writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN), - docptr + DOC_ASICMODECONFIRM); - - writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1); - - poll_status(doc); -} - -static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf) -{ - /* read the 7 hw-generated ecc bytes */ - - int i; - for (i = 0; i < 7; i++) { /* hw quirk; read twice */ - ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); - ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); - } -} - -static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page) -{ - /* - * Called after a page read when hardware reports bitflips. - * Up to four bitflips can be corrected. - */ - - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - int i, numerrs, errpos[4]; - const uint8_t blank_read_hwecc[8] = { - 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 }; - - read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */ - - /* check if read error is due to a blank page */ - if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7)) - return 0; /* yes */ - - /* skip additional check of "written flag" if ignore_badblocks */ - if (ignore_badblocks == false) { - - /* - * If the hw ecc bytes are not those of a blank page, there's - * still a chance that the page is blank, but was read with - * errors. Check the "written flag" in last oob byte, which - * is set to zero when a page is written. If more than half - * the bits are set, assume a blank page. Unfortunately, the - * bit flips(s) are not reported in stats. - */ - - if (nand->oob_poi[15]) { - int bit, numsetbits = 0; - unsigned long written_flag = nand->oob_poi[15]; - for_each_set_bit(bit, &written_flag, 8) - numsetbits++; - if (numsetbits > 4) { /* assume blank */ - dev_warn(doc->dev, - "error(s) in blank page " - "at offset %08x\n", - page * DOCG4_PAGE_SIZE); - return 0; - } - } - } - - /* - * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch - * algorithm is used to decode this. However the hw operates on page - * data in a bit order that is the reverse of that of the bch alg, - * requiring that the bits be reversed on the result. Thanks to Ivan - * Djelic for his analysis! - */ - for (i = 0; i < 7; i++) - doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]); - - numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL, - doc->ecc_buf, NULL, errpos); - - if (numerrs == -EBADMSG) { - dev_warn(doc->dev, "uncorrectable errors at offset %08x\n", - page * DOCG4_PAGE_SIZE); - return -EBADMSG; - } - - BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */ - - /* undo last step in BCH alg (modulo mirroring not needed) */ - for (i = 0; i < numerrs; i++) - errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7)); - - /* fix the errors */ - for (i = 0; i < numerrs; i++) { - - /* ignore if error within oob ecc bytes */ - if (errpos[i] > DOCG4_USERDATA_LEN * 8) - continue; - - /* if error within oob area preceeding ecc bytes... */ - if (errpos[i] > DOCG4_PAGE_SIZE * 8) - change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8, - (unsigned long *)nand->oob_poi); - - else /* error in page data */ - change_bit(errpos[i], (unsigned long *)buf); - } - - dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n", - numerrs, page * DOCG4_PAGE_SIZE); - - return numerrs; -} - -static uint8_t docg4_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - - dev_dbg(doc->dev, "%s\n", __func__); - - if (doc->last_command.command == NAND_CMD_STATUS) { - int status; - - /* - * Previous nand command was status request, so nand - * infrastructure code expects to read the status here. If an - * error occurred in a previous operation, report it. - */ - doc->last_command.command = 0; - - if (doc->status) { - status = doc->status; - doc->status = 0; - } - - /* why is NAND_STATUS_WP inverse logic?? */ - else - status = NAND_STATUS_WP | NAND_STATUS_READY; - - return status; - } - - dev_warn(doc->dev, "unexpected call to read_byte()\n"); - - return 0; -} - -static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr) -{ - /* write the four address bytes packed in docg4_addr to the device */ - - void __iomem *docptr = doc->virtadr; - writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); - docg4_addr >>= 8; - writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); - docg4_addr >>= 8; - writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); - docg4_addr >>= 8; - writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); -} - -static int read_progstatus(struct docg4_priv *doc) -{ - /* - * This apparently checks the status of programming. Done after an - * erasure, and after page data is written. On error, the status is - * saved, to be later retrieved by the nand infrastructure code. - */ - void __iomem *docptr = doc->virtadr; - - /* status is read from the I/O reg */ - uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA); - uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA); - uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG); - - dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n", - __func__, status1, status2, status3); - - if (status1 != DOCG4_PROGSTATUS_GOOD - || status2 != DOCG4_PROGSTATUS_GOOD_2 - || status3 != DOCG4_PROGSTATUS_GOOD_2) { - doc->status = NAND_STATUS_FAIL; - dev_warn(doc->dev, "read_progstatus failed: " - "%02x, %02x, %02x\n", status1, status2, status3); - return -EIO; - } - return 0; -} - -static int pageprog(struct mtd_info *mtd) -{ - /* - * Final step in writing a page. Writes the contents of its - * internal buffer out to the flash array, or some such. - */ - - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - int retval = 0; - - dev_dbg(doc->dev, "docg4: %s\n", __func__); - - writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE); - writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND); - write_nop(docptr); - write_nop(docptr); - - /* Just busy-wait; usleep_range() slows things down noticeably. */ - poll_status(doc); - - writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); - writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); - writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - - retval = read_progstatus(doc); - writew(0, docptr + DOC_DATAEND); - write_nop(docptr); - poll_status(doc); - write_nop(docptr); - - return retval; -} - -static void sequence_reset(struct mtd_info *mtd) -{ - /* common starting sequence for all operations */ - - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - - writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL); - writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); - writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); - write_nop(docptr); - write_nop(docptr); - poll_status(doc); - write_nop(docptr); -} - -static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) -{ - /* first step in reading a page */ - - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - - dev_dbg(doc->dev, - "docg4: %s: g4 page %08x\n", __func__, docg4_addr); - - sequence_reset(mtd); - - writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); - writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); - write_nop(docptr); - - write_addr(doc, docg4_addr); - - write_nop(docptr); - writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); - write_nop(docptr); - write_nop(docptr); - - poll_status(doc); -} - -static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) -{ - /* first step in writing a page */ - - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - - dev_dbg(doc->dev, - "docg4: %s: g4 addr: %x\n", __func__, docg4_addr); - sequence_reset(mtd); - - if (unlikely(reliable_mode)) { - writew(DOCG4_SEQ_SETMODE, docptr + DOC_FLASHSEQUENCE); - writew(DOCG4_CMD_FAST_MODE, docptr + DOC_FLASHCOMMAND); - writew(DOC_CMD_RELIABLE_MODE, docptr + DOC_FLASHCOMMAND); - write_nop(docptr); - } - - writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE); - writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND); - write_nop(docptr); - write_addr(doc, docg4_addr); - write_nop(docptr); - write_nop(docptr); - poll_status(doc); -} - -static uint32_t mtd_to_docg4_address(int page, int column) -{ - /* - * Convert mtd address to format used by the device, 32 bit packed. - * - * Some notes on G4 addressing... The M-Sys documentation on this device - * claims that pages are 2K in length, and indeed, the format of the - * address used by the device reflects that. But within each page are - * four 512 byte "sub-pages", each with its own oob data that is - * read/written immediately after the 512 bytes of page data. This oob - * data contains the ecc bytes for the preceeding 512 bytes. - * - * Rather than tell the mtd nand infrastructure that page size is 2k, - * with four sub-pages each, we engage in a little subterfuge and tell - * the infrastructure code that pages are 512 bytes in size. This is - * done because during the course of reverse-engineering the device, I - * never observed an instance where an entire 2K "page" was read or - * written as a unit. Each "sub-page" is always addressed individually, - * its data read/written, and ecc handled before the next "sub-page" is - * addressed. - * - * This requires us to convert addresses passed by the mtd nand - * infrastructure code to those used by the device. - * - * The address that is written to the device consists of four bytes: the - * first two are the 2k page number, and the second is the index into - * the page. The index is in terms of 16-bit half-words and includes - * the preceeding oob data, so e.g., the index into the second - * "sub-page" is 0x108, and the full device address of the start of mtd - * page 0x201 is 0x00800108. - */ - int g4_page = page / 4; /* device's 2K page */ - int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */ - return (g4_page << 16) | g4_index; /* pack */ -} - -static void docg4_command(struct mtd_info *mtd, unsigned command, int column, - int page_addr) -{ - /* handle standard nand commands */ - - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - uint32_t g4_addr = mtd_to_docg4_address(page_addr, column); - - dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n", - __func__, command, page_addr, column); - - /* - * Save the command and its arguments. This enables emulation of - * standard flash devices, and also some optimizations. - */ - doc->last_command.command = command; - doc->last_command.column = column; - doc->last_command.page = page_addr; - - switch (command) { - - case NAND_CMD_RESET: - reset(mtd); - break; - - case NAND_CMD_READ0: - read_page_prologue(mtd, g4_addr); - break; - - case NAND_CMD_STATUS: - /* next call to read_byte() will expect a status */ - break; - - case NAND_CMD_SEQIN: - if (unlikely(reliable_mode)) { - uint16_t g4_page = g4_addr >> 16; - - /* writes to odd-numbered 2k pages are invalid */ - if (g4_page & 0x01) - dev_warn(doc->dev, - "invalid reliable mode address\n"); - } - - write_page_prologue(mtd, g4_addr); - - /* hack for deferred write of oob bytes */ - if (doc->oob_page == page_addr) - memcpy(nand->oob_poi, doc->oob_buf, 16); - break; - - case NAND_CMD_PAGEPROG: - pageprog(mtd); - break; - - /* we don't expect these, based on review of nand_base.c */ - case NAND_CMD_READOOB: - case NAND_CMD_READID: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - dev_warn(doc->dev, "docg4_command: " - "unexpected nand command 0x%x\n", command); - break; - - } -} - -static int read_page(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int page, bool use_ecc) -{ - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - uint16_t status, edc_err, *buf16; - int bits_corrected = 0; - - dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); - - nand_read_page_op(nand, page, 0, NULL, 0); - - writew(DOC_ECCCONF0_READ_MODE | - DOC_ECCCONF0_ECC_ENABLE | - DOC_ECCCONF0_UNKNOWN | - DOCG4_BCH_SIZE, - docptr + DOC_ECCCONF0); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - - /* the 1st byte from the I/O reg is a status; the rest is page data */ - status = readw(docptr + DOC_IOSPACE_DATA); - if (status & DOCG4_READ_ERROR) { - dev_err(doc->dev, - "docg4_read_page: bad status: 0x%02x\n", status); - writew(0, docptr + DOC_DATAEND); - return -EIO; - } - - dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); - - docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */ - - /* this device always reads oob after page data */ - /* first 14 oob bytes read from I/O reg */ - docg4_read_buf(mtd, nand->oob_poi, 14); - - /* last 2 read from another reg */ - buf16 = (uint16_t *)(nand->oob_poi + 14); - *buf16 = readw(docptr + DOCG4_MYSTERY_REG); - - write_nop(docptr); - - if (likely(use_ecc == true)) { - - /* read the register that tells us if bitflip(s) detected */ - edc_err = readw(docptr + DOC_ECCCONF1); - edc_err = readw(docptr + DOC_ECCCONF1); - dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err); - - /* If bitflips are reported, attempt to correct with ecc */ - if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { - bits_corrected = correct_data(mtd, buf, page); - if (bits_corrected == -EBADMSG) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += bits_corrected; - } - } - - writew(0, docptr + DOC_DATAEND); - if (bits_corrected == -EBADMSG) /* uncorrectable errors */ - return 0; - return bits_corrected; -} - - -static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int oob_required, int page) -{ - return read_page(mtd, nand, buf, page, false); -} - -static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int oob_required, int page) -{ - return read_page(mtd, nand, buf, page, true); -} - -static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, - int page) -{ - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - uint16_t status; - - dev_dbg(doc->dev, "%s: page %x\n", __func__, page); - - nand_read_page_op(nand, page, nand->ecc.size, NULL, 0); - - writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - - /* the 1st byte from the I/O reg is a status; the rest is oob data */ - status = readw(docptr + DOC_IOSPACE_DATA); - if (status & DOCG4_READ_ERROR) { - dev_warn(doc->dev, - "docg4_read_oob failed: status = 0x%02x\n", status); - return -EIO; - } - - dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); - - docg4_read_buf(mtd, nand->oob_poi, 16); - - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - writew(0, docptr + DOC_DATAEND); - write_nop(docptr); - - return 0; -} - -static int docg4_erase_block(struct mtd_info *mtd, int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - uint16_t g4_page; - int status; - - dev_dbg(doc->dev, "%s: page %04x\n", __func__, page); - - sequence_reset(mtd); - - writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE); - writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND); - write_nop(docptr); - - /* only 2 bytes of address are written to specify erase block */ - g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */ - writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); - g4_page >>= 8; - writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); - write_nop(docptr); - - /* start the erasure */ - writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND); - write_nop(docptr); - write_nop(docptr); - - usleep_range(500, 1000); /* erasure is long; take a snooze */ - poll_status(doc); - writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); - writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); - writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - write_nop(docptr); - - read_progstatus(doc); - - writew(0, docptr + DOC_DATAEND); - write_nop(docptr); - poll_status(doc); - write_nop(docptr); - - status = nand->waitfunc(mtd, nand); - if (status < 0) - return status; - - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -static int write_page(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf, int page, bool use_ecc) -{ - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - uint8_t ecc_buf[8]; - - dev_dbg(doc->dev, "%s...\n", __func__); - - nand_prog_page_begin_op(nand, page, 0, NULL, 0); - - writew(DOC_ECCCONF0_ECC_ENABLE | - DOC_ECCCONF0_UNKNOWN | - DOCG4_BCH_SIZE, - docptr + DOC_ECCCONF0); - write_nop(docptr); - - /* write the page data */ - docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE); - - /* oob bytes 0 through 5 are written to I/O reg */ - docg4_write_buf16(mtd, nand->oob_poi, 6); - - /* oob byte 6 written to a separate reg */ - writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7); - - write_nop(docptr); - write_nop(docptr); - - /* write hw-generated ecc bytes to oob */ - if (likely(use_ecc == true)) { - /* oob byte 7 is hamming code */ - uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY); - hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */ - writew(hamming, docptr + DOCG4_OOB_6_7); - write_nop(docptr); - - /* read the 7 bch bytes from ecc regs */ - read_hw_ecc(docptr, ecc_buf); - ecc_buf[7] = 0; /* clear the "page written" flag */ - } - - /* write user-supplied bytes to oob */ - else { - writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7); - write_nop(docptr); - memcpy(ecc_buf, &nand->oob_poi[8], 8); - } - - docg4_write_buf16(mtd, ecc_buf, 8); - write_nop(docptr); - write_nop(docptr); - writew(0, docptr + DOC_DATAEND); - write_nop(docptr); - - return nand_prog_page_end_op(nand); -} - -static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf, int oob_required, int page) -{ - return write_page(mtd, nand, buf, page, false); -} - -static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf, int oob_required, int page) -{ - return write_page(mtd, nand, buf, page, true); -} - -static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand, - int page) -{ - /* - * Writing oob-only is not really supported, because MLC nand must write - * oob bytes at the same time as page data. Nonetheless, we save the - * oob buffer contents here, and then write it along with the page data - * if the same page is subsequently written. This allows user space - * utilities that write the oob data prior to the page data to work - * (e.g., nandwrite). The disdvantage is that, if the intention was to - * write oob only, the operation is quietly ignored. Also, oob can get - * corrupted if two concurrent processes are running nandwrite. - */ - - /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */ - struct docg4_priv *doc = nand_get_controller_data(nand); - doc->oob_page = page; - memcpy(doc->oob_buf, nand->oob_poi, 16); - return 0; -} - -static int __init read_factory_bbt(struct mtd_info *mtd) -{ - /* - * The device contains a read-only factory bad block table. Read it and - * update the memory-based bbt accordingly. - */ - - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); - uint8_t *buf; - int i, block; - __u32 eccfailed_stats = mtd->ecc_stats.failed; - - buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - read_page_prologue(mtd, g4_addr); - docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); - - /* - * If no memory-based bbt was created, exit. This will happen if module - * parameter ignore_badblocks is set. Then why even call this function? - * For an unknown reason, block erase always fails if it's the first - * operation after device power-up. The above read ensures it never is. - * Ugly, I know. - */ - if (nand->bbt == NULL) /* no memory-based bbt */ - goto exit; - - if (mtd->ecc_stats.failed > eccfailed_stats) { - /* - * Whoops, an ecc failure ocurred reading the factory bbt. - * It is stored redundantly, so we get another chance. - */ - eccfailed_stats = mtd->ecc_stats.failed; - docg4_read_page(mtd, nand, buf, 0, DOCG4_REDUNDANT_BBT_PAGE); - if (mtd->ecc_stats.failed > eccfailed_stats) { - dev_warn(doc->dev, - "The factory bbt could not be read!\n"); - goto exit; - } - } - - /* - * Parse factory bbt and update memory-based bbt. Factory bbt format is - * simple: one bit per block, block numbers increase left to right (msb - * to lsb). Bit clear means bad block. - */ - for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) { - int bitnum; - unsigned long bits = ~buf[i]; - for_each_set_bit(bitnum, &bits, 8) { - int badblock = block + 7 - bitnum; - nand->bbt[badblock / 4] |= - 0x03 << ((badblock % 4) * 2); - mtd->ecc_stats.badblocks++; - dev_notice(doc->dev, "factory-marked bad block: %d\n", - badblock); - } - } - exit: - kfree(buf); - return 0; -} - -static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - /* - * Mark a block as bad. Bad blocks are marked in the oob area of the - * first page of the block. The default scan_bbt() in the nand - * infrastructure code works fine for building the memory-based bbt - * during initialization, as does the nand infrastructure function that - * checks if a block is bad by reading the bbt. This function replaces - * the nand default because writes to oob-only are not supported. - */ - - int ret, i; - uint8_t *buf; - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - struct nand_bbt_descr *bbtd = nand->badblock_pattern; - int page = (int)(ofs >> nand->page_shift); - uint32_t g4_addr = mtd_to_docg4_address(page, 0); - - dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs); - - if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1))) - dev_warn(doc->dev, "%s: ofs %llx not start of block!\n", - __func__, ofs); - - /* allocate blank buffer for page data */ - buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - /* write bit-wise negation of pattern to oob buffer */ - memset(nand->oob_poi, 0xff, mtd->oobsize); - for (i = 0; i < bbtd->len; i++) - nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i]; - - /* write first page of block */ - write_page_prologue(mtd, g4_addr); - docg4_write_page(mtd, nand, buf, 1, page); - ret = pageprog(mtd); - - kfree(buf); - - return ret; -} - -static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs) -{ - /* only called when module_param ignore_badblocks is set */ - return 0; -} - -static int docg4_suspend(struct platform_device *pdev, pm_message_t state) -{ - /* - * Put the device into "deep power-down" mode. Note that CE# must be - * deasserted for this to take effect. The xscale, e.g., can be - * configured to float this signal when the processor enters power-down, - * and a suitable pull-up ensures its deassertion. - */ - - int i; - uint8_t pwr_down; - struct docg4_priv *doc = platform_get_drvdata(pdev); - void __iomem *docptr = doc->virtadr; - - dev_dbg(doc->dev, "%s...\n", __func__); - - /* poll the register that tells us we're ready to go to sleep */ - for (i = 0; i < 10; i++) { - pwr_down = readb(docptr + DOC_POWERMODE); - if (pwr_down & DOC_POWERDOWN_READY) - break; - usleep_range(1000, 4000); - } - - if (pwr_down & DOC_POWERDOWN_READY) { - dev_err(doc->dev, "suspend failed; " - "timeout polling DOC_POWERDOWN_READY\n"); - return -EIO; - } - - writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN, - docptr + DOC_ASICMODE); - writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN), - docptr + DOC_ASICMODECONFIRM); - - write_nop(docptr); - - return 0; -} - -static int docg4_resume(struct platform_device *pdev) -{ - - /* - * Exit power-down. Twelve consecutive reads of the address below - * accomplishes this, assuming CE# has been asserted. - */ - - struct docg4_priv *doc = platform_get_drvdata(pdev); - void __iomem *docptr = doc->virtadr; - int i; - - dev_dbg(doc->dev, "%s...\n", __func__); - - for (i = 0; i < 12; i++) - readb(docptr + 0x1fff); - - return 0; -} - -static void __init init_mtd_structs(struct mtd_info *mtd) -{ - /* initialize mtd and nand data structures */ - - /* - * Note that some of the following initializations are not usually - * required within a nand driver because they are performed by the nand - * infrastructure code as part of nand_scan(). In this case they need - * to be initialized here because we skip call to nand_scan_ident() (the - * first half of nand_scan()). The call to nand_scan_ident() is skipped - * because for this device the chip id is not read in the manner of a - * standard nand device. Unfortunately, nand_scan_ident() does other - * things as well, such as call nand_set_defaults(). - */ - - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - - mtd->size = DOCG4_CHIP_SIZE; - mtd->name = "Msys_Diskonchip_G4"; - mtd->writesize = DOCG4_PAGE_SIZE; - mtd->erasesize = DOCG4_BLOCK_SIZE; - mtd->oobsize = DOCG4_OOB_SIZE; - mtd_set_ooblayout(mtd, &docg4_ooblayout_ops); - nand->chipsize = DOCG4_CHIP_SIZE; - nand->chip_shift = DOCG4_CHIP_SHIFT; - nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT; - nand->chip_delay = 20; - nand->page_shift = DOCG4_PAGE_SHIFT; - nand->pagemask = 0x3ffff; - nand->badblockpos = NAND_LARGE_BADBLOCK_POS; - nand->badblockbits = 8; - nand->ecc.mode = NAND_ECC_HW_SYNDROME; - nand->ecc.size = DOCG4_PAGE_SIZE; - nand->ecc.prepad = 8; - nand->ecc.bytes = 8; - nand->ecc.strength = DOCG4_T; - nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; - nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; - nand->controller = &nand->hwcontrol; - nand_hw_control_init(nand->controller); - - /* methods */ - nand->cmdfunc = docg4_command; - nand->waitfunc = docg4_wait; - nand->select_chip = docg4_select_chip; - nand->read_byte = docg4_read_byte; - nand->block_markbad = docg4_block_markbad; - nand->read_buf = docg4_read_buf; - nand->write_buf = docg4_write_buf16; - nand->erase = docg4_erase_block; - nand->onfi_set_features = nand_onfi_get_set_features_notsupp; - nand->onfi_get_features = nand_onfi_get_set_features_notsupp; - nand->ecc.read_page = docg4_read_page; - nand->ecc.write_page = docg4_write_page; - nand->ecc.read_page_raw = docg4_read_page_raw; - nand->ecc.write_page_raw = docg4_write_page_raw; - nand->ecc.read_oob = docg4_read_oob; - nand->ecc.write_oob = docg4_write_oob; - - /* - * The way the nand infrastructure code is written, a memory-based bbt - * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt, - * nand->block_bad() is used. So when ignoring bad blocks, we skip the - * scan and define a dummy block_bad() which always returns 0. - */ - if (ignore_badblocks) { - nand->options |= NAND_SKIP_BBTSCAN; - nand->block_bad = docg4_block_neverbad; - } - -} - -static int __init read_id_reg(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct docg4_priv *doc = nand_get_controller_data(nand); - void __iomem *docptr = doc->virtadr; - uint16_t id1, id2; - - /* check for presence of g4 chip by reading id registers */ - id1 = readw(docptr + DOC_CHIPID); - id1 = readw(docptr + DOCG4_MYSTERY_REG); - id2 = readw(docptr + DOC_CHIPID_INV); - id2 = readw(docptr + DOCG4_MYSTERY_REG); - - if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) { - dev_info(doc->dev, - "NAND device: 128MiB Diskonchip G4 detected\n"); - return 0; - } - - return -ENODEV; -} - -static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL }; - -static int __init probe_docg4(struct platform_device *pdev) -{ - struct mtd_info *mtd; - struct nand_chip *nand; - void __iomem *virtadr; - struct docg4_priv *doc; - int len, retval; - struct resource *r; - struct device *dev = &pdev->dev; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - dev_err(dev, "no io memory resource defined!\n"); - return -ENODEV; - } - - virtadr = ioremap(r->start, resource_size(r)); - if (!virtadr) { - dev_err(dev, "Diskonchip ioremap failed: %pR\n", r); - return -EIO; - } - - len = sizeof(struct nand_chip) + sizeof(struct docg4_priv); - nand = kzalloc(len, GFP_KERNEL); - if (nand == NULL) { - retval = -ENOMEM; - goto fail_unmap; - } - - mtd = nand_to_mtd(nand); - doc = (struct docg4_priv *) (nand + 1); - nand_set_controller_data(nand, doc); - mtd->dev.parent = &pdev->dev; - doc->virtadr = virtadr; - doc->dev = dev; - - init_mtd_structs(mtd); - - /* initialize kernel bch algorithm */ - doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); - if (doc->bch == NULL) { - retval = -EINVAL; - goto fail; - } - - platform_set_drvdata(pdev, doc); - - reset(mtd); - retval = read_id_reg(mtd); - if (retval == -ENODEV) { - dev_warn(dev, "No diskonchip G4 device found.\n"); - goto fail; - } - - retval = nand_scan_tail(mtd); - if (retval) - goto fail; - - retval = read_factory_bbt(mtd); - if (retval) - goto fail; - - retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); - if (retval) - goto fail; - - doc->mtd = mtd; - return 0; - -fail: - nand_release(mtd); /* deletes partitions and mtd devices */ - free_bch(doc->bch); - kfree(nand); - -fail_unmap: - iounmap(virtadr); - - return retval; -} - -static int __exit cleanup_docg4(struct platform_device *pdev) -{ - struct docg4_priv *doc = platform_get_drvdata(pdev); - nand_release(doc->mtd); - free_bch(doc->bch); - kfree(mtd_to_nand(doc->mtd)); - iounmap(doc->virtadr); - return 0; -} - -static struct platform_driver docg4_driver = { - .driver = { - .name = "docg4", - }, - .suspend = docg4_suspend, - .resume = docg4_resume, - .remove = __exit_p(cleanup_docg4), -}; - -module_platform_driver_probe(docg4_driver, probe_docg4); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mike Dunn"); -MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver"); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c deleted file mode 100644 index 8b6dcd739ecb..000000000000 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ /dev/null @@ -1,979 +0,0 @@ -/* Freescale Enhanced Local Bus Controller NAND driver - * - * Copyright © 2006-2007, 2010 Freescale Semiconductor - * - * Authors: Nick Spence , - * Scott Wood - * Jack Lan - * Roy Zang - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#define MAX_BANKS 8 -#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ -#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */ - -/* mtd information per set */ - -struct fsl_elbc_mtd { - struct nand_chip chip; - struct fsl_lbc_ctrl *ctrl; - - struct device *dev; - int bank; /* Chip select bank number */ - u8 __iomem *vbase; /* Chip select base virtual address */ - int page_size; /* NAND page size (0=512, 1=2048) */ - unsigned int fmr; /* FCM Flash Mode Register value */ -}; - -/* Freescale eLBC FCM controller information */ - -struct fsl_elbc_fcm_ctrl { - struct nand_hw_control controller; - struct fsl_elbc_mtd *chips[MAX_BANKS]; - - u8 __iomem *addr; /* Address of assigned FCM buffer */ - unsigned int page; /* Last page written to / read from */ - unsigned int read_bytes; /* Number of bytes read during command */ - unsigned int column; /* Saved column from SEQIN */ - unsigned int index; /* Pointer to next byte to 'read' */ - unsigned int status; /* status read from LTESR after last op */ - unsigned int mdr; /* UPM/FCM Data Register value */ - unsigned int use_mdr; /* Non zero if the MDR is to be set */ - unsigned int oob; /* Non zero if operating on OOB data */ - unsigned int counter; /* counter for the initializations */ - unsigned int max_bitflips; /* Saved during READ0 cmd */ -}; - -/* These map to the positions used by the FCM hardware ECC generator */ - -static int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - - if (section >= chip->ecc.steps) - return -ERANGE; - - oobregion->offset = (16 * section) + 6; - if (priv->fmr & FMR_ECCM) - oobregion->offset += 2; - - oobregion->length = chip->ecc.bytes; - - return 0; -} - -static int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - - if (section > chip->ecc.steps) - return -ERANGE; - - if (!section) { - oobregion->offset = 0; - if (mtd->writesize > 512) - oobregion->offset++; - oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5; - } else { - oobregion->offset = (16 * section) - - ((priv->fmr & FMR_ECCM) ? 5 : 7); - if (section < chip->ecc.steps) - oobregion->length = 13; - else - oobregion->length = mtd->oobsize - oobregion->offset; - } - - return 0; -} - -static const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = { - .ecc = fsl_elbc_ooblayout_ecc, - .free = fsl_elbc_ooblayout_free, -}; - -/* - * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, - * interfere with ECC positions, that's why we implement our own descriptors. - * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. - */ -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 11, - .len = 4, - .veroffs = 15, - .maxblocks = 4, - .pattern = bbt_pattern, -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 11, - .len = 4, - .veroffs = 15, - .maxblocks = 4, - .pattern = mirror_pattern, -}; - -/*=================================*/ - -/* - * Set up the FCM hardware block and page address fields, and the fcm - * structure addr field to point to the correct FCM buffer in memory - */ -static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_lbc_ctrl *ctrl = priv->ctrl; - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; - int buf_num; - - elbc_fcm_ctrl->page = page_addr; - - if (priv->page_size) { - /* - * large page size chip : FPAR[PI] save the lowest 6 bits, - * FBAR[BLK] save the other bits. - */ - out_be32(&lbc->fbar, page_addr >> 6); - out_be32(&lbc->fpar, - ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | - (oob ? FPAR_LP_MS : 0) | column); - buf_num = (page_addr & 1) << 2; - } else { - /* - * small page size chip : FPAR[PI] save the lowest 5 bits, - * FBAR[BLK] save the other bits. - */ - out_be32(&lbc->fbar, page_addr >> 5); - out_be32(&lbc->fpar, - ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | - (oob ? FPAR_SP_MS : 0) | column); - buf_num = page_addr & 7; - } - - elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024; - elbc_fcm_ctrl->index = column; - - /* for OOB data point to the second half of the buffer */ - if (oob) - elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512; - - dev_vdbg(priv->dev, "set_addr: bank=%d, " - "elbc_fcm_ctrl->addr=0x%p (0x%p), " - "index %x, pes %d ps %d\n", - buf_num, elbc_fcm_ctrl->addr, priv->vbase, - elbc_fcm_ctrl->index, - chip->phys_erase_shift, chip->page_shift); -} - -/* - * execute FCM command and wait for it to complete - */ -static int fsl_elbc_run_command(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_lbc_ctrl *ctrl = priv->ctrl; - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - - /* Setup the FMR[OP] to execute without write protection */ - out_be32(&lbc->fmr, priv->fmr | 3); - if (elbc_fcm_ctrl->use_mdr) - out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr); - - dev_vdbg(priv->dev, - "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", - in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); - dev_vdbg(priv->dev, - "fsl_elbc_run_command: fbar=%08x fpar=%08x " - "fbcr=%08x bank=%d\n", - in_be32(&lbc->fbar), in_be32(&lbc->fpar), - in_be32(&lbc->fbcr), priv->bank); - - ctrl->irq_status = 0; - /* execute special operation */ - out_be32(&lbc->lsor, priv->bank); - - /* wait for FCM complete flag or timeout */ - wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, - FCM_TIMEOUT_MSECS * HZ/1000); - elbc_fcm_ctrl->status = ctrl->irq_status; - /* store mdr value in case it was needed */ - if (elbc_fcm_ctrl->use_mdr) - elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr); - - elbc_fcm_ctrl->use_mdr = 0; - - if (elbc_fcm_ctrl->status != LTESR_CC) { - dev_info(priv->dev, - "command failed: fir %x fcr %x status %x mdr %x\n", - in_be32(&lbc->fir), in_be32(&lbc->fcr), - elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr); - return -EIO; - } - - if (chip->ecc.mode != NAND_ECC_HW) - return 0; - - elbc_fcm_ctrl->max_bitflips = 0; - - if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) { - uint32_t lteccr = in_be32(&lbc->lteccr); - /* - * if command was a full page read and the ELBC - * has the LTECCR register, then bits 12-15 (ppc order) of - * LTECCR indicates which 512 byte sub-pages had fixed errors. - * bits 28-31 are uncorrectable errors, marked elsewhere. - * for small page nand only 1 bit is used. - * if the ELBC doesn't have the lteccr register it reads 0 - * FIXME: 4 bits can be corrected on NANDs with 2k pages, so - * count the number of sub-pages with bitflips and update - * ecc_stats.corrected accordingly. - */ - if (lteccr & 0x000F000F) - out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */ - if (lteccr & 0x000F0000) { - mtd->ecc_stats.corrected++; - elbc_fcm_ctrl->max_bitflips = 1; - } - } - - return 0; -} - -static void fsl_elbc_do_read(struct nand_chip *chip, int oob) -{ - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_lbc_ctrl *ctrl = priv->ctrl; - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - - if (priv->page_size) { - out_be32(&lbc->fir, - (FIR_OP_CM0 << FIR_OP0_SHIFT) | - (FIR_OP_CA << FIR_OP1_SHIFT) | - (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_CM1 << FIR_OP3_SHIFT) | - (FIR_OP_RBW << FIR_OP4_SHIFT)); - - out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | - (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); - } else { - out_be32(&lbc->fir, - (FIR_OP_CM0 << FIR_OP0_SHIFT) | - (FIR_OP_CA << FIR_OP1_SHIFT) | - (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_RBW << FIR_OP3_SHIFT)); - - if (oob) - out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT); - else - out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); - } -} - -/* cmdfunc send commands to the FCM */ -static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_lbc_ctrl *ctrl = priv->ctrl; - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - - elbc_fcm_ctrl->use_mdr = 0; - - /* clear the read buffer */ - elbc_fcm_ctrl->read_bytes = 0; - if (command != NAND_CMD_PAGEPROG) - elbc_fcm_ctrl->index = 0; - - switch (command) { - /* READ0 and READ1 read the entire buffer to use hardware ECC. */ - case NAND_CMD_READ1: - column += 256; - - /* fall-through */ - case NAND_CMD_READ0: - dev_dbg(priv->dev, - "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" - " 0x%x, column: 0x%x.\n", page_addr, column); - - - out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ - set_addr(mtd, 0, page_addr, 0); - - elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; - elbc_fcm_ctrl->index += column; - - fsl_elbc_do_read(chip, 0); - fsl_elbc_run_command(mtd); - return; - - /* READOOB reads only the OOB because no ECC is performed. */ - case NAND_CMD_READOOB: - dev_vdbg(priv->dev, - "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" - " 0x%x, column: 0x%x.\n", page_addr, column); - - out_be32(&lbc->fbcr, mtd->oobsize - column); - set_addr(mtd, column, page_addr, 1); - - elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; - - fsl_elbc_do_read(chip, 1); - fsl_elbc_run_command(mtd); - return; - - case NAND_CMD_READID: - case NAND_CMD_PARAM: - dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD %x\n", command); - - out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | - (FIR_OP_UA << FIR_OP1_SHIFT) | - (FIR_OP_RBW << FIR_OP2_SHIFT)); - out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); - /* - * although currently it's 8 bytes for READID, we always read - * the maximum 256 bytes(for PARAM) - */ - out_be32(&lbc->fbcr, 256); - elbc_fcm_ctrl->read_bytes = 256; - elbc_fcm_ctrl->use_mdr = 1; - elbc_fcm_ctrl->mdr = column; - set_addr(mtd, 0, 0, 0); - fsl_elbc_run_command(mtd); - return; - - /* ERASE1 stores the block and page address */ - case NAND_CMD_ERASE1: - dev_vdbg(priv->dev, - "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " - "page_addr: 0x%x.\n", page_addr); - set_addr(mtd, 0, page_addr, 0); - return; - - /* ERASE2 uses the block and page address from ERASE1 */ - case NAND_CMD_ERASE2: - dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); - - out_be32(&lbc->fir, - (FIR_OP_CM0 << FIR_OP0_SHIFT) | - (FIR_OP_PA << FIR_OP1_SHIFT) | - (FIR_OP_CM2 << FIR_OP2_SHIFT) | - (FIR_OP_CW1 << FIR_OP3_SHIFT) | - (FIR_OP_RS << FIR_OP4_SHIFT)); - - out_be32(&lbc->fcr, - (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | - (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | - (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT)); - - out_be32(&lbc->fbcr, 0); - elbc_fcm_ctrl->read_bytes = 0; - elbc_fcm_ctrl->use_mdr = 1; - - fsl_elbc_run_command(mtd); - return; - - /* SEQIN sets up the addr buffer and all registers except the length */ - case NAND_CMD_SEQIN: { - __be32 fcr; - dev_vdbg(priv->dev, - "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " - "page_addr: 0x%x, column: 0x%x.\n", - page_addr, column); - - elbc_fcm_ctrl->column = column; - elbc_fcm_ctrl->use_mdr = 1; - - if (column >= mtd->writesize) { - /* OOB area */ - column -= mtd->writesize; - elbc_fcm_ctrl->oob = 1; - } else { - WARN_ON(column != 0); - elbc_fcm_ctrl->oob = 0; - } - - fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | - (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) | - (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT); - - if (priv->page_size) { - out_be32(&lbc->fir, - (FIR_OP_CM2 << FIR_OP0_SHIFT) | - (FIR_OP_CA << FIR_OP1_SHIFT) | - (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_WB << FIR_OP3_SHIFT) | - (FIR_OP_CM3 << FIR_OP4_SHIFT) | - (FIR_OP_CW1 << FIR_OP5_SHIFT) | - (FIR_OP_RS << FIR_OP6_SHIFT)); - } else { - out_be32(&lbc->fir, - (FIR_OP_CM0 << FIR_OP0_SHIFT) | - (FIR_OP_CM2 << FIR_OP1_SHIFT) | - (FIR_OP_CA << FIR_OP2_SHIFT) | - (FIR_OP_PA << FIR_OP3_SHIFT) | - (FIR_OP_WB << FIR_OP4_SHIFT) | - (FIR_OP_CM3 << FIR_OP5_SHIFT) | - (FIR_OP_CW1 << FIR_OP6_SHIFT) | - (FIR_OP_RS << FIR_OP7_SHIFT)); - - if (elbc_fcm_ctrl->oob) - /* OOB area --> READOOB */ - fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; - else - /* First 256 bytes --> READ0 */ - fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; - } - - out_be32(&lbc->fcr, fcr); - set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob); - return; - } - - /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ - case NAND_CMD_PAGEPROG: { - dev_vdbg(priv->dev, - "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " - "writing %d bytes.\n", elbc_fcm_ctrl->index); - - /* if the write did not start at 0 or is not a full page - * then set the exact length, otherwise use a full page - * write so the HW generates the ECC. - */ - if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 || - elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) - out_be32(&lbc->fbcr, - elbc_fcm_ctrl->index - elbc_fcm_ctrl->column); - else - out_be32(&lbc->fbcr, 0); - - fsl_elbc_run_command(mtd); - return; - } - - /* CMD_STATUS must read the status byte while CEB is active */ - /* Note - it does not wait for the ready line */ - case NAND_CMD_STATUS: - out_be32(&lbc->fir, - (FIR_OP_CM0 << FIR_OP0_SHIFT) | - (FIR_OP_RBW << FIR_OP1_SHIFT)); - out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); - out_be32(&lbc->fbcr, 1); - set_addr(mtd, 0, 0, 0); - elbc_fcm_ctrl->read_bytes = 1; - - fsl_elbc_run_command(mtd); - - /* The chip always seems to report that it is - * write-protected, even when it is not. - */ - setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP); - return; - - /* RESET without waiting for the ready line */ - case NAND_CMD_RESET: - dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); - out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); - out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); - fsl_elbc_run_command(mtd); - return; - - default: - dev_err(priv->dev, - "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", - command); - } -} - -static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip) -{ - /* The hardware does not seem to support multiple - * chips per bank. - */ -} - -/* - * Write buf to the FCM Controller Data Buffer - */ -static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; - unsigned int bufsize = mtd->writesize + mtd->oobsize; - - if (len <= 0) { - dev_err(priv->dev, "write_buf of %d bytes", len); - elbc_fcm_ctrl->status = 0; - return; - } - - if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) { - dev_err(priv->dev, - "write_buf beyond end of buffer " - "(%d requested, %u available)\n", - len, bufsize - elbc_fcm_ctrl->index); - len = bufsize - elbc_fcm_ctrl->index; - } - - memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len); - /* - * This is workaround for the weird elbc hangs during nand write, - * Scott Wood says: "...perhaps difference in how long it takes a - * write to make it through the localbus compared to a write to IMMR - * is causing problems, and sync isn't helping for some reason." - * Reading back the last byte helps though. - */ - in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1); - - elbc_fcm_ctrl->index += len; -} - -/* - * read a byte from either the FCM hardware buffer if it has any data left - * otherwise issue a command to read a single byte. - */ -static u8 fsl_elbc_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; - - /* If there are still bytes in the FCM, then use the next byte. */ - if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes) - return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]); - - dev_err(priv->dev, "read_byte beyond end of buffer\n"); - return ERR_BYTE; -} - -/* - * Read from the FCM Controller Data Buffer - */ -static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; - int avail; - - if (len < 0) - return; - - avail = min((unsigned int)len, - elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index); - memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail); - elbc_fcm_ctrl->index += avail; - - if (len > avail) - dev_err(priv->dev, - "read_buf beyond end of buffer " - "(%d requested, %d available)\n", - len, avail); -} - -/* This function is called after Program and Erase Operations to - * check for success or failure. - */ -static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) -{ - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; - - if (elbc_fcm_ctrl->status != LTESR_CC) - return NAND_STATUS_FAIL; - - /* The chip always seems to report that it is - * write-protected, even when it is not. - */ - return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP; -} - -static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_lbc_ctrl *ctrl = priv->ctrl; - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - unsigned int al; - - /* calculate FMR Address Length field */ - al = 0; - if (chip->pagemask & 0xffff0000) - al++; - if (chip->pagemask & 0xff000000) - al++; - - priv->fmr |= al << FMR_AL_SHIFT; - - dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n", - chip->numchips); - dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n", - chip->chipsize); - dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n", - chip->pagemask); - dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n", - chip->chip_delay); - dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n", - chip->badblockpos); - dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n", - chip->chip_shift); - dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n", - chip->page_shift); - dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", - chip->phys_erase_shift); - dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", - chip->ecc.mode); - dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", - chip->ecc.steps); - dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", - chip->ecc.bytes); - dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n", - chip->ecc.total); - dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n", - mtd->ooblayout); - dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); - dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size); - dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n", - mtd->erasesize); - dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n", - mtd->writesize); - dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n", - mtd->oobsize); - - /* adjust Option Register and ECC to match Flash page size */ - if (mtd->writesize == 512) { - priv->page_size = 0; - clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); - } else if (mtd->writesize == 2048) { - priv->page_size = 1; - setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); - } else { - dev_err(priv->dev, - "fsl_elbc_init: page size %d is not supported\n", - mtd->writesize); - return -1; - } - - return 0; -} - -static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_lbc_ctrl *ctrl = priv->ctrl; - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; - - nand_read_page_op(chip, page, 0, buf, mtd->writesize); - if (oob_required) - fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) - mtd->ecc_stats.failed++; - - return elbc_fcm_ctrl->max_bitflips; -} - -/* ECC will be calculated automatically, and errors will be detected in - * waitfunc. - */ -static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -/* ECC will be calculated automatically, and errors will be detected in - * waitfunc. - */ -static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offset, uint32_t data_len, - const uint8_t *buf, int oob_required, int page) -{ - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - fsl_elbc_write_buf(mtd, buf, mtd->writesize); - fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - return nand_prog_page_end_op(chip); -} - -static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) -{ - struct fsl_lbc_ctrl *ctrl = priv->ctrl; - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; - struct nand_chip *chip = &priv->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - - dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank); - - /* Fill in fsl_elbc_mtd structure */ - mtd->dev.parent = priv->dev; - nand_set_flash_node(chip, priv->dev->of_node); - - /* set timeout to maximum */ - priv->fmr = 15 << FMR_CWTO_SHIFT; - if (in_be32(&lbc->bank[priv->bank].or) & OR_FCM_PGS) - priv->fmr |= FMR_ECCM; - - /* fill in nand_chip structure */ - /* set up function call table */ - chip->read_byte = fsl_elbc_read_byte; - chip->write_buf = fsl_elbc_write_buf; - chip->read_buf = fsl_elbc_read_buf; - chip->select_chip = fsl_elbc_select_chip; - chip->cmdfunc = fsl_elbc_cmdfunc; - chip->waitfunc = fsl_elbc_wait; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; - - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; - - /* set up nand options */ - chip->bbt_options = NAND_BBT_USE_FLASH; - - chip->controller = &elbc_fcm_ctrl->controller; - nand_set_controller_data(chip, priv); - - chip->ecc.read_page = fsl_elbc_read_page; - chip->ecc.write_page = fsl_elbc_write_page; - chip->ecc.write_subpage = fsl_elbc_write_subpage; - - /* If CS Base Register selects full hardware ECC then use it */ - if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == - BR_DECC_CHK_GEN) { - chip->ecc.mode = NAND_ECC_HW; - mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops); - chip->ecc.size = 512; - chip->ecc.bytes = 3; - chip->ecc.strength = 1; - } else { - /* otherwise fall back to default software ECC */ - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - } - - return 0; -} - -static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) -{ - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; - struct mtd_info *mtd = nand_to_mtd(&priv->chip); - - nand_release(mtd); - - kfree(mtd->name); - - if (priv->vbase) - iounmap(priv->vbase); - - elbc_fcm_ctrl->chips[priv->bank] = NULL; - kfree(priv); - return 0; -} - -static DEFINE_MUTEX(fsl_elbc_nand_mutex); - -static int fsl_elbc_nand_probe(struct platform_device *pdev) -{ - struct fsl_lbc_regs __iomem *lbc; - struct fsl_elbc_mtd *priv; - struct resource res; - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl; - static const char *part_probe_types[] - = { "cmdlinepart", "RedBoot", "ofpart", NULL }; - int ret; - int bank; - struct device *dev; - struct device_node *node = pdev->dev.of_node; - struct mtd_info *mtd; - - if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) - return -ENODEV; - lbc = fsl_lbc_ctrl_dev->regs; - dev = fsl_lbc_ctrl_dev->dev; - - /* get, allocate and map the memory resource */ - ret = of_address_to_resource(node, 0, &res); - if (ret) { - dev_err(dev, "failed to get resource\n"); - return ret; - } - - /* find which chip select it is connected to */ - for (bank = 0; bank < MAX_BANKS; bank++) - if ((in_be32(&lbc->bank[bank].br) & BR_V) && - (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM && - (in_be32(&lbc->bank[bank].br) & - in_be32(&lbc->bank[bank].or) & BR_BA) - == fsl_lbc_addr(res.start)) - break; - - if (bank >= MAX_BANKS) { - dev_err(dev, "address did not match any chip selects\n"); - return -ENODEV; - } - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - mutex_lock(&fsl_elbc_nand_mutex); - if (!fsl_lbc_ctrl_dev->nand) { - elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL); - if (!elbc_fcm_ctrl) { - mutex_unlock(&fsl_elbc_nand_mutex); - ret = -ENOMEM; - goto err; - } - elbc_fcm_ctrl->counter++; - - nand_hw_control_init(&elbc_fcm_ctrl->controller); - fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl; - } else { - elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; - } - mutex_unlock(&fsl_elbc_nand_mutex); - - elbc_fcm_ctrl->chips[bank] = priv; - priv->bank = bank; - priv->ctrl = fsl_lbc_ctrl_dev; - priv->dev = &pdev->dev; - dev_set_drvdata(priv->dev, priv); - - priv->vbase = ioremap(res.start, resource_size(&res)); - if (!priv->vbase) { - dev_err(dev, "failed to map chip region\n"); - ret = -ENOMEM; - goto err; - } - - mtd = nand_to_mtd(&priv->chip); - mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); - if (!nand_to_mtd(&priv->chip)->name) { - ret = -ENOMEM; - goto err; - } - - ret = fsl_elbc_chip_init(priv); - if (ret) - goto err; - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - goto err; - - ret = fsl_elbc_chip_init_tail(mtd); - if (ret) - goto err; - - ret = nand_scan_tail(mtd); - if (ret) - goto err; - - /* First look for RedBoot table or partitions on the command - * line, these take precedence over device tree information */ - mtd_device_parse_register(mtd, part_probe_types, NULL, - NULL, 0); - - printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n", - (unsigned long long)res.start, priv->bank); - return 0; - -err: - fsl_elbc_chip_remove(priv); - return ret; -} - -static int fsl_elbc_nand_remove(struct platform_device *pdev) -{ - struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; - struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev); - - fsl_elbc_chip_remove(priv); - - mutex_lock(&fsl_elbc_nand_mutex); - elbc_fcm_ctrl->counter--; - if (!elbc_fcm_ctrl->counter) { - fsl_lbc_ctrl_dev->nand = NULL; - kfree(elbc_fcm_ctrl); - } - mutex_unlock(&fsl_elbc_nand_mutex); - - return 0; - -} - -static const struct of_device_id fsl_elbc_nand_match[] = { - { .compatible = "fsl,elbc-fcm-nand", }, - {} -}; -MODULE_DEVICE_TABLE(of, fsl_elbc_nand_match); - -static struct platform_driver fsl_elbc_nand_driver = { - .driver = { - .name = "fsl,elbc-fcm-nand", - .of_match_table = fsl_elbc_nand_match, - }, - .probe = fsl_elbc_nand_probe, - .remove = fsl_elbc_nand_remove, -}; - -module_platform_driver(fsl_elbc_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Freescale"); -MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver"); diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c deleted file mode 100644 index 4872a7ba6503..000000000000 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ /dev/null @@ -1,1117 +0,0 @@ -/* - * Freescale Integrated Flash Controller NAND driver - * - * Copyright 2011-2012 Freescale Semiconductor, Inc - * - * Author: Dipen Dudhat - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ERR_BYTE 0xFF /* Value returned for read - bytes when read failed */ -#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait - for IFC NAND Machine */ - -struct fsl_ifc_ctrl; - -/* mtd information per set */ -struct fsl_ifc_mtd { - struct nand_chip chip; - struct fsl_ifc_ctrl *ctrl; - - struct device *dev; - int bank; /* Chip select bank number */ - unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ - u8 __iomem *vbase; /* Chip select base virtual address */ -}; - -/* overview of the fsl ifc controller */ -struct fsl_ifc_nand_ctrl { - struct nand_hw_control controller; - struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT]; - - void __iomem *addr; /* Address of assigned IFC buffer */ - unsigned int page; /* Last page written to / read from */ - unsigned int read_bytes;/* Number of bytes read during command */ - unsigned int column; /* Saved column from SEQIN */ - unsigned int index; /* Pointer to next byte to 'read' */ - unsigned int oob; /* Non zero if operating on OOB data */ - unsigned int eccread; /* Non zero for a full-page ECC read */ - unsigned int counter; /* counter for the initializations */ - unsigned int max_bitflips; /* Saved during READ0 cmd */ -}; - -static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; - -/* - * Generic flash bbt descriptors - */ -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 2, /* 0 on 8-bit small page */ - .len = 4, - .veroffs = 6, - .maxblocks = 4, - .pattern = bbt_pattern, -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 2, /* 0 on 8-bit small page */ - .len = 4, - .veroffs = 6, - .maxblocks = 4, - .pattern = mirror_pattern, -}; - -static int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section) - return -ERANGE; - - oobregion->offset = 8; - oobregion->length = chip->ecc.total; - - return 0; -} - -static int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section > 1) - return -ERANGE; - - if (mtd->writesize == 512 && - !(chip->options & NAND_BUSWIDTH_16)) { - if (!section) { - oobregion->offset = 0; - oobregion->length = 5; - } else { - oobregion->offset = 6; - oobregion->length = 2; - } - - return 0; - } - - if (!section) { - oobregion->offset = 2; - oobregion->length = 6; - } else { - oobregion->offset = chip->ecc.total + 8; - oobregion->length = mtd->oobsize - oobregion->offset; - } - - return 0; -} - -static const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = { - .ecc = fsl_ifc_ooblayout_ecc, - .free = fsl_ifc_ooblayout_free, -}; - -/* - * Set up the IFC hardware block and page address fields, and the ifc nand - * structure addr field to point to the correct IFC buffer in memory - */ -static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - int buf_num; - - ifc_nand_ctrl->page = page_addr; - /* Program ROW0/COL0 */ - ifc_out32(page_addr, &ifc->ifc_nand.row0); - ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); - - buf_num = page_addr & priv->bufnum_mask; - - ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); - ifc_nand_ctrl->index = column; - - /* for OOB data point to the second half of the buffer */ - if (oob) - ifc_nand_ctrl->index += mtd->writesize; -} - -/* returns nonzero if entire page is blank */ -static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, - u32 *eccstat, unsigned int bufnum) -{ - u32 reg = eccstat[bufnum / 4]; - int errors; - - errors = (reg >> ((3 - bufnum % 4) * 8)) & 15; - - return errors; -} - -/* - * execute IFC NAND command and wait for it to complete - */ -static void fsl_ifc_run_command(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; - struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - u32 eccstat[4]; - int i; - - /* set the chip select for NAND Transaction */ - ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT, - &ifc->ifc_nand.nand_csel); - - dev_vdbg(priv->dev, - "%s: fir0=%08x fcr0=%08x\n", - __func__, - ifc_in32(&ifc->ifc_nand.nand_fir0), - ifc_in32(&ifc->ifc_nand.nand_fcr0)); - - ctrl->nand_stat = 0; - - /* start read/write seq */ - ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); - - /* wait for command complete flag or timeout */ - wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, - msecs_to_jiffies(IFC_TIMEOUT_MSECS)); - - /* ctrl->nand_stat will be updated from IRQ context */ - if (!ctrl->nand_stat) - dev_err(priv->dev, "Controller is not responding\n"); - if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER) - dev_err(priv->dev, "NAND Flash Timeout Error\n"); - if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) - dev_err(priv->dev, "NAND Flash Write Protect Error\n"); - - nctrl->max_bitflips = 0; - - if (nctrl->eccread) { - int errors; - int bufnum = nctrl->page & priv->bufnum_mask; - int sector = bufnum * chip->ecc.steps; - int sector_end = sector + chip->ecc.steps - 1; - __be32 *eccstat_regs; - - if (ctrl->version >= FSL_IFC_VERSION_2_0_0) - eccstat_regs = ifc->ifc_nand.v2_nand_eccstat; - else - eccstat_regs = ifc->ifc_nand.v1_nand_eccstat; - - for (i = sector / 4; i <= sector_end / 4; i++) - eccstat[i] = ifc_in32(&eccstat_regs[i]); - - for (i = sector; i <= sector_end; i++) { - errors = check_read_ecc(mtd, ctrl, eccstat, i); - - if (errors == 15) { - /* - * Uncorrectable error. - * We'll check for blank pages later. - * - * We disable ECCER reporting due to... - * erratum IFC-A002770 -- so report it now if we - * see an uncorrectable error in ECCSTAT. - */ - ctrl->nand_stat |= IFC_NAND_EVTER_STAT_ECCER; - continue; - } - - mtd->ecc_stats.corrected += errors; - nctrl->max_bitflips = max_t(unsigned int, - nctrl->max_bitflips, - errors); - } - - nctrl->eccread = 0; - } -} - -static void fsl_ifc_do_read(struct nand_chip *chip, - int oob, - struct mtd_info *mtd) -{ - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - - /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ - if (mtd->writesize > 512) { - ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), - &ifc->ifc_nand.nand_fir0); - ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); - - ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), - &ifc->ifc_nand.nand_fcr0); - } else { - ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), - &ifc->ifc_nand.nand_fir0); - ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); - - if (oob) - ifc_out32(NAND_CMD_READOOB << - IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - else - ifc_out32(NAND_CMD_READ0 << - IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - } -} - -/* cmdfunc send commands to the IFC NAND Machine */ -static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) { - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - - /* clear the read buffer */ - ifc_nand_ctrl->read_bytes = 0; - if (command != NAND_CMD_PAGEPROG) - ifc_nand_ctrl->index = 0; - - switch (command) { - /* READ0 read the entire buffer to use hardware ECC. */ - case NAND_CMD_READ0: - ifc_out32(0, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, 0, page_addr, 0); - - ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; - ifc_nand_ctrl->index += column; - - if (chip->ecc.mode == NAND_ECC_HW) - ifc_nand_ctrl->eccread = 1; - - fsl_ifc_do_read(chip, 0, mtd); - fsl_ifc_run_command(mtd); - return; - - /* READOOB reads only the OOB because no ECC is performed. */ - case NAND_CMD_READOOB: - ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, column, page_addr, 1); - - ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; - - fsl_ifc_do_read(chip, 1, mtd); - fsl_ifc_run_command(mtd); - - return; - - case NAND_CMD_READID: - case NAND_CMD_PARAM: { - int timing = IFC_FIR_OP_RB; - if (command == NAND_CMD_PARAM) - timing = IFC_FIR_OP_RBCD; - - ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (timing << IFC_NAND_FIR0_OP2_SHIFT), - &ifc->ifc_nand.nand_fir0); - ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - ifc_out32(column, &ifc->ifc_nand.row3); - - /* - * although currently it's 8 bytes for READID, we always read - * the maximum 256 bytes(for PARAM) - */ - ifc_out32(256, &ifc->ifc_nand.nand_fbcr); - ifc_nand_ctrl->read_bytes = 256; - - set_addr(mtd, 0, 0, 0); - fsl_ifc_run_command(mtd); - return; - } - - /* ERASE1 stores the block and page address */ - case NAND_CMD_ERASE1: - set_addr(mtd, 0, page_addr, 0); - return; - - /* ERASE2 uses the block and page address from ERASE1 */ - case NAND_CMD_ERASE2: - ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), - &ifc->ifc_nand.nand_fir0); - - ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), - &ifc->ifc_nand.nand_fcr0); - - ifc_out32(0, &ifc->ifc_nand.nand_fbcr); - ifc_nand_ctrl->read_bytes = 0; - fsl_ifc_run_command(mtd); - return; - - /* SEQIN sets up the addr buffer and all registers except the length */ - case NAND_CMD_SEQIN: { - u32 nand_fcr0; - ifc_nand_ctrl->column = column; - ifc_nand_ctrl->oob = 0; - - if (mtd->writesize > 512) { - nand_fcr0 = - (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | - (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); - - ifc_out32( - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), - &ifc->ifc_nand.nand_fir0); - ifc_out32( - (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | - (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) | - (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), - &ifc->ifc_nand.nand_fir1); - } else { - nand_fcr0 = ((NAND_CMD_PAGEPROG << - IFC_NAND_FCR0_CMD1_SHIFT) | - (NAND_CMD_SEQIN << - IFC_NAND_FCR0_CMD2_SHIFT) | - (NAND_CMD_STATUS << - IFC_NAND_FCR0_CMD3_SHIFT)); - - ifc_out32( - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), - &ifc->ifc_nand.nand_fir0); - ifc_out32( - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | - (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | - (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) | - (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), - &ifc->ifc_nand.nand_fir1); - - if (column >= mtd->writesize) - nand_fcr0 |= - NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; - else - nand_fcr0 |= - NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; - } - - if (column >= mtd->writesize) { - /* OOB area --> READOOB */ - column -= mtd->writesize; - ifc_nand_ctrl->oob = 1; - } - ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0); - set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); - return; - } - - /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ - case NAND_CMD_PAGEPROG: { - if (ifc_nand_ctrl->oob) { - ifc_out32(ifc_nand_ctrl->index - - ifc_nand_ctrl->column, - &ifc->ifc_nand.nand_fbcr); - } else { - ifc_out32(0, &ifc->ifc_nand.nand_fbcr); - } - - fsl_ifc_run_command(mtd); - return; - } - - case NAND_CMD_STATUS: { - void __iomem *addr; - - ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), - &ifc->ifc_nand.nand_fir0); - ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - ifc_out32(1, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, 0, 0, 0); - ifc_nand_ctrl->read_bytes = 1; - - fsl_ifc_run_command(mtd); - - /* - * The chip always seems to report that it is - * write-protected, even when it is not. - */ - addr = ifc_nand_ctrl->addr; - if (chip->options & NAND_BUSWIDTH_16) - ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr); - else - ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr); - return; - } - - case NAND_CMD_RESET: - ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, - &ifc->ifc_nand.nand_fir0); - ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - fsl_ifc_run_command(mtd); - return; - - default: - dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n", - __func__, command); - } -} - -static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) -{ - /* The hardware does not seem to support multiple - * chips per bank. - */ -} - -/* - * Write buf to the IFC NAND Controller Data Buffer - */ -static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - unsigned int bufsize = mtd->writesize + mtd->oobsize; - - if (len <= 0) { - dev_err(priv->dev, "%s: len %d bytes", __func__, len); - return; - } - - if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) { - dev_err(priv->dev, - "%s: beyond end of buffer (%d requested, %u available)\n", - __func__, len, bufsize - ifc_nand_ctrl->index); - len = bufsize - ifc_nand_ctrl->index; - } - - memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len); - ifc_nand_ctrl->index += len; -} - -/* - * Read a byte from either the IFC hardware buffer - * read function for 8-bit buswidth - */ -static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - unsigned int offset; - - /* - * If there are still bytes in the IFC buffer, then use the - * next byte. - */ - if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { - offset = ifc_nand_ctrl->index++; - return ifc_in8(ifc_nand_ctrl->addr + offset); - } - - dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); - return ERR_BYTE; -} - -/* - * Read two bytes from the IFC hardware buffer - * read function for 16-bit buswith - */ -static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - uint16_t data; - - /* - * If there are still bytes in the IFC buffer, then use the - * next byte. - */ - if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { - data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); - ifc_nand_ctrl->index += 2; - return (uint8_t) data; - } - - dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); - return ERR_BYTE; -} - -/* - * Read from the IFC Controller Data Buffer - */ -static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - int avail; - - if (len < 0) { - dev_err(priv->dev, "%s: len %d bytes", __func__, len); - return; - } - - avail = min((unsigned int)len, - ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index); - memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail); - ifc_nand_ctrl->index += avail; - - if (len > avail) - dev_err(priv->dev, - "%s: beyond end of buffer (%d requested, %d available)\n", - __func__, len, avail); -} - -/* - * This function is called after Program and Erase Operations to - * check for success or failure. - */ -static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) -{ - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; - u32 nand_fsr; - - /* Use READ_STATUS command, but wait for the device to be ready */ - ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), - &ifc->ifc_nand.nand_fir0); - ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - ifc_out32(1, &ifc->ifc_nand.nand_fbcr); - set_addr(mtd, 0, 0, 0); - ifc_nand_ctrl->read_bytes = 1; - - fsl_ifc_run_command(mtd); - - nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); - - /* - * The chip always seems to report that it is - * write-protected, even when it is not. - */ - return nand_fsr | NAND_STATUS_WP; -} - -/* - * The controller does not check for bitflips in erased pages, - * therefore software must check instead. - */ -static int check_erased_page(struct nand_chip *chip, u8 *buf) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - u8 *ecc = chip->oob_poi; - const int ecc_size = chip->ecc.bytes; - const int pkt_size = chip->ecc.size; - int i, res, bitflips = 0; - struct mtd_oob_region oobregion = { }; - - mtd_ooblayout_ecc(mtd, 0, &oobregion); - ecc += oobregion.offset; - - for (i = 0; i < chip->ecc.steps; ++i) { - res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, - NULL, 0, - chip->ecc.strength); - if (res < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += res; - - bitflips = max(res, bitflips); - buf += pkt_size; - ecc += ecc_size; - } - - return bitflips; -} - -static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; - - nand_read_page_op(chip, page, 0, buf, mtd->writesize); - if (oob_required) - fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) { - if (!oob_required) - fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - return check_erased_page(chip, buf); - } - - if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) - mtd->ecc_stats.failed++; - - return nctrl->max_bitflips; -} - -/* ECC will be calculated automatically, and errors will be detected in - * waitfunc. - */ -static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - - dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, - chip->numchips); - dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__, - chip->chipsize); - dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__, - chip->pagemask); - dev_dbg(priv->dev, "%s: nand->chip_delay = %d\n", __func__, - chip->chip_delay); - dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__, - chip->badblockpos); - dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__, - chip->chip_shift); - dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__, - chip->page_shift); - dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, - chip->phys_erase_shift); - dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__, - chip->ecc.mode); - dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, - chip->ecc.steps); - dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__, - chip->ecc.bytes); - dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__, - chip->ecc.total); - dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__, - mtd->ooblayout); - dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags); - dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size); - dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__, - mtd->erasesize); - dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__, - mtd->writesize); - dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__, - mtd->oobsize); - - return 0; -} - -static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) -{ - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; - struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; - uint32_t csor = 0, csor_8k = 0, csor_ext = 0; - uint32_t cs = priv->bank; - - /* Save CSOR and CSOR_ext */ - csor = ifc_in32(&ifc_global->csor_cs[cs].csor); - csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext); - - /* chage PageSize 8K and SpareSize 1K*/ - csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; - ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor); - ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext); - - /* READID */ - ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), - &ifc_runtime->ifc_nand.nand_fir0); - ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc_runtime->ifc_nand.nand_fcr0); - ifc_out32(0x0, &ifc_runtime->ifc_nand.row3); - - ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr); - - /* Program ROW0/COL0 */ - ifc_out32(0x0, &ifc_runtime->ifc_nand.row0); - ifc_out32(0x0, &ifc_runtime->ifc_nand.col0); - - /* set the chip select for NAND Transaction */ - ifc_out32(cs << IFC_NAND_CSEL_SHIFT, - &ifc_runtime->ifc_nand.nand_csel); - - /* start read seq */ - ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, - &ifc_runtime->ifc_nand.nandseq_strt); - - /* wait for command complete flag or timeout */ - wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, - msecs_to_jiffies(IFC_TIMEOUT_MSECS)); - - if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) - printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); - - /* Restore CSOR and CSOR_ext */ - ifc_out32(csor, &ifc_global->csor_cs[cs].csor); - ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext); -} - -static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) -{ - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; - struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; - struct nand_chip *chip = &priv->chip; - struct mtd_info *mtd = nand_to_mtd(&priv->chip); - u32 csor; - - /* Fill in fsl_ifc_mtd structure */ - mtd->dev.parent = priv->dev; - nand_set_flash_node(chip, priv->dev->of_node); - - /* fill in nand_chip structure */ - /* set up function call table */ - if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)) - & CSPR_PORT_SIZE_16) - chip->read_byte = fsl_ifc_read_byte16; - else - chip->read_byte = fsl_ifc_read_byte; - - chip->write_buf = fsl_ifc_write_buf; - chip->read_buf = fsl_ifc_read_buf; - chip->select_chip = fsl_ifc_select_chip; - chip->cmdfunc = fsl_ifc_cmdfunc; - chip->waitfunc = fsl_ifc_wait; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; - - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; - - ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr); - - /* set up nand options */ - chip->bbt_options = NAND_BBT_USE_FLASH; - chip->options = NAND_NO_SUBPAGE_WRITE; - - if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr) - & CSPR_PORT_SIZE_16) { - chip->read_byte = fsl_ifc_read_byte16; - chip->options |= NAND_BUSWIDTH_16; - } else { - chip->read_byte = fsl_ifc_read_byte; - } - - chip->controller = &ifc_nand_ctrl->controller; - nand_set_controller_data(chip, priv); - - chip->ecc.read_page = fsl_ifc_read_page; - chip->ecc.write_page = fsl_ifc_write_page; - - csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); - - switch (csor & CSOR_NAND_PGS_MASK) { - case CSOR_NAND_PGS_512: - if (!(chip->options & NAND_BUSWIDTH_16)) { - /* Avoid conflict with bad block marker */ - bbt_main_descr.offs = 0; - bbt_mirror_descr.offs = 0; - } - - priv->bufnum_mask = 15; - break; - - case CSOR_NAND_PGS_2K: - priv->bufnum_mask = 3; - break; - - case CSOR_NAND_PGS_4K: - priv->bufnum_mask = 1; - break; - - case CSOR_NAND_PGS_8K: - priv->bufnum_mask = 0; - break; - - default: - dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); - return -ENODEV; - } - - /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ - if (csor & CSOR_NAND_ECC_DEC_EN) { - chip->ecc.mode = NAND_ECC_HW; - mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); - - /* Hardware generates ECC per 512 Bytes */ - chip->ecc.size = 512; - if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) { - chip->ecc.bytes = 8; - chip->ecc.strength = 4; - } else { - chip->ecc.bytes = 16; - chip->ecc.strength = 8; - } - } else { - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - } - - if (ctrl->version >= FSL_IFC_VERSION_1_1_0) - fsl_ifc_sram_init(priv); - - /* - * As IFC version 2.0.0 has 16KB of internal SRAM as compared to older - * versions which had 8KB. Hence bufnum mask needs to be updated. - */ - if (ctrl->version >= FSL_IFC_VERSION_2_0_0) - priv->bufnum_mask = (priv->bufnum_mask * 2) + 1; - - return 0; -} - -static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) -{ - struct mtd_info *mtd = nand_to_mtd(&priv->chip); - - nand_release(mtd); - - kfree(mtd->name); - - if (priv->vbase) - iounmap(priv->vbase); - - ifc_nand_ctrl->chips[priv->bank] = NULL; - - return 0; -} - -static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank, - phys_addr_t addr) -{ - u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr); - - if (!(cspr & CSPR_V)) - return 0; - if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND) - return 0; - - return (cspr & CSPR_BA) == convert_ifc_address(addr); -} - -static DEFINE_MUTEX(fsl_ifc_nand_mutex); - -static int fsl_ifc_nand_probe(struct platform_device *dev) -{ - struct fsl_ifc_runtime __iomem *ifc; - struct fsl_ifc_mtd *priv; - struct resource res; - static const char *part_probe_types[] - = { "cmdlinepart", "RedBoot", "ofpart", NULL }; - int ret; - int bank; - struct device_node *node = dev->dev.of_node; - struct mtd_info *mtd; - - if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) - return -ENODEV; - ifc = fsl_ifc_ctrl_dev->rregs; - - /* get, allocate and map the memory resource */ - ret = of_address_to_resource(node, 0, &res); - if (ret) { - dev_err(&dev->dev, "%s: failed to get resource\n", __func__); - return ret; - } - - /* find which chip select it is connected to */ - for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { - if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) - break; - } - - if (bank >= fsl_ifc_ctrl_dev->banks) { - dev_err(&dev->dev, "%s: address did not match any chip selects\n", - __func__); - return -ENODEV; - } - - priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - mutex_lock(&fsl_ifc_nand_mutex); - if (!fsl_ifc_ctrl_dev->nand) { - ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL); - if (!ifc_nand_ctrl) { - mutex_unlock(&fsl_ifc_nand_mutex); - return -ENOMEM; - } - - ifc_nand_ctrl->read_bytes = 0; - ifc_nand_ctrl->index = 0; - ifc_nand_ctrl->addr = NULL; - fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; - - nand_hw_control_init(&ifc_nand_ctrl->controller); - } else { - ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; - } - mutex_unlock(&fsl_ifc_nand_mutex); - - ifc_nand_ctrl->chips[bank] = priv; - priv->bank = bank; - priv->ctrl = fsl_ifc_ctrl_dev; - priv->dev = &dev->dev; - - priv->vbase = ioremap(res.start, resource_size(&res)); - if (!priv->vbase) { - dev_err(priv->dev, "%s: failed to map chip region\n", __func__); - ret = -ENOMEM; - goto err; - } - - dev_set_drvdata(priv->dev, priv); - - ifc_out32(IFC_NAND_EVTER_EN_OPC_EN | - IFC_NAND_EVTER_EN_FTOER_EN | - IFC_NAND_EVTER_EN_WPER_EN, - &ifc->ifc_nand.nand_evter_en); - - /* enable NAND Machine Interrupts */ - ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN | - IFC_NAND_EVTER_INTR_FTOERIR_EN | - IFC_NAND_EVTER_INTR_WPERIR_EN, - &ifc->ifc_nand.nand_evter_intr_en); - - mtd = nand_to_mtd(&priv->chip); - mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); - if (!mtd->name) { - ret = -ENOMEM; - goto err; - } - - ret = fsl_ifc_chip_init(priv); - if (ret) - goto err; - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - goto err; - - ret = fsl_ifc_chip_init_tail(mtd); - if (ret) - goto err; - - ret = nand_scan_tail(mtd); - if (ret) - goto err; - - /* First look for RedBoot table or partitions on the command - * line, these take precedence over device tree information */ - mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); - - dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", - (unsigned long long)res.start, priv->bank); - return 0; - -err: - fsl_ifc_chip_remove(priv); - return ret; -} - -static int fsl_ifc_nand_remove(struct platform_device *dev) -{ - struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); - - fsl_ifc_chip_remove(priv); - - mutex_lock(&fsl_ifc_nand_mutex); - ifc_nand_ctrl->counter--; - if (!ifc_nand_ctrl->counter) { - fsl_ifc_ctrl_dev->nand = NULL; - kfree(ifc_nand_ctrl); - } - mutex_unlock(&fsl_ifc_nand_mutex); - - return 0; -} - -static const struct of_device_id fsl_ifc_nand_match[] = { - { - .compatible = "fsl,ifc-nand", - }, - {} -}; -MODULE_DEVICE_TABLE(of, fsl_ifc_nand_match); - -static struct platform_driver fsl_ifc_nand_driver = { - .driver = { - .name = "fsl,ifc-nand", - .of_match_table = fsl_ifc_nand_match, - }, - .probe = fsl_ifc_nand_probe, - .remove = fsl_ifc_nand_remove, -}; - -module_platform_driver(fsl_ifc_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Freescale"); -MODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver"); diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c deleted file mode 100644 index a88e2cf66e0f..000000000000 --- a/drivers/mtd/nand/fsl_upm.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Freescale UPM NAND driver. - * - * Copyright © 2007-2008 MontaVista Software, Inc. - * - * Author: Anton Vorontsov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define FSL_UPM_WAIT_RUN_PATTERN 0x1 -#define FSL_UPM_WAIT_WRITE_BYTE 0x2 -#define FSL_UPM_WAIT_WRITE_BUFFER 0x4 - -struct fsl_upm_nand { - struct device *dev; - struct nand_chip chip; - int last_ctrl; - struct mtd_partition *parts; - struct fsl_upm upm; - uint8_t upm_addr_offset; - uint8_t upm_cmd_offset; - void __iomem *io_base; - int rnb_gpio[NAND_MAX_CHIPS]; - uint32_t mchip_offsets[NAND_MAX_CHIPS]; - uint32_t mchip_count; - uint32_t mchip_number; - int chip_delay; - uint32_t wait_flags; -}; - -static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo) -{ - return container_of(mtd_to_nand(mtdinfo), struct fsl_upm_nand, - chip); -} - -static int fun_chip_ready(struct mtd_info *mtd) -{ - struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); - - if (gpio_get_value(fun->rnb_gpio[fun->mchip_number])) - return 1; - - dev_vdbg(fun->dev, "busy\n"); - return 0; -} - -static void fun_wait_rnb(struct fsl_upm_nand *fun) -{ - if (fun->rnb_gpio[fun->mchip_number] >= 0) { - struct mtd_info *mtd = nand_to_mtd(&fun->chip); - int cnt = 1000000; - - while (--cnt && !fun_chip_ready(mtd)) - cpu_relax(); - if (!cnt) - dev_err(fun->dev, "tired waiting for RNB\n"); - } else { - ndelay(100); - } -} - -static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); - u32 mar; - - if (!(ctrl & fun->last_ctrl)) { - fsl_upm_end_pattern(&fun->upm); - - if (cmd == NAND_CMD_NONE) - return; - - fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); - } - - if (ctrl & NAND_CTRL_CHANGE) { - if (ctrl & NAND_ALE) - fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); - else if (ctrl & NAND_CLE) - fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); - } - - mar = (cmd << (32 - fun->upm.width)) | - fun->mchip_offsets[fun->mchip_number]; - fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar); - - if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) - fun_wait_rnb(fun); -} - -static void fun_select_chip(struct mtd_info *mtd, int mchip_nr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); - - if (mchip_nr == -1) { - chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); - } else if (mchip_nr >= 0 && mchip_nr < NAND_MAX_CHIPS) { - fun->mchip_number = mchip_nr; - chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr]; - chip->IO_ADDR_W = chip->IO_ADDR_R; - } else { - BUG(); - } -} - -static uint8_t fun_read_byte(struct mtd_info *mtd) -{ - struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); - - return in_8(fun->chip.IO_ADDR_R); -} - -static void fun_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); - int i; - - for (i = 0; i < len; i++) - buf[i] = in_8(fun->chip.IO_ADDR_R); -} - -static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); - int i; - - for (i = 0; i < len; i++) { - out_8(fun->chip.IO_ADDR_W, buf[i]); - if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) - fun_wait_rnb(fun); - } - if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) - fun_wait_rnb(fun); -} - -static int fun_chip_init(struct fsl_upm_nand *fun, - const struct device_node *upm_np, - const struct resource *io_res) -{ - struct mtd_info *mtd = nand_to_mtd(&fun->chip); - int ret; - struct device_node *flash_np; - - fun->chip.IO_ADDR_R = fun->io_base; - fun->chip.IO_ADDR_W = fun->io_base; - fun->chip.cmd_ctrl = fun_cmd_ctrl; - fun->chip.chip_delay = fun->chip_delay; - fun->chip.read_byte = fun_read_byte; - fun->chip.read_buf = fun_read_buf; - fun->chip.write_buf = fun_write_buf; - fun->chip.ecc.mode = NAND_ECC_SOFT; - fun->chip.ecc.algo = NAND_ECC_HAMMING; - if (fun->mchip_count > 1) - fun->chip.select_chip = fun_select_chip; - - if (fun->rnb_gpio[0] >= 0) - fun->chip.dev_ready = fun_chip_ready; - - mtd->dev.parent = fun->dev; - - flash_np = of_get_next_child(upm_np, NULL); - if (!flash_np) - return -ENODEV; - - nand_set_flash_node(&fun->chip, flash_np); - mtd->name = kasprintf(GFP_KERNEL, "0x%llx.%s", (u64)io_res->start, - flash_np->name); - if (!mtd->name) { - ret = -ENOMEM; - goto err; - } - - ret = nand_scan(mtd, fun->mchip_count); - if (ret) - goto err; - - ret = mtd_device_register(mtd, NULL, 0); -err: - of_node_put(flash_np); - if (ret) - kfree(mtd->name); - return ret; -} - -static int fun_probe(struct platform_device *ofdev) -{ - struct fsl_upm_nand *fun; - struct resource io_res; - const __be32 *prop; - int rnb_gpio; - int ret; - int size; - int i; - - fun = kzalloc(sizeof(*fun), GFP_KERNEL); - if (!fun) - return -ENOMEM; - - ret = of_address_to_resource(ofdev->dev.of_node, 0, &io_res); - if (ret) { - dev_err(&ofdev->dev, "can't get IO base\n"); - goto err1; - } - - ret = fsl_upm_find(io_res.start, &fun->upm); - if (ret) { - dev_err(&ofdev->dev, "can't find UPM\n"); - goto err1; - } - - prop = of_get_property(ofdev->dev.of_node, "fsl,upm-addr-offset", - &size); - if (!prop || size != sizeof(uint32_t)) { - dev_err(&ofdev->dev, "can't get UPM address offset\n"); - ret = -EINVAL; - goto err1; - } - fun->upm_addr_offset = *prop; - - prop = of_get_property(ofdev->dev.of_node, "fsl,upm-cmd-offset", &size); - if (!prop || size != sizeof(uint32_t)) { - dev_err(&ofdev->dev, "can't get UPM command offset\n"); - ret = -EINVAL; - goto err1; - } - fun->upm_cmd_offset = *prop; - - prop = of_get_property(ofdev->dev.of_node, - "fsl,upm-addr-line-cs-offsets", &size); - if (prop && (size / sizeof(uint32_t)) > 0) { - fun->mchip_count = size / sizeof(uint32_t); - if (fun->mchip_count >= NAND_MAX_CHIPS) { - dev_err(&ofdev->dev, "too much multiple chips\n"); - goto err1; - } - for (i = 0; i < fun->mchip_count; i++) - fun->mchip_offsets[i] = be32_to_cpu(prop[i]); - } else { - fun->mchip_count = 1; - } - - for (i = 0; i < fun->mchip_count; i++) { - fun->rnb_gpio[i] = -1; - rnb_gpio = of_get_gpio(ofdev->dev.of_node, i); - if (rnb_gpio >= 0) { - ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev)); - if (ret) { - dev_err(&ofdev->dev, - "can't request RNB gpio #%d\n", i); - goto err2; - } - gpio_direction_input(rnb_gpio); - fun->rnb_gpio[i] = rnb_gpio; - } else if (rnb_gpio == -EINVAL) { - dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i); - goto err2; - } - } - - prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL); - if (prop) - fun->chip_delay = be32_to_cpup(prop); - else - fun->chip_delay = 50; - - prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size); - if (prop && size == sizeof(uint32_t)) - fun->wait_flags = be32_to_cpup(prop); - else - fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN | - FSL_UPM_WAIT_WRITE_BYTE; - - fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, - resource_size(&io_res)); - if (!fun->io_base) { - ret = -ENOMEM; - goto err2; - } - - fun->dev = &ofdev->dev; - fun->last_ctrl = NAND_CLE; - - ret = fun_chip_init(fun, ofdev->dev.of_node, &io_res); - if (ret) - goto err2; - - dev_set_drvdata(&ofdev->dev, fun); - - return 0; -err2: - for (i = 0; i < fun->mchip_count; i++) { - if (fun->rnb_gpio[i] < 0) - break; - gpio_free(fun->rnb_gpio[i]); - } -err1: - kfree(fun); - - return ret; -} - -static int fun_remove(struct platform_device *ofdev) -{ - struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); - struct mtd_info *mtd = nand_to_mtd(&fun->chip); - int i; - - nand_release(mtd); - kfree(mtd->name); - - for (i = 0; i < fun->mchip_count; i++) { - if (fun->rnb_gpio[i] < 0) - break; - gpio_free(fun->rnb_gpio[i]); - } - - kfree(fun); - - return 0; -} - -static const struct of_device_id of_fun_match[] = { - { .compatible = "fsl,upm-nand" }, - {}, -}; -MODULE_DEVICE_TABLE(of, of_fun_match); - -static struct platform_driver of_fun_driver = { - .driver = { - .name = "fsl,upm-nand", - .of_match_table = of_fun_match, - }, - .probe = fun_probe, - .remove = fun_remove, -}; - -module_platform_driver(of_fun_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Anton Vorontsov "); -MODULE_DESCRIPTION("Driver for NAND chips working through Freescale " - "LocalBus User-Programmable Machine"); diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c deleted file mode 100644 index 7e66268f8154..000000000000 --- a/drivers/mtd/nand/fsmc_nand.c +++ /dev/null @@ -1,1175 +0,0 @@ -/* - * ST Microelectronics - * Flexible Static Memory Controller (FSMC) - * Driver for NAND portions - * - * Copyright © 2010 ST Microelectronics - * Vipin Kumar - * Ashish Priyadarshi - * - * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8) - * Copyright © 2007 STMicroelectronics Pvt. Ltd. - * Copyright © 2009 Alessandro Rubini - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* fsmc controller registers for NOR flash */ -#define CTRL 0x0 - /* ctrl register definitions */ - #define BANK_ENABLE (1 << 0) - #define MUXED (1 << 1) - #define NOR_DEV (2 << 2) - #define WIDTH_8 (0 << 4) - #define WIDTH_16 (1 << 4) - #define RSTPWRDWN (1 << 6) - #define WPROT (1 << 7) - #define WRT_ENABLE (1 << 12) - #define WAIT_ENB (1 << 13) - -#define CTRL_TIM 0x4 - /* ctrl_tim register definitions */ - -#define FSMC_NOR_BANK_SZ 0x8 -#define FSMC_NOR_REG_SIZE 0x40 - -#define FSMC_NOR_REG(base, bank, reg) (base + \ - FSMC_NOR_BANK_SZ * (bank) + \ - reg) - -/* fsmc controller registers for NAND flash */ -#define PC 0x00 - /* pc register definitions */ - #define FSMC_RESET (1 << 0) - #define FSMC_WAITON (1 << 1) - #define FSMC_ENABLE (1 << 2) - #define FSMC_DEVTYPE_NAND (1 << 3) - #define FSMC_DEVWID_8 (0 << 4) - #define FSMC_DEVWID_16 (1 << 4) - #define FSMC_ECCEN (1 << 6) - #define FSMC_ECCPLEN_512 (0 << 7) - #define FSMC_ECCPLEN_256 (1 << 7) - #define FSMC_TCLR_1 (1) - #define FSMC_TCLR_SHIFT (9) - #define FSMC_TCLR_MASK (0xF) - #define FSMC_TAR_1 (1) - #define FSMC_TAR_SHIFT (13) - #define FSMC_TAR_MASK (0xF) -#define STS 0x04 - /* sts register definitions */ - #define FSMC_CODE_RDY (1 << 15) -#define COMM 0x08 - /* comm register definitions */ - #define FSMC_TSET_0 0 - #define FSMC_TSET_SHIFT 0 - #define FSMC_TSET_MASK 0xFF - #define FSMC_TWAIT_6 6 - #define FSMC_TWAIT_SHIFT 8 - #define FSMC_TWAIT_MASK 0xFF - #define FSMC_THOLD_4 4 - #define FSMC_THOLD_SHIFT 16 - #define FSMC_THOLD_MASK 0xFF - #define FSMC_THIZ_1 1 - #define FSMC_THIZ_SHIFT 24 - #define FSMC_THIZ_MASK 0xFF -#define ATTRIB 0x0C -#define IOATA 0x10 -#define ECC1 0x14 -#define ECC2 0x18 -#define ECC3 0x1C -#define FSMC_NAND_BANK_SZ 0x20 - -#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \ - (FSMC_NAND_BANK_SZ * (bank)) + \ - reg) - -#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ) - -struct fsmc_nand_timings { - uint8_t tclr; - uint8_t tar; - uint8_t thiz; - uint8_t thold; - uint8_t twait; - uint8_t tset; -}; - -enum access_mode { - USE_DMA_ACCESS = 1, - USE_WORD_ACCESS, -}; - -/** - * struct fsmc_nand_data - structure for FSMC NAND device state - * - * @pid: Part ID on the AMBA PrimeCell format - * @mtd: MTD info for a NAND flash. - * @nand: Chip related info for a NAND flash. - * @partitions: Partition info for a NAND Flash. - * @nr_partitions: Total number of partition of a NAND flash. - * - * @bank: Bank number for probed device. - * @clk: Clock structure for FSMC. - * - * @read_dma_chan: DMA channel for read access - * @write_dma_chan: DMA channel for write access to NAND - * @dma_access_complete: Completion structure - * - * @data_pa: NAND Physical port for Data. - * @data_va: NAND port for Data. - * @cmd_va: NAND port for Command. - * @addr_va: NAND port for Address. - * @regs_va: FSMC regs base address. - */ -struct fsmc_nand_data { - u32 pid; - struct nand_chip nand; - - unsigned int bank; - struct device *dev; - enum access_mode mode; - struct clk *clk; - - /* DMA related objects */ - struct dma_chan *read_dma_chan; - struct dma_chan *write_dma_chan; - struct completion dma_access_complete; - - struct fsmc_nand_timings *dev_timings; - - dma_addr_t data_pa; - void __iomem *data_va; - void __iomem *cmd_va; - void __iomem *addr_va; - void __iomem *regs_va; -}; - -static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section >= chip->ecc.steps) - return -ERANGE; - - oobregion->offset = (section * 16) + 2; - oobregion->length = 3; - - return 0; -} - -static int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section >= chip->ecc.steps) - return -ERANGE; - - oobregion->offset = (section * 16) + 8; - - if (section < chip->ecc.steps - 1) - oobregion->length = 8; - else - oobregion->length = mtd->oobsize - oobregion->offset; - - return 0; -} - -static const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = { - .ecc = fsmc_ecc1_ooblayout_ecc, - .free = fsmc_ecc1_ooblayout_free, -}; - -/* - * ECC placement definitions in oobfree type format. - * There are 13 bytes of ecc for every 512 byte block and it has to be read - * consecutively and immediately after the 512 byte data block for hardware to - * generate the error bit offsets in 512 byte data. - */ -static int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section >= chip->ecc.steps) - return -ERANGE; - - oobregion->length = chip->ecc.bytes; - - if (!section && mtd->writesize <= 512) - oobregion->offset = 0; - else - oobregion->offset = (section * 16) + 2; - - return 0; -} - -static int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section >= chip->ecc.steps) - return -ERANGE; - - oobregion->offset = (section * 16) + 15; - - if (section < chip->ecc.steps - 1) - oobregion->length = 3; - else - oobregion->length = mtd->oobsize - oobregion->offset; - - return 0; -} - -static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = { - .ecc = fsmc_ecc4_ooblayout_ecc, - .free = fsmc_ecc4_ooblayout_free, -}; - -static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand); -} - -/* - * fsmc_cmd_ctrl - For facilitaing Hardware access - * This routine allows hardware specific access to control-lines(ALE,CLE) - */ -static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - unsigned int bank = host->bank; - - if (ctrl & NAND_CTRL_CHANGE) { - u32 pc; - - if (ctrl & NAND_CLE) { - this->IO_ADDR_R = host->cmd_va; - this->IO_ADDR_W = host->cmd_va; - } else if (ctrl & NAND_ALE) { - this->IO_ADDR_R = host->addr_va; - this->IO_ADDR_W = host->addr_va; - } else { - this->IO_ADDR_R = host->data_va; - this->IO_ADDR_W = host->data_va; - } - - pc = readl(FSMC_NAND_REG(regs, bank, PC)); - if (ctrl & NAND_NCE) - pc |= FSMC_ENABLE; - else - pc &= ~FSMC_ENABLE; - writel_relaxed(pc, FSMC_NAND_REG(regs, bank, PC)); - } - - mb(); - - if (cmd != NAND_CMD_NONE) - writeb_relaxed(cmd, this->IO_ADDR_W); -} - -/* - * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine - * - * This routine initializes timing parameters related to NAND memory access in - * FSMC registers - */ -static void fsmc_nand_setup(struct fsmc_nand_data *host, - struct fsmc_nand_timings *tims) -{ - uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; - uint32_t tclr, tar, thiz, thold, twait, tset; - unsigned int bank = host->bank; - void __iomem *regs = host->regs_va; - - tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; - tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; - thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT; - thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT; - twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT; - tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; - - if (host->nand.options & NAND_BUSWIDTH_16) - writel_relaxed(value | FSMC_DEVWID_16, - FSMC_NAND_REG(regs, bank, PC)); - else - writel_relaxed(value | FSMC_DEVWID_8, - FSMC_NAND_REG(regs, bank, PC)); - - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar, - FSMC_NAND_REG(regs, bank, PC)); - writel_relaxed(thiz | thold | twait | tset, - FSMC_NAND_REG(regs, bank, COMM)); - writel_relaxed(thiz | thold | twait | tset, - FSMC_NAND_REG(regs, bank, ATTRIB)); -} - -static int fsmc_calc_timings(struct fsmc_nand_data *host, - const struct nand_sdr_timings *sdrt, - struct fsmc_nand_timings *tims) -{ - unsigned long hclk = clk_get_rate(host->clk); - unsigned long hclkn = NSEC_PER_SEC / hclk; - uint32_t thiz, thold, twait, tset; - - if (sdrt->tRC_min < 30000) - return -EOPNOTSUPP; - - tims->tar = DIV_ROUND_UP(sdrt->tAR_min / 1000, hclkn) - 1; - if (tims->tar > FSMC_TAR_MASK) - tims->tar = FSMC_TAR_MASK; - tims->tclr = DIV_ROUND_UP(sdrt->tCLR_min / 1000, hclkn) - 1; - if (tims->tclr > FSMC_TCLR_MASK) - tims->tclr = FSMC_TCLR_MASK; - - thiz = sdrt->tCS_min - sdrt->tWP_min; - tims->thiz = DIV_ROUND_UP(thiz / 1000, hclkn); - - thold = sdrt->tDH_min; - if (thold < sdrt->tCH_min) - thold = sdrt->tCH_min; - if (thold < sdrt->tCLH_min) - thold = sdrt->tCLH_min; - if (thold < sdrt->tWH_min) - thold = sdrt->tWH_min; - if (thold < sdrt->tALH_min) - thold = sdrt->tALH_min; - if (thold < sdrt->tREH_min) - thold = sdrt->tREH_min; - tims->thold = DIV_ROUND_UP(thold / 1000, hclkn); - if (tims->thold == 0) - tims->thold = 1; - else if (tims->thold > FSMC_THOLD_MASK) - tims->thold = FSMC_THOLD_MASK; - - twait = max(sdrt->tRP_min, sdrt->tWP_min); - tims->twait = DIV_ROUND_UP(twait / 1000, hclkn) - 1; - if (tims->twait == 0) - tims->twait = 1; - else if (tims->twait > FSMC_TWAIT_MASK) - tims->twait = FSMC_TWAIT_MASK; - - tset = max(sdrt->tCS_min - sdrt->tWP_min, - sdrt->tCEA_max - sdrt->tREA_max); - tims->tset = DIV_ROUND_UP(tset / 1000, hclkn) - 1; - if (tims->tset == 0) - tims->tset = 1; - else if (tims->tset > FSMC_TSET_MASK) - tims->tset = FSMC_TSET_MASK; - - return 0; -} - -static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline, - const struct nand_data_interface *conf) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct fsmc_nand_data *host = nand_get_controller_data(nand); - struct fsmc_nand_timings tims; - const struct nand_sdr_timings *sdrt; - int ret; - - sdrt = nand_get_sdr_timings(conf); - if (IS_ERR(sdrt)) - return PTR_ERR(sdrt); - - ret = fsmc_calc_timings(host, sdrt, &tims); - if (ret) - return ret; - - if (csline == NAND_DATA_IFACE_CHECK_ONLY) - return 0; - - fsmc_nand_setup(host, &tims); - - return 0; -} - -/* - * fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers - */ -static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - uint32_t bank = host->bank; - - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256, - FSMC_NAND_REG(regs, bank, PC)); - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN, - FSMC_NAND_REG(regs, bank, PC)); - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN, - FSMC_NAND_REG(regs, bank, PC)); -} - -/* - * fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by - * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to - * max of 8-bits) - */ -static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, - uint8_t *ecc) -{ - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - uint32_t bank = host->bank; - uint32_t ecc_tmp; - unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; - - do { - if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) - break; - else - cond_resched(); - } while (!time_after_eq(jiffies, deadline)); - - if (time_after_eq(jiffies, deadline)) { - dev_err(host->dev, "calculate ecc timed out\n"); - return -ETIMEDOUT; - } - - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); - ecc[0] = (uint8_t) (ecc_tmp >> 0); - ecc[1] = (uint8_t) (ecc_tmp >> 8); - ecc[2] = (uint8_t) (ecc_tmp >> 16); - ecc[3] = (uint8_t) (ecc_tmp >> 24); - - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); - ecc[4] = (uint8_t) (ecc_tmp >> 0); - ecc[5] = (uint8_t) (ecc_tmp >> 8); - ecc[6] = (uint8_t) (ecc_tmp >> 16); - ecc[7] = (uint8_t) (ecc_tmp >> 24); - - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); - ecc[8] = (uint8_t) (ecc_tmp >> 0); - ecc[9] = (uint8_t) (ecc_tmp >> 8); - ecc[10] = (uint8_t) (ecc_tmp >> 16); - ecc[11] = (uint8_t) (ecc_tmp >> 24); - - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); - ecc[12] = (uint8_t) (ecc_tmp >> 16); - - return 0; -} - -/* - * fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by - * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to - * max of 1-bit) - */ -static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, - uint8_t *ecc) -{ - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - uint32_t bank = host->bank; - uint32_t ecc_tmp; - - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); - ecc[0] = (uint8_t) (ecc_tmp >> 0); - ecc[1] = (uint8_t) (ecc_tmp >> 8); - ecc[2] = (uint8_t) (ecc_tmp >> 16); - - return 0; -} - -/* Count the number of 0's in buff upto a max of max_bits */ -static int count_written_bits(uint8_t *buff, int size, int max_bits) -{ - int k, written_bits = 0; - - for (k = 0; k < size; k++) { - written_bits += hweight8(~buff[k]); - if (written_bits > max_bits) - break; - } - - return written_bits; -} - -static void dma_complete(void *param) -{ - struct fsmc_nand_data *host = param; - - complete(&host->dma_access_complete); -} - -static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, - enum dma_data_direction direction) -{ - struct dma_chan *chan; - struct dma_device *dma_dev; - struct dma_async_tx_descriptor *tx; - dma_addr_t dma_dst, dma_src, dma_addr; - dma_cookie_t cookie; - unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; - int ret; - unsigned long time_left; - - if (direction == DMA_TO_DEVICE) - chan = host->write_dma_chan; - else if (direction == DMA_FROM_DEVICE) - chan = host->read_dma_chan; - else - return -EINVAL; - - dma_dev = chan->device; - dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); - - if (direction == DMA_TO_DEVICE) { - dma_src = dma_addr; - dma_dst = host->data_pa; - } else { - dma_src = host->data_pa; - dma_dst = dma_addr; - } - - tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, - len, flags); - if (!tx) { - dev_err(host->dev, "device_prep_dma_memcpy error\n"); - ret = -EIO; - goto unmap_dma; - } - - tx->callback = dma_complete; - tx->callback_param = host; - cookie = tx->tx_submit(tx); - - ret = dma_submit_error(cookie); - if (ret) { - dev_err(host->dev, "dma_submit_error %d\n", cookie); - goto unmap_dma; - } - - dma_async_issue_pending(chan); - - time_left = - wait_for_completion_timeout(&host->dma_access_complete, - msecs_to_jiffies(3000)); - if (time_left == 0) { - dmaengine_terminate_all(chan); - dev_err(host->dev, "wait_for_completion_timeout\n"); - ret = -ETIMEDOUT; - goto unmap_dma; - } - - ret = 0; - -unmap_dma: - dma_unmap_single(dma_dev->dev, dma_addr, len, direction); - - return ret; -} - -/* - * fsmc_write_buf - write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - int i; - struct nand_chip *chip = mtd_to_nand(mtd); - - if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && - IS_ALIGNED(len, sizeof(uint32_t))) { - uint32_t *p = (uint32_t *)buf; - len = len >> 2; - for (i = 0; i < len; i++) - writel_relaxed(p[i], chip->IO_ADDR_W); - } else { - for (i = 0; i < len; i++) - writeb_relaxed(buf[i], chip->IO_ADDR_W); - } -} - -/* - * fsmc_read_buf - read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - int i; - struct nand_chip *chip = mtd_to_nand(mtd); - - if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && - IS_ALIGNED(len, sizeof(uint32_t))) { - uint32_t *p = (uint32_t *)buf; - len = len >> 2; - for (i = 0; i < len; i++) - p[i] = readl_relaxed(chip->IO_ADDR_R); - } else { - for (i = 0; i < len; i++) - buf[i] = readb_relaxed(chip->IO_ADDR_R); - } -} - -/* - * fsmc_read_buf_dma - read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - - dma_xfer(host, buf, len, DMA_FROM_DEVICE); -} - -/* - * fsmc_write_buf_dma - write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - - dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); -} - -/* - * fsmc_read_page_hwecc - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller expects OOB data read to chip->oob_poi - * @page: page number to read - * - * This routine is needed for fsmc version 8 as reading from NAND chip has to be - * performed in a strict sequence as follows: - * data(512 byte) -> ecc(13 byte) - * After this read, fsmc hardware generates and reports error data bits(up to a - * max of 8 bits) - */ -static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int i, j, s, stat, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->ecc.calc_buf; - uint8_t *ecc_code = chip->ecc.code_buf; - int off, len, group = 0; - /* - * ecc_oob is intentionally taken as uint16_t. In 16bit devices, we - * end up reading 14 bytes (7 words) from oob. The local array is - * to maintain word alignment - */ - uint16_t ecc_oob[7]; - uint8_t *oob = (uint8_t *)&ecc_oob[0]; - unsigned int max_bitflips = 0; - - for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { - nand_read_page_op(chip, page, s * eccsize, NULL, 0); - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - - for (j = 0; j < eccbytes;) { - struct mtd_oob_region oobregion; - int ret; - - ret = mtd_ooblayout_ecc(mtd, group++, &oobregion); - if (ret) - return ret; - - off = oobregion.offset; - len = oobregion.length; - - /* - * length is intentionally kept a higher multiple of 2 - * to read at least 13 bytes even in case of 16 bit NAND - * devices - */ - if (chip->options & NAND_BUSWIDTH_16) - len = roundup(len, 2); - - nand_read_oob_op(chip, page, off, oob + j, len); - j += len; - } - - memcpy(&ecc_code[i], oob, chip->ecc.bytes); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - - return max_bitflips; -} - -/* - * fsmc_bch8_correct_data - * @mtd: mtd info structure - * @dat: buffer of read data - * @read_ecc: ecc read from device spare area - * @calc_ecc: ecc calculated from read data - * - * calc_ecc is a 104 bit information containing maximum of 8 error - * offset informations of 13 bits each in 512 bytes of read data. - */ -static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - unsigned int bank = host->bank; - uint32_t err_idx[8]; - uint32_t num_err, i; - uint32_t ecc1, ecc2, ecc3, ecc4; - - num_err = (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF; - - /* no bit flipping */ - if (likely(num_err == 0)) - return 0; - - /* too many errors */ - if (unlikely(num_err > 8)) { - /* - * This is a temporary erase check. A newly erased page read - * would result in an ecc error because the oob data is also - * erased to FF and the calculated ecc for an FF data is not - * FF..FF. - * This is a workaround to skip performing correction in case - * data is FF..FF - * - * Logic: - * For every page, each bit written as 0 is counted until these - * number of bits are greater than 8 (the maximum correction - * capability of FSMC for each 512 + 13 bytes) - */ - - int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8); - int bits_data = count_written_bits(dat, chip->ecc.size, 8); - - if ((bits_ecc + bits_data) <= 8) { - if (bits_data) - memset(dat, 0xff, chip->ecc.size); - return bits_data; - } - - return -EBADMSG; - } - - /* - * ------------------- calc_ecc[] bit wise -----------|--13 bits--| - * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--| - * - * calc_ecc is a 104 bit information containing maximum of 8 error - * offset informations of 13 bits each. calc_ecc is copied into a - * uint64_t array and error offset indexes are populated in err_idx - * array - */ - ecc1 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); - ecc2 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); - ecc3 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); - ecc4 = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); - - err_idx[0] = (ecc1 >> 0) & 0x1FFF; - err_idx[1] = (ecc1 >> 13) & 0x1FFF; - err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); - err_idx[3] = (ecc2 >> 7) & 0x1FFF; - err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); - err_idx[5] = (ecc3 >> 1) & 0x1FFF; - err_idx[6] = (ecc3 >> 14) & 0x1FFF; - err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); - - i = 0; - while (num_err--) { - change_bit(0, (unsigned long *)&err_idx[i]); - change_bit(1, (unsigned long *)&err_idx[i]); - - if (err_idx[i] < chip->ecc.size * 8) { - change_bit(err_idx[i], (unsigned long *)dat); - i++; - } - } - return i; -} - -static bool filter(struct dma_chan *chan, void *slave) -{ - chan->private = slave; - return true; -} - -static int fsmc_nand_probe_config_dt(struct platform_device *pdev, - struct fsmc_nand_data *host, - struct nand_chip *nand) -{ - struct device_node *np = pdev->dev.of_node; - u32 val; - int ret; - - nand->options = 0; - - if (!of_property_read_u32(np, "bank-width", &val)) { - if (val == 2) { - nand->options |= NAND_BUSWIDTH_16; - } else if (val != 1) { - dev_err(&pdev->dev, "invalid bank-width %u\n", val); - return -EINVAL; - } - } - - if (of_get_property(np, "nand-skip-bbtscan", NULL)) - nand->options |= NAND_SKIP_BBTSCAN; - - host->dev_timings = devm_kzalloc(&pdev->dev, - sizeof(*host->dev_timings), GFP_KERNEL); - if (!host->dev_timings) - return -ENOMEM; - ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings, - sizeof(*host->dev_timings)); - if (ret) - host->dev_timings = NULL; - - /* Set default NAND bank to 0 */ - host->bank = 0; - if (!of_property_read_u32(np, "bank", &val)) { - if (val > 3) { - dev_err(&pdev->dev, "invalid bank %u\n", val); - return -EINVAL; - } - host->bank = val; - } - return 0; -} - -/* - * fsmc_nand_probe - Probe function - * @pdev: platform device structure - */ -static int __init fsmc_nand_probe(struct platform_device *pdev) -{ - struct fsmc_nand_data *host; - struct mtd_info *mtd; - struct nand_chip *nand; - struct resource *res; - dma_cap_mask_t mask; - int ret = 0; - u32 pid; - int i; - - /* Allocate memory for the device structure (and zero it) */ - host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); - if (!host) - return -ENOMEM; - - nand = &host->nand; - - ret = fsmc_nand_probe_config_dt(pdev, host, nand); - if (ret) - return ret; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - host->data_va = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->data_va)) - return PTR_ERR(host->data_va); - - host->data_pa = (dma_addr_t)res->start; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); - host->addr_va = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->addr_va)) - return PTR_ERR(host->addr_va); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); - host->cmd_va = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->cmd_va)) - return PTR_ERR(host->cmd_va); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); - host->regs_va = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->regs_va)) - return PTR_ERR(host->regs_va); - - host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - dev_err(&pdev->dev, "failed to fetch block clock\n"); - return PTR_ERR(host->clk); - } - - ret = clk_prepare_enable(host->clk); - if (ret) - return ret; - - /* - * This device ID is actually a common AMBA ID as used on the - * AMBA PrimeCell bus. However it is not a PrimeCell. - */ - for (pid = 0, i = 0; i < 4; i++) - pid |= (readl(host->regs_va + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8); - host->pid = pid; - dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, " - "revision %02x, config %02x\n", - AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid), - AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid)); - - host->dev = &pdev->dev; - - if (host->mode == USE_DMA_ACCESS) - init_completion(&host->dma_access_complete); - - /* Link all private pointers */ - mtd = nand_to_mtd(&host->nand); - nand_set_controller_data(nand, host); - nand_set_flash_node(nand, pdev->dev.of_node); - - mtd->dev.parent = &pdev->dev; - nand->IO_ADDR_R = host->data_va; - nand->IO_ADDR_W = host->data_va; - nand->cmd_ctrl = fsmc_cmd_ctrl; - nand->chip_delay = 30; - - /* - * Setup default ECC mode. nand_dt_init() called from nand_scan_ident() - * can overwrite this value if the DT provides a different value. - */ - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.hwctl = fsmc_enable_hwecc; - nand->ecc.size = 512; - nand->badblockbits = 7; - - switch (host->mode) { - case USE_DMA_ACCESS: - dma_cap_zero(mask); - dma_cap_set(DMA_MEMCPY, mask); - host->read_dma_chan = dma_request_channel(mask, filter, NULL); - if (!host->read_dma_chan) { - dev_err(&pdev->dev, "Unable to get read dma channel\n"); - goto err_req_read_chnl; - } - host->write_dma_chan = dma_request_channel(mask, filter, NULL); - if (!host->write_dma_chan) { - dev_err(&pdev->dev, "Unable to get write dma channel\n"); - goto err_req_write_chnl; - } - nand->read_buf = fsmc_read_buf_dma; - nand->write_buf = fsmc_write_buf_dma; - break; - - default: - case USE_WORD_ACCESS: - nand->read_buf = fsmc_read_buf; - nand->write_buf = fsmc_write_buf; - break; - } - - if (host->dev_timings) - fsmc_nand_setup(host, host->dev_timings); - else - nand->setup_data_interface = fsmc_setup_data_interface; - - if (AMBA_REV_BITS(host->pid) >= 8) { - nand->ecc.read_page = fsmc_read_page_hwecc; - nand->ecc.calculate = fsmc_read_hwecc_ecc4; - nand->ecc.correct = fsmc_bch8_correct_data; - nand->ecc.bytes = 13; - nand->ecc.strength = 8; - } - - /* - * Scan to find existence of the device - */ - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) { - dev_err(&pdev->dev, "No NAND Device found!\n"); - goto err_scan_ident; - } - - if (AMBA_REV_BITS(host->pid) >= 8) { - switch (mtd->oobsize) { - case 16: - case 64: - case 128: - case 224: - case 256: - break; - default: - dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", - mtd->oobsize); - ret = -EINVAL; - goto err_probe; - } - - mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops); - } else { - switch (nand->ecc.mode) { - case NAND_ECC_HW: - dev_info(&pdev->dev, "Using 1-bit HW ECC scheme\n"); - nand->ecc.calculate = fsmc_read_hwecc_ecc1; - nand->ecc.correct = nand_correct_data; - nand->ecc.bytes = 3; - nand->ecc.strength = 1; - break; - - case NAND_ECC_SOFT: - if (nand->ecc.algo == NAND_ECC_BCH) { - dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n"); - break; - } - - case NAND_ECC_ON_DIE: - break; - - default: - dev_err(&pdev->dev, "Unsupported ECC mode!\n"); - goto err_probe; - } - - /* - * Don't set layout for BCH4 SW ECC. This will be - * generated later in nand_bch_init() later. - */ - if (nand->ecc.mode == NAND_ECC_HW) { - switch (mtd->oobsize) { - case 16: - case 64: - case 128: - mtd_set_ooblayout(mtd, - &fsmc_ecc1_ooblayout_ops); - break; - default: - dev_warn(&pdev->dev, - "No oob scheme defined for oobsize %d\n", - mtd->oobsize); - ret = -EINVAL; - goto err_probe; - } - } - } - - /* Second stage of scan to fill MTD data-structures */ - ret = nand_scan_tail(mtd); - if (ret) - goto err_probe; - - mtd->name = "nand"; - ret = mtd_device_register(mtd, NULL, 0); - if (ret) - goto err_probe; - - platform_set_drvdata(pdev, host); - dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); - return 0; - -err_probe: -err_scan_ident: - if (host->mode == USE_DMA_ACCESS) - dma_release_channel(host->write_dma_chan); -err_req_write_chnl: - if (host->mode == USE_DMA_ACCESS) - dma_release_channel(host->read_dma_chan); -err_req_read_chnl: - clk_disable_unprepare(host->clk); - return ret; -} - -/* - * Clean up routine - */ -static int fsmc_nand_remove(struct platform_device *pdev) -{ - struct fsmc_nand_data *host = platform_get_drvdata(pdev); - - if (host) { - nand_release(nand_to_mtd(&host->nand)); - - if (host->mode == USE_DMA_ACCESS) { - dma_release_channel(host->write_dma_chan); - dma_release_channel(host->read_dma_chan); - } - clk_disable_unprepare(host->clk); - } - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int fsmc_nand_suspend(struct device *dev) -{ - struct fsmc_nand_data *host = dev_get_drvdata(dev); - if (host) - clk_disable_unprepare(host->clk); - return 0; -} - -static int fsmc_nand_resume(struct device *dev) -{ - struct fsmc_nand_data *host = dev_get_drvdata(dev); - if (host) { - clk_prepare_enable(host->clk); - if (host->dev_timings) - fsmc_nand_setup(host, host->dev_timings); - } - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume); - -static const struct of_device_id fsmc_nand_id_table[] = { - { .compatible = "st,spear600-fsmc-nand" }, - { .compatible = "stericsson,fsmc-nand" }, - {} -}; -MODULE_DEVICE_TABLE(of, fsmc_nand_id_table); - -static struct platform_driver fsmc_nand_driver = { - .remove = fsmc_nand_remove, - .driver = { - .name = "fsmc-nand", - .of_match_table = fsmc_nand_id_table, - .pm = &fsmc_nand_pm_ops, - }, -}; - -module_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Vipin Kumar , Ashish Priyadarshi"); -MODULE_DESCRIPTION("NAND driver for SPEAr Platforms"); diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c deleted file mode 100644 index 2780af26d9ab..000000000000 --- a/drivers/mtd/nand/gpio.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Updated, and converted to generic GPIO based driver by Russell King. - * - * Written by Ben Dooks - * Based on 2.4 version by Mark Whittaker - * - * © 2004 Simtec Electronics - * - * Device driver for NAND flash that uses a memory mapped interface to - * read/write the NAND commands and data, and GPIO pins for control signals - * (the DT binding refers to this as "GPIO assisted NAND flash") - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct gpiomtd { - void __iomem *io_sync; - struct nand_chip nand_chip; - struct gpio_nand_platdata plat; - struct gpio_desc *nce; /* Optional chip enable */ - struct gpio_desc *cle; - struct gpio_desc *ale; - struct gpio_desc *rdy; - struct gpio_desc *nwp; /* Optional write protection */ -}; - -static inline struct gpiomtd *gpio_nand_getpriv(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct gpiomtd, nand_chip); -} - - -#ifdef CONFIG_ARM -/* gpio_nand_dosync() - * - * Make sure the GPIO state changes occur in-order with writes to NAND - * memory region. - * Needed on PXA due to bus-reordering within the SoC itself (see section on - * I/O ordering in PXA manual (section 2.3, p35) - */ -static void gpio_nand_dosync(struct gpiomtd *gpiomtd) -{ - unsigned long tmp; - - if (gpiomtd->io_sync) { - /* - * Linux memory barriers don't cater for what's required here. - * What's required is what's here - a read from a separate - * region with a dependency on that read. - */ - tmp = readl(gpiomtd->io_sync); - asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp)); - } -} -#else -static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {} -#endif - -static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); - - gpio_nand_dosync(gpiomtd); - - if (ctrl & NAND_CTRL_CHANGE) { - if (gpiomtd->nce) - gpiod_set_value(gpiomtd->nce, !(ctrl & NAND_NCE)); - gpiod_set_value(gpiomtd->cle, !!(ctrl & NAND_CLE)); - gpiod_set_value(gpiomtd->ale, !!(ctrl & NAND_ALE)); - gpio_nand_dosync(gpiomtd); - } - if (cmd == NAND_CMD_NONE) - return; - - writeb(cmd, gpiomtd->nand_chip.IO_ADDR_W); - gpio_nand_dosync(gpiomtd); -} - -static int gpio_nand_devready(struct mtd_info *mtd) -{ - struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); - - return gpiod_get_value(gpiomtd->rdy); -} - -#ifdef CONFIG_OF -static const struct of_device_id gpio_nand_id_table[] = { - { .compatible = "gpio-control-nand" }, - {} -}; -MODULE_DEVICE_TABLE(of, gpio_nand_id_table); - -static int gpio_nand_get_config_of(const struct device *dev, - struct gpio_nand_platdata *plat) -{ - u32 val; - - if (!dev->of_node) - return -ENODEV; - - if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { - if (val == 2) { - plat->options |= NAND_BUSWIDTH_16; - } else if (val != 1) { - dev_err(dev, "invalid bank-width %u\n", val); - return -EINVAL; - } - } - - if (!of_property_read_u32(dev->of_node, "chip-delay", &val)) - plat->chip_delay = val; - - return 0; -} - -static struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev) -{ - struct resource *r; - u64 addr; - - if (of_property_read_u64(pdev->dev.of_node, - "gpio-control-nand,io-sync-reg", &addr)) - return NULL; - - r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL); - if (!r) - return NULL; - - r->start = addr; - r->end = r->start + 0x3; - r->flags = IORESOURCE_MEM; - - return r; -} -#else /* CONFIG_OF */ -static inline int gpio_nand_get_config_of(const struct device *dev, - struct gpio_nand_platdata *plat) -{ - return -ENOSYS; -} - -static inline struct resource * -gpio_nand_get_io_sync_of(struct platform_device *pdev) -{ - return NULL; -} -#endif /* CONFIG_OF */ - -static inline int gpio_nand_get_config(const struct device *dev, - struct gpio_nand_platdata *plat) -{ - int ret = gpio_nand_get_config_of(dev, plat); - - if (!ret) - return ret; - - if (dev_get_platdata(dev)) { - memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); - return 0; - } - - return -EINVAL; -} - -static inline struct resource * -gpio_nand_get_io_sync(struct platform_device *pdev) -{ - struct resource *r = gpio_nand_get_io_sync_of(pdev); - - if (r) - return r; - - return platform_get_resource(pdev, IORESOURCE_MEM, 1); -} - -static int gpio_nand_remove(struct platform_device *pdev) -{ - struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); - - nand_release(nand_to_mtd(&gpiomtd->nand_chip)); - - /* Enable write protection and disable the chip */ - if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) - gpiod_set_value(gpiomtd->nwp, 0); - if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) - gpiod_set_value(gpiomtd->nce, 0); - - return 0; -} - -static int gpio_nand_probe(struct platform_device *pdev) -{ - struct gpiomtd *gpiomtd; - struct nand_chip *chip; - struct mtd_info *mtd; - struct resource *res; - struct device *dev = &pdev->dev; - int ret = 0; - - if (!dev->of_node && !dev_get_platdata(dev)) - return -EINVAL; - - gpiomtd = devm_kzalloc(dev, sizeof(*gpiomtd), GFP_KERNEL); - if (!gpiomtd) - return -ENOMEM; - - chip = &gpiomtd->nand_chip; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - chip->IO_ADDR_R = devm_ioremap_resource(dev, res); - if (IS_ERR(chip->IO_ADDR_R)) - return PTR_ERR(chip->IO_ADDR_R); - - res = gpio_nand_get_io_sync(pdev); - if (res) { - gpiomtd->io_sync = devm_ioremap_resource(dev, res); - if (IS_ERR(gpiomtd->io_sync)) - return PTR_ERR(gpiomtd->io_sync); - } - - ret = gpio_nand_get_config(dev, &gpiomtd->plat); - if (ret) - return ret; - - /* Just enable the chip */ - gpiomtd->nce = devm_gpiod_get_optional(dev, "nce", GPIOD_OUT_HIGH); - if (IS_ERR(gpiomtd->nce)) - return PTR_ERR(gpiomtd->nce); - - /* We disable write protection once we know probe() will succeed */ - gpiomtd->nwp = devm_gpiod_get_optional(dev, "nwp", GPIOD_OUT_LOW); - if (IS_ERR(gpiomtd->nwp)) { - ret = PTR_ERR(gpiomtd->nwp); - goto out_ce; - } - - gpiomtd->ale = devm_gpiod_get(dev, "ale", GPIOD_OUT_LOW); - if (IS_ERR(gpiomtd->ale)) { - ret = PTR_ERR(gpiomtd->ale); - goto out_ce; - } - - gpiomtd->cle = devm_gpiod_get(dev, "cle", GPIOD_OUT_LOW); - if (IS_ERR(gpiomtd->cle)) { - ret = PTR_ERR(gpiomtd->cle); - goto out_ce; - } - - gpiomtd->rdy = devm_gpiod_get_optional(dev, "rdy", GPIOD_IN); - if (IS_ERR(gpiomtd->rdy)) { - ret = PTR_ERR(gpiomtd->rdy); - goto out_ce; - } - /* Using RDY pin */ - if (gpiomtd->rdy) - chip->dev_ready = gpio_nand_devready; - - nand_set_flash_node(chip, pdev->dev.of_node); - chip->IO_ADDR_W = chip->IO_ADDR_R; - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - chip->options = gpiomtd->plat.options; - chip->chip_delay = gpiomtd->plat.chip_delay; - chip->cmd_ctrl = gpio_nand_cmd_ctrl; - - mtd = nand_to_mtd(chip); - mtd->dev.parent = dev; - - platform_set_drvdata(pdev, gpiomtd); - - /* Disable write protection, if wired up */ - if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) - gpiod_direction_output(gpiomtd->nwp, 1); - - ret = nand_scan(mtd, 1); - if (ret) - goto err_wp; - - if (gpiomtd->plat.adjust_parts) - gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size); - - ret = mtd_device_register(mtd, gpiomtd->plat.parts, - gpiomtd->plat.num_parts); - if (!ret) - return 0; - -err_wp: - if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) - gpiod_set_value(gpiomtd->nwp, 0); -out_ce: - if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) - gpiod_set_value(gpiomtd->nce, 0); - - return ret; -} - -static struct platform_driver gpio_nand_driver = { - .probe = gpio_nand_probe, - .remove = gpio_nand_remove, - .driver = { - .name = "gpio-nand", - .of_match_table = of_match_ptr(gpio_nand_id_table), - }, -}; - -module_platform_driver(gpio_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_DESCRIPTION("GPIO NAND Driver"); diff --git a/drivers/mtd/nand/gpmi-nand/Makefile b/drivers/mtd/nand/gpmi-nand/Makefile deleted file mode 100644 index 3a462487c35e..000000000000 --- a/drivers/mtd/nand/gpmi-nand/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o -gpmi_nand-objs += gpmi-nand.o -gpmi_nand-objs += gpmi-lib.o diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h deleted file mode 100644 index 05bb91f2f4c4..000000000000 --- a/drivers/mtd/nand/gpmi-nand/bch-regs.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Freescale GPMI NAND Flash Driver - * - * Copyright 2008-2011 Freescale Semiconductor, Inc. - * Copyright 2008 Embedded Alley Solutions, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#ifndef __GPMI_NAND_BCH_REGS_H -#define __GPMI_NAND_BCH_REGS_H - -#define HW_BCH_CTRL 0x00000000 -#define HW_BCH_CTRL_SET 0x00000004 -#define HW_BCH_CTRL_CLR 0x00000008 -#define HW_BCH_CTRL_TOG 0x0000000c - -#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1 << 8) -#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0) - -#define HW_BCH_STATUS0 0x00000010 -#define HW_BCH_MODE 0x00000020 -#define HW_BCH_ENCODEPTR 0x00000030 -#define HW_BCH_DATAPTR 0x00000040 -#define HW_BCH_METAPTR 0x00000050 -#define HW_BCH_LAYOUTSELECT 0x00000070 - -#define HW_BCH_FLASH0LAYOUT0 0x00000080 - -#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 -#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS) -#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ - (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) - -#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 -#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE) -#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ - (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\ - & BM_BCH_FLASH0LAYOUT0_META_SIZE) - -#define BP_BCH_FLASH0LAYOUT0_ECC0 12 -#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) -#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11 -#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) -#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \ - (GPMI_IS_MX6(x) \ - ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \ - & MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \ - : (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \ - & BM_BCH_FLASH0LAYOUT0_ECC0) \ - ) - -#define MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14 10 -#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \ - (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) -#define BF_BCH_FLASH0LAYOUT0_GF(v, x) \ - ((GPMI_IS_MX6(x) && ((v) == 14)) \ - ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \ - & MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \ - : 0 \ - ) - -#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 -#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ - (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) -#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ - (0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) -#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \ - (GPMI_IS_MX6(x) \ - ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ - : ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ - ) - -#define HW_BCH_FLASH0LAYOUT1 0x00000090 - -#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 -#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \ - (0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) -#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ - (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \ - & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) - -#define BP_BCH_FLASH0LAYOUT1_ECCN 12 -#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) -#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11 -#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) -#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \ - (GPMI_IS_MX6(x) \ - ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \ - & MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \ - : (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \ - & BM_BCH_FLASH0LAYOUT1_ECCN) \ - ) - -#define MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14 10 -#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \ - (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) -#define BF_BCH_FLASH0LAYOUT1_GF(v, x) \ - ((GPMI_IS_MX6(x) && ((v) == 14)) \ - ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \ - & MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \ - : 0 \ - ) - -#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 -#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ - (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) -#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ - (0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) -#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \ - (GPMI_IS_MX6(x) \ - ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ - : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ - ) - -#define HW_BCH_VERSION 0x00000160 -#endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c deleted file mode 100644 index 97787246af41..000000000000 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ /dev/null @@ -1,1510 +0,0 @@ -/* - * Freescale GPMI NAND Flash Driver - * - * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. - * Copyright (C) 2008 Embedded Alley Solutions, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#include -#include -#include - -#include "gpmi-nand.h" -#include "gpmi-regs.h" -#include "bch-regs.h" - -static struct timing_threshold timing_default_threshold = { - .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> - BP_GPMI_TIMING0_DATA_SETUP), - .internal_data_setup_in_ns = 0, - .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >> - BP_GPMI_CTRL1_RDN_DELAY), - .max_dll_clock_period_in_ns = 32, - .max_dll_delay_in_ns = 16, -}; - -#define MXS_SET_ADDR 0x4 -#define MXS_CLR_ADDR 0x8 -/* - * Clear the bit and poll it cleared. This is usually called with - * a reset address and mask being either SFTRST(bit 31) or CLKGATE - * (bit 30). - */ -static int clear_poll_bit(void __iomem *addr, u32 mask) -{ - int timeout = 0x400; - - /* clear the bit */ - writel(mask, addr + MXS_CLR_ADDR); - - /* - * SFTRST needs 3 GPMI clocks to settle, the reference manual - * recommends to wait 1us. - */ - udelay(1); - - /* poll the bit becoming clear */ - while ((readl(addr) & mask) && --timeout) - /* nothing */; - - return !timeout; -} - -#define MODULE_CLKGATE (1 << 30) -#define MODULE_SFTRST (1 << 31) -/* - * The current mxs_reset_block() will do two things: - * [1] enable the module. - * [2] reset the module. - * - * In most of the cases, it's ok. - * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). - * If you try to soft reset the BCH block, it becomes unusable until - * the next hard reset. This case occurs in the NAND boot mode. When the board - * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. - * So If the driver tries to reset the BCH again, the BCH will not work anymore. - * You will see a DMA timeout in this case. The bug has been fixed - * in the following chips, such as MX28. - * - * To avoid this bug, just add a new parameter `just_enable` for - * the mxs_reset_block(), and rewrite it here. - */ -static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) -{ - int ret; - int timeout = 0x400; - - /* clear and poll SFTRST */ - ret = clear_poll_bit(reset_addr, MODULE_SFTRST); - if (unlikely(ret)) - goto error; - - /* clear CLKGATE */ - writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); - - if (!just_enable) { - /* set SFTRST to reset the block */ - writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); - udelay(1); - - /* poll CLKGATE becoming set */ - while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout) - /* nothing */; - if (unlikely(!timeout)) - goto error; - } - - /* clear and poll SFTRST */ - ret = clear_poll_bit(reset_addr, MODULE_SFTRST); - if (unlikely(ret)) - goto error; - - /* clear and poll CLKGATE */ - ret = clear_poll_bit(reset_addr, MODULE_CLKGATE); - if (unlikely(ret)) - goto error; - - return 0; - -error: - pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); - return -ETIMEDOUT; -} - -static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) -{ - struct clk *clk; - int ret; - int i; - - for (i = 0; i < GPMI_CLK_MAX; i++) { - clk = this->resources.clock[i]; - if (!clk) - break; - - if (v) { - ret = clk_prepare_enable(clk); - if (ret) - goto err_clk; - } else { - clk_disable_unprepare(clk); - } - } - return 0; - -err_clk: - for (; i > 0; i--) - clk_disable_unprepare(this->resources.clock[i - 1]); - return ret; -} - -#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true) -#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false) - -int gpmi_init(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - int ret; - - ret = gpmi_enable_clk(this); - if (ret) - return ret; - ret = gpmi_reset_block(r->gpmi_regs, false); - if (ret) - goto err_out; - - /* - * Reset BCH here, too. We got failures otherwise :( - * See later BCH reset for explanation of MX23 handling - */ - ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); - if (ret) - goto err_out; - - - /* Choose NAND mode. */ - writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); - - /* Set the IRQ polarity. */ - writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, - r->gpmi_regs + HW_GPMI_CTRL1_SET); - - /* Disable Write-Protection. */ - writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); - - /* Select BCH ECC. */ - writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); - - /* - * Decouple the chip select from dma channel. We use dma0 for all - * the chips. - */ - writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET); - - gpmi_disable_clk(this); - return 0; -err_out: - gpmi_disable_clk(this); - return ret; -} - -/* This function is very useful. It is called only when the bug occur. */ -void gpmi_dump_info(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - struct bch_geometry *geo = &this->bch_geometry; - u32 reg; - int i; - - dev_err(this->dev, "Show GPMI registers :\n"); - for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) { - reg = readl(r->gpmi_regs + i * 0x10); - dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); - } - - /* start to print out the BCH info */ - dev_err(this->dev, "Show BCH registers :\n"); - for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) { - reg = readl(r->bch_regs + i * 0x10); - dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); - } - dev_err(this->dev, "BCH Geometry :\n" - "GF length : %u\n" - "ECC Strength : %u\n" - "Page Size in Bytes : %u\n" - "Metadata Size in Bytes : %u\n" - "ECC Chunk Size in Bytes: %u\n" - "ECC Chunk Count : %u\n" - "Payload Size in Bytes : %u\n" - "Auxiliary Size in Bytes: %u\n" - "Auxiliary Status Offset: %u\n" - "Block Mark Byte Offset : %u\n" - "Block Mark Bit Offset : %u\n", - geo->gf_len, - geo->ecc_strength, - geo->page_size, - geo->metadata_size, - geo->ecc_chunk_size, - geo->ecc_chunk_count, - geo->payload_size, - geo->auxiliary_size, - geo->auxiliary_status_offset, - geo->block_mark_byte_offset, - geo->block_mark_bit_offset); -} - -/* Configures the geometry for BCH. */ -int bch_set_geometry(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - struct bch_geometry *bch_geo = &this->bch_geometry; - unsigned int block_count; - unsigned int block_size; - unsigned int metadata_size; - unsigned int ecc_strength; - unsigned int page_size; - unsigned int gf_len; - int ret; - - if (common_nfc_set_geometry(this)) - return !0; - - block_count = bch_geo->ecc_chunk_count - 1; - block_size = bch_geo->ecc_chunk_size; - metadata_size = bch_geo->metadata_size; - ecc_strength = bch_geo->ecc_strength >> 1; - page_size = bch_geo->page_size; - gf_len = bch_geo->gf_len; - - ret = gpmi_enable_clk(this); - if (ret) - return ret; - - /* - * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this - * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. - * On the other hand, the MX28 needs the reset, because one case has been - * seen where the BCH produced ECC errors constantly after 10000 - * consecutive reboots. The latter case has not been seen on the MX23 - * yet, still we don't know if it could happen there as well. - */ - ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); - if (ret) - goto err_out; - - /* Configure layout 0. */ - writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) - | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) - | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) - | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) - | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), - r->bch_regs + HW_BCH_FLASH0LAYOUT0); - - writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) - | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) - | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) - | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), - r->bch_regs + HW_BCH_FLASH0LAYOUT1); - - /* Set *all* chip selects to use layout 0. */ - writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); - - /* Enable interrupts. */ - writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, - r->bch_regs + HW_BCH_CTRL_SET); - - gpmi_disable_clk(this); - return 0; -err_out: - gpmi_disable_clk(this); - return ret; -} - -/* Converts time in nanoseconds to cycles. */ -static unsigned int ns_to_cycles(unsigned int time, - unsigned int period, unsigned int min) -{ - unsigned int k; - - k = (time + period - 1) / period; - return max(k, min); -} - -#define DEF_MIN_PROP_DELAY 5 -#define DEF_MAX_PROP_DELAY 9 -/* Apply timing to current hardware conditions. */ -static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, - struct gpmi_nfc_hardware_timing *hw) -{ - struct timing_threshold *nfc = &timing_default_threshold; - struct resources *r = &this->resources; - struct nand_chip *nand = &this->nand; - struct nand_timing target = this->timing; - bool improved_timing_is_available; - unsigned long clock_frequency_in_hz; - unsigned int clock_period_in_ns; - bool dll_use_half_periods; - unsigned int dll_delay_shift; - unsigned int max_sample_delay_in_ns; - unsigned int address_setup_in_cycles; - unsigned int data_setup_in_ns; - unsigned int data_setup_in_cycles; - unsigned int data_hold_in_cycles; - int ideal_sample_delay_in_ns; - unsigned int sample_delay_factor; - int tEYE; - unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY; - unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY; - - /* - * If there are multiple chips, we need to relax the timings to allow - * for signal distortion due to higher capacitance. - */ - if (nand->numchips > 2) { - target.data_setup_in_ns += 10; - target.data_hold_in_ns += 10; - target.address_setup_in_ns += 10; - } else if (nand->numchips > 1) { - target.data_setup_in_ns += 5; - target.data_hold_in_ns += 5; - target.address_setup_in_ns += 5; - } - - /* Check if improved timing information is available. */ - improved_timing_is_available = - (target.tREA_in_ns >= 0) && - (target.tRLOH_in_ns >= 0) && - (target.tRHOH_in_ns >= 0); - - /* Inspect the clock. */ - nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]); - clock_frequency_in_hz = nfc->clock_frequency_in_hz; - clock_period_in_ns = NSEC_PER_SEC / clock_frequency_in_hz; - - /* - * The NFC quantizes setup and hold parameters in terms of clock cycles. - * Here, we quantize the setup and hold timing parameters to the - * next-highest clock period to make sure we apply at least the - * specified times. - * - * For data setup and data hold, the hardware interprets a value of zero - * as the largest possible delay. This is not what's intended by a zero - * in the input parameter, so we impose a minimum of one cycle. - */ - data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns, - clock_period_in_ns, 1); - data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns, - clock_period_in_ns, 1); - address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns, - clock_period_in_ns, 0); - - /* - * The clock's period affects the sample delay in a number of ways: - * - * (1) The NFC HAL tells us the maximum clock period the sample delay - * DLL can tolerate. If the clock period is greater than half that - * maximum, we must configure the DLL to be driven by half periods. - * - * (2) We need to convert from an ideal sample delay, in ns, to a - * "sample delay factor," which the NFC uses. This factor depends on - * whether we're driving the DLL with full or half periods. - * Paraphrasing the reference manual: - * - * AD = SDF x 0.125 x RP - * - * where: - * - * AD is the applied delay, in ns. - * SDF is the sample delay factor, which is dimensionless. - * RP is the reference period, in ns, which is a full clock period - * if the DLL is being driven by full periods, or half that if - * the DLL is being driven by half periods. - * - * Let's re-arrange this in a way that's more useful to us: - * - * 8 - * SDF = AD x ---- - * RP - * - * The reference period is either the clock period or half that, so this - * is: - * - * 8 AD x DDF - * SDF = AD x ----- = -------- - * f x P P - * - * where: - * - * f is 1 or 1/2, depending on how we're driving the DLL. - * P is the clock period. - * DDF is the DLL Delay Factor, a dimensionless value that - * incorporates all the constants in the conversion. - * - * DDF will be either 8 or 16, both of which are powers of two. We can - * reduce the cost of this conversion by using bit shifts instead of - * multiplication or division. Thus: - * - * AD << DDS - * SDF = --------- - * P - * - * or - * - * AD = (SDF >> DDS) x P - * - * where: - * - * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF. - */ - if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) { - dll_use_half_periods = true; - dll_delay_shift = 3 + 1; - } else { - dll_use_half_periods = false; - dll_delay_shift = 3; - } - - /* - * Compute the maximum sample delay the NFC allows, under current - * conditions. If the clock is running too slowly, no sample delay is - * possible. - */ - if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns) - max_sample_delay_in_ns = 0; - else { - /* - * Compute the delay implied by the largest sample delay factor - * the NFC allows. - */ - max_sample_delay_in_ns = - (nfc->max_sample_delay_factor * clock_period_in_ns) >> - dll_delay_shift; - - /* - * Check if the implied sample delay larger than the NFC - * actually allows. - */ - if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns) - max_sample_delay_in_ns = nfc->max_dll_delay_in_ns; - } - - /* - * Check if improved timing information is available. If not, we have to - * use a less-sophisticated algorithm. - */ - if (!improved_timing_is_available) { - /* - * Fold the read setup time required by the NFC into the ideal - * sample delay. - */ - ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns + - nfc->internal_data_setup_in_ns; - - /* - * The ideal sample delay may be greater than the maximum - * allowed by the NFC. If so, we can trade off sample delay time - * for more data setup time. - * - * In each iteration of the following loop, we add a cycle to - * the data setup time and subtract a corresponding amount from - * the sample delay until we've satisified the constraints or - * can't do any better. - */ - while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && - (data_setup_in_cycles < nfc->max_data_setup_cycles)) { - - data_setup_in_cycles++; - ideal_sample_delay_in_ns -= clock_period_in_ns; - - if (ideal_sample_delay_in_ns < 0) - ideal_sample_delay_in_ns = 0; - - } - - /* - * Compute the sample delay factor that corresponds most closely - * to the ideal sample delay. If the result is too large for the - * NFC, use the maximum value. - * - * Notice that we use the ns_to_cycles function to compute the - * sample delay factor. We do this because the form of the - * computation is the same as that for calculating cycles. - */ - sample_delay_factor = - ns_to_cycles( - ideal_sample_delay_in_ns << dll_delay_shift, - clock_period_in_ns, 0); - - if (sample_delay_factor > nfc->max_sample_delay_factor) - sample_delay_factor = nfc->max_sample_delay_factor; - - /* Skip to the part where we return our results. */ - goto return_results; - } - - /* - * If control arrives here, we have more detailed timing information, - * so we can use a better algorithm. - */ - - /* - * Fold the read setup time required by the NFC into the maximum - * propagation delay. - */ - max_prop_delay_in_ns += nfc->internal_data_setup_in_ns; - - /* - * Earlier, we computed the number of clock cycles required to satisfy - * the data setup time. Now, we need to know the actual nanoseconds. - */ - data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles; - - /* - * Compute tEYE, the width of the data eye when reading from the NAND - * Flash. The eye width is fundamentally determined by the data setup - * time, perturbed by propagation delays and some characteristics of the - * NAND Flash device. - * - * start of the eye = max_prop_delay + tREA - * end of the eye = min_prop_delay + tRHOH + data_setup - */ - tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns + - (int)data_setup_in_ns; - - tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns; - - /* - * The eye must be open. If it's not, we can try to open it by - * increasing its main forcer, the data setup time. - * - * In each iteration of the following loop, we increase the data setup - * time by a single clock cycle. We do this until either the eye is - * open or we run into NFC limits. - */ - while ((tEYE <= 0) && - (data_setup_in_cycles < nfc->max_data_setup_cycles)) { - /* Give a cycle to data setup. */ - data_setup_in_cycles++; - /* Synchronize the data setup time with the cycles. */ - data_setup_in_ns += clock_period_in_ns; - /* Adjust tEYE accordingly. */ - tEYE += clock_period_in_ns; - } - - /* - * When control arrives here, the eye is open. The ideal time to sample - * the data is in the center of the eye: - * - * end of the eye + start of the eye - * --------------------------------- - data_setup - * 2 - * - * After some algebra, this simplifies to the code immediately below. - */ - ideal_sample_delay_in_ns = - ((int)max_prop_delay_in_ns + - (int)target.tREA_in_ns + - (int)min_prop_delay_in_ns + - (int)target.tRHOH_in_ns - - (int)data_setup_in_ns) >> 1; - - /* - * The following figure illustrates some aspects of a NAND Flash read: - * - * - * __ _____________________________________ - * RDN \_________________/ - * - * <---- tEYE -----> - * /-----------------\ - * Read Data ----------------------------< >--------- - * \-----------------/ - * ^ ^ ^ ^ - * | | | | - * |<--Data Setup -->|<--Delay Time -->| | - * | | | | - * | | | - * | |<-- Quantized Delay Time -->| - * | | | - * - * - * We have some issues we must now address: - * - * (1) The *ideal* sample delay time must not be negative. If it is, we - * jam it to zero. - * - * (2) The *ideal* sample delay time must not be greater than that - * allowed by the NFC. If it is, we can increase the data setup - * time, which will reduce the delay between the end of the data - * setup and the center of the eye. It will also make the eye - * larger, which might help with the next issue... - * - * (3) The *quantized* sample delay time must not fall either before the - * eye opens or after it closes (the latter is the problem - * illustrated in the above figure). - */ - - /* Jam a negative ideal sample delay to zero. */ - if (ideal_sample_delay_in_ns < 0) - ideal_sample_delay_in_ns = 0; - - /* - * Extend the data setup as needed to reduce the ideal sample delay - * below the maximum permitted by the NFC. - */ - while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && - (data_setup_in_cycles < nfc->max_data_setup_cycles)) { - - /* Give a cycle to data setup. */ - data_setup_in_cycles++; - /* Synchronize the data setup time with the cycles. */ - data_setup_in_ns += clock_period_in_ns; - /* Adjust tEYE accordingly. */ - tEYE += clock_period_in_ns; - - /* - * Decrease the ideal sample delay by one half cycle, to keep it - * in the middle of the eye. - */ - ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); - - /* Jam a negative ideal sample delay to zero. */ - if (ideal_sample_delay_in_ns < 0) - ideal_sample_delay_in_ns = 0; - } - - /* - * Compute the sample delay factor that corresponds to the ideal sample - * delay. If the result is too large, then use the maximum allowed - * value. - * - * Notice that we use the ns_to_cycles function to compute the sample - * delay factor. We do this because the form of the computation is the - * same as that for calculating cycles. - */ - sample_delay_factor = - ns_to_cycles(ideal_sample_delay_in_ns << dll_delay_shift, - clock_period_in_ns, 0); - - if (sample_delay_factor > nfc->max_sample_delay_factor) - sample_delay_factor = nfc->max_sample_delay_factor; - - /* - * These macros conveniently encapsulate a computation we'll use to - * continuously evaluate whether or not the data sample delay is inside - * the eye. - */ - #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns) - - #define QUANTIZED_DELAY \ - ((int) ((sample_delay_factor * clock_period_in_ns) >> \ - dll_delay_shift)) - - #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY)) - - #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1)) - - /* - * While the quantized sample time falls outside the eye, reduce the - * sample delay or extend the data setup to move the sampling point back - * toward the eye. Do not allow the number of data setup cycles to - * exceed the maximum allowed by the NFC. - */ - while (SAMPLE_IS_NOT_WITHIN_THE_EYE && - (data_setup_in_cycles < nfc->max_data_setup_cycles)) { - /* - * If control arrives here, the quantized sample delay falls - * outside the eye. Check if it's before the eye opens, or after - * the eye closes. - */ - if (QUANTIZED_DELAY > IDEAL_DELAY) { - /* - * If control arrives here, the quantized sample delay - * falls after the eye closes. Decrease the quantized - * delay time and then go back to re-evaluate. - */ - if (sample_delay_factor != 0) - sample_delay_factor--; - continue; - } - - /* - * If control arrives here, the quantized sample delay falls - * before the eye opens. Shift the sample point by increasing - * data setup time. This will also make the eye larger. - */ - - /* Give a cycle to data setup. */ - data_setup_in_cycles++; - /* Synchronize the data setup time with the cycles. */ - data_setup_in_ns += clock_period_in_ns; - /* Adjust tEYE accordingly. */ - tEYE += clock_period_in_ns; - - /* - * Decrease the ideal sample delay by one half cycle, to keep it - * in the middle of the eye. - */ - ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); - - /* ...and one less period for the delay time. */ - ideal_sample_delay_in_ns -= clock_period_in_ns; - - /* Jam a negative ideal sample delay to zero. */ - if (ideal_sample_delay_in_ns < 0) - ideal_sample_delay_in_ns = 0; - - /* - * We have a new ideal sample delay, so re-compute the quantized - * delay. - */ - sample_delay_factor = - ns_to_cycles( - ideal_sample_delay_in_ns << dll_delay_shift, - clock_period_in_ns, 0); - - if (sample_delay_factor > nfc->max_sample_delay_factor) - sample_delay_factor = nfc->max_sample_delay_factor; - } - - /* Control arrives here when we're ready to return our results. */ -return_results: - hw->data_setup_in_cycles = data_setup_in_cycles; - hw->data_hold_in_cycles = data_hold_in_cycles; - hw->address_setup_in_cycles = address_setup_in_cycles; - hw->use_half_periods = dll_use_half_periods; - hw->sample_delay_factor = sample_delay_factor; - hw->device_busy_timeout = GPMI_DEFAULT_BUSY_TIMEOUT; - hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; - - /* Return success. */ - return 0; -} - -/* - * <1> Firstly, we should know what's the GPMI-clock means. - * The GPMI-clock is the internal clock in the gpmi nand controller. - * If you set 100MHz to gpmi nand controller, the GPMI-clock's period - * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period. - * - * <2> Secondly, we should know what's the frequency on the nand chip pins. - * The frequency on the nand chip pins is derived from the GPMI-clock. - * We can get it from the following equation: - * - * F = G / (DS + DH) - * - * F : the frequency on the nand chip pins. - * G : the GPMI clock, such as 100MHz. - * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP - * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD - * - * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz, - * the nand EDO(extended Data Out) timing could be applied. - * The GPMI implements a feedback read strobe to sample the read data. - * The feedback read strobe can be delayed to support the nand EDO timing - * where the read strobe may deasserts before the read data is valid, and - * read data is valid for some time after read strobe. - * - * The following figure illustrates some aspects of a NAND Flash read: - * - * |<---tREA---->| - * | | - * | | | - * |<--tRP-->| | - * | | | - * __ ___|__________________________________ - * RDN \________/ | - * | - * /---------\ - * Read Data --------------< >--------- - * \---------/ - * | | - * |<-D->| - * FeedbackRDN ________ ____________ - * \___________/ - * - * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY. - * - * - * <4> Now, we begin to describe how to compute the right RDN_DELAY. - * - * 4.1) From the aspect of the nand chip pins: - * Delay = (tREA + C - tRP) {1} - * - * tREA : the maximum read access time. From the ONFI nand standards, - * we know that tREA is 16ns in mode 5, tREA is 20ns is mode 4. - * Please check it in : www.onfi.org - * C : a constant for adjust the delay. default is 4. - * tRP : the read pulse width. - * Specified by the HW_GPMI_TIMING0:DATA_SETUP: - * tRP = (GPMI-clock-period) * DATA_SETUP - * - * 4.2) From the aspect of the GPMI nand controller: - * Delay = RDN_DELAY * 0.125 * RP {2} - * - * RP : the DLL reference period. - * if (GPMI-clock-period > DLL_THRETHOLD) - * RP = GPMI-clock-period / 2; - * else - * RP = GPMI-clock-period; - * - * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period - * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD - * is 16ns, but in mx6q, we use 12ns. - * - * 4.3) since {1} equals {2}, we get: - * - * (tREA + 4 - tRP) * 8 - * RDN_DELAY = --------------------- {3} - * RP - * - * 4.4) We only support the fastest asynchronous mode of ONFI nand. - * For some ONFI nand, the mode 4 is the fastest mode; - * while for some ONFI nand, the mode 5 is the fastest mode. - * So we only support the mode 4 and mode 5. It is no need to - * support other modes. - */ -static void gpmi_compute_edo_timing(struct gpmi_nand_data *this, - struct gpmi_nfc_hardware_timing *hw) -{ - struct resources *r = &this->resources; - unsigned long rate = clk_get_rate(r->clock[0]); - int mode = this->timing_mode; - int dll_threshold = this->devdata->max_chain_delay; - unsigned long delay; - unsigned long clk_period; - int t_rea; - int c = 4; - int t_rp; - int rp; - - /* - * [1] for GPMI_HW_GPMI_TIMING0: - * The async mode requires 40MHz for mode 4, 50MHz for mode 5. - * The GPMI can support 100MHz at most. So if we want to - * get the 40MHz or 50MHz, we have to set DS=1, DH=1. - * Set the ADDRESS_SETUP to 0 in mode 4. - */ - hw->data_setup_in_cycles = 1; - hw->data_hold_in_cycles = 1; - hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0); - - /* [2] for GPMI_HW_GPMI_TIMING1 */ - hw->device_busy_timeout = 0x9000; - - /* [3] for GPMI_HW_GPMI_CTRL1 */ - hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; - - /* - * Enlarge 10 times for the numerator and denominator in {3}. - * This make us to get more accurate result. - */ - clk_period = NSEC_PER_SEC / (rate / 10); - dll_threshold *= 10; - t_rea = ((mode == 5) ? 16 : 20) * 10; - c *= 10; - - t_rp = clk_period * 1; /* DATA_SETUP is 1 */ - - if (clk_period > dll_threshold) { - hw->use_half_periods = 1; - rp = clk_period / 2; - } else { - hw->use_half_periods = 0; - rp = clk_period; - } - - /* - * Multiply the numerator with 10, we could do a round off: - * 7.8 round up to 8; 7.4 round down to 7. - */ - delay = (((t_rea + c - t_rp) * 8) * 10) / rp; - delay = (delay + 5) / 10; - - hw->sample_delay_factor = delay; -} - -static int enable_edo_mode(struct gpmi_nand_data *this, int mode) -{ - struct resources *r = &this->resources; - struct nand_chip *nand = &this->nand; - struct mtd_info *mtd = nand_to_mtd(nand); - uint8_t *feature; - unsigned long rate; - int ret; - - feature = kzalloc(ONFI_SUBFEATURE_PARAM_LEN, GFP_KERNEL); - if (!feature) - return -ENOMEM; - - nand->select_chip(mtd, 0); - - /* [1] send SET FEATURE command to NAND */ - feature[0] = mode; - ret = nand->onfi_set_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); - if (ret) - goto err_out; - - /* [2] send GET FEATURE command to double-check the timing mode */ - memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN); - ret = nand->onfi_get_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); - if (ret || feature[0] != mode) - goto err_out; - - nand->select_chip(mtd, -1); - - /* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */ - rate = (mode == 5) ? 100000000 : 80000000; - clk_set_rate(r->clock[0], rate); - - /* Let the gpmi_begin() re-compute the timing again. */ - this->flags &= ~GPMI_TIMING_INIT_OK; - - this->flags |= GPMI_ASYNC_EDO_ENABLED; - this->timing_mode = mode; - kfree(feature); - dev_info(this->dev, "enable the asynchronous EDO mode %d\n", mode); - return 0; - -err_out: - nand->select_chip(mtd, -1); - kfree(feature); - dev_err(this->dev, "mode:%d ,failed in set feature.\n", mode); - return -EINVAL; -} - -int gpmi_extra_init(struct gpmi_nand_data *this) -{ - struct nand_chip *chip = &this->nand; - - /* Enable the asynchronous EDO feature. */ - if (GPMI_IS_MX6(this) && chip->onfi_version) { - int mode = onfi_get_async_timing_mode(chip); - - /* We only support the timing mode 4 and mode 5. */ - if (mode & ONFI_TIMING_MODE_5) - mode = 5; - else if (mode & ONFI_TIMING_MODE_4) - mode = 4; - else - return 0; - - return enable_edo_mode(this, mode); - } - return 0; -} - -/* Begin the I/O */ -void gpmi_begin(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - void __iomem *gpmi_regs = r->gpmi_regs; - unsigned int clock_period_in_ns; - uint32_t reg; - unsigned int dll_wait_time_in_us; - struct gpmi_nfc_hardware_timing hw; - int ret; - - /* Enable the clock. */ - ret = gpmi_enable_clk(this); - if (ret) { - dev_err(this->dev, "We failed in enable the clk\n"); - goto err_out; - } - - /* Only initialize the timing once */ - if (this->flags & GPMI_TIMING_INIT_OK) - return; - this->flags |= GPMI_TIMING_INIT_OK; - - if (this->flags & GPMI_ASYNC_EDO_ENABLED) - gpmi_compute_edo_timing(this, &hw); - else - gpmi_nfc_compute_hardware_timing(this, &hw); - - /* [1] Set HW_GPMI_TIMING0 */ - reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) | - BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) | - BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles); - - writel(reg, gpmi_regs + HW_GPMI_TIMING0); - - /* [2] Set HW_GPMI_TIMING1 */ - writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw.device_busy_timeout), - gpmi_regs + HW_GPMI_TIMING1); - - /* [3] The following code is to set the HW_GPMI_CTRL1. */ - - /* Set the WRN_DLY_SEL */ - writel(BM_GPMI_CTRL1_WRN_DLY_SEL, gpmi_regs + HW_GPMI_CTRL1_CLR); - writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw.wrn_dly_sel), - gpmi_regs + HW_GPMI_CTRL1_SET); - - /* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. */ - writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); - - /* Clear out the DLL control fields. */ - reg = BM_GPMI_CTRL1_RDN_DELAY | BM_GPMI_CTRL1_HALF_PERIOD; - writel(reg, gpmi_regs + HW_GPMI_CTRL1_CLR); - - /* If no sample delay is called for, return immediately. */ - if (!hw.sample_delay_factor) - return; - - /* Set RDN_DELAY or HALF_PERIOD. */ - reg = ((hw.use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0) - | BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor); - - writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET); - - /* At last, we enable the DLL. */ - writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); - - /* - * After we enable the GPMI DLL, we have to wait 64 clock cycles before - * we can use the GPMI. Calculate the amount of time we need to wait, - * in microseconds. - */ - clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]); - dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; - - if (!dll_wait_time_in_us) - dll_wait_time_in_us = 1; - - /* Wait for the DLL to settle. */ - udelay(dll_wait_time_in_us); - -err_out: - return; -} - -void gpmi_end(struct gpmi_nand_data *this) -{ - gpmi_disable_clk(this); -} - -/* Clears a BCH interrupt. */ -void gpmi_clear_bch(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); -} - -/* Returns the Ready/Busy status of the given chip. */ -int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) -{ - struct resources *r = &this->resources; - uint32_t mask = 0; - uint32_t reg = 0; - - if (GPMI_IS_MX23(this)) { - mask = MX23_BM_GPMI_DEBUG_READY0 << chip; - reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); - } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) { - /* - * In the imx6, all the ready/busy pins are bound - * together. So we only need to check chip 0. - */ - if (GPMI_IS_MX6(this)) - chip = 0; - - /* MX28 shares the same R/B register as MX6Q. */ - mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); - reg = readl(r->gpmi_regs + HW_GPMI_STAT); - } else - dev_err(this->dev, "unknown arch.\n"); - return reg & mask; -} - -static inline void set_dma_type(struct gpmi_nand_data *this, - enum dma_ops_type type) -{ - this->last_dma_type = this->dma_type; - this->dma_type = type; -} - -int gpmi_send_command(struct gpmi_nand_data *this) -{ - struct dma_chan *channel = get_dma_chan(this); - struct dma_async_tx_descriptor *desc; - struct scatterlist *sgl; - int chip = this->current_chip; - u32 pio[3]; - - /* [1] send out the PIO words */ - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) - | BM_GPMI_CTRL0_ADDRESS_INCREMENT - | BF_GPMI_CTRL0_XFER_COUNT(this->command_length); - pio[1] = pio[2] = 0; - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); - if (!desc) - return -EINVAL; - - /* [2] send out the COMMAND + ADDRESS string stored in @buffer */ - sgl = &this->cmd_sgl; - - sg_init_one(sgl, this->cmd_buffer, this->command_length); - dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); - desc = dmaengine_prep_slave_sg(channel, - sgl, 1, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [3] submit the DMA */ - set_dma_type(this, DMA_FOR_COMMAND); - return start_dma_without_bch_irq(this, desc); -} - -int gpmi_send_data(struct gpmi_nand_data *this) -{ - struct dma_async_tx_descriptor *desc; - struct dma_chan *channel = get_dma_chan(this); - int chip = this->current_chip; - uint32_t command_mode; - uint32_t address; - u32 pio[2]; - - /* [1] PIO */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); - pio[1] = 0; - desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); - if (!desc) - return -EINVAL; - - /* [2] send DMA request */ - prepare_data_dma(this, DMA_TO_DEVICE); - desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [3] submit the DMA */ - set_dma_type(this, DMA_FOR_WRITE_DATA); - return start_dma_without_bch_irq(this, desc); -} - -int gpmi_read_data(struct gpmi_nand_data *this) -{ - struct dma_async_tx_descriptor *desc; - struct dma_chan *channel = get_dma_chan(this); - int chip = this->current_chip; - u32 pio[2]; - - /* [1] : send PIO */ - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) - | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); - pio[1] = 0; - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); - if (!desc) - return -EINVAL; - - /* [2] : send DMA request */ - prepare_data_dma(this, DMA_FROM_DEVICE); - desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [3] : submit the DMA */ - set_dma_type(this, DMA_FOR_READ_DATA); - return start_dma_without_bch_irq(this, desc); -} - -int gpmi_send_page(struct gpmi_nand_data *this, - dma_addr_t payload, dma_addr_t auxiliary) -{ - struct bch_geometry *geo = &this->bch_geometry; - uint32_t command_mode; - uint32_t address; - uint32_t ecc_command; - uint32_t buffer_mask; - struct dma_async_tx_descriptor *desc; - struct dma_chan *channel = get_dma_chan(this); - int chip = this->current_chip; - u32 pio[6]; - - /* A DMA descriptor that does an ECC page read. */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE; - buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | - BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(0); - pio[1] = 0; - pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC - | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) - | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); - pio[3] = geo->page_size; - pio[4] = payload; - pio[5] = auxiliary; - - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, - DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE); - return start_dma_with_bch_irq(this, desc); -} - -int gpmi_read_page(struct gpmi_nand_data *this, - dma_addr_t payload, dma_addr_t auxiliary) -{ - struct bch_geometry *geo = &this->bch_geometry; - uint32_t command_mode; - uint32_t address; - uint32_t ecc_command; - uint32_t buffer_mask; - struct dma_async_tx_descriptor *desc; - struct dma_chan *channel = get_dma_chan(this); - int chip = this->current_chip; - u32 pio[6]; - - /* [1] Wait for the chip to report ready. */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(0); - pio[1] = 0; - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, 2, - DMA_TRANS_NONE, 0); - if (!desc) - return -EINVAL; - - /* [2] Enable the BCH block and read. */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE; - buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE - | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); - - pio[1] = 0; - pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC - | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) - | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); - pio[3] = geo->page_size; - pio[4] = payload; - pio[5] = auxiliary; - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [3] Disable the BCH block */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); - pio[1] = 0; - pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */ - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, 3, - DMA_TRANS_NONE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [4] submit the DMA */ - set_dma_type(this, DMA_FOR_READ_ECC_PAGE); - return start_dma_with_bch_irq(this, desc); -} - -/** - * gpmi_copy_bits - copy bits from one memory region to another - * @dst: destination buffer - * @dst_bit_off: bit offset we're starting to write at - * @src: source buffer - * @src_bit_off: bit offset we're starting to read from - * @nbits: number of bits to copy - * - * This functions copies bits from one memory region to another, and is used by - * the GPMI driver to copy ECC sections which are not guaranteed to be byte - * aligned. - * - * src and dst should not overlap. - * - */ -void gpmi_copy_bits(u8 *dst, size_t dst_bit_off, - const u8 *src, size_t src_bit_off, - size_t nbits) -{ - size_t i; - size_t nbytes; - u32 src_buffer = 0; - size_t bits_in_src_buffer = 0; - - if (!nbits) - return; - - /* - * Move src and dst pointers to the closest byte pointer and store bit - * offsets within a byte. - */ - src += src_bit_off / 8; - src_bit_off %= 8; - - dst += dst_bit_off / 8; - dst_bit_off %= 8; - - /* - * Initialize the src_buffer value with bits available in the first - * byte of data so that we end up with a byte aligned src pointer. - */ - if (src_bit_off) { - src_buffer = src[0] >> src_bit_off; - if (nbits >= (8 - src_bit_off)) { - bits_in_src_buffer += 8 - src_bit_off; - } else { - src_buffer &= GENMASK(nbits - 1, 0); - bits_in_src_buffer += nbits; - } - nbits -= bits_in_src_buffer; - src++; - } - - /* Calculate the number of bytes that can be copied from src to dst. */ - nbytes = nbits / 8; - - /* Try to align dst to a byte boundary. */ - if (dst_bit_off) { - if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) { - src_buffer |= src[0] << bits_in_src_buffer; - bits_in_src_buffer += 8; - src++; - nbytes--; - } - - if (bits_in_src_buffer >= (8 - dst_bit_off)) { - dst[0] &= GENMASK(dst_bit_off - 1, 0); - dst[0] |= src_buffer << dst_bit_off; - src_buffer >>= (8 - dst_bit_off); - bits_in_src_buffer -= (8 - dst_bit_off); - dst_bit_off = 0; - dst++; - if (bits_in_src_buffer > 7) { - bits_in_src_buffer -= 8; - dst[0] = src_buffer; - dst++; - src_buffer >>= 8; - } - } - } - - if (!bits_in_src_buffer && !dst_bit_off) { - /* - * Both src and dst pointers are byte aligned, thus we can - * just use the optimized memcpy function. - */ - if (nbytes) - memcpy(dst, src, nbytes); - } else { - /* - * src buffer is not byte aligned, hence we have to copy each - * src byte to the src_buffer variable before extracting a byte - * to store in dst. - */ - for (i = 0; i < nbytes; i++) { - src_buffer |= src[i] << bits_in_src_buffer; - dst[i] = src_buffer; - src_buffer >>= 8; - } - } - /* Update dst and src pointers */ - dst += nbytes; - src += nbytes; - - /* - * nbits is the number of remaining bits. It should not exceed 8 as - * we've already copied as much bytes as possible. - */ - nbits %= 8; - - /* - * If there's no more bits to copy to the destination and src buffer - * was already byte aligned, then we're done. - */ - if (!nbits && !bits_in_src_buffer) - return; - - /* Copy the remaining bits to src_buffer */ - if (nbits) - src_buffer |= (*src & GENMASK(nbits - 1, 0)) << - bits_in_src_buffer; - bits_in_src_buffer += nbits; - - /* - * In case there were not enough bits to get a byte aligned dst buffer - * prepare the src_buffer variable to match the dst organization (shift - * src_buffer by dst_bit_off and retrieve the least significant bits - * from dst). - */ - if (dst_bit_off) - src_buffer = (src_buffer << dst_bit_off) | - (*dst & GENMASK(dst_bit_off - 1, 0)); - bits_in_src_buffer += dst_bit_off; - - /* - * Keep most significant bits from dst if we end up with an unaligned - * number of bits. - */ - nbytes = bits_in_src_buffer / 8; - if (bits_in_src_buffer % 8) { - src_buffer |= (dst[nbytes] & - GENMASK(7, bits_in_src_buffer % 8)) << - (nbytes * 8); - nbytes++; - } - - /* Copy the remaining bytes to dst */ - for (i = 0; i < nbytes; i++) { - dst[i] = src_buffer; - src_buffer >>= 8; - } -} diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c deleted file mode 100644 index 61fdd733492f..000000000000 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ /dev/null @@ -1,2182 +0,0 @@ -/* - * Freescale GPMI NAND Flash Driver - * - * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. - * Copyright (C) 2008 Embedded Alley Solutions, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "gpmi-nand.h" -#include "bch-regs.h" - -/* Resource names for the GPMI NAND driver. */ -#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" -#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" -#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" - -/* add our owner bbt descriptor */ -static uint8_t scan_ff_pattern[] = { 0xff }; -static struct nand_bbt_descr gpmi_bbt_descr = { - .options = 0, - .offs = 0, - .len = 1, - .pattern = scan_ff_pattern -}; - -/* - * We may change the layout if we can get the ECC info from the datasheet, - * else we will use all the (page + OOB). - */ -static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - struct bch_geometry *geo = &this->bch_geometry; - - if (section) - return -ERANGE; - - oobregion->offset = 0; - oobregion->length = geo->page_size - mtd->writesize; - - return 0; -} - -static int gpmi_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - struct bch_geometry *geo = &this->bch_geometry; - - if (section) - return -ERANGE; - - /* The available oob size we have. */ - if (geo->page_size < mtd->writesize + mtd->oobsize) { - oobregion->offset = geo->page_size - mtd->writesize; - oobregion->length = mtd->oobsize - oobregion->offset; - } - - return 0; -} - -static const char * const gpmi_clks_for_mx2x[] = { - "gpmi_io", -}; - -static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { - .ecc = gpmi_ooblayout_ecc, - .free = gpmi_ooblayout_free, -}; - -static const struct gpmi_devdata gpmi_devdata_imx23 = { - .type = IS_MX23, - .bch_max_ecc_strength = 20, - .max_chain_delay = 16, - .clks = gpmi_clks_for_mx2x, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), -}; - -static const struct gpmi_devdata gpmi_devdata_imx28 = { - .type = IS_MX28, - .bch_max_ecc_strength = 20, - .max_chain_delay = 16, - .clks = gpmi_clks_for_mx2x, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), -}; - -static const char * const gpmi_clks_for_mx6[] = { - "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", -}; - -static const struct gpmi_devdata gpmi_devdata_imx6q = { - .type = IS_MX6Q, - .bch_max_ecc_strength = 40, - .max_chain_delay = 12, - .clks = gpmi_clks_for_mx6, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), -}; - -static const struct gpmi_devdata gpmi_devdata_imx6sx = { - .type = IS_MX6SX, - .bch_max_ecc_strength = 62, - .max_chain_delay = 12, - .clks = gpmi_clks_for_mx6, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), -}; - -static const char * const gpmi_clks_for_mx7d[] = { - "gpmi_io", "gpmi_bch_apb", -}; - -static const struct gpmi_devdata gpmi_devdata_imx7d = { - .type = IS_MX7D, - .bch_max_ecc_strength = 62, - .max_chain_delay = 12, - .clks = gpmi_clks_for_mx7d, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), -}; - -static irqreturn_t bch_irq(int irq, void *cookie) -{ - struct gpmi_nand_data *this = cookie; - - gpmi_clear_bch(this); - complete(&this->bch_done); - return IRQ_HANDLED; -} - -/* - * Calculate the ECC strength by hand: - * E : The ECC strength. - * G : the length of Galois Field. - * N : The chunk count of per page. - * O : the oobsize of the NAND chip. - * M : the metasize of per page. - * - * The formula is : - * E * G * N - * ------------ <= (O - M) - * 8 - * - * So, we get E by: - * (O - M) * 8 - * E <= ------------- - * G * N - */ -static inline int get_ecc_strength(struct gpmi_nand_data *this) -{ - struct bch_geometry *geo = &this->bch_geometry; - struct mtd_info *mtd = nand_to_mtd(&this->nand); - int ecc_strength; - - ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8) - / (geo->gf_len * geo->ecc_chunk_count); - - /* We need the minor even number. */ - return round_down(ecc_strength, 2); -} - -static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) -{ - struct bch_geometry *geo = &this->bch_geometry; - - /* Do the sanity check. */ - if (GPMI_IS_MX23(this) || GPMI_IS_MX28(this)) { - /* The mx23/mx28 only support the GF13. */ - if (geo->gf_len == 14) - return false; - } - return geo->ecc_strength <= this->devdata->bch_max_ecc_strength; -} - -/* - * If we can get the ECC information from the nand chip, we do not - * need to calculate them ourselves. - * - * We may have available oob space in this case. - */ -static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) -{ - struct bch_geometry *geo = &this->bch_geometry; - struct nand_chip *chip = &this->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - unsigned int block_mark_bit_offset; - - if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) - return -EINVAL; - - switch (chip->ecc_step_ds) { - case SZ_512: - geo->gf_len = 13; - break; - case SZ_1K: - geo->gf_len = 14; - break; - default: - dev_err(this->dev, - "unsupported nand chip. ecc bits : %d, ecc size : %d\n", - chip->ecc_strength_ds, chip->ecc_step_ds); - return -EINVAL; - } - geo->ecc_chunk_size = chip->ecc_step_ds; - geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); - if (!gpmi_check_ecc(this)) - return -EINVAL; - - /* Keep the C >= O */ - if (geo->ecc_chunk_size < mtd->oobsize) { - dev_err(this->dev, - "unsupported nand chip. ecc size: %d, oob size : %d\n", - chip->ecc_step_ds, mtd->oobsize); - return -EINVAL; - } - - /* The default value, see comment in the legacy_set_geometry(). */ - geo->metadata_size = 10; - - geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; - - /* - * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: - * - * | P | - * |<----------------------------------------------------->| - * | | - * | (Block Mark) | - * | P' | | | | - * |<-------------------------------------------->| D | | O' | - * | |<---->| |<--->| - * V V V V V - * +---+----------+-+----------+-+----------+-+----------+-+-----+ - * | M | data |E| data |E| data |E| data |E| | - * +---+----------+-+----------+-+----------+-+----------+-+-----+ - * ^ ^ - * | O | - * |<------------>| - * | | - * - * P : the page size for BCH module. - * E : The ECC strength. - * G : the length of Galois Field. - * N : The chunk count of per page. - * M : the metasize of per page. - * C : the ecc chunk size, aka the "data" above. - * P': the nand chip's page size. - * O : the nand chip's oob size. - * O': the free oob. - * - * The formula for P is : - * - * E * G * N - * P = ------------ + P' + M - * 8 - * - * The position of block mark moves forward in the ECC-based view - * of page, and the delta is: - * - * E * G * (N - 1) - * D = (---------------- + M) - * 8 - * - * Please see the comment in legacy_set_geometry(). - * With the condition C >= O , we still can get same result. - * So the bit position of the physical block mark within the ECC-based - * view of the page is : - * (P' - D) * 8 - */ - geo->page_size = mtd->writesize + geo->metadata_size + - (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; - - geo->payload_size = mtd->writesize; - - geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); - geo->auxiliary_size = ALIGN(geo->metadata_size, 4) - + ALIGN(geo->ecc_chunk_count, 4); - - if (!this->swap_block_mark) - return 0; - - /* For bit swap. */ - block_mark_bit_offset = mtd->writesize * 8 - - (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) - + geo->metadata_size * 8); - - geo->block_mark_byte_offset = block_mark_bit_offset / 8; - geo->block_mark_bit_offset = block_mark_bit_offset % 8; - return 0; -} - -static int legacy_set_geometry(struct gpmi_nand_data *this) -{ - struct bch_geometry *geo = &this->bch_geometry; - struct mtd_info *mtd = nand_to_mtd(&this->nand); - unsigned int metadata_size; - unsigned int status_size; - unsigned int block_mark_bit_offset; - - /* - * The size of the metadata can be changed, though we set it to 10 - * bytes now. But it can't be too large, because we have to save - * enough space for BCH. - */ - geo->metadata_size = 10; - - /* The default for the length of Galois Field. */ - geo->gf_len = 13; - - /* The default for chunk size. */ - geo->ecc_chunk_size = 512; - while (geo->ecc_chunk_size < mtd->oobsize) { - geo->ecc_chunk_size *= 2; /* keep C >= O */ - geo->gf_len = 14; - } - - geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; - - /* We use the same ECC strength for all chunks. */ - geo->ecc_strength = get_ecc_strength(this); - if (!gpmi_check_ecc(this)) { - dev_err(this->dev, - "ecc strength: %d cannot be supported by the controller (%d)\n" - "try to use minimum ecc strength that NAND chip required\n", - geo->ecc_strength, - this->devdata->bch_max_ecc_strength); - return -EINVAL; - } - - geo->page_size = mtd->writesize + geo->metadata_size + - (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; - geo->payload_size = mtd->writesize; - - /* - * The auxiliary buffer contains the metadata and the ECC status. The - * metadata is padded to the nearest 32-bit boundary. The ECC status - * contains one byte for every ECC chunk, and is also padded to the - * nearest 32-bit boundary. - */ - metadata_size = ALIGN(geo->metadata_size, 4); - status_size = ALIGN(geo->ecc_chunk_count, 4); - - geo->auxiliary_size = metadata_size + status_size; - geo->auxiliary_status_offset = metadata_size; - - if (!this->swap_block_mark) - return 0; - - /* - * We need to compute the byte and bit offsets of - * the physical block mark within the ECC-based view of the page. - * - * NAND chip with 2K page shows below: - * (Block Mark) - * | | - * | D | - * |<---->| - * V V - * +---+----------+-+----------+-+----------+-+----------+-+ - * | M | data |E| data |E| data |E| data |E| - * +---+----------+-+----------+-+----------+-+----------+-+ - * - * The position of block mark moves forward in the ECC-based view - * of page, and the delta is: - * - * E * G * (N - 1) - * D = (---------------- + M) - * 8 - * - * With the formula to compute the ECC strength, and the condition - * : C >= O (C is the ecc chunk size) - * - * It's easy to deduce to the following result: - * - * E * G (O - M) C - M C - M - * ----------- <= ------- <= -------- < --------- - * 8 N N (N - 1) - * - * So, we get: - * - * E * G * (N - 1) - * D = (---------------- + M) < C - * 8 - * - * The above inequality means the position of block mark - * within the ECC-based view of the page is still in the data chunk, - * and it's NOT in the ECC bits of the chunk. - * - * Use the following to compute the bit position of the - * physical block mark within the ECC-based view of the page: - * (page_size - D) * 8 - * - * --Huang Shijie - */ - block_mark_bit_offset = mtd->writesize * 8 - - (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) - + geo->metadata_size * 8); - - geo->block_mark_byte_offset = block_mark_bit_offset / 8; - geo->block_mark_bit_offset = block_mark_bit_offset % 8; - return 0; -} - -int common_nfc_set_geometry(struct gpmi_nand_data *this) -{ - if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) - || legacy_set_geometry(this)) - return set_geometry_by_ecc_info(this); - - return 0; -} - -struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) -{ - /* We use the DMA channel 0 to access all the nand chips. */ - return this->dma_chans[0]; -} - -/* Can we use the upper's buffer directly for DMA? */ -void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) -{ - struct scatterlist *sgl = &this->data_sgl; - int ret; - - /* first try to map the upper buffer directly */ - if (virt_addr_valid(this->upper_buf) && - !object_is_on_stack(this->upper_buf)) { - sg_init_one(sgl, this->upper_buf, this->upper_len); - ret = dma_map_sg(this->dev, sgl, 1, dr); - if (ret == 0) - goto map_fail; - - this->direct_dma_map_ok = true; - return; - } - -map_fail: - /* We have to use our own DMA buffer. */ - sg_init_one(sgl, this->data_buffer_dma, this->upper_len); - - if (dr == DMA_TO_DEVICE) - memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len); - - dma_map_sg(this->dev, sgl, 1, dr); - - this->direct_dma_map_ok = false; -} - -/* This will be called after the DMA operation is finished. */ -static void dma_irq_callback(void *param) -{ - struct gpmi_nand_data *this = param; - struct completion *dma_c = &this->dma_done; - - switch (this->dma_type) { - case DMA_FOR_COMMAND: - dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE); - break; - - case DMA_FOR_READ_DATA: - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); - if (this->direct_dma_map_ok == false) - memcpy(this->upper_buf, this->data_buffer_dma, - this->upper_len); - break; - - case DMA_FOR_WRITE_DATA: - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); - break; - - case DMA_FOR_READ_ECC_PAGE: - case DMA_FOR_WRITE_ECC_PAGE: - /* We have to wait the BCH interrupt to finish. */ - break; - - default: - dev_err(this->dev, "in wrong DMA operation.\n"); - } - - complete(dma_c); -} - -int start_dma_without_bch_irq(struct gpmi_nand_data *this, - struct dma_async_tx_descriptor *desc) -{ - struct completion *dma_c = &this->dma_done; - unsigned long timeout; - - init_completion(dma_c); - - desc->callback = dma_irq_callback; - desc->callback_param = this; - dmaengine_submit(desc); - dma_async_issue_pending(get_dma_chan(this)); - - /* Wait for the interrupt from the DMA block. */ - timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000)); - if (!timeout) { - dev_err(this->dev, "DMA timeout, last DMA :%d\n", - this->last_dma_type); - gpmi_dump_info(this); - return -ETIMEDOUT; - } - return 0; -} - -/* - * This function is used in BCH reading or BCH writing pages. - * It will wait for the BCH interrupt as long as ONE second. - * Actually, we must wait for two interrupts : - * [1] firstly the DMA interrupt and - * [2] secondly the BCH interrupt. - */ -int start_dma_with_bch_irq(struct gpmi_nand_data *this, - struct dma_async_tx_descriptor *desc) -{ - struct completion *bch_c = &this->bch_done; - unsigned long timeout; - - /* Prepare to receive an interrupt from the BCH block. */ - init_completion(bch_c); - - /* start the DMA */ - start_dma_without_bch_irq(this, desc); - - /* Wait for the interrupt from the BCH block. */ - timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000)); - if (!timeout) { - dev_err(this->dev, "BCH timeout, last DMA :%d\n", - this->last_dma_type); - gpmi_dump_info(this); - return -ETIMEDOUT; - } - return 0; -} - -static int acquire_register_block(struct gpmi_nand_data *this, - const char *res_name) -{ - struct platform_device *pdev = this->pdev; - struct resources *res = &this->resources; - struct resource *r; - void __iomem *p; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); - p = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(p)) - return PTR_ERR(p); - - if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME)) - res->gpmi_regs = p; - else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME)) - res->bch_regs = p; - else - dev_err(this->dev, "unknown resource name : %s\n", res_name); - - return 0; -} - -static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h) -{ - struct platform_device *pdev = this->pdev; - const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME; - struct resource *r; - int err; - - r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name); - if (!r) { - dev_err(this->dev, "Can't get resource for %s\n", res_name); - return -ENODEV; - } - - err = devm_request_irq(this->dev, r->start, irq_h, 0, res_name, this); - if (err) - dev_err(this->dev, "error requesting BCH IRQ\n"); - - return err; -} - -static void release_dma_channels(struct gpmi_nand_data *this) -{ - unsigned int i; - for (i = 0; i < DMA_CHANS; i++) - if (this->dma_chans[i]) { - dma_release_channel(this->dma_chans[i]); - this->dma_chans[i] = NULL; - } -} - -static int acquire_dma_channels(struct gpmi_nand_data *this) -{ - struct platform_device *pdev = this->pdev; - struct dma_chan *dma_chan; - - /* request dma channel */ - dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx"); - if (!dma_chan) { - dev_err(this->dev, "Failed to request DMA channel.\n"); - goto acquire_err; - } - - this->dma_chans[0] = dma_chan; - return 0; - -acquire_err: - release_dma_channels(this); - return -EINVAL; -} - -static int gpmi_get_clks(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - struct clk *clk; - int err, i; - - for (i = 0; i < this->devdata->clks_count; i++) { - clk = devm_clk_get(this->dev, this->devdata->clks[i]); - if (IS_ERR(clk)) { - err = PTR_ERR(clk); - goto err_clock; - } - - r->clock[i] = clk; - } - - if (GPMI_IS_MX6(this)) - /* - * Set the default value for the gpmi clock. - * - * If you want to use the ONFI nand which is in the - * Synchronous Mode, you should change the clock as you need. - */ - clk_set_rate(r->clock[0], 22000000); - - return 0; - -err_clock: - dev_dbg(this->dev, "failed in finding the clocks.\n"); - return err; -} - -static int acquire_resources(struct gpmi_nand_data *this) -{ - int ret; - - ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME); - if (ret) - goto exit_regs; - - ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME); - if (ret) - goto exit_regs; - - ret = acquire_bch_irq(this, bch_irq); - if (ret) - goto exit_regs; - - ret = acquire_dma_channels(this); - if (ret) - goto exit_regs; - - ret = gpmi_get_clks(this); - if (ret) - goto exit_clock; - return 0; - -exit_clock: - release_dma_channels(this); -exit_regs: - return ret; -} - -static void release_resources(struct gpmi_nand_data *this) -{ - release_dma_channels(this); -} - -static int init_hardware(struct gpmi_nand_data *this) -{ - int ret; - - /* - * This structure contains the "safe" GPMI timing that should succeed - * with any NAND Flash device - * (although, with less-than-optimal performance). - */ - struct nand_timing safe_timing = { - .data_setup_in_ns = 80, - .data_hold_in_ns = 60, - .address_setup_in_ns = 25, - .gpmi_sample_delay_in_ns = 6, - .tREA_in_ns = -1, - .tRLOH_in_ns = -1, - .tRHOH_in_ns = -1, - }; - - /* Initialize the hardwares. */ - ret = gpmi_init(this); - if (ret) - return ret; - - this->timing = safe_timing; - return 0; -} - -static int read_page_prepare(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void **use_virt, dma_addr_t *use_phys) -{ - struct device *dev = this->dev; - - if (virt_addr_valid(destination)) { - dma_addr_t dest_phys; - - dest_phys = dma_map_single(dev, destination, - length, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, dest_phys)) { - if (alt_size < length) { - dev_err(dev, "Alternate buffer is too small\n"); - return -ENOMEM; - } - goto map_failed; - } - *use_virt = destination; - *use_phys = dest_phys; - this->direct_dma_map_ok = true; - return 0; - } - -map_failed: - *use_virt = alt_virt; - *use_phys = alt_phys; - this->direct_dma_map_ok = false; - return 0; -} - -static inline void read_page_end(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void *used_virt, dma_addr_t used_phys) -{ - if (this->direct_dma_map_ok) - dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE); -} - -static inline void read_page_swap_end(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void *used_virt, dma_addr_t used_phys) -{ - if (!this->direct_dma_map_ok) - memcpy(destination, alt_virt, length); -} - -static int send_page_prepare(struct gpmi_nand_data *this, - const void *source, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - const void **use_virt, dma_addr_t *use_phys) -{ - struct device *dev = this->dev; - - if (virt_addr_valid(source)) { - dma_addr_t source_phys; - - source_phys = dma_map_single(dev, (void *)source, length, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, source_phys)) { - if (alt_size < length) { - dev_err(dev, "Alternate buffer is too small\n"); - return -ENOMEM; - } - goto map_failed; - } - *use_virt = source; - *use_phys = source_phys; - return 0; - } -map_failed: - /* - * Copy the content of the source buffer into the alternate - * buffer and set up the return values accordingly. - */ - memcpy(alt_virt, source, length); - - *use_virt = alt_virt; - *use_phys = alt_phys; - return 0; -} - -static void send_page_end(struct gpmi_nand_data *this, - const void *source, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - const void *used_virt, dma_addr_t used_phys) -{ - struct device *dev = this->dev; - if (used_virt == source) - dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE); -} - -static void gpmi_free_dma_buffer(struct gpmi_nand_data *this) -{ - struct device *dev = this->dev; - - if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt)) - dma_free_coherent(dev, this->page_buffer_size, - this->page_buffer_virt, - this->page_buffer_phys); - kfree(this->cmd_buffer); - kfree(this->data_buffer_dma); - kfree(this->raw_buffer); - - this->cmd_buffer = NULL; - this->data_buffer_dma = NULL; - this->raw_buffer = NULL; - this->page_buffer_virt = NULL; - this->page_buffer_size = 0; -} - -/* Allocate the DMA buffers */ -static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) -{ - struct bch_geometry *geo = &this->bch_geometry; - struct device *dev = this->dev; - struct mtd_info *mtd = nand_to_mtd(&this->nand); - - /* [1] Allocate a command buffer. PAGE_SIZE is enough. */ - this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL); - if (this->cmd_buffer == NULL) - goto error_alloc; - - /* - * [2] Allocate a read/write data buffer. - * The gpmi_alloc_dma_buffer can be called twice. - * We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer - * is called before the nand_scan_ident; and we allocate a buffer - * of the real NAND page size when the gpmi_alloc_dma_buffer is - * called after the nand_scan_ident. - */ - this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE, - GFP_DMA | GFP_KERNEL); - if (this->data_buffer_dma == NULL) - goto error_alloc; - - /* - * [3] Allocate the page buffer. - * - * Both the payload buffer and the auxiliary buffer must appear on - * 32-bit boundaries. We presume the size of the payload buffer is a - * power of two and is much larger than four, which guarantees the - * auxiliary buffer will appear on a 32-bit boundary. - */ - this->page_buffer_size = geo->payload_size + geo->auxiliary_size; - this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size, - &this->page_buffer_phys, GFP_DMA); - if (!this->page_buffer_virt) - goto error_alloc; - - this->raw_buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); - if (!this->raw_buffer) - goto error_alloc; - - /* Slice up the page buffer. */ - this->payload_virt = this->page_buffer_virt; - this->payload_phys = this->page_buffer_phys; - this->auxiliary_virt = this->payload_virt + geo->payload_size; - this->auxiliary_phys = this->payload_phys + geo->payload_size; - return 0; - -error_alloc: - gpmi_free_dma_buffer(this); - return -ENOMEM; -} - -static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - int ret; - - /* - * Every operation begins with a command byte and a series of zero or - * more address bytes. These are distinguished by either the Address - * Latch Enable (ALE) or Command Latch Enable (CLE) signals being - * asserted. When MTD is ready to execute the command, it will deassert - * both latch enables. - * - * Rather than run a separate DMA operation for every single byte, we - * queue them up and run a single DMA operation for the entire series - * of command and data bytes. NAND_CMD_NONE means the END of the queue. - */ - if ((ctrl & (NAND_ALE | NAND_CLE))) { - if (data != NAND_CMD_NONE) - this->cmd_buffer[this->command_length++] = data; - return; - } - - if (!this->command_length) - return; - - ret = gpmi_send_command(this); - if (ret) - dev_err(this->dev, "Chip: %u, Error %d\n", - this->current_chip, ret); - - this->command_length = 0; -} - -static int gpmi_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - - return gpmi_is_ready(this, this->current_chip); -} - -static void gpmi_select_chip(struct mtd_info *mtd, int chipnr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - - if ((this->current_chip < 0) && (chipnr >= 0)) - gpmi_begin(this); - else if ((this->current_chip >= 0) && (chipnr < 0)) - gpmi_end(this); - - this->current_chip = chipnr; -} - -static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - - dev_dbg(this->dev, "len is %d\n", len); - this->upper_buf = buf; - this->upper_len = len; - - gpmi_read_data(this); -} - -static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - - dev_dbg(this->dev, "len is %d\n", len); - this->upper_buf = (uint8_t *)buf; - this->upper_len = len; - - gpmi_send_data(this); -} - -static uint8_t gpmi_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - uint8_t *buf = this->data_buffer_dma; - - gpmi_read_buf(mtd, buf, 1); - return buf[0]; -} - -/* - * Handles block mark swapping. - * It can be called in swapping the block mark, or swapping it back, - * because the the operations are the same. - */ -static void block_mark_swapping(struct gpmi_nand_data *this, - void *payload, void *auxiliary) -{ - struct bch_geometry *nfc_geo = &this->bch_geometry; - unsigned char *p; - unsigned char *a; - unsigned int bit; - unsigned char mask; - unsigned char from_data; - unsigned char from_oob; - - if (!this->swap_block_mark) - return; - - /* - * If control arrives here, we're swapping. Make some convenience - * variables. - */ - bit = nfc_geo->block_mark_bit_offset; - p = payload + nfc_geo->block_mark_byte_offset; - a = auxiliary; - - /* - * Get the byte from the data area that overlays the block mark. Since - * the ECC engine applies its own view to the bits in the page, the - * physical block mark won't (in general) appear on a byte boundary in - * the data. - */ - from_data = (p[0] >> bit) | (p[1] << (8 - bit)); - - /* Get the byte from the OOB. */ - from_oob = a[0]; - - /* Swap them. */ - a[0] = from_data; - - mask = (0x1 << bit) - 1; - p[0] = (p[0] & mask) | (from_oob << bit); - - mask = ~0 << bit; - p[1] = (p[1] & mask) | (from_oob >> (8 - bit)); -} - -static int gpmi_ecc_read_page_data(struct nand_chip *chip, - uint8_t *buf, int oob_required, - int page) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - struct bch_geometry *nfc_geo = &this->bch_geometry; - struct mtd_info *mtd = nand_to_mtd(chip); - void *payload_virt; - dma_addr_t payload_phys; - void *auxiliary_virt; - dma_addr_t auxiliary_phys; - unsigned int i; - unsigned char *status; - unsigned int max_bitflips = 0; - int ret; - - dev_dbg(this->dev, "page number is : %d\n", page); - ret = read_page_prepare(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - &payload_virt, &payload_phys); - if (ret) { - dev_err(this->dev, "Inadequate DMA buffer\n"); - ret = -ENOMEM; - return ret; - } - auxiliary_virt = this->auxiliary_virt; - auxiliary_phys = this->auxiliary_phys; - - /* go! */ - ret = gpmi_read_page(this, payload_phys, auxiliary_phys); - read_page_end(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); - if (ret) { - dev_err(this->dev, "Error in ECC-based read: %d\n", ret); - return ret; - } - - /* Loop over status bytes, accumulating ECC status. */ - status = auxiliary_virt + nfc_geo->auxiliary_status_offset; - - read_page_swap_end(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); - - for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { - if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) - continue; - - if (*status == STATUS_UNCORRECTABLE) { - int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; - u8 *eccbuf = this->raw_buffer; - int offset, bitoffset; - int eccbytes; - int flips; - - /* Read ECC bytes into our internal raw_buffer */ - offset = nfc_geo->metadata_size * 8; - offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1); - offset -= eccbits; - bitoffset = offset % 8; - eccbytes = DIV_ROUND_UP(offset + eccbits, 8); - offset /= 8; - eccbytes -= offset; - nand_change_read_column_op(chip, offset, eccbuf, - eccbytes, false); - - /* - * ECC data are not byte aligned and we may have - * in-band data in the first and last byte of - * eccbuf. Set non-eccbits to one so that - * nand_check_erased_ecc_chunk() does not count them - * as bitflips. - */ - if (bitoffset) - eccbuf[0] |= GENMASK(bitoffset - 1, 0); - - bitoffset = (bitoffset + eccbits) % 8; - if (bitoffset) - eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset); - - /* - * The ECC hardware has an uncorrectable ECC status - * code in case we have bitflips in an erased page. As - * nothing was written into this subpage the ECC is - * obviously wrong and we can not trust it. We assume - * at this point that we are reading an erased page and - * try to correct the bitflips in buffer up to - * ecc_strength bitflips. If this is a page with random - * data, we exceed this number of bitflips and have a - * ECC failure. Otherwise we use the corrected buffer. - */ - if (i == 0) { - /* The first block includes metadata */ - flips = nand_check_erased_ecc_chunk( - buf + i * nfc_geo->ecc_chunk_size, - nfc_geo->ecc_chunk_size, - eccbuf, eccbytes, - auxiliary_virt, - nfc_geo->metadata_size, - nfc_geo->ecc_strength); - } else { - flips = nand_check_erased_ecc_chunk( - buf + i * nfc_geo->ecc_chunk_size, - nfc_geo->ecc_chunk_size, - eccbuf, eccbytes, - NULL, 0, - nfc_geo->ecc_strength); - } - - if (flips > 0) { - max_bitflips = max_t(unsigned int, max_bitflips, - flips); - mtd->ecc_stats.corrected += flips; - continue; - } - - mtd->ecc_stats.failed++; - continue; - } - - mtd->ecc_stats.corrected += *status; - max_bitflips = max_t(unsigned int, max_bitflips, *status); - } - - /* handle the block mark swapping */ - block_mark_swapping(this, buf, auxiliary_virt); - - if (oob_required) { - /* - * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() - * for details about our policy for delivering the OOB. - * - * We fill the caller's buffer with set bits, and then copy the - * block mark to th caller's buffer. Note that, if block mark - * swapping was necessary, it has already been done, so we can - * rely on the first byte of the auxiliary buffer to contain - * the block mark. - */ - memset(chip->oob_poi, ~0, mtd->oobsize); - chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; - } - - return max_bitflips; -} - -static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - nand_read_page_op(chip, page, 0, NULL, 0); - - return gpmi_ecc_read_page_data(chip, buf, oob_required, page); -} - -/* Fake a virtual small page for the subpage read */ -static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offs, uint32_t len, uint8_t *buf, int page) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - void __iomem *bch_regs = this->resources.bch_regs; - struct bch_geometry old_geo = this->bch_geometry; - struct bch_geometry *geo = &this->bch_geometry; - int size = chip->ecc.size; /* ECC chunk size */ - int meta, n, page_size; - u32 r1_old, r2_old, r1_new, r2_new; - unsigned int max_bitflips; - int first, last, marker_pos; - int ecc_parity_size; - int col = 0; - int old_swap_block_mark = this->swap_block_mark; - - /* The size of ECC parity */ - ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; - - /* Align it with the chunk size */ - first = offs / size; - last = (offs + len - 1) / size; - - if (this->swap_block_mark) { - /* - * Find the chunk which contains the Block Marker. - * If this chunk is in the range of [first, last], - * we have to read out the whole page. - * Why? since we had swapped the data at the position of Block - * Marker to the metadata which is bound with the chunk 0. - */ - marker_pos = geo->block_mark_byte_offset / size; - if (last >= marker_pos && first <= marker_pos) { - dev_dbg(this->dev, - "page:%d, first:%d, last:%d, marker at:%d\n", - page, first, last, marker_pos); - return gpmi_ecc_read_page(mtd, chip, buf, 0, page); - } - } - - meta = geo->metadata_size; - if (first) { - col = meta + (size + ecc_parity_size) * first; - meta = 0; - buf = buf + first * size; - } - - nand_read_page_op(chip, page, col, NULL, 0); - - /* Save the old environment */ - r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0); - r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1); - - /* change the BCH registers and bch_geometry{} */ - n = last - first + 1; - page_size = meta + (size + ecc_parity_size) * n; - - r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS | - BM_BCH_FLASH0LAYOUT0_META_SIZE); - r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) - | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta); - writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0); - - r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE; - r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size); - writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1); - - geo->ecc_chunk_count = n; - geo->payload_size = n * size; - geo->page_size = page_size; - geo->auxiliary_status_offset = ALIGN(meta, 4); - - dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n", - page, offs, len, col, first, n, page_size); - - /* Read the subpage now */ - this->swap_block_mark = false; - max_bitflips = gpmi_ecc_read_page_data(chip, buf, 0, page); - - /* Restore */ - writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0); - writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1); - this->bch_geometry = old_geo; - this->swap_block_mark = old_swap_block_mark; - - return max_bitflips; -} - -static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - struct bch_geometry *nfc_geo = &this->bch_geometry; - const void *payload_virt; - dma_addr_t payload_phys; - const void *auxiliary_virt; - dma_addr_t auxiliary_phys; - int ret; - - dev_dbg(this->dev, "ecc write page.\n"); - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - if (this->swap_block_mark) { - /* - * If control arrives here, we're doing block mark swapping. - * Since we can't modify the caller's buffers, we must copy them - * into our own. - */ - memcpy(this->payload_virt, buf, mtd->writesize); - payload_virt = this->payload_virt; - payload_phys = this->payload_phys; - - memcpy(this->auxiliary_virt, chip->oob_poi, - nfc_geo->auxiliary_size); - auxiliary_virt = this->auxiliary_virt; - auxiliary_phys = this->auxiliary_phys; - - /* Handle block mark swapping. */ - block_mark_swapping(this, - (void *)payload_virt, (void *)auxiliary_virt); - } else { - /* - * If control arrives here, we're not doing block mark swapping, - * so we can to try and use the caller's buffers. - */ - ret = send_page_prepare(this, - buf, mtd->writesize, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - &payload_virt, &payload_phys); - if (ret) { - dev_err(this->dev, "Inadequate payload DMA buffer\n"); - return 0; - } - - ret = send_page_prepare(this, - chip->oob_poi, mtd->oobsize, - this->auxiliary_virt, this->auxiliary_phys, - nfc_geo->auxiliary_size, - &auxiliary_virt, &auxiliary_phys); - if (ret) { - dev_err(this->dev, "Inadequate auxiliary DMA buffer\n"); - goto exit_auxiliary; - } - } - - /* Ask the NFC. */ - ret = gpmi_send_page(this, payload_phys, auxiliary_phys); - if (ret) - dev_err(this->dev, "Error in ECC-based write: %d\n", ret); - - if (!this->swap_block_mark) { - send_page_end(this, chip->oob_poi, mtd->oobsize, - this->auxiliary_virt, this->auxiliary_phys, - nfc_geo->auxiliary_size, - auxiliary_virt, auxiliary_phys); -exit_auxiliary: - send_page_end(this, buf, mtd->writesize, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); - } - - if (ret) - return ret; - - return nand_prog_page_end_op(chip); -} - -/* - * There are several places in this driver where we have to handle the OOB and - * block marks. This is the function where things are the most complicated, so - * this is where we try to explain it all. All the other places refer back to - * here. - * - * These are the rules, in order of decreasing importance: - * - * 1) Nothing the caller does can be allowed to imperil the block mark. - * - * 2) In read operations, the first byte of the OOB we return must reflect the - * true state of the block mark, no matter where that block mark appears in - * the physical page. - * - * 3) ECC-based read operations return an OOB full of set bits (since we never - * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads - * return). - * - * 4) "Raw" read operations return a direct view of the physical bytes in the - * page, using the conventional definition of which bytes are data and which - * are OOB. This gives the caller a way to see the actual, physical bytes - * in the page, without the distortions applied by our ECC engine. - * - * - * What we do for this specific read operation depends on two questions: - * - * 1) Are we doing a "raw" read, or an ECC-based read? - * - * 2) Are we using block mark swapping or transcription? - * - * There are four cases, illustrated by the following Karnaugh map: - * - * | Raw | ECC-based | - * -------------+-------------------------+-------------------------+ - * | Read the conventional | | - * | OOB at the end of the | | - * Swapping | page and return it. It | | - * | contains exactly what | | - * | we want. | Read the block mark and | - * -------------+-------------------------+ return it in a buffer | - * | Read the conventional | full of set bits. | - * | OOB at the end of the | | - * | page and also the block | | - * Transcribing | mark in the metadata. | | - * | Copy the block mark | | - * | into the first byte of | | - * | the OOB. | | - * -------------+-------------------------+-------------------------+ - * - * Note that we break rule #4 in the Transcribing/Raw case because we're not - * giving an accurate view of the actual, physical bytes in the page (we're - * overwriting the block mark). That's OK because it's more important to follow - * rule #2. - * - * It turns out that knowing whether we want an "ECC-based" or "raw" read is not - * easy. When reading a page, for example, the NAND Flash MTD code calls our - * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an - * ECC-based or raw view of the page is implicit in which function it calls - * (there is a similar pair of ECC-based/raw functions for writing). - */ -static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - - dev_dbg(this->dev, "page number is %d\n", page); - /* clear the OOB buffer */ - memset(chip->oob_poi, ~0, mtd->oobsize); - - /* Read out the conventional OOB. */ - nand_read_page_op(chip, page, mtd->writesize, NULL, 0); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - /* - * Now, we want to make sure the block mark is correct. In the - * non-transcribing case (!GPMI_IS_MX23()), we already have it. - * Otherwise, we need to explicitly read it. - */ - if (GPMI_IS_MX23(this)) { - /* Read the block mark into the first byte of the OOB buffer. */ - nand_read_page_op(chip, page, 0, NULL, 0); - chip->oob_poi[0] = chip->read_byte(mtd); - } - - return 0; -} - -static int -gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) -{ - struct mtd_oob_region of = { }; - - /* Do we have available oob area? */ - mtd_ooblayout_free(mtd, 0, &of); - if (!of.length) - return -EPERM; - - if (!nand_is_slc(chip)) - return -EPERM; - - return nand_prog_page_op(chip, page, mtd->writesize + of.offset, - chip->oob_poi + of.offset, of.length); -} - -/* - * This function reads a NAND page without involving the ECC engine (no HW - * ECC correction). - * The tricky part in the GPMI/BCH controller is that it stores ECC bits - * inline (interleaved with payload DATA), and do not align data chunk on - * byte boundaries. - * We thus need to take care moving the payload data and ECC bits stored in the - * page into the provided buffers, which is why we're using gpmi_copy_bits. - * - * See set_geometry_by_ecc_info inline comments to have a full description - * of the layout used by the GPMI controller. - */ -static int gpmi_ecc_read_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - struct bch_geometry *nfc_geo = &this->bch_geometry; - int eccsize = nfc_geo->ecc_chunk_size; - int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; - u8 *tmp_buf = this->raw_buffer; - size_t src_bit_off; - size_t oob_bit_off; - size_t oob_byte_off; - uint8_t *oob = chip->oob_poi; - int step; - - nand_read_page_op(chip, page, 0, tmp_buf, - mtd->writesize + mtd->oobsize); - - /* - * If required, swap the bad block marker and the data stored in the - * metadata section, so that we don't wrongly consider a block as bad. - * - * See the layout description for a detailed explanation on why this - * is needed. - */ - if (this->swap_block_mark) - swap(tmp_buf[0], tmp_buf[mtd->writesize]); - - /* - * Copy the metadata section into the oob buffer (this section is - * guaranteed to be aligned on a byte boundary). - */ - if (oob_required) - memcpy(oob, tmp_buf, nfc_geo->metadata_size); - - oob_bit_off = nfc_geo->metadata_size * 8; - src_bit_off = oob_bit_off; - - /* Extract interleaved payload data and ECC bits */ - for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { - if (buf) - gpmi_copy_bits(buf, step * eccsize * 8, - tmp_buf, src_bit_off, - eccsize * 8); - src_bit_off += eccsize * 8; - - /* Align last ECC block to align a byte boundary */ - if (step == nfc_geo->ecc_chunk_count - 1 && - (oob_bit_off + eccbits) % 8) - eccbits += 8 - ((oob_bit_off + eccbits) % 8); - - if (oob_required) - gpmi_copy_bits(oob, oob_bit_off, - tmp_buf, src_bit_off, - eccbits); - - src_bit_off += eccbits; - oob_bit_off += eccbits; - } - - if (oob_required) { - oob_byte_off = oob_bit_off / 8; - - if (oob_byte_off < mtd->oobsize) - memcpy(oob + oob_byte_off, - tmp_buf + mtd->writesize + oob_byte_off, - mtd->oobsize - oob_byte_off); - } - - return 0; -} - -/* - * This function writes a NAND page without involving the ECC engine (no HW - * ECC generation). - * The tricky part in the GPMI/BCH controller is that it stores ECC bits - * inline (interleaved with payload DATA), and do not align data chunk on - * byte boundaries. - * We thus need to take care moving the OOB area at the right place in the - * final page, which is why we're using gpmi_copy_bits. - * - * See set_geometry_by_ecc_info inline comments to have a full description - * of the layout used by the GPMI controller. - */ -static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required, int page) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - struct bch_geometry *nfc_geo = &this->bch_geometry; - int eccsize = nfc_geo->ecc_chunk_size; - int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; - u8 *tmp_buf = this->raw_buffer; - uint8_t *oob = chip->oob_poi; - size_t dst_bit_off; - size_t oob_bit_off; - size_t oob_byte_off; - int step; - - /* - * Initialize all bits to 1 in case we don't have a buffer for the - * payload or oob data in order to leave unspecified bits of data - * to their initial state. - */ - if (!buf || !oob_required) - memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize); - - /* - * First copy the metadata section (stored in oob buffer) at the - * beginning of the page, as imposed by the GPMI layout. - */ - memcpy(tmp_buf, oob, nfc_geo->metadata_size); - oob_bit_off = nfc_geo->metadata_size * 8; - dst_bit_off = oob_bit_off; - - /* Interleave payload data and ECC bits */ - for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { - if (buf) - gpmi_copy_bits(tmp_buf, dst_bit_off, - buf, step * eccsize * 8, eccsize * 8); - dst_bit_off += eccsize * 8; - - /* Align last ECC block to align a byte boundary */ - if (step == nfc_geo->ecc_chunk_count - 1 && - (oob_bit_off + eccbits) % 8) - eccbits += 8 - ((oob_bit_off + eccbits) % 8); - - if (oob_required) - gpmi_copy_bits(tmp_buf, dst_bit_off, - oob, oob_bit_off, eccbits); - - dst_bit_off += eccbits; - oob_bit_off += eccbits; - } - - oob_byte_off = oob_bit_off / 8; - - if (oob_required && oob_byte_off < mtd->oobsize) - memcpy(tmp_buf + mtd->writesize + oob_byte_off, - oob + oob_byte_off, mtd->oobsize - oob_byte_off); - - /* - * If required, swap the bad block marker and the first byte of the - * metadata section, so that we don't modify the bad block marker. - * - * See the layout description for a detailed explanation on why this - * is needed. - */ - if (this->swap_block_mark) - swap(tmp_buf[0], tmp_buf[mtd->writesize]); - - return nand_prog_page_op(chip, page, 0, tmp_buf, - mtd->writesize + mtd->oobsize); -} - -static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - return gpmi_ecc_read_page_raw(mtd, chip, NULL, 1, page); -} - -static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1, page); -} - -static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - int ret = 0; - uint8_t *block_mark; - int column, page, chipnr; - - chipnr = (int)(ofs >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - column = !GPMI_IS_MX23(this) ? mtd->writesize : 0; - - /* Write the block mark. */ - block_mark = this->data_buffer_dma; - block_mark[0] = 0; /* bad block marker */ - - /* Shift to get page */ - page = (int)(ofs >> chip->page_shift); - - ret = nand_prog_page_op(chip, page, column, block_mark, 1); - - chip->select_chip(mtd, -1); - - return ret; -} - -static int nand_boot_set_geometry(struct gpmi_nand_data *this) -{ - struct boot_rom_geometry *geometry = &this->rom_geometry; - - /* - * Set the boot block stride size. - * - * In principle, we should be reading this from the OTP bits, since - * that's where the ROM is going to get it. In fact, we don't have any - * way to read the OTP bits, so we go with the default and hope for the - * best. - */ - geometry->stride_size_in_pages = 64; - - /* - * Set the search area stride exponent. - * - * In principle, we should be reading this from the OTP bits, since - * that's where the ROM is going to get it. In fact, we don't have any - * way to read the OTP bits, so we go with the default and hope for the - * best. - */ - geometry->search_area_stride_exponent = 2; - return 0; -} - -static const char *fingerprint = "STMP"; -static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) -{ - struct boot_rom_geometry *rom_geo = &this->rom_geometry; - struct device *dev = this->dev; - struct nand_chip *chip = &this->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - unsigned int search_area_size_in_strides; - unsigned int stride; - unsigned int page; - uint8_t *buffer = chip->data_buf; - int saved_chip_number; - int found_an_ncb_fingerprint = false; - - /* Compute the number of strides in a search area. */ - search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; - - saved_chip_number = this->current_chip; - chip->select_chip(mtd, 0); - - /* - * Loop through the first search area, looking for the NCB fingerprint. - */ - dev_dbg(dev, "Scanning for an NCB fingerprint...\n"); - - for (stride = 0; stride < search_area_size_in_strides; stride++) { - /* Compute the page addresses. */ - page = stride * rom_geo->stride_size_in_pages; - - dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page); - - /* - * Read the NCB fingerprint. The fingerprint is four bytes long - * and starts in the 12th byte of the page. - */ - nand_read_page_op(chip, page, 12, NULL, 0); - chip->read_buf(mtd, buffer, strlen(fingerprint)); - - /* Look for the fingerprint. */ - if (!memcmp(buffer, fingerprint, strlen(fingerprint))) { - found_an_ncb_fingerprint = true; - break; - } - - } - - chip->select_chip(mtd, saved_chip_number); - - if (found_an_ncb_fingerprint) - dev_dbg(dev, "\tFound a fingerprint\n"); - else - dev_dbg(dev, "\tNo fingerprint found\n"); - return found_an_ncb_fingerprint; -} - -/* Writes a transcription stamp. */ -static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) -{ - struct device *dev = this->dev; - struct boot_rom_geometry *rom_geo = &this->rom_geometry; - struct nand_chip *chip = &this->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - unsigned int block_size_in_pages; - unsigned int search_area_size_in_strides; - unsigned int search_area_size_in_pages; - unsigned int search_area_size_in_blocks; - unsigned int block; - unsigned int stride; - unsigned int page; - uint8_t *buffer = chip->data_buf; - int saved_chip_number; - int status; - - /* Compute the search area geometry. */ - block_size_in_pages = mtd->erasesize / mtd->writesize; - search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; - search_area_size_in_pages = search_area_size_in_strides * - rom_geo->stride_size_in_pages; - search_area_size_in_blocks = - (search_area_size_in_pages + (block_size_in_pages - 1)) / - block_size_in_pages; - - dev_dbg(dev, "Search Area Geometry :\n"); - dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks); - dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides); - dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages); - - /* Select chip 0. */ - saved_chip_number = this->current_chip; - chip->select_chip(mtd, 0); - - /* Loop over blocks in the first search area, erasing them. */ - dev_dbg(dev, "Erasing the search area...\n"); - - for (block = 0; block < search_area_size_in_blocks; block++) { - /* Erase this block. */ - dev_dbg(dev, "\tErasing block 0x%x\n", block); - status = nand_erase_op(chip, block); - if (status) - dev_err(dev, "[%s] Erase failed.\n", __func__); - } - - /* Write the NCB fingerprint into the page buffer. */ - memset(buffer, ~0, mtd->writesize); - memcpy(buffer + 12, fingerprint, strlen(fingerprint)); - - /* Loop through the first search area, writing NCB fingerprints. */ - dev_dbg(dev, "Writing NCB fingerprints...\n"); - for (stride = 0; stride < search_area_size_in_strides; stride++) { - /* Compute the page addresses. */ - page = stride * rom_geo->stride_size_in_pages; - - /* Write the first page of the current stride. */ - dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); - - status = chip->ecc.write_page_raw(mtd, chip, buffer, 0, page); - if (status) - dev_err(dev, "[%s] Write failed.\n", __func__); - } - - /* Deselect chip 0. */ - chip->select_chip(mtd, saved_chip_number); - return 0; -} - -static int mx23_boot_init(struct gpmi_nand_data *this) -{ - struct device *dev = this->dev; - struct nand_chip *chip = &this->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - unsigned int block_count; - unsigned int block; - int chipnr; - int page; - loff_t byte; - uint8_t block_mark; - int ret = 0; - - /* - * If control arrives here, we can't use block mark swapping, which - * means we're forced to use transcription. First, scan for the - * transcription stamp. If we find it, then we don't have to do - * anything -- the block marks are already transcribed. - */ - if (mx23_check_transcription_stamp(this)) - return 0; - - /* - * If control arrives here, we couldn't find a transcription stamp, so - * so we presume the block marks are in the conventional location. - */ - dev_dbg(dev, "Transcribing bad block marks...\n"); - - /* Compute the number of blocks in the entire medium. */ - block_count = chip->chipsize >> chip->phys_erase_shift; - - /* - * Loop over all the blocks in the medium, transcribing block marks as - * we go. - */ - for (block = 0; block < block_count; block++) { - /* - * Compute the chip, page and byte addresses for this block's - * conventional mark. - */ - chipnr = block >> (chip->chip_shift - chip->phys_erase_shift); - page = block << (chip->phys_erase_shift - chip->page_shift); - byte = block << chip->phys_erase_shift; - - /* Send the command to read the conventional block mark. */ - chip->select_chip(mtd, chipnr); - nand_read_page_op(chip, page, mtd->writesize, NULL, 0); - block_mark = chip->read_byte(mtd); - chip->select_chip(mtd, -1); - - /* - * Check if the block is marked bad. If so, we need to mark it - * again, but this time the result will be a mark in the - * location where we transcribe block marks. - */ - if (block_mark != 0xff) { - dev_dbg(dev, "Transcribing mark in block %u\n", block); - ret = chip->block_markbad(mtd, byte); - if (ret) - dev_err(dev, - "Failed to mark block bad with ret %d\n", - ret); - } - } - - /* Write the stamp that indicates we've transcribed the block marks. */ - mx23_write_transcription_stamp(this); - return 0; -} - -static int nand_boot_init(struct gpmi_nand_data *this) -{ - nand_boot_set_geometry(this); - - /* This is ROM arch-specific initilization before the BBT scanning. */ - if (GPMI_IS_MX23(this)) - return mx23_boot_init(this); - return 0; -} - -static int gpmi_set_geometry(struct gpmi_nand_data *this) -{ - int ret; - - /* Free the temporary DMA memory for reading ID. */ - gpmi_free_dma_buffer(this); - - /* Set up the NFC geometry which is used by BCH. */ - ret = bch_set_geometry(this); - if (ret) { - dev_err(this->dev, "Error setting BCH geometry : %d\n", ret); - return ret; - } - - /* Alloc the new DMA buffers according to the pagesize and oobsize */ - return gpmi_alloc_dma_buffer(this); -} - -static int gpmi_init_last(struct gpmi_nand_data *this) -{ - struct nand_chip *chip = &this->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - struct bch_geometry *bch_geo = &this->bch_geometry; - int ret; - - /* Set up the medium geometry */ - ret = gpmi_set_geometry(this); - if (ret) - return ret; - - /* Init the nand_ecc_ctrl{} */ - ecc->read_page = gpmi_ecc_read_page; - ecc->write_page = gpmi_ecc_write_page; - ecc->read_oob = gpmi_ecc_read_oob; - ecc->write_oob = gpmi_ecc_write_oob; - ecc->read_page_raw = gpmi_ecc_read_page_raw; - ecc->write_page_raw = gpmi_ecc_write_page_raw; - ecc->read_oob_raw = gpmi_ecc_read_oob_raw; - ecc->write_oob_raw = gpmi_ecc_write_oob_raw; - ecc->mode = NAND_ECC_HW; - ecc->size = bch_geo->ecc_chunk_size; - ecc->strength = bch_geo->ecc_strength; - mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); - - /* - * We only enable the subpage read when: - * (1) the chip is imx6, and - * (2) the size of the ECC parity is byte aligned. - */ - if (GPMI_IS_MX6(this) && - ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) { - ecc->read_subpage = gpmi_ecc_read_subpage; - chip->options |= NAND_SUBPAGE_READ; - } - - /* - * Can we enable the extra features? such as EDO or Sync mode. - * - * We do not check the return value now. That's means if we fail in - * enable the extra features, we still can run in the normal way. - */ - gpmi_extra_init(this); - - return 0; -} - -static int gpmi_nand_init(struct gpmi_nand_data *this) -{ - struct nand_chip *chip = &this->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - /* init current chip */ - this->current_chip = -1; - - /* init the MTD data structures */ - mtd->name = "gpmi-nand"; - mtd->dev.parent = this->dev; - - /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ - nand_set_controller_data(chip, this); - nand_set_flash_node(chip, this->pdev->dev.of_node); - chip->select_chip = gpmi_select_chip; - chip->cmd_ctrl = gpmi_cmd_ctrl; - chip->dev_ready = gpmi_dev_ready; - chip->read_byte = gpmi_read_byte; - chip->read_buf = gpmi_read_buf; - chip->write_buf = gpmi_write_buf; - chip->badblock_pattern = &gpmi_bbt_descr; - chip->block_markbad = gpmi_block_markbad; - chip->options |= NAND_NO_SUBPAGE_WRITE; - - /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ - this->swap_block_mark = !GPMI_IS_MX23(this); - - /* - * Allocate a temporary DMA buffer for reading ID in the - * nand_scan_ident(). - */ - this->bch_geometry.payload_size = 1024; - this->bch_geometry.auxiliary_size = 128; - ret = gpmi_alloc_dma_buffer(this); - if (ret) - goto err_out; - - ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL); - if (ret) - goto err_out; - - if (chip->bbt_options & NAND_BBT_USE_FLASH) { - chip->bbt_options |= NAND_BBT_NO_OOB; - - if (of_property_read_bool(this->dev->of_node, - "fsl,no-blockmark-swap")) - this->swap_block_mark = false; - } - dev_dbg(this->dev, "Blockmark swapping %sabled\n", - this->swap_block_mark ? "en" : "dis"); - - ret = gpmi_init_last(this); - if (ret) - goto err_out; - - chip->options |= NAND_SKIP_BBTSCAN; - ret = nand_scan_tail(mtd); - if (ret) - goto err_out; - - ret = nand_boot_init(this); - if (ret) - goto err_nand_cleanup; - ret = chip->scan_bbt(mtd); - if (ret) - goto err_nand_cleanup; - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) - goto err_nand_cleanup; - return 0; - -err_nand_cleanup: - nand_cleanup(chip); -err_out: - gpmi_free_dma_buffer(this); - return ret; -} - -static const struct of_device_id gpmi_nand_id_table[] = { - { - .compatible = "fsl,imx23-gpmi-nand", - .data = &gpmi_devdata_imx23, - }, { - .compatible = "fsl,imx28-gpmi-nand", - .data = &gpmi_devdata_imx28, - }, { - .compatible = "fsl,imx6q-gpmi-nand", - .data = &gpmi_devdata_imx6q, - }, { - .compatible = "fsl,imx6sx-gpmi-nand", - .data = &gpmi_devdata_imx6sx, - }, { - .compatible = "fsl,imx7d-gpmi-nand", - .data = &gpmi_devdata_imx7d, - }, {} -}; -MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); - -static int gpmi_nand_probe(struct platform_device *pdev) -{ - struct gpmi_nand_data *this; - const struct of_device_id *of_id; - int ret; - - this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); - if (!this) - return -ENOMEM; - - of_id = of_match_device(gpmi_nand_id_table, &pdev->dev); - if (of_id) { - this->devdata = of_id->data; - } else { - dev_err(&pdev->dev, "Failed to find the right device id.\n"); - return -ENODEV; - } - - platform_set_drvdata(pdev, this); - this->pdev = pdev; - this->dev = &pdev->dev; - - ret = acquire_resources(this); - if (ret) - goto exit_acquire_resources; - - ret = init_hardware(this); - if (ret) - goto exit_nfc_init; - - ret = gpmi_nand_init(this); - if (ret) - goto exit_nfc_init; - - dev_info(this->dev, "driver registered.\n"); - - return 0; - -exit_nfc_init: - release_resources(this); -exit_acquire_resources: - - return ret; -} - -static int gpmi_nand_remove(struct platform_device *pdev) -{ - struct gpmi_nand_data *this = platform_get_drvdata(pdev); - - nand_release(nand_to_mtd(&this->nand)); - gpmi_free_dma_buffer(this); - release_resources(this); - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int gpmi_pm_suspend(struct device *dev) -{ - struct gpmi_nand_data *this = dev_get_drvdata(dev); - - release_dma_channels(this); - return 0; -} - -static int gpmi_pm_resume(struct device *dev) -{ - struct gpmi_nand_data *this = dev_get_drvdata(dev); - int ret; - - ret = acquire_dma_channels(this); - if (ret < 0) - return ret; - - /* re-init the GPMI registers */ - this->flags &= ~GPMI_TIMING_INIT_OK; - ret = gpmi_init(this); - if (ret) { - dev_err(this->dev, "Error setting GPMI : %d\n", ret); - return ret; - } - - /* re-init the BCH registers */ - ret = bch_set_geometry(this); - if (ret) { - dev_err(this->dev, "Error setting BCH : %d\n", ret); - return ret; - } - - /* re-init others */ - gpmi_extra_init(this); - - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -static const struct dev_pm_ops gpmi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume) -}; - -static struct platform_driver gpmi_nand_driver = { - .driver = { - .name = "gpmi-nand", - .pm = &gpmi_pm_ops, - .of_match_table = gpmi_nand_id_table, - }, - .probe = gpmi_nand_probe, - .remove = gpmi_nand_remove, -}; -module_platform_driver(gpmi_nand_driver); - -MODULE_AUTHOR("Freescale Semiconductor, Inc."); -MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h deleted file mode 100644 index 06c1f993912c..000000000000 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Freescale GPMI NAND Flash Driver - * - * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. - * Copyright (C) 2008 Embedded Alley Solutions, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H -#define __DRIVERS_MTD_NAND_GPMI_NAND_H - -#include -#include -#include -#include - -#define GPMI_CLK_MAX 5 /* MX6Q needs five clocks */ -struct resources { - void __iomem *gpmi_regs; - void __iomem *bch_regs; - unsigned int dma_low_channel; - unsigned int dma_high_channel; - struct clk *clock[GPMI_CLK_MAX]; -}; - -/** - * struct bch_geometry - BCH geometry description. - * @gf_len: The length of Galois Field. (e.g., 13 or 14) - * @ecc_strength: A number that describes the strength of the ECC - * algorithm. - * @page_size: The size, in bytes, of a physical page, including - * both data and OOB. - * @metadata_size: The size, in bytes, of the metadata. - * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note - * the first chunk in the page includes both data and - * metadata, so it's a bit larger than this value. - * @ecc_chunk_count: The number of ECC chunks in the page, - * @payload_size: The size, in bytes, of the payload buffer. - * @auxiliary_size: The size, in bytes, of the auxiliary buffer. - * @auxiliary_status_offset: The offset into the auxiliary buffer at which - * the ECC status appears. - * @block_mark_byte_offset: The byte offset in the ECC-based page view at - * which the underlying physical block mark appears. - * @block_mark_bit_offset: The bit offset into the ECC-based page view at - * which the underlying physical block mark appears. - */ -struct bch_geometry { - unsigned int gf_len; - unsigned int ecc_strength; - unsigned int page_size; - unsigned int metadata_size; - unsigned int ecc_chunk_size; - unsigned int ecc_chunk_count; - unsigned int payload_size; - unsigned int auxiliary_size; - unsigned int auxiliary_status_offset; - unsigned int block_mark_byte_offset; - unsigned int block_mark_bit_offset; -}; - -/** - * struct boot_rom_geometry - Boot ROM geometry description. - * @stride_size_in_pages: The size of a boot block stride, in pages. - * @search_area_stride_exponent: The logarithm to base 2 of the size of a - * search area in boot block strides. - */ -struct boot_rom_geometry { - unsigned int stride_size_in_pages; - unsigned int search_area_stride_exponent; -}; - -/* DMA operations types */ -enum dma_ops_type { - DMA_FOR_COMMAND = 1, - DMA_FOR_READ_DATA, - DMA_FOR_WRITE_DATA, - DMA_FOR_READ_ECC_PAGE, - DMA_FOR_WRITE_ECC_PAGE -}; - -/** - * struct nand_timing - Fundamental timing attributes for NAND. - * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the - * maximum of tDS and tWP. A negative value - * indicates this characteristic isn't known. - * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the - * maximum of tDH, tWH and tREH. A negative value - * indicates this characteristic isn't known. - * @address_setup_in_ns: The address setup time, in nanoseconds. Usually - * the maximum of tCLS, tCS and tALS. A negative - * value indicates this characteristic isn't known. - * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value - * indicates this characteristic isn't known. - * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A - * negative value indicates this characteristic isn't - * known. - * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A - * negative value indicates this characteristic isn't - * known. - * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A - * negative value indicates this characteristic isn't - * known. - */ -struct nand_timing { - int8_t data_setup_in_ns; - int8_t data_hold_in_ns; - int8_t address_setup_in_ns; - int8_t gpmi_sample_delay_in_ns; - int8_t tREA_in_ns; - int8_t tRLOH_in_ns; - int8_t tRHOH_in_ns; -}; - -enum gpmi_type { - IS_MX23, - IS_MX28, - IS_MX6Q, - IS_MX6SX, - IS_MX7D, -}; - -struct gpmi_devdata { - enum gpmi_type type; - int bch_max_ecc_strength; - int max_chain_delay; /* See the async EDO mode */ - const char * const *clks; - const int clks_count; -}; - -struct gpmi_nand_data { - /* flags */ -#define GPMI_ASYNC_EDO_ENABLED (1 << 0) -#define GPMI_TIMING_INIT_OK (1 << 1) - int flags; - const struct gpmi_devdata *devdata; - - /* System Interface */ - struct device *dev; - struct platform_device *pdev; - - /* Resources */ - struct resources resources; - - /* Flash Hardware */ - struct nand_timing timing; - int timing_mode; - - /* BCH */ - struct bch_geometry bch_geometry; - struct completion bch_done; - - /* NAND Boot issue */ - bool swap_block_mark; - struct boot_rom_geometry rom_geometry; - - /* MTD / NAND */ - struct nand_chip nand; - - /* General-use Variables */ - int current_chip; - unsigned int command_length; - - /* passed from upper layer */ - uint8_t *upper_buf; - int upper_len; - - /* for DMA operations */ - bool direct_dma_map_ok; - - struct scatterlist cmd_sgl; - char *cmd_buffer; - - struct scatterlist data_sgl; - char *data_buffer_dma; - - void *page_buffer_virt; - dma_addr_t page_buffer_phys; - unsigned int page_buffer_size; - - void *payload_virt; - dma_addr_t payload_phys; - - void *auxiliary_virt; - dma_addr_t auxiliary_phys; - - void *raw_buffer; - - /* DMA channels */ -#define DMA_CHANS 8 - struct dma_chan *dma_chans[DMA_CHANS]; - enum dma_ops_type last_dma_type; - enum dma_ops_type dma_type; - struct completion dma_done; - - /* private */ - void *private; -}; - -/** - * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters. - * @data_setup_in_cycles: The data setup time, in cycles. - * @data_hold_in_cycles: The data hold time, in cycles. - * @address_setup_in_cycles: The address setup time, in cycles. - * @device_busy_timeout: The timeout waiting for NAND Ready/Busy, - * this value is the number of cycles multiplied - * by 4096. - * @use_half_periods: Indicates the clock is running slowly, so the - * NFC DLL should use half-periods. - * @sample_delay_factor: The sample delay factor. - * @wrn_dly_sel: The delay on the GPMI write strobe. - */ -struct gpmi_nfc_hardware_timing { - /* for HW_GPMI_TIMING0 */ - uint8_t data_setup_in_cycles; - uint8_t data_hold_in_cycles; - uint8_t address_setup_in_cycles; - - /* for HW_GPMI_TIMING1 */ - uint16_t device_busy_timeout; -#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/ - - /* for HW_GPMI_CTRL1 */ - bool use_half_periods; - uint8_t sample_delay_factor; - uint8_t wrn_dly_sel; -}; - -/** - * struct timing_threshold - Timing threshold - * @max_data_setup_cycles: The maximum number of data setup cycles that - * can be expressed in the hardware. - * @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires - * for data read internal setup. In the Reference - * Manual, see the chapter "High-Speed NAND - * Timing" for more details. - * @max_sample_delay_factor: The maximum sample delay factor that can be - * expressed in the hardware. - * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the - * sample delay DLL hardware can possibly work - * with (the DLL is unusable with longer periods). - * If the full-cycle period is greater than HALF - * this value, the DLL must be configured to use - * half-periods. - * @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the - * DLL can implement. - * @clock_frequency_in_hz: The clock frequency, in Hz, during the current - * I/O transaction. If no I/O transaction is in - * progress, this is the clock frequency during - * the most recent I/O transaction. - */ -struct timing_threshold { - const unsigned int max_chip_count; - const unsigned int max_data_setup_cycles; - const unsigned int internal_data_setup_in_ns; - const unsigned int max_sample_delay_factor; - const unsigned int max_dll_clock_period_in_ns; - const unsigned int max_dll_delay_in_ns; - unsigned long clock_frequency_in_hz; - -}; - -/* Common Services */ -int common_nfc_set_geometry(struct gpmi_nand_data *); -struct dma_chan *get_dma_chan(struct gpmi_nand_data *); -void prepare_data_dma(struct gpmi_nand_data *, - enum dma_data_direction dr); -int start_dma_without_bch_irq(struct gpmi_nand_data *, - struct dma_async_tx_descriptor *); -int start_dma_with_bch_irq(struct gpmi_nand_data *, - struct dma_async_tx_descriptor *); - -/* GPMI-NAND helper function library */ -int gpmi_init(struct gpmi_nand_data *); -int gpmi_extra_init(struct gpmi_nand_data *); -void gpmi_clear_bch(struct gpmi_nand_data *); -void gpmi_dump_info(struct gpmi_nand_data *); -int bch_set_geometry(struct gpmi_nand_data *); -int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip); -int gpmi_send_command(struct gpmi_nand_data *); -void gpmi_begin(struct gpmi_nand_data *); -void gpmi_end(struct gpmi_nand_data *); -int gpmi_read_data(struct gpmi_nand_data *); -int gpmi_send_data(struct gpmi_nand_data *); -int gpmi_send_page(struct gpmi_nand_data *, - dma_addr_t payload, dma_addr_t auxiliary); -int gpmi_read_page(struct gpmi_nand_data *, - dma_addr_t payload, dma_addr_t auxiliary); - -void gpmi_copy_bits(u8 *dst, size_t dst_bit_off, - const u8 *src, size_t src_bit_off, - size_t nbits); - -/* BCH : Status Block Completion Codes */ -#define STATUS_GOOD 0x00 -#define STATUS_ERASED 0xff -#define STATUS_UNCORRECTABLE 0xfe - -/* Use the devdata to distinguish different Archs. */ -#define GPMI_IS_MX23(x) ((x)->devdata->type == IS_MX23) -#define GPMI_IS_MX28(x) ((x)->devdata->type == IS_MX28) -#define GPMI_IS_MX6Q(x) ((x)->devdata->type == IS_MX6Q) -#define GPMI_IS_MX6SX(x) ((x)->devdata->type == IS_MX6SX) -#define GPMI_IS_MX7D(x) ((x)->devdata->type == IS_MX7D) - -#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x) || \ - GPMI_IS_MX7D(x)) -#endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h deleted file mode 100644 index 82114cdc8330..000000000000 --- a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Freescale GPMI NAND Flash Driver - * - * Copyright 2008-2011 Freescale Semiconductor, Inc. - * Copyright 2008 Embedded Alley Solutions, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#ifndef __GPMI_NAND_GPMI_REGS_H -#define __GPMI_NAND_GPMI_REGS_H - -#define HW_GPMI_CTRL0 0x00000000 -#define HW_GPMI_CTRL0_SET 0x00000004 -#define HW_GPMI_CTRL0_CLR 0x00000008 -#define HW_GPMI_CTRL0_TOG 0x0000000c - -#define BP_GPMI_CTRL0_COMMAND_MODE 24 -#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE) -#define BF_GPMI_CTRL0_COMMAND_MODE(v) \ - (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE) -#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0 -#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1 -#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2 -#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3 - -#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23) -#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0 -#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1 - -/* - * Difference in LOCK_CS between imx23 and imx28 : - * This bit may impact the _POWER_ consumption. So some chips - * do not set it. - */ -#define MX23_BP_GPMI_CTRL0_LOCK_CS 22 -#define MX28_BP_GPMI_CTRL0_LOCK_CS 27 -#define LOCK_CS_ENABLE 0x1 -#define BF_GPMI_CTRL0_LOCK_CS(v, x) 0x0 - -/* Difference in CS between imx23 and imx28 */ -#define BP_GPMI_CTRL0_CS 20 -#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS) -#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS) -#define BF_GPMI_CTRL0_CS(v, x) (((v) << BP_GPMI_CTRL0_CS) & \ - (GPMI_IS_MX23((x)) \ - ? MX23_BM_GPMI_CTRL0_CS \ - : MX28_BM_GPMI_CTRL0_CS)) - -#define BP_GPMI_CTRL0_ADDRESS 17 -#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS) -#define BF_GPMI_CTRL0_ADDRESS(v) \ - (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS) -#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0 -#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1 -#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2 - -#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) -#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0 -#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1 - -#define BP_GPMI_CTRL0_XFER_COUNT 0 -#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT) -#define BF_GPMI_CTRL0_XFER_COUNT(v) \ - (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT) - -#define HW_GPMI_COMPARE 0x00000010 - -#define HW_GPMI_ECCCTRL 0x00000020 -#define HW_GPMI_ECCCTRL_SET 0x00000024 -#define HW_GPMI_ECCCTRL_CLR 0x00000028 -#define HW_GPMI_ECCCTRL_TOG 0x0000002c - -#define BP_GPMI_ECCCTRL_ECC_CMD 13 -#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD) -#define BF_GPMI_ECCCTRL_ECC_CMD(v) \ - (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD) -#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0 -#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1 - -#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12) -#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1 -#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0 - -#define BP_GPMI_ECCCTRL_BUFFER_MASK 0 -#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK) -#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \ - (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK) -#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 -#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF - -#define HW_GPMI_ECCCOUNT 0x00000030 -#define HW_GPMI_PAYLOAD 0x00000040 -#define HW_GPMI_AUXILIARY 0x00000050 -#define HW_GPMI_CTRL1 0x00000060 -#define HW_GPMI_CTRL1_SET 0x00000064 -#define HW_GPMI_CTRL1_CLR 0x00000068 -#define HW_GPMI_CTRL1_TOG 0x0000006c - -#define BP_GPMI_CTRL1_DECOUPLE_CS 24 -#define BM_GPMI_CTRL1_DECOUPLE_CS (1 << BP_GPMI_CTRL1_DECOUPLE_CS) - -#define BP_GPMI_CTRL1_WRN_DLY_SEL 22 -#define BM_GPMI_CTRL1_WRN_DLY_SEL (0x3 << BP_GPMI_CTRL1_WRN_DLY_SEL) -#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \ - (((v) << BP_GPMI_CTRL1_WRN_DLY_SEL) & BM_GPMI_CTRL1_WRN_DLY_SEL) -#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0 -#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1 -#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2 -#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3 - -#define BM_GPMI_CTRL1_BCH_MODE (1 << 18) - -#define BP_GPMI_CTRL1_DLL_ENABLE 17 -#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE) - -#define BP_GPMI_CTRL1_HALF_PERIOD 16 -#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD) - -#define BP_GPMI_CTRL1_RDN_DELAY 12 -#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY) -#define BF_GPMI_CTRL1_RDN_DELAY(v) \ - (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY) - -#define BM_GPMI_CTRL1_DEV_RESET (1 << 3) -#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 -#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 - -#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) -#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 -#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 - -#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1) -#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 -#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 - -#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) - -#define HW_GPMI_TIMING0 0x00000070 - -#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 -#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << BP_GPMI_TIMING0_ADDRESS_SETUP) -#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \ - (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP) - -#define BP_GPMI_TIMING0_DATA_HOLD 8 -#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD) -#define BF_GPMI_TIMING0_DATA_HOLD(v) \ - (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD) - -#define BP_GPMI_TIMING0_DATA_SETUP 0 -#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP) -#define BF_GPMI_TIMING0_DATA_SETUP(v) \ - (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP) - -#define HW_GPMI_TIMING1 0x00000080 -#define BP_GPMI_TIMING1_BUSY_TIMEOUT 16 -#define BM_GPMI_TIMING1_BUSY_TIMEOUT (0xffff << BP_GPMI_TIMING1_BUSY_TIMEOUT) -#define BF_GPMI_TIMING1_BUSY_TIMEOUT(v) \ - (((v) << BP_GPMI_TIMING1_BUSY_TIMEOUT) & BM_GPMI_TIMING1_BUSY_TIMEOUT) - -#define HW_GPMI_TIMING2 0x00000090 -#define HW_GPMI_DATA 0x000000a0 - -/* MX28 uses this to detect READY. */ -#define HW_GPMI_STAT 0x000000b0 -#define MX28_BP_GPMI_STAT_READY_BUSY 24 -#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << MX28_BP_GPMI_STAT_READY_BUSY) -#define MX28_BF_GPMI_STAT_READY_BUSY(v) \ - (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY) - -/* MX23 uses this to detect READY. */ -#define HW_GPMI_DEBUG 0x000000c0 -#define MX23_BP_GPMI_DEBUG_READY0 28 -#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0) -#endif diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c deleted file mode 100644 index cb862793ab6d..000000000000 --- a/drivers/mtd/nand/hisi504_nand.c +++ /dev/null @@ -1,896 +0,0 @@ -/* - * Hisilicon NAND Flash controller driver - * - * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd. - * http://www.hisilicon.com - * - * Author: Zhou Wang - * The initial developer of the original code is Zhiyong Cai - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define HINFC504_MAX_CHIP (4) -#define HINFC504_W_LATCH (5) -#define HINFC504_R_LATCH (7) -#define HINFC504_RW_LATCH (3) - -#define HINFC504_NFC_TIMEOUT (2 * HZ) -#define HINFC504_NFC_PM_TIMEOUT (1 * HZ) -#define HINFC504_NFC_DMA_TIMEOUT (5 * HZ) -#define HINFC504_CHIP_DELAY (25) - -#define HINFC504_REG_BASE_ADDRESS_LEN (0x100) -#define HINFC504_BUFFER_BASE_ADDRESS_LEN (2048 + 128) - -#define HINFC504_ADDR_CYCLE_MASK 0x4 - -#define HINFC504_CON 0x00 -#define HINFC504_CON_OP_MODE_NORMAL BIT(0) -#define HINFC504_CON_PAGEISZE_SHIFT (1) -#define HINFC504_CON_PAGESIZE_MASK (0x07) -#define HINFC504_CON_BUS_WIDTH BIT(4) -#define HINFC504_CON_READY_BUSY_SEL BIT(8) -#define HINFC504_CON_ECCTYPE_SHIFT (9) -#define HINFC504_CON_ECCTYPE_MASK (0x07) - -#define HINFC504_PWIDTH 0x04 -#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \ - ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8)) - -#define HINFC504_CMD 0x0C -#define HINFC504_ADDRL 0x10 -#define HINFC504_ADDRH 0x14 -#define HINFC504_DATA_NUM 0x18 - -#define HINFC504_OP 0x1C -#define HINFC504_OP_READ_DATA_EN BIT(1) -#define HINFC504_OP_WAIT_READY_EN BIT(2) -#define HINFC504_OP_CMD2_EN BIT(3) -#define HINFC504_OP_WRITE_DATA_EN BIT(4) -#define HINFC504_OP_ADDR_EN BIT(5) -#define HINFC504_OP_CMD1_EN BIT(6) -#define HINFC504_OP_NF_CS_SHIFT (7) -#define HINFC504_OP_NF_CS_MASK (3) -#define HINFC504_OP_ADDR_CYCLE_SHIFT (9) -#define HINFC504_OP_ADDR_CYCLE_MASK (7) - -#define HINFC504_STATUS 0x20 -#define HINFC504_READY BIT(0) - -#define HINFC504_INTEN 0x24 -#define HINFC504_INTEN_DMA BIT(9) -#define HINFC504_INTEN_UE BIT(6) -#define HINFC504_INTEN_CE BIT(5) - -#define HINFC504_INTS 0x28 -#define HINFC504_INTS_DMA BIT(9) -#define HINFC504_INTS_UE BIT(6) -#define HINFC504_INTS_CE BIT(5) - -#define HINFC504_INTCLR 0x2C -#define HINFC504_INTCLR_DMA BIT(9) -#define HINFC504_INTCLR_UE BIT(6) -#define HINFC504_INTCLR_CE BIT(5) - -#define HINFC504_ECC_STATUS 0x5C -#define HINFC504_ECC_16_BIT_SHIFT 12 - -#define HINFC504_DMA_CTRL 0x60 -#define HINFC504_DMA_CTRL_DMA_START BIT(0) -#define HINFC504_DMA_CTRL_WE BIT(1) -#define HINFC504_DMA_CTRL_DATA_AREA_EN BIT(2) -#define HINFC504_DMA_CTRL_OOB_AREA_EN BIT(3) -#define HINFC504_DMA_CTRL_BURST4_EN BIT(4) -#define HINFC504_DMA_CTRL_BURST8_EN BIT(5) -#define HINFC504_DMA_CTRL_BURST16_EN BIT(6) -#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT (7) -#define HINFC504_DMA_CTRL_ADDR_NUM_MASK (1) -#define HINFC504_DMA_CTRL_CS_SHIFT (8) -#define HINFC504_DMA_CTRL_CS_MASK (0x03) - -#define HINFC504_DMA_ADDR_DATA 0x64 -#define HINFC504_DMA_ADDR_OOB 0x68 - -#define HINFC504_DMA_LEN 0x6C -#define HINFC504_DMA_LEN_OOB_SHIFT (16) -#define HINFC504_DMA_LEN_OOB_MASK (0xFFF) - -#define HINFC504_DMA_PARA 0x70 -#define HINFC504_DMA_PARA_DATA_RW_EN BIT(0) -#define HINFC504_DMA_PARA_OOB_RW_EN BIT(1) -#define HINFC504_DMA_PARA_DATA_EDC_EN BIT(2) -#define HINFC504_DMA_PARA_OOB_EDC_EN BIT(3) -#define HINFC504_DMA_PARA_DATA_ECC_EN BIT(4) -#define HINFC504_DMA_PARA_OOB_ECC_EN BIT(5) - -#define HINFC_VERSION 0x74 -#define HINFC504_LOG_READ_ADDR 0x7C -#define HINFC504_LOG_READ_LEN 0x80 - -#define HINFC504_NANDINFO_LEN 0x10 - -struct hinfc_host { - struct nand_chip chip; - struct device *dev; - void __iomem *iobase; - void __iomem *mmio; - struct completion cmd_complete; - unsigned int offset; - unsigned int command; - int chipselect; - unsigned int addr_cycle; - u32 addr_value[2]; - u32 cache_addr_value[2]; - char *buffer; - dma_addr_t dma_buffer; - dma_addr_t dma_oob; - int version; - unsigned int irq_status; /* interrupt status */ -}; - -static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg) -{ - return readl(host->iobase + reg); -} - -static inline void hinfc_write(struct hinfc_host *host, unsigned int value, - unsigned int reg) -{ - writel(value, host->iobase + reg); -} - -static void wait_controller_finished(struct hinfc_host *host) -{ - unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT; - int val; - - while (time_before(jiffies, timeout)) { - val = hinfc_read(host, HINFC504_STATUS); - if (host->command == NAND_CMD_ERASE2) { - /* nfc is ready */ - while (!(val & HINFC504_READY)) { - usleep_range(500, 1000); - val = hinfc_read(host, HINFC504_STATUS); - } - return; - } - - if (val & HINFC504_READY) - return; - } - - /* wait cmd timeout */ - dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n"); -} - -static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev) -{ - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - unsigned long val; - int ret; - - hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA); - hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB); - - if (chip->ecc.mode == NAND_ECC_NONE) { - hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK) - << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN); - - hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN - | HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA); - } else { - if (host->command == NAND_CMD_READOOB) - hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN - | HINFC504_DMA_PARA_OOB_EDC_EN - | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); - else - hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN - | HINFC504_DMA_PARA_OOB_RW_EN - | HINFC504_DMA_PARA_DATA_EDC_EN - | HINFC504_DMA_PARA_OOB_EDC_EN - | HINFC504_DMA_PARA_DATA_ECC_EN - | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); - - } - - val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN - | HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN - | HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN - | ((host->addr_cycle == 4 ? 1 : 0) - << HINFC504_DMA_CTRL_ADDR_NUM_SHIFT) - | ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK) - << HINFC504_DMA_CTRL_CS_SHIFT)); - - if (todev) - val |= HINFC504_DMA_CTRL_WE; - - init_completion(&host->cmd_complete); - - hinfc_write(host, val, HINFC504_DMA_CTRL); - ret = wait_for_completion_timeout(&host->cmd_complete, - HINFC504_NFC_DMA_TIMEOUT); - - if (!ret) { - dev_err(host->dev, "DMA operation(irq) timeout!\n"); - /* sanity check */ - val = hinfc_read(host, HINFC504_DMA_CTRL); - if (!(val & HINFC504_DMA_CTRL_DMA_START)) - dev_err(host->dev, "DMA is already done but without irq ACK!\n"); - else - dev_err(host->dev, "DMA is really timeout!\n"); - } -} - -static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host) -{ - host->addr_value[0] &= 0xffff0000; - - hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); - hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); - hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN, - HINFC504_CMD); - - hisi_nfc_dma_transfer(host, 1); - - return 0; -} - -static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host) -{ - struct mtd_info *mtd = nand_to_mtd(&host->chip); - - if ((host->addr_value[0] == host->cache_addr_value[0]) && - (host->addr_value[1] == host->cache_addr_value[1])) - return 0; - - host->addr_value[0] &= 0xffff0000; - - hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); - hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); - hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0, - HINFC504_CMD); - - hinfc_write(host, 0, HINFC504_LOG_READ_ADDR); - hinfc_write(host, mtd->writesize + mtd->oobsize, - HINFC504_LOG_READ_LEN); - - hisi_nfc_dma_transfer(host, 0); - - host->cache_addr_value[0] = host->addr_value[0]; - host->cache_addr_value[1] = host->addr_value[1]; - - return 0; -} - -static int hisi_nfc_send_cmd_erase(struct hinfc_host *host) -{ - hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); - hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1, - HINFC504_CMD); - - hinfc_write(host, HINFC504_OP_WAIT_READY_EN - | HINFC504_OP_CMD2_EN - | HINFC504_OP_CMD1_EN - | HINFC504_OP_ADDR_EN - | ((host->chipselect & HINFC504_OP_NF_CS_MASK) - << HINFC504_OP_NF_CS_SHIFT) - | ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK) - << HINFC504_OP_ADDR_CYCLE_SHIFT), - HINFC504_OP); - - wait_controller_finished(host); - - return 0; -} - -static int hisi_nfc_send_cmd_readid(struct hinfc_host *host) -{ - hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); - hinfc_write(host, NAND_CMD_READID, HINFC504_CMD); - hinfc_write(host, 0, HINFC504_ADDRL); - - hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN - | HINFC504_OP_READ_DATA_EN - | ((host->chipselect & HINFC504_OP_NF_CS_MASK) - << HINFC504_OP_NF_CS_SHIFT) - | 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP); - - wait_controller_finished(host); - - return 0; -} - -static int hisi_nfc_send_cmd_status(struct hinfc_host *host) -{ - hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); - hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD); - hinfc_write(host, HINFC504_OP_CMD1_EN - | HINFC504_OP_READ_DATA_EN - | ((host->chipselect & HINFC504_OP_NF_CS_MASK) - << HINFC504_OP_NF_CS_SHIFT), - HINFC504_OP); - - wait_controller_finished(host); - - return 0; -} - -static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect) -{ - hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD); - - hinfc_write(host, HINFC504_OP_CMD1_EN - | ((chipselect & HINFC504_OP_NF_CS_MASK) - << HINFC504_OP_NF_CS_SHIFT) - | HINFC504_OP_WAIT_READY_EN, - HINFC504_OP); - - wait_controller_finished(host); - - return 0; -} - -static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct hinfc_host *host = nand_get_controller_data(chip); - - if (chipselect < 0) - return; - - host->chipselect = chipselect; -} - -static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct hinfc_host *host = nand_get_controller_data(chip); - - if (host->command == NAND_CMD_STATUS) - return *(uint8_t *)(host->mmio); - - host->offset++; - - if (host->command == NAND_CMD_READID) - return *(uint8_t *)(host->mmio + host->offset - 1); - - return *(uint8_t *)(host->buffer + host->offset - 1); -} - -static u16 hisi_nfc_read_word(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct hinfc_host *host = nand_get_controller_data(chip); - - host->offset += 2; - return *(u16 *)(host->buffer + host->offset - 2); -} - -static void -hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct hinfc_host *host = nand_get_controller_data(chip); - - memcpy(host->buffer + host->offset, buf, len); - host->offset += len; -} - -static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct hinfc_host *host = nand_get_controller_data(chip); - - memcpy(buf, host->buffer + host->offset, len); - host->offset += len; -} - -static void set_addr(struct mtd_info *mtd, int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct hinfc_host *host = nand_get_controller_data(chip); - unsigned int command = host->command; - - host->addr_cycle = 0; - host->addr_value[0] = 0; - host->addr_value[1] = 0; - - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - - host->addr_value[0] = column & 0xffff; - host->addr_cycle = 2; - } - if (page_addr != -1) { - host->addr_value[0] |= (page_addr & 0xffff) - << (host->addr_cycle * 8); - host->addr_cycle += 2; - if (chip->options & NAND_ROW_ADDR_3) { - host->addr_cycle += 1; - if (host->command == NAND_CMD_ERASE1) - host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16; - else - host->addr_value[1] |= ((page_addr >> 16) & 0xff); - } - } -} - -static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column, - int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct hinfc_host *host = nand_get_controller_data(chip); - int is_cache_invalid = 1; - unsigned int flag = 0; - - host->command = command; - - switch (command) { - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - if (command == NAND_CMD_READ0) - host->offset = column; - else - host->offset = column + mtd->writesize; - - is_cache_invalid = 0; - set_addr(mtd, column, page_addr); - hisi_nfc_send_cmd_readstart(host); - break; - - case NAND_CMD_SEQIN: - host->offset = column; - set_addr(mtd, column, page_addr); - break; - - case NAND_CMD_ERASE1: - set_addr(mtd, column, page_addr); - break; - - case NAND_CMD_PAGEPROG: - hisi_nfc_send_cmd_pageprog(host); - break; - - case NAND_CMD_ERASE2: - hisi_nfc_send_cmd_erase(host); - break; - - case NAND_CMD_READID: - host->offset = column; - memset(host->mmio, 0, 0x10); - hisi_nfc_send_cmd_readid(host); - break; - - case NAND_CMD_STATUS: - flag = hinfc_read(host, HINFC504_CON); - if (chip->ecc.mode == NAND_ECC_HW) - hinfc_write(host, - flag & ~(HINFC504_CON_ECCTYPE_MASK << - HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON); - - host->offset = 0; - memset(host->mmio, 0, 0x10); - hisi_nfc_send_cmd_status(host); - hinfc_write(host, flag, HINFC504_CON); - break; - - case NAND_CMD_RESET: - hisi_nfc_send_cmd_reset(host, host->chipselect); - break; - - default: - dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n", - command, column, page_addr); - } - - if (is_cache_invalid) { - host->cache_addr_value[0] = ~0; - host->cache_addr_value[1] = ~0; - } -} - -static irqreturn_t hinfc_irq_handle(int irq, void *devid) -{ - struct hinfc_host *host = devid; - unsigned int flag; - - flag = hinfc_read(host, HINFC504_INTS); - /* store interrupts state */ - host->irq_status |= flag; - - if (flag & HINFC504_INTS_DMA) { - hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR); - complete(&host->cmd_complete); - } else if (flag & HINFC504_INTS_CE) { - hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR); - } else if (flag & HINFC504_INTS_UE) { - hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR); - } - - return IRQ_HANDLED; -} - -static int hisi_nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, int page) -{ - struct hinfc_host *host = nand_get_controller_data(chip); - int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc; - int stat_1, stat_2; - - nand_read_page_op(chip, page, 0, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - /* errors which can not be corrected by ECC */ - if (host->irq_status & HINFC504_INTS_UE) { - mtd->ecc_stats.failed++; - } else if (host->irq_status & HINFC504_INTS_CE) { - /* TODO: need add other ECC modes! */ - switch (chip->ecc.strength) { - case 16: - status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >> - HINFC504_ECC_16_BIT_SHIFT & 0x0fff; - stat_2 = status_ecc & 0x3f; - stat_1 = status_ecc >> 6 & 0x3f; - stat = stat_1 + stat_2; - stat_max = max_t(int, stat_1, stat_2); - } - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(int, max_bitflips, stat_max); - } - host->irq_status = 0; - - return max_bitflips; -} - -static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct hinfc_host *host = nand_get_controller_data(chip); - - nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); - - if (host->irq_status & HINFC504_INTS_UE) { - host->irq_status = 0; - return -EBADMSG; - } - - host->irq_status = 0; - return 0; -} - -static int hisi_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required, - int page) -{ - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - if (oob_required) - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -static void hisi_nfc_host_init(struct hinfc_host *host) -{ - struct nand_chip *chip = &host->chip; - unsigned int flag = 0; - - host->version = hinfc_read(host, HINFC_VERSION); - host->addr_cycle = 0; - host->addr_value[0] = 0; - host->addr_value[1] = 0; - host->cache_addr_value[0] = ~0; - host->cache_addr_value[1] = ~0; - host->chipselect = 0; - - /* default page size: 2K, ecc_none. need modify */ - flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL - | ((0x001 & HINFC504_CON_PAGESIZE_MASK) - << HINFC504_CON_PAGEISZE_SHIFT) - | ((0x0 & HINFC504_CON_ECCTYPE_MASK) - << HINFC504_CON_ECCTYPE_SHIFT) - | ((chip->options & NAND_BUSWIDTH_16) ? - HINFC504_CON_BUS_WIDTH : 0); - hinfc_write(host, flag, HINFC504_CON); - - memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN); - - hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, - HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); - - /* enable DMA irq */ - hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN); -} - -static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - /* FIXME: add ECC bytes position */ - return -ENOTSUPP; -} - -static int hisi_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->offset = 2; - oobregion->length = 6; - - return 0; -} - -static const struct mtd_ooblayout_ops hisi_ooblayout_ops = { - .ecc = hisi_ooblayout_ecc, - .free = hisi_ooblayout_free, -}; - -static int hisi_nfc_ecc_probe(struct hinfc_host *host) -{ - unsigned int flag; - int size, strength, ecc_bits; - struct device *dev = host->dev; - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - - size = chip->ecc.size; - strength = chip->ecc.strength; - if (size != 1024) { - dev_err(dev, "error ecc size: %d\n", size); - return -EINVAL; - } - - if ((size == 1024) && ((strength != 8) && (strength != 16) && - (strength != 24) && (strength != 40))) { - dev_err(dev, "ecc size and strength do not match\n"); - return -EINVAL; - } - - chip->ecc.size = size; - chip->ecc.strength = strength; - - chip->ecc.read_page = hisi_nand_read_page_hwecc; - chip->ecc.read_oob = hisi_nand_read_oob; - chip->ecc.write_page = hisi_nand_write_page_hwecc; - - switch (chip->ecc.strength) { - case 16: - ecc_bits = 6; - if (mtd->writesize == 2048) - mtd_set_ooblayout(mtd, &hisi_ooblayout_ops); - - /* TODO: add more page size support */ - break; - - /* TODO: add more ecc strength support */ - default: - dev_err(dev, "not support strength: %d\n", chip->ecc.strength); - return -EINVAL; - } - - flag = hinfc_read(host, HINFC504_CON); - /* add ecc type configure */ - flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK) - << HINFC504_CON_ECCTYPE_SHIFT); - hinfc_write(host, flag, HINFC504_CON); - - /* enable ecc irq */ - flag = hinfc_read(host, HINFC504_INTEN) & 0xfff; - hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE, - HINFC504_INTEN); - - return 0; -} - -static int hisi_nfc_probe(struct platform_device *pdev) -{ - int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP; - struct device *dev = &pdev->dev; - struct hinfc_host *host; - struct nand_chip *chip; - struct mtd_info *mtd; - struct resource *res; - struct device_node *np = dev->of_node; - - host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); - if (!host) - return -ENOMEM; - host->dev = dev; - - platform_set_drvdata(pdev, host); - chip = &host->chip; - mtd = nand_to_mtd(chip); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no IRQ resource defined\n"); - ret = -ENXIO; - goto err_res; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->iobase = devm_ioremap_resource(dev, res); - if (IS_ERR(host->iobase)) { - ret = PTR_ERR(host->iobase); - goto err_res; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - host->mmio = devm_ioremap_resource(dev, res); - if (IS_ERR(host->mmio)) { - ret = PTR_ERR(host->mmio); - dev_err(dev, "devm_ioremap_resource[1] fail\n"); - goto err_res; - } - - mtd->name = "hisi_nand"; - mtd->dev.parent = &pdev->dev; - - nand_set_controller_data(chip, host); - nand_set_flash_node(chip, np); - chip->cmdfunc = hisi_nfc_cmdfunc; - chip->select_chip = hisi_nfc_select_chip; - chip->read_byte = hisi_nfc_read_byte; - chip->read_word = hisi_nfc_read_word; - chip->write_buf = hisi_nfc_write_buf; - chip->read_buf = hisi_nfc_read_buf; - chip->chip_delay = HINFC504_CHIP_DELAY; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; - - hisi_nfc_host_init(host); - - ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host); - if (ret) { - dev_err(dev, "failed to request IRQ\n"); - goto err_res; - } - - ret = nand_scan_ident(mtd, max_chips, NULL); - if (ret) - goto err_res; - - host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize, - &host->dma_buffer, GFP_KERNEL); - if (!host->buffer) { - ret = -ENOMEM; - goto err_res; - } - - host->dma_oob = host->dma_buffer + mtd->writesize; - memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize); - - flag = hinfc_read(host, HINFC504_CON); - flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT); - switch (mtd->writesize) { - case 2048: - flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break; - /* - * TODO: add more pagesize support, - * default pagesize has been set in hisi_nfc_host_init - */ - default: - dev_err(dev, "NON-2KB page size nand flash\n"); - ret = -EINVAL; - goto err_res; - } - hinfc_write(host, flag, HINFC504_CON); - - if (chip->ecc.mode == NAND_ECC_HW) - hisi_nfc_ecc_probe(host); - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(dev, "nand_scan_tail failed: %d\n", ret); - goto err_res; - } - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - dev_err(dev, "Err MTD partition=%d\n", ret); - goto err_mtd; - } - - return 0; - -err_mtd: - nand_release(mtd); -err_res: - return ret; -} - -static int hisi_nfc_remove(struct platform_device *pdev) -{ - struct hinfc_host *host = platform_get_drvdata(pdev); - struct mtd_info *mtd = nand_to_mtd(&host->chip); - - nand_release(mtd); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int hisi_nfc_suspend(struct device *dev) -{ - struct hinfc_host *host = dev_get_drvdata(dev); - unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT; - - while (time_before(jiffies, timeout)) { - if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) && - (hinfc_read(host, HINFC504_DMA_CTRL) & - HINFC504_DMA_CTRL_DMA_START)) { - cond_resched(); - return 0; - } - } - - dev_err(host->dev, "nand controller suspend timeout.\n"); - - return -EAGAIN; -} - -static int hisi_nfc_resume(struct device *dev) -{ - int cs; - struct hinfc_host *host = dev_get_drvdata(dev); - struct nand_chip *chip = &host->chip; - - for (cs = 0; cs < chip->numchips; cs++) - hisi_nfc_send_cmd_reset(host, cs); - hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, - HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); - - return 0; -} -#endif -static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume); - -static const struct of_device_id nfc_id_table[] = { - { .compatible = "hisilicon,504-nfc" }, - {} -}; -MODULE_DEVICE_TABLE(of, nfc_id_table); - -static struct platform_driver hisi_nfc_driver = { - .driver = { - .name = "hisi_nand", - .of_match_table = nfc_id_table, - .pm = &hisi_nfc_pm_ops, - }, - .probe = hisi_nfc_probe, - .remove = hisi_nfc_remove, -}; - -module_platform_driver(hisi_nfc_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Zhou Wang"); -MODULE_AUTHOR("Zhiyong Cai"); -MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver"); diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c deleted file mode 100644 index 613b00a9604b..000000000000 --- a/drivers/mtd/nand/jz4740_nand.c +++ /dev/null @@ -1,536 +0,0 @@ -/* - * Copyright (C) 2009-2010, Lars-Peter Clausen - * JZ4740 SoC NAND controller driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#define JZ_REG_NAND_CTRL 0x50 -#define JZ_REG_NAND_ECC_CTRL 0x100 -#define JZ_REG_NAND_DATA 0x104 -#define JZ_REG_NAND_PAR0 0x108 -#define JZ_REG_NAND_PAR1 0x10C -#define JZ_REG_NAND_PAR2 0x110 -#define JZ_REG_NAND_IRQ_STAT 0x114 -#define JZ_REG_NAND_IRQ_CTRL 0x118 -#define JZ_REG_NAND_ERR(x) (0x11C + ((x) << 2)) - -#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) -#define JZ_NAND_ECC_CTRL_ENCODING BIT(3) -#define JZ_NAND_ECC_CTRL_RS BIT(2) -#define JZ_NAND_ECC_CTRL_RESET BIT(1) -#define JZ_NAND_ECC_CTRL_ENABLE BIT(0) - -#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) -#define JZ_NAND_STATUS_PAD_FINISH BIT(4) -#define JZ_NAND_STATUS_DEC_FINISH BIT(3) -#define JZ_NAND_STATUS_ENC_FINISH BIT(2) -#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) -#define JZ_NAND_STATUS_ERROR BIT(0) - -#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) -#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) -#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa - -#define JZ_NAND_MEM_CMD_OFFSET 0x08000 -#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 - -struct jz_nand { - struct nand_chip chip; - void __iomem *base; - struct resource *mem; - - unsigned char banks[JZ_NAND_NUM_BANKS]; - void __iomem *bank_base[JZ_NAND_NUM_BANKS]; - struct resource *bank_mem[JZ_NAND_NUM_BANKS]; - - int selected_bank; - - struct gpio_desc *busy_gpio; - bool is_reading; -}; - -static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct jz_nand, chip); -} - -static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) -{ - struct jz_nand *nand = mtd_to_jz_nand(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - uint32_t ctrl; - int banknr; - - ctrl = readl(nand->base + JZ_REG_NAND_CTRL); - ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; - - if (chipnr == -1) { - banknr = -1; - } else { - banknr = nand->banks[chipnr] - 1; - chip->IO_ADDR_R = nand->bank_base[banknr]; - chip->IO_ADDR_W = nand->bank_base[banknr]; - } - writel(ctrl, nand->base + JZ_REG_NAND_CTRL); - - nand->selected_bank = banknr; -} - -static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) -{ - struct jz_nand *nand = mtd_to_jz_nand(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - uint32_t reg; - void __iomem *bank_base = nand->bank_base[nand->selected_bank]; - - BUG_ON(nand->selected_bank < 0); - - if (ctrl & NAND_CTRL_CHANGE) { - BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); - if (ctrl & NAND_ALE) - bank_base += JZ_NAND_MEM_ADDR_OFFSET; - else if (ctrl & NAND_CLE) - bank_base += JZ_NAND_MEM_CMD_OFFSET; - chip->IO_ADDR_W = bank_base; - - reg = readl(nand->base + JZ_REG_NAND_CTRL); - if (ctrl & NAND_NCE) - reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); - else - reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); - writel(reg, nand->base + JZ_REG_NAND_CTRL); - } - if (dat != NAND_CMD_NONE) - writeb(dat, chip->IO_ADDR_W); -} - -static int jz_nand_dev_ready(struct mtd_info *mtd) -{ - struct jz_nand *nand = mtd_to_jz_nand(mtd); - return gpiod_get_value_cansleep(nand->busy_gpio); -} - -static void jz_nand_hwctl(struct mtd_info *mtd, int mode) -{ - struct jz_nand *nand = mtd_to_jz_nand(mtd); - uint32_t reg; - - writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); - reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); - - reg |= JZ_NAND_ECC_CTRL_RESET; - reg |= JZ_NAND_ECC_CTRL_ENABLE; - reg |= JZ_NAND_ECC_CTRL_RS; - - switch (mode) { - case NAND_ECC_READ: - reg &= ~JZ_NAND_ECC_CTRL_ENCODING; - nand->is_reading = true; - break; - case NAND_ECC_WRITE: - reg |= JZ_NAND_ECC_CTRL_ENCODING; - nand->is_reading = false; - break; - default: - break; - } - - writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); -} - -static int jz_nand_calculate_ecc_rs(struct mtd_info *mtd, const uint8_t *dat, - uint8_t *ecc_code) -{ - struct jz_nand *nand = mtd_to_jz_nand(mtd); - uint32_t reg, status; - int i; - unsigned int timeout = 1000; - static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4, - 0x8b, 0xff, 0xb7, 0x6f}; - - if (nand->is_reading) - return 0; - - do { - status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); - } while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout); - - if (timeout == 0) - return -1; - - reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); - reg &= ~JZ_NAND_ECC_CTRL_ENABLE; - writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); - - for (i = 0; i < 9; ++i) - ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); - - /* If the written data is completly 0xff, we also want to write 0xff as - * ecc, otherwise we will get in trouble when doing subpage writes. */ - if (memcmp(ecc_code, empty_block_ecc, 9) == 0) - memset(ecc_code, 0xff, 9); - - return 0; -} - -static void jz_nand_correct_data(uint8_t *dat, int index, int mask) -{ - int offset = index & 0x7; - uint16_t data; - - index += (index >> 3); - - data = dat[index]; - data |= dat[index+1] << 8; - - mask ^= (data >> offset) & 0x1ff; - data &= ~(0x1ff << offset); - data |= (mask << offset); - - dat[index] = data & 0xff; - dat[index+1] = (data >> 8) & 0xff; -} - -static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - struct jz_nand *nand = mtd_to_jz_nand(mtd); - int i, error_count, index; - uint32_t reg, status, error; - unsigned int timeout = 1000; - - for (i = 0; i < 9; ++i) - writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); - - reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); - reg |= JZ_NAND_ECC_CTRL_PAR_READY; - writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); - - do { - status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); - } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout); - - if (timeout == 0) - return -ETIMEDOUT; - - reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); - reg &= ~JZ_NAND_ECC_CTRL_ENABLE; - writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); - - if (status & JZ_NAND_STATUS_ERROR) { - if (status & JZ_NAND_STATUS_UNCOR_ERROR) - return -EBADMSG; - - error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; - - for (i = 0; i < error_count; ++i) { - error = readl(nand->base + JZ_REG_NAND_ERR(i)); - index = ((error >> 16) & 0x1ff) - 1; - if (index >= 0 && index < 512) - jz_nand_correct_data(dat, index, error & 0x1ff); - } - - return error_count; - } - - return 0; -} - -static int jz_nand_ioremap_resource(struct platform_device *pdev, - const char *name, struct resource **res, void *__iomem *base) -{ - int ret; - - *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - if (!*res) { - dev_err(&pdev->dev, "Failed to get platform %s memory\n", name); - ret = -ENXIO; - goto err; - } - - *res = request_mem_region((*res)->start, resource_size(*res), - pdev->name); - if (!*res) { - dev_err(&pdev->dev, "Failed to request %s memory region\n", name); - ret = -EBUSY; - goto err; - } - - *base = ioremap((*res)->start, resource_size(*res)); - if (!*base) { - dev_err(&pdev->dev, "Failed to ioremap %s memory region\n", name); - ret = -EBUSY; - goto err_release_mem; - } - - return 0; - -err_release_mem: - release_mem_region((*res)->start, resource_size(*res)); -err: - *res = NULL; - *base = NULL; - return ret; -} - -static inline void jz_nand_iounmap_resource(struct resource *res, - void __iomem *base) -{ - iounmap(base); - release_mem_region(res->start, resource_size(res)); -} - -static int jz_nand_detect_bank(struct platform_device *pdev, - struct jz_nand *nand, unsigned char bank, - size_t chipnr, uint8_t *nand_maf_id, - uint8_t *nand_dev_id) -{ - int ret; - char res_name[6]; - uint32_t ctrl; - struct nand_chip *chip = &nand->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - u8 id[2]; - - /* Request I/O resource. */ - sprintf(res_name, "bank%d", bank); - ret = jz_nand_ioremap_resource(pdev, res_name, - &nand->bank_mem[bank - 1], - &nand->bank_base[bank - 1]); - if (ret) - return ret; - - /* Enable chip in bank. */ - ctrl = readl(nand->base + JZ_REG_NAND_CTRL); - ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); - writel(ctrl, nand->base + JZ_REG_NAND_CTRL); - - if (chipnr == 0) { - /* Detect first chip. */ - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - goto notfound_id; - - /* Retrieve the IDs from the first chip. */ - chip->select_chip(mtd, 0); - nand_reset_op(chip); - nand_readid_op(chip, 0, id, sizeof(id)); - *nand_maf_id = id[0]; - *nand_dev_id = id[1]; - } else { - /* Detect additional chip. */ - chip->select_chip(mtd, chipnr); - nand_reset_op(chip); - nand_readid_op(chip, 0, id, sizeof(id)); - if (*nand_maf_id != id[0] || *nand_dev_id != id[1]) { - ret = -ENODEV; - goto notfound_id; - } - - /* Update size of the MTD. */ - chip->numchips++; - mtd->size += chip->chipsize; - } - - dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); - return 0; - -notfound_id: - dev_info(&pdev->dev, "No chip found on bank %i\n", bank); - ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); - writel(ctrl, nand->base + JZ_REG_NAND_CTRL); - jz_nand_iounmap_resource(nand->bank_mem[bank - 1], - nand->bank_base[bank - 1]); - return ret; -} - -static int jz_nand_probe(struct platform_device *pdev) -{ - int ret; - struct jz_nand *nand; - struct nand_chip *chip; - struct mtd_info *mtd; - struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); - size_t chipnr, bank_idx; - uint8_t nand_maf_id = 0, nand_dev_id = 0; - - nand = kzalloc(sizeof(*nand), GFP_KERNEL); - if (!nand) - return -ENOMEM; - - ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); - if (ret) - goto err_free; - - nand->busy_gpio = devm_gpiod_get_optional(&pdev->dev, "busy", GPIOD_IN); - if (IS_ERR(nand->busy_gpio)) { - ret = PTR_ERR(nand->busy_gpio); - dev_err(&pdev->dev, "Failed to request busy gpio %d\n", - ret); - goto err_iounmap_mmio; - } - - chip = &nand->chip; - mtd = nand_to_mtd(chip); - mtd->dev.parent = &pdev->dev; - mtd->name = "jz4740-nand"; - - chip->ecc.hwctl = jz_nand_hwctl; - chip->ecc.calculate = jz_nand_calculate_ecc_rs; - chip->ecc.correct = jz_nand_correct_ecc_rs; - chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; - chip->ecc.size = 512; - chip->ecc.bytes = 9; - chip->ecc.strength = 4; - chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; - - chip->chip_delay = 50; - chip->cmd_ctrl = jz_nand_cmd_ctrl; - chip->select_chip = jz_nand_select_chip; - - if (nand->busy_gpio) - chip->dev_ready = jz_nand_dev_ready; - - platform_set_drvdata(pdev, nand); - - /* We are going to autodetect NAND chips in the banks specified in the - * platform data. Although nand_scan_ident() can detect multiple chips, - * it requires those chips to be numbered consecuitively, which is not - * always the case for external memory banks. And a fixed chip-to-bank - * mapping is not practical either, since for example Dingoo units - * produced at different times have NAND chips in different banks. - */ - chipnr = 0; - for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { - unsigned char bank; - - /* If there is no platform data, look for NAND in bank 1, - * which is the most likely bank since it is the only one - * that can be booted from. - */ - bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; - if (bank == 0) - break; - if (bank > JZ_NAND_NUM_BANKS) { - dev_warn(&pdev->dev, - "Skipping non-existing bank: %d\n", bank); - continue; - } - /* The detection routine will directly or indirectly call - * jz_nand_select_chip(), so nand->banks has to contain the - * bank we're checking. - */ - nand->banks[chipnr] = bank; - if (jz_nand_detect_bank(pdev, nand, bank, chipnr, - &nand_maf_id, &nand_dev_id) == 0) - chipnr++; - else - nand->banks[chipnr] = 0; - } - if (chipnr == 0) { - dev_err(&pdev->dev, "No NAND chips found\n"); - goto err_iounmap_mmio; - } - - if (pdata && pdata->ident_callback) { - pdata->ident_callback(pdev, mtd, &pdata->partitions, - &pdata->num_partitions); - } - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(&pdev->dev, "Failed to scan NAND\n"); - goto err_unclaim_banks; - } - - ret = mtd_device_parse_register(mtd, NULL, NULL, - pdata ? pdata->partitions : NULL, - pdata ? pdata->num_partitions : 0); - - if (ret) { - dev_err(&pdev->dev, "Failed to add mtd device\n"); - goto err_nand_release; - } - - dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); - - return 0; - -err_nand_release: - nand_release(mtd); -err_unclaim_banks: - while (chipnr--) { - unsigned char bank = nand->banks[chipnr]; - jz_nand_iounmap_resource(nand->bank_mem[bank - 1], - nand->bank_base[bank - 1]); - } - writel(0, nand->base + JZ_REG_NAND_CTRL); -err_iounmap_mmio: - jz_nand_iounmap_resource(nand->mem, nand->base); -err_free: - kfree(nand); - return ret; -} - -static int jz_nand_remove(struct platform_device *pdev) -{ - struct jz_nand *nand = platform_get_drvdata(pdev); - size_t i; - - nand_release(nand_to_mtd(&nand->chip)); - - /* Deassert and disable all chips */ - writel(0, nand->base + JZ_REG_NAND_CTRL); - - for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { - unsigned char bank = nand->banks[i]; - if (bank != 0) { - jz_nand_iounmap_resource(nand->bank_mem[bank - 1], - nand->bank_base[bank - 1]); - } - } - - jz_nand_iounmap_resource(nand->mem, nand->base); - - kfree(nand); - - return 0; -} - -static struct platform_driver jz_nand_driver = { - .probe = jz_nand_probe, - .remove = jz_nand_remove, - .driver = { - .name = "jz4740-nand", - }, -}; - -module_platform_driver(jz_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("NAND controller driver for JZ4740 SoC"); -MODULE_ALIAS("platform:jz4740-nand"); diff --git a/drivers/mtd/nand/jz4780_bch.c b/drivers/mtd/nand/jz4780_bch.c deleted file mode 100644 index 731c6051d91e..000000000000 --- a/drivers/mtd/nand/jz4780_bch.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * JZ4780 BCH controller - * - * Copyright (c) 2015 Imagination Technologies - * Author: Alex Smith - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "jz4780_bch.h" - -#define BCH_BHCR 0x0 -#define BCH_BHCCR 0x8 -#define BCH_BHCNT 0xc -#define BCH_BHDR 0x10 -#define BCH_BHPAR0 0x14 -#define BCH_BHERR0 0x84 -#define BCH_BHINT 0x184 -#define BCH_BHINTES 0x188 -#define BCH_BHINTEC 0x18c -#define BCH_BHINTE 0x190 - -#define BCH_BHCR_BSEL_SHIFT 4 -#define BCH_BHCR_BSEL_MASK (0x7f << BCH_BHCR_BSEL_SHIFT) -#define BCH_BHCR_ENCE BIT(2) -#define BCH_BHCR_INIT BIT(1) -#define BCH_BHCR_BCHE BIT(0) - -#define BCH_BHCNT_PARITYSIZE_SHIFT 16 -#define BCH_BHCNT_PARITYSIZE_MASK (0x7f << BCH_BHCNT_PARITYSIZE_SHIFT) -#define BCH_BHCNT_BLOCKSIZE_SHIFT 0 -#define BCH_BHCNT_BLOCKSIZE_MASK (0x7ff << BCH_BHCNT_BLOCKSIZE_SHIFT) - -#define BCH_BHERR_MASK_SHIFT 16 -#define BCH_BHERR_MASK_MASK (0xffff << BCH_BHERR_MASK_SHIFT) -#define BCH_BHERR_INDEX_SHIFT 0 -#define BCH_BHERR_INDEX_MASK (0x7ff << BCH_BHERR_INDEX_SHIFT) - -#define BCH_BHINT_ERRC_SHIFT 24 -#define BCH_BHINT_ERRC_MASK (0x7f << BCH_BHINT_ERRC_SHIFT) -#define BCH_BHINT_TERRC_SHIFT 16 -#define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT) -#define BCH_BHINT_DECF BIT(3) -#define BCH_BHINT_ENCF BIT(2) -#define BCH_BHINT_UNCOR BIT(1) -#define BCH_BHINT_ERR BIT(0) - -#define BCH_CLK_RATE (200 * 1000 * 1000) - -/* Timeout for BCH calculation/correction. */ -#define BCH_TIMEOUT_US 100000 - -struct jz4780_bch { - struct device *dev; - void __iomem *base; - struct clk *clk; - struct mutex lock; -}; - -static void jz4780_bch_init(struct jz4780_bch *bch, - struct jz4780_bch_params *params, bool encode) -{ - u32 reg; - - /* Clear interrupt status. */ - writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); - - /* Set up BCH count register. */ - reg = params->size << BCH_BHCNT_BLOCKSIZE_SHIFT; - reg |= params->bytes << BCH_BHCNT_PARITYSIZE_SHIFT; - writel(reg, bch->base + BCH_BHCNT); - - /* Initialise and enable BCH. */ - reg = BCH_BHCR_BCHE | BCH_BHCR_INIT; - reg |= params->strength << BCH_BHCR_BSEL_SHIFT; - if (encode) - reg |= BCH_BHCR_ENCE; - writel(reg, bch->base + BCH_BHCR); -} - -static void jz4780_bch_disable(struct jz4780_bch *bch) -{ - writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); - writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR); -} - -static void jz4780_bch_write_data(struct jz4780_bch *bch, const void *buf, - size_t size) -{ - size_t size32 = size / sizeof(u32); - size_t size8 = size % sizeof(u32); - const u32 *src32; - const u8 *src8; - - src32 = (const u32 *)buf; - while (size32--) - writel(*src32++, bch->base + BCH_BHDR); - - src8 = (const u8 *)src32; - while (size8--) - writeb(*src8++, bch->base + BCH_BHDR); -} - -static void jz4780_bch_read_parity(struct jz4780_bch *bch, void *buf, - size_t size) -{ - size_t size32 = size / sizeof(u32); - size_t size8 = size % sizeof(u32); - u32 *dest32; - u8 *dest8; - u32 val, offset = 0; - - dest32 = (u32 *)buf; - while (size32--) { - *dest32++ = readl(bch->base + BCH_BHPAR0 + offset); - offset += sizeof(u32); - } - - dest8 = (u8 *)dest32; - val = readl(bch->base + BCH_BHPAR0 + offset); - switch (size8) { - case 3: - dest8[2] = (val >> 16) & 0xff; - case 2: - dest8[1] = (val >> 8) & 0xff; - case 1: - dest8[0] = val & 0xff; - break; - } -} - -static bool jz4780_bch_wait_complete(struct jz4780_bch *bch, unsigned int irq, - u32 *status) -{ - u32 reg; - int ret; - - /* - * While we could use interrupts here and sleep until the operation - * completes, the controller works fairly quickly (usually a few - * microseconds) and so the overhead of sleeping until we get an - * interrupt quite noticeably decreases performance. - */ - ret = readl_poll_timeout(bch->base + BCH_BHINT, reg, - (reg & irq) == irq, 0, BCH_TIMEOUT_US); - if (ret) - return false; - - if (status) - *status = reg; - - writel(reg, bch->base + BCH_BHINT); - return true; -} - -/** - * jz4780_bch_calculate() - calculate ECC for a data buffer - * @bch: BCH device. - * @params: BCH parameters. - * @buf: input buffer with raw data. - * @ecc_code: output buffer with ECC. - * - * Return: 0 on success, -ETIMEDOUT if timed out while waiting for BCH - * controller. - */ -int jz4780_bch_calculate(struct jz4780_bch *bch, struct jz4780_bch_params *params, - const u8 *buf, u8 *ecc_code) -{ - int ret = 0; - - mutex_lock(&bch->lock); - jz4780_bch_init(bch, params, true); - jz4780_bch_write_data(bch, buf, params->size); - - if (jz4780_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) { - jz4780_bch_read_parity(bch, ecc_code, params->bytes); - } else { - dev_err(bch->dev, "timed out while calculating ECC\n"); - ret = -ETIMEDOUT; - } - - jz4780_bch_disable(bch); - mutex_unlock(&bch->lock); - return ret; -} -EXPORT_SYMBOL(jz4780_bch_calculate); - -/** - * jz4780_bch_correct() - detect and correct bit errors - * @bch: BCH device. - * @params: BCH parameters. - * @buf: raw data read from the chip. - * @ecc_code: ECC read from the chip. - * - * Given the raw data and the ECC read from the NAND device, detects and - * corrects errors in the data. - * - * Return: the number of bit errors corrected, -EBADMSG if there are too many - * errors to correct or -ETIMEDOUT if we timed out waiting for the controller. - */ -int jz4780_bch_correct(struct jz4780_bch *bch, struct jz4780_bch_params *params, - u8 *buf, u8 *ecc_code) -{ - u32 reg, mask, index; - int i, ret, count; - - mutex_lock(&bch->lock); - - jz4780_bch_init(bch, params, false); - jz4780_bch_write_data(bch, buf, params->size); - jz4780_bch_write_data(bch, ecc_code, params->bytes); - - if (!jz4780_bch_wait_complete(bch, BCH_BHINT_DECF, ®)) { - dev_err(bch->dev, "timed out while correcting data\n"); - ret = -ETIMEDOUT; - goto out; - } - - if (reg & BCH_BHINT_UNCOR) { - dev_warn(bch->dev, "uncorrectable ECC error\n"); - ret = -EBADMSG; - goto out; - } - - /* Correct any detected errors. */ - if (reg & BCH_BHINT_ERR) { - count = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT; - ret = (reg & BCH_BHINT_TERRC_MASK) >> BCH_BHINT_TERRC_SHIFT; - - for (i = 0; i < count; i++) { - reg = readl(bch->base + BCH_BHERR0 + (i * 4)); - mask = (reg & BCH_BHERR_MASK_MASK) >> - BCH_BHERR_MASK_SHIFT; - index = (reg & BCH_BHERR_INDEX_MASK) >> - BCH_BHERR_INDEX_SHIFT; - buf[(index * 2) + 0] ^= mask; - buf[(index * 2) + 1] ^= mask >> 8; - } - } else { - ret = 0; - } - -out: - jz4780_bch_disable(bch); - mutex_unlock(&bch->lock); - return ret; -} -EXPORT_SYMBOL(jz4780_bch_correct); - -/** - * jz4780_bch_get() - get the BCH controller device - * @np: BCH device tree node. - * - * Gets the BCH controller device from the specified device tree node. The - * device must be released with jz4780_bch_release() when it is no longer being - * used. - * - * Return: a pointer to jz4780_bch, errors are encoded into the pointer. - * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet. - */ -static struct jz4780_bch *jz4780_bch_get(struct device_node *np) -{ - struct platform_device *pdev; - struct jz4780_bch *bch; - - pdev = of_find_device_by_node(np); - if (!pdev || !platform_get_drvdata(pdev)) - return ERR_PTR(-EPROBE_DEFER); - - get_device(&pdev->dev); - - bch = platform_get_drvdata(pdev); - clk_prepare_enable(bch->clk); - - return bch; -} - -/** - * of_jz4780_bch_get() - get the BCH controller from a DT node - * @of_node: the node that contains a bch-controller property. - * - * Get the bch-controller property from the given device tree - * node and pass it to jz4780_bch_get to do the work. - * - * Return: a pointer to jz4780_bch, errors are encoded into the pointer. - * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet. - */ -struct jz4780_bch *of_jz4780_bch_get(struct device_node *of_node) -{ - struct jz4780_bch *bch = NULL; - struct device_node *np; - - np = of_parse_phandle(of_node, "ingenic,bch-controller", 0); - - if (np) { - bch = jz4780_bch_get(np); - of_node_put(np); - } - return bch; -} -EXPORT_SYMBOL(of_jz4780_bch_get); - -/** - * jz4780_bch_release() - release the BCH controller device - * @bch: BCH device. - */ -void jz4780_bch_release(struct jz4780_bch *bch) -{ - clk_disable_unprepare(bch->clk); - put_device(bch->dev); -} -EXPORT_SYMBOL(jz4780_bch_release); - -static int jz4780_bch_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct jz4780_bch *bch; - struct resource *res; - - bch = devm_kzalloc(dev, sizeof(*bch), GFP_KERNEL); - if (!bch) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - bch->base = devm_ioremap_resource(dev, res); - if (IS_ERR(bch->base)) - return PTR_ERR(bch->base); - - jz4780_bch_disable(bch); - - bch->clk = devm_clk_get(dev, NULL); - if (IS_ERR(bch->clk)) { - dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(bch->clk)); - return PTR_ERR(bch->clk); - } - - clk_set_rate(bch->clk, BCH_CLK_RATE); - - mutex_init(&bch->lock); - - bch->dev = dev; - platform_set_drvdata(pdev, bch); - - return 0; -} - -static const struct of_device_id jz4780_bch_dt_match[] = { - { .compatible = "ingenic,jz4780-bch" }, - {}, -}; -MODULE_DEVICE_TABLE(of, jz4780_bch_dt_match); - -static struct platform_driver jz4780_bch_driver = { - .probe = jz4780_bch_probe, - .driver = { - .name = "jz4780-bch", - .of_match_table = of_match_ptr(jz4780_bch_dt_match), - }, -}; -module_platform_driver(jz4780_bch_driver); - -MODULE_AUTHOR("Alex Smith "); -MODULE_AUTHOR("Harvey Hunt "); -MODULE_DESCRIPTION("Ingenic JZ4780 BCH error correction driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/jz4780_bch.h b/drivers/mtd/nand/jz4780_bch.h deleted file mode 100644 index bf4718088a3a..000000000000 --- a/drivers/mtd/nand/jz4780_bch.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * JZ4780 BCH controller - * - * Copyright (c) 2015 Imagination Technologies - * Author: Alex Smith - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - */ - -#ifndef __DRIVERS_MTD_NAND_JZ4780_BCH_H__ -#define __DRIVERS_MTD_NAND_JZ4780_BCH_H__ - -#include - -struct device; -struct device_node; -struct jz4780_bch; - -/** - * struct jz4780_bch_params - BCH parameters - * @size: data bytes per ECC step. - * @bytes: ECC bytes per step. - * @strength: number of correctable bits per ECC step. - */ -struct jz4780_bch_params { - int size; - int bytes; - int strength; -}; - -int jz4780_bch_calculate(struct jz4780_bch *bch, - struct jz4780_bch_params *params, - const u8 *buf, u8 *ecc_code); -int jz4780_bch_correct(struct jz4780_bch *bch, - struct jz4780_bch_params *params, u8 *buf, - u8 *ecc_code); - -void jz4780_bch_release(struct jz4780_bch *bch); -struct jz4780_bch *of_jz4780_bch_get(struct device_node *np); - -#endif /* __DRIVERS_MTD_NAND_JZ4780_BCH_H__ */ diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/jz4780_nand.c deleted file mode 100644 index e69f6ae4c539..000000000000 --- a/drivers/mtd/nand/jz4780_nand.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * JZ4780 NAND driver - * - * Copyright (c) 2015 Imagination Technologies - * Author: Alex Smith - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "jz4780_bch.h" - -#define DRV_NAME "jz4780-nand" - -#define OFFSET_DATA 0x00000000 -#define OFFSET_CMD 0x00400000 -#define OFFSET_ADDR 0x00800000 - -/* Command delay when there is no R/B pin. */ -#define RB_DELAY_US 100 - -struct jz4780_nand_cs { - unsigned int bank; - void __iomem *base; -}; - -struct jz4780_nand_controller { - struct device *dev; - struct jz4780_bch *bch; - struct nand_hw_control controller; - unsigned int num_banks; - struct list_head chips; - int selected; - struct jz4780_nand_cs cs[]; -}; - -struct jz4780_nand_chip { - struct nand_chip chip; - struct list_head chip_list; - - struct gpio_desc *busy_gpio; - struct gpio_desc *wp_gpio; - unsigned int reading: 1; -}; - -static inline struct jz4780_nand_chip *to_jz4780_nand_chip(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct jz4780_nand_chip, chip); -} - -static inline struct jz4780_nand_controller *to_jz4780_nand_controller(struct nand_hw_control *ctrl) -{ - return container_of(ctrl, struct jz4780_nand_controller, controller); -} - -static void jz4780_nand_select_chip(struct mtd_info *mtd, int chipnr) -{ - struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); - struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); - struct jz4780_nand_cs *cs; - - /* Ensure the currently selected chip is deasserted. */ - if (chipnr == -1 && nfc->selected >= 0) { - cs = &nfc->cs[nfc->selected]; - jz4780_nemc_assert(nfc->dev, cs->bank, false); - } - - nfc->selected = chipnr; -} - -static void jz4780_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); - struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); - struct jz4780_nand_cs *cs; - - if (WARN_ON(nfc->selected < 0)) - return; - - cs = &nfc->cs[nfc->selected]; - - jz4780_nemc_assert(nfc->dev, cs->bank, ctrl & NAND_NCE); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_ALE) - writeb(cmd, cs->base + OFFSET_ADDR); - else if (ctrl & NAND_CLE) - writeb(cmd, cs->base + OFFSET_CMD); -} - -static int jz4780_nand_dev_ready(struct mtd_info *mtd) -{ - struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); - - return !gpiod_get_value_cansleep(nand->busy_gpio); -} - -static void jz4780_nand_ecc_hwctl(struct mtd_info *mtd, int mode) -{ - struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); - - nand->reading = (mode == NAND_ECC_READ); -} - -static int jz4780_nand_ecc_calculate(struct mtd_info *mtd, const u8 *dat, - u8 *ecc_code) -{ - struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); - struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); - struct jz4780_bch_params params; - - /* - * Don't need to generate the ECC when reading, BCH does it for us as - * part of decoding/correction. - */ - if (nand->reading) - return 0; - - params.size = nand->chip.ecc.size; - params.bytes = nand->chip.ecc.bytes; - params.strength = nand->chip.ecc.strength; - - return jz4780_bch_calculate(nfc->bch, ¶ms, dat, ecc_code); -} - -static int jz4780_nand_ecc_correct(struct mtd_info *mtd, u8 *dat, - u8 *read_ecc, u8 *calc_ecc) -{ - struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); - struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); - struct jz4780_bch_params params; - - params.size = nand->chip.ecc.size; - params.bytes = nand->chip.ecc.bytes; - params.strength = nand->chip.ecc.strength; - - return jz4780_bch_correct(nfc->bch, ¶ms, dat, read_ecc); -} - -static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *dev) -{ - struct nand_chip *chip = &nand->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller); - int eccbytes; - - chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) * - (chip->ecc.strength / 8); - - switch (chip->ecc.mode) { - case NAND_ECC_HW: - if (!nfc->bch) { - dev_err(dev, "HW BCH selected, but BCH controller not found\n"); - return -ENODEV; - } - - chip->ecc.hwctl = jz4780_nand_ecc_hwctl; - chip->ecc.calculate = jz4780_nand_ecc_calculate; - chip->ecc.correct = jz4780_nand_ecc_correct; - /* fall through */ - case NAND_ECC_SOFT: - dev_info(dev, "using %s (strength %d, size %d, bytes %d)\n", - (nfc->bch) ? "hardware BCH" : "software ECC", - chip->ecc.strength, chip->ecc.size, chip->ecc.bytes); - break; - case NAND_ECC_NONE: - dev_info(dev, "not using ECC\n"); - break; - default: - dev_err(dev, "ECC mode %d not supported\n", chip->ecc.mode); - return -EINVAL; - } - - /* The NAND core will generate the ECC layout for SW ECC */ - if (chip->ecc.mode != NAND_ECC_HW) - return 0; - - /* Generate ECC layout. ECC codes are right aligned in the OOB area. */ - eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes; - - if (eccbytes > mtd->oobsize - 2) { - dev_err(dev, - "invalid ECC config: required %d ECC bytes, but only %d are available", - eccbytes, mtd->oobsize - 2); - return -EINVAL; - } - - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); - - return 0; -} - -static int jz4780_nand_init_chip(struct platform_device *pdev, - struct jz4780_nand_controller *nfc, - struct device_node *np, - unsigned int chipnr) -{ - struct device *dev = &pdev->dev; - struct jz4780_nand_chip *nand; - struct jz4780_nand_cs *cs; - struct resource *res; - struct nand_chip *chip; - struct mtd_info *mtd; - const __be32 *reg; - int ret = 0; - - cs = &nfc->cs[chipnr]; - - reg = of_get_property(np, "reg", NULL); - if (!reg) - return -EINVAL; - - cs->bank = be32_to_cpu(*reg); - - jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND); - - res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr); - cs->base = devm_ioremap_resource(dev, res); - if (IS_ERR(cs->base)) - return PTR_ERR(cs->base); - - nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL); - if (!nand) - return -ENOMEM; - - nand->busy_gpio = devm_gpiod_get_optional(dev, "rb", GPIOD_IN); - - if (IS_ERR(nand->busy_gpio)) { - ret = PTR_ERR(nand->busy_gpio); - dev_err(dev, "failed to request busy GPIO: %d\n", ret); - return ret; - } else if (nand->busy_gpio) { - nand->chip.dev_ready = jz4780_nand_dev_ready; - } - - nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW); - - if (IS_ERR(nand->wp_gpio)) { - ret = PTR_ERR(nand->wp_gpio); - dev_err(dev, "failed to request WP GPIO: %d\n", ret); - return ret; - } - - chip = &nand->chip; - mtd = nand_to_mtd(chip); - mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev), - cs->bank); - if (!mtd->name) - return -ENOMEM; - mtd->dev.parent = dev; - - chip->IO_ADDR_R = cs->base + OFFSET_DATA; - chip->IO_ADDR_W = cs->base + OFFSET_DATA; - chip->chip_delay = RB_DELAY_US; - chip->options = NAND_NO_SUBPAGE_WRITE; - chip->select_chip = jz4780_nand_select_chip; - chip->cmd_ctrl = jz4780_nand_cmd_ctrl; - chip->ecc.mode = NAND_ECC_HW; - chip->controller = &nfc->controller; - nand_set_flash_node(chip, np); - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - ret = jz4780_nand_init_ecc(nand, dev); - if (ret) - return ret; - - ret = nand_scan_tail(mtd); - if (ret) - return ret; - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - nand_release(mtd); - return ret; - } - - list_add_tail(&nand->chip_list, &nfc->chips); - - return 0; -} - -static void jz4780_nand_cleanup_chips(struct jz4780_nand_controller *nfc) -{ - struct jz4780_nand_chip *chip; - - while (!list_empty(&nfc->chips)) { - chip = list_first_entry(&nfc->chips, struct jz4780_nand_chip, chip_list); - nand_release(nand_to_mtd(&chip->chip)); - list_del(&chip->chip_list); - } -} - -static int jz4780_nand_init_chips(struct jz4780_nand_controller *nfc, - struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np; - int i = 0; - int ret; - int num_chips = of_get_child_count(dev->of_node); - - if (num_chips > nfc->num_banks) { - dev_err(dev, "found %d chips but only %d banks\n", num_chips, nfc->num_banks); - return -EINVAL; - } - - for_each_child_of_node(dev->of_node, np) { - ret = jz4780_nand_init_chip(pdev, nfc, np, i); - if (ret) { - jz4780_nand_cleanup_chips(nfc); - return ret; - } - - i++; - } - - return 0; -} - -static int jz4780_nand_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - unsigned int num_banks; - struct jz4780_nand_controller *nfc; - int ret; - - num_banks = jz4780_nemc_num_banks(dev); - if (num_banks == 0) { - dev_err(dev, "no banks found\n"); - return -ENODEV; - } - - nfc = devm_kzalloc(dev, sizeof(*nfc) + (sizeof(nfc->cs[0]) * num_banks), GFP_KERNEL); - if (!nfc) - return -ENOMEM; - - /* - * Check for BCH HW before we call nand_scan_ident, to prevent us from - * having to call it again if the BCH driver returns -EPROBE_DEFER. - */ - nfc->bch = of_jz4780_bch_get(dev->of_node); - if (IS_ERR(nfc->bch)) - return PTR_ERR(nfc->bch); - - nfc->dev = dev; - nfc->num_banks = num_banks; - - nand_hw_control_init(&nfc->controller); - INIT_LIST_HEAD(&nfc->chips); - - ret = jz4780_nand_init_chips(nfc, pdev); - if (ret) { - if (nfc->bch) - jz4780_bch_release(nfc->bch); - return ret; - } - - platform_set_drvdata(pdev, nfc); - return 0; -} - -static int jz4780_nand_remove(struct platform_device *pdev) -{ - struct jz4780_nand_controller *nfc = platform_get_drvdata(pdev); - - if (nfc->bch) - jz4780_bch_release(nfc->bch); - - jz4780_nand_cleanup_chips(nfc); - - return 0; -} - -static const struct of_device_id jz4780_nand_dt_match[] = { - { .compatible = "ingenic,jz4780-nand" }, - {}, -}; -MODULE_DEVICE_TABLE(of, jz4780_nand_dt_match); - -static struct platform_driver jz4780_nand_driver = { - .probe = jz4780_nand_probe, - .remove = jz4780_nand_remove, - .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(jz4780_nand_dt_match), - }, -}; -module_platform_driver(jz4780_nand_driver); - -MODULE_AUTHOR("Alex Smith "); -MODULE_AUTHOR("Harvey Hunt "); -MODULE_DESCRIPTION("Ingenic JZ4780 NAND driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c deleted file mode 100644 index e357948a7505..000000000000 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ /dev/null @@ -1,909 +0,0 @@ -/* - * Driver for NAND MLC Controller in LPC32xx - * - * Author: Roland Stigge - * - * Copyright © 2011 WORK Microwave GmbH - * Copyright © 2011, 2012 Roland Stigge - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * NAND Flash Controller Operation: - * - Read: Auto Decode - * - Write: Auto Encode - * - Tested Page Sizes: 2048, 4096 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "lpc32xx_mlc" - -/********************************************************************** -* MLC NAND controller register offsets -**********************************************************************/ - -#define MLC_BUFF(x) (x + 0x00000) -#define MLC_DATA(x) (x + 0x08000) -#define MLC_CMD(x) (x + 0x10000) -#define MLC_ADDR(x) (x + 0x10004) -#define MLC_ECC_ENC_REG(x) (x + 0x10008) -#define MLC_ECC_DEC_REG(x) (x + 0x1000C) -#define MLC_ECC_AUTO_ENC_REG(x) (x + 0x10010) -#define MLC_ECC_AUTO_DEC_REG(x) (x + 0x10014) -#define MLC_RPR(x) (x + 0x10018) -#define MLC_WPR(x) (x + 0x1001C) -#define MLC_RUBP(x) (x + 0x10020) -#define MLC_ROBP(x) (x + 0x10024) -#define MLC_SW_WP_ADD_LOW(x) (x + 0x10028) -#define MLC_SW_WP_ADD_HIG(x) (x + 0x1002C) -#define MLC_ICR(x) (x + 0x10030) -#define MLC_TIME_REG(x) (x + 0x10034) -#define MLC_IRQ_MR(x) (x + 0x10038) -#define MLC_IRQ_SR(x) (x + 0x1003C) -#define MLC_LOCK_PR(x) (x + 0x10044) -#define MLC_ISR(x) (x + 0x10048) -#define MLC_CEH(x) (x + 0x1004C) - -/********************************************************************** -* MLC_CMD bit definitions -**********************************************************************/ -#define MLCCMD_RESET 0xFF - -/********************************************************************** -* MLC_ICR bit definitions -**********************************************************************/ -#define MLCICR_WPROT (1 << 3) -#define MLCICR_LARGEBLOCK (1 << 2) -#define MLCICR_LONGADDR (1 << 1) -#define MLCICR_16BIT (1 << 0) /* unsupported by LPC32x0! */ - -/********************************************************************** -* MLC_TIME_REG bit definitions -**********************************************************************/ -#define MLCTIMEREG_TCEA_DELAY(n) (((n) & 0x03) << 24) -#define MLCTIMEREG_BUSY_DELAY(n) (((n) & 0x1F) << 19) -#define MLCTIMEREG_NAND_TA(n) (((n) & 0x07) << 16) -#define MLCTIMEREG_RD_HIGH(n) (((n) & 0x0F) << 12) -#define MLCTIMEREG_RD_LOW(n) (((n) & 0x0F) << 8) -#define MLCTIMEREG_WR_HIGH(n) (((n) & 0x0F) << 4) -#define MLCTIMEREG_WR_LOW(n) (((n) & 0x0F) << 0) - -/********************************************************************** -* MLC_IRQ_MR and MLC_IRQ_SR bit definitions -**********************************************************************/ -#define MLCIRQ_NAND_READY (1 << 5) -#define MLCIRQ_CONTROLLER_READY (1 << 4) -#define MLCIRQ_DECODE_FAILURE (1 << 3) -#define MLCIRQ_DECODE_ERROR (1 << 2) -#define MLCIRQ_ECC_READY (1 << 1) -#define MLCIRQ_WRPROT_FAULT (1 << 0) - -/********************************************************************** -* MLC_LOCK_PR bit definitions -**********************************************************************/ -#define MLCLOCKPR_MAGIC 0xA25E - -/********************************************************************** -* MLC_ISR bit definitions -**********************************************************************/ -#define MLCISR_DECODER_FAILURE (1 << 6) -#define MLCISR_ERRORS ((1 << 4) | (1 << 5)) -#define MLCISR_ERRORS_DETECTED (1 << 3) -#define MLCISR_ECC_READY (1 << 2) -#define MLCISR_CONTROLLER_READY (1 << 1) -#define MLCISR_NAND_READY (1 << 0) - -/********************************************************************** -* MLC_CEH bit definitions -**********************************************************************/ -#define MLCCEH_NORMAL (1 << 0) - -struct lpc32xx_nand_cfg_mlc { - uint32_t tcea_delay; - uint32_t busy_delay; - uint32_t nand_ta; - uint32_t rd_high; - uint32_t rd_low; - uint32_t wr_high; - uint32_t wr_low; - int wp_gpio; - struct mtd_partition *parts; - unsigned num_parts; -}; - -static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - - if (section >= nand_chip->ecc.steps) - return -ERANGE; - - oobregion->offset = ((section + 1) * 16) - nand_chip->ecc.bytes; - oobregion->length = nand_chip->ecc.bytes; - - return 0; -} - -static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - - if (section >= nand_chip->ecc.steps) - return -ERANGE; - - oobregion->offset = 16 * section; - oobregion->length = 16 - nand_chip->ecc.bytes; - - return 0; -} - -static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = { - .ecc = lpc32xx_ooblayout_ecc, - .free = lpc32xx_ooblayout_free, -}; - -static struct nand_bbt_descr lpc32xx_nand_bbt = { - .options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB | - NAND_BBT_WRITE, - .pages = { 524224, 0, 0, 0, 0, 0, 0, 0 }, -}; - -static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = { - .options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB | - NAND_BBT_WRITE, - .pages = { 524160, 0, 0, 0, 0, 0, 0, 0 }, -}; - -struct lpc32xx_nand_host { - struct nand_chip nand_chip; - struct lpc32xx_mlc_platform_data *pdata; - struct clk *clk; - void __iomem *io_base; - int irq; - struct lpc32xx_nand_cfg_mlc *ncfg; - struct completion comp_nand; - struct completion comp_controller; - uint32_t llptr; - /* - * Physical addresses of ECC buffer, DMA data buffers, OOB data buffer - */ - dma_addr_t oob_buf_phy; - /* - * Virtual addresses of ECC buffer, DMA data buffers, OOB data buffer - */ - uint8_t *oob_buf; - /* Physical address of DMA base address */ - dma_addr_t io_base_phy; - - struct completion comp_dma; - struct dma_chan *dma_chan; - struct dma_slave_config dma_slave_config; - struct scatterlist sgl; - uint8_t *dma_buf; - uint8_t *dummy_buf; - int mlcsubpages; /* number of 512bytes-subpages */ -}; - -/* - * Activate/Deactivate DMA Operation: - * - * Using the PL080 DMA Controller for transferring the 512 byte subpages - * instead of doing readl() / writel() in a loop slows it down significantly. - * Measurements via getnstimeofday() upon 512 byte subpage reads reveal: - * - * - readl() of 128 x 32 bits in a loop: ~20us - * - DMA read of 512 bytes (32 bit, 4...128 words bursts): ~60us - * - DMA read of 512 bytes (32 bit, no bursts): ~100us - * - * This applies to the transfer itself. In the DMA case: only the - * wait_for_completion() (DMA setup _not_ included). - * - * Note that the 512 bytes subpage transfer is done directly from/to a - * FIFO/buffer inside the NAND controller. Most of the time (~400-800us for a - * 2048 bytes page) is spent waiting for the NAND IRQ, anyway. (The NAND - * controller transferring data between its internal buffer to/from the NAND - * chip.) - * - * Therefore, using the PL080 DMA is disabled by default, for now. - * - */ -static int use_dma; - -static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) -{ - uint32_t clkrate, tmp; - - /* Reset MLC controller */ - writel(MLCCMD_RESET, MLC_CMD(host->io_base)); - udelay(1000); - - /* Get base clock for MLC block */ - clkrate = clk_get_rate(host->clk); - if (clkrate == 0) - clkrate = 104000000; - - /* Unlock MLC_ICR - * (among others, will be locked again automatically) */ - writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base)); - - /* Configure MLC Controller: Large Block, 5 Byte Address */ - tmp = MLCICR_LARGEBLOCK | MLCICR_LONGADDR; - writel(tmp, MLC_ICR(host->io_base)); - - /* Unlock MLC_TIME_REG - * (among others, will be locked again automatically) */ - writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base)); - - /* Compute clock setup values, see LPC and NAND manual */ - tmp = 0; - tmp |= MLCTIMEREG_TCEA_DELAY(clkrate / host->ncfg->tcea_delay + 1); - tmp |= MLCTIMEREG_BUSY_DELAY(clkrate / host->ncfg->busy_delay + 1); - tmp |= MLCTIMEREG_NAND_TA(clkrate / host->ncfg->nand_ta + 1); - tmp |= MLCTIMEREG_RD_HIGH(clkrate / host->ncfg->rd_high + 1); - tmp |= MLCTIMEREG_RD_LOW(clkrate / host->ncfg->rd_low); - tmp |= MLCTIMEREG_WR_HIGH(clkrate / host->ncfg->wr_high + 1); - tmp |= MLCTIMEREG_WR_LOW(clkrate / host->ncfg->wr_low); - writel(tmp, MLC_TIME_REG(host->io_base)); - - /* Enable IRQ for CONTROLLER_READY and NAND_READY */ - writeb(MLCIRQ_CONTROLLER_READY | MLCIRQ_NAND_READY, - MLC_IRQ_MR(host->io_base)); - - /* Normal nCE operation: nCE controlled by controller */ - writel(MLCCEH_NORMAL, MLC_CEH(host->io_base)); -} - -/* - * Hardware specific access to control lines - */ -static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip); - - if (cmd != NAND_CMD_NONE) { - if (ctrl & NAND_CLE) - writel(cmd, MLC_CMD(host->io_base)); - else - writel(cmd, MLC_ADDR(host->io_base)); - } -} - -/* - * Read Device Ready (NAND device _and_ controller ready) - */ -static int lpc32xx_nand_device_ready(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip); - - if ((readb(MLC_ISR(host->io_base)) & - (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) == - (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) - return 1; - - return 0; -} - -static irqreturn_t lpc3xxx_nand_irq(int irq, struct lpc32xx_nand_host *host) -{ - uint8_t sr; - - /* Clear interrupt flag by reading status */ - sr = readb(MLC_IRQ_SR(host->io_base)); - if (sr & MLCIRQ_NAND_READY) - complete(&host->comp_nand); - if (sr & MLCIRQ_CONTROLLER_READY) - complete(&host->comp_controller); - - return IRQ_HANDLED; -} - -static int lpc32xx_waitfunc_nand(struct mtd_info *mtd, struct nand_chip *chip) -{ - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - - if (readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY) - goto exit; - - wait_for_completion(&host->comp_nand); - - while (!(readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)) { - /* Seems to be delayed sometimes by controller */ - dev_dbg(&mtd->dev, "Warning: NAND not ready.\n"); - cpu_relax(); - } - -exit: - return NAND_STATUS_READY; -} - -static int lpc32xx_waitfunc_controller(struct mtd_info *mtd, - struct nand_chip *chip) -{ - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - - if (readb(MLC_ISR(host->io_base)) & MLCISR_CONTROLLER_READY) - goto exit; - - wait_for_completion(&host->comp_controller); - - while (!(readb(MLC_ISR(host->io_base)) & - MLCISR_CONTROLLER_READY)) { - dev_dbg(&mtd->dev, "Warning: Controller not ready.\n"); - cpu_relax(); - } - -exit: - return NAND_STATUS_READY; -} - -static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) -{ - lpc32xx_waitfunc_nand(mtd, chip); - lpc32xx_waitfunc_controller(mtd, chip); - - return NAND_STATUS_READY; -} - -/* - * Enable NAND write protect - */ -static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) -{ - if (gpio_is_valid(host->ncfg->wp_gpio)) - gpio_set_value(host->ncfg->wp_gpio, 0); -} - -/* - * Disable NAND write protect - */ -static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) -{ - if (gpio_is_valid(host->ncfg->wp_gpio)) - gpio_set_value(host->ncfg->wp_gpio, 1); -} - -static void lpc32xx_dma_complete_func(void *completion) -{ - complete(completion); -} - -static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, - enum dma_transfer_direction dir) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - struct dma_async_tx_descriptor *desc; - int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; - int res; - - sg_init_one(&host->sgl, mem, len); - - res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); - if (res != 1) { - dev_err(mtd->dev.parent, "Failed to map sg list\n"); - return -ENXIO; - } - desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir, - flags); - if (!desc) { - dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); - goto out1; - } - - init_completion(&host->comp_dma); - desc->callback = lpc32xx_dma_complete_func; - desc->callback_param = &host->comp_dma; - - dmaengine_submit(desc); - dma_async_issue_pending(host->dma_chan); - - wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000)); - - dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); - return 0; -out1: - dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); - return -ENXIO; -} - -static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - int i, j; - uint8_t *oobbuf = chip->oob_poi; - uint32_t mlc_isr; - int res; - uint8_t *dma_buf; - bool dma_mapped; - - if ((void *)buf <= high_memory) { - dma_buf = buf; - dma_mapped = true; - } else { - dma_buf = host->dma_buf; - dma_mapped = false; - } - - /* Writing Command and Address */ - nand_read_page_op(chip, page, 0, NULL, 0); - - /* For all sub-pages */ - for (i = 0; i < host->mlcsubpages; i++) { - /* Start Auto Decode Command */ - writeb(0x00, MLC_ECC_AUTO_DEC_REG(host->io_base)); - - /* Wait for Controller Ready */ - lpc32xx_waitfunc_controller(mtd, chip); - - /* Check ECC Error status */ - mlc_isr = readl(MLC_ISR(host->io_base)); - if (mlc_isr & MLCISR_DECODER_FAILURE) { - mtd->ecc_stats.failed++; - dev_warn(&mtd->dev, "%s: DECODER_FAILURE\n", __func__); - } else if (mlc_isr & MLCISR_ERRORS_DETECTED) { - mtd->ecc_stats.corrected += ((mlc_isr >> 4) & 0x3) + 1; - } - - /* Read 512 + 16 Bytes */ - if (use_dma) { - res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512, - DMA_DEV_TO_MEM); - if (res) - return res; - } else { - for (j = 0; j < (512 >> 2); j++) { - *((uint32_t *)(buf)) = - readl(MLC_BUFF(host->io_base)); - buf += 4; - } - } - for (j = 0; j < (16 >> 2); j++) { - *((uint32_t *)(oobbuf)) = - readl(MLC_BUFF(host->io_base)); - oobbuf += 4; - } - } - - if (use_dma && !dma_mapped) - memcpy(buf, dma_buf, mtd->writesize); - - return 0; -} - -static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - const uint8_t *oobbuf = chip->oob_poi; - uint8_t *dma_buf = (uint8_t *)buf; - int res; - int i, j; - - if (use_dma && (void *)buf >= high_memory) { - dma_buf = host->dma_buf; - memcpy(dma_buf, buf, mtd->writesize); - } - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - for (i = 0; i < host->mlcsubpages; i++) { - /* Start Encode */ - writeb(0x00, MLC_ECC_ENC_REG(host->io_base)); - - /* Write 512 + 6 Bytes to Buffer */ - if (use_dma) { - res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512, - DMA_MEM_TO_DEV); - if (res) - return res; - } else { - for (j = 0; j < (512 >> 2); j++) { - writel(*((uint32_t *)(buf)), - MLC_BUFF(host->io_base)); - buf += 4; - } - } - writel(*((uint32_t *)(oobbuf)), MLC_BUFF(host->io_base)); - oobbuf += 4; - writew(*((uint16_t *)(oobbuf)), MLC_BUFF(host->io_base)); - oobbuf += 12; - - /* Auto Encode w/ Bit 8 = 0 (see LPC MLC Controller manual) */ - writeb(0x00, MLC_ECC_AUTO_ENC_REG(host->io_base)); - - /* Wait for Controller Ready */ - lpc32xx_waitfunc_controller(mtd, chip); - } - - return nand_prog_page_end_op(chip); -} - -static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - - /* Read whole page - necessary with MLC controller! */ - lpc32xx_read_page(mtd, chip, host->dummy_buf, 1, page); - - return 0; -} - -static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - /* None, write_oob conflicts with the automatic LPC MLC ECC decoder! */ - return 0; -} - -/* Prepares MLC for transfers with H/W ECC enabled: always enabled anyway */ -static void lpc32xx_ecc_enable(struct mtd_info *mtd, int mode) -{ - /* Always enabled! */ -} - -static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host) -{ - struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); - dma_cap_mask_t mask; - - if (!host->pdata || !host->pdata->dma_filter) { - dev_err(mtd->dev.parent, "no DMA platform data\n"); - return -ENOENT; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, - "nand-mlc"); - if (!host->dma_chan) { - dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); - return -EBUSY; - } - - /* - * Set direction to a sensible value even if the dmaengine driver - * should ignore it. With the default (DMA_MEM_TO_MEM), the amba-pl08x - * driver criticizes it as "alien transfer direction". - */ - host->dma_slave_config.direction = DMA_DEV_TO_MEM; - host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_slave_config.src_maxburst = 128; - host->dma_slave_config.dst_maxburst = 128; - /* DMA controller does flow control: */ - host->dma_slave_config.device_fc = false; - host->dma_slave_config.src_addr = MLC_BUFF(host->io_base_phy); - host->dma_slave_config.dst_addr = MLC_BUFF(host->io_base_phy); - if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) { - dev_err(mtd->dev.parent, "Failed to setup DMA slave\n"); - goto out1; - } - - return 0; -out1: - dma_release_channel(host->dma_chan); - return -ENXIO; -} - -static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev) -{ - struct lpc32xx_nand_cfg_mlc *ncfg; - struct device_node *np = dev->of_node; - - ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL); - if (!ncfg) - return NULL; - - of_property_read_u32(np, "nxp,tcea-delay", &ncfg->tcea_delay); - of_property_read_u32(np, "nxp,busy-delay", &ncfg->busy_delay); - of_property_read_u32(np, "nxp,nand-ta", &ncfg->nand_ta); - of_property_read_u32(np, "nxp,rd-high", &ncfg->rd_high); - of_property_read_u32(np, "nxp,rd-low", &ncfg->rd_low); - of_property_read_u32(np, "nxp,wr-high", &ncfg->wr_high); - of_property_read_u32(np, "nxp,wr-low", &ncfg->wr_low); - - if (!ncfg->tcea_delay || !ncfg->busy_delay || !ncfg->nand_ta || - !ncfg->rd_high || !ncfg->rd_low || !ncfg->wr_high || - !ncfg->wr_low) { - dev_err(dev, "chip parameters not specified correctly\n"); - return NULL; - } - - ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); - - return ncfg; -} - -/* - * Probe for NAND controller - */ -static int lpc32xx_nand_probe(struct platform_device *pdev) -{ - struct lpc32xx_nand_host *host; - struct mtd_info *mtd; - struct nand_chip *nand_chip; - struct resource *rc; - int res; - - /* Allocate memory for the device structure (and zero it) */ - host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); - if (!host) - return -ENOMEM; - - rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->io_base = devm_ioremap_resource(&pdev->dev, rc); - if (IS_ERR(host->io_base)) - return PTR_ERR(host->io_base); - - host->io_base_phy = rc->start; - - nand_chip = &host->nand_chip; - mtd = nand_to_mtd(nand_chip); - if (pdev->dev.of_node) - host->ncfg = lpc32xx_parse_dt(&pdev->dev); - if (!host->ncfg) { - dev_err(&pdev->dev, - "Missing or bad NAND config from device tree\n"); - return -ENOENT; - } - if (host->ncfg->wp_gpio == -EPROBE_DEFER) - return -EPROBE_DEFER; - if (gpio_is_valid(host->ncfg->wp_gpio) && - gpio_request(host->ncfg->wp_gpio, "NAND WP")) { - dev_err(&pdev->dev, "GPIO not available\n"); - return -EBUSY; - } - lpc32xx_wp_disable(host); - - host->pdata = dev_get_platdata(&pdev->dev); - - /* link the private data structures */ - nand_set_controller_data(nand_chip, host); - nand_set_flash_node(nand_chip, pdev->dev.of_node); - mtd->dev.parent = &pdev->dev; - - /* Get NAND clock */ - host->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - dev_err(&pdev->dev, "Clock initialization failure\n"); - res = -ENOENT; - goto err_exit1; - } - res = clk_prepare_enable(host->clk); - if (res) - goto err_put_clk; - - nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; - nand_chip->dev_ready = lpc32xx_nand_device_ready; - nand_chip->chip_delay = 25; /* us */ - nand_chip->IO_ADDR_R = MLC_DATA(host->io_base); - nand_chip->IO_ADDR_W = MLC_DATA(host->io_base); - - /* Init NAND controller */ - lpc32xx_nand_setup(host); - - platform_set_drvdata(pdev, host); - - /* Initialize function pointers */ - nand_chip->ecc.hwctl = lpc32xx_ecc_enable; - nand_chip->ecc.read_page_raw = lpc32xx_read_page; - nand_chip->ecc.read_page = lpc32xx_read_page; - nand_chip->ecc.write_page_raw = lpc32xx_write_page_lowlevel; - nand_chip->ecc.write_page = lpc32xx_write_page_lowlevel; - nand_chip->ecc.write_oob = lpc32xx_write_oob; - nand_chip->ecc.read_oob = lpc32xx_read_oob; - nand_chip->ecc.strength = 4; - nand_chip->ecc.bytes = 10; - nand_chip->waitfunc = lpc32xx_waitfunc; - - nand_chip->options = NAND_NO_SUBPAGE_WRITE; - nand_chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; - nand_chip->bbt_td = &lpc32xx_nand_bbt; - nand_chip->bbt_md = &lpc32xx_nand_bbt_mirror; - - if (use_dma) { - res = lpc32xx_dma_setup(host); - if (res) { - res = -EIO; - goto err_exit2; - } - } - - /* - * Scan to find existance of the device and - * Get the type of NAND device SMALL block or LARGE block - */ - res = nand_scan_ident(mtd, 1, NULL); - if (res) - goto err_exit3; - - host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); - if (!host->dma_buf) { - res = -ENOMEM; - goto err_exit3; - } - - host->dummy_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); - if (!host->dummy_buf) { - res = -ENOMEM; - goto err_exit3; - } - - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); - host->mlcsubpages = mtd->writesize / 512; - - /* initially clear interrupt status */ - readb(MLC_IRQ_SR(host->io_base)); - - init_completion(&host->comp_nand); - init_completion(&host->comp_controller); - - host->irq = platform_get_irq(pdev, 0); - if (host->irq < 0) { - dev_err(&pdev->dev, "failed to get platform irq\n"); - res = -EINVAL; - goto err_exit3; - } - - if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq, - IRQF_TRIGGER_HIGH, DRV_NAME, host)) { - dev_err(&pdev->dev, "Error requesting NAND IRQ\n"); - res = -ENXIO; - goto err_exit3; - } - - /* - * Fills out all the uninitialized function pointers with the defaults - * And scans for a bad block table if appropriate. - */ - res = nand_scan_tail(mtd); - if (res) - goto err_exit4; - - mtd->name = DRV_NAME; - - res = mtd_device_register(mtd, host->ncfg->parts, - host->ncfg->num_parts); - if (!res) - return res; - - nand_release(mtd); - -err_exit4: - free_irq(host->irq, host); -err_exit3: - if (use_dma) - dma_release_channel(host->dma_chan); -err_exit2: - clk_disable_unprepare(host->clk); -err_put_clk: - clk_put(host->clk); -err_exit1: - lpc32xx_wp_enable(host); - gpio_free(host->ncfg->wp_gpio); - - return res; -} - -/* - * Remove NAND device - */ -static int lpc32xx_nand_remove(struct platform_device *pdev) -{ - struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); - struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); - - nand_release(mtd); - free_irq(host->irq, host); - if (use_dma) - dma_release_channel(host->dma_chan); - - clk_disable_unprepare(host->clk); - clk_put(host->clk); - - lpc32xx_wp_enable(host); - gpio_free(host->ncfg->wp_gpio); - - return 0; -} - -#ifdef CONFIG_PM -static int lpc32xx_nand_resume(struct platform_device *pdev) -{ - struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); - int ret; - - /* Re-enable NAND clock */ - ret = clk_prepare_enable(host->clk); - if (ret) - return ret; - - /* Fresh init of NAND controller */ - lpc32xx_nand_setup(host); - - /* Disable write protect */ - lpc32xx_wp_disable(host); - - return 0; -} - -static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) -{ - struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); - - /* Enable write protect for safety */ - lpc32xx_wp_enable(host); - - /* Disable clock */ - clk_disable_unprepare(host->clk); - return 0; -} - -#else -#define lpc32xx_nand_resume NULL -#define lpc32xx_nand_suspend NULL -#endif - -static const struct of_device_id lpc32xx_nand_match[] = { - { .compatible = "nxp,lpc3220-mlc" }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); - -static struct platform_driver lpc32xx_nand_driver = { - .probe = lpc32xx_nand_probe, - .remove = lpc32xx_nand_remove, - .resume = lpc32xx_nand_resume, - .suspend = lpc32xx_nand_suspend, - .driver = { - .name = DRV_NAME, - .of_match_table = lpc32xx_nand_match, - }, -}; - -module_platform_driver(lpc32xx_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Roland Stigge "); -MODULE_DESCRIPTION("NAND driver for the NXP LPC32XX MLC controller"); diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c deleted file mode 100644 index 5f7cc6da0a7f..000000000000 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ /dev/null @@ -1,1032 +0,0 @@ -/* - * NXP LPC32XX NAND SLC driver - * - * Authors: - * Kevin Wells - * Roland Stigge - * - * Copyright © 2011 NXP Semiconductors - * Copyright © 2012 Roland Stigge - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LPC32XX_MODNAME "lpc32xx-nand" - -/********************************************************************** -* SLC NAND controller register offsets -**********************************************************************/ - -#define SLC_DATA(x) (x + 0x000) -#define SLC_ADDR(x) (x + 0x004) -#define SLC_CMD(x) (x + 0x008) -#define SLC_STOP(x) (x + 0x00C) -#define SLC_CTRL(x) (x + 0x010) -#define SLC_CFG(x) (x + 0x014) -#define SLC_STAT(x) (x + 0x018) -#define SLC_INT_STAT(x) (x + 0x01C) -#define SLC_IEN(x) (x + 0x020) -#define SLC_ISR(x) (x + 0x024) -#define SLC_ICR(x) (x + 0x028) -#define SLC_TAC(x) (x + 0x02C) -#define SLC_TC(x) (x + 0x030) -#define SLC_ECC(x) (x + 0x034) -#define SLC_DMA_DATA(x) (x + 0x038) - -/********************************************************************** -* slc_ctrl register definitions -**********************************************************************/ -#define SLCCTRL_SW_RESET (1 << 2) /* Reset the NAND controller bit */ -#define SLCCTRL_ECC_CLEAR (1 << 1) /* Reset ECC bit */ -#define SLCCTRL_DMA_START (1 << 0) /* Start DMA channel bit */ - -/********************************************************************** -* slc_cfg register definitions -**********************************************************************/ -#define SLCCFG_CE_LOW (1 << 5) /* Force CE low bit */ -#define SLCCFG_DMA_ECC (1 << 4) /* Enable DMA ECC bit */ -#define SLCCFG_ECC_EN (1 << 3) /* ECC enable bit */ -#define SLCCFG_DMA_BURST (1 << 2) /* DMA burst bit */ -#define SLCCFG_DMA_DIR (1 << 1) /* DMA write(0)/read(1) bit */ -#define SLCCFG_WIDTH (1 << 0) /* External device width, 0=8bit */ - -/********************************************************************** -* slc_stat register definitions -**********************************************************************/ -#define SLCSTAT_DMA_FIFO (1 << 2) /* DMA FIFO has data bit */ -#define SLCSTAT_SLC_FIFO (1 << 1) /* SLC FIFO has data bit */ -#define SLCSTAT_NAND_READY (1 << 0) /* NAND device is ready bit */ - -/********************************************************************** -* slc_int_stat, slc_ien, slc_isr, and slc_icr register definitions -**********************************************************************/ -#define SLCSTAT_INT_TC (1 << 1) /* Transfer count bit */ -#define SLCSTAT_INT_RDY_EN (1 << 0) /* Ready interrupt bit */ - -/********************************************************************** -* slc_tac register definitions -**********************************************************************/ -/* Computation of clock cycles on basis of controller and device clock rates */ -#define SLCTAC_CLOCKS(c, n, s) (min_t(u32, DIV_ROUND_UP(c, n) - 1, 0xF) << s) - -/* Clock setting for RDY write sample wait time in 2*n clocks */ -#define SLCTAC_WDR(n) (((n) & 0xF) << 28) -/* Write pulse width in clock cycles, 1 to 16 clocks */ -#define SLCTAC_WWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 24)) -/* Write hold time of control and data signals, 1 to 16 clocks */ -#define SLCTAC_WHOLD(c, n) (SLCTAC_CLOCKS(c, n, 20)) -/* Write setup time of control and data signals, 1 to 16 clocks */ -#define SLCTAC_WSETUP(c, n) (SLCTAC_CLOCKS(c, n, 16)) -/* Clock setting for RDY read sample wait time in 2*n clocks */ -#define SLCTAC_RDR(n) (((n) & 0xF) << 12) -/* Read pulse width in clock cycles, 1 to 16 clocks */ -#define SLCTAC_RWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 8)) -/* Read hold time of control and data signals, 1 to 16 clocks */ -#define SLCTAC_RHOLD(c, n) (SLCTAC_CLOCKS(c, n, 4)) -/* Read setup time of control and data signals, 1 to 16 clocks */ -#define SLCTAC_RSETUP(c, n) (SLCTAC_CLOCKS(c, n, 0)) - -/********************************************************************** -* slc_ecc register definitions -**********************************************************************/ -/* ECC line party fetch macro */ -#define SLCECC_TO_LINEPAR(n) (((n) >> 6) & 0x7FFF) -#define SLCECC_TO_COLPAR(n) ((n) & 0x3F) - -/* - * DMA requires storage space for the DMA local buffer and the hardware ECC - * storage area. The DMA local buffer is only used if DMA mapping fails - * during runtime. - */ -#define LPC32XX_DMA_DATA_SIZE 4096 -#define LPC32XX_ECC_SAVE_SIZE ((4096 / 256) * 4) - -/* Number of bytes used for ECC stored in NAND per 256 bytes */ -#define LPC32XX_SLC_DEV_ECC_BYTES 3 - -/* - * If the NAND base clock frequency can't be fetched, this frequency will be - * used instead as the base. This rate is used to setup the timing registers - * used for NAND accesses. - */ -#define LPC32XX_DEF_BUS_RATE 133250000 - -/* Milliseconds for DMA FIFO timeout (unlikely anyway) */ -#define LPC32XX_DMA_TIMEOUT 100 - -/* - * NAND ECC Layout for small page NAND devices - * Note: For large and huge page devices, the default layouts are used - */ -static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->length = 6; - oobregion->offset = 10; - - return 0; -} - -static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 1) - return -ERANGE; - - if (!section) { - oobregion->offset = 0; - oobregion->length = 4; - } else { - oobregion->offset = 6; - oobregion->length = 4; - } - - return 0; -} - -static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = { - .ecc = lpc32xx_ooblayout_ecc, - .free = lpc32xx_ooblayout_free, -}; - -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; - -/* - * Small page FLASH BBT descriptors, marker at offset 0, version at offset 6 - * Note: Large page devices used the default layout - */ -static struct nand_bbt_descr bbt_smallpage_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 0, - .len = 4, - .veroffs = 6, - .maxblocks = 4, - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_smallpage_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 0, - .len = 4, - .veroffs = 6, - .maxblocks = 4, - .pattern = mirror_pattern -}; - -/* - * NAND platform configuration structure - */ -struct lpc32xx_nand_cfg_slc { - uint32_t wdr_clks; - uint32_t wwidth; - uint32_t whold; - uint32_t wsetup; - uint32_t rdr_clks; - uint32_t rwidth; - uint32_t rhold; - uint32_t rsetup; - int wp_gpio; - struct mtd_partition *parts; - unsigned num_parts; -}; - -struct lpc32xx_nand_host { - struct nand_chip nand_chip; - struct lpc32xx_slc_platform_data *pdata; - struct clk *clk; - void __iomem *io_base; - struct lpc32xx_nand_cfg_slc *ncfg; - - struct completion comp; - struct dma_chan *dma_chan; - uint32_t dma_buf_len; - struct dma_slave_config dma_slave_config; - struct scatterlist sgl; - - /* - * DMA and CPU addresses of ECC work area and data buffer - */ - uint32_t *ecc_buf; - uint8_t *data_buf; - dma_addr_t io_base_dma; -}; - -static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) -{ - uint32_t clkrate, tmp; - - /* Reset SLC controller */ - writel(SLCCTRL_SW_RESET, SLC_CTRL(host->io_base)); - udelay(1000); - - /* Basic setup */ - writel(0, SLC_CFG(host->io_base)); - writel(0, SLC_IEN(host->io_base)); - writel((SLCSTAT_INT_TC | SLCSTAT_INT_RDY_EN), - SLC_ICR(host->io_base)); - - /* Get base clock for SLC block */ - clkrate = clk_get_rate(host->clk); - if (clkrate == 0) - clkrate = LPC32XX_DEF_BUS_RATE; - - /* Compute clock setup values */ - tmp = SLCTAC_WDR(host->ncfg->wdr_clks) | - SLCTAC_WWIDTH(clkrate, host->ncfg->wwidth) | - SLCTAC_WHOLD(clkrate, host->ncfg->whold) | - SLCTAC_WSETUP(clkrate, host->ncfg->wsetup) | - SLCTAC_RDR(host->ncfg->rdr_clks) | - SLCTAC_RWIDTH(clkrate, host->ncfg->rwidth) | - SLCTAC_RHOLD(clkrate, host->ncfg->rhold) | - SLCTAC_RSETUP(clkrate, host->ncfg->rsetup); - writel(tmp, SLC_TAC(host->io_base)); -} - -/* - * Hardware specific access to control lines - */ -static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - uint32_t tmp; - struct nand_chip *chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - - /* Does CE state need to be changed? */ - tmp = readl(SLC_CFG(host->io_base)); - if (ctrl & NAND_NCE) - tmp |= SLCCFG_CE_LOW; - else - tmp &= ~SLCCFG_CE_LOW; - writel(tmp, SLC_CFG(host->io_base)); - - if (cmd != NAND_CMD_NONE) { - if (ctrl & NAND_CLE) - writel(cmd, SLC_CMD(host->io_base)); - else - writel(cmd, SLC_ADDR(host->io_base)); - } -} - -/* - * Read the Device Ready pin - */ -static int lpc32xx_nand_device_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - int rdy = 0; - - if ((readl(SLC_STAT(host->io_base)) & SLCSTAT_NAND_READY) != 0) - rdy = 1; - - return rdy; -} - -/* - * Enable NAND write protect - */ -static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) -{ - if (gpio_is_valid(host->ncfg->wp_gpio)) - gpio_set_value(host->ncfg->wp_gpio, 0); -} - -/* - * Disable NAND write protect - */ -static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) -{ - if (gpio_is_valid(host->ncfg->wp_gpio)) - gpio_set_value(host->ncfg->wp_gpio, 1); -} - -/* - * Prepares SLC for transfers with H/W ECC enabled - */ -static void lpc32xx_nand_ecc_enable(struct mtd_info *mtd, int mode) -{ - /* Hardware ECC is enabled automatically in hardware as needed */ -} - -/* - * Calculates the ECC for the data - */ -static int lpc32xx_nand_ecc_calculate(struct mtd_info *mtd, - const unsigned char *buf, - unsigned char *code) -{ - /* - * ECC is calculated automatically in hardware during syndrome read - * and write operations, so it doesn't need to be calculated here. - */ - return 0; -} - -/* - * Read a single byte from NAND device - */ -static uint8_t lpc32xx_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - - return (uint8_t)readl(SLC_DATA(host->io_base)); -} - -/* - * Simple device read without ECC - */ -static void lpc32xx_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - - /* Direct device read with no ECC */ - while (len-- > 0) - *buf++ = (uint8_t)readl(SLC_DATA(host->io_base)); -} - -/* - * Simple device write without ECC - */ -static void lpc32xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - - /* Direct device write with no ECC */ - while (len-- > 0) - writel((uint32_t)*buf++, SLC_DATA(host->io_base)); -} - -/* - * Read the OOB data from the device without ECC using FIFO method - */ -static int lpc32xx_nand_read_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); -} - -/* - * Write the OOB data to the device without ECC using FIFO method - */ -static int lpc32xx_nand_write_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, - mtd->oobsize); -} - -/* - * Fills in the ECC fields in the OOB buffer with the hardware generated ECC - */ -static void lpc32xx_slc_ecc_copy(uint8_t *spare, const uint32_t *ecc, int count) -{ - int i; - - for (i = 0; i < (count * 3); i += 3) { - uint32_t ce = ecc[i / 3]; - ce = ~(ce << 2) & 0xFFFFFF; - spare[i + 2] = (uint8_t)(ce & 0xFF); - ce >>= 8; - spare[i + 1] = (uint8_t)(ce & 0xFF); - ce >>= 8; - spare[i] = (uint8_t)(ce & 0xFF); - } -} - -static void lpc32xx_dma_complete_func(void *completion) -{ - complete(completion); -} - -static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, - void *mem, int len, enum dma_transfer_direction dir) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - struct dma_async_tx_descriptor *desc; - int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; - int res; - - host->dma_slave_config.direction = dir; - host->dma_slave_config.src_addr = dma; - host->dma_slave_config.dst_addr = dma; - host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_slave_config.src_maxburst = 4; - host->dma_slave_config.dst_maxburst = 4; - /* DMA controller does flow control: */ - host->dma_slave_config.device_fc = false; - if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) { - dev_err(mtd->dev.parent, "Failed to setup DMA slave\n"); - return -ENXIO; - } - - sg_init_one(&host->sgl, mem, len); - - res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); - if (res != 1) { - dev_err(mtd->dev.parent, "Failed to map sg list\n"); - return -ENXIO; - } - desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir, - flags); - if (!desc) { - dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); - goto out1; - } - - init_completion(&host->comp); - desc->callback = lpc32xx_dma_complete_func; - desc->callback_param = &host->comp; - - dmaengine_submit(desc); - dma_async_issue_pending(host->dma_chan); - - wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000)); - - dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); - - return 0; -out1: - dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); - return -ENXIO; -} - -/* - * DMA read/write transfers with ECC support - */ -static int lpc32xx_xfer(struct mtd_info *mtd, uint8_t *buf, int eccsubpages, - int read) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - int i, status = 0; - unsigned long timeout; - int res; - enum dma_transfer_direction dir = - read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; - uint8_t *dma_buf; - bool dma_mapped; - - if ((void *)buf <= high_memory) { - dma_buf = buf; - dma_mapped = true; - } else { - dma_buf = host->data_buf; - dma_mapped = false; - if (!read) - memcpy(host->data_buf, buf, mtd->writesize); - } - - if (read) { - writel(readl(SLC_CFG(host->io_base)) | - SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | - SLCCFG_DMA_BURST, SLC_CFG(host->io_base)); - } else { - writel((readl(SLC_CFG(host->io_base)) | - SLCCFG_ECC_EN | SLCCFG_DMA_ECC | SLCCFG_DMA_BURST) & - ~SLCCFG_DMA_DIR, - SLC_CFG(host->io_base)); - } - - /* Clear initial ECC */ - writel(SLCCTRL_ECC_CLEAR, SLC_CTRL(host->io_base)); - - /* Transfer size is data area only */ - writel(mtd->writesize, SLC_TC(host->io_base)); - - /* Start transfer in the NAND controller */ - writel(readl(SLC_CTRL(host->io_base)) | SLCCTRL_DMA_START, - SLC_CTRL(host->io_base)); - - for (i = 0; i < chip->ecc.steps; i++) { - /* Data */ - res = lpc32xx_xmit_dma(mtd, SLC_DMA_DATA(host->io_base_dma), - dma_buf + i * chip->ecc.size, - mtd->writesize / chip->ecc.steps, dir); - if (res) - return res; - - /* Always _read_ ECC */ - if (i == chip->ecc.steps - 1) - break; - if (!read) /* ECC availability delayed on write */ - udelay(10); - res = lpc32xx_xmit_dma(mtd, SLC_ECC(host->io_base_dma), - &host->ecc_buf[i], 4, DMA_DEV_TO_MEM); - if (res) - return res; - } - - /* - * According to NXP, the DMA can be finished here, but the NAND - * controller may still have buffered data. After porting to using the - * dmaengine DMA driver (amba-pl080), the condition (DMA_FIFO empty) - * appears to be always true, according to tests. Keeping the check for - * safety reasons for now. - */ - if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) { - dev_warn(mtd->dev.parent, "FIFO not empty!\n"); - timeout = jiffies + msecs_to_jiffies(LPC32XX_DMA_TIMEOUT); - while ((readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) && - time_before(jiffies, timeout)) - cpu_relax(); - if (!time_before(jiffies, timeout)) { - dev_err(mtd->dev.parent, "FIFO held data too long\n"); - status = -EIO; - } - } - - /* Read last calculated ECC value */ - if (!read) - udelay(10); - host->ecc_buf[chip->ecc.steps - 1] = - readl(SLC_ECC(host->io_base)); - - /* Flush DMA */ - dmaengine_terminate_all(host->dma_chan); - - if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO || - readl(SLC_TC(host->io_base))) { - /* Something is left in the FIFO, something is wrong */ - dev_err(mtd->dev.parent, "DMA FIFO failure\n"); - status = -EIO; - } - - /* Stop DMA & HW ECC */ - writel(readl(SLC_CTRL(host->io_base)) & ~SLCCTRL_DMA_START, - SLC_CTRL(host->io_base)); - writel(readl(SLC_CFG(host->io_base)) & - ~(SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | - SLCCFG_DMA_BURST), SLC_CFG(host->io_base)); - - if (!dma_mapped && read) - memcpy(buf, host->data_buf, mtd->writesize); - - return status; -} - -/* - * Read the data and OOB data from the device, use ECC correction with the - * data, disable ECC for the OOB data - */ -static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - struct mtd_oob_region oobregion = { }; - int stat, i, status, error; - uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE]; - - /* Issue read command */ - nand_read_page_op(chip, page, 0, NULL, 0); - - /* Read data and oob, calculate ECC */ - status = lpc32xx_xfer(mtd, buf, chip->ecc.steps, 1); - - /* Get OOB data */ - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - /* Convert to stored ECC format */ - lpc32xx_slc_ecc_copy(tmpecc, (uint32_t *) host->ecc_buf, chip->ecc.steps); - - /* Pointer to ECC data retrieved from NAND spare area */ - error = mtd_ooblayout_ecc(mtd, 0, &oobregion); - if (error) - return error; - - oobecc = chip->oob_poi + oobregion.offset; - - for (i = 0; i < chip->ecc.steps; i++) { - stat = chip->ecc.correct(mtd, buf, oobecc, - &tmpecc[i * chip->ecc.bytes]); - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - - buf += chip->ecc.size; - oobecc += chip->ecc.bytes; - } - - return status; -} - -/* - * Read the data and OOB data from the device, no ECC correction with the - * data or OOB data - */ -static int lpc32xx_nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int oob_required, - int page) -{ - /* Issue read command */ - nand_read_page_op(chip, page, 0, NULL, 0); - - /* Raw reads can just use the FIFO interface */ - chip->read_buf(mtd, buf, chip->ecc.size * chip->ecc.steps); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - -/* - * Write the data and OOB data to the device, use ECC with the data, - * disable ECC for the OOB data - */ -static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required, int page) -{ - struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - struct mtd_oob_region oobregion = { }; - uint8_t *pb; - int error; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - /* Write data, calculate ECC on outbound data */ - error = lpc32xx_xfer(mtd, (uint8_t *)buf, chip->ecc.steps, 0); - if (error) - return error; - - /* - * The calculated ECC needs some manual work done to it before - * committing it to NAND. Process the calculated ECC and place - * the resultant values directly into the OOB buffer. */ - error = mtd_ooblayout_ecc(mtd, 0, &oobregion); - if (error) - return error; - - pb = chip->oob_poi + oobregion.offset; - lpc32xx_slc_ecc_copy(pb, (uint32_t *)host->ecc_buf, chip->ecc.steps); - - /* Write ECC data to device */ - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -/* - * Write the data and OOB data to the device, no ECC correction with the - * data or OOB data - */ -static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required, int page) -{ - /* Raw writes can just use the FIFO interface */ - nand_prog_page_begin_op(chip, page, 0, buf, - chip->ecc.size * chip->ecc.steps); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) -{ - struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); - dma_cap_mask_t mask; - - if (!host->pdata || !host->pdata->dma_filter) { - dev_err(mtd->dev.parent, "no DMA platform data\n"); - return -ENOENT; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, - "nand-slc"); - if (!host->dma_chan) { - dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); - return -EBUSY; - } - - return 0; -} - -static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev) -{ - struct lpc32xx_nand_cfg_slc *ncfg; - struct device_node *np = dev->of_node; - - ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL); - if (!ncfg) - return NULL; - - of_property_read_u32(np, "nxp,wdr-clks", &ncfg->wdr_clks); - of_property_read_u32(np, "nxp,wwidth", &ncfg->wwidth); - of_property_read_u32(np, "nxp,whold", &ncfg->whold); - of_property_read_u32(np, "nxp,wsetup", &ncfg->wsetup); - of_property_read_u32(np, "nxp,rdr-clks", &ncfg->rdr_clks); - of_property_read_u32(np, "nxp,rwidth", &ncfg->rwidth); - of_property_read_u32(np, "nxp,rhold", &ncfg->rhold); - of_property_read_u32(np, "nxp,rsetup", &ncfg->rsetup); - - if (!ncfg->wdr_clks || !ncfg->wwidth || !ncfg->whold || - !ncfg->wsetup || !ncfg->rdr_clks || !ncfg->rwidth || - !ncfg->rhold || !ncfg->rsetup) { - dev_err(dev, "chip parameters not specified correctly\n"); - return NULL; - } - - ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); - - return ncfg; -} - -/* - * Probe for NAND controller - */ -static int lpc32xx_nand_probe(struct platform_device *pdev) -{ - struct lpc32xx_nand_host *host; - struct mtd_info *mtd; - struct nand_chip *chip; - struct resource *rc; - int res; - - /* Allocate memory for the device structure (and zero it) */ - host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); - if (!host) - return -ENOMEM; - - rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->io_base = devm_ioremap_resource(&pdev->dev, rc); - if (IS_ERR(host->io_base)) - return PTR_ERR(host->io_base); - - host->io_base_dma = rc->start; - if (pdev->dev.of_node) - host->ncfg = lpc32xx_parse_dt(&pdev->dev); - if (!host->ncfg) { - dev_err(&pdev->dev, - "Missing or bad NAND config from device tree\n"); - return -ENOENT; - } - if (host->ncfg->wp_gpio == -EPROBE_DEFER) - return -EPROBE_DEFER; - if (gpio_is_valid(host->ncfg->wp_gpio) && devm_gpio_request(&pdev->dev, - host->ncfg->wp_gpio, "NAND WP")) { - dev_err(&pdev->dev, "GPIO not available\n"); - return -EBUSY; - } - lpc32xx_wp_disable(host); - - host->pdata = dev_get_platdata(&pdev->dev); - - chip = &host->nand_chip; - mtd = nand_to_mtd(chip); - nand_set_controller_data(chip, host); - nand_set_flash_node(chip, pdev->dev.of_node); - mtd->owner = THIS_MODULE; - mtd->dev.parent = &pdev->dev; - - /* Get NAND clock */ - host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - dev_err(&pdev->dev, "Clock failure\n"); - res = -ENOENT; - goto err_exit1; - } - res = clk_prepare_enable(host->clk); - if (res) - goto err_exit1; - - /* Set NAND IO addresses and command/ready functions */ - chip->IO_ADDR_R = SLC_DATA(host->io_base); - chip->IO_ADDR_W = SLC_DATA(host->io_base); - chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; - chip->dev_ready = lpc32xx_nand_device_ready; - chip->chip_delay = 20; /* 20us command delay time */ - - /* Init NAND controller */ - lpc32xx_nand_setup(host); - - platform_set_drvdata(pdev, host); - - /* NAND callbacks for LPC32xx SLC hardware */ - chip->ecc.mode = NAND_ECC_HW_SYNDROME; - chip->read_byte = lpc32xx_nand_read_byte; - chip->read_buf = lpc32xx_nand_read_buf; - chip->write_buf = lpc32xx_nand_write_buf; - chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; - chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; - chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; - chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; - chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; - chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; - chip->ecc.calculate = lpc32xx_nand_ecc_calculate; - chip->ecc.correct = nand_correct_data; - chip->ecc.strength = 1; - chip->ecc.hwctl = lpc32xx_nand_ecc_enable; - - /* - * Allocate a large enough buffer for a single huge page plus - * extra space for the spare area and ECC storage area - */ - host->dma_buf_len = LPC32XX_DMA_DATA_SIZE + LPC32XX_ECC_SAVE_SIZE; - host->data_buf = devm_kzalloc(&pdev->dev, host->dma_buf_len, - GFP_KERNEL); - if (host->data_buf == NULL) { - res = -ENOMEM; - goto err_exit2; - } - - res = lpc32xx_nand_dma_setup(host); - if (res) { - res = -EIO; - goto err_exit2; - } - - /* Find NAND device */ - res = nand_scan_ident(mtd, 1, NULL); - if (res) - goto err_exit3; - - /* OOB and ECC CPU and DMA work areas */ - host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); - - /* - * Small page FLASH has a unique OOB layout, but large and huge - * page FLASH use the standard layout. Small page FLASH uses a - * custom BBT marker layout. - */ - if (mtd->writesize <= 512) - mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); - - /* These sizes remain the same regardless of page size */ - chip->ecc.size = 256; - chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; - chip->ecc.prepad = chip->ecc.postpad = 0; - - /* - * Use a custom BBT marker setup for small page FLASH that - * won't interfere with the ECC layout. Large and huge page - * FLASH use the standard layout. - */ - if ((chip->bbt_options & NAND_BBT_USE_FLASH) && - mtd->writesize <= 512) { - chip->bbt_td = &bbt_smallpage_main_descr; - chip->bbt_md = &bbt_smallpage_mirror_descr; - } - - /* - * Fills out all the uninitialized function pointers with the defaults - */ - res = nand_scan_tail(mtd); - if (res) - goto err_exit3; - - mtd->name = "nxp_lpc3220_slc"; - res = mtd_device_register(mtd, host->ncfg->parts, - host->ncfg->num_parts); - if (!res) - return res; - - nand_release(mtd); - -err_exit3: - dma_release_channel(host->dma_chan); -err_exit2: - clk_disable_unprepare(host->clk); -err_exit1: - lpc32xx_wp_enable(host); - - return res; -} - -/* - * Remove NAND device. - */ -static int lpc32xx_nand_remove(struct platform_device *pdev) -{ - uint32_t tmp; - struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); - struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); - - nand_release(mtd); - dma_release_channel(host->dma_chan); - - /* Force CE high */ - tmp = readl(SLC_CTRL(host->io_base)); - tmp &= ~SLCCFG_CE_LOW; - writel(tmp, SLC_CTRL(host->io_base)); - - clk_disable_unprepare(host->clk); - lpc32xx_wp_enable(host); - - return 0; -} - -#ifdef CONFIG_PM -static int lpc32xx_nand_resume(struct platform_device *pdev) -{ - struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); - int ret; - - /* Re-enable NAND clock */ - ret = clk_prepare_enable(host->clk); - if (ret) - return ret; - - /* Fresh init of NAND controller */ - lpc32xx_nand_setup(host); - - /* Disable write protect */ - lpc32xx_wp_disable(host); - - return 0; -} - -static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) -{ - uint32_t tmp; - struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); - - /* Force CE high */ - tmp = readl(SLC_CTRL(host->io_base)); - tmp &= ~SLCCFG_CE_LOW; - writel(tmp, SLC_CTRL(host->io_base)); - - /* Enable write protect for safety */ - lpc32xx_wp_enable(host); - - /* Disable clock */ - clk_disable_unprepare(host->clk); - - return 0; -} - -#else -#define lpc32xx_nand_resume NULL -#define lpc32xx_nand_suspend NULL -#endif - -static const struct of_device_id lpc32xx_nand_match[] = { - { .compatible = "nxp,lpc3220-slc" }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); - -static struct platform_driver lpc32xx_nand_driver = { - .probe = lpc32xx_nand_probe, - .remove = lpc32xx_nand_remove, - .resume = lpc32xx_nand_resume, - .suspend = lpc32xx_nand_suspend, - .driver = { - .name = LPC32XX_MODNAME, - .of_match_table = lpc32xx_nand_match, - }, -}; - -module_platform_driver(lpc32xx_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kevin Wells "); -MODULE_AUTHOR("Roland Stigge "); -MODULE_DESCRIPTION("NAND driver for the NXP LPC32XX SLC controller"); diff --git a/drivers/mtd/nand/marvell_nand.c b/drivers/mtd/nand/marvell_nand.c deleted file mode 100644 index cc21f9633edd..000000000000 --- a/drivers/mtd/nand/marvell_nand.c +++ /dev/null @@ -1,2915 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Marvell NAND flash controller driver - * - * Copyright (C) 2017 Marvell - * Author: Miquel RAYNAL - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* Data FIFO granularity, FIFO reads/writes must be a multiple of this length */ -#define FIFO_DEPTH 8 -#define FIFO_REP(x) (x / sizeof(u32)) -#define BCH_SEQ_READS (32 / FIFO_DEPTH) -/* NFC does not support transfers of larger chunks at a time */ -#define MAX_CHUNK_SIZE 2112 -/* NFCv1 cannot read more that 7 bytes of ID */ -#define NFCV1_READID_LEN 7 -/* Polling is done at a pace of POLL_PERIOD us until POLL_TIMEOUT is reached */ -#define POLL_PERIOD 0 -#define POLL_TIMEOUT 100000 -/* Interrupt maximum wait period in ms */ -#define IRQ_TIMEOUT 1000 -/* Latency in clock cycles between SoC pins and NFC logic */ -#define MIN_RD_DEL_CNT 3 -/* Maximum number of contiguous address cycles */ -#define MAX_ADDRESS_CYC_NFCV1 5 -#define MAX_ADDRESS_CYC_NFCV2 7 -/* System control registers/bits to enable the NAND controller on some SoCs */ -#define GENCONF_SOC_DEVICE_MUX 0x208 -#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0) -#define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20) -#define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21) -#define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25) -#define GENCONF_CLK_GATING_CTRL 0x220 -#define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2) -#define GENCONF_ND_CLK_CTRL 0x700 -#define GENCONF_ND_CLK_CTRL_EN BIT(0) - -/* NAND controller data flash control register */ -#define NDCR 0x00 -#define NDCR_ALL_INT GENMASK(11, 0) -#define NDCR_CS1_CMDDM BIT(7) -#define NDCR_CS0_CMDDM BIT(8) -#define NDCR_RDYM BIT(11) -#define NDCR_ND_ARB_EN BIT(12) -#define NDCR_RA_START BIT(15) -#define NDCR_RD_ID_CNT(x) (min_t(unsigned int, x, 0x7) << 16) -#define NDCR_PAGE_SZ(x) (x >= 2048 ? BIT(24) : 0) -#define NDCR_DWIDTH_M BIT(26) -#define NDCR_DWIDTH_C BIT(27) -#define NDCR_ND_RUN BIT(28) -#define NDCR_DMA_EN BIT(29) -#define NDCR_ECC_EN BIT(30) -#define NDCR_SPARE_EN BIT(31) -#define NDCR_GENERIC_FIELDS_MASK (~(NDCR_RA_START | NDCR_PAGE_SZ(2048) | \ - NDCR_DWIDTH_M | NDCR_DWIDTH_C)) - -/* NAND interface timing parameter 0 register */ -#define NDTR0 0x04 -#define NDTR0_TRP(x) ((min_t(unsigned int, x, 0xF) & 0x7) << 0) -#define NDTR0_TRH(x) (min_t(unsigned int, x, 0x7) << 3) -#define NDTR0_ETRP(x) ((min_t(unsigned int, x, 0xF) & 0x8) << 3) -#define NDTR0_SEL_NRE_EDGE BIT(7) -#define NDTR0_TWP(x) (min_t(unsigned int, x, 0x7) << 8) -#define NDTR0_TWH(x) (min_t(unsigned int, x, 0x7) << 11) -#define NDTR0_TCS(x) (min_t(unsigned int, x, 0x7) << 16) -#define NDTR0_TCH(x) (min_t(unsigned int, x, 0x7) << 19) -#define NDTR0_RD_CNT_DEL(x) (min_t(unsigned int, x, 0xF) << 22) -#define NDTR0_SELCNTR BIT(26) -#define NDTR0_TADL(x) (min_t(unsigned int, x, 0x1F) << 27) - -/* NAND interface timing parameter 1 register */ -#define NDTR1 0x0C -#define NDTR1_TAR(x) (min_t(unsigned int, x, 0xF) << 0) -#define NDTR1_TWHR(x) (min_t(unsigned int, x, 0xF) << 4) -#define NDTR1_TRHW(x) (min_t(unsigned int, x / 16, 0x3) << 8) -#define NDTR1_PRESCALE BIT(14) -#define NDTR1_WAIT_MODE BIT(15) -#define NDTR1_TR(x) (min_t(unsigned int, x, 0xFFFF) << 16) - -/* NAND controller status register */ -#define NDSR 0x14 -#define NDSR_WRCMDREQ BIT(0) -#define NDSR_RDDREQ BIT(1) -#define NDSR_WRDREQ BIT(2) -#define NDSR_CORERR BIT(3) -#define NDSR_UNCERR BIT(4) -#define NDSR_CMDD(cs) BIT(8 - cs) -#define NDSR_RDY(rb) BIT(11 + rb) -#define NDSR_ERRCNT(x) ((x >> 16) & 0x1F) - -/* NAND ECC control register */ -#define NDECCCTRL 0x28 -#define NDECCCTRL_BCH_EN BIT(0) - -/* NAND controller data buffer register */ -#define NDDB 0x40 - -/* NAND controller command buffer 0 register */ -#define NDCB0 0x48 -#define NDCB0_CMD1(x) ((x & 0xFF) << 0) -#define NDCB0_CMD2(x) ((x & 0xFF) << 8) -#define NDCB0_ADDR_CYC(x) ((x & 0x7) << 16) -#define NDCB0_ADDR_GET_NUM_CYC(x) (((x) >> 16) & 0x7) -#define NDCB0_DBC BIT(19) -#define NDCB0_CMD_TYPE(x) ((x & 0x7) << 21) -#define NDCB0_CSEL BIT(24) -#define NDCB0_RDY_BYP BIT(27) -#define NDCB0_LEN_OVRD BIT(28) -#define NDCB0_CMD_XTYPE(x) ((x & 0x7) << 29) - -/* NAND controller command buffer 1 register */ -#define NDCB1 0x4C -#define NDCB1_COLS(x) ((x & 0xFFFF) << 0) -#define NDCB1_ADDRS_PAGE(x) (x << 16) - -/* NAND controller command buffer 2 register */ -#define NDCB2 0x50 -#define NDCB2_ADDR5_PAGE(x) (((x >> 16) & 0xFF) << 0) -#define NDCB2_ADDR5_CYC(x) ((x & 0xFF) << 0) - -/* NAND controller command buffer 3 register */ -#define NDCB3 0x54 -#define NDCB3_ADDR6_CYC(x) ((x & 0xFF) << 16) -#define NDCB3_ADDR7_CYC(x) ((x & 0xFF) << 24) - -/* NAND controller command buffer 0 register 'type' and 'xtype' fields */ -#define TYPE_READ 0 -#define TYPE_WRITE 1 -#define TYPE_ERASE 2 -#define TYPE_READ_ID 3 -#define TYPE_STATUS 4 -#define TYPE_RESET 5 -#define TYPE_NAKED_CMD 6 -#define TYPE_NAKED_ADDR 7 -#define TYPE_MASK 7 -#define XTYPE_MONOLITHIC_RW 0 -#define XTYPE_LAST_NAKED_RW 1 -#define XTYPE_FINAL_COMMAND 3 -#define XTYPE_READ 4 -#define XTYPE_WRITE_DISPATCH 4 -#define XTYPE_NAKED_RW 5 -#define XTYPE_COMMAND_DISPATCH 6 -#define XTYPE_MASK 7 - -/** - * Marvell ECC engine works differently than the others, in order to limit the - * size of the IP, hardware engineers chose to set a fixed strength at 16 bits - * per subpage, and depending on a the desired strength needed by the NAND chip, - * a particular layout mixing data/spare/ecc is defined, with a possible last - * chunk smaller that the others. - * - * @writesize: Full page size on which the layout applies - * @chunk: Desired ECC chunk size on which the layout applies - * @strength: Desired ECC strength (per chunk size bytes) on which the - * layout applies - * @nchunks: Total number of chunks - * @full_chunk_cnt: Number of full-sized chunks, which is the number of - * repetitions of the pattern: - * (data_bytes + spare_bytes + ecc_bytes). - * @data_bytes: Number of data bytes per chunk - * @spare_bytes: Number of spare bytes per chunk - * @ecc_bytes: Number of ecc bytes per chunk - * @last_data_bytes: Number of data bytes in the last chunk - * @last_spare_bytes: Number of spare bytes in the last chunk - * @last_ecc_bytes: Number of ecc bytes in the last chunk - */ -struct marvell_hw_ecc_layout { - /* Constraints */ - int writesize; - int chunk; - int strength; - /* Corresponding layout */ - int nchunks; - int full_chunk_cnt; - int data_bytes; - int spare_bytes; - int ecc_bytes; - int last_data_bytes; - int last_spare_bytes; - int last_ecc_bytes; -}; - -#define MARVELL_LAYOUT(ws, dc, ds, nc, fcc, db, sb, eb, ldb, lsb, leb) \ - { \ - .writesize = ws, \ - .chunk = dc, \ - .strength = ds, \ - .nchunks = nc, \ - .full_chunk_cnt = fcc, \ - .data_bytes = db, \ - .spare_bytes = sb, \ - .ecc_bytes = eb, \ - .last_data_bytes = ldb, \ - .last_spare_bytes = lsb, \ - .last_ecc_bytes = leb, \ - } - -/* Layouts explained in AN-379_Marvell_SoC_NFC_ECC */ -static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = { - MARVELL_LAYOUT( 512, 512, 1, 1, 1, 512, 8, 8, 0, 0, 0), - MARVELL_LAYOUT( 2048, 512, 1, 1, 1, 2048, 40, 24, 0, 0, 0), - MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0), - MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0), - MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30), -}; - -/** - * The Nand Flash Controller has up to 4 CE and 2 RB pins. The CE selection - * is made by a field in NDCB0 register, and in another field in NDCB2 register. - * The datasheet describes the logic with an error: ADDR5 field is once - * declared at the beginning of NDCB2, and another time at its end. Because the - * ADDR5 field of NDCB2 may be used by other bytes, it would be more logical - * to use the last bit of this field instead of the first ones. - * - * @cs: Wanted CE lane. - * @ndcb0_csel: Value of the NDCB0 register with or without the flag - * selecting the wanted CE lane. This is set once when - * the Device Tree is probed. - * @rb: Ready/Busy pin for the flash chip - */ -struct marvell_nand_chip_sel { - unsigned int cs; - u32 ndcb0_csel; - unsigned int rb; -}; - -/** - * NAND chip structure: stores NAND chip device related information - * - * @chip: Base NAND chip structure - * @node: Used to store NAND chips into a list - * @layout NAND layout when using hardware ECC - * @ndcr: Controller register value for this NAND chip - * @ndtr0: Timing registers 0 value for this NAND chip - * @ndtr1: Timing registers 1 value for this NAND chip - * @selected_die: Current active CS - * @nsels: Number of CS lines required by the NAND chip - * @sels: Array of CS lines descriptions - */ -struct marvell_nand_chip { - struct nand_chip chip; - struct list_head node; - const struct marvell_hw_ecc_layout *layout; - u32 ndcr; - u32 ndtr0; - u32 ndtr1; - int addr_cyc; - int selected_die; - unsigned int nsels; - struct marvell_nand_chip_sel sels[0]; -}; - -static inline struct marvell_nand_chip *to_marvell_nand(struct nand_chip *chip) -{ - return container_of(chip, struct marvell_nand_chip, chip); -} - -static inline struct marvell_nand_chip_sel *to_nand_sel(struct marvell_nand_chip - *nand) -{ - return &nand->sels[nand->selected_die]; -} - -/** - * NAND controller capabilities for distinction between compatible strings - * - * @max_cs_nb: Number of Chip Select lines available - * @max_rb_nb: Number of Ready/Busy lines available - * @need_system_controller: Indicates if the SoC needs to have access to the - * system controller (ie. to enable the NAND controller) - * @legacy_of_bindings: Indicates if DT parsing must be done using the old - * fashion way - * @is_nfcv2: NFCv2 has numerous enhancements compared to NFCv1, ie. - * BCH error detection and correction algorithm, - * NDCB3 register has been added - * @use_dma: Use dma for data transfers - */ -struct marvell_nfc_caps { - unsigned int max_cs_nb; - unsigned int max_rb_nb; - bool need_system_controller; - bool legacy_of_bindings; - bool is_nfcv2; - bool use_dma; -}; - -/** - * NAND controller structure: stores Marvell NAND controller information - * - * @controller: Base controller structure - * @dev: Parent device (used to print error messages) - * @regs: NAND controller registers - * @ecc_clk: ECC block clock, two times the NAND controller clock - * @complete: Completion object to wait for NAND controller events - * @assigned_cs: Bitmask describing already assigned CS lines - * @chips: List containing all the NAND chips attached to - * this NAND controller - * @caps: NAND controller capabilities for each compatible string - * @dma_chan: DMA channel (NFCv1 only) - * @dma_buf: 32-bit aligned buffer for DMA transfers (NFCv1 only) - */ -struct marvell_nfc { - struct nand_hw_control controller; - struct device *dev; - void __iomem *regs; - struct clk *ecc_clk; - struct completion complete; - unsigned long assigned_cs; - struct list_head chips; - struct nand_chip *selected_chip; - const struct marvell_nfc_caps *caps; - - /* DMA (NFCv1 only) */ - bool use_dma; - struct dma_chan *dma_chan; - u8 *dma_buf; -}; - -static inline struct marvell_nfc *to_marvell_nfc(struct nand_hw_control *ctrl) -{ - return container_of(ctrl, struct marvell_nfc, controller); -} - -/** - * NAND controller timings expressed in NAND Controller clock cycles - * - * @tRP: ND_nRE pulse width - * @tRH: ND_nRE high duration - * @tWP: ND_nWE pulse time - * @tWH: ND_nWE high duration - * @tCS: Enable signal setup time - * @tCH: Enable signal hold time - * @tADL: Address to write data delay - * @tAR: ND_ALE low to ND_nRE low delay - * @tWHR: ND_nWE high to ND_nRE low for status read - * @tRHW: ND_nRE high duration, read to write delay - * @tR: ND_nWE high to ND_nRE low for read - */ -struct marvell_nfc_timings { - /* NDTR0 fields */ - unsigned int tRP; - unsigned int tRH; - unsigned int tWP; - unsigned int tWH; - unsigned int tCS; - unsigned int tCH; - unsigned int tADL; - /* NDTR1 fields */ - unsigned int tAR; - unsigned int tWHR; - unsigned int tRHW; - unsigned int tR; -}; - -/** - * Derives a duration in numbers of clock cycles. - * - * @ps: Duration in pico-seconds - * @period_ns: Clock period in nano-seconds - * - * Convert the duration in nano-seconds, then divide by the period and - * return the number of clock periods. - */ -#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns)) -#define TO_CYCLES64(ps, period_ns) (DIV_ROUND_UP_ULL(div_u64(ps, 1000), \ - period_ns)) - -/** - * NAND driver structure filled during the parsing of the ->exec_op() subop - * subset of instructions. - * - * @ndcb: Array of values written to NDCBx registers - * @cle_ale_delay_ns: Optional delay after the last CMD or ADDR cycle - * @rdy_timeout_ms: Timeout for waits on Ready/Busy pin - * @rdy_delay_ns: Optional delay after waiting for the RB pin - * @data_delay_ns: Optional delay after the data xfer - * @data_instr_idx: Index of the data instruction in the subop - * @data_instr: Pointer to the data instruction in the subop - */ -struct marvell_nfc_op { - u32 ndcb[4]; - unsigned int cle_ale_delay_ns; - unsigned int rdy_timeout_ms; - unsigned int rdy_delay_ns; - unsigned int data_delay_ns; - unsigned int data_instr_idx; - const struct nand_op_instr *data_instr; -}; - -/* - * Internal helper to conditionnally apply a delay (from the above structure, - * most of the time). - */ -static void cond_delay(unsigned int ns) -{ - if (!ns) - return; - - if (ns < 10000) - ndelay(ns); - else - udelay(DIV_ROUND_UP(ns, 1000)); -} - -/* - * The controller has many flags that could generate interrupts, most of them - * are disabled and polling is used. For the very slow signals, using interrupts - * may relax the CPU charge. - */ -static void marvell_nfc_disable_int(struct marvell_nfc *nfc, u32 int_mask) -{ - u32 reg; - - /* Writing 1 disables the interrupt */ - reg = readl_relaxed(nfc->regs + NDCR); - writel_relaxed(reg | int_mask, nfc->regs + NDCR); -} - -static void marvell_nfc_enable_int(struct marvell_nfc *nfc, u32 int_mask) -{ - u32 reg; - - /* Writing 0 enables the interrupt */ - reg = readl_relaxed(nfc->regs + NDCR); - writel_relaxed(reg & ~int_mask, nfc->regs + NDCR); -} - -static void marvell_nfc_clear_int(struct marvell_nfc *nfc, u32 int_mask) -{ - writel_relaxed(int_mask, nfc->regs + NDSR); -} - -static void marvell_nfc_force_byte_access(struct nand_chip *chip, - bool force_8bit) -{ - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - u32 ndcr; - - /* - * Callers of this function do not verify if the NAND is using a 16-bit - * an 8-bit bus for normal operations, so we need to take care of that - * here by leaving the configuration unchanged if the NAND does not have - * the NAND_BUSWIDTH_16 flag set. - */ - if (!(chip->options & NAND_BUSWIDTH_16)) - return; - - ndcr = readl_relaxed(nfc->regs + NDCR); - - if (force_8bit) - ndcr &= ~(NDCR_DWIDTH_M | NDCR_DWIDTH_C); - else - ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C; - - writel_relaxed(ndcr, nfc->regs + NDCR); -} - -static int marvell_nfc_wait_ndrun(struct nand_chip *chip) -{ - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - u32 val; - int ret; - - /* - * The command is being processed, wait for the ND_RUN bit to be - * cleared by the NFC. If not, we must clear it by hand. - */ - ret = readl_relaxed_poll_timeout(nfc->regs + NDCR, val, - (val & NDCR_ND_RUN) == 0, - POLL_PERIOD, POLL_TIMEOUT); - if (ret) { - dev_err(nfc->dev, "Timeout on NAND controller run mode\n"); - writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN, - nfc->regs + NDCR); - return ret; - } - - return 0; -} - -/* - * Any time a command has to be sent to the controller, the following sequence - * has to be followed: - * - call marvell_nfc_prepare_cmd() - * -> activate the ND_RUN bit that will kind of 'start a job' - * -> wait the signal indicating the NFC is waiting for a command - * - send the command (cmd and address cycles) - * - enventually send or receive the data - * - call marvell_nfc_end_cmd() with the corresponding flag - * -> wait the flag to be triggered or cancel the job with a timeout - * - * The following helpers are here to factorize the code a bit so that - * specialized functions responsible for executing the actual NAND - * operations do not have to replicate the same code blocks. - */ -static int marvell_nfc_prepare_cmd(struct nand_chip *chip) -{ - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - u32 ndcr, val; - int ret; - - /* Poll ND_RUN and clear NDSR before issuing any command */ - ret = marvell_nfc_wait_ndrun(chip); - if (ret) { - dev_err(nfc->dev, "Last operation did not succeed\n"); - return ret; - } - - ndcr = readl_relaxed(nfc->regs + NDCR); - writel_relaxed(readl(nfc->regs + NDSR), nfc->regs + NDSR); - - /* Assert ND_RUN bit and wait the NFC to be ready */ - writel_relaxed(ndcr | NDCR_ND_RUN, nfc->regs + NDCR); - ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val, - val & NDSR_WRCMDREQ, - POLL_PERIOD, POLL_TIMEOUT); - if (ret) { - dev_err(nfc->dev, "Timeout on WRCMDRE\n"); - return -ETIMEDOUT; - } - - /* Command may be written, clear WRCMDREQ status bit */ - writel_relaxed(NDSR_WRCMDREQ, nfc->regs + NDSR); - - return 0; -} - -static void marvell_nfc_send_cmd(struct nand_chip *chip, - struct marvell_nfc_op *nfc_op) -{ - struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - - dev_dbg(nfc->dev, "\nNDCR: 0x%08x\n" - "NDCB0: 0x%08x\nNDCB1: 0x%08x\nNDCB2: 0x%08x\nNDCB3: 0x%08x\n", - (u32)readl_relaxed(nfc->regs + NDCR), nfc_op->ndcb[0], - nfc_op->ndcb[1], nfc_op->ndcb[2], nfc_op->ndcb[3]); - - writel_relaxed(to_nand_sel(marvell_nand)->ndcb0_csel | nfc_op->ndcb[0], - nfc->regs + NDCB0); - writel_relaxed(nfc_op->ndcb[1], nfc->regs + NDCB0); - writel(nfc_op->ndcb[2], nfc->regs + NDCB0); - - /* - * Write NDCB0 four times only if LEN_OVRD is set or if ADDR6 or ADDR7 - * fields are used (only available on NFCv2). - */ - if (nfc_op->ndcb[0] & NDCB0_LEN_OVRD || - NDCB0_ADDR_GET_NUM_CYC(nfc_op->ndcb[0]) >= 6) { - if (!WARN_ON_ONCE(!nfc->caps->is_nfcv2)) - writel(nfc_op->ndcb[3], nfc->regs + NDCB0); - } -} - -static int marvell_nfc_end_cmd(struct nand_chip *chip, int flag, - const char *label) -{ - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - u32 val; - int ret; - - ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val, - val & flag, - POLL_PERIOD, POLL_TIMEOUT); - - if (ret) { - dev_err(nfc->dev, "Timeout on %s (NDSR: 0x%08x)\n", - label, val); - if (nfc->dma_chan) - dmaengine_terminate_all(nfc->dma_chan); - return ret; - } - - /* - * DMA function uses this helper to poll on CMDD bits without wanting - * them to be cleared. - */ - if (nfc->use_dma && (readl_relaxed(nfc->regs + NDCR) & NDCR_DMA_EN)) - return 0; - - writel_relaxed(flag, nfc->regs + NDSR); - - return 0; -} - -static int marvell_nfc_wait_cmdd(struct nand_chip *chip) -{ - struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); - int cs_flag = NDSR_CMDD(to_nand_sel(marvell_nand)->ndcb0_csel); - - return marvell_nfc_end_cmd(chip, cs_flag, "CMDD"); -} - -static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms) -{ - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - int ret; - - /* Timeout is expressed in ms */ - if (!timeout_ms) - timeout_ms = IRQ_TIMEOUT; - - init_completion(&nfc->complete); - - marvell_nfc_enable_int(nfc, NDCR_RDYM); - ret = wait_for_completion_timeout(&nfc->complete, - msecs_to_jiffies(timeout_ms)); - marvell_nfc_disable_int(nfc, NDCR_RDYM); - marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1)); - if (!ret) { - dev_err(nfc->dev, "Timeout waiting for RB signal\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static void marvell_nfc_select_chip(struct mtd_info *mtd, int die_nr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - u32 ndcr_generic; - - if (chip == nfc->selected_chip && die_nr == marvell_nand->selected_die) - return; - - if (die_nr < 0 || die_nr >= marvell_nand->nsels) { - nfc->selected_chip = NULL; - marvell_nand->selected_die = -1; - return; - } - - /* - * Do not change the timing registers when using the DT property - * marvell,nand-keep-config; in that case ->ndtr0 and ->ndtr1 from the - * marvell_nand structure are supposedly empty. - */ - writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0); - writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1); - - /* - * Reset the NDCR register to a clean state for this particular chip, - * also clear ND_RUN bit. - */ - ndcr_generic = readl_relaxed(nfc->regs + NDCR) & - NDCR_GENERIC_FIELDS_MASK & ~NDCR_ND_RUN; - writel_relaxed(ndcr_generic | marvell_nand->ndcr, nfc->regs + NDCR); - - /* Also reset the interrupt status register */ - marvell_nfc_clear_int(nfc, NDCR_ALL_INT); - - nfc->selected_chip = chip; - marvell_nand->selected_die = die_nr; -} - -static irqreturn_t marvell_nfc_isr(int irq, void *dev_id) -{ - struct marvell_nfc *nfc = dev_id; - u32 st = readl_relaxed(nfc->regs + NDSR); - u32 ien = (~readl_relaxed(nfc->regs + NDCR)) & NDCR_ALL_INT; - - /* - * RDY interrupt mask is one bit in NDCR while there are two status - * bit in NDSR (RDY[cs0/cs2] and RDY[cs1/cs3]). - */ - if (st & NDSR_RDY(1)) - st |= NDSR_RDY(0); - - if (!(st & ien)) - return IRQ_NONE; - - marvell_nfc_disable_int(nfc, st & NDCR_ALL_INT); - - if (!(st & (NDSR_RDDREQ | NDSR_WRDREQ | NDSR_WRCMDREQ))) - complete(&nfc->complete); - - return IRQ_HANDLED; -} - -/* HW ECC related functions */ -static void marvell_nfc_enable_hw_ecc(struct nand_chip *chip) -{ - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - u32 ndcr = readl_relaxed(nfc->regs + NDCR); - - if (!(ndcr & NDCR_ECC_EN)) { - writel_relaxed(ndcr | NDCR_ECC_EN, nfc->regs + NDCR); - - /* - * When enabling BCH, set threshold to 0 to always know the - * number of corrected bitflips. - */ - if (chip->ecc.algo == NAND_ECC_BCH) - writel_relaxed(NDECCCTRL_BCH_EN, nfc->regs + NDECCCTRL); - } -} - -static void marvell_nfc_disable_hw_ecc(struct nand_chip *chip) -{ - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - u32 ndcr = readl_relaxed(nfc->regs + NDCR); - - if (ndcr & NDCR_ECC_EN) { - writel_relaxed(ndcr & ~NDCR_ECC_EN, nfc->regs + NDCR); - if (chip->ecc.algo == NAND_ECC_BCH) - writel_relaxed(0, nfc->regs + NDECCCTRL); - } -} - -/* DMA related helpers */ -static void marvell_nfc_enable_dma(struct marvell_nfc *nfc) -{ - u32 reg; - - reg = readl_relaxed(nfc->regs + NDCR); - writel_relaxed(reg | NDCR_DMA_EN, nfc->regs + NDCR); -} - -static void marvell_nfc_disable_dma(struct marvell_nfc *nfc) -{ - u32 reg; - - reg = readl_relaxed(nfc->regs + NDCR); - writel_relaxed(reg & ~NDCR_DMA_EN, nfc->regs + NDCR); -} - -/* Read/write PIO/DMA accessors */ -static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc, - enum dma_data_direction direction, - unsigned int len) -{ - unsigned int dma_len = min_t(int, ALIGN(len, 32), MAX_CHUNK_SIZE); - struct dma_async_tx_descriptor *tx; - struct scatterlist sg; - dma_cookie_t cookie; - int ret; - - marvell_nfc_enable_dma(nfc); - /* Prepare the DMA transfer */ - sg_init_one(&sg, nfc->dma_buf, dma_len); - dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction); - tx = dmaengine_prep_slave_sg(nfc->dma_chan, &sg, 1, - direction == DMA_FROM_DEVICE ? - DMA_DEV_TO_MEM : DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT); - if (!tx) { - dev_err(nfc->dev, "Could not prepare DMA S/G list\n"); - return -ENXIO; - } - - /* Do the task and wait for it to finish */ - cookie = dmaengine_submit(tx); - ret = dma_submit_error(cookie); - if (ret) - return -EIO; - - dma_async_issue_pending(nfc->dma_chan); - ret = marvell_nfc_wait_cmdd(nfc->selected_chip); - dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction); - marvell_nfc_disable_dma(nfc); - if (ret) { - dev_err(nfc->dev, "Timeout waiting for DMA (status: %d)\n", - dmaengine_tx_status(nfc->dma_chan, cookie, NULL)); - dmaengine_terminate_all(nfc->dma_chan); - return -ETIMEDOUT; - } - - return 0; -} - -static int marvell_nfc_xfer_data_in_pio(struct marvell_nfc *nfc, u8 *in, - unsigned int len) -{ - unsigned int last_len = len % FIFO_DEPTH; - unsigned int last_full_offset = round_down(len, FIFO_DEPTH); - int i; - - for (i = 0; i < last_full_offset; i += FIFO_DEPTH) - ioread32_rep(nfc->regs + NDDB, in + i, FIFO_REP(FIFO_DEPTH)); - - if (last_len) { - u8 tmp_buf[FIFO_DEPTH]; - - ioread32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH)); - memcpy(in + last_full_offset, tmp_buf, last_len); - } - - return 0; -} - -static int marvell_nfc_xfer_data_out_pio(struct marvell_nfc *nfc, const u8 *out, - unsigned int len) -{ - unsigned int last_len = len % FIFO_DEPTH; - unsigned int last_full_offset = round_down(len, FIFO_DEPTH); - int i; - - for (i = 0; i < last_full_offset; i += FIFO_DEPTH) - iowrite32_rep(nfc->regs + NDDB, out + i, FIFO_REP(FIFO_DEPTH)); - - if (last_len) { - u8 tmp_buf[FIFO_DEPTH]; - - memcpy(tmp_buf, out + last_full_offset, last_len); - iowrite32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH)); - } - - return 0; -} - -static void marvell_nfc_check_empty_chunk(struct nand_chip *chip, - u8 *data, int data_len, - u8 *spare, int spare_len, - u8 *ecc, int ecc_len, - unsigned int *max_bitflips) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int bf; - - /* - * Blank pages (all 0xFF) that have not been written may be recognized - * as bad if bitflips occur, so whenever an uncorrectable error occurs, - * check if the entire page (with ECC bytes) is actually blank or not. - */ - if (!data) - data_len = 0; - if (!spare) - spare_len = 0; - if (!ecc) - ecc_len = 0; - - bf = nand_check_erased_ecc_chunk(data, data_len, ecc, ecc_len, - spare, spare_len, chip->ecc.strength); - if (bf < 0) { - mtd->ecc_stats.failed++; - return; - } - - /* Update the stats and max_bitflips */ - mtd->ecc_stats.corrected += bf; - *max_bitflips = max_t(unsigned int, *max_bitflips, bf); -} - -/* - * Check a chunk is correct or not according to hardware ECC engine. - * mtd->ecc_stats.corrected is updated, as well as max_bitflips, however - * mtd->ecc_stats.failure is not, the function will instead return a non-zero - * value indicating that a check on the emptyness of the subpage must be - * performed before declaring the subpage corrupted. - */ -static int marvell_nfc_hw_ecc_correct(struct nand_chip *chip, - unsigned int *max_bitflips) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - int bf = 0; - u32 ndsr; - - ndsr = readl_relaxed(nfc->regs + NDSR); - - /* Check uncorrectable error flag */ - if (ndsr & NDSR_UNCERR) { - writel_relaxed(ndsr, nfc->regs + NDSR); - - /* - * Do not increment ->ecc_stats.failed now, instead, return a - * non-zero value to indicate that this chunk was apparently - * bad, and it should be check to see if it empty or not. If - * the chunk (with ECC bytes) is not declared empty, the calling - * function must increment the failure count. - */ - return -EBADMSG; - } - - /* Check correctable error flag */ - if (ndsr & NDSR_CORERR) { - writel_relaxed(ndsr, nfc->regs + NDSR); - - if (chip->ecc.algo == NAND_ECC_BCH) - bf = NDSR_ERRCNT(ndsr); - else - bf = 1; - } - - /* Update the stats and max_bitflips */ - mtd->ecc_stats.corrected += bf; - *max_bitflips = max_t(unsigned int, *max_bitflips, bf); - - return 0; -} - -/* Hamming read helpers */ -static int marvell_nfc_hw_ecc_hmg_do_read_page(struct nand_chip *chip, - u8 *data_buf, u8 *oob_buf, - bool raw, int page) -{ - struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - struct marvell_nfc_op nfc_op = { - .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) | - NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | - NDCB0_DBC | - NDCB0_CMD1(NAND_CMD_READ0) | - NDCB0_CMD2(NAND_CMD_READSTART), - .ndcb[1] = NDCB1_ADDRS_PAGE(page), - .ndcb[2] = NDCB2_ADDR5_PAGE(page), - }; - unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0); - int ret; - - /* NFCv2 needs more information about the operation being executed */ - if (nfc->caps->is_nfcv2) - nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW); - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return ret; - - marvell_nfc_send_cmd(chip, &nfc_op); - ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ, - "RDDREQ while draining FIFO (data/oob)"); - if (ret) - return ret; - - /* - * Read the page then the OOB area. Unlike what is shown in current - * documentation, spare bytes are protected by the ECC engine, and must - * be at the beginning of the OOB area or running this driver on legacy - * systems will prevent the discovery of the BBM/BBT. - */ - if (nfc->use_dma) { - marvell_nfc_xfer_data_dma(nfc, DMA_FROM_DEVICE, - lt->data_bytes + oob_bytes); - memcpy(data_buf, nfc->dma_buf, lt->data_bytes); - memcpy(oob_buf, nfc->dma_buf + lt->data_bytes, oob_bytes); - } else { - marvell_nfc_xfer_data_in_pio(nfc, data_buf, lt->data_bytes); - marvell_nfc_xfer_data_in_pio(nfc, oob_buf, oob_bytes); - } - - ret = marvell_nfc_wait_cmdd(chip); - - return ret; -} - -static int marvell_nfc_hw_ecc_hmg_read_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, - int oob_required, int page) -{ - return marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, - true, page); -} - -static int marvell_nfc_hw_ecc_hmg_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - u8 *buf, int oob_required, - int page) -{ - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - unsigned int full_sz = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes; - int max_bitflips = 0, ret; - u8 *raw_buf; - - marvell_nfc_enable_hw_ecc(chip); - marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, false, - page); - ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips); - marvell_nfc_disable_hw_ecc(chip); - - if (!ret) - return max_bitflips; - - /* - * When ECC failures are detected, check if the full page has been - * written or not. Ignore the failure if it is actually empty. - */ - raw_buf = kmalloc(full_sz, GFP_KERNEL); - if (!raw_buf) - return -ENOMEM; - - marvell_nfc_hw_ecc_hmg_do_read_page(chip, raw_buf, raw_buf + - lt->data_bytes, true, page); - marvell_nfc_check_empty_chunk(chip, raw_buf, full_sz, NULL, 0, NULL, 0, - &max_bitflips); - kfree(raw_buf); - - return max_bitflips; -} - -/* - * Spare area in Hamming layouts is not protected by the ECC engine (even if - * it appears before the ECC bytes when reading), the ->read_oob_raw() function - * also stands for ->read_oob(). - */ -static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - /* Invalidate page cache */ - chip->pagebuf = -1; - - return marvell_nfc_hw_ecc_hmg_do_read_page(chip, chip->data_buf, - chip->oob_poi, true, page); -} - -/* Hamming write helpers */ -static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip, - const u8 *data_buf, - const u8 *oob_buf, bool raw, - int page) -{ - struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - struct marvell_nfc_op nfc_op = { - .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) | - NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | - NDCB0_CMD1(NAND_CMD_SEQIN) | - NDCB0_CMD2(NAND_CMD_PAGEPROG) | - NDCB0_DBC, - .ndcb[1] = NDCB1_ADDRS_PAGE(page), - .ndcb[2] = NDCB2_ADDR5_PAGE(page), - }; - unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0); - int ret; - - /* NFCv2 needs more information about the operation being executed */ - if (nfc->caps->is_nfcv2) - nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW); - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return ret; - - marvell_nfc_send_cmd(chip, &nfc_op); - ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ, - "WRDREQ while loading FIFO (data)"); - if (ret) - return ret; - - /* Write the page then the OOB area */ - if (nfc->use_dma) { - memcpy(nfc->dma_buf, data_buf, lt->data_bytes); - memcpy(nfc->dma_buf + lt->data_bytes, oob_buf, oob_bytes); - marvell_nfc_xfer_data_dma(nfc, DMA_TO_DEVICE, lt->data_bytes + - lt->ecc_bytes + lt->spare_bytes); - } else { - marvell_nfc_xfer_data_out_pio(nfc, data_buf, lt->data_bytes); - marvell_nfc_xfer_data_out_pio(nfc, oob_buf, oob_bytes); - } - - ret = marvell_nfc_wait_cmdd(chip); - if (ret) - return ret; - - ret = marvell_nfc_wait_op(chip, - chip->data_interface.timings.sdr.tPROG_max); - return ret; -} - -static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, - const u8 *buf, - int oob_required, int page) -{ - return marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi, - true, page); -} - -static int marvell_nfc_hw_ecc_hmg_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const u8 *buf, - int oob_required, int page) -{ - int ret; - - marvell_nfc_enable_hw_ecc(chip); - ret = marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi, - false, page); - marvell_nfc_disable_hw_ecc(chip); - - return ret; -} - -/* - * Spare area in Hamming layouts is not protected by the ECC engine (even if - * it appears before the ECC bytes when reading), the ->write_oob_raw() function - * also stands for ->write_oob(). - */ -static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct mtd_info *mtd, - struct nand_chip *chip, - int page) -{ - /* Invalidate page cache */ - chip->pagebuf = -1; - - memset(chip->data_buf, 0xFF, mtd->writesize); - - return marvell_nfc_hw_ecc_hmg_do_write_page(chip, chip->data_buf, - chip->oob_poi, true, page); -} - -/* BCH read helpers */ -static int marvell_nfc_hw_ecc_bch_read_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, - int oob_required, int page) -{ - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - u8 *oob = chip->oob_poi; - int chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes; - int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) + - lt->last_spare_bytes; - int data_len = lt->data_bytes; - int spare_len = lt->spare_bytes; - int ecc_len = lt->ecc_bytes; - int chunk; - - if (oob_required) - memset(chip->oob_poi, 0xFF, mtd->oobsize); - - nand_read_page_op(chip, page, 0, NULL, 0); - - for (chunk = 0; chunk < lt->nchunks; chunk++) { - /* Update last chunk length */ - if (chunk >= lt->full_chunk_cnt) { - data_len = lt->last_data_bytes; - spare_len = lt->last_spare_bytes; - ecc_len = lt->last_ecc_bytes; - } - - /* Read data bytes*/ - nand_change_read_column_op(chip, chunk * chunk_size, - buf + (lt->data_bytes * chunk), - data_len, false); - - /* Read spare bytes */ - nand_read_data_op(chip, oob + (lt->spare_bytes * chunk), - spare_len, false); - - /* Read ECC bytes */ - nand_read_data_op(chip, oob + ecc_offset + - (ALIGN(lt->ecc_bytes, 32) * chunk), - ecc_len, false); - } - - return 0; -} - -static void marvell_nfc_hw_ecc_bch_read_chunk(struct nand_chip *chip, int chunk, - u8 *data, unsigned int data_len, - u8 *spare, unsigned int spare_len, - int page) -{ - struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - int i, ret; - struct marvell_nfc_op nfc_op = { - .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) | - NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | - NDCB0_LEN_OVRD, - .ndcb[1] = NDCB1_ADDRS_PAGE(page), - .ndcb[2] = NDCB2_ADDR5_PAGE(page), - .ndcb[3] = data_len + spare_len, - }; - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return; - - if (chunk == 0) - nfc_op.ndcb[0] |= NDCB0_DBC | - NDCB0_CMD1(NAND_CMD_READ0) | - NDCB0_CMD2(NAND_CMD_READSTART); - - /* - * Trigger the naked read operation only on the last chunk. - * Otherwise, use monolithic read. - */ - if (lt->nchunks == 1 || (chunk < lt->nchunks - 1)) - nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW); - else - nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); - - marvell_nfc_send_cmd(chip, &nfc_op); - - /* - * According to the datasheet, when reading from NDDB - * with BCH enabled, after each 32 bytes reads, we - * have to make sure that the NDSR.RDDREQ bit is set. - * - * Drain the FIFO, 8 32-bit reads at a time, and skip - * the polling on the last read. - * - * Length is a multiple of 32 bytes, hence it is a multiple of 8 too. - */ - for (i = 0; i < data_len; i += FIFO_DEPTH * BCH_SEQ_READS) { - marvell_nfc_end_cmd(chip, NDSR_RDDREQ, - "RDDREQ while draining FIFO (data)"); - marvell_nfc_xfer_data_in_pio(nfc, data, - FIFO_DEPTH * BCH_SEQ_READS); - data += FIFO_DEPTH * BCH_SEQ_READS; - } - - for (i = 0; i < spare_len; i += FIFO_DEPTH * BCH_SEQ_READS) { - marvell_nfc_end_cmd(chip, NDSR_RDDREQ, - "RDDREQ while draining FIFO (OOB)"); - marvell_nfc_xfer_data_in_pio(nfc, spare, - FIFO_DEPTH * BCH_SEQ_READS); - spare += FIFO_DEPTH * BCH_SEQ_READS; - } -} - -static int marvell_nfc_hw_ecc_bch_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - u8 *buf, int oob_required, - int page) -{ - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - int data_len = lt->data_bytes, spare_len = lt->spare_bytes, ecc_len; - u8 *data = buf, *spare = chip->oob_poi, *ecc; - int max_bitflips = 0; - u32 failure_mask = 0; - int chunk, ecc_offset_in_page, ret; - - /* - * With BCH, OOB is not fully used (and thus not read entirely), not - * expected bytes could show up at the end of the OOB buffer if not - * explicitly erased. - */ - if (oob_required) - memset(chip->oob_poi, 0xFF, mtd->oobsize); - - marvell_nfc_enable_hw_ecc(chip); - - for (chunk = 0; chunk < lt->nchunks; chunk++) { - /* Update length for the last chunk */ - if (chunk >= lt->full_chunk_cnt) { - data_len = lt->last_data_bytes; - spare_len = lt->last_spare_bytes; - } - - /* Read the chunk and detect number of bitflips */ - marvell_nfc_hw_ecc_bch_read_chunk(chip, chunk, data, data_len, - spare, spare_len, page); - ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips); - if (ret) - failure_mask |= BIT(chunk); - - data += data_len; - spare += spare_len; - } - - marvell_nfc_disable_hw_ecc(chip); - - if (!failure_mask) - return max_bitflips; - - /* - * Please note that dumping the ECC bytes during a normal read with OOB - * area would add a significant overhead as ECC bytes are "consumed" by - * the controller in normal mode and must be re-read in raw mode. To - * avoid dropping the performances, we prefer not to include them. The - * user should re-read the page in raw mode if ECC bytes are required. - * - * However, for any subpage read error reported by ->correct(), the ECC - * bytes must be read in raw mode and the full subpage must be checked - * to see if it is entirely empty of if there was an actual error. - */ - for (chunk = 0; chunk < lt->nchunks; chunk++) { - /* No failure reported for this chunk, move to the next one */ - if (!(failure_mask & BIT(chunk))) - continue; - - /* Derive ECC bytes positions (in page/buffer) and length */ - ecc = chip->oob_poi + - (lt->full_chunk_cnt * lt->spare_bytes) + - lt->last_spare_bytes + - (chunk * ALIGN(lt->ecc_bytes, 32)); - ecc_offset_in_page = - (chunk * (lt->data_bytes + lt->spare_bytes + - lt->ecc_bytes)) + - (chunk < lt->full_chunk_cnt ? - lt->data_bytes + lt->spare_bytes : - lt->last_data_bytes + lt->last_spare_bytes); - ecc_len = chunk < lt->full_chunk_cnt ? - lt->ecc_bytes : lt->last_ecc_bytes; - - /* Do the actual raw read of the ECC bytes */ - nand_change_read_column_op(chip, ecc_offset_in_page, - ecc, ecc_len, false); - - /* Derive data/spare bytes positions (in buffer) and length */ - data = buf + (chunk * lt->data_bytes); - data_len = chunk < lt->full_chunk_cnt ? - lt->data_bytes : lt->last_data_bytes; - spare = chip->oob_poi + (chunk * (lt->spare_bytes + - lt->ecc_bytes)); - spare_len = chunk < lt->full_chunk_cnt ? - lt->spare_bytes : lt->last_spare_bytes; - - /* Check the entire chunk (data + spare + ecc) for emptyness */ - marvell_nfc_check_empty_chunk(chip, data, data_len, spare, - spare_len, ecc, ecc_len, - &max_bitflips); - } - - return max_bitflips; -} - -static int marvell_nfc_hw_ecc_bch_read_oob_raw(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - /* Invalidate page cache */ - chip->pagebuf = -1; - - return chip->ecc.read_page_raw(mtd, chip, chip->data_buf, true, page); -} - -static int marvell_nfc_hw_ecc_bch_read_oob(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - /* Invalidate page cache */ - chip->pagebuf = -1; - - return chip->ecc.read_page(mtd, chip, chip->data_buf, true, page); -} - -/* BCH write helpers */ -static int marvell_nfc_hw_ecc_bch_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, - const u8 *buf, - int oob_required, int page) -{ - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - int full_chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes; - int data_len = lt->data_bytes; - int spare_len = lt->spare_bytes; - int ecc_len = lt->ecc_bytes; - int spare_offset = 0; - int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) + - lt->last_spare_bytes; - int chunk; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - for (chunk = 0; chunk < lt->nchunks; chunk++) { - if (chunk >= lt->full_chunk_cnt) { - data_len = lt->last_data_bytes; - spare_len = lt->last_spare_bytes; - ecc_len = lt->last_ecc_bytes; - } - - /* Point to the column of the next chunk */ - nand_change_write_column_op(chip, chunk * full_chunk_size, - NULL, 0, false); - - /* Write the data */ - nand_write_data_op(chip, buf + (chunk * lt->data_bytes), - data_len, false); - - if (!oob_required) - continue; - - /* Write the spare bytes */ - if (spare_len) - nand_write_data_op(chip, chip->oob_poi + spare_offset, - spare_len, false); - - /* Write the ECC bytes */ - if (ecc_len) - nand_write_data_op(chip, chip->oob_poi + ecc_offset, - ecc_len, false); - - spare_offset += spare_len; - ecc_offset += ALIGN(ecc_len, 32); - } - - return nand_prog_page_end_op(chip); -} - -static int -marvell_nfc_hw_ecc_bch_write_chunk(struct nand_chip *chip, int chunk, - const u8 *data, unsigned int data_len, - const u8 *spare, unsigned int spare_len, - int page) -{ - struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - int ret; - struct marvell_nfc_op nfc_op = { - .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) | NDCB0_LEN_OVRD, - .ndcb[3] = data_len + spare_len, - }; - - /* - * First operation dispatches the CMD_SEQIN command, issue the address - * cycles and asks for the first chunk of data. - * All operations in the middle (if any) will issue a naked write and - * also ask for data. - * Last operation (if any) asks for the last chunk of data through a - * last naked write. - */ - if (chunk == 0) { - nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_WRITE_DISPATCH) | - NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | - NDCB0_CMD1(NAND_CMD_SEQIN); - nfc_op.ndcb[1] |= NDCB1_ADDRS_PAGE(page); - nfc_op.ndcb[2] |= NDCB2_ADDR5_PAGE(page); - } else if (chunk < lt->nchunks - 1) { - nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_NAKED_RW); - } else { - nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); - } - - /* Always dispatch the PAGEPROG command on the last chunk */ - if (chunk == lt->nchunks - 1) - nfc_op.ndcb[0] |= NDCB0_CMD2(NAND_CMD_PAGEPROG) | NDCB0_DBC; - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return ret; - - marvell_nfc_send_cmd(chip, &nfc_op); - ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ, - "WRDREQ while loading FIFO (data)"); - if (ret) - return ret; - - /* Transfer the contents */ - iowrite32_rep(nfc->regs + NDDB, data, FIFO_REP(data_len)); - iowrite32_rep(nfc->regs + NDDB, spare, FIFO_REP(spare_len)); - - return 0; -} - -static int marvell_nfc_hw_ecc_bch_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const u8 *buf, - int oob_required, int page) -{ - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - const u8 *data = buf; - const u8 *spare = chip->oob_poi; - int data_len = lt->data_bytes; - int spare_len = lt->spare_bytes; - int chunk, ret; - - /* Spare data will be written anyway, so clear it to avoid garbage */ - if (!oob_required) - memset(chip->oob_poi, 0xFF, mtd->oobsize); - - marvell_nfc_enable_hw_ecc(chip); - - for (chunk = 0; chunk < lt->nchunks; chunk++) { - if (chunk >= lt->full_chunk_cnt) { - data_len = lt->last_data_bytes; - spare_len = lt->last_spare_bytes; - } - - marvell_nfc_hw_ecc_bch_write_chunk(chip, chunk, data, data_len, - spare, spare_len, page); - data += data_len; - spare += spare_len; - - /* - * Waiting only for CMDD or PAGED is not enough, ECC are - * partially written. No flag is set once the operation is - * really finished but the ND_RUN bit is cleared, so wait for it - * before stepping into the next command. - */ - marvell_nfc_wait_ndrun(chip); - } - - ret = marvell_nfc_wait_op(chip, - chip->data_interface.timings.sdr.tPROG_max); - - marvell_nfc_disable_hw_ecc(chip); - - if (ret) - return ret; - - return 0; -} - -static int marvell_nfc_hw_ecc_bch_write_oob_raw(struct mtd_info *mtd, - struct nand_chip *chip, - int page) -{ - /* Invalidate page cache */ - chip->pagebuf = -1; - - memset(chip->data_buf, 0xFF, mtd->writesize); - - return chip->ecc.write_page_raw(mtd, chip, chip->data_buf, true, page); -} - -static int marvell_nfc_hw_ecc_bch_write_oob(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - /* Invalidate page cache */ - chip->pagebuf = -1; - - memset(chip->data_buf, 0xFF, mtd->writesize); - - return chip->ecc.write_page(mtd, chip, chip->data_buf, true, page); -} - -/* NAND framework ->exec_op() hooks and related helpers */ -static void marvell_nfc_parse_instructions(struct nand_chip *chip, - const struct nand_subop *subop, - struct marvell_nfc_op *nfc_op) -{ - const struct nand_op_instr *instr = NULL; - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - bool first_cmd = true; - unsigned int op_id; - int i; - - /* Reset the input structure as most of its fields will be OR'ed */ - memset(nfc_op, 0, sizeof(struct marvell_nfc_op)); - - for (op_id = 0; op_id < subop->ninstrs; op_id++) { - unsigned int offset, naddrs; - const u8 *addrs; - int len = nand_subop_get_data_len(subop, op_id); - - instr = &subop->instrs[op_id]; - - switch (instr->type) { - case NAND_OP_CMD_INSTR: - if (first_cmd) - nfc_op->ndcb[0] |= - NDCB0_CMD1(instr->ctx.cmd.opcode); - else - nfc_op->ndcb[0] |= - NDCB0_CMD2(instr->ctx.cmd.opcode) | - NDCB0_DBC; - - nfc_op->cle_ale_delay_ns = instr->delay_ns; - first_cmd = false; - break; - - case NAND_OP_ADDR_INSTR: - offset = nand_subop_get_addr_start_off(subop, op_id); - naddrs = nand_subop_get_num_addr_cyc(subop, op_id); - addrs = &instr->ctx.addr.addrs[offset]; - - nfc_op->ndcb[0] |= NDCB0_ADDR_CYC(naddrs); - - for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) - nfc_op->ndcb[1] |= addrs[i] << (8 * i); - - if (naddrs >= 5) - nfc_op->ndcb[2] |= NDCB2_ADDR5_CYC(addrs[4]); - if (naddrs >= 6) - nfc_op->ndcb[3] |= NDCB3_ADDR6_CYC(addrs[5]); - if (naddrs == 7) - nfc_op->ndcb[3] |= NDCB3_ADDR7_CYC(addrs[6]); - - nfc_op->cle_ale_delay_ns = instr->delay_ns; - break; - - case NAND_OP_DATA_IN_INSTR: - nfc_op->data_instr = instr; - nfc_op->data_instr_idx = op_id; - nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ); - if (nfc->caps->is_nfcv2) { - nfc_op->ndcb[0] |= - NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) | - NDCB0_LEN_OVRD; - nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH); - } - nfc_op->data_delay_ns = instr->delay_ns; - break; - - case NAND_OP_DATA_OUT_INSTR: - nfc_op->data_instr = instr; - nfc_op->data_instr_idx = op_id; - nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE); - if (nfc->caps->is_nfcv2) { - nfc_op->ndcb[0] |= - NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) | - NDCB0_LEN_OVRD; - nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH); - } - nfc_op->data_delay_ns = instr->delay_ns; - break; - - case NAND_OP_WAITRDY_INSTR: - nfc_op->rdy_timeout_ms = instr->ctx.waitrdy.timeout_ms; - nfc_op->rdy_delay_ns = instr->delay_ns; - break; - } - } -} - -static int marvell_nfc_xfer_data_pio(struct nand_chip *chip, - const struct nand_subop *subop, - struct marvell_nfc_op *nfc_op) -{ - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - const struct nand_op_instr *instr = nfc_op->data_instr; - unsigned int op_id = nfc_op->data_instr_idx; - unsigned int len = nand_subop_get_data_len(subop, op_id); - unsigned int offset = nand_subop_get_data_start_off(subop, op_id); - bool reading = (instr->type == NAND_OP_DATA_IN_INSTR); - int ret; - - if (instr->ctx.data.force_8bit) - marvell_nfc_force_byte_access(chip, true); - - if (reading) { - u8 *in = instr->ctx.data.buf.in + offset; - - ret = marvell_nfc_xfer_data_in_pio(nfc, in, len); - } else { - const u8 *out = instr->ctx.data.buf.out + offset; - - ret = marvell_nfc_xfer_data_out_pio(nfc, out, len); - } - - if (instr->ctx.data.force_8bit) - marvell_nfc_force_byte_access(chip, false); - - return ret; -} - -static int marvell_nfc_monolithic_access_exec(struct nand_chip *chip, - const struct nand_subop *subop) -{ - struct marvell_nfc_op nfc_op; - bool reading; - int ret; - - marvell_nfc_parse_instructions(chip, subop, &nfc_op); - reading = (nfc_op.data_instr->type == NAND_OP_DATA_IN_INSTR); - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return ret; - - marvell_nfc_send_cmd(chip, &nfc_op); - ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ, - "RDDREQ/WRDREQ while draining raw data"); - if (ret) - return ret; - - cond_delay(nfc_op.cle_ale_delay_ns); - - if (reading) { - if (nfc_op.rdy_timeout_ms) { - ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); - if (ret) - return ret; - } - - cond_delay(nfc_op.rdy_delay_ns); - } - - marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); - ret = marvell_nfc_wait_cmdd(chip); - if (ret) - return ret; - - cond_delay(nfc_op.data_delay_ns); - - if (!reading) { - if (nfc_op.rdy_timeout_ms) { - ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); - if (ret) - return ret; - } - - cond_delay(nfc_op.rdy_delay_ns); - } - - /* - * NDCR ND_RUN bit should be cleared automatically at the end of each - * operation but experience shows that the behavior is buggy when it - * comes to writes (with LEN_OVRD). Clear it by hand in this case. - */ - if (!reading) { - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - - writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN, - nfc->regs + NDCR); - } - - return 0; -} - -static int marvell_nfc_naked_access_exec(struct nand_chip *chip, - const struct nand_subop *subop) -{ - struct marvell_nfc_op nfc_op; - int ret; - - marvell_nfc_parse_instructions(chip, subop, &nfc_op); - - /* - * Naked access are different in that they need to be flagged as naked - * by the controller. Reset the controller registers fields that inform - * on the type and refill them according to the ongoing operation. - */ - nfc_op.ndcb[0] &= ~(NDCB0_CMD_TYPE(TYPE_MASK) | - NDCB0_CMD_XTYPE(XTYPE_MASK)); - switch (subop->instrs[0].type) { - case NAND_OP_CMD_INSTR: - nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_CMD); - break; - case NAND_OP_ADDR_INSTR: - nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_ADDR); - break; - case NAND_OP_DATA_IN_INSTR: - nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ) | - NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); - break; - case NAND_OP_DATA_OUT_INSTR: - nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE) | - NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); - break; - default: - /* This should never happen */ - break; - } - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return ret; - - marvell_nfc_send_cmd(chip, &nfc_op); - - if (!nfc_op.data_instr) { - ret = marvell_nfc_wait_cmdd(chip); - cond_delay(nfc_op.cle_ale_delay_ns); - return ret; - } - - ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ, - "RDDREQ/WRDREQ while draining raw data"); - if (ret) - return ret; - - marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); - ret = marvell_nfc_wait_cmdd(chip); - if (ret) - return ret; - - /* - * NDCR ND_RUN bit should be cleared automatically at the end of each - * operation but experience shows that the behavior is buggy when it - * comes to writes (with LEN_OVRD). Clear it by hand in this case. - */ - if (subop->instrs[0].type == NAND_OP_DATA_OUT_INSTR) { - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - - writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN, - nfc->regs + NDCR); - } - - return 0; -} - -static int marvell_nfc_naked_waitrdy_exec(struct nand_chip *chip, - const struct nand_subop *subop) -{ - struct marvell_nfc_op nfc_op; - int ret; - - marvell_nfc_parse_instructions(chip, subop, &nfc_op); - - ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); - cond_delay(nfc_op.rdy_delay_ns); - - return ret; -} - -static int marvell_nfc_read_id_type_exec(struct nand_chip *chip, - const struct nand_subop *subop) -{ - struct marvell_nfc_op nfc_op; - int ret; - - marvell_nfc_parse_instructions(chip, subop, &nfc_op); - nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ); - nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ_ID); - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return ret; - - marvell_nfc_send_cmd(chip, &nfc_op); - ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ, - "RDDREQ while reading ID"); - if (ret) - return ret; - - cond_delay(nfc_op.cle_ale_delay_ns); - - if (nfc_op.rdy_timeout_ms) { - ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); - if (ret) - return ret; - } - - cond_delay(nfc_op.rdy_delay_ns); - - marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); - ret = marvell_nfc_wait_cmdd(chip); - if (ret) - return ret; - - cond_delay(nfc_op.data_delay_ns); - - return 0; -} - -static int marvell_nfc_read_status_exec(struct nand_chip *chip, - const struct nand_subop *subop) -{ - struct marvell_nfc_op nfc_op; - int ret; - - marvell_nfc_parse_instructions(chip, subop, &nfc_op); - nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ); - nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_STATUS); - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return ret; - - marvell_nfc_send_cmd(chip, &nfc_op); - ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ, - "RDDREQ while reading status"); - if (ret) - return ret; - - cond_delay(nfc_op.cle_ale_delay_ns); - - if (nfc_op.rdy_timeout_ms) { - ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); - if (ret) - return ret; - } - - cond_delay(nfc_op.rdy_delay_ns); - - marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); - ret = marvell_nfc_wait_cmdd(chip); - if (ret) - return ret; - - cond_delay(nfc_op.data_delay_ns); - - return 0; -} - -static int marvell_nfc_reset_cmd_type_exec(struct nand_chip *chip, - const struct nand_subop *subop) -{ - struct marvell_nfc_op nfc_op; - int ret; - - marvell_nfc_parse_instructions(chip, subop, &nfc_op); - nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_RESET); - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return ret; - - marvell_nfc_send_cmd(chip, &nfc_op); - ret = marvell_nfc_wait_cmdd(chip); - if (ret) - return ret; - - cond_delay(nfc_op.cle_ale_delay_ns); - - ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); - if (ret) - return ret; - - cond_delay(nfc_op.rdy_delay_ns); - - return 0; -} - -static int marvell_nfc_erase_cmd_type_exec(struct nand_chip *chip, - const struct nand_subop *subop) -{ - struct marvell_nfc_op nfc_op; - int ret; - - marvell_nfc_parse_instructions(chip, subop, &nfc_op); - nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_ERASE); - - ret = marvell_nfc_prepare_cmd(chip); - if (ret) - return ret; - - marvell_nfc_send_cmd(chip, &nfc_op); - ret = marvell_nfc_wait_cmdd(chip); - if (ret) - return ret; - - cond_delay(nfc_op.cle_ale_delay_ns); - - ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); - if (ret) - return ret; - - cond_delay(nfc_op.rdy_delay_ns); - - return 0; -} - -static const struct nand_op_parser marvell_nfcv2_op_parser = NAND_OP_PARSER( - /* Monolithic reads/writes */ - NAND_OP_PARSER_PATTERN( - marvell_nfc_monolithic_access_exec, - NAND_OP_PARSER_PAT_CMD_ELEM(false), - NAND_OP_PARSER_PAT_ADDR_ELEM(true, MAX_ADDRESS_CYC_NFCV2), - NAND_OP_PARSER_PAT_CMD_ELEM(true), - NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), - NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)), - NAND_OP_PARSER_PATTERN( - marvell_nfc_monolithic_access_exec, - NAND_OP_PARSER_PAT_CMD_ELEM(false), - NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2), - NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE), - NAND_OP_PARSER_PAT_CMD_ELEM(true), - NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), - /* Naked commands */ - NAND_OP_PARSER_PATTERN( - marvell_nfc_naked_access_exec, - NAND_OP_PARSER_PAT_CMD_ELEM(false)), - NAND_OP_PARSER_PATTERN( - marvell_nfc_naked_access_exec, - NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2)), - NAND_OP_PARSER_PATTERN( - marvell_nfc_naked_access_exec, - NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)), - NAND_OP_PARSER_PATTERN( - marvell_nfc_naked_access_exec, - NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE)), - NAND_OP_PARSER_PATTERN( - marvell_nfc_naked_waitrdy_exec, - NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), - ); - -static const struct nand_op_parser marvell_nfcv1_op_parser = NAND_OP_PARSER( - /* Naked commands not supported, use a function for each pattern */ - NAND_OP_PARSER_PATTERN( - marvell_nfc_read_id_type_exec, - NAND_OP_PARSER_PAT_CMD_ELEM(false), - NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1), - NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)), - NAND_OP_PARSER_PATTERN( - marvell_nfc_erase_cmd_type_exec, - NAND_OP_PARSER_PAT_CMD_ELEM(false), - NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1), - NAND_OP_PARSER_PAT_CMD_ELEM(false), - NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), - NAND_OP_PARSER_PATTERN( - marvell_nfc_read_status_exec, - NAND_OP_PARSER_PAT_CMD_ELEM(false), - NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)), - NAND_OP_PARSER_PATTERN( - marvell_nfc_reset_cmd_type_exec, - NAND_OP_PARSER_PAT_CMD_ELEM(false), - NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), - NAND_OP_PARSER_PATTERN( - marvell_nfc_naked_waitrdy_exec, - NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), - ); - -static int marvell_nfc_exec_op(struct nand_chip *chip, - const struct nand_operation *op, - bool check_only) -{ - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - - if (nfc->caps->is_nfcv2) - return nand_op_parser_exec_op(chip, &marvell_nfcv2_op_parser, - op, check_only); - else - return nand_op_parser_exec_op(chip, &marvell_nfcv1_op_parser, - op, check_only); -} - -/* - * Layouts were broken in old pxa3xx_nand driver, these are supposed to be - * usable. - */ -static int marvell_nand_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - - if (section) - return -ERANGE; - - oobregion->length = (lt->full_chunk_cnt * lt->ecc_bytes) + - lt->last_ecc_bytes; - oobregion->offset = mtd->oobsize - oobregion->length; - - return 0; -} - -static int marvell_nand_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; - - if (section) - return -ERANGE; - - /* - * Bootrom looks in bytes 0 & 5 for bad blocks for the - * 4KB page / 4bit BCH combination. - */ - if (mtd->writesize == SZ_4K && lt->data_bytes == SZ_2K) - oobregion->offset = 6; - else - oobregion->offset = 2; - - oobregion->length = (lt->full_chunk_cnt * lt->spare_bytes) + - lt->last_spare_bytes - oobregion->offset; - - return 0; -} - -static const struct mtd_ooblayout_ops marvell_nand_ooblayout_ops = { - .ecc = marvell_nand_ooblayout_ecc, - .free = marvell_nand_ooblayout_free, -}; - -static int marvell_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - const struct marvell_hw_ecc_layout *l; - int i; - - if (!nfc->caps->is_nfcv2 && - (mtd->writesize + mtd->oobsize > MAX_CHUNK_SIZE)) { - dev_err(nfc->dev, - "NFCv1: writesize (%d) cannot be bigger than a chunk (%d)\n", - mtd->writesize, MAX_CHUNK_SIZE - mtd->oobsize); - return -ENOTSUPP; - } - - to_marvell_nand(chip)->layout = NULL; - for (i = 0; i < ARRAY_SIZE(marvell_nfc_layouts); i++) { - l = &marvell_nfc_layouts[i]; - if (mtd->writesize == l->writesize && - ecc->size == l->chunk && ecc->strength == l->strength) { - to_marvell_nand(chip)->layout = l; - break; - } - } - - if (!to_marvell_nand(chip)->layout || - (!nfc->caps->is_nfcv2 && ecc->strength > 1)) { - dev_err(nfc->dev, - "ECC strength %d at page size %d is not supported\n", - ecc->strength, mtd->writesize); - return -ENOTSUPP; - } - - mtd_set_ooblayout(mtd, &marvell_nand_ooblayout_ops); - ecc->steps = l->nchunks; - ecc->size = l->data_bytes; - - if (ecc->strength == 1) { - chip->ecc.algo = NAND_ECC_HAMMING; - ecc->read_page_raw = marvell_nfc_hw_ecc_hmg_read_page_raw; - ecc->read_page = marvell_nfc_hw_ecc_hmg_read_page; - ecc->read_oob_raw = marvell_nfc_hw_ecc_hmg_read_oob_raw; - ecc->read_oob = ecc->read_oob_raw; - ecc->write_page_raw = marvell_nfc_hw_ecc_hmg_write_page_raw; - ecc->write_page = marvell_nfc_hw_ecc_hmg_write_page; - ecc->write_oob_raw = marvell_nfc_hw_ecc_hmg_write_oob_raw; - ecc->write_oob = ecc->write_oob_raw; - } else { - chip->ecc.algo = NAND_ECC_BCH; - ecc->strength = 16; - ecc->read_page_raw = marvell_nfc_hw_ecc_bch_read_page_raw; - ecc->read_page = marvell_nfc_hw_ecc_bch_read_page; - ecc->read_oob_raw = marvell_nfc_hw_ecc_bch_read_oob_raw; - ecc->read_oob = marvell_nfc_hw_ecc_bch_read_oob; - ecc->write_page_raw = marvell_nfc_hw_ecc_bch_write_page_raw; - ecc->write_page = marvell_nfc_hw_ecc_bch_write_page; - ecc->write_oob_raw = marvell_nfc_hw_ecc_bch_write_oob_raw; - ecc->write_oob = marvell_nfc_hw_ecc_bch_write_oob; - } - - return 0; -} - -static int marvell_nand_ecc_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - int ret; - - if (ecc->mode != NAND_ECC_NONE && (!ecc->size || !ecc->strength)) { - if (chip->ecc_step_ds && chip->ecc_strength_ds) { - ecc->size = chip->ecc_step_ds; - ecc->strength = chip->ecc_strength_ds; - } else { - dev_info(nfc->dev, - "No minimum ECC strength, using 1b/512B\n"); - ecc->size = 512; - ecc->strength = 1; - } - } - - switch (ecc->mode) { - case NAND_ECC_HW: - ret = marvell_nand_hw_ecc_ctrl_init(mtd, ecc); - if (ret) - return ret; - break; - case NAND_ECC_NONE: - case NAND_ECC_SOFT: - if (!nfc->caps->is_nfcv2 && mtd->writesize != SZ_512 && - mtd->writesize != SZ_2K) { - dev_err(nfc->dev, "NFCv1 cannot write %d bytes pages\n", - mtd->writesize); - return -EINVAL; - } - break; - default: - return -EINVAL; - } - - return 0; -} - -static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; -static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 8, - .len = 6, - .veroffs = 14, - .maxblocks = 8, /* Last 8 blocks in each chip */ - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 8, - .len = 6, - .veroffs = 14, - .maxblocks = 8, /* Last 8 blocks in each chip */ - .pattern = bbt_mirror_pattern -}; - -static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr, - const struct nand_data_interface - *conf) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); - struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); - unsigned int period_ns = 1000000000 / clk_get_rate(nfc->ecc_clk) * 2; - const struct nand_sdr_timings *sdr; - struct marvell_nfc_timings nfc_tmg; - int read_delay; - - sdr = nand_get_sdr_timings(conf); - if (IS_ERR(sdr)) - return PTR_ERR(sdr); - - /* - * SDR timings are given in pico-seconds while NFC timings must be - * expressed in NAND controller clock cycles, which is half of the - * frequency of the accessible ECC clock retrieved by clk_get_rate(). - * This is not written anywhere in the datasheet but was observed - * with an oscilloscope. - * - * NFC datasheet gives equations from which thoses calculations - * are derived, they tend to be slightly more restrictives than the - * given core timings and may improve the overall speed. - */ - nfc_tmg.tRP = TO_CYCLES(DIV_ROUND_UP(sdr->tRC_min, 2), period_ns) - 1; - nfc_tmg.tRH = nfc_tmg.tRP; - nfc_tmg.tWP = TO_CYCLES(DIV_ROUND_UP(sdr->tWC_min, 2), period_ns) - 1; - nfc_tmg.tWH = nfc_tmg.tWP; - nfc_tmg.tCS = TO_CYCLES(sdr->tCS_min, period_ns); - nfc_tmg.tCH = TO_CYCLES(sdr->tCH_min, period_ns) - 1; - nfc_tmg.tADL = TO_CYCLES(sdr->tADL_min, period_ns); - /* - * Read delay is the time of propagation from SoC pins to NFC internal - * logic. With non-EDO timings, this is MIN_RD_DEL_CNT clock cycles. In - * EDO mode, an additional delay of tRH must be taken into account so - * the data is sampled on the falling edge instead of the rising edge. - */ - read_delay = sdr->tRC_min >= 30000 ? - MIN_RD_DEL_CNT : MIN_RD_DEL_CNT + nfc_tmg.tRH; - - nfc_tmg.tAR = TO_CYCLES(sdr->tAR_min, period_ns); - /* - * tWHR and tRHW are supposed to be read to write delays (and vice - * versa) but in some cases, ie. when doing a change column, they must - * be greater than that to be sure tCCS delay is respected. - */ - nfc_tmg.tWHR = TO_CYCLES(max_t(int, sdr->tWHR_min, sdr->tCCS_min), - period_ns) - 2, - nfc_tmg.tRHW = TO_CYCLES(max_t(int, sdr->tRHW_min, sdr->tCCS_min), - period_ns); - - /* - * NFCv2: Use WAIT_MODE (wait for RB line), do not rely only on delays. - * NFCv1: No WAIT_MODE, tR must be maximal. - */ - if (nfc->caps->is_nfcv2) { - nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns); - } else { - nfc_tmg.tR = TO_CYCLES64(sdr->tWB_max + sdr->tR_max, - period_ns); - if (nfc_tmg.tR + 3 > nfc_tmg.tCH) - nfc_tmg.tR = nfc_tmg.tCH - 3; - else - nfc_tmg.tR = 0; - } - - if (chipnr < 0) - return 0; - - marvell_nand->ndtr0 = - NDTR0_TRP(nfc_tmg.tRP) | - NDTR0_TRH(nfc_tmg.tRH) | - NDTR0_ETRP(nfc_tmg.tRP) | - NDTR0_TWP(nfc_tmg.tWP) | - NDTR0_TWH(nfc_tmg.tWH) | - NDTR0_TCS(nfc_tmg.tCS) | - NDTR0_TCH(nfc_tmg.tCH); - - marvell_nand->ndtr1 = - NDTR1_TAR(nfc_tmg.tAR) | - NDTR1_TWHR(nfc_tmg.tWHR) | - NDTR1_TR(nfc_tmg.tR); - - if (nfc->caps->is_nfcv2) { - marvell_nand->ndtr0 |= - NDTR0_RD_CNT_DEL(read_delay) | - NDTR0_SELCNTR | - NDTR0_TADL(nfc_tmg.tADL); - - marvell_nand->ndtr1 |= - NDTR1_TRHW(nfc_tmg.tRHW) | - NDTR1_WAIT_MODE; - } - - return 0; -} - -static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, - struct device_node *np) -{ - struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(dev); - struct marvell_nand_chip *marvell_nand; - struct mtd_info *mtd; - struct nand_chip *chip; - int nsels, ret, i; - u32 cs, rb; - - /* - * The legacy "num-cs" property indicates the number of CS on the only - * chip connected to the controller (legacy bindings does not support - * more than one chip). CS are only incremented one by one while the RB - * pin is always the #0. - * - * When not using legacy bindings, a couple of "reg" and "nand-rb" - * properties must be filled. For each chip, expressed as a subnode, - * "reg" points to the CS lines and "nand-rb" to the RB line. - */ - if (pdata) { - nsels = 1; - } else if (nfc->caps->legacy_of_bindings && - !of_get_property(np, "num-cs", &nsels)) { - dev_err(dev, "missing num-cs property\n"); - return -EINVAL; - } else if (!of_get_property(np, "reg", &nsels)) { - dev_err(dev, "missing reg property\n"); - return -EINVAL; - } - - if (!pdata) - nsels /= sizeof(u32); - if (!nsels) { - dev_err(dev, "invalid reg property size\n"); - return -EINVAL; - } - - /* Alloc the nand chip structure */ - marvell_nand = devm_kzalloc(dev, sizeof(*marvell_nand) + - (nsels * - sizeof(struct marvell_nand_chip_sel)), - GFP_KERNEL); - if (!marvell_nand) { - dev_err(dev, "could not allocate chip structure\n"); - return -ENOMEM; - } - - marvell_nand->nsels = nsels; - marvell_nand->selected_die = -1; - - for (i = 0; i < nsels; i++) { - if (pdata || nfc->caps->legacy_of_bindings) { - /* - * Legacy bindings use the CS lines in natural - * order (0, 1, ...) - */ - cs = i; - } else { - /* Retrieve CS id */ - ret = of_property_read_u32_index(np, "reg", i, &cs); - if (ret) { - dev_err(dev, "could not retrieve reg property: %d\n", - ret); - return ret; - } - } - - if (cs >= nfc->caps->max_cs_nb) { - dev_err(dev, "invalid reg value: %u (max CS = %d)\n", - cs, nfc->caps->max_cs_nb); - return -EINVAL; - } - - if (test_and_set_bit(cs, &nfc->assigned_cs)) { - dev_err(dev, "CS %d already assigned\n", cs); - return -EINVAL; - } - - /* - * The cs variable represents the chip select id, which must be - * converted in bit fields for NDCB0 and NDCB2 to select the - * right chip. Unfortunately, due to a lack of information on - * the subject and incoherent documentation, the user should not - * use CS1 and CS3 at all as asserting them is not supported in - * a reliable way (due to multiplexing inside ADDR5 field). - */ - marvell_nand->sels[i].cs = cs; - switch (cs) { - case 0: - case 2: - marvell_nand->sels[i].ndcb0_csel = 0; - break; - case 1: - case 3: - marvell_nand->sels[i].ndcb0_csel = NDCB0_CSEL; - break; - default: - return -EINVAL; - } - - /* Retrieve RB id */ - if (pdata || nfc->caps->legacy_of_bindings) { - /* Legacy bindings always use RB #0 */ - rb = 0; - } else { - ret = of_property_read_u32_index(np, "nand-rb", i, - &rb); - if (ret) { - dev_err(dev, - "could not retrieve RB property: %d\n", - ret); - return ret; - } - } - - if (rb >= nfc->caps->max_rb_nb) { - dev_err(dev, "invalid reg value: %u (max RB = %d)\n", - rb, nfc->caps->max_rb_nb); - return -EINVAL; - } - - marvell_nand->sels[i].rb = rb; - } - - chip = &marvell_nand->chip; - chip->controller = &nfc->controller; - nand_set_flash_node(chip, np); - - chip->exec_op = marvell_nfc_exec_op; - chip->select_chip = marvell_nfc_select_chip; - if (!of_property_read_bool(np, "marvell,nand-keep-config")) - chip->setup_data_interface = marvell_nfc_setup_data_interface; - - mtd = nand_to_mtd(chip); - mtd->dev.parent = dev; - - /* - * Default to HW ECC engine mode. If the nand-ecc-mode property is given - * in the DT node, this entry will be overwritten in nand_scan_ident(). - */ - chip->ecc.mode = NAND_ECC_HW; - - /* - * Save a reference value for timing registers before - * ->setup_data_interface() is called. - */ - marvell_nand->ndtr0 = readl_relaxed(nfc->regs + NDTR0); - marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1); - - chip->options |= NAND_BUSWIDTH_AUTO; - ret = nand_scan_ident(mtd, marvell_nand->nsels, NULL); - if (ret) { - dev_err(dev, "could not identify the nand chip\n"); - return ret; - } - - if (pdata && pdata->flash_bbt) - chip->bbt_options |= NAND_BBT_USE_FLASH; - - if (chip->bbt_options & NAND_BBT_USE_FLASH) { - /* - * We'll use a bad block table stored in-flash and don't - * allow writing the bad block marker to the flash. - */ - chip->bbt_options |= NAND_BBT_NO_OOB_BBM; - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; - } - - /* Save the chip-specific fields of NDCR */ - marvell_nand->ndcr = NDCR_PAGE_SZ(mtd->writesize); - if (chip->options & NAND_BUSWIDTH_16) - marvell_nand->ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C; - - /* - * On small page NANDs, only one cycle is needed to pass the - * column address. - */ - if (mtd->writesize <= 512) { - marvell_nand->addr_cyc = 1; - } else { - marvell_nand->addr_cyc = 2; - marvell_nand->ndcr |= NDCR_RA_START; - } - - /* - * Now add the number of cycles needed to pass the row - * address. - * - * Addressing a chip using CS 2 or 3 should also need the third row - * cycle but due to inconsistance in the documentation and lack of - * hardware to test this situation, this case is not supported. - */ - if (chip->options & NAND_ROW_ADDR_3) - marvell_nand->addr_cyc += 3; - else - marvell_nand->addr_cyc += 2; - - if (pdata) { - chip->ecc.size = pdata->ecc_step_size; - chip->ecc.strength = pdata->ecc_strength; - } - - ret = marvell_nand_ecc_init(mtd, &chip->ecc); - if (ret) { - dev_err(dev, "ECC init failed: %d\n", ret); - return ret; - } - - if (chip->ecc.mode == NAND_ECC_HW) { - /* - * Subpage write not available with hardware ECC, prohibit also - * subpage read as in userspace subpage access would still be - * allowed and subpage write, if used, would lead to numerous - * uncorrectable ECC errors. - */ - chip->options |= NAND_NO_SUBPAGE_WRITE; - } - - if (pdata || nfc->caps->legacy_of_bindings) { - /* - * We keep the MTD name unchanged to avoid breaking platforms - * where the MTD cmdline parser is used and the bootloader - * has not been updated to use the new naming scheme. - */ - mtd->name = "pxa3xx_nand-0"; - } else if (!mtd->name) { - /* - * If the new bindings are used and the bootloader has not been - * updated to pass a new mtdparts parameter on the cmdline, you - * should define the following property in your NAND node, ie: - * - * label = "main-storage"; - * - * This way, mtd->name will be set by the core when - * nand_set_flash_node() is called. - */ - mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL, - "%s:nand.%d", dev_name(nfc->dev), - marvell_nand->sels[0].cs); - if (!mtd->name) { - dev_err(nfc->dev, "Failed to allocate mtd->name\n"); - return -ENOMEM; - } - } - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(dev, "nand_scan_tail failed: %d\n", ret); - return ret; - } - - if (pdata) - /* Legacy bindings support only one chip */ - ret = mtd_device_register(mtd, pdata->parts[0], - pdata->nr_parts[0]); - else - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - dev_err(dev, "failed to register mtd device: %d\n", ret); - nand_release(mtd); - return ret; - } - - list_add_tail(&marvell_nand->node, &nfc->chips); - - return 0; -} - -static int marvell_nand_chips_init(struct device *dev, struct marvell_nfc *nfc) -{ - struct device_node *np = dev->of_node; - struct device_node *nand_np; - int max_cs = nfc->caps->max_cs_nb; - int nchips; - int ret; - - if (!np) - nchips = 1; - else - nchips = of_get_child_count(np); - - if (nchips > max_cs) { - dev_err(dev, "too many NAND chips: %d (max = %d CS)\n", nchips, - max_cs); - return -EINVAL; - } - - /* - * Legacy bindings do not use child nodes to exhibit NAND chip - * properties and layout. Instead, NAND properties are mixed with the - * controller ones, and partitions are defined as direct subnodes of the - * NAND controller node. - */ - if (nfc->caps->legacy_of_bindings) { - ret = marvell_nand_chip_init(dev, nfc, np); - return ret; - } - - for_each_child_of_node(np, nand_np) { - ret = marvell_nand_chip_init(dev, nfc, nand_np); - if (ret) { - of_node_put(nand_np); - return ret; - } - } - - return 0; -} - -static void marvell_nand_chips_cleanup(struct marvell_nfc *nfc) -{ - struct marvell_nand_chip *entry, *temp; - - list_for_each_entry_safe(entry, temp, &nfc->chips, node) { - nand_release(nand_to_mtd(&entry->chip)); - list_del(&entry->node); - } -} - -static int marvell_nfc_init_dma(struct marvell_nfc *nfc) -{ - struct platform_device *pdev = container_of(nfc->dev, - struct platform_device, - dev); - struct dma_slave_config config = {}; - struct resource *r; - dma_cap_mask_t mask; - struct pxad_param param; - int ret; - - if (!IS_ENABLED(CONFIG_PXA_DMA)) { - dev_warn(nfc->dev, - "DMA not enabled in configuration\n"); - return -ENOTSUPP; - } - - ret = dma_set_mask_and_coherent(nfc->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!r) { - dev_err(nfc->dev, "No resource defined for data DMA\n"); - return -ENXIO; - } - - param.drcmr = r->start; - param.prio = PXAD_PRIO_LOWEST; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - nfc->dma_chan = - dma_request_slave_channel_compat(mask, pxad_filter_fn, - ¶m, nfc->dev, - "data"); - if (!nfc->dma_chan) { - dev_err(nfc->dev, - "Unable to request data DMA channel\n"); - return -ENODEV; - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - return -ENXIO; - - config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.src_addr = r->start + NDDB; - config.dst_addr = r->start + NDDB; - config.src_maxburst = 32; - config.dst_maxburst = 32; - ret = dmaengine_slave_config(nfc->dma_chan, &config); - if (ret < 0) { - dev_err(nfc->dev, "Failed to configure DMA channel\n"); - return ret; - } - - /* - * DMA must act on length multiple of 32 and this length may be - * bigger than the destination buffer. Use this buffer instead - * for DMA transfers and then copy the desired amount of data to - * the provided buffer. - */ - nfc->dma_buf = kmalloc(MAX_CHUNK_SIZE, GFP_KERNEL | GFP_DMA); - if (!nfc->dma_buf) - return -ENOMEM; - - nfc->use_dma = true; - - return 0; -} - -static int marvell_nfc_init(struct marvell_nfc *nfc) -{ - struct device_node *np = nfc->dev->of_node; - - /* - * Some SoCs like A7k/A8k need to enable manually the NAND - * controller, gated clocks and reset bits to avoid being bootloader - * dependent. This is done through the use of the System Functions - * registers. - */ - if (nfc->caps->need_system_controller) { - struct regmap *sysctrl_base = - syscon_regmap_lookup_by_phandle(np, - "marvell,system-controller"); - u32 reg; - - if (IS_ERR(sysctrl_base)) - return PTR_ERR(sysctrl_base); - - reg = GENCONF_SOC_DEVICE_MUX_NFC_EN | - GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST | - GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST | - GENCONF_SOC_DEVICE_MUX_NFC_INT_EN; - regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg); - - regmap_read(sysctrl_base, GENCONF_CLK_GATING_CTRL, ®); - reg |= GENCONF_CLK_GATING_CTRL_ND_GATE; - regmap_write(sysctrl_base, GENCONF_CLK_GATING_CTRL, reg); - - regmap_read(sysctrl_base, GENCONF_ND_CLK_CTRL, ®); - reg |= GENCONF_ND_CLK_CTRL_EN; - regmap_write(sysctrl_base, GENCONF_ND_CLK_CTRL, reg); - } - - /* Configure the DMA if appropriate */ - if (!nfc->caps->is_nfcv2) - marvell_nfc_init_dma(nfc); - - /* - * ECC operations and interruptions are only enabled when specifically - * needed. ECC shall not be activated in the early stages (fails probe). - * Arbiter flag, even if marked as "reserved", must be set (empirical). - * SPARE_EN bit must always be set or ECC bytes will not be at the same - * offset in the read page and this will fail the protection. - */ - writel_relaxed(NDCR_ALL_INT | NDCR_ND_ARB_EN | NDCR_SPARE_EN | - NDCR_RD_ID_CNT(NFCV1_READID_LEN), nfc->regs + NDCR); - writel_relaxed(0xFFFFFFFF, nfc->regs + NDSR); - writel_relaxed(0, nfc->regs + NDECCCTRL); - - return 0; -} - -static int marvell_nfc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *r; - struct marvell_nfc *nfc; - int ret; - int irq; - - nfc = devm_kzalloc(&pdev->dev, sizeof(struct marvell_nfc), - GFP_KERNEL); - if (!nfc) - return -ENOMEM; - - nfc->dev = dev; - nand_hw_control_init(&nfc->controller); - INIT_LIST_HEAD(&nfc->chips); - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nfc->regs = devm_ioremap_resource(dev, r); - if (IS_ERR(nfc->regs)) - return PTR_ERR(nfc->regs); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to retrieve irq\n"); - return irq; - } - - nfc->ecc_clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(nfc->ecc_clk)) - return PTR_ERR(nfc->ecc_clk); - - ret = clk_prepare_enable(nfc->ecc_clk); - if (ret) - return ret; - - marvell_nfc_disable_int(nfc, NDCR_ALL_INT); - marvell_nfc_clear_int(nfc, NDCR_ALL_INT); - ret = devm_request_irq(dev, irq, marvell_nfc_isr, - 0, "marvell-nfc", nfc); - if (ret) - goto unprepare_clk; - - /* Get NAND controller capabilities */ - if (pdev->id_entry) - nfc->caps = (void *)pdev->id_entry->driver_data; - else - nfc->caps = of_device_get_match_data(&pdev->dev); - - if (!nfc->caps) { - dev_err(dev, "Could not retrieve NFC caps\n"); - ret = -EINVAL; - goto unprepare_clk; - } - - /* Init the controller and then probe the chips */ - ret = marvell_nfc_init(nfc); - if (ret) - goto unprepare_clk; - - platform_set_drvdata(pdev, nfc); - - ret = marvell_nand_chips_init(dev, nfc); - if (ret) - goto unprepare_clk; - - return 0; - -unprepare_clk: - clk_disable_unprepare(nfc->ecc_clk); - - return ret; -} - -static int marvell_nfc_remove(struct platform_device *pdev) -{ - struct marvell_nfc *nfc = platform_get_drvdata(pdev); - - marvell_nand_chips_cleanup(nfc); - - if (nfc->use_dma) { - dmaengine_terminate_all(nfc->dma_chan); - dma_release_channel(nfc->dma_chan); - } - - clk_disable_unprepare(nfc->ecc_clk); - - return 0; -} - -static const struct marvell_nfc_caps marvell_armada_8k_nfc_caps = { - .max_cs_nb = 4, - .max_rb_nb = 2, - .need_system_controller = true, - .is_nfcv2 = true, -}; - -static const struct marvell_nfc_caps marvell_armada370_nfc_caps = { - .max_cs_nb = 4, - .max_rb_nb = 2, - .is_nfcv2 = true, -}; - -static const struct marvell_nfc_caps marvell_pxa3xx_nfc_caps = { - .max_cs_nb = 2, - .max_rb_nb = 1, - .use_dma = true, -}; - -static const struct marvell_nfc_caps marvell_armada_8k_nfc_legacy_caps = { - .max_cs_nb = 4, - .max_rb_nb = 2, - .need_system_controller = true, - .legacy_of_bindings = true, - .is_nfcv2 = true, -}; - -static const struct marvell_nfc_caps marvell_armada370_nfc_legacy_caps = { - .max_cs_nb = 4, - .max_rb_nb = 2, - .legacy_of_bindings = true, - .is_nfcv2 = true, -}; - -static const struct marvell_nfc_caps marvell_pxa3xx_nfc_legacy_caps = { - .max_cs_nb = 2, - .max_rb_nb = 1, - .legacy_of_bindings = true, - .use_dma = true, -}; - -static const struct platform_device_id marvell_nfc_platform_ids[] = { - { - .name = "pxa3xx-nand", - .driver_data = (kernel_ulong_t)&marvell_pxa3xx_nfc_legacy_caps, - }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(platform, marvell_nfc_platform_ids); - -static const struct of_device_id marvell_nfc_of_ids[] = { - { - .compatible = "marvell,armada-8k-nand-controller", - .data = &marvell_armada_8k_nfc_caps, - }, - { - .compatible = "marvell,armada370-nand-controller", - .data = &marvell_armada370_nfc_caps, - }, - { - .compatible = "marvell,pxa3xx-nand-controller", - .data = &marvell_pxa3xx_nfc_caps, - }, - /* Support for old/deprecated bindings: */ - { - .compatible = "marvell,armada-8k-nand", - .data = &marvell_armada_8k_nfc_legacy_caps, - }, - { - .compatible = "marvell,armada370-nand", - .data = &marvell_armada370_nfc_legacy_caps, - }, - { - .compatible = "marvell,pxa3xx-nand", - .data = &marvell_pxa3xx_nfc_legacy_caps, - }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, marvell_nfc_of_ids); - -static struct platform_driver marvell_nfc_driver = { - .driver = { - .name = "marvell-nfc", - .of_match_table = marvell_nfc_of_ids, - }, - .id_table = marvell_nfc_platform_ids, - .probe = marvell_nfc_probe, - .remove = marvell_nfc_remove, -}; -module_platform_driver(marvell_nfc_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Marvell NAND controller driver"); diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c deleted file mode 100644 index 913b9d1225c6..000000000000 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ /dev/null @@ -1,856 +0,0 @@ -/* - * Copyright 2004-2008 Freescale Semiconductor, Inc. - * Copyright 2009 Semihalf. - * - * Approved as OSADL project by a majority of OSADL members and funded - * by OSADL membership fees in 2009; for details see www.osadl.org. - * - * Based on original driver from Freescale Semiconductor - * written by John Rigby on basis of mxc_nand.c. - * Reworked and extended by Piotr Ziecik . - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Addresses for NFC MAIN RAM BUFFER areas */ -#define NFC_MAIN_AREA(n) ((n) * 0x200) - -/* Addresses for NFC SPARE BUFFER areas */ -#define NFC_SPARE_BUFFERS 8 -#define NFC_SPARE_LEN 0x40 -#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN)) - -/* MPC5121 NFC registers */ -#define NFC_BUF_ADDR 0x1E04 -#define NFC_FLASH_ADDR 0x1E06 -#define NFC_FLASH_CMD 0x1E08 -#define NFC_CONFIG 0x1E0A -#define NFC_ECC_STATUS1 0x1E0C -#define NFC_ECC_STATUS2 0x1E0E -#define NFC_SPAS 0x1E10 -#define NFC_WRPROT 0x1E12 -#define NFC_NF_WRPRST 0x1E18 -#define NFC_CONFIG1 0x1E1A -#define NFC_CONFIG2 0x1E1C -#define NFC_UNLOCKSTART_BLK0 0x1E20 -#define NFC_UNLOCKEND_BLK0 0x1E22 -#define NFC_UNLOCKSTART_BLK1 0x1E24 -#define NFC_UNLOCKEND_BLK1 0x1E26 -#define NFC_UNLOCKSTART_BLK2 0x1E28 -#define NFC_UNLOCKEND_BLK2 0x1E2A -#define NFC_UNLOCKSTART_BLK3 0x1E2C -#define NFC_UNLOCKEND_BLK3 0x1E2E - -/* Bit Definitions: NFC_BUF_ADDR */ -#define NFC_RBA_MASK (7 << 0) -#define NFC_ACTIVE_CS_SHIFT 5 -#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT) - -/* Bit Definitions: NFC_CONFIG */ -#define NFC_BLS_UNLOCKED (1 << 1) - -/* Bit Definitions: NFC_CONFIG1 */ -#define NFC_ECC_4BIT (1 << 0) -#define NFC_FULL_PAGE_DMA (1 << 1) -#define NFC_SPARE_ONLY (1 << 2) -#define NFC_ECC_ENABLE (1 << 3) -#define NFC_INT_MASK (1 << 4) -#define NFC_BIG_ENDIAN (1 << 5) -#define NFC_RESET (1 << 6) -#define NFC_CE (1 << 7) -#define NFC_ONE_CYCLE (1 << 8) -#define NFC_PPB_32 (0 << 9) -#define NFC_PPB_64 (1 << 9) -#define NFC_PPB_128 (2 << 9) -#define NFC_PPB_256 (3 << 9) -#define NFC_PPB_MASK (3 << 9) -#define NFC_FULL_PAGE_INT (1 << 11) - -/* Bit Definitions: NFC_CONFIG2 */ -#define NFC_COMMAND (1 << 0) -#define NFC_ADDRESS (1 << 1) -#define NFC_INPUT (1 << 2) -#define NFC_OUTPUT (1 << 3) -#define NFC_ID (1 << 4) -#define NFC_STATUS (1 << 5) -#define NFC_CMD_FAIL (1 << 15) -#define NFC_INT (1 << 15) - -/* Bit Definitions: NFC_WRPROT */ -#define NFC_WPC_LOCK_TIGHT (1 << 0) -#define NFC_WPC_LOCK (1 << 1) -#define NFC_WPC_UNLOCK (1 << 2) - -#define DRV_NAME "mpc5121_nfc" - -/* Timeouts */ -#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ -#define NFC_TIMEOUT (HZ / 10) /* 1/10 s */ - -struct mpc5121_nfc_prv { - struct nand_chip chip; - int irq; - void __iomem *regs; - struct clk *clk; - wait_queue_head_t irq_waitq; - uint column; - int spareonly; - void __iomem *csreg; - struct device *dev; -}; - -static void mpc5121_nfc_done(struct mtd_info *mtd); - -/* Read NFC register */ -static inline u16 nfc_read(struct mtd_info *mtd, uint reg) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - - return in_be16(prv->regs + reg); -} - -/* Write NFC register */ -static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - - out_be16(prv->regs + reg, val); -} - -/* Set bits in NFC register */ -static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits) -{ - nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); -} - -/* Clear bits in NFC register */ -static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits) -{ - nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); -} - -/* Invoke address cycle */ -static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr) -{ - nfc_write(mtd, NFC_FLASH_ADDR, addr); - nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS); - mpc5121_nfc_done(mtd); -} - -/* Invoke command cycle */ -static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd) -{ - nfc_write(mtd, NFC_FLASH_CMD, cmd); - nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND); - mpc5121_nfc_done(mtd); -} - -/* Send data from NFC buffers to NAND flash */ -static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd) -{ - nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); - nfc_write(mtd, NFC_CONFIG2, NFC_INPUT); - mpc5121_nfc_done(mtd); -} - -/* Receive data from NAND flash */ -static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd) -{ - nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); - nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT); - mpc5121_nfc_done(mtd); -} - -/* Receive ID from NAND flash */ -static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd) -{ - nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); - nfc_write(mtd, NFC_CONFIG2, NFC_ID); - mpc5121_nfc_done(mtd); -} - -/* Receive status from NAND flash */ -static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd) -{ - nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); - nfc_write(mtd, NFC_CONFIG2, NFC_STATUS); - mpc5121_nfc_done(mtd); -} - -/* NFC interrupt handler */ -static irqreturn_t mpc5121_nfc_irq(int irq, void *data) -{ - struct mtd_info *mtd = data; - struct nand_chip *chip = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - - nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK); - wake_up(&prv->irq_waitq); - - return IRQ_HANDLED; -} - -/* Wait for operation complete */ -static void mpc5121_nfc_done(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - int rv; - - if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) { - nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK); - rv = wait_event_timeout(prv->irq_waitq, - (nfc_read(mtd, NFC_CONFIG2) & NFC_INT), NFC_TIMEOUT); - - if (!rv) - dev_warn(prv->dev, - "Timeout while waiting for interrupt.\n"); - } - - nfc_clear(mtd, NFC_CONFIG2, NFC_INT); -} - -/* Do address cycle(s) */ -static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u32 pagemask = chip->pagemask; - - if (column != -1) { - mpc5121_nfc_send_addr(mtd, column); - if (mtd->writesize > 512) - mpc5121_nfc_send_addr(mtd, column >> 8); - } - - if (page != -1) { - do { - mpc5121_nfc_send_addr(mtd, page & 0xFF); - page >>= 8; - pagemask >>= 8; - } while (pagemask); - } -} - -/* Control chip select signals */ -static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) -{ - if (chip < 0) { - nfc_clear(mtd, NFC_CONFIG1, NFC_CE); - return; - } - - nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK); - nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) & - NFC_ACTIVE_CS_MASK); - nfc_set(mtd, NFC_CONFIG1, NFC_CE); -} - -/* Init external chip select logic on ADS5121 board */ -static int ads5121_chipselect_init(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - struct device_node *dn; - - dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld"); - if (dn) { - prv->csreg = of_iomap(dn, 0); - of_node_put(dn); - if (!prv->csreg) - return -ENOMEM; - - /* CPLD Register 9 controls NAND /CE Lines */ - prv->csreg += 9; - return 0; - } - - return -EINVAL; -} - -/* Control chips select signal on ADS5121 board */ -static void ads5121_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand); - u8 v; - - v = in_8(prv->csreg); - v |= 0x0F; - - if (chip >= 0) { - mpc5121_nfc_select_chip(mtd, 0); - v &= ~(1 << chip); - } else - mpc5121_nfc_select_chip(mtd, -1); - - out_8(prv->csreg, v); -} - -/* Read NAND Ready/Busy signal */ -static int mpc5121_nfc_dev_ready(struct mtd_info *mtd) -{ - /* - * NFC handles ready/busy signal internally. Therefore, this function - * always returns status as ready. - */ - return 1; -} - -/* Write command to NAND flash */ -static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command, - int column, int page) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - - prv->column = (column >= 0) ? column : 0; - prv->spareonly = 0; - - switch (command) { - case NAND_CMD_PAGEPROG: - mpc5121_nfc_send_prog_page(mtd); - break; - /* - * NFC does not support sub-page reads and writes, - * so emulate them using full page transfers. - */ - case NAND_CMD_READ0: - column = 0; - break; - - case NAND_CMD_READ1: - prv->column += 256; - command = NAND_CMD_READ0; - column = 0; - break; - - case NAND_CMD_READOOB: - prv->spareonly = 1; - command = NAND_CMD_READ0; - column = 0; - break; - - case NAND_CMD_SEQIN: - mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page); - column = 0; - break; - - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_READID: - case NAND_CMD_STATUS: - break; - - default: - return; - } - - mpc5121_nfc_send_cmd(mtd, command); - mpc5121_nfc_addr_cycle(mtd, column, page); - - switch (command) { - case NAND_CMD_READ0: - if (mtd->writesize > 512) - mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART); - mpc5121_nfc_send_read_page(mtd); - break; - - case NAND_CMD_READID: - mpc5121_nfc_send_read_id(mtd); - break; - - case NAND_CMD_STATUS: - mpc5121_nfc_send_read_status(mtd); - if (chip->options & NAND_BUSWIDTH_16) - prv->column = 1; - else - prv->column = 0; - break; - } -} - -/* Copy data from/to NFC spare buffers. */ -static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset, - u8 *buffer, uint size, int wr) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand); - uint o, s, sbsize, blksize; - - /* - * NAND spare area is available through NFC spare buffers. - * The NFC divides spare area into (page_size / 512) chunks. - * Each chunk is placed into separate spare memory area, using - * first (spare_size / num_of_chunks) bytes of the buffer. - * - * For NAND device in which the spare area is not divided fully - * by the number of chunks, number of used bytes in each spare - * buffer is rounded down to the nearest even number of bytes, - * and all remaining bytes are added to the last used spare area. - * - * For more information read section 26.6.10 of MPC5121e - * Microcontroller Reference Manual, Rev. 3. - */ - - /* Calculate number of valid bytes in each spare buffer */ - sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1; - - while (size) { - /* Calculate spare buffer number */ - s = offset / sbsize; - if (s > NFC_SPARE_BUFFERS - 1) - s = NFC_SPARE_BUFFERS - 1; - - /* - * Calculate offset to requested data block in selected spare - * buffer and its size. - */ - o = offset - (s * sbsize); - blksize = min(sbsize - o, size); - - if (wr) - memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, - buffer, blksize); - else - memcpy_fromio(buffer, - prv->regs + NFC_SPARE_AREA(s) + o, blksize); - - buffer += blksize; - offset += blksize; - size -= blksize; - }; -} - -/* Copy data from/to NFC main and spare buffers */ -static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len, - int wr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - uint c = prv->column; - uint l; - - /* Handle spare area access */ - if (prv->spareonly || c >= mtd->writesize) { - /* Calculate offset from beginning of spare area */ - if (c >= mtd->writesize) - c -= mtd->writesize; - - prv->column += len; - mpc5121_nfc_copy_spare(mtd, c, buf, len, wr); - return; - } - - /* - * Handle main area access - limit copy length to prevent - * crossing main/spare boundary. - */ - l = min((uint)len, mtd->writesize - c); - prv->column += l; - - if (wr) - memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); - else - memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); - - /* Handle crossing main/spare boundary */ - if (l != len) { - buf += l; - len -= l; - mpc5121_nfc_buf_copy(mtd, buf, len, wr); - } -} - -/* Read data from NFC buffers */ -static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - mpc5121_nfc_buf_copy(mtd, buf, len, 0); -} - -/* Write data to NFC buffers */ -static void mpc5121_nfc_write_buf(struct mtd_info *mtd, - const u_char *buf, int len) -{ - mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1); -} - -/* Read byte from NFC buffers */ -static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) -{ - u8 tmp; - - mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp)); - - return tmp; -} - -/* Read word from NFC buffers */ -static u16 mpc5121_nfc_read_word(struct mtd_info *mtd) -{ - u16 tmp; - - mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); - - return tmp; -} - -/* - * Read NFC configuration from Reset Config Word - * - * NFC is configured during reset in basis of information stored - * in Reset Config Word. There is no other way to set NAND block - * size, spare size and bus width. - */ -static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - struct mpc512x_reset_module *rm; - struct device_node *rmnode; - uint rcw_pagesize = 0; - uint rcw_sparesize = 0; - uint rcw_width; - uint rcwh; - uint romloc, ps; - int ret = 0; - - rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset"); - if (!rmnode) { - dev_err(prv->dev, "Missing 'fsl,mpc5121-reset' " - "node in device tree!\n"); - return -ENODEV; - } - - rm = of_iomap(rmnode, 0); - if (!rm) { - dev_err(prv->dev, "Error mapping reset module node!\n"); - ret = -EBUSY; - goto out; - } - - rcwh = in_be32(&rm->rcwhr); - - /* Bit 6: NFC bus width */ - rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1; - - /* Bit 7: NFC Page/Spare size */ - ps = (rcwh >> 7) & 0x1; - - /* Bits [22:21]: ROM Location */ - romloc = (rcwh >> 21) & 0x3; - - /* Decode RCW bits */ - switch ((ps << 2) | romloc) { - case 0x00: - case 0x01: - rcw_pagesize = 512; - rcw_sparesize = 16; - break; - case 0x02: - case 0x03: - rcw_pagesize = 4096; - rcw_sparesize = 128; - break; - case 0x04: - case 0x05: - rcw_pagesize = 2048; - rcw_sparesize = 64; - break; - case 0x06: - case 0x07: - rcw_pagesize = 4096; - rcw_sparesize = 218; - break; - } - - mtd->writesize = rcw_pagesize; - mtd->oobsize = rcw_sparesize; - if (rcw_width == 2) - chip->options |= NAND_BUSWIDTH_16; - - dev_notice(prv->dev, "Configured for " - "%u-bit NAND, page size %u " - "with %u spare.\n", - rcw_width * 8, rcw_pagesize, - rcw_sparesize); - iounmap(rm); -out: - of_node_put(rmnode); - return ret; -} - -/* Free driver resources */ -static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - - if (prv->clk) - clk_disable_unprepare(prv->clk); - - if (prv->csreg) - iounmap(prv->csreg); -} - -static int mpc5121_nfc_probe(struct platform_device *op) -{ - struct device_node *dn = op->dev.of_node; - struct clk *clk; - struct device *dev = &op->dev; - struct mpc5121_nfc_prv *prv; - struct resource res; - struct mtd_info *mtd; - struct nand_chip *chip; - unsigned long regs_paddr, regs_size; - const __be32 *chips_no; - int resettime = 0; - int retval = 0; - int rev, len; - - /* - * Check SoC revision. This driver supports only NFC - * in MPC5121 revision 2 and MPC5123 revision 3. - */ - rev = (mfspr(SPRN_SVR) >> 4) & 0xF; - if ((rev != 2) && (rev != 3)) { - dev_err(dev, "SoC revision %u is not supported!\n", rev); - return -ENXIO; - } - - prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL); - if (!prv) - return -ENOMEM; - - chip = &prv->chip; - mtd = nand_to_mtd(chip); - - mtd->dev.parent = dev; - nand_set_controller_data(chip, prv); - nand_set_flash_node(chip, dn); - prv->dev = dev; - - /* Read NFC configuration from Reset Config Word */ - retval = mpc5121_nfc_read_hw_config(mtd); - if (retval) { - dev_err(dev, "Unable to read NFC config!\n"); - return retval; - } - - prv->irq = irq_of_parse_and_map(dn, 0); - if (prv->irq == NO_IRQ) { - dev_err(dev, "Error mapping IRQ!\n"); - return -EINVAL; - } - - retval = of_address_to_resource(dn, 0, &res); - if (retval) { - dev_err(dev, "Error parsing memory region!\n"); - return retval; - } - - chips_no = of_get_property(dn, "chips", &len); - if (!chips_no || len != sizeof(*chips_no)) { - dev_err(dev, "Invalid/missing 'chips' property!\n"); - return -EINVAL; - } - - regs_paddr = res.start; - regs_size = resource_size(&res); - - if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) { - dev_err(dev, "Error requesting memory region!\n"); - return -EBUSY; - } - - prv->regs = devm_ioremap(dev, regs_paddr, regs_size); - if (!prv->regs) { - dev_err(dev, "Error mapping memory region!\n"); - return -ENOMEM; - } - - mtd->name = "MPC5121 NAND"; - chip->dev_ready = mpc5121_nfc_dev_ready; - chip->cmdfunc = mpc5121_nfc_command; - chip->read_byte = mpc5121_nfc_read_byte; - chip->read_word = mpc5121_nfc_read_word; - chip->read_buf = mpc5121_nfc_read_buf; - chip->write_buf = mpc5121_nfc_write_buf; - chip->select_chip = mpc5121_nfc_select_chip; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; - chip->bbt_options = NAND_BBT_USE_FLASH; - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - - /* Support external chip-select logic on ADS5121 board */ - if (of_machine_is_compatible("fsl,mpc5121ads")) { - retval = ads5121_chipselect_init(mtd); - if (retval) { - dev_err(dev, "Chipselect init error!\n"); - return retval; - } - - chip->select_chip = ads5121_select_chip; - } - - /* Enable NFC clock */ - clk = devm_clk_get(dev, "ipg"); - if (IS_ERR(clk)) { - dev_err(dev, "Unable to acquire NFC clock!\n"); - retval = PTR_ERR(clk); - goto error; - } - retval = clk_prepare_enable(clk); - if (retval) { - dev_err(dev, "Unable to enable NFC clock!\n"); - goto error; - } - prv->clk = clk; - - /* Reset NAND Flash controller */ - nfc_set(mtd, NFC_CONFIG1, NFC_RESET); - while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) { - if (resettime++ >= NFC_RESET_TIMEOUT) { - dev_err(dev, "Timeout while resetting NFC!\n"); - retval = -EINVAL; - goto error; - } - - udelay(1); - } - - /* Enable write to NFC memory */ - nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED); - - /* Enable write to all NAND pages */ - nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000); - nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF); - nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK); - - /* - * Setup NFC: - * - Big Endian transfers, - * - Interrupt after full page read/write. - */ - nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK | - NFC_FULL_PAGE_INT); - - /* Set spare area size */ - nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1); - - init_waitqueue_head(&prv->irq_waitq); - retval = devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq, 0, DRV_NAME, - mtd); - if (retval) { - dev_err(dev, "Error requesting IRQ!\n"); - goto error; - } - - /* Detect NAND chips */ - retval = nand_scan(mtd, be32_to_cpup(chips_no)); - if (retval) { - dev_err(dev, "NAND Flash not found !\n"); - goto error; - } - - /* Set erase block size */ - switch (mtd->erasesize / mtd->writesize) { - case 32: - nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32); - break; - - case 64: - nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64); - break; - - case 128: - nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128); - break; - - case 256: - nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256); - break; - - default: - dev_err(dev, "Unsupported NAND flash!\n"); - retval = -ENXIO; - goto error; - } - - dev_set_drvdata(dev, mtd); - - /* Register device in MTD */ - retval = mtd_device_register(mtd, NULL, 0); - if (retval) { - dev_err(dev, "Error adding MTD device!\n"); - goto error; - } - - return 0; -error: - mpc5121_nfc_free(dev, mtd); - return retval; -} - -static int mpc5121_nfc_remove(struct platform_device *op) -{ - struct device *dev = &op->dev; - struct mtd_info *mtd = dev_get_drvdata(dev); - - nand_release(mtd); - mpc5121_nfc_free(dev, mtd); - - return 0; -} - -static const struct of_device_id mpc5121_nfc_match[] = { - { .compatible = "fsl,mpc5121-nfc", }, - {}, -}; -MODULE_DEVICE_TABLE(of, mpc5121_nfc_match); - -static struct platform_driver mpc5121_nfc_driver = { - .probe = mpc5121_nfc_probe, - .remove = mpc5121_nfc_remove, - .driver = { - .name = DRV_NAME, - .of_match_table = mpc5121_nfc_match, - }, -}; - -module_platform_driver(mpc5121_nfc_driver); - -MODULE_AUTHOR("Freescale Semiconductor, Inc."); -MODULE_DESCRIPTION("MPC5121 NAND MTD driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/mtk_ecc.c deleted file mode 100644 index 40d86a861a70..000000000000 --- a/drivers/mtd/nand/mtk_ecc.c +++ /dev/null @@ -1,608 +0,0 @@ -/* - * MTK ECC controller driver. - * Copyright (C) 2016 MediaTek Inc. - * Authors: Xiaolei Li - * Jorge Ramirez-Ortiz - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mtk_ecc.h" - -#define ECC_IDLE_MASK BIT(0) -#define ECC_IRQ_EN BIT(0) -#define ECC_PG_IRQ_SEL BIT(1) -#define ECC_OP_ENABLE (1) -#define ECC_OP_DISABLE (0) - -#define ECC_ENCCON (0x00) -#define ECC_ENCCNFG (0x04) -#define ECC_MS_SHIFT (16) -#define ECC_ENCDIADDR (0x08) -#define ECC_ENCIDLE (0x0C) -#define ECC_DECCON (0x100) -#define ECC_DECCNFG (0x104) -#define DEC_EMPTY_EN BIT(31) -#define DEC_CNFG_CORRECT (0x3 << 12) -#define ECC_DECIDLE (0x10C) -#define ECC_DECENUM0 (0x114) - -#define ECC_TIMEOUT (500000) - -#define ECC_IDLE_REG(op) ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE) -#define ECC_CTL_REG(op) ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON) - -struct mtk_ecc_caps { - u32 err_mask; - const u8 *ecc_strength; - const u32 *ecc_regs; - u8 num_ecc_strength; - u8 ecc_mode_shift; - u32 parity_bits; - int pg_irq_sel; -}; - -struct mtk_ecc { - struct device *dev; - const struct mtk_ecc_caps *caps; - void __iomem *regs; - struct clk *clk; - - struct completion done; - struct mutex lock; - u32 sectors; - - u8 *eccdata; -}; - -/* ecc strength that each IP supports */ -static const u8 ecc_strength_mt2701[] = { - 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, - 40, 44, 48, 52, 56, 60 -}; - -static const u8 ecc_strength_mt2712[] = { - 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, - 40, 44, 48, 52, 56, 60, 68, 72, 80 -}; - -static const u8 ecc_strength_mt7622[] = { - 4, 6, 8, 10, 12, 14, 16 -}; - -enum mtk_ecc_regs { - ECC_ENCPAR00, - ECC_ENCIRQ_EN, - ECC_ENCIRQ_STA, - ECC_DECDONE, - ECC_DECIRQ_EN, - ECC_DECIRQ_STA, -}; - -static int mt2701_ecc_regs[] = { - [ECC_ENCPAR00] = 0x10, - [ECC_ENCIRQ_EN] = 0x80, - [ECC_ENCIRQ_STA] = 0x84, - [ECC_DECDONE] = 0x124, - [ECC_DECIRQ_EN] = 0x200, - [ECC_DECIRQ_STA] = 0x204, -}; - -static int mt2712_ecc_regs[] = { - [ECC_ENCPAR00] = 0x300, - [ECC_ENCIRQ_EN] = 0x80, - [ECC_ENCIRQ_STA] = 0x84, - [ECC_DECDONE] = 0x124, - [ECC_DECIRQ_EN] = 0x200, - [ECC_DECIRQ_STA] = 0x204, -}; - -static int mt7622_ecc_regs[] = { - [ECC_ENCPAR00] = 0x10, - [ECC_ENCIRQ_EN] = 0x30, - [ECC_ENCIRQ_STA] = 0x34, - [ECC_DECDONE] = 0x11c, - [ECC_DECIRQ_EN] = 0x140, - [ECC_DECIRQ_STA] = 0x144, -}; - -static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc, - enum mtk_ecc_operation op) -{ - struct device *dev = ecc->dev; - u32 val; - int ret; - - ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val, - val & ECC_IDLE_MASK, - 10, ECC_TIMEOUT); - if (ret) - dev_warn(dev, "%s NOT idle\n", - op == ECC_ENCODE ? "encoder" : "decoder"); -} - -static irqreturn_t mtk_ecc_irq(int irq, void *id) -{ - struct mtk_ecc *ecc = id; - u32 dec, enc; - - dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]) - & ECC_IRQ_EN; - if (dec) { - dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]); - if (dec & ecc->sectors) { - /* - * Clear decode IRQ status once again to ensure that - * there will be no extra IRQ. - */ - readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]); - ecc->sectors = 0; - complete(&ecc->done); - } else { - return IRQ_HANDLED; - } - } else { - enc = readl(ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_STA]) - & ECC_IRQ_EN; - if (enc) - complete(&ecc->done); - else - return IRQ_NONE; - } - - return IRQ_HANDLED; -} - -static int mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config) -{ - u32 ecc_bit, dec_sz, enc_sz; - u32 reg, i; - - for (i = 0; i < ecc->caps->num_ecc_strength; i++) { - if (ecc->caps->ecc_strength[i] == config->strength) - break; - } - - if (i == ecc->caps->num_ecc_strength) { - dev_err(ecc->dev, "invalid ecc strength %d\n", - config->strength); - return -EINVAL; - } - - ecc_bit = i; - - if (config->op == ECC_ENCODE) { - /* configure ECC encoder (in bits) */ - enc_sz = config->len << 3; - - reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift); - reg |= (enc_sz << ECC_MS_SHIFT); - writel(reg, ecc->regs + ECC_ENCCNFG); - - if (config->mode != ECC_NFI_MODE) - writel(lower_32_bits(config->addr), - ecc->regs + ECC_ENCDIADDR); - - } else { - /* configure ECC decoder (in bits) */ - dec_sz = (config->len << 3) + - config->strength * ecc->caps->parity_bits; - - reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift); - reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT; - reg |= DEC_EMPTY_EN; - writel(reg, ecc->regs + ECC_DECCNFG); - - if (config->sectors) - ecc->sectors = 1 << (config->sectors - 1); - } - - return 0; -} - -void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats, - int sectors) -{ - u32 offset, i, err; - u32 bitflips = 0; - - stats->corrected = 0; - stats->failed = 0; - - for (i = 0; i < sectors; i++) { - offset = (i >> 2) << 2; - err = readl(ecc->regs + ECC_DECENUM0 + offset); - err = err >> ((i % 4) * 8); - err &= ecc->caps->err_mask; - if (err == ecc->caps->err_mask) { - /* uncorrectable errors */ - stats->failed++; - continue; - } - - stats->corrected += err; - bitflips = max_t(u32, bitflips, err); - } - - stats->bitflips = bitflips; -} -EXPORT_SYMBOL(mtk_ecc_get_stats); - -void mtk_ecc_release(struct mtk_ecc *ecc) -{ - clk_disable_unprepare(ecc->clk); - put_device(ecc->dev); -} -EXPORT_SYMBOL(mtk_ecc_release); - -static void mtk_ecc_hw_init(struct mtk_ecc *ecc) -{ - mtk_ecc_wait_idle(ecc, ECC_ENCODE); - writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON); - - mtk_ecc_wait_idle(ecc, ECC_DECODE); - writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON); -} - -static struct mtk_ecc *mtk_ecc_get(struct device_node *np) -{ - struct platform_device *pdev; - struct mtk_ecc *ecc; - - pdev = of_find_device_by_node(np); - if (!pdev || !platform_get_drvdata(pdev)) - return ERR_PTR(-EPROBE_DEFER); - - get_device(&pdev->dev); - ecc = platform_get_drvdata(pdev); - clk_prepare_enable(ecc->clk); - mtk_ecc_hw_init(ecc); - - return ecc; -} - -struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node) -{ - struct mtk_ecc *ecc = NULL; - struct device_node *np; - - np = of_parse_phandle(of_node, "ecc-engine", 0); - if (np) { - ecc = mtk_ecc_get(np); - of_node_put(np); - } - - return ecc; -} -EXPORT_SYMBOL(of_mtk_ecc_get); - -int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config) -{ - enum mtk_ecc_operation op = config->op; - u16 reg_val; - int ret; - - ret = mutex_lock_interruptible(&ecc->lock); - if (ret) { - dev_err(ecc->dev, "interrupted when attempting to lock\n"); - return ret; - } - - mtk_ecc_wait_idle(ecc, op); - - ret = mtk_ecc_config(ecc, config); - if (ret) { - mutex_unlock(&ecc->lock); - return ret; - } - - if (config->mode != ECC_NFI_MODE || op != ECC_ENCODE) { - init_completion(&ecc->done); - reg_val = ECC_IRQ_EN; - /* - * For ECC_NFI_MODE, if ecc->caps->pg_irq_sel is 1, then it - * means this chip can only generate one ecc irq during page - * read / write. If is 0, generate one ecc irq each ecc step. - */ - if (ecc->caps->pg_irq_sel && config->mode == ECC_NFI_MODE) - reg_val |= ECC_PG_IRQ_SEL; - if (op == ECC_ENCODE) - writew(reg_val, ecc->regs + - ecc->caps->ecc_regs[ECC_ENCIRQ_EN]); - else - writew(reg_val, ecc->regs + - ecc->caps->ecc_regs[ECC_DECIRQ_EN]); - } - - writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op)); - - return 0; -} -EXPORT_SYMBOL(mtk_ecc_enable); - -void mtk_ecc_disable(struct mtk_ecc *ecc) -{ - enum mtk_ecc_operation op = ECC_ENCODE; - - /* find out the running operation */ - if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE) - op = ECC_DECODE; - - /* disable it */ - mtk_ecc_wait_idle(ecc, op); - if (op == ECC_DECODE) { - /* - * Clear decode IRQ status in case there is a timeout to wait - * decode IRQ. - */ - readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]); - writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_EN]); - } else { - writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_EN]); - } - - writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op)); - - mutex_unlock(&ecc->lock); -} -EXPORT_SYMBOL(mtk_ecc_disable); - -int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op) -{ - int ret; - - ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500)); - if (!ret) { - dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n", - (op == ECC_ENCODE) ? "encoder" : "decoder"); - return -ETIMEDOUT; - } - - return 0; -} -EXPORT_SYMBOL(mtk_ecc_wait_done); - -int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config, - u8 *data, u32 bytes) -{ - dma_addr_t addr; - u32 len; - int ret; - - addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE); - ret = dma_mapping_error(ecc->dev, addr); - if (ret) { - dev_err(ecc->dev, "dma mapping error\n"); - return -EINVAL; - } - - config->op = ECC_ENCODE; - config->addr = addr; - ret = mtk_ecc_enable(ecc, config); - if (ret) { - dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); - return ret; - } - - ret = mtk_ecc_wait_done(ecc, ECC_ENCODE); - if (ret) - goto timeout; - - mtk_ecc_wait_idle(ecc, ECC_ENCODE); - - /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */ - len = (config->strength * ecc->caps->parity_bits + 7) >> 3; - - /* write the parity bytes generated by the ECC back to temp buffer */ - __ioread32_copy(ecc->eccdata, - ecc->regs + ecc->caps->ecc_regs[ECC_ENCPAR00], - round_up(len, 4)); - - /* copy into possibly unaligned OOB region with actual length */ - memcpy(data + bytes, ecc->eccdata, len); -timeout: - - dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); - mtk_ecc_disable(ecc); - - return ret; -} -EXPORT_SYMBOL(mtk_ecc_encode); - -void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p) -{ - const u8 *ecc_strength = ecc->caps->ecc_strength; - int i; - - for (i = 0; i < ecc->caps->num_ecc_strength; i++) { - if (*p <= ecc_strength[i]) { - if (!i) - *p = ecc_strength[i]; - else if (*p != ecc_strength[i]) - *p = ecc_strength[i - 1]; - return; - } - } - - *p = ecc_strength[ecc->caps->num_ecc_strength - 1]; -} -EXPORT_SYMBOL(mtk_ecc_adjust_strength); - -unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc) -{ - return ecc->caps->parity_bits; -} -EXPORT_SYMBOL(mtk_ecc_get_parity_bits); - -static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = { - .err_mask = 0x3f, - .ecc_strength = ecc_strength_mt2701, - .ecc_regs = mt2701_ecc_regs, - .num_ecc_strength = 20, - .ecc_mode_shift = 5, - .parity_bits = 14, - .pg_irq_sel = 0, -}; - -static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = { - .err_mask = 0x7f, - .ecc_strength = ecc_strength_mt2712, - .ecc_regs = mt2712_ecc_regs, - .num_ecc_strength = 23, - .ecc_mode_shift = 5, - .parity_bits = 14, - .pg_irq_sel = 1, -}; - -static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = { - .err_mask = 0x3f, - .ecc_strength = ecc_strength_mt7622, - .ecc_regs = mt7622_ecc_regs, - .num_ecc_strength = 7, - .ecc_mode_shift = 4, - .parity_bits = 13, - .pg_irq_sel = 0, -}; - -static const struct of_device_id mtk_ecc_dt_match[] = { - { - .compatible = "mediatek,mt2701-ecc", - .data = &mtk_ecc_caps_mt2701, - }, { - .compatible = "mediatek,mt2712-ecc", - .data = &mtk_ecc_caps_mt2712, - }, { - .compatible = "mediatek,mt7622-ecc", - .data = &mtk_ecc_caps_mt7622, - }, - {}, -}; - -static int mtk_ecc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mtk_ecc *ecc; - struct resource *res; - const struct of_device_id *of_ecc_id = NULL; - u32 max_eccdata_size; - int irq, ret; - - ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); - if (!ecc) - return -ENOMEM; - - of_ecc_id = of_match_device(mtk_ecc_dt_match, &pdev->dev); - if (!of_ecc_id) - return -ENODEV; - - ecc->caps = of_ecc_id->data; - - max_eccdata_size = ecc->caps->num_ecc_strength - 1; - max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size]; - max_eccdata_size = (max_eccdata_size * ecc->caps->parity_bits + 7) >> 3; - max_eccdata_size = round_up(max_eccdata_size, 4); - ecc->eccdata = devm_kzalloc(dev, max_eccdata_size, GFP_KERNEL); - if (!ecc->eccdata) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ecc->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(ecc->regs)) { - dev_err(dev, "failed to map regs: %ld\n", PTR_ERR(ecc->regs)); - return PTR_ERR(ecc->regs); - } - - ecc->clk = devm_clk_get(dev, NULL); - if (IS_ERR(ecc->clk)) { - dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk)); - return PTR_ERR(ecc->clk); - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to get irq: %d\n", irq); - return irq; - } - - ret = dma_set_mask(dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(dev, "failed to set DMA mask\n"); - return ret; - } - - ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc); - if (ret) { - dev_err(dev, "failed to request irq\n"); - return -EINVAL; - } - - ecc->dev = dev; - mutex_init(&ecc->lock); - platform_set_drvdata(pdev, ecc); - dev_info(dev, "probed\n"); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int mtk_ecc_suspend(struct device *dev) -{ - struct mtk_ecc *ecc = dev_get_drvdata(dev); - - clk_disable_unprepare(ecc->clk); - - return 0; -} - -static int mtk_ecc_resume(struct device *dev) -{ - struct mtk_ecc *ecc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(ecc->clk); - if (ret) { - dev_err(dev, "failed to enable clk\n"); - return ret; - } - - return 0; -} - -static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume); -#endif - -MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match); - -static struct platform_driver mtk_ecc_driver = { - .probe = mtk_ecc_probe, - .driver = { - .name = "mtk-ecc", - .of_match_table = of_match_ptr(mtk_ecc_dt_match), -#ifdef CONFIG_PM_SLEEP - .pm = &mtk_ecc_pm_ops, -#endif - }, -}; - -module_platform_driver(mtk_ecc_driver); - -MODULE_AUTHOR("Xiaolei Li "); -MODULE_DESCRIPTION("MTK Nand ECC Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/mtk_ecc.h b/drivers/mtd/nand/mtk_ecc.h deleted file mode 100644 index a455df080952..000000000000 --- a/drivers/mtd/nand/mtk_ecc.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * MTK SDG1 ECC controller - * - * Copyright (c) 2016 Mediatek - * Authors: Xiaolei Li - * Jorge Ramirez-Ortiz - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - */ - -#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__ -#define __DRIVERS_MTD_NAND_MTK_ECC_H__ - -#include - -enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1}; -enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE}; - -struct device_node; -struct mtk_ecc; - -struct mtk_ecc_stats { - u32 corrected; - u32 bitflips; - u32 failed; -}; - -struct mtk_ecc_config { - enum mtk_ecc_operation op; - enum mtk_ecc_mode mode; - dma_addr_t addr; - u32 strength; - u32 sectors; - u32 len; -}; - -int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32); -void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int); -int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation); -int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *); -void mtk_ecc_disable(struct mtk_ecc *); -void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p); -unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc); - -struct mtk_ecc *of_mtk_ecc_get(struct device_node *); -void mtk_ecc_release(struct mtk_ecc *); - -#endif diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c deleted file mode 100644 index 6977da3a26aa..000000000000 --- a/drivers/mtd/nand/mtk_nand.c +++ /dev/null @@ -1,1599 +0,0 @@ -/* - * MTK NAND Flash controller driver. - * Copyright (C) 2016 MediaTek Inc. - * Authors: Xiaolei Li - * Jorge Ramirez-Ortiz - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mtk_ecc.h" - -/* NAND controller register definition */ -#define NFI_CNFG (0x00) -#define CNFG_AHB BIT(0) -#define CNFG_READ_EN BIT(1) -#define CNFG_DMA_BURST_EN BIT(2) -#define CNFG_BYTE_RW BIT(6) -#define CNFG_HW_ECC_EN BIT(8) -#define CNFG_AUTO_FMT_EN BIT(9) -#define CNFG_OP_CUST (6 << 12) -#define NFI_PAGEFMT (0x04) -#define PAGEFMT_FDM_ECC_SHIFT (12) -#define PAGEFMT_FDM_SHIFT (8) -#define PAGEFMT_SEC_SEL_512 BIT(2) -#define PAGEFMT_512_2K (0) -#define PAGEFMT_2K_4K (1) -#define PAGEFMT_4K_8K (2) -#define PAGEFMT_8K_16K (3) -/* NFI control */ -#define NFI_CON (0x08) -#define CON_FIFO_FLUSH BIT(0) -#define CON_NFI_RST BIT(1) -#define CON_BRD BIT(8) /* burst read */ -#define CON_BWR BIT(9) /* burst write */ -#define CON_SEC_SHIFT (12) -/* Timming control register */ -#define NFI_ACCCON (0x0C) -#define NFI_INTR_EN (0x10) -#define INTR_AHB_DONE_EN BIT(6) -#define NFI_INTR_STA (0x14) -#define NFI_CMD (0x20) -#define NFI_ADDRNOB (0x30) -#define NFI_COLADDR (0x34) -#define NFI_ROWADDR (0x38) -#define NFI_STRDATA (0x40) -#define STAR_EN (1) -#define STAR_DE (0) -#define NFI_CNRNB (0x44) -#define NFI_DATAW (0x50) -#define NFI_DATAR (0x54) -#define NFI_PIO_DIRDY (0x58) -#define PIO_DI_RDY (0x01) -#define NFI_STA (0x60) -#define STA_CMD BIT(0) -#define STA_ADDR BIT(1) -#define STA_BUSY BIT(8) -#define STA_EMP_PAGE BIT(12) -#define NFI_FSM_CUSTDATA (0xe << 16) -#define NFI_FSM_MASK (0xf << 16) -#define NFI_ADDRCNTR (0x70) -#define CNTR_MASK GENMASK(16, 12) -#define ADDRCNTR_SEC_SHIFT (12) -#define ADDRCNTR_SEC(val) \ - (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT) -#define NFI_STRADDR (0x80) -#define NFI_BYTELEN (0x84) -#define NFI_CSEL (0x90) -#define NFI_FDML(x) (0xA0 + (x) * sizeof(u32) * 2) -#define NFI_FDMM(x) (0xA4 + (x) * sizeof(u32) * 2) -#define NFI_FDM_MAX_SIZE (8) -#define NFI_FDM_MIN_SIZE (1) -#define NFI_MASTER_STA (0x224) -#define MASTER_STA_MASK (0x0FFF) -#define NFI_EMPTY_THRESH (0x23C) - -#define MTK_NAME "mtk-nand" -#define KB(x) ((x) * 1024UL) -#define MB(x) (KB(x) * 1024UL) - -#define MTK_TIMEOUT (500000) -#define MTK_RESET_TIMEOUT (1000000) -#define MTK_NAND_MAX_NSELS (2) -#define MTK_NFC_MIN_SPARE (16) -#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \ - ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \ - (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt)) - -struct mtk_nfc_caps { - const u8 *spare_size; - u8 num_spare_size; - u8 pageformat_spare_shift; - u8 nfi_clk_div; - u8 max_sector; - u32 max_sector_size; -}; - -struct mtk_nfc_bad_mark_ctl { - void (*bm_swap)(struct mtd_info *, u8 *buf, int raw); - u32 sec; - u32 pos; -}; - -/* - * FDM: region used to store free OOB data - */ -struct mtk_nfc_fdm { - u32 reg_size; - u32 ecc_size; -}; - -struct mtk_nfc_nand_chip { - struct list_head node; - struct nand_chip nand; - - struct mtk_nfc_bad_mark_ctl bad_mark; - struct mtk_nfc_fdm fdm; - u32 spare_per_sector; - - int nsels; - u8 sels[0]; - /* nothing after this field */ -}; - -struct mtk_nfc_clk { - struct clk *nfi_clk; - struct clk *pad_clk; -}; - -struct mtk_nfc { - struct nand_hw_control controller; - struct mtk_ecc_config ecc_cfg; - struct mtk_nfc_clk clk; - struct mtk_ecc *ecc; - - struct device *dev; - const struct mtk_nfc_caps *caps; - void __iomem *regs; - - struct completion done; - struct list_head chips; - - u8 *buffer; -}; - -/* - * supported spare size of each IP. - * order should be the same with the spare size bitfiled defination of - * register NFI_PAGEFMT. - */ -static const u8 spare_size_mt2701[] = { - 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 63, 64 -}; - -static const u8 spare_size_mt2712[] = { - 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, 67, - 74 -}; - -static const u8 spare_size_mt7622[] = { - 16, 26, 27, 28 -}; - -static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct nand_chip *nand) -{ - return container_of(nand, struct mtk_nfc_nand_chip, nand); -} - -static inline u8 *data_ptr(struct nand_chip *chip, const u8 *p, int i) -{ - return (u8 *)p + i * chip->ecc.size; -} - -static inline u8 *oob_ptr(struct nand_chip *chip, int i) -{ - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - u8 *poi; - - /* map the sector's FDM data to free oob: - * the beginning of the oob area stores the FDM data of bad mark sectors - */ - - if (i < mtk_nand->bad_mark.sec) - poi = chip->oob_poi + (i + 1) * mtk_nand->fdm.reg_size; - else if (i == mtk_nand->bad_mark.sec) - poi = chip->oob_poi; - else - poi = chip->oob_poi + i * mtk_nand->fdm.reg_size; - - return poi; -} - -static inline int mtk_data_len(struct nand_chip *chip) -{ - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - - return chip->ecc.size + mtk_nand->spare_per_sector; -} - -static inline u8 *mtk_data_ptr(struct nand_chip *chip, int i) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - - return nfc->buffer + i * mtk_data_len(chip); -} - -static inline u8 *mtk_oob_ptr(struct nand_chip *chip, int i) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - - return nfc->buffer + i * mtk_data_len(chip) + chip->ecc.size; -} - -static inline void nfi_writel(struct mtk_nfc *nfc, u32 val, u32 reg) -{ - writel(val, nfc->regs + reg); -} - -static inline void nfi_writew(struct mtk_nfc *nfc, u16 val, u32 reg) -{ - writew(val, nfc->regs + reg); -} - -static inline void nfi_writeb(struct mtk_nfc *nfc, u8 val, u32 reg) -{ - writeb(val, nfc->regs + reg); -} - -static inline u32 nfi_readl(struct mtk_nfc *nfc, u32 reg) -{ - return readl_relaxed(nfc->regs + reg); -} - -static inline u16 nfi_readw(struct mtk_nfc *nfc, u32 reg) -{ - return readw_relaxed(nfc->regs + reg); -} - -static inline u8 nfi_readb(struct mtk_nfc *nfc, u32 reg) -{ - return readb_relaxed(nfc->regs + reg); -} - -static void mtk_nfc_hw_reset(struct mtk_nfc *nfc) -{ - struct device *dev = nfc->dev; - u32 val; - int ret; - - /* reset all registers and force the NFI master to terminate */ - nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON); - - /* wait for the master to finish the last transaction */ - ret = readl_poll_timeout(nfc->regs + NFI_MASTER_STA, val, - !(val & MASTER_STA_MASK), 50, - MTK_RESET_TIMEOUT); - if (ret) - dev_warn(dev, "master active in reset [0x%x] = 0x%x\n", - NFI_MASTER_STA, val); - - /* ensure any status register affected by the NFI master is reset */ - nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON); - nfi_writew(nfc, STAR_DE, NFI_STRDATA); -} - -static int mtk_nfc_send_command(struct mtk_nfc *nfc, u8 command) -{ - struct device *dev = nfc->dev; - u32 val; - int ret; - - nfi_writel(nfc, command, NFI_CMD); - - ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val, - !(val & STA_CMD), 10, MTK_TIMEOUT); - if (ret) { - dev_warn(dev, "nfi core timed out entering command mode\n"); - return -EIO; - } - - return 0; -} - -static int mtk_nfc_send_address(struct mtk_nfc *nfc, int addr) -{ - struct device *dev = nfc->dev; - u32 val; - int ret; - - nfi_writel(nfc, addr, NFI_COLADDR); - nfi_writel(nfc, 0, NFI_ROWADDR); - nfi_writew(nfc, 1, NFI_ADDRNOB); - - ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val, - !(val & STA_ADDR), 10, MTK_TIMEOUT); - if (ret) { - dev_warn(dev, "nfi core timed out entering address mode\n"); - return -EIO; - } - - return 0; -} - -static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - struct mtk_nfc *nfc = nand_get_controller_data(chip); - u32 fmt, spare, i; - - if (!mtd->writesize) - return 0; - - spare = mtk_nand->spare_per_sector; - - switch (mtd->writesize) { - case 512: - fmt = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512; - break; - case KB(2): - if (chip->ecc.size == 512) - fmt = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512; - else - fmt = PAGEFMT_512_2K; - break; - case KB(4): - if (chip->ecc.size == 512) - fmt = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512; - else - fmt = PAGEFMT_2K_4K; - break; - case KB(8): - if (chip->ecc.size == 512) - fmt = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512; - else - fmt = PAGEFMT_4K_8K; - break; - case KB(16): - fmt = PAGEFMT_8K_16K; - break; - default: - dev_err(nfc->dev, "invalid page len: %d\n", mtd->writesize); - return -EINVAL; - } - - /* - * the hardware will double the value for this eccsize, so we need to - * halve it - */ - if (chip->ecc.size == 1024) - spare >>= 1; - - for (i = 0; i < nfc->caps->num_spare_size; i++) { - if (nfc->caps->spare_size[i] == spare) - break; - } - - if (i == nfc->caps->num_spare_size) { - dev_err(nfc->dev, "invalid spare size %d\n", spare); - return -EINVAL; - } - - fmt |= i << nfc->caps->pageformat_spare_shift; - - fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT; - fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT; - nfi_writel(nfc, fmt, NFI_PAGEFMT); - - nfc->ecc_cfg.strength = chip->ecc.strength; - nfc->ecc_cfg.len = chip->ecc.size + mtk_nand->fdm.ecc_size; - - return 0; -} - -static void mtk_nfc_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mtk_nfc *nfc = nand_get_controller_data(nand); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand); - - if (chip < 0) - return; - - mtk_nfc_hw_runtime_config(mtd); - - nfi_writel(nfc, mtk_nand->sels[chip], NFI_CSEL); -} - -static int mtk_nfc_dev_ready(struct mtd_info *mtd) -{ - struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); - - if (nfi_readl(nfc, NFI_STA) & STA_BUSY) - return 0; - - return 1; -} - -static void mtk_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) -{ - struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); - - if (ctrl & NAND_ALE) { - mtk_nfc_send_address(nfc, dat); - } else if (ctrl & NAND_CLE) { - mtk_nfc_hw_reset(nfc); - - nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG); - mtk_nfc_send_command(nfc, dat); - } -} - -static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc) -{ - int rc; - u8 val; - - rc = readb_poll_timeout_atomic(nfc->regs + NFI_PIO_DIRDY, val, - val & PIO_DI_RDY, 10, MTK_TIMEOUT); - if (rc < 0) - dev_err(nfc->dev, "data not ready\n"); -} - -static inline u8 mtk_nfc_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtk_nfc *nfc = nand_get_controller_data(chip); - u32 reg; - - /* after each byte read, the NFI_STA reg is reset by the hardware */ - reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK; - if (reg != NFI_FSM_CUSTDATA) { - reg = nfi_readw(nfc, NFI_CNFG); - reg |= CNFG_BYTE_RW | CNFG_READ_EN; - nfi_writew(nfc, reg, NFI_CNFG); - - /* - * set to max sector to allow the HW to continue reading over - * unaligned accesses - */ - reg = (nfc->caps->max_sector << CON_SEC_SHIFT) | CON_BRD; - nfi_writel(nfc, reg, NFI_CON); - - /* trigger to fetch data */ - nfi_writew(nfc, STAR_EN, NFI_STRDATA); - } - - mtk_nfc_wait_ioready(nfc); - - return nfi_readb(nfc, NFI_DATAR); -} - -static void mtk_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - buf[i] = mtk_nfc_read_byte(mtd); -} - -static void mtk_nfc_write_byte(struct mtd_info *mtd, u8 byte) -{ - struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); - u32 reg; - - reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK; - - if (reg != NFI_FSM_CUSTDATA) { - reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW; - nfi_writew(nfc, reg, NFI_CNFG); - - reg = nfc->caps->max_sector << CON_SEC_SHIFT | CON_BWR; - nfi_writel(nfc, reg, NFI_CON); - - nfi_writew(nfc, STAR_EN, NFI_STRDATA); - } - - mtk_nfc_wait_ioready(nfc); - nfi_writeb(nfc, byte, NFI_DATAW); -} - -static void mtk_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - mtk_nfc_write_byte(mtd, buf[i]); -} - -static int mtk_nfc_setup_data_interface(struct mtd_info *mtd, int csline, - const struct nand_data_interface *conf) -{ - struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); - const struct nand_sdr_timings *timings; - u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt; - - timings = nand_get_sdr_timings(conf); - if (IS_ERR(timings)) - return -ENOTSUPP; - - if (csline == NAND_DATA_IFACE_CHECK_ONLY) - return 0; - - rate = clk_get_rate(nfc->clk.nfi_clk); - /* There is a frequency divider in some IPs */ - rate /= nfc->caps->nfi_clk_div; - - /* turn clock rate into KHZ */ - rate /= 1000; - - tpoecs = max(timings->tALH_min, timings->tCLH_min) / 1000; - tpoecs = DIV_ROUND_UP(tpoecs * rate, 1000000); - tpoecs &= 0xf; - - tprecs = max(timings->tCLS_min, timings->tALS_min) / 1000; - tprecs = DIV_ROUND_UP(tprecs * rate, 1000000); - tprecs &= 0x3f; - - /* sdr interface has no tCR which means CE# low to RE# low */ - tc2r = 0; - - tw2r = timings->tWHR_min / 1000; - tw2r = DIV_ROUND_UP(tw2r * rate, 1000000); - tw2r = DIV_ROUND_UP(tw2r - 1, 2); - tw2r &= 0xf; - - twh = max(timings->tREH_min, timings->tWH_min) / 1000; - twh = DIV_ROUND_UP(twh * rate, 1000000) - 1; - twh &= 0xf; - - twst = timings->tWP_min / 1000; - twst = DIV_ROUND_UP(twst * rate, 1000000) - 1; - twst &= 0xf; - - trlt = max(timings->tREA_max, timings->tRP_min) / 1000; - trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1; - trlt &= 0xf; - - /* - * ACCON: access timing control register - * ------------------------------------- - * 31:28: tpoecs, minimum required time for CS post pulling down after - * accessing the device - * 27:22: tprecs, minimum required time for CS pre pulling down before - * accessing the device - * 21:16: tc2r, minimum required time from NCEB low to NREB low - * 15:12: tw2r, minimum required time from NWEB high to NREB low. - * 11:08: twh, write enable hold time - * 07:04: twst, write wait states - * 03:00: trlt, read wait states - */ - trlt = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt); - nfi_writel(nfc, trlt, NFI_ACCCON); - - return 0; -} - -static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - int size = chip->ecc.size + mtk_nand->fdm.reg_size; - - nfc->ecc_cfg.mode = ECC_DMA_MODE; - nfc->ecc_cfg.op = ECC_ENCODE; - - return mtk_ecc_encode(nfc->ecc, &nfc->ecc_cfg, data, size); -} - -static void mtk_nfc_no_bad_mark_swap(struct mtd_info *a, u8 *b, int c) -{ - /* nop */ -} - -static void mtk_nfc_bad_mark_swap(struct mtd_info *mtd, u8 *buf, int raw) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtk_nfc_nand_chip *nand = to_mtk_nand(chip); - u32 bad_pos = nand->bad_mark.pos; - - if (raw) - bad_pos += nand->bad_mark.sec * mtk_data_len(chip); - else - bad_pos += nand->bad_mark.sec * chip->ecc.size; - - swap(chip->oob_poi[0], buf[bad_pos]); -} - -static int mtk_nfc_format_subpage(struct mtd_info *mtd, u32 offset, - u32 len, const u8 *buf) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; - u32 start, end; - int i, ret; - - start = offset / chip->ecc.size; - end = DIV_ROUND_UP(offset + len, chip->ecc.size); - - memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); - for (i = 0; i < chip->ecc.steps; i++) { - memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i), - chip->ecc.size); - - if (start > i || i >= end) - continue; - - if (i == mtk_nand->bad_mark.sec) - mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); - - memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size); - - /* program the CRC back to the OOB */ - ret = mtk_nfc_sector_encode(chip, mtk_data_ptr(chip, i)); - if (ret < 0) - return ret; - } - - return 0; -} - -static void mtk_nfc_format_page(struct mtd_info *mtd, const u8 *buf) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; - u32 i; - - memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); - for (i = 0; i < chip->ecc.steps; i++) { - if (buf) - memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i), - chip->ecc.size); - - if (i == mtk_nand->bad_mark.sec) - mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); - - memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size); - } -} - -static inline void mtk_nfc_read_fdm(struct nand_chip *chip, u32 start, - u32 sectors) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; - u32 vall, valm; - u8 *oobptr; - int i, j; - - for (i = 0; i < sectors; i++) { - oobptr = oob_ptr(chip, start + i); - vall = nfi_readl(nfc, NFI_FDML(i)); - valm = nfi_readl(nfc, NFI_FDMM(i)); - - for (j = 0; j < fdm->reg_size; j++) - oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8); - } -} - -static inline void mtk_nfc_write_fdm(struct nand_chip *chip) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; - u32 vall, valm; - u8 *oobptr; - int i, j; - - for (i = 0; i < chip->ecc.steps; i++) { - oobptr = oob_ptr(chip, i); - vall = 0; - valm = 0; - for (j = 0; j < 8; j++) { - if (j < 4) - vall |= (j < fdm->reg_size ? oobptr[j] : 0xff) - << (j * 8); - else - valm |= (j < fdm->reg_size ? oobptr[j] : 0xff) - << ((j - 4) * 8); - } - nfi_writel(nfc, vall, NFI_FDML(i)); - nfi_writel(nfc, valm, NFI_FDMM(i)); - } -} - -static int mtk_nfc_do_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const u8 *buf, int page, int len) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct device *dev = nfc->dev; - dma_addr_t addr; - u32 reg; - int ret; - - addr = dma_map_single(dev, (void *)buf, len, DMA_TO_DEVICE); - ret = dma_mapping_error(nfc->dev, addr); - if (ret) { - dev_err(nfc->dev, "dma mapping error\n"); - return -EINVAL; - } - - reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AHB | CNFG_DMA_BURST_EN; - nfi_writew(nfc, reg, NFI_CNFG); - - nfi_writel(nfc, chip->ecc.steps << CON_SEC_SHIFT, NFI_CON); - nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR); - nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN); - - init_completion(&nfc->done); - - reg = nfi_readl(nfc, NFI_CON) | CON_BWR; - nfi_writel(nfc, reg, NFI_CON); - nfi_writew(nfc, STAR_EN, NFI_STRDATA); - - ret = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500)); - if (!ret) { - dev_err(dev, "program ahb done timeout\n"); - nfi_writew(nfc, 0, NFI_INTR_EN); - ret = -ETIMEDOUT; - goto timeout; - } - - ret = readl_poll_timeout_atomic(nfc->regs + NFI_ADDRCNTR, reg, - ADDRCNTR_SEC(reg) >= chip->ecc.steps, - 10, MTK_TIMEOUT); - if (ret) - dev_err(dev, "hwecc write timeout\n"); - -timeout: - - dma_unmap_single(nfc->dev, addr, len, DMA_TO_DEVICE); - nfi_writel(nfc, 0, NFI_CON); - - return ret; -} - -static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const u8 *buf, int page, int raw) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - size_t len; - const u8 *bufpoi; - u32 reg; - int ret; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - if (!raw) { - /* OOB => FDM: from register, ECC: from HW */ - reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AUTO_FMT_EN; - nfi_writew(nfc, reg | CNFG_HW_ECC_EN, NFI_CNFG); - - nfc->ecc_cfg.op = ECC_ENCODE; - nfc->ecc_cfg.mode = ECC_NFI_MODE; - ret = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg); - if (ret) { - /* clear NFI config */ - reg = nfi_readw(nfc, NFI_CNFG); - reg &= ~(CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); - nfi_writew(nfc, reg, NFI_CNFG); - - return ret; - } - - memcpy(nfc->buffer, buf, mtd->writesize); - mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, raw); - bufpoi = nfc->buffer; - - /* write OOB into the FDM registers (OOB area in MTK NAND) */ - mtk_nfc_write_fdm(chip); - } else { - bufpoi = buf; - } - - len = mtd->writesize + (raw ? mtd->oobsize : 0); - ret = mtk_nfc_do_write_page(mtd, chip, bufpoi, page, len); - - if (!raw) - mtk_ecc_disable(nfc->ecc); - - if (ret) - return ret; - - return nand_prog_page_end_op(chip); -} - -static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const u8 *buf, - int oob_on, int page) -{ - return mtk_nfc_write_page(mtd, chip, buf, page, 0); -} - -static int mtk_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const u8 *buf, int oob_on, int pg) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - - mtk_nfc_format_page(mtd, buf); - return mtk_nfc_write_page(mtd, chip, nfc->buffer, pg, 1); -} - -static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, u32 offset, - u32 data_len, const u8 *buf, - int oob_on, int page) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - int ret; - - ret = mtk_nfc_format_subpage(mtd, offset, data_len, buf); - if (ret < 0) - return ret; - - /* use the data in the private buffer (now with FDM and CRC) */ - return mtk_nfc_write_page(mtd, chip, nfc->buffer, page, 1); -} - -static int mtk_nfc_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - return mtk_nfc_write_page_raw(mtd, chip, NULL, 1, page); -} - -static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - struct mtk_ecc_stats stats; - int rc, i; - - rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE; - if (rc) { - memset(buf, 0xff, sectors * chip->ecc.size); - for (i = 0; i < sectors; i++) - memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size); - return 0; - } - - mtk_ecc_get_stats(nfc->ecc, &stats, sectors); - mtd->ecc_stats.corrected += stats.corrected; - mtd->ecc_stats.failed += stats.failed; - - return stats.bitflips; -} - -static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, - u32 data_offs, u32 readlen, - u8 *bufpoi, int page, int raw) -{ - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - u32 spare = mtk_nand->spare_per_sector; - u32 column, sectors, start, end, reg; - dma_addr_t addr; - int bitflips; - size_t len; - u8 *buf; - int rc; - - start = data_offs / chip->ecc.size; - end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); - - sectors = end - start; - column = start * (chip->ecc.size + spare); - - len = sectors * chip->ecc.size + (raw ? sectors * spare : 0); - buf = bufpoi + start * chip->ecc.size; - - nand_read_page_op(chip, page, column, NULL, 0); - - addr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE); - rc = dma_mapping_error(nfc->dev, addr); - if (rc) { - dev_err(nfc->dev, "dma mapping error\n"); - - return -EINVAL; - } - - reg = nfi_readw(nfc, NFI_CNFG); - reg |= CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_AHB; - if (!raw) { - reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN; - nfi_writew(nfc, reg, NFI_CNFG); - - nfc->ecc_cfg.mode = ECC_NFI_MODE; - nfc->ecc_cfg.sectors = sectors; - nfc->ecc_cfg.op = ECC_DECODE; - rc = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg); - if (rc) { - dev_err(nfc->dev, "ecc enable\n"); - /* clear NFI_CNFG */ - reg &= ~(CNFG_DMA_BURST_EN | CNFG_AHB | CNFG_READ_EN | - CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); - nfi_writew(nfc, reg, NFI_CNFG); - dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); - - return rc; - } - } else { - nfi_writew(nfc, reg, NFI_CNFG); - } - - nfi_writel(nfc, sectors << CON_SEC_SHIFT, NFI_CON); - nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN); - nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR); - - init_completion(&nfc->done); - reg = nfi_readl(nfc, NFI_CON) | CON_BRD; - nfi_writel(nfc, reg, NFI_CON); - nfi_writew(nfc, STAR_EN, NFI_STRDATA); - - rc = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500)); - if (!rc) - dev_warn(nfc->dev, "read ahb/dma done timeout\n"); - - rc = readl_poll_timeout_atomic(nfc->regs + NFI_BYTELEN, reg, - ADDRCNTR_SEC(reg) >= sectors, 10, - MTK_TIMEOUT); - if (rc < 0) { - dev_err(nfc->dev, "subpage done timeout\n"); - bitflips = -EIO; - } else { - bitflips = 0; - if (!raw) { - rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE); - bitflips = rc < 0 ? -ETIMEDOUT : - mtk_nfc_update_ecc_stats(mtd, buf, sectors); - mtk_nfc_read_fdm(chip, start, sectors); - } - } - - dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); - - if (raw) - goto done; - - mtk_ecc_disable(nfc->ecc); - - if (clamp(mtk_nand->bad_mark.sec, start, end) == mtk_nand->bad_mark.sec) - mtk_nand->bad_mark.bm_swap(mtd, bufpoi, raw); -done: - nfi_writel(nfc, 0, NFI_CON); - - return bitflips; -} - -static int mtk_nfc_read_subpage_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, u32 off, - u32 len, u8 *p, int pg) -{ - return mtk_nfc_read_subpage(mtd, chip, off, len, p, pg, 0); -} - -static int mtk_nfc_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, u8 *p, - int oob_on, int pg) -{ - return mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, p, pg, 0); -} - -static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - u8 *buf, int oob_on, int page) -{ - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - struct mtk_nfc *nfc = nand_get_controller_data(chip); - struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; - int i, ret; - - memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); - ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, nfc->buffer, - page, 1); - if (ret < 0) - return ret; - - for (i = 0; i < chip->ecc.steps; i++) { - memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size); - - if (i == mtk_nand->bad_mark.sec) - mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); - - if (buf) - memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i), - chip->ecc.size); - } - - return ret; -} - -static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - return mtk_nfc_read_page_raw(mtd, chip, NULL, 1, page); -} - -static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc) -{ - /* - * CNRNB: nand ready/busy register - * ------------------------------- - * 7:4: timeout register for polling the NAND busy/ready signal - * 0 : poll the status of the busy/ready signal after [7:4]*16 cycles. - */ - nfi_writew(nfc, 0xf1, NFI_CNRNB); - nfi_writel(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT); - - mtk_nfc_hw_reset(nfc); - - nfi_readl(nfc, NFI_INTR_STA); - nfi_writel(nfc, 0, NFI_INTR_EN); -} - -static irqreturn_t mtk_nfc_irq(int irq, void *id) -{ - struct mtk_nfc *nfc = id; - u16 sta, ien; - - sta = nfi_readw(nfc, NFI_INTR_STA); - ien = nfi_readw(nfc, NFI_INTR_EN); - - if (!(sta & ien)) - return IRQ_NONE; - - nfi_writew(nfc, ~sta & ien, NFI_INTR_EN); - complete(&nfc->done); - - return IRQ_HANDLED; -} - -static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk) -{ - int ret; - - ret = clk_prepare_enable(clk->nfi_clk); - if (ret) { - dev_err(dev, "failed to enable nfi clk\n"); - return ret; - } - - ret = clk_prepare_enable(clk->pad_clk); - if (ret) { - dev_err(dev, "failed to enable pad clk\n"); - clk_disable_unprepare(clk->nfi_clk); - return ret; - } - - return 0; -} - -static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk) -{ - clk_disable_unprepare(clk->nfi_clk); - clk_disable_unprepare(clk->pad_clk); -} - -static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oob_region) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; - u32 eccsteps; - - eccsteps = mtd->writesize / chip->ecc.size; - - if (section >= eccsteps) - return -ERANGE; - - oob_region->length = fdm->reg_size - fdm->ecc_size; - oob_region->offset = section * fdm->reg_size + fdm->ecc_size; - - return 0; -} - -static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oob_region) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); - u32 eccsteps; - - if (section) - return -ERANGE; - - eccsteps = mtd->writesize / chip->ecc.size; - oob_region->offset = mtk_nand->fdm.reg_size * eccsteps; - oob_region->length = mtd->oobsize - oob_region->offset; - - return 0; -} - -static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = { - .free = mtk_nfc_ooblayout_free, - .ecc = mtk_nfc_ooblayout_ecc, -}; - -static void mtk_nfc_set_fdm(struct mtk_nfc_fdm *fdm, struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand); - struct mtk_nfc *nfc = nand_get_controller_data(nand); - u32 ecc_bytes; - - ecc_bytes = DIV_ROUND_UP(nand->ecc.strength * - mtk_ecc_get_parity_bits(nfc->ecc), 8); - - fdm->reg_size = chip->spare_per_sector - ecc_bytes; - if (fdm->reg_size > NFI_FDM_MAX_SIZE) - fdm->reg_size = NFI_FDM_MAX_SIZE; - - /* bad block mark storage */ - fdm->ecc_size = 1; -} - -static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl, - struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - - if (mtd->writesize == 512) { - bm_ctl->bm_swap = mtk_nfc_no_bad_mark_swap; - } else { - bm_ctl->bm_swap = mtk_nfc_bad_mark_swap; - bm_ctl->sec = mtd->writesize / mtk_data_len(nand); - bm_ctl->pos = mtd->writesize % mtk_data_len(nand); - } -} - -static int mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mtk_nfc *nfc = nand_get_controller_data(nand); - const u8 *spare = nfc->caps->spare_size; - u32 eccsteps, i, closest_spare = 0; - - eccsteps = mtd->writesize / nand->ecc.size; - *sps = mtd->oobsize / eccsteps; - - if (nand->ecc.size == 1024) - *sps >>= 1; - - if (*sps < MTK_NFC_MIN_SPARE) - return -EINVAL; - - for (i = 0; i < nfc->caps->num_spare_size; i++) { - if (*sps >= spare[i] && spare[i] >= spare[closest_spare]) { - closest_spare = i; - if (*sps == spare[i]) - break; - } - } - - *sps = spare[closest_spare]; - - if (nand->ecc.size == 1024) - *sps <<= 1; - - return 0; -} - -static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mtk_nfc *nfc = nand_get_controller_data(nand); - u32 spare; - int free, ret; - - /* support only ecc hw mode */ - if (nand->ecc.mode != NAND_ECC_HW) { - dev_err(dev, "ecc.mode not supported\n"); - return -EINVAL; - } - - /* if optional dt settings not present */ - if (!nand->ecc.size || !nand->ecc.strength) { - /* use datasheet requirements */ - nand->ecc.strength = nand->ecc_strength_ds; - nand->ecc.size = nand->ecc_step_ds; - - /* - * align eccstrength and eccsize - * this controller only supports 512 and 1024 sizes - */ - if (nand->ecc.size < 1024) { - if (mtd->writesize > 512 && - nfc->caps->max_sector_size > 512) { - nand->ecc.size = 1024; - nand->ecc.strength <<= 1; - } else { - nand->ecc.size = 512; - } - } else { - nand->ecc.size = 1024; - } - - ret = mtk_nfc_set_spare_per_sector(&spare, mtd); - if (ret) - return ret; - - /* calculate oob bytes except ecc parity data */ - free = (nand->ecc.strength * mtk_ecc_get_parity_bits(nfc->ecc) - + 7) >> 3; - free = spare - free; - - /* - * enhance ecc strength if oob left is bigger than max FDM size - * or reduce ecc strength if oob size is not enough for ecc - * parity data. - */ - if (free > NFI_FDM_MAX_SIZE) { - spare -= NFI_FDM_MAX_SIZE; - nand->ecc.strength = (spare << 3) / - mtk_ecc_get_parity_bits(nfc->ecc); - } else if (free < 0) { - spare -= NFI_FDM_MIN_SIZE; - nand->ecc.strength = (spare << 3) / - mtk_ecc_get_parity_bits(nfc->ecc); - } - } - - mtk_ecc_adjust_strength(nfc->ecc, &nand->ecc.strength); - - dev_info(dev, "eccsize %d eccstrength %d\n", - nand->ecc.size, nand->ecc.strength); - - return 0; -} - -static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, - struct device_node *np) -{ - struct mtk_nfc_nand_chip *chip; - struct nand_chip *nand; - struct mtd_info *mtd; - int nsels, len; - u32 tmp; - int ret; - int i; - - if (!of_get_property(np, "reg", &nsels)) - return -ENODEV; - - nsels /= sizeof(u32); - if (!nsels || nsels > MTK_NAND_MAX_NSELS) { - dev_err(dev, "invalid reg property size %d\n", nsels); - return -EINVAL; - } - - chip = devm_kzalloc(dev, sizeof(*chip) + nsels * sizeof(u8), - GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->nsels = nsels; - for (i = 0; i < nsels; i++) { - ret = of_property_read_u32_index(np, "reg", i, &tmp); - if (ret) { - dev_err(dev, "reg property failure : %d\n", ret); - return ret; - } - chip->sels[i] = tmp; - } - - nand = &chip->nand; - nand->controller = &nfc->controller; - - nand_set_flash_node(nand, np); - nand_set_controller_data(nand, nfc); - - nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ; - nand->dev_ready = mtk_nfc_dev_ready; - nand->select_chip = mtk_nfc_select_chip; - nand->write_byte = mtk_nfc_write_byte; - nand->write_buf = mtk_nfc_write_buf; - nand->read_byte = mtk_nfc_read_byte; - nand->read_buf = mtk_nfc_read_buf; - nand->cmd_ctrl = mtk_nfc_cmd_ctrl; - nand->setup_data_interface = mtk_nfc_setup_data_interface; - - /* set default mode in case dt entry is missing */ - nand->ecc.mode = NAND_ECC_HW; - - nand->ecc.write_subpage = mtk_nfc_write_subpage_hwecc; - nand->ecc.write_page_raw = mtk_nfc_write_page_raw; - nand->ecc.write_page = mtk_nfc_write_page_hwecc; - nand->ecc.write_oob_raw = mtk_nfc_write_oob_std; - nand->ecc.write_oob = mtk_nfc_write_oob_std; - - nand->ecc.read_subpage = mtk_nfc_read_subpage_hwecc; - nand->ecc.read_page_raw = mtk_nfc_read_page_raw; - nand->ecc.read_page = mtk_nfc_read_page_hwecc; - nand->ecc.read_oob_raw = mtk_nfc_read_oob_std; - nand->ecc.read_oob = mtk_nfc_read_oob_std; - - mtd = nand_to_mtd(nand); - mtd->owner = THIS_MODULE; - mtd->dev.parent = dev; - mtd->name = MTK_NAME; - mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops); - - mtk_nfc_hw_init(nfc); - - ret = nand_scan_ident(mtd, nsels, NULL); - if (ret) - return ret; - - /* store bbt magic in page, cause OOB is not protected */ - if (nand->bbt_options & NAND_BBT_USE_FLASH) - nand->bbt_options |= NAND_BBT_NO_OOB; - - ret = mtk_nfc_ecc_init(dev, mtd); - if (ret) - return -EINVAL; - - if (nand->options & NAND_BUSWIDTH_16) { - dev_err(dev, "16bits buswidth not supported"); - return -EINVAL; - } - - ret = mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd); - if (ret) - return ret; - - mtk_nfc_set_fdm(&chip->fdm, mtd); - mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, mtd); - - len = mtd->writesize + mtd->oobsize; - nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL); - if (!nfc->buffer) - return -ENOMEM; - - ret = nand_scan_tail(mtd); - if (ret) - return ret; - - ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); - if (ret) { - dev_err(dev, "mtd parse partition error\n"); - nand_release(mtd); - return ret; - } - - list_add_tail(&chip->node, &nfc->chips); - - return 0; -} - -static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc) -{ - struct device_node *np = dev->of_node; - struct device_node *nand_np; - int ret; - - for_each_child_of_node(np, nand_np) { - ret = mtk_nfc_nand_chip_init(dev, nfc, nand_np); - if (ret) { - of_node_put(nand_np); - return ret; - } - } - - return 0; -} - -static const struct mtk_nfc_caps mtk_nfc_caps_mt2701 = { - .spare_size = spare_size_mt2701, - .num_spare_size = 16, - .pageformat_spare_shift = 4, - .nfi_clk_div = 1, - .max_sector = 16, - .max_sector_size = 1024, -}; - -static const struct mtk_nfc_caps mtk_nfc_caps_mt2712 = { - .spare_size = spare_size_mt2712, - .num_spare_size = 19, - .pageformat_spare_shift = 16, - .nfi_clk_div = 2, - .max_sector = 16, - .max_sector_size = 1024, -}; - -static const struct mtk_nfc_caps mtk_nfc_caps_mt7622 = { - .spare_size = spare_size_mt7622, - .num_spare_size = 4, - .pageformat_spare_shift = 4, - .nfi_clk_div = 1, - .max_sector = 8, - .max_sector_size = 512, -}; - -static const struct of_device_id mtk_nfc_id_table[] = { - { - .compatible = "mediatek,mt2701-nfc", - .data = &mtk_nfc_caps_mt2701, - }, { - .compatible = "mediatek,mt2712-nfc", - .data = &mtk_nfc_caps_mt2712, - }, { - .compatible = "mediatek,mt7622-nfc", - .data = &mtk_nfc_caps_mt7622, - }, - {} -}; -MODULE_DEVICE_TABLE(of, mtk_nfc_id_table); - -static int mtk_nfc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct mtk_nfc *nfc; - struct resource *res; - const struct of_device_id *of_nfc_id = NULL; - int ret, irq; - - nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); - if (!nfc) - return -ENOMEM; - - spin_lock_init(&nfc->controller.lock); - init_waitqueue_head(&nfc->controller.wq); - INIT_LIST_HEAD(&nfc->chips); - - /* probe defer if not ready */ - nfc->ecc = of_mtk_ecc_get(np); - if (IS_ERR(nfc->ecc)) - return PTR_ERR(nfc->ecc); - else if (!nfc->ecc) - return -ENODEV; - - nfc->dev = dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nfc->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(nfc->regs)) { - ret = PTR_ERR(nfc->regs); - goto release_ecc; - } - - nfc->clk.nfi_clk = devm_clk_get(dev, "nfi_clk"); - if (IS_ERR(nfc->clk.nfi_clk)) { - dev_err(dev, "no clk\n"); - ret = PTR_ERR(nfc->clk.nfi_clk); - goto release_ecc; - } - - nfc->clk.pad_clk = devm_clk_get(dev, "pad_clk"); - if (IS_ERR(nfc->clk.pad_clk)) { - dev_err(dev, "no pad clk\n"); - ret = PTR_ERR(nfc->clk.pad_clk); - goto release_ecc; - } - - ret = mtk_nfc_enable_clk(dev, &nfc->clk); - if (ret) - goto release_ecc; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no nfi irq resource\n"); - ret = -EINVAL; - goto clk_disable; - } - - ret = devm_request_irq(dev, irq, mtk_nfc_irq, 0x0, "mtk-nand", nfc); - if (ret) { - dev_err(dev, "failed to request nfi irq\n"); - goto clk_disable; - } - - ret = dma_set_mask(dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(dev, "failed to set dma mask\n"); - goto clk_disable; - } - - of_nfc_id = of_match_device(mtk_nfc_id_table, &pdev->dev); - if (!of_nfc_id) { - ret = -ENODEV; - goto clk_disable; - } - - nfc->caps = of_nfc_id->data; - - platform_set_drvdata(pdev, nfc); - - ret = mtk_nfc_nand_chips_init(dev, nfc); - if (ret) { - dev_err(dev, "failed to init nand chips\n"); - goto clk_disable; - } - - return 0; - -clk_disable: - mtk_nfc_disable_clk(&nfc->clk); - -release_ecc: - mtk_ecc_release(nfc->ecc); - - return ret; -} - -static int mtk_nfc_remove(struct platform_device *pdev) -{ - struct mtk_nfc *nfc = platform_get_drvdata(pdev); - struct mtk_nfc_nand_chip *chip; - - while (!list_empty(&nfc->chips)) { - chip = list_first_entry(&nfc->chips, struct mtk_nfc_nand_chip, - node); - nand_release(nand_to_mtd(&chip->nand)); - list_del(&chip->node); - } - - mtk_ecc_release(nfc->ecc); - mtk_nfc_disable_clk(&nfc->clk); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int mtk_nfc_suspend(struct device *dev) -{ - struct mtk_nfc *nfc = dev_get_drvdata(dev); - - mtk_nfc_disable_clk(&nfc->clk); - - return 0; -} - -static int mtk_nfc_resume(struct device *dev) -{ - struct mtk_nfc *nfc = dev_get_drvdata(dev); - struct mtk_nfc_nand_chip *chip; - struct nand_chip *nand; - int ret; - u32 i; - - udelay(200); - - ret = mtk_nfc_enable_clk(dev, &nfc->clk); - if (ret) - return ret; - - /* reset NAND chip if VCC was powered off */ - list_for_each_entry(chip, &nfc->chips, node) { - nand = &chip->nand; - for (i = 0; i < chip->nsels; i++) - nand_reset(nand, i); - } - - return 0; -} - -static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume); -#endif - -static struct platform_driver mtk_nfc_driver = { - .probe = mtk_nfc_probe, - .remove = mtk_nfc_remove, - .driver = { - .name = MTK_NAME, - .of_match_table = mtk_nfc_id_table, -#ifdef CONFIG_PM_SLEEP - .pm = &mtk_nfc_pm_ops, -#endif - }, -}; - -module_platform_driver(mtk_nfc_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Xiaolei Li "); -MODULE_DESCRIPTION("MTK Nand Flash Controller Driver"); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c deleted file mode 100644 index 87b5ee66e501..000000000000 --- a/drivers/mtd/nand/mxc_nand.c +++ /dev/null @@ -1,1966 +0,0 @@ -/* - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2008 Sascha Hauer, kernel@pengutronix.de - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define DRIVER_NAME "mxc_nand" - -/* Addresses for NFC registers */ -#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00) -#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04) -#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06) -#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08) -#define NFC_V1_V2_CONFIG (host->regs + 0x0a) -#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c) -#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e) -#define NFC_V1_V2_RSLTSPARE_AREA (host->regs + 0x10) -#define NFC_V1_V2_WRPROT (host->regs + 0x12) -#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) -#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) -#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20) -#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24) -#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28) -#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c) -#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22) -#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26) -#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a) -#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e) -#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18) -#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) -#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) - -#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) -#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2) -#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) -#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) -#define NFC_V1_V2_CONFIG1_BIG (1 << 5) -#define NFC_V1_V2_CONFIG1_RST (1 << 6) -#define NFC_V1_V2_CONFIG1_CE (1 << 7) -#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) -#define NFC_V2_CONFIG1_PPB(x) (((x) & 0x3) << 9) -#define NFC_V2_CONFIG1_FP_INT (1 << 11) - -#define NFC_V1_V2_CONFIG2_INT (1 << 15) - -/* - * Operation modes for the NFC. Valid for v1, v2 and v3 - * type controllers. - */ -#define NFC_CMD (1 << 0) -#define NFC_ADDR (1 << 1) -#define NFC_INPUT (1 << 2) -#define NFC_OUTPUT (1 << 3) -#define NFC_ID (1 << 4) -#define NFC_STATUS (1 << 5) - -#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00) -#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04) - -#define NFC_V3_CONFIG1 (host->regs_axi + 0x34) -#define NFC_V3_CONFIG1_SP_EN (1 << 0) -#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4) - -#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38) - -#define NFC_V3_LAUNCH (host->regs_axi + 0x40) - -#define NFC_V3_WRPROT (host->regs_ip + 0x0) -#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0) -#define NFC_V3_WRPROT_LOCK (1 << 1) -#define NFC_V3_WRPROT_UNLOCK (1 << 2) -#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) - -#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04) - -#define NFC_V3_CONFIG2 (host->regs_ip + 0x24) -#define NFC_V3_CONFIG2_PS_512 (0 << 0) -#define NFC_V3_CONFIG2_PS_2048 (1 << 0) -#define NFC_V3_CONFIG2_PS_4096 (2 << 0) -#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) -#define NFC_V3_CONFIG2_ECC_EN (1 << 3) -#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) -#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5) -#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) -#define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift) -#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12) -#define NFC_V3_CONFIG2_INT_MSK (1 << 15) -#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) -#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) - -#define NFC_V3_CONFIG3 (host->regs_ip + 0x28) -#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) -#define NFC_V3_CONFIG3_FW8 (1 << 3) -#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) -#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12) -#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) -#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) - -#define NFC_V3_IPC (host->regs_ip + 0x2C) -#define NFC_V3_IPC_CREQ (1 << 0) -#define NFC_V3_IPC_INT (1 << 31) - -#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) - -struct mxc_nand_host; - -struct mxc_nand_devtype_data { - void (*preset)(struct mtd_info *); - int (*read_page)(struct nand_chip *chip, void *buf, void *oob, bool ecc, - int page); - void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); - void (*send_addr)(struct mxc_nand_host *, uint16_t, int); - void (*send_page)(struct mtd_info *, unsigned int); - void (*send_read_id)(struct mxc_nand_host *); - uint16_t (*get_dev_status)(struct mxc_nand_host *); - int (*check_int)(struct mxc_nand_host *); - void (*irq_control)(struct mxc_nand_host *, int); - u32 (*get_ecc_status)(struct mxc_nand_host *); - const struct mtd_ooblayout_ops *ooblayout; - void (*select_chip)(struct mtd_info *mtd, int chip); - int (*setup_data_interface)(struct mtd_info *mtd, int csline, - const struct nand_data_interface *conf); - void (*enable_hwecc)(struct nand_chip *chip, bool enable); - - /* - * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked - * (CONFIG1:INT_MSK is set). To handle this the driver uses - * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK - */ - int irqpending_quirk; - int needs_ip; - - size_t regs_offset; - size_t spare0_offset; - size_t axi_offset; - - int spare_len; - int eccbytes; - int eccsize; - int ppb_shift; -}; - -struct mxc_nand_host { - struct nand_chip nand; - struct device *dev; - - void __iomem *spare0; - void __iomem *main_area0; - - void __iomem *base; - void __iomem *regs; - void __iomem *regs_axi; - void __iomem *regs_ip; - int status_request; - struct clk *clk; - int clk_act; - int irq; - int eccsize; - int used_oobsize; - int active_cs; - - struct completion op_completion; - - uint8_t *data_buf; - unsigned int buf_start; - - const struct mxc_nand_devtype_data *devtype_data; - struct mxc_nand_platform_data pdata; -}; - -static const char * const part_probes[] = { - "cmdlinepart", "RedBoot", "ofpart", NULL }; - -static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) -{ - int i; - u32 *t = trg; - const __iomem u32 *s = src; - - for (i = 0; i < (size >> 2); i++) - *t++ = __raw_readl(s++); -} - -static void memcpy16_fromio(void *trg, const void __iomem *src, size_t size) -{ - int i; - u16 *t = trg; - const __iomem u16 *s = src; - - /* We assume that src (IO) is always 32bit aligned */ - if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) { - memcpy32_fromio(trg, src, size); - return; - } - - for (i = 0; i < (size >> 1); i++) - *t++ = __raw_readw(s++); -} - -static inline void memcpy32_toio(void __iomem *trg, const void *src, int size) -{ - /* __iowrite32_copy use 32bit size values so divide by 4 */ - __iowrite32_copy(trg, src, size / 4); -} - -static void memcpy16_toio(void __iomem *trg, const void *src, int size) -{ - int i; - __iomem u16 *t = trg; - const u16 *s = src; - - /* We assume that trg (IO) is always 32bit aligned */ - if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) { - memcpy32_toio(trg, src, size); - return; - } - - for (i = 0; i < (size >> 1); i++) - __raw_writew(*s++, t++); -} - -/* - * The controller splits a page into data chunks of 512 bytes + partial oob. - * There are writesize / 512 such chunks, the size of the partial oob parts is - * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then - * contains additionally the byte lost by rounding (if any). - * This function handles the needed shuffling between host->data_buf (which - * holds a page in natural order, i.e. writesize bytes data + oobsize bytes - * spare) and the NFC buffer. - */ -static void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(this); - u16 i, oob_chunk_size; - u16 num_chunks = mtd->writesize / 512; - - u8 *d = buf; - u8 __iomem *s = host->spare0; - u16 sparebuf_size = host->devtype_data->spare_len; - - /* size of oob chunk for all but possibly the last one */ - oob_chunk_size = (host->used_oobsize / num_chunks) & ~1; - - if (bfrom) { - for (i = 0; i < num_chunks - 1; i++) - memcpy16_fromio(d + i * oob_chunk_size, - s + i * sparebuf_size, - oob_chunk_size); - - /* the last chunk */ - memcpy16_fromio(d + i * oob_chunk_size, - s + i * sparebuf_size, - host->used_oobsize - i * oob_chunk_size); - } else { - for (i = 0; i < num_chunks - 1; i++) - memcpy16_toio(&s[i * sparebuf_size], - &d[i * oob_chunk_size], - oob_chunk_size); - - /* the last chunk */ - memcpy16_toio(&s[i * sparebuf_size], - &d[i * oob_chunk_size], - host->used_oobsize - i * oob_chunk_size); - } -} - -/* - * MXC NANDFC can only perform full page+spare or spare-only read/write. When - * the upper layers perform a read/write buf operation, the saved column address - * is used to index into the full page. So usually this function is called with - * column == 0 (unless no column cycle is needed indicated by column == -1) - */ -static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - /* Write out column address, if necessary */ - if (column != -1) { - host->devtype_data->send_addr(host, column & 0xff, - page_addr == -1); - if (mtd->writesize > 512) - /* another col addr cycle for 2k page */ - host->devtype_data->send_addr(host, - (column >> 8) & 0xff, - false); - } - - /* Write out page address, if necessary */ - if (page_addr != -1) { - /* paddr_0 - p_addr_7 */ - host->devtype_data->send_addr(host, (page_addr & 0xff), false); - - if (mtd->writesize > 512) { - if (mtd->size >= 0x10000000) { - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, - false); - host->devtype_data->send_addr(host, - (page_addr >> 16) & 0xff, - true); - } else - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, true); - } else { - if (nand_chip->options & NAND_ROW_ADDR_3) { - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, - false); - host->devtype_data->send_addr(host, - (page_addr >> 16) & 0xff, - true); - } else - /* paddr_8 - paddr_15 */ - host->devtype_data->send_addr(host, - (page_addr >> 8) & 0xff, true); - } - } -} - -static int check_int_v3(struct mxc_nand_host *host) -{ - uint32_t tmp; - - tmp = readl(NFC_V3_IPC); - if (!(tmp & NFC_V3_IPC_INT)) - return 0; - - tmp &= ~NFC_V3_IPC_INT; - writel(tmp, NFC_V3_IPC); - - return 1; -} - -static int check_int_v1_v2(struct mxc_nand_host *host) -{ - uint32_t tmp; - - tmp = readw(NFC_V1_V2_CONFIG2); - if (!(tmp & NFC_V1_V2_CONFIG2_INT)) - return 0; - - if (!host->devtype_data->irqpending_quirk) - writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); - - return 1; -} - -static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) -{ - uint16_t tmp; - - tmp = readw(NFC_V1_V2_CONFIG1); - - if (activate) - tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK; - else - tmp |= NFC_V1_V2_CONFIG1_INT_MSK; - - writew(tmp, NFC_V1_V2_CONFIG1); -} - -static void irq_control_v3(struct mxc_nand_host *host, int activate) -{ - uint32_t tmp; - - tmp = readl(NFC_V3_CONFIG2); - - if (activate) - tmp &= ~NFC_V3_CONFIG2_INT_MSK; - else - tmp |= NFC_V3_CONFIG2_INT_MSK; - - writel(tmp, NFC_V3_CONFIG2); -} - -static void irq_control(struct mxc_nand_host *host, int activate) -{ - if (host->devtype_data->irqpending_quirk) { - if (activate) - enable_irq(host->irq); - else - disable_irq_nosync(host->irq); - } else { - host->devtype_data->irq_control(host, activate); - } -} - -static u32 get_ecc_status_v1(struct mxc_nand_host *host) -{ - return readw(NFC_V1_V2_ECC_STATUS_RESULT); -} - -static u32 get_ecc_status_v2(struct mxc_nand_host *host) -{ - return readl(NFC_V1_V2_ECC_STATUS_RESULT); -} - -static u32 get_ecc_status_v3(struct mxc_nand_host *host) -{ - return readl(NFC_V3_ECC_STATUS_RESULT); -} - -static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) -{ - struct mxc_nand_host *host = dev_id; - - if (!host->devtype_data->check_int(host)) - return IRQ_NONE; - - irq_control(host, 0); - - complete(&host->op_completion); - - return IRQ_HANDLED; -} - -/* This function polls the NANDFC to wait for the basic operation to - * complete by checking the INT bit of config2 register. - */ -static int wait_op_done(struct mxc_nand_host *host, int useirq) -{ - int ret = 0; - - /* - * If operation is already complete, don't bother to setup an irq or a - * loop. - */ - if (host->devtype_data->check_int(host)) - return 0; - - if (useirq) { - unsigned long timeout; - - reinit_completion(&host->op_completion); - - irq_control(host, 1); - - timeout = wait_for_completion_timeout(&host->op_completion, HZ); - if (!timeout && !host->devtype_data->check_int(host)) { - dev_dbg(host->dev, "timeout waiting for irq\n"); - ret = -ETIMEDOUT; - } - } else { - int max_retries = 8000; - int done; - - do { - udelay(1); - - done = host->devtype_data->check_int(host); - if (done) - break; - - } while (--max_retries); - - if (!done) { - dev_dbg(host->dev, "timeout polling for completion\n"); - ret = -ETIMEDOUT; - } - } - - WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq); - - return ret; -} - -static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) -{ - /* fill command */ - writel(cmd, NFC_V3_FLASH_CMD); - - /* send out command */ - writel(NFC_CMD, NFC_V3_LAUNCH); - - /* Wait for operation to complete */ - wait_op_done(host, useirq); -} - -/* This function issues the specified command to the NAND device and - * waits for completion. */ -static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) -{ - dev_dbg(host->dev, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); - - writew(cmd, NFC_V1_V2_FLASH_CMD); - writew(NFC_CMD, NFC_V1_V2_CONFIG2); - - if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) { - int max_retries = 100; - /* Reset completion is indicated by NFC_CONFIG2 */ - /* being set to 0 */ - while (max_retries-- > 0) { - if (readw(NFC_V1_V2_CONFIG2) == 0) { - break; - } - udelay(1); - } - if (max_retries < 0) - dev_dbg(host->dev, "%s: RESET failed\n", __func__); - } else { - /* Wait for operation to complete */ - wait_op_done(host, useirq); - } -} - -static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast) -{ - /* fill address */ - writel(addr, NFC_V3_FLASH_ADDR0); - - /* send out address */ - writel(NFC_ADDR, NFC_V3_LAUNCH); - - wait_op_done(host, 0); -} - -/* This function sends an address (or partial address) to the - * NAND device. The address is used to select the source/destination for - * a NAND command. */ -static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast) -{ - dev_dbg(host->dev, "send_addr(host, 0x%x %d)\n", addr, islast); - - writew(addr, NFC_V1_V2_FLASH_ADDR); - writew(NFC_ADDR, NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host, islast); -} - -static void send_page_v3(struct mtd_info *mtd, unsigned int ops) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - uint32_t tmp; - - tmp = readl(NFC_V3_CONFIG1); - tmp &= ~(7 << 4); - writel(tmp, NFC_V3_CONFIG1); - - /* transfer data from NFC ram to nand */ - writel(ops, NFC_V3_LAUNCH); - - wait_op_done(host, false); -} - -static void send_page_v2(struct mtd_info *mtd, unsigned int ops) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - /* NANDFC buffer 0 is used for page read/write */ - writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); - - writew(ops, NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host, true); -} - -static void send_page_v1(struct mtd_info *mtd, unsigned int ops) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - int bufs, i; - - if (mtd->writesize > 512) - bufs = 4; - else - bufs = 1; - - for (i = 0; i < bufs; i++) { - - /* NANDFC buffer 0 is used for page read/write */ - writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); - - writew(ops, NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host, true); - } -} - -static void send_read_id_v3(struct mxc_nand_host *host) -{ - /* Read ID into main buffer */ - writel(NFC_ID, NFC_V3_LAUNCH); - - wait_op_done(host, true); - - memcpy32_fromio(host->data_buf, host->main_area0, 16); -} - -/* Request the NANDFC to perform a read of the NAND device ID. */ -static void send_read_id_v1_v2(struct mxc_nand_host *host) -{ - /* NANDFC buffer 0 is used for device ID output */ - writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); - - writew(NFC_ID, NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host, true); - - memcpy32_fromio(host->data_buf, host->main_area0, 16); -} - -static uint16_t get_dev_status_v3(struct mxc_nand_host *host) -{ - writew(NFC_STATUS, NFC_V3_LAUNCH); - wait_op_done(host, true); - - return readl(NFC_V3_CONFIG1) >> 16; -} - -/* This function requests the NANDFC to perform a read of the - * NAND device status and returns the current status. */ -static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) -{ - void __iomem *main_buf = host->main_area0; - uint32_t store; - uint16_t ret; - - writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); - - /* - * The device status is stored in main_area0. To - * prevent corruption of the buffer save the value - * and restore it afterwards. - */ - store = readl(main_buf); - - writew(NFC_STATUS, NFC_V1_V2_CONFIG2); - wait_op_done(host, true); - - ret = readw(main_buf); - - writel(store, main_buf); - - return ret; -} - -static void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - uint16_t config1; - - if (chip->ecc.mode != NAND_ECC_HW) - return; - - config1 = readw(NFC_V1_V2_CONFIG1); - - if (enable) - config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - else - config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN; - - writew(config1, NFC_V1_V2_CONFIG1); -} - -static void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - uint32_t config2; - - if (chip->ecc.mode != NAND_ECC_HW) - return; - - config2 = readl(NFC_V3_CONFIG2); - - if (enable) - config2 |= NFC_V3_CONFIG2_ECC_EN; - else - config2 &= ~NFC_V3_CONFIG2_ECC_EN; - - writel(config2, NFC_V3_CONFIG2); -} - -/* This functions is used by upper layer to checks if device is ready */ -static int mxc_nand_dev_ready(struct mtd_info *mtd) -{ - /* - * NFC handles R/B internally. Therefore, this function - * always returns status as ready. - */ - return 1; -} - -static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, - bool ecc, int page) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct mxc_nand_host *host = nand_get_controller_data(chip); - unsigned int bitflips_corrected = 0; - int no_subpages; - int i; - - host->devtype_data->enable_hwecc(chip, ecc); - - host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); - mxc_do_addr_cycle(mtd, 0, page); - - if (mtd->writesize > 512) - host->devtype_data->send_cmd(host, NAND_CMD_READSTART, true); - - no_subpages = mtd->writesize >> 9; - - for (i = 0; i < no_subpages; i++) { - uint16_t ecc_stats; - - /* NANDFC buffer 0 is used for page read/write */ - writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); - - writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host, true); - - ecc_stats = get_ecc_status_v1(host); - - ecc_stats >>= 2; - - if (buf && ecc) { - switch (ecc_stats & 0x3) { - case 0: - default: - break; - case 1: - mtd->ecc_stats.corrected++; - bitflips_corrected = 1; - break; - case 2: - mtd->ecc_stats.failed++; - break; - } - } - } - - if (buf) - memcpy32_fromio(buf, host->main_area0, mtd->writesize); - if (oob) - copy_spare(mtd, true, oob); - - return bitflips_corrected; -} - -static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf, - void *oob, bool ecc, int page) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct mxc_nand_host *host = nand_get_controller_data(chip); - unsigned int max_bitflips = 0; - u32 ecc_stat, err; - int no_subpages; - u8 ecc_bit_mask, err_limit; - - host->devtype_data->enable_hwecc(chip, ecc); - - host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); - mxc_do_addr_cycle(mtd, 0, page); - - if (mtd->writesize > 512) - host->devtype_data->send_cmd(host, - NAND_CMD_READSTART, true); - - host->devtype_data->send_page(mtd, NFC_OUTPUT); - - if (buf) - memcpy32_fromio(buf, host->main_area0, mtd->writesize); - if (oob) - copy_spare(mtd, true, oob); - - ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; - err_limit = (host->eccsize == 4) ? 0x4 : 0x8; - - no_subpages = mtd->writesize >> 9; - - ecc_stat = host->devtype_data->get_ecc_status(host); - - do { - err = ecc_stat & ecc_bit_mask; - if (err > err_limit) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += err; - max_bitflips = max_t(unsigned int, max_bitflips, err); - } - - ecc_stat >>= 4; - } while (--no_subpages); - - return max_bitflips; -} - -static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - void *oob_buf; - - if (oob_required) - oob_buf = chip->oob_poi; - else - oob_buf = NULL; - - return host->devtype_data->read_page(chip, buf, oob_buf, 1, page); -} - -static int mxc_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - void *oob_buf; - - if (oob_required) - oob_buf = chip->oob_poi; - else - oob_buf = NULL; - - return host->devtype_data->read_page(chip, buf, oob_buf, 0, page); -} - -static int mxc_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - - return host->devtype_data->read_page(chip, NULL, chip->oob_poi, 0, - page); -} - -static int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf, - bool ecc, int page) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct mxc_nand_host *host = nand_get_controller_data(chip); - - host->devtype_data->enable_hwecc(chip, ecc); - - host->devtype_data->send_cmd(host, NAND_CMD_SEQIN, false); - mxc_do_addr_cycle(mtd, 0, page); - - memcpy32_toio(host->main_area0, buf, mtd->writesize); - copy_spare(mtd, false, chip->oob_poi); - - host->devtype_data->send_page(mtd, NFC_INPUT); - host->devtype_data->send_cmd(host, NAND_CMD_PAGEPROG, true); - mxc_do_addr_cycle(mtd, 0, page); - - return 0; -} - -static int mxc_nand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - return mxc_nand_write_page(chip, buf, true, page); -} - -static int mxc_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - return mxc_nand_write_page(chip, buf, false, page); -} - -static int mxc_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - - memset(host->data_buf, 0xff, mtd->writesize); - - return mxc_nand_write_page(chip, host->data_buf, false, page); -} - -static u_char mxc_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - uint8_t ret; - - /* Check for status request */ - if (host->status_request) - return host->devtype_data->get_dev_status(host) & 0xFF; - - if (nand_chip->options & NAND_BUSWIDTH_16) { - /* only take the lower byte of each word */ - ret = *(uint16_t *)(host->data_buf + host->buf_start); - - host->buf_start += 2; - } else { - ret = *(uint8_t *)(host->data_buf + host->buf_start); - host->buf_start++; - } - - dev_dbg(host->dev, "%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start); - return ret; -} - -static uint16_t mxc_nand_read_word(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - uint16_t ret; - - ret = *(uint16_t *)(host->data_buf + host->buf_start); - host->buf_start += 2; - - return ret; -} - -/* Write data of length len to buffer buf. The data to be - * written on NAND Flash is first copied to RAMbuffer. After the Data Input - * Operation by the NFC, the data is written to NAND Flash */ -static void mxc_nand_write_buf(struct mtd_info *mtd, - const u_char *buf, int len) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - u16 col = host->buf_start; - int n = mtd->oobsize + mtd->writesize - col; - - n = min(n, len); - - memcpy(host->data_buf + col, buf, n); - - host->buf_start += n; -} - -/* Read the data buffer from the NAND Flash. To read the data from NAND - * Flash first the data output cycle is initiated by the NFC, which copies - * the data to RAMbuffer. This data of length len is then copied to buffer buf. - */ -static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - u16 col = host->buf_start; - int n = mtd->oobsize + mtd->writesize - col; - - n = min(n, len); - - memcpy(buf, host->data_buf + col, n); - - host->buf_start += n; -} - -/* This function is used by upper layer for select and - * deselect of the NAND chip */ -static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - if (chip == -1) { - /* Disable the NFC clock */ - if (host->clk_act) { - clk_disable_unprepare(host->clk); - host->clk_act = 0; - } - return; - } - - if (!host->clk_act) { - /* Enable the NFC clock */ - clk_prepare_enable(host->clk); - host->clk_act = 1; - } -} - -static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - if (chip == -1) { - /* Disable the NFC clock */ - if (host->clk_act) { - clk_disable_unprepare(host->clk); - host->clk_act = 0; - } - return; - } - - if (!host->clk_act) { - /* Enable the NFC clock */ - clk_prepare_enable(host->clk); - host->clk_act = 1; - } - - host->active_cs = chip; - writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); -} - -#define MXC_V1_ECCBYTES 5 - -static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - - if (section >= nand_chip->ecc.steps) - return -ERANGE; - - oobregion->offset = (section * 16) + 6; - oobregion->length = MXC_V1_ECCBYTES; - - return 0; -} - -static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - - if (section > nand_chip->ecc.steps) - return -ERANGE; - - if (!section) { - if (mtd->writesize <= 512) { - oobregion->offset = 0; - oobregion->length = 5; - } else { - oobregion->offset = 2; - oobregion->length = 4; - } - } else { - oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6; - if (section < nand_chip->ecc.steps) - oobregion->length = (section * 16) + 6 - - oobregion->offset; - else - oobregion->length = mtd->oobsize - oobregion->offset; - } - - return 0; -} - -static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = { - .ecc = mxc_v1_ooblayout_ecc, - .free = mxc_v1_ooblayout_free, -}; - -static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; - - if (section >= nand_chip->ecc.steps) - return -ERANGE; - - oobregion->offset = (section * stepsize) + 7; - oobregion->length = nand_chip->ecc.bytes; - - return 0; -} - -static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; - - if (section >= nand_chip->ecc.steps) - return -ERANGE; - - if (!section) { - if (mtd->writesize <= 512) { - oobregion->offset = 0; - oobregion->length = 5; - } else { - oobregion->offset = 2; - oobregion->length = 4; - } - } else { - oobregion->offset = section * stepsize; - oobregion->length = 7; - } - - return 0; -} - -static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = { - .ecc = mxc_v2_ooblayout_ecc, - .free = mxc_v2_ooblayout_free, -}; - -/* - * v2 and v3 type controllers can do 4bit or 8bit ecc depending - * on how much oob the nand chip has. For 8bit ecc we need at least - * 26 bytes of oob data per 512 byte block. - */ -static int get_eccsize(struct mtd_info *mtd) -{ - int oobbytes_per_512 = 0; - - oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize; - - if (oobbytes_per_512 < 26) - return 4; - else - return 8; -} - -static void preset_v1(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - uint16_t config1 = 0; - - if (nand_chip->ecc.mode == NAND_ECC_HW && mtd->writesize) - config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - - if (!host->devtype_data->irqpending_quirk) - config1 |= NFC_V1_V2_CONFIG1_INT_MSK; - - host->eccsize = 1; - - writew(config1, NFC_V1_V2_CONFIG1); - /* preset operation */ - - /* Unlock the internal RAM Buffer */ - writew(0x2, NFC_V1_V2_CONFIG); - - /* Blocks to be unlocked */ - writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); - writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); - - /* Unlock Block Command for given address range */ - writew(0x4, NFC_V1_V2_WRPROT); -} - -static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd, int csline, - const struct nand_data_interface *conf) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - int tRC_min_ns, tRC_ps, ret; - unsigned long rate, rate_round; - const struct nand_sdr_timings *timings; - u16 config1; - - timings = nand_get_sdr_timings(conf); - if (IS_ERR(timings)) - return -ENOTSUPP; - - config1 = readw(NFC_V1_V2_CONFIG1); - - tRC_min_ns = timings->tRC_min / 1000; - rate = 1000000000 / tRC_min_ns; - - /* - * For tRC < 30ns we have to use EDO mode. In this case the controller - * does one access per clock cycle. Otherwise the controller does one - * access in two clock cycles, thus we have to double the rate to the - * controller. - */ - if (tRC_min_ns < 30) { - rate_round = clk_round_rate(host->clk, rate); - config1 |= NFC_V2_CONFIG1_ONE_CYCLE; - tRC_ps = 1000000000 / (rate_round / 1000); - } else { - rate *= 2; - rate_round = clk_round_rate(host->clk, rate); - config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE; - tRC_ps = 1000000000 / (rate_round / 1000 / 2); - } - - /* - * The timing values compared against are from the i.MX25 Automotive - * datasheet, Table 50. NFC Timing Parameters - */ - if (timings->tCLS_min > tRC_ps - 1000 || - timings->tCLH_min > tRC_ps - 2000 || - timings->tCS_min > tRC_ps - 1000 || - timings->tCH_min > tRC_ps - 2000 || - timings->tWP_min > tRC_ps - 1500 || - timings->tALS_min > tRC_ps || - timings->tALH_min > tRC_ps - 3000 || - timings->tDS_min > tRC_ps || - timings->tDH_min > tRC_ps - 5000 || - timings->tWC_min > 2 * tRC_ps || - timings->tWH_min > tRC_ps - 2500 || - timings->tRR_min > 6 * tRC_ps || - timings->tRP_min > 3 * tRC_ps / 2 || - timings->tRC_min > 2 * tRC_ps || - timings->tREH_min > (tRC_ps / 2) - 2500) { - dev_dbg(host->dev, "Timing out of bounds\n"); - return -EINVAL; - } - - if (csline == NAND_DATA_IFACE_CHECK_ONLY) - return 0; - - ret = clk_set_rate(host->clk, rate); - if (ret) - return ret; - - writew(config1, NFC_V1_V2_CONFIG1); - - dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round, - config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" : - "normal"); - - return 0; -} - -static void preset_v2(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - uint16_t config1 = 0; - - config1 |= NFC_V2_CONFIG1_FP_INT; - - if (!host->devtype_data->irqpending_quirk) - config1 |= NFC_V1_V2_CONFIG1_INT_MSK; - - if (mtd->writesize) { - uint16_t pages_per_block = mtd->erasesize / mtd->writesize; - - if (nand_chip->ecc.mode == NAND_ECC_HW) - config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - - host->eccsize = get_eccsize(mtd); - if (host->eccsize == 4) - config1 |= NFC_V2_CONFIG1_ECC_MODE_4; - - config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6); - } else { - host->eccsize = 1; - } - - writew(config1, NFC_V1_V2_CONFIG1); - /* preset operation */ - - /* Unlock the internal RAM Buffer */ - writew(0x2, NFC_V1_V2_CONFIG); - - /* Blocks to be unlocked */ - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); - - /* Unlock Block Command for given address range */ - writew(0x4, NFC_V1_V2_WRPROT); -} - -static void preset_v3(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(chip); - uint32_t config2, config3; - int i, addr_phases; - - writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1); - writel(NFC_V3_IPC_CREQ, NFC_V3_IPC); - - /* Unlock the internal RAM Buffer */ - writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, - NFC_V3_WRPROT); - - /* Blocks to be unlocked */ - for (i = 0; i < NAND_MAX_CHIPS; i++) - writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2)); - - writel(0, NFC_V3_IPC); - - config2 = NFC_V3_CONFIG2_ONE_CYCLE | - NFC_V3_CONFIG2_2CMD_PHASES | - NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | - NFC_V3_CONFIG2_ST_CMD(0x70) | - NFC_V3_CONFIG2_INT_MSK | - NFC_V3_CONFIG2_NUM_ADDR_PHASE0; - - addr_phases = fls(chip->pagemask) >> 3; - - if (mtd->writesize == 2048) { - config2 |= NFC_V3_CONFIG2_PS_2048; - config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); - } else if (mtd->writesize == 4096) { - config2 |= NFC_V3_CONFIG2_PS_4096; - config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); - } else { - config2 |= NFC_V3_CONFIG2_PS_512; - config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1); - } - - if (mtd->writesize) { - if (chip->ecc.mode == NAND_ECC_HW) - config2 |= NFC_V3_CONFIG2_ECC_EN; - - config2 |= NFC_V3_CONFIG2_PPB( - ffs(mtd->erasesize / mtd->writesize) - 6, - host->devtype_data->ppb_shift); - host->eccsize = get_eccsize(mtd); - if (host->eccsize == 8) - config2 |= NFC_V3_CONFIG2_ECC_MODE_8; - } - - writel(config2, NFC_V3_CONFIG2); - - config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) | - NFC_V3_CONFIG3_NO_SDMA | - NFC_V3_CONFIG3_RBB_MODE | - NFC_V3_CONFIG3_SBB(6) | /* Reset default */ - NFC_V3_CONFIG3_ADD_OP(0); - - if (!(chip->options & NAND_BUSWIDTH_16)) - config3 |= NFC_V3_CONFIG3_FW8; - - writel(config3, NFC_V3_CONFIG3); - - writel(0, NFC_V3_DELAY_LINE); -} - -/* Used by the upper layer to write command to NAND Flash for - * different operations to be carried out on NAND Flash */ -static void mxc_nand_command(struct mtd_info *mtd, unsigned command, - int column, int page_addr) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - dev_dbg(host->dev, "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", - command, column, page_addr); - - /* Reset command state information */ - host->status_request = false; - - /* Command pre-processing step */ - switch (command) { - case NAND_CMD_RESET: - host->devtype_data->preset(mtd); - host->devtype_data->send_cmd(host, command, false); - break; - - case NAND_CMD_STATUS: - host->buf_start = 0; - host->status_request = true; - - host->devtype_data->send_cmd(host, command, true); - WARN_ONCE(column != -1 || page_addr != -1, - "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", - command, column, page_addr); - mxc_do_addr_cycle(mtd, column, page_addr); - break; - - case NAND_CMD_READID: - host->devtype_data->send_cmd(host, command, true); - mxc_do_addr_cycle(mtd, column, page_addr); - host->devtype_data->send_read_id(host); - host->buf_start = 0; - break; - - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - host->devtype_data->send_cmd(host, command, false); - WARN_ONCE(column != -1, - "Unexpected column value (cmd=%u, col=%d)\n", - command, column); - mxc_do_addr_cycle(mtd, column, page_addr); - - break; - case NAND_CMD_PARAM: - host->devtype_data->send_cmd(host, command, false); - mxc_do_addr_cycle(mtd, column, page_addr); - host->devtype_data->send_page(mtd, NFC_OUTPUT); - memcpy32_fromio(host->data_buf, host->main_area0, 512); - host->buf_start = 0; - break; - default: - WARN_ONCE(1, "Unimplemented command (cmd=%u)\n", - command); - break; - } -} - -static int mxc_nand_onfi_set_features(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - int i; - - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - - host->buf_start = 0; - - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - chip->write_byte(mtd, subfeature_param[i]); - - memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); - host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false); - mxc_do_addr_cycle(mtd, addr, -1); - host->devtype_data->send_page(mtd, NFC_INPUT); - - return 0; -} - -static int mxc_nand_onfi_get_features(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - int i; - - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - - host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false); - mxc_do_addr_cycle(mtd, addr, -1); - host->devtype_data->send_page(mtd, NFC_OUTPUT); - memcpy32_fromio(host->data_buf, host->main_area0, 512); - host->buf_start = 0; - - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - *subfeature_param++ = chip->read_byte(mtd); - - return 0; -} - -/* - * The generic flash bbt decriptors overlap with our ecc - * hardware, so define some i.MX specific ones. - */ -static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; -static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 0, - .len = 4, - .veroffs = 4, - .maxblocks = 4, - .pattern = bbt_pattern, -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 0, - .len = 4, - .veroffs = 4, - .maxblocks = 4, - .pattern = mirror_pattern, -}; - -/* v1 + irqpending_quirk: i.MX21 */ -static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { - .preset = preset_v1, - .read_page = mxc_nand_read_page_v1, - .send_cmd = send_cmd_v1_v2, - .send_addr = send_addr_v1_v2, - .send_page = send_page_v1, - .send_read_id = send_read_id_v1_v2, - .get_dev_status = get_dev_status_v1_v2, - .check_int = check_int_v1_v2, - .irq_control = irq_control_v1_v2, - .get_ecc_status = get_ecc_status_v1, - .ooblayout = &mxc_v1_ooblayout_ops, - .select_chip = mxc_nand_select_chip_v1_v3, - .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, - .irqpending_quirk = 1, - .needs_ip = 0, - .regs_offset = 0xe00, - .spare0_offset = 0x800, - .spare_len = 16, - .eccbytes = 3, - .eccsize = 1, -}; - -/* v1 + !irqpending_quirk: i.MX27, i.MX31 */ -static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { - .preset = preset_v1, - .read_page = mxc_nand_read_page_v1, - .send_cmd = send_cmd_v1_v2, - .send_addr = send_addr_v1_v2, - .send_page = send_page_v1, - .send_read_id = send_read_id_v1_v2, - .get_dev_status = get_dev_status_v1_v2, - .check_int = check_int_v1_v2, - .irq_control = irq_control_v1_v2, - .get_ecc_status = get_ecc_status_v1, - .ooblayout = &mxc_v1_ooblayout_ops, - .select_chip = mxc_nand_select_chip_v1_v3, - .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, - .irqpending_quirk = 0, - .needs_ip = 0, - .regs_offset = 0xe00, - .spare0_offset = 0x800, - .axi_offset = 0, - .spare_len = 16, - .eccbytes = 3, - .eccsize = 1, -}; - -/* v21: i.MX25, i.MX35 */ -static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { - .preset = preset_v2, - .read_page = mxc_nand_read_page_v2_v3, - .send_cmd = send_cmd_v1_v2, - .send_addr = send_addr_v1_v2, - .send_page = send_page_v2, - .send_read_id = send_read_id_v1_v2, - .get_dev_status = get_dev_status_v1_v2, - .check_int = check_int_v1_v2, - .irq_control = irq_control_v1_v2, - .get_ecc_status = get_ecc_status_v2, - .ooblayout = &mxc_v2_ooblayout_ops, - .select_chip = mxc_nand_select_chip_v2, - .setup_data_interface = mxc_nand_v2_setup_data_interface, - .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, - .irqpending_quirk = 0, - .needs_ip = 0, - .regs_offset = 0x1e00, - .spare0_offset = 0x1000, - .axi_offset = 0, - .spare_len = 64, - .eccbytes = 9, - .eccsize = 0, -}; - -/* v3.2a: i.MX51 */ -static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { - .preset = preset_v3, - .read_page = mxc_nand_read_page_v2_v3, - .send_cmd = send_cmd_v3, - .send_addr = send_addr_v3, - .send_page = send_page_v3, - .send_read_id = send_read_id_v3, - .get_dev_status = get_dev_status_v3, - .check_int = check_int_v3, - .irq_control = irq_control_v3, - .get_ecc_status = get_ecc_status_v3, - .ooblayout = &mxc_v2_ooblayout_ops, - .select_chip = mxc_nand_select_chip_v1_v3, - .enable_hwecc = mxc_nand_enable_hwecc_v3, - .irqpending_quirk = 0, - .needs_ip = 1, - .regs_offset = 0, - .spare0_offset = 0x1000, - .axi_offset = 0x1e00, - .spare_len = 64, - .eccbytes = 0, - .eccsize = 0, - .ppb_shift = 7, -}; - -/* v3.2b: i.MX53 */ -static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { - .preset = preset_v3, - .read_page = mxc_nand_read_page_v2_v3, - .send_cmd = send_cmd_v3, - .send_addr = send_addr_v3, - .send_page = send_page_v3, - .send_read_id = send_read_id_v3, - .get_dev_status = get_dev_status_v3, - .check_int = check_int_v3, - .irq_control = irq_control_v3, - .get_ecc_status = get_ecc_status_v3, - .ooblayout = &mxc_v2_ooblayout_ops, - .select_chip = mxc_nand_select_chip_v1_v3, - .enable_hwecc = mxc_nand_enable_hwecc_v3, - .irqpending_quirk = 0, - .needs_ip = 1, - .regs_offset = 0, - .spare0_offset = 0x1000, - .axi_offset = 0x1e00, - .spare_len = 64, - .eccbytes = 0, - .eccsize = 0, - .ppb_shift = 8, -}; - -static inline int is_imx21_nfc(struct mxc_nand_host *host) -{ - return host->devtype_data == &imx21_nand_devtype_data; -} - -static inline int is_imx27_nfc(struct mxc_nand_host *host) -{ - return host->devtype_data == &imx27_nand_devtype_data; -} - -static inline int is_imx25_nfc(struct mxc_nand_host *host) -{ - return host->devtype_data == &imx25_nand_devtype_data; -} - -static inline int is_imx51_nfc(struct mxc_nand_host *host) -{ - return host->devtype_data == &imx51_nand_devtype_data; -} - -static inline int is_imx53_nfc(struct mxc_nand_host *host) -{ - return host->devtype_data == &imx53_nand_devtype_data; -} - -static const struct platform_device_id mxcnd_devtype[] = { - { - .name = "imx21-nand", - .driver_data = (kernel_ulong_t) &imx21_nand_devtype_data, - }, { - .name = "imx27-nand", - .driver_data = (kernel_ulong_t) &imx27_nand_devtype_data, - }, { - .name = "imx25-nand", - .driver_data = (kernel_ulong_t) &imx25_nand_devtype_data, - }, { - .name = "imx51-nand", - .driver_data = (kernel_ulong_t) &imx51_nand_devtype_data, - }, { - .name = "imx53-nand", - .driver_data = (kernel_ulong_t) &imx53_nand_devtype_data, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, mxcnd_devtype); - -#ifdef CONFIG_OF -static const struct of_device_id mxcnd_dt_ids[] = { - { - .compatible = "fsl,imx21-nand", - .data = &imx21_nand_devtype_data, - }, { - .compatible = "fsl,imx27-nand", - .data = &imx27_nand_devtype_data, - }, { - .compatible = "fsl,imx25-nand", - .data = &imx25_nand_devtype_data, - }, { - .compatible = "fsl,imx51-nand", - .data = &imx51_nand_devtype_data, - }, { - .compatible = "fsl,imx53-nand", - .data = &imx53_nand_devtype_data, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mxcnd_dt_ids); - -static int __init mxcnd_probe_dt(struct mxc_nand_host *host) -{ - struct device_node *np = host->dev->of_node; - const struct of_device_id *of_id = - of_match_device(mxcnd_dt_ids, host->dev); - - if (!np) - return 1; - - host->devtype_data = of_id->data; - - return 0; -} -#else -static int __init mxcnd_probe_dt(struct mxc_nand_host *host) -{ - return 1; -} -#endif - -static int mxcnd_probe(struct platform_device *pdev) -{ - struct nand_chip *this; - struct mtd_info *mtd; - struct mxc_nand_host *host; - struct resource *res; - int err = 0; - - /* Allocate memory for MTD device structure and private data */ - host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host), - GFP_KERNEL); - if (!host) - return -ENOMEM; - - /* allocate a temporary buffer for the nand_scan_ident() */ - host->data_buf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL); - if (!host->data_buf) - return -ENOMEM; - - host->dev = &pdev->dev; - /* structures must be linked */ - this = &host->nand; - mtd = nand_to_mtd(this); - mtd->dev.parent = &pdev->dev; - mtd->name = DRIVER_NAME; - - /* 50 us command delay time */ - this->chip_delay = 5; - - nand_set_controller_data(this, host); - nand_set_flash_node(this, pdev->dev.of_node), - this->dev_ready = mxc_nand_dev_ready; - this->cmdfunc = mxc_nand_command; - this->read_byte = mxc_nand_read_byte; - this->read_word = mxc_nand_read_word; - this->write_buf = mxc_nand_write_buf; - this->read_buf = mxc_nand_read_buf; - this->onfi_set_features = mxc_nand_onfi_set_features; - this->onfi_get_features = mxc_nand_onfi_get_features; - - host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) - return PTR_ERR(host->clk); - - err = mxcnd_probe_dt(host); - if (err > 0) { - struct mxc_nand_platform_data *pdata = - dev_get_platdata(&pdev->dev); - if (pdata) { - host->pdata = *pdata; - host->devtype_data = (struct mxc_nand_devtype_data *) - pdev->id_entry->driver_data; - } else { - err = -ENODEV; - } - } - if (err < 0) - return err; - - this->setup_data_interface = host->devtype_data->setup_data_interface; - - if (host->devtype_data->needs_ip) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->regs_ip = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->regs_ip)) - return PTR_ERR(host->regs_ip); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - } else { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - } - - host->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->base)) - return PTR_ERR(host->base); - - host->main_area0 = host->base; - - if (host->devtype_data->regs_offset) - host->regs = host->base + host->devtype_data->regs_offset; - host->spare0 = host->base + host->devtype_data->spare0_offset; - if (host->devtype_data->axi_offset) - host->regs_axi = host->base + host->devtype_data->axi_offset; - - this->ecc.bytes = host->devtype_data->eccbytes; - host->eccsize = host->devtype_data->eccsize; - - this->select_chip = host->devtype_data->select_chip; - this->ecc.size = 512; - mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); - - if (host->pdata.hw_ecc) { - this->ecc.mode = NAND_ECC_HW; - } else { - this->ecc.mode = NAND_ECC_SOFT; - this->ecc.algo = NAND_ECC_HAMMING; - } - - /* NAND bus width determines access functions used by upper layer */ - if (host->pdata.width == 2) - this->options |= NAND_BUSWIDTH_16; - - /* update flash based bbt */ - if (host->pdata.flash_bbt) - this->bbt_options |= NAND_BBT_USE_FLASH; - - init_completion(&host->op_completion); - - host->irq = platform_get_irq(pdev, 0); - if (host->irq < 0) - return host->irq; - - /* - * Use host->devtype_data->irq_control() here instead of irq_control() - * because we must not disable_irq_nosync without having requested the - * irq. - */ - host->devtype_data->irq_control(host, 0); - - err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq, - 0, DRIVER_NAME, host); - if (err) - return err; - - err = clk_prepare_enable(host->clk); - if (err) - return err; - host->clk_act = 1; - - /* - * Now that we "own" the interrupt make sure the interrupt mask bit is - * cleared on i.MX21. Otherwise we can't read the interrupt status bit - * on this machine. - */ - if (host->devtype_data->irqpending_quirk) { - disable_irq_nosync(host->irq); - host->devtype_data->irq_control(host, 1); - } - - /* first scan to find the device and get the page size */ - err = nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL); - if (err) - goto escan; - - switch (this->ecc.mode) { - case NAND_ECC_HW: - this->ecc.read_page = mxc_nand_read_page; - this->ecc.read_page_raw = mxc_nand_read_page_raw; - this->ecc.read_oob = mxc_nand_read_oob; - this->ecc.write_page = mxc_nand_write_page_ecc; - this->ecc.write_page_raw = mxc_nand_write_page_raw; - this->ecc.write_oob = mxc_nand_write_oob; - break; - - case NAND_ECC_SOFT: - break; - - default: - err = -EINVAL; - goto escan; - } - - if (this->bbt_options & NAND_BBT_USE_FLASH) { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; - } - - /* allocate the right size buffer now */ - devm_kfree(&pdev->dev, (void *)host->data_buf); - host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize, - GFP_KERNEL); - if (!host->data_buf) { - err = -ENOMEM; - goto escan; - } - - /* Call preset again, with correct writesize this time */ - host->devtype_data->preset(mtd); - - if (!this->ecc.bytes) { - if (host->eccsize == 8) - this->ecc.bytes = 18; - else if (host->eccsize == 4) - this->ecc.bytes = 9; - } - - /* - * Experimentation shows that i.MX NFC can only handle up to 218 oob - * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare() - * into copying invalid data to/from the spare IO buffer, as this - * might cause ECC data corruption when doing sub-page write to a - * partially written page. - */ - host->used_oobsize = min(mtd->oobsize, 218U); - - if (this->ecc.mode == NAND_ECC_HW) { - if (is_imx21_nfc(host) || is_imx27_nfc(host)) - this->ecc.strength = 1; - else - this->ecc.strength = (host->eccsize == 4) ? 4 : 8; - } - - /* second phase scan */ - err = nand_scan_tail(mtd); - if (err) - goto escan; - - /* Register the partitions */ - mtd_device_parse_register(mtd, part_probes, - NULL, - host->pdata.parts, - host->pdata.nr_parts); - - platform_set_drvdata(pdev, host); - - return 0; - -escan: - if (host->clk_act) - clk_disable_unprepare(host->clk); - - return err; -} - -static int mxcnd_remove(struct platform_device *pdev) -{ - struct mxc_nand_host *host = platform_get_drvdata(pdev); - - nand_release(nand_to_mtd(&host->nand)); - if (host->clk_act) - clk_disable_unprepare(host->clk); - - return 0; -} - -static struct platform_driver mxcnd_driver = { - .driver = { - .name = DRIVER_NAME, - .of_match_table = of_match_ptr(mxcnd_dt_ids), - }, - .id_table = mxcnd_devtype, - .probe = mxcnd_probe, - .remove = mxcnd_remove, -}; -module_platform_driver(mxcnd_driver); - -MODULE_AUTHOR("Freescale Semiconductor, Inc."); -MODULE_DESCRIPTION("MXC NAND MTD driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/nand_amd.c deleted file mode 100644 index 22f060f38123..000000000000 --- a/drivers/mtd/nand/nand_amd.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2017 Free Electrons - * Copyright (C) 2017 NextThing Co - * - * Author: Boris Brezillon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include - -static void amd_nand_decode_id(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - nand_decode_ext_id(chip); - - /* - * Check for Spansion/AMD ID + repeating 5th, 6th byte since - * some Spansion chips have erasesize that conflicts with size - * listed in nand_ids table. - * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) - */ - if (chip->id.data[4] != 0x00 && chip->id.data[5] == 0x00 && - chip->id.data[6] == 0x00 && chip->id.data[7] == 0x00 && - mtd->writesize == 512) { - mtd->erasesize = 128 * 1024; - mtd->erasesize <<= ((chip->id.data[3] & 0x03) << 1); - } -} - -static int amd_nand_init(struct nand_chip *chip) -{ - if (nand_is_slc(chip)) - chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; - - return 0; -} - -const struct nand_manufacturer_ops amd_nand_manuf_ops = { - .detect = amd_nand_decode_id, - .init = amd_nand_init, -}; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c deleted file mode 100644 index e70ca16a5118..000000000000 --- a/drivers/mtd/nand/nand_base.c +++ /dev/null @@ -1,6582 +0,0 @@ -/* - * Overview: - * This is the generic MTD driver for NAND flash devices. It should be - * capable of working with almost all NAND chips currently available. - * - * Additional technical information is available on - * http://www.linux-mtd.infradead.org/doc/nand.html - * - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * 2002-2006 Thomas Gleixner (tglx@linutronix.de) - * - * Credits: - * David Woodhouse for adding multichip support - * - * Aleph One Ltd. and Toby Churchill Ltd. for supporting the - * rework for 2K page size chips - * - * TODO: - * Enable cached programming for 2k page size chips - * Check, if mtd->ecctype should be set to MTD_ECC_HW - * if we have HW ECC support. - * BBT table is not serialized, has to be fixed - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int nand_get_device(struct mtd_info *mtd, int new_state); - -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops); - -/* Define default oob placement schemes for large and small page devices */ -static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section > 1) - return -ERANGE; - - if (!section) { - oobregion->offset = 0; - if (mtd->oobsize == 16) - oobregion->length = 4; - else - oobregion->length = 3; - } else { - if (mtd->oobsize == 8) - return -ERANGE; - - oobregion->offset = 6; - oobregion->length = ecc->total - 4; - } - - return 0; -} - -static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 1) - return -ERANGE; - - if (mtd->oobsize == 16) { - if (section) - return -ERANGE; - - oobregion->length = 8; - oobregion->offset = 8; - } else { - oobregion->length = 2; - if (!section) - oobregion->offset = 3; - else - oobregion->offset = 6; - } - - return 0; -} - -const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = { - .ecc = nand_ooblayout_ecc_sp, - .free = nand_ooblayout_free_sp, -}; -EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops); - -static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section || !ecc->total) - return -ERANGE; - - oobregion->length = ecc->total; - oobregion->offset = mtd->oobsize - oobregion->length; - - return 0; -} - -static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section) - return -ERANGE; - - oobregion->length = mtd->oobsize - ecc->total - 2; - oobregion->offset = 2; - - return 0; -} - -const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = { - .ecc = nand_ooblayout_ecc_lp, - .free = nand_ooblayout_free_lp, -}; -EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops); - -/* - * Support the old "large page" layout used for 1-bit Hamming ECC where ECC - * are placed at a fixed offset. - */ -static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section) - return -ERANGE; - - switch (mtd->oobsize) { - case 64: - oobregion->offset = 40; - break; - case 128: - oobregion->offset = 80; - break; - default: - return -EINVAL; - } - - oobregion->length = ecc->total; - if (oobregion->offset + oobregion->length > mtd->oobsize) - return -ERANGE; - - return 0; -} - -static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ecc_offset = 0; - - if (section < 0 || section > 1) - return -ERANGE; - - switch (mtd->oobsize) { - case 64: - ecc_offset = 40; - break; - case 128: - ecc_offset = 80; - break; - default: - return -EINVAL; - } - - if (section == 0) { - oobregion->offset = 2; - oobregion->length = ecc_offset - 2; - } else { - oobregion->offset = ecc_offset + ecc->total; - oobregion->length = mtd->oobsize - oobregion->offset; - } - - return 0; -} - -static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = { - .ecc = nand_ooblayout_ecc_lp_hamming, - .free = nand_ooblayout_free_lp_hamming, -}; - -static int check_offs_len(struct mtd_info *mtd, - loff_t ofs, uint64_t len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int ret = 0; - - /* Start address must align on block boundary */ - if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { - pr_debug("%s: unaligned address\n", __func__); - ret = -EINVAL; - } - - /* Length must align on block boundary */ - if (len & ((1ULL << chip->phys_erase_shift) - 1)) { - pr_debug("%s: length not block aligned\n", __func__); - ret = -EINVAL; - } - - return ret; -} - -/** - * nand_release_device - [GENERIC] release chip - * @mtd: MTD device structure - * - * Release chip lock and wake up anyone waiting on the device. - */ -static void nand_release_device(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - /* Release the controller and the chip */ - spin_lock(&chip->controller->lock); - chip->controller->active = NULL; - chip->state = FL_READY; - wake_up(&chip->controller->wq); - spin_unlock(&chip->controller->lock); -} - -/** - * nand_read_byte - [DEFAULT] read one byte from the chip - * @mtd: MTD device structure - * - * Default read function for 8bit buswidth - */ -static uint8_t nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - return readb(chip->IO_ADDR_R); -} - -/** - * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip - * @mtd: MTD device structure - * - * Default read function for 16bit buswidth with endianness conversion. - * - */ -static uint8_t nand_read_byte16(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); -} - -/** - * nand_read_word - [DEFAULT] read one word from the chip - * @mtd: MTD device structure - * - * Default read function for 16bit buswidth without endianness conversion. - */ -static u16 nand_read_word(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - return readw(chip->IO_ADDR_R); -} - -/** - * nand_select_chip - [DEFAULT] control CE line - * @mtd: MTD device structure - * @chipnr: chipnumber to select, -1 for deselect - * - * Default select function for 1 chip devices. - */ -static void nand_select_chip(struct mtd_info *mtd, int chipnr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - switch (chipnr) { - case -1: - chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); - break; - case 0: - break; - - default: - BUG(); - } -} - -/** - * nand_write_byte - [DEFAULT] write single byte to chip - * @mtd: MTD device structure - * @byte: value to write - * - * Default function to write a byte to I/O[7:0] - */ -static void nand_write_byte(struct mtd_info *mtd, uint8_t byte) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - chip->write_buf(mtd, &byte, 1); -} - -/** - * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16 - * @mtd: MTD device structure - * @byte: value to write - * - * Default function to write a byte to I/O[7:0] on a 16-bit wide chip. - */ -static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - uint16_t word = byte; - - /* - * It's not entirely clear what should happen to I/O[15:8] when writing - * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads: - * - * When the host supports a 16-bit bus width, only data is - * transferred at the 16-bit width. All address and command line - * transfers shall use only the lower 8-bits of the data bus. During - * command transfers, the host may place any value on the upper - * 8-bits of the data bus. During address transfers, the host shall - * set the upper 8-bits of the data bus to 00h. - * - * One user of the write_byte callback is nand_onfi_set_features. The - * four parameters are specified to be written to I/O[7:0], but this is - * neither an address nor a command transfer. Let's assume a 0 on the - * upper I/O lines is OK. - */ - chip->write_buf(mtd, (uint8_t *)&word, 2); -} - -/** - * nand_write_buf - [DEFAULT] write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - * - * Default write function for 8bit buswidth. - */ -static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - iowrite8_rep(chip->IO_ADDR_W, buf, len); -} - -/** - * nand_read_buf - [DEFAULT] read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - * - * Default read function for 8bit buswidth. - */ -static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - ioread8_rep(chip->IO_ADDR_R, buf, len); -} - -/** - * nand_write_buf16 - [DEFAULT] write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - * - * Default write function for 16bit buswidth. - */ -static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u16 *p = (u16 *) buf; - - iowrite16_rep(chip->IO_ADDR_W, p, len >> 1); -} - -/** - * nand_read_buf16 - [DEFAULT] read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - * - * Default read function for 16bit buswidth. - */ -static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u16 *p = (u16 *) buf; - - ioread16_rep(chip->IO_ADDR_R, p, len >> 1); -} - -/** - * nand_block_bad - [DEFAULT] Read bad block marker from the chip - * @mtd: MTD device structure - * @ofs: offset from device start - * - * Check, if the block is bad. - */ -static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) -{ - int page, page_end, res; - struct nand_chip *chip = mtd_to_nand(mtd); - u8 bad; - - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - ofs += mtd->erasesize - mtd->writesize; - - page = (int)(ofs >> chip->page_shift) & chip->pagemask; - page_end = page + (chip->bbt_options & NAND_BBT_SCAN2NDPAGE ? 2 : 1); - - for (; page < page_end; page++) { - res = chip->ecc.read_oob(mtd, chip, page); - if (res) - return res; - - bad = chip->oob_poi[chip->badblockpos]; - - if (likely(chip->badblockbits == 8)) - res = bad != 0xFF; - else - res = hweight8(bad) < chip->badblockbits; - if (res) - return res; - } - - return 0; -} - -/** - * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker - * @mtd: MTD device structure - * @ofs: offset from device start - * - * This is the default implementation, which can be overridden by a hardware - * specific driver. It provides the details for writing a bad block marker to a - * block. - */ -static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtd_oob_ops ops; - uint8_t buf[2] = { 0, 0 }; - int ret = 0, res, i = 0; - - memset(&ops, 0, sizeof(ops)); - ops.oobbuf = buf; - ops.ooboffs = chip->badblockpos; - if (chip->options & NAND_BUSWIDTH_16) { - ops.ooboffs &= ~0x01; - ops.len = ops.ooblen = 2; - } else { - ops.len = ops.ooblen = 1; - } - ops.mode = MTD_OPS_PLACE_OOB; - - /* Write to first/last page(s) if necessary */ - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - ofs += mtd->erasesize - mtd->writesize; - do { - res = nand_do_write_oob(mtd, ofs, &ops); - if (!ret) - ret = res; - - i++; - ofs += mtd->writesize; - } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); - - return ret; -} - -/** - * nand_block_markbad_lowlevel - mark a block bad - * @mtd: MTD device structure - * @ofs: offset from device start - * - * This function performs the generic NAND bad block marking steps (i.e., bad - * block table(s) and/or marker(s)). We only allow the hardware driver to - * specify how to write bad block markers to OOB (chip->block_markbad). - * - * We try operations in the following order: - * - * (1) erase the affected block, to allow OOB marker to be written cleanly - * (2) write bad block marker to OOB area of affected block (unless flag - * NAND_BBT_NO_OOB_BBM is present) - * (3) update the BBT - * - * Note that we retain the first error encountered in (2) or (3), finish the - * procedures, and dump the error in the end. -*/ -static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int res, ret = 0; - - if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { - struct erase_info einfo; - - /* Attempt erase before marking OOB */ - memset(&einfo, 0, sizeof(einfo)); - einfo.mtd = mtd; - einfo.addr = ofs; - einfo.len = 1ULL << chip->phys_erase_shift; - nand_erase_nand(mtd, &einfo, 0); - - /* Write bad block marker to OOB */ - nand_get_device(mtd, FL_WRITING); - ret = chip->block_markbad(mtd, ofs); - nand_release_device(mtd); - } - - /* Mark block bad in BBT */ - if (chip->bbt) { - res = nand_markbad_bbt(mtd, ofs); - if (!ret) - ret = res; - } - - if (!ret) - mtd->ecc_stats.badblocks++; - - return ret; -} - -/** - * nand_check_wp - [GENERIC] check if the chip is write protected - * @mtd: MTD device structure - * - * Check, if the device is write protected. The function expects, that the - * device is already selected. - */ -static int nand_check_wp(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u8 status; - int ret; - - /* Broken xD cards report WP despite being writable */ - if (chip->options & NAND_BROKEN_XD) - return 0; - - /* Check the WP bit */ - ret = nand_status_op(chip, &status); - if (ret) - return ret; - - return status & NAND_STATUS_WP ? 0 : 1; -} - -/** - * nand_block_isreserved - [GENERIC] Check if a block is marked reserved. - * @mtd: MTD device structure - * @ofs: offset from device start - * - * Check if the block is marked as reserved. - */ -static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (!chip->bbt) - return 0; - /* Return info from the table */ - return nand_isreserved_bbt(mtd, ofs); -} - -/** - * nand_block_checkbad - [GENERIC] Check if a block is marked bad - * @mtd: MTD device structure - * @ofs: offset from device start - * @allowbbt: 1, if its allowed to access the bbt area - * - * Check, if the block is bad. Either by reading the bad block table or - * calling of the scan function. - */ -static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (!chip->bbt) - return chip->block_bad(mtd, ofs); - - /* Return info from the table */ - return nand_isbad_bbt(mtd, ofs, allowbbt); -} - -/** - * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands. - * @mtd: MTD device structure - * @timeo: Timeout - * - * Helper function for nand_wait_ready used when needing to wait in interrupt - * context. - */ -static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int i; - - /* Wait for the device to get ready */ - for (i = 0; i < timeo; i++) { - if (chip->dev_ready(mtd)) - break; - touch_softlockup_watchdog(); - mdelay(1); - } -} - -/** - * nand_wait_ready - [GENERIC] Wait for the ready pin after commands. - * @mtd: MTD device structure - * - * Wait for the ready pin after a command, and warn if a timeout occurs. - */ -void nand_wait_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - unsigned long timeo = 400; - - if (in_interrupt() || oops_in_progress) - return panic_nand_wait_ready(mtd, timeo); - - /* Wait until command is processed or timeout occurs */ - timeo = jiffies + msecs_to_jiffies(timeo); - do { - if (chip->dev_ready(mtd)) - return; - cond_resched(); - } while (time_before(jiffies, timeo)); - - if (!chip->dev_ready(mtd)) - pr_warn_ratelimited("timeout while waiting for chip to become ready\n"); -} -EXPORT_SYMBOL_GPL(nand_wait_ready); - -/** - * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands. - * @mtd: MTD device structure - * @timeo: Timeout in ms - * - * Wait for status ready (i.e. command done) or timeout. - */ -static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - timeo = jiffies + msecs_to_jiffies(timeo); - do { - u8 status; - - ret = nand_read_data_op(chip, &status, sizeof(status), true); - if (ret) - return; - - if (status & NAND_STATUS_READY) - break; - touch_softlockup_watchdog(); - } while (time_before(jiffies, timeo)); -}; - -/** - * nand_soft_waitrdy - Poll STATUS reg until RDY bit is set to 1 - * @chip: NAND chip structure - * @timeout_ms: Timeout in ms - * - * Poll the STATUS register using ->exec_op() until the RDY bit becomes 1. - * If that does not happen whitin the specified timeout, -ETIMEDOUT is - * returned. - * - * This helper is intended to be used when the controller does not have access - * to the NAND R/B pin. - * - * Be aware that calling this helper from an ->exec_op() implementation means - * ->exec_op() must be re-entrant. - * - * Return 0 if the NAND chip is ready, a negative error otherwise. - */ -int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) -{ - u8 status = 0; - int ret; - - if (!chip->exec_op) - return -ENOTSUPP; - - ret = nand_status_op(chip, NULL); - if (ret) - return ret; - - timeout_ms = jiffies + msecs_to_jiffies(timeout_ms); - do { - ret = nand_read_data_op(chip, &status, sizeof(status), true); - if (ret) - break; - - if (status & NAND_STATUS_READY) - break; - - /* - * Typical lowest execution time for a tR on most NANDs is 10us, - * use this as polling delay before doing something smarter (ie. - * deriving a delay from the timeout value, timeout_ms/ratio). - */ - udelay(10); - } while (time_before(jiffies, timeout_ms)); - - /* - * We have to exit READ_STATUS mode in order to read real data on the - * bus in case the WAITRDY instruction is preceding a DATA_IN - * instruction. - */ - nand_exit_status_op(chip); - - if (ret) - return ret; - - return status & NAND_STATUS_READY ? 0 : -ETIMEDOUT; -}; -EXPORT_SYMBOL_GPL(nand_soft_waitrdy); - -/** - * nand_command - [DEFAULT] Send command to NAND device - * @mtd: MTD device structure - * @command: the command to be sent - * @column: the column address for this command, -1 if none - * @page_addr: the page address for this command, -1 if none - * - * Send command to NAND device. This function is used for small page devices - * (512 Bytes per page). - */ -static void nand_command(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; - - /* Write out the command to the device */ - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->writesize) { - /* OOB area */ - column -= mtd->writesize; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - chip->cmd_ctrl(mtd, readcmd, ctrl); - ctrl &= ~NAND_CTRL_CHANGE; - } - if (command != NAND_CMD_NONE) - chip->cmd_ctrl(mtd, command, ctrl); - - /* Address cycle, when necessary */ - ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - chip->cmd_ctrl(mtd, column, ctrl); - ctrl &= ~NAND_CTRL_CHANGE; - } - if (page_addr != -1) { - chip->cmd_ctrl(mtd, page_addr, ctrl); - ctrl &= ~NAND_CTRL_CHANGE; - chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); - if (chip->options & NAND_ROW_ADDR_3) - chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); - } - chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - /* - * Program and erase have their own busy handlers status and sequential - * in needs no delay - */ - switch (command) { - - case NAND_CMD_NONE: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_STATUS: - case NAND_CMD_READID: - case NAND_CMD_SET_FEATURES: - return; - - case NAND_CMD_RESET: - if (chip->dev_ready) - break; - udelay(chip->chip_delay); - chip->cmd_ctrl(mtd, NAND_CMD_STATUS, - NAND_CTRL_CLE | NAND_CTRL_CHANGE); - chip->cmd_ctrl(mtd, - NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ - nand_wait_status_ready(mtd, 250); - return; - - /* This applies to read commands */ - case NAND_CMD_READ0: - /* - * READ0 is sometimes used to exit GET STATUS mode. When this - * is the case no address cycles are requested, and we can use - * this information to detect that we should not wait for the - * device to be ready. - */ - if (column == -1 && page_addr == -1) - return; - - default: - /* - * If we don't have access to the busy pin, we apply the given - * command delay - */ - if (!chip->dev_ready) { - udelay(chip->chip_delay); - return; - } - } - /* - * Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. - */ - ndelay(100); - - nand_wait_ready(mtd); -} - -static void nand_ccs_delay(struct nand_chip *chip) -{ - /* - * The controller already takes care of waiting for tCCS when the RNDIN - * or RNDOUT command is sent, return directly. - */ - if (!(chip->options & NAND_WAIT_TCCS)) - return; - - /* - * Wait tCCS_min if it is correctly defined, otherwise wait 500ns - * (which should be safe for all NANDs). - */ - if (chip->setup_data_interface) - ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000); - else - ndelay(500); -} - -/** - * nand_command_lp - [DEFAULT] Send command to NAND large page device - * @mtd: MTD device structure - * @command: the command to be sent - * @column: the column address for this command, -1 if none - * @page_addr: the page address for this command, -1 if none - * - * Send command to NAND device. This is the version for the new large page - * devices. We don't have the separate regions as we have in the small page - * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. - */ -static void nand_command_lp(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - - /* Emulate NAND_CMD_READOOB */ - if (command == NAND_CMD_READOOB) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - /* Command latch cycle */ - if (command != NAND_CMD_NONE) - chip->cmd_ctrl(mtd, command, - NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); - - if (column != -1 || page_addr != -1) { - int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; - - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - chip->cmd_ctrl(mtd, column, ctrl); - ctrl &= ~NAND_CTRL_CHANGE; - - /* Only output a single addr cycle for 8bits opcodes. */ - if (!nand_opcode_8bits(command)) - chip->cmd_ctrl(mtd, column >> 8, ctrl); - } - if (page_addr != -1) { - chip->cmd_ctrl(mtd, page_addr, ctrl); - chip->cmd_ctrl(mtd, page_addr >> 8, - NAND_NCE | NAND_ALE); - if (chip->options & NAND_ROW_ADDR_3) - chip->cmd_ctrl(mtd, page_addr >> 16, - NAND_NCE | NAND_ALE); - } - } - chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - /* - * Program and erase have their own busy handlers status, sequential - * in and status need no delay. - */ - switch (command) { - - case NAND_CMD_NONE: - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_STATUS: - case NAND_CMD_READID: - case NAND_CMD_SET_FEATURES: - return; - - case NAND_CMD_RNDIN: - nand_ccs_delay(chip); - return; - - case NAND_CMD_RESET: - if (chip->dev_ready) - break; - udelay(chip->chip_delay); - chip->cmd_ctrl(mtd, NAND_CMD_STATUS, - NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); - chip->cmd_ctrl(mtd, NAND_CMD_NONE, - NAND_NCE | NAND_CTRL_CHANGE); - /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ - nand_wait_status_ready(mtd, 250); - return; - - case NAND_CMD_RNDOUT: - /* No ready / busy check necessary */ - chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, - NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); - chip->cmd_ctrl(mtd, NAND_CMD_NONE, - NAND_NCE | NAND_CTRL_CHANGE); - - nand_ccs_delay(chip); - return; - - case NAND_CMD_READ0: - /* - * READ0 is sometimes used to exit GET STATUS mode. When this - * is the case no address cycles are requested, and we can use - * this information to detect that READSTART should not be - * issued. - */ - if (column == -1 && page_addr == -1) - return; - - chip->cmd_ctrl(mtd, NAND_CMD_READSTART, - NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); - chip->cmd_ctrl(mtd, NAND_CMD_NONE, - NAND_NCE | NAND_CTRL_CHANGE); - - /* This applies to read commands */ - default: - /* - * If we don't have access to the busy pin, we apply the given - * command delay. - */ - if (!chip->dev_ready) { - udelay(chip->chip_delay); - return; - } - } - - /* - * Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. - */ - ndelay(100); - - nand_wait_ready(mtd); -} - -/** - * panic_nand_get_device - [GENERIC] Get chip for selected access - * @chip: the nand chip descriptor - * @mtd: MTD device structure - * @new_state: the state which is requested - * - * Used when in panic, no locks are taken. - */ -static void panic_nand_get_device(struct nand_chip *chip, - struct mtd_info *mtd, int new_state) -{ - /* Hardware controller shared among independent devices */ - chip->controller->active = chip; - chip->state = new_state; -} - -/** - * nand_get_device - [GENERIC] Get chip for selected access - * @mtd: MTD device structure - * @new_state: the state which is requested - * - * Get the device and lock it for exclusive access - */ -static int -nand_get_device(struct mtd_info *mtd, int new_state) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - spinlock_t *lock = &chip->controller->lock; - wait_queue_head_t *wq = &chip->controller->wq; - DECLARE_WAITQUEUE(wait, current); -retry: - spin_lock(lock); - - /* Hardware controller shared among independent devices */ - if (!chip->controller->active) - chip->controller->active = chip; - - if (chip->controller->active == chip && chip->state == FL_READY) { - chip->state = new_state; - spin_unlock(lock); - return 0; - } - if (new_state == FL_PM_SUSPENDED) { - if (chip->controller->active->state == FL_PM_SUSPENDED) { - chip->state = FL_PM_SUSPENDED; - spin_unlock(lock); - return 0; - } - } - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(wq, &wait); - spin_unlock(lock); - schedule(); - remove_wait_queue(wq, &wait); - goto retry; -} - -/** - * panic_nand_wait - [GENERIC] wait until the command is done - * @mtd: MTD device structure - * @chip: NAND chip structure - * @timeo: timeout - * - * Wait for command done. This is a helper function for nand_wait used when - * we are in interrupt context. May happen when in panic and trying to write - * an oops through mtdoops. - */ -static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, - unsigned long timeo) -{ - int i; - for (i = 0; i < timeo; i++) { - if (chip->dev_ready) { - if (chip->dev_ready(mtd)) - break; - } else { - int ret; - u8 status; - - ret = nand_read_data_op(chip, &status, sizeof(status), - true); - if (ret) - return; - - if (status & NAND_STATUS_READY) - break; - } - mdelay(1); - } -} - -/** - * nand_wait - [DEFAULT] wait until the command is done - * @mtd: MTD device structure - * @chip: NAND chip structure - * - * Wait for command done. This applies to erase and program only. - */ -static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) -{ - - unsigned long timeo = 400; - u8 status; - int ret; - - /* - * Apply this short delay always to ensure that we do wait tWB in any - * case on any machine. - */ - ndelay(100); - - ret = nand_status_op(chip, NULL); - if (ret) - return ret; - - if (in_interrupt() || oops_in_progress) - panic_nand_wait(mtd, chip, timeo); - else { - timeo = jiffies + msecs_to_jiffies(timeo); - do { - if (chip->dev_ready) { - if (chip->dev_ready(mtd)) - break; - } else { - ret = nand_read_data_op(chip, &status, - sizeof(status), true); - if (ret) - return ret; - - if (status & NAND_STATUS_READY) - break; - } - cond_resched(); - } while (time_before(jiffies, timeo)); - } - - ret = nand_read_data_op(chip, &status, sizeof(status), true); - if (ret) - return ret; - - /* This can happen if in case of timeout or buggy dev_ready */ - WARN_ON(!(status & NAND_STATUS_READY)); - return status; -} - -/** - * nand_reset_data_interface - Reset data interface and timings - * @chip: The NAND chip - * @chipnr: Internal die id - * - * Reset the Data interface and timings to ONFI mode 0. - * - * Returns 0 for success or negative error code otherwise. - */ -static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - if (!chip->setup_data_interface) - return 0; - - /* - * The ONFI specification says: - * " - * To transition from NV-DDR or NV-DDR2 to the SDR data - * interface, the host shall use the Reset (FFh) command - * using SDR timing mode 0. A device in any timing mode is - * required to recognize Reset (FFh) command issued in SDR - * timing mode 0. - * " - * - * Configure the data interface in SDR mode and set the - * timings to timing mode 0. - */ - - onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); - ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); - if (ret) - pr_err("Failed to configure data interface to SDR timing mode 0\n"); - - return ret; -} - -/** - * nand_setup_data_interface - Setup the best data interface and timings - * @chip: The NAND chip - * @chipnr: Internal die id - * - * Find and configure the best data interface and NAND timings supported by - * the chip and the driver. - * First tries to retrieve supported timing modes from ONFI information, - * and if the NAND chip does not support ONFI, relies on the - * ->onfi_timing_mode_default specified in the nand_ids table. - * - * Returns 0 for success or negative error code otherwise. - */ -static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - if (!chip->setup_data_interface) - return 0; - - /* - * Ensure the timing mode has been changed on the chip side - * before changing timings on the controller side. - */ - if (chip->onfi_version && - (le16_to_cpu(chip->onfi_params.opt_cmd) & - ONFI_OPT_CMD_SET_GET_FEATURES)) { - u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { - chip->onfi_timing_mode_default, - }; - - ret = chip->onfi_set_features(mtd, chip, - ONFI_FEATURE_ADDR_TIMING_MODE, - tmode_param); - if (ret) - goto err; - } - - ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); -err: - return ret; -} - -/** - * nand_init_data_interface - find the best data interface and timings - * @chip: The NAND chip - * - * Find the best data interface and NAND timings supported by the chip - * and the driver. - * First tries to retrieve supported timing modes from ONFI information, - * and if the NAND chip does not support ONFI, relies on the - * ->onfi_timing_mode_default specified in the nand_ids table. After this - * function nand_chip->data_interface is initialized with the best timing mode - * available. - * - * Returns 0 for success or negative error code otherwise. - */ -static int nand_init_data_interface(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int modes, mode, ret; - - if (!chip->setup_data_interface) - return 0; - - /* - * First try to identify the best timings from ONFI parameters and - * if the NAND does not support ONFI, fallback to the default ONFI - * timing mode. - */ - modes = onfi_get_async_timing_mode(chip); - if (modes == ONFI_TIMING_MODE_UNKNOWN) { - if (!chip->onfi_timing_mode_default) - return 0; - - modes = GENMASK(chip->onfi_timing_mode_default, 0); - } - - - for (mode = fls(modes) - 1; mode >= 0; mode--) { - ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode); - if (ret) - continue; - - /* - * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the - * controller supports the requested timings. - */ - ret = chip->setup_data_interface(mtd, - NAND_DATA_IFACE_CHECK_ONLY, - &chip->data_interface); - if (!ret) { - chip->onfi_timing_mode_default = mode; - break; - } - } - - return 0; -} - -/** - * nand_fill_column_cycles - fill the column cycles of an address - * @chip: The NAND chip - * @addrs: Array of address cycles to fill - * @offset_in_page: The offset in the page - * - * Fills the first or the first two bytes of the @addrs field depending - * on the NAND bus width and the page size. - * - * Returns the number of cycles needed to encode the column, or a negative - * error code in case one of the arguments is invalid. - */ -static int nand_fill_column_cycles(struct nand_chip *chip, u8 *addrs, - unsigned int offset_in_page) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - /* Make sure the offset is less than the actual page size. */ - if (offset_in_page > mtd->writesize + mtd->oobsize) - return -EINVAL; - - /* - * On small page NANDs, there's a dedicated command to access the OOB - * area, and the column address is relative to the start of the OOB - * area, not the start of the page. Asjust the address accordingly. - */ - if (mtd->writesize <= 512 && offset_in_page >= mtd->writesize) - offset_in_page -= mtd->writesize; - - /* - * The offset in page is expressed in bytes, if the NAND bus is 16-bit - * wide, then it must be divided by 2. - */ - if (chip->options & NAND_BUSWIDTH_16) { - if (WARN_ON(offset_in_page % 2)) - return -EINVAL; - - offset_in_page /= 2; - } - - addrs[0] = offset_in_page; - - /* - * Small page NANDs use 1 cycle for the columns, while large page NANDs - * need 2 - */ - if (mtd->writesize <= 512) - return 1; - - addrs[1] = offset_in_page >> 8; - - return 2; -} - -static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page, - unsigned int offset_in_page, void *buf, - unsigned int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - u8 addrs[4]; - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_READ0, 0), - NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), - PSEC_TO_NSEC(sdr->tRR_min)), - NAND_OP_DATA_IN(len, buf, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - int ret; - - /* Drop the DATA_IN instruction if len is set to 0. */ - if (!len) - op.ninstrs--; - - if (offset_in_page >= mtd->writesize) - instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB; - else if (offset_in_page >= 256 && - !(chip->options & NAND_BUSWIDTH_16)) - instrs[0].ctx.cmd.opcode = NAND_CMD_READ1; - - ret = nand_fill_column_cycles(chip, addrs, offset_in_page); - if (ret < 0) - return ret; - - addrs[1] = page; - addrs[2] = page >> 8; - - if (chip->options & NAND_ROW_ADDR_3) { - addrs[3] = page >> 16; - instrs[1].ctx.addr.naddrs++; - } - - return nand_exec_op(chip, &op); -} - -static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, - unsigned int offset_in_page, void *buf, - unsigned int len) -{ - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - u8 addrs[5]; - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_READ0, 0), - NAND_OP_ADDR(4, addrs, 0), - NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), - PSEC_TO_NSEC(sdr->tRR_min)), - NAND_OP_DATA_IN(len, buf, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - int ret; - - /* Drop the DATA_IN instruction if len is set to 0. */ - if (!len) - op.ninstrs--; - - ret = nand_fill_column_cycles(chip, addrs, offset_in_page); - if (ret < 0) - return ret; - - addrs[2] = page; - addrs[3] = page >> 8; - - if (chip->options & NAND_ROW_ADDR_3) { - addrs[4] = page >> 16; - instrs[1].ctx.addr.naddrs++; - } - - return nand_exec_op(chip, &op); -} - -/** - * nand_read_page_op - Do a READ PAGE operation - * @chip: The NAND chip - * @page: page to read - * @offset_in_page: offset within the page - * @buf: buffer used to store the data - * @len: length of the buffer - * - * This function issues a READ PAGE operation. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_read_page_op(struct nand_chip *chip, unsigned int page, - unsigned int offset_in_page, void *buf, unsigned int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (len && !buf) - return -EINVAL; - - if (offset_in_page + len > mtd->writesize + mtd->oobsize) - return -EINVAL; - - if (chip->exec_op) { - if (mtd->writesize > 512) - return nand_lp_exec_read_page_op(chip, page, - offset_in_page, buf, - len); - - return nand_sp_exec_read_page_op(chip, page, offset_in_page, - buf, len); - } - - chip->cmdfunc(mtd, NAND_CMD_READ0, offset_in_page, page); - if (len) - chip->read_buf(mtd, buf, len); - - return 0; -} -EXPORT_SYMBOL_GPL(nand_read_page_op); - -/** - * nand_read_param_page_op - Do a READ PARAMETER PAGE operation - * @chip: The NAND chip - * @page: parameter page to read - * @buf: buffer used to store the data - * @len: length of the buffer - * - * This function issues a READ PARAMETER PAGE operation. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, - unsigned int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - unsigned int i; - u8 *p = buf; - - if (len && !buf) - return -EINVAL; - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_PARAM, 0), - NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), - PSEC_TO_NSEC(sdr->tRR_min)), - NAND_OP_8BIT_DATA_IN(len, buf, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - /* Drop the DATA_IN instruction if len is set to 0. */ - if (!len) - op.ninstrs--; - - return nand_exec_op(chip, &op); - } - - chip->cmdfunc(mtd, NAND_CMD_PARAM, page, -1); - for (i = 0; i < len; i++) - p[i] = chip->read_byte(mtd); - - return 0; -} - -/** - * nand_change_read_column_op - Do a CHANGE READ COLUMN operation - * @chip: The NAND chip - * @offset_in_page: offset within the page - * @buf: buffer used to store the data - * @len: length of the buffer - * @force_8bit: force 8-bit bus access - * - * This function issues a CHANGE READ COLUMN operation. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_change_read_column_op(struct nand_chip *chip, - unsigned int offset_in_page, void *buf, - unsigned int len, bool force_8bit) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (len && !buf) - return -EINVAL; - - if (offset_in_page + len > mtd->writesize + mtd->oobsize) - return -EINVAL; - - /* Small page NANDs do not support column change. */ - if (mtd->writesize <= 512) - return -ENOTSUPP; - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - u8 addrs[2] = {}; - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_RNDOUT, 0), - NAND_OP_ADDR(2, addrs, 0), - NAND_OP_CMD(NAND_CMD_RNDOUTSTART, - PSEC_TO_NSEC(sdr->tCCS_min)), - NAND_OP_DATA_IN(len, buf, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - int ret; - - ret = nand_fill_column_cycles(chip, addrs, offset_in_page); - if (ret < 0) - return ret; - - /* Drop the DATA_IN instruction if len is set to 0. */ - if (!len) - op.ninstrs--; - - instrs[3].ctx.data.force_8bit = force_8bit; - - return nand_exec_op(chip, &op); - } - - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset_in_page, -1); - if (len) - chip->read_buf(mtd, buf, len); - - return 0; -} -EXPORT_SYMBOL_GPL(nand_change_read_column_op); - -/** - * nand_read_oob_op - Do a READ OOB operation - * @chip: The NAND chip - * @page: page to read - * @offset_in_oob: offset within the OOB area - * @buf: buffer used to store the data - * @len: length of the buffer - * - * This function issues a READ OOB operation. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_read_oob_op(struct nand_chip *chip, unsigned int page, - unsigned int offset_in_oob, void *buf, unsigned int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (len && !buf) - return -EINVAL; - - if (offset_in_oob + len > mtd->oobsize) - return -EINVAL; - - if (chip->exec_op) - return nand_read_page_op(chip, page, - mtd->writesize + offset_in_oob, - buf, len); - - chip->cmdfunc(mtd, NAND_CMD_READOOB, offset_in_oob, page); - if (len) - chip->read_buf(mtd, buf, len); - - return 0; -} -EXPORT_SYMBOL_GPL(nand_read_oob_op); - -static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, - unsigned int offset_in_page, const void *buf, - unsigned int len, bool prog) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - u8 addrs[5] = {}; - struct nand_op_instr instrs[] = { - /* - * The first instruction will be dropped if we're dealing - * with a large page NAND and adjusted if we're dealing - * with a small page NAND and the page offset is > 255. - */ - NAND_OP_CMD(NAND_CMD_READ0, 0), - NAND_OP_CMD(NAND_CMD_SEQIN, 0), - NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)), - NAND_OP_DATA_OUT(len, buf, 0), - NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); - int ret; - u8 status; - - if (naddrs < 0) - return naddrs; - - addrs[naddrs++] = page; - addrs[naddrs++] = page >> 8; - if (chip->options & NAND_ROW_ADDR_3) - addrs[naddrs++] = page >> 16; - - instrs[2].ctx.addr.naddrs = naddrs; - - /* Drop the last two instructions if we're not programming the page. */ - if (!prog) { - op.ninstrs -= 2; - /* Also drop the DATA_OUT instruction if empty. */ - if (!len) - op.ninstrs--; - } - - if (mtd->writesize <= 512) { - /* - * Small pages need some more tweaking: we have to adjust the - * first instruction depending on the page offset we're trying - * to access. - */ - if (offset_in_page >= mtd->writesize) - instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB; - else if (offset_in_page >= 256 && - !(chip->options & NAND_BUSWIDTH_16)) - instrs[0].ctx.cmd.opcode = NAND_CMD_READ1; - } else { - /* - * Drop the first command if we're dealing with a large page - * NAND. - */ - op.instrs++; - op.ninstrs--; - } - - ret = nand_exec_op(chip, &op); - if (!prog || ret) - return ret; - - ret = nand_status_op(chip, &status); - if (ret) - return ret; - - return status; -} - -/** - * nand_prog_page_begin_op - starts a PROG PAGE operation - * @chip: The NAND chip - * @page: page to write - * @offset_in_page: offset within the page - * @buf: buffer containing the data to write to the page - * @len: length of the buffer - * - * This function issues the first half of a PROG PAGE operation. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, - unsigned int offset_in_page, const void *buf, - unsigned int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (len && !buf) - return -EINVAL; - - if (offset_in_page + len > mtd->writesize + mtd->oobsize) - return -EINVAL; - - if (chip->exec_op) - return nand_exec_prog_page_op(chip, page, offset_in_page, buf, - len, false); - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); - - if (buf) - chip->write_buf(mtd, buf, len); - - return 0; -} -EXPORT_SYMBOL_GPL(nand_prog_page_begin_op); - -/** - * nand_prog_page_end_op - ends a PROG PAGE operation - * @chip: The NAND chip - * - * This function issues the second half of a PROG PAGE operation. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_prog_page_end_op(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - u8 status; - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_PAGEPROG, - PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - ret = nand_exec_op(chip, &op); - if (ret) - return ret; - - ret = nand_status_op(chip, &status); - if (ret) - return ret; - } else { - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - ret = chip->waitfunc(mtd, chip); - if (ret < 0) - return ret; - - status = ret; - } - - if (status & NAND_STATUS_FAIL) - return -EIO; - - return 0; -} -EXPORT_SYMBOL_GPL(nand_prog_page_end_op); - -/** - * nand_prog_page_op - Do a full PROG PAGE operation - * @chip: The NAND chip - * @page: page to write - * @offset_in_page: offset within the page - * @buf: buffer containing the data to write to the page - * @len: length of the buffer - * - * This function issues a full PROG PAGE operation. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_prog_page_op(struct nand_chip *chip, unsigned int page, - unsigned int offset_in_page, const void *buf, - unsigned int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int status; - - if (!len || !buf) - return -EINVAL; - - if (offset_in_page + len > mtd->writesize + mtd->oobsize) - return -EINVAL; - - if (chip->exec_op) { - status = nand_exec_prog_page_op(chip, page, offset_in_page, buf, - len, true); - } else { - chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); - chip->write_buf(mtd, buf, len); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - } - - if (status & NAND_STATUS_FAIL) - return -EIO; - - return 0; -} -EXPORT_SYMBOL_GPL(nand_prog_page_op); - -/** - * nand_change_write_column_op - Do a CHANGE WRITE COLUMN operation - * @chip: The NAND chip - * @offset_in_page: offset within the page - * @buf: buffer containing the data to send to the NAND - * @len: length of the buffer - * @force_8bit: force 8-bit bus access - * - * This function issues a CHANGE WRITE COLUMN operation. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_change_write_column_op(struct nand_chip *chip, - unsigned int offset_in_page, - const void *buf, unsigned int len, - bool force_8bit) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (len && !buf) - return -EINVAL; - - if (offset_in_page + len > mtd->writesize + mtd->oobsize) - return -EINVAL; - - /* Small page NANDs do not support column change. */ - if (mtd->writesize <= 512) - return -ENOTSUPP; - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - u8 addrs[2]; - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_RNDIN, 0), - NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)), - NAND_OP_DATA_OUT(len, buf, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - int ret; - - ret = nand_fill_column_cycles(chip, addrs, offset_in_page); - if (ret < 0) - return ret; - - instrs[2].ctx.data.force_8bit = force_8bit; - - /* Drop the DATA_OUT instruction if len is set to 0. */ - if (!len) - op.ninstrs--; - - return nand_exec_op(chip, &op); - } - - chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset_in_page, -1); - if (len) - chip->write_buf(mtd, buf, len); - - return 0; -} -EXPORT_SYMBOL_GPL(nand_change_write_column_op); - -/** - * nand_readid_op - Do a READID operation - * @chip: The NAND chip - * @addr: address cycle to pass after the READID command - * @buf: buffer used to store the ID - * @len: length of the buffer - * - * This function sends a READID command and reads back the ID returned by the - * NAND. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, - unsigned int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - unsigned int i; - u8 *id = buf; - - if (len && !buf) - return -EINVAL; - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_READID, 0), - NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)), - NAND_OP_8BIT_DATA_IN(len, buf, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - /* Drop the DATA_IN instruction if len is set to 0. */ - if (!len) - op.ninstrs--; - - return nand_exec_op(chip, &op); - } - - chip->cmdfunc(mtd, NAND_CMD_READID, addr, -1); - - for (i = 0; i < len; i++) - id[i] = chip->read_byte(mtd); - - return 0; -} -EXPORT_SYMBOL_GPL(nand_readid_op); - -/** - * nand_status_op - Do a STATUS operation - * @chip: The NAND chip - * @status: out variable to store the NAND status - * - * This function sends a STATUS command and reads back the status returned by - * the NAND. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_status_op(struct nand_chip *chip, u8 *status) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_STATUS, - PSEC_TO_NSEC(sdr->tADL_min)), - NAND_OP_8BIT_DATA_IN(1, status, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - if (!status) - op.ninstrs--; - - return nand_exec_op(chip, &op); - } - - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - if (status) - *status = chip->read_byte(mtd); - - return 0; -} -EXPORT_SYMBOL_GPL(nand_status_op); - -/** - * nand_exit_status_op - Exit a STATUS operation - * @chip: The NAND chip - * - * This function sends a READ0 command to cancel the effect of the STATUS - * command to avoid reading only the status until a new read command is sent. - * - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_exit_status_op(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (chip->exec_op) { - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_READ0, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - return nand_exec_op(chip, &op); - } - - chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1); - - return 0; -} -EXPORT_SYMBOL_GPL(nand_exit_status_op); - -/** - * nand_erase_op - Do an erase operation - * @chip: The NAND chip - * @eraseblock: block to erase - * - * This function sends an ERASE command and waits for the NAND to be ready - * before returning. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - unsigned int page = eraseblock << - (chip->phys_erase_shift - chip->page_shift); - int ret; - u8 status; - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - u8 addrs[3] = { page, page >> 8, page >> 16 }; - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_ERASE1, 0), - NAND_OP_ADDR(2, addrs, 0), - NAND_OP_CMD(NAND_CMD_ERASE2, - PSEC_TO_MSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - if (chip->options & NAND_ROW_ADDR_3) - instrs[1].ctx.addr.naddrs++; - - ret = nand_exec_op(chip, &op); - if (ret) - return ret; - - ret = nand_status_op(chip, &status); - if (ret) - return ret; - } else { - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); - - ret = chip->waitfunc(mtd, chip); - if (ret < 0) - return ret; - - status = ret; - } - - if (status & NAND_STATUS_FAIL) - return -EIO; - - return 0; -} -EXPORT_SYMBOL_GPL(nand_erase_op); - -/** - * nand_set_features_op - Do a SET FEATURES operation - * @chip: The NAND chip - * @feature: feature id - * @data: 4 bytes of data - * - * This function sends a SET FEATURES command and waits for the NAND to be - * ready before returning. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -static int nand_set_features_op(struct nand_chip *chip, u8 feature, - const void *data) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const u8 *params = data; - int i, ret; - u8 status; - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0), - NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)), - NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data, - PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - ret = nand_exec_op(chip, &op); - if (ret) - return ret; - - ret = nand_status_op(chip, &status); - if (ret) - return ret; - } else { - chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - chip->write_byte(mtd, params[i]); - - ret = chip->waitfunc(mtd, chip); - if (ret < 0) - return ret; - - status = ret; - } - - if (status & NAND_STATUS_FAIL) - return -EIO; - - return 0; -} - -/** - * nand_get_features_op - Do a GET FEATURES operation - * @chip: The NAND chip - * @feature: feature id - * @data: 4 bytes of data - * - * This function sends a GET FEATURES command and waits for the NAND to be - * ready before returning. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -static int nand_get_features_op(struct nand_chip *chip, u8 feature, - void *data) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - u8 *params = data; - int i; - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0), - NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), - PSEC_TO_NSEC(sdr->tRR_min)), - NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN, - data, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - return nand_exec_op(chip, &op); - } - - chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, feature, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - params[i] = chip->read_byte(mtd); - - return 0; -} - -/** - * nand_reset_op - Do a reset operation - * @chip: The NAND chip - * - * This function sends a RESET command and waits for the NAND to be ready - * before returning. - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_reset_op(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (chip->exec_op) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(&chip->data_interface); - struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - return nand_exec_op(chip, &op); - } - - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - return 0; -} -EXPORT_SYMBOL_GPL(nand_reset_op); - -/** - * nand_read_data_op - Read data from the NAND - * @chip: The NAND chip - * @buf: buffer used to store the data - * @len: length of the buffer - * @force_8bit: force 8-bit bus access - * - * This function does a raw data read on the bus. Usually used after launching - * another NAND operation like nand_read_page_op(). - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, - bool force_8bit) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (!len || !buf) - return -EINVAL; - - if (chip->exec_op) { - struct nand_op_instr instrs[] = { - NAND_OP_DATA_IN(len, buf, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - instrs[0].ctx.data.force_8bit = force_8bit; - - return nand_exec_op(chip, &op); - } - - if (force_8bit) { - u8 *p = buf; - unsigned int i; - - for (i = 0; i < len; i++) - p[i] = chip->read_byte(mtd); - } else { - chip->read_buf(mtd, buf, len); - } - - return 0; -} -EXPORT_SYMBOL_GPL(nand_read_data_op); - -/** - * nand_write_data_op - Write data from the NAND - * @chip: The NAND chip - * @buf: buffer containing the data to send on the bus - * @len: length of the buffer - * @force_8bit: force 8-bit bus access - * - * This function does a raw data write on the bus. Usually used after launching - * another NAND operation like nand_write_page_begin_op(). - * This function does not select/unselect the CS line. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_write_data_op(struct nand_chip *chip, const void *buf, - unsigned int len, bool force_8bit) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (!len || !buf) - return -EINVAL; - - if (chip->exec_op) { - struct nand_op_instr instrs[] = { - NAND_OP_DATA_OUT(len, buf, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - instrs[0].ctx.data.force_8bit = force_8bit; - - return nand_exec_op(chip, &op); - } - - if (force_8bit) { - const u8 *p = buf; - unsigned int i; - - for (i = 0; i < len; i++) - chip->write_byte(mtd, p[i]); - } else { - chip->write_buf(mtd, buf, len); - } - - return 0; -} -EXPORT_SYMBOL_GPL(nand_write_data_op); - -/** - * struct nand_op_parser_ctx - Context used by the parser - * @instrs: array of all the instructions that must be addressed - * @ninstrs: length of the @instrs array - * @subop: Sub-operation to be passed to the NAND controller - * - * This structure is used by the core to split NAND operations into - * sub-operations that can be handled by the NAND controller. - */ -struct nand_op_parser_ctx { - const struct nand_op_instr *instrs; - unsigned int ninstrs; - struct nand_subop subop; -}; - -/** - * nand_op_parser_must_split_instr - Checks if an instruction must be split - * @pat: the parser pattern element that matches @instr - * @instr: pointer to the instruction to check - * @start_offset: this is an in/out parameter. If @instr has already been - * split, then @start_offset is the offset from which to start - * (either an address cycle or an offset in the data buffer). - * Conversely, if the function returns true (ie. instr must be - * split), this parameter is updated to point to the first - * data/address cycle that has not been taken care of. - * - * Some NAND controllers are limited and cannot send X address cycles with a - * unique operation, or cannot read/write more than Y bytes at the same time. - * In this case, split the instruction that does not fit in a single - * controller-operation into two or more chunks. - * - * Returns true if the instruction must be split, false otherwise. - * The @start_offset parameter is also updated to the offset at which the next - * bundle of instruction must start (if an address or a data instruction). - */ -static bool -nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat, - const struct nand_op_instr *instr, - unsigned int *start_offset) -{ - switch (pat->type) { - case NAND_OP_ADDR_INSTR: - if (!pat->ctx.addr.maxcycles) - break; - - if (instr->ctx.addr.naddrs - *start_offset > - pat->ctx.addr.maxcycles) { - *start_offset += pat->ctx.addr.maxcycles; - return true; - } - break; - - case NAND_OP_DATA_IN_INSTR: - case NAND_OP_DATA_OUT_INSTR: - if (!pat->ctx.data.maxlen) - break; - - if (instr->ctx.data.len - *start_offset > - pat->ctx.data.maxlen) { - *start_offset += pat->ctx.data.maxlen; - return true; - } - break; - - default: - break; - } - - return false; -} - -/** - * nand_op_parser_match_pat - Checks if a pattern matches the instructions - * remaining in the parser context - * @pat: the pattern to test - * @ctx: the parser context structure to match with the pattern @pat - * - * Check if @pat matches the set or a sub-set of instructions remaining in @ctx. - * Returns true if this is the case, false ortherwise. When true is returned, - * @ctx->subop is updated with the set of instructions to be passed to the - * controller driver. - */ -static bool -nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat, - struct nand_op_parser_ctx *ctx) -{ - unsigned int instr_offset = ctx->subop.first_instr_start_off; - const struct nand_op_instr *end = ctx->instrs + ctx->ninstrs; - const struct nand_op_instr *instr = ctx->subop.instrs; - unsigned int i, ninstrs; - - for (i = 0, ninstrs = 0; i < pat->nelems && instr < end; i++) { - /* - * The pattern instruction does not match the operation - * instruction. If the instruction is marked optional in the - * pattern definition, we skip the pattern element and continue - * to the next one. If the element is mandatory, there's no - * match and we can return false directly. - */ - if (instr->type != pat->elems[i].type) { - if (!pat->elems[i].optional) - return false; - - continue; - } - - /* - * Now check the pattern element constraints. If the pattern is - * not able to handle the whole instruction in a single step, - * we have to split it. - * The last_instr_end_off value comes back updated to point to - * the position where we have to split the instruction (the - * start of the next subop chunk). - */ - if (nand_op_parser_must_split_instr(&pat->elems[i], instr, - &instr_offset)) { - ninstrs++; - i++; - break; - } - - instr++; - ninstrs++; - instr_offset = 0; - } - - /* - * This can happen if all instructions of a pattern are optional. - * Still, if there's not at least one instruction handled by this - * pattern, this is not a match, and we should try the next one (if - * any). - */ - if (!ninstrs) - return false; - - /* - * We had a match on the pattern head, but the pattern may be longer - * than the instructions we're asked to execute. We need to make sure - * there's no mandatory elements in the pattern tail. - */ - for (; i < pat->nelems; i++) { - if (!pat->elems[i].optional) - return false; - } - - /* - * We have a match: update the subop structure accordingly and return - * true. - */ - ctx->subop.ninstrs = ninstrs; - ctx->subop.last_instr_end_off = instr_offset; - - return true; -} - -#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) -static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) -{ - const struct nand_op_instr *instr; - char *prefix = " "; - unsigned int i; - - pr_debug("executing subop:\n"); - - for (i = 0; i < ctx->ninstrs; i++) { - instr = &ctx->instrs[i]; - - if (instr == &ctx->subop.instrs[0]) - prefix = " ->"; - - switch (instr->type) { - case NAND_OP_CMD_INSTR: - pr_debug("%sCMD [0x%02x]\n", prefix, - instr->ctx.cmd.opcode); - break; - case NAND_OP_ADDR_INSTR: - pr_debug("%sADDR [%d cyc: %*ph]\n", prefix, - instr->ctx.addr.naddrs, - instr->ctx.addr.naddrs < 64 ? - instr->ctx.addr.naddrs : 64, - instr->ctx.addr.addrs); - break; - case NAND_OP_DATA_IN_INSTR: - pr_debug("%sDATA_IN [%d B%s]\n", prefix, - instr->ctx.data.len, - instr->ctx.data.force_8bit ? - ", force 8-bit" : ""); - break; - case NAND_OP_DATA_OUT_INSTR: - pr_debug("%sDATA_OUT [%d B%s]\n", prefix, - instr->ctx.data.len, - instr->ctx.data.force_8bit ? - ", force 8-bit" : ""); - break; - case NAND_OP_WAITRDY_INSTR: - pr_debug("%sWAITRDY [max %d ms]\n", prefix, - instr->ctx.waitrdy.timeout_ms); - break; - } - - if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1]) - prefix = " "; - } -} -#else -static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) -{ - /* NOP */ -} -#endif - -/** - * nand_op_parser_exec_op - exec_op parser - * @chip: the NAND chip - * @parser: patterns description provided by the controller driver - * @op: the NAND operation to address - * @check_only: when true, the function only checks if @op can be handled but - * does not execute the operation - * - * Helper function designed to ease integration of NAND controller drivers that - * only support a limited set of instruction sequences. The supported sequences - * are described in @parser, and the framework takes care of splitting @op into - * multiple sub-operations (if required) and pass them back to the ->exec() - * callback of the matching pattern if @check_only is set to false. - * - * NAND controller drivers should call this function from their own ->exec_op() - * implementation. - * - * Returns 0 on success, a negative error code otherwise. A failure can be - * caused by an unsupported operation (none of the supported patterns is able - * to handle the requested operation), or an error returned by one of the - * matching pattern->exec() hook. - */ -int nand_op_parser_exec_op(struct nand_chip *chip, - const struct nand_op_parser *parser, - const struct nand_operation *op, bool check_only) -{ - struct nand_op_parser_ctx ctx = { - .subop.instrs = op->instrs, - .instrs = op->instrs, - .ninstrs = op->ninstrs, - }; - unsigned int i; - - while (ctx.subop.instrs < op->instrs + op->ninstrs) { - int ret; - - for (i = 0; i < parser->npatterns; i++) { - const struct nand_op_parser_pattern *pattern; - - pattern = &parser->patterns[i]; - if (!nand_op_parser_match_pat(pattern, &ctx)) - continue; - - nand_op_parser_trace(&ctx); - - if (check_only) - break; - - ret = pattern->exec(chip, &ctx.subop); - if (ret) - return ret; - - break; - } - - if (i == parser->npatterns) { - pr_debug("->exec_op() parser: pattern not found!\n"); - return -ENOTSUPP; - } - - /* - * Update the context structure by pointing to the start of the - * next subop. - */ - ctx.subop.instrs = ctx.subop.instrs + ctx.subop.ninstrs; - if (ctx.subop.last_instr_end_off) - ctx.subop.instrs -= 1; - - ctx.subop.first_instr_start_off = ctx.subop.last_instr_end_off; - } - - return 0; -} -EXPORT_SYMBOL_GPL(nand_op_parser_exec_op); - -static bool nand_instr_is_data(const struct nand_op_instr *instr) -{ - return instr && (instr->type == NAND_OP_DATA_IN_INSTR || - instr->type == NAND_OP_DATA_OUT_INSTR); -} - -static bool nand_subop_instr_is_valid(const struct nand_subop *subop, - unsigned int instr_idx) -{ - return subop && instr_idx < subop->ninstrs; -} - -static int nand_subop_get_start_off(const struct nand_subop *subop, - unsigned int instr_idx) -{ - if (instr_idx) - return 0; - - return subop->first_instr_start_off; -} - -/** - * nand_subop_get_addr_start_off - Get the start offset in an address array - * @subop: The entire sub-operation - * @instr_idx: Index of the instruction inside the sub-operation - * - * During driver development, one could be tempted to directly use the - * ->addr.addrs field of address instructions. This is wrong as address - * instructions might be split. - * - * Given an address instruction, returns the offset of the first cycle to issue. - */ -int nand_subop_get_addr_start_off(const struct nand_subop *subop, - unsigned int instr_idx) -{ - if (!nand_subop_instr_is_valid(subop, instr_idx) || - subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR) - return -EINVAL; - - return nand_subop_get_start_off(subop, instr_idx); -} -EXPORT_SYMBOL_GPL(nand_subop_get_addr_start_off); - -/** - * nand_subop_get_num_addr_cyc - Get the remaining address cycles to assert - * @subop: The entire sub-operation - * @instr_idx: Index of the instruction inside the sub-operation - * - * During driver development, one could be tempted to directly use the - * ->addr->naddrs field of a data instruction. This is wrong as instructions - * might be split. - * - * Given an address instruction, returns the number of address cycle to issue. - */ -int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, - unsigned int instr_idx) -{ - int start_off, end_off; - - if (!nand_subop_instr_is_valid(subop, instr_idx) || - subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR) - return -EINVAL; - - start_off = nand_subop_get_addr_start_off(subop, instr_idx); - - if (instr_idx == subop->ninstrs - 1 && - subop->last_instr_end_off) - end_off = subop->last_instr_end_off; - else - end_off = subop->instrs[instr_idx].ctx.addr.naddrs; - - return end_off - start_off; -} -EXPORT_SYMBOL_GPL(nand_subop_get_num_addr_cyc); - -/** - * nand_subop_get_data_start_off - Get the start offset in a data array - * @subop: The entire sub-operation - * @instr_idx: Index of the instruction inside the sub-operation - * - * During driver development, one could be tempted to directly use the - * ->data->buf.{in,out} field of data instructions. This is wrong as data - * instructions might be split. - * - * Given a data instruction, returns the offset to start from. - */ -int nand_subop_get_data_start_off(const struct nand_subop *subop, - unsigned int instr_idx) -{ - if (!nand_subop_instr_is_valid(subop, instr_idx) || - !nand_instr_is_data(&subop->instrs[instr_idx])) - return -EINVAL; - - return nand_subop_get_start_off(subop, instr_idx); -} -EXPORT_SYMBOL_GPL(nand_subop_get_data_start_off); - -/** - * nand_subop_get_data_len - Get the number of bytes to retrieve - * @subop: The entire sub-operation - * @instr_idx: Index of the instruction inside the sub-operation - * - * During driver development, one could be tempted to directly use the - * ->data->len field of a data instruction. This is wrong as data instructions - * might be split. - * - * Returns the length of the chunk of data to send/receive. - */ -int nand_subop_get_data_len(const struct nand_subop *subop, - unsigned int instr_idx) -{ - int start_off = 0, end_off; - - if (!nand_subop_instr_is_valid(subop, instr_idx) || - !nand_instr_is_data(&subop->instrs[instr_idx])) - return -EINVAL; - - start_off = nand_subop_get_data_start_off(subop, instr_idx); - - if (instr_idx == subop->ninstrs - 1 && - subop->last_instr_end_off) - end_off = subop->last_instr_end_off; - else - end_off = subop->instrs[instr_idx].ctx.data.len; - - return end_off - start_off; -} -EXPORT_SYMBOL_GPL(nand_subop_get_data_len); - -/** - * nand_reset - Reset and initialize a NAND device - * @chip: The NAND chip - * @chipnr: Internal die id - * - * Save the timings data structure, then apply SDR timings mode 0 (see - * nand_reset_data_interface for details), do the reset operation, and - * apply back the previous timings. - * - * Returns 0 on success, a negative error code otherwise. - */ -int nand_reset(struct nand_chip *chip, int chipnr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_data_interface saved_data_intf = chip->data_interface; - int ret; - - ret = nand_reset_data_interface(chip, chipnr); - if (ret) - return ret; - - /* - * The CS line has to be released before we can apply the new NAND - * interface settings, hence this weird ->select_chip() dance. - */ - chip->select_chip(mtd, chipnr); - ret = nand_reset_op(chip); - chip->select_chip(mtd, -1); - if (ret) - return ret; - - chip->select_chip(mtd, chipnr); - chip->data_interface = saved_data_intf; - ret = nand_setup_data_interface(chip, chipnr); - chip->select_chip(mtd, -1); - if (ret) - return ret; - - return 0; -} -EXPORT_SYMBOL_GPL(nand_reset); - -/** - * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data - * @buf: buffer to test - * @len: buffer length - * @bitflips_threshold: maximum number of bitflips - * - * Check if a buffer contains only 0xff, which means the underlying region - * has been erased and is ready to be programmed. - * The bitflips_threshold specify the maximum number of bitflips before - * considering the region is not erased. - * Note: The logic of this function has been extracted from the memweight - * implementation, except that nand_check_erased_buf function exit before - * testing the whole buffer if the number of bitflips exceed the - * bitflips_threshold value. - * - * Returns a positive number of bitflips less than or equal to - * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the - * threshold. - */ -static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold) -{ - const unsigned char *bitmap = buf; - int bitflips = 0; - int weight; - - for (; len && ((uintptr_t)bitmap) % sizeof(long); - len--, bitmap++) { - weight = hweight8(*bitmap); - bitflips += BITS_PER_BYTE - weight; - if (unlikely(bitflips > bitflips_threshold)) - return -EBADMSG; - } - - for (; len >= sizeof(long); - len -= sizeof(long), bitmap += sizeof(long)) { - unsigned long d = *((unsigned long *)bitmap); - if (d == ~0UL) - continue; - weight = hweight_long(d); - bitflips += BITS_PER_LONG - weight; - if (unlikely(bitflips > bitflips_threshold)) - return -EBADMSG; - } - - for (; len > 0; len--, bitmap++) { - weight = hweight8(*bitmap); - bitflips += BITS_PER_BYTE - weight; - if (unlikely(bitflips > bitflips_threshold)) - return -EBADMSG; - } - - return bitflips; -} - -/** - * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only - * 0xff data - * @data: data buffer to test - * @datalen: data length - * @ecc: ECC buffer - * @ecclen: ECC length - * @extraoob: extra OOB buffer - * @extraooblen: extra OOB length - * @bitflips_threshold: maximum number of bitflips - * - * Check if a data buffer and its associated ECC and OOB data contains only - * 0xff pattern, which means the underlying region has been erased and is - * ready to be programmed. - * The bitflips_threshold specify the maximum number of bitflips before - * considering the region as not erased. - * - * Note: - * 1/ ECC algorithms are working on pre-defined block sizes which are usually - * different from the NAND page size. When fixing bitflips, ECC engines will - * report the number of errors per chunk, and the NAND core infrastructure - * expect you to return the maximum number of bitflips for the whole page. - * This is why you should always use this function on a single chunk and - * not on the whole page. After checking each chunk you should update your - * max_bitflips value accordingly. - * 2/ When checking for bitflips in erased pages you should not only check - * the payload data but also their associated ECC data, because a user might - * have programmed almost all bits to 1 but a few. In this case, we - * shouldn't consider the chunk as erased, and checking ECC bytes prevent - * this case. - * 3/ The extraoob argument is optional, and should be used if some of your OOB - * data are protected by the ECC engine. - * It could also be used if you support subpages and want to attach some - * extra OOB data to an ECC chunk. - * - * Returns a positive number of bitflips less than or equal to - * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the - * threshold. In case of success, the passed buffers are filled with 0xff. - */ -int nand_check_erased_ecc_chunk(void *data, int datalen, - void *ecc, int ecclen, - void *extraoob, int extraooblen, - int bitflips_threshold) -{ - int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0; - - data_bitflips = nand_check_erased_buf(data, datalen, - bitflips_threshold); - if (data_bitflips < 0) - return data_bitflips; - - bitflips_threshold -= data_bitflips; - - ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold); - if (ecc_bitflips < 0) - return ecc_bitflips; - - bitflips_threshold -= ecc_bitflips; - - extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen, - bitflips_threshold); - if (extraoob_bitflips < 0) - return extraoob_bitflips; - - if (data_bitflips) - memset(data, 0xff, datalen); - - if (ecc_bitflips) - memset(ecc, 0xff, ecclen); - - if (extraoob_bitflips) - memset(extraoob, 0xff, extraooblen); - - return data_bitflips + ecc_bitflips + extraoob_bitflips; -} -EXPORT_SYMBOL(nand_check_erased_ecc_chunk); - -/** - * nand_read_page_raw - [INTERN] read raw page data without ecc - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Not for syndrome calculating ECC controllers, which use a special oob layout. - */ -int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int ret; - - ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize); - if (ret) - return ret; - - if (oob_required) { - ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, - false); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL(nand_read_page_raw); - -/** - * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * We need a special oob layout and handling even when OOB isn't used. - */ -static int nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - uint8_t *oob = chip->oob_poi; - int steps, size, ret; - - ret = nand_read_page_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - for (steps = chip->ecc.steps; steps > 0; steps--) { - ret = nand_read_data_op(chip, buf, eccsize, false); - if (ret) - return ret; - - buf += eccsize; - - if (chip->ecc.prepad) { - ret = nand_read_data_op(chip, oob, chip->ecc.prepad, - false); - if (ret) - return ret; - - oob += chip->ecc.prepad; - } - - ret = nand_read_data_op(chip, oob, eccbytes, false); - if (ret) - return ret; - - oob += eccbytes; - - if (chip->ecc.postpad) { - ret = nand_read_data_op(chip, oob, chip->ecc.postpad, - false); - if (ret) - return ret; - - oob += chip->ecc.postpad; - } - } - - size = mtd->oobsize - (oob - chip->oob_poi); - if (size) { - ret = nand_read_data_op(chip, oob, size, false); - if (ret) - return ret; - } - - return 0; -} - -/** - * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - */ -static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size, ret; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->ecc.calc_buf; - uint8_t *ecc_code = chip->ecc.code_buf; - unsigned int max_bitflips = 0; - - chip->ecc.read_page_raw(mtd, chip, buf, 1, page); - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - eccsteps = chip->ecc.steps; - p = buf; - - for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - return max_bitflips; -} - -/** - * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @data_offs: offset of requested data within the page - * @readlen: data length - * @bufpoi: buffer to store read data - * @page: page number to read - */ -static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, - int page) -{ - int start_step, end_step, num_steps, ret; - uint8_t *p; - int data_col_addr, i, gaps = 0; - int datafrag_len, eccfrag_len, aligned_len, aligned_pos; - int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; - int index, section = 0; - unsigned int max_bitflips = 0; - struct mtd_oob_region oobregion = { }; - - /* Column address within the page aligned to ECC size (256bytes) */ - start_step = data_offs / chip->ecc.size; - end_step = (data_offs + readlen - 1) / chip->ecc.size; - num_steps = end_step - start_step + 1; - index = start_step * chip->ecc.bytes; - - /* Data size aligned to ECC ecc.size */ - datafrag_len = num_steps * chip->ecc.size; - eccfrag_len = num_steps * chip->ecc.bytes; - - data_col_addr = start_step * chip->ecc.size; - /* If we read not a page aligned data */ - p = bufpoi + data_col_addr; - ret = nand_read_page_op(chip, page, data_col_addr, p, datafrag_len); - if (ret) - return ret; - - /* Calculate ECC */ - for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) - chip->ecc.calculate(mtd, p, &chip->ecc.calc_buf[i]); - - /* - * The performance is faster if we position offsets according to - * ecc.pos. Let's make sure that there are no gaps in ECC positions. - */ - ret = mtd_ooblayout_find_eccregion(mtd, index, §ion, &oobregion); - if (ret) - return ret; - - if (oobregion.length < eccfrag_len) - gaps = 1; - - if (gaps) { - ret = nand_change_read_column_op(chip, mtd->writesize, - chip->oob_poi, mtd->oobsize, - false); - if (ret) - return ret; - } else { - /* - * Send the command to read the particular ECC bytes take care - * about buswidth alignment in read_buf. - */ - aligned_pos = oobregion.offset & ~(busw - 1); - aligned_len = eccfrag_len; - if (oobregion.offset & (busw - 1)) - aligned_len++; - if ((oobregion.offset + (num_steps * chip->ecc.bytes)) & - (busw - 1)) - aligned_len++; - - ret = nand_change_read_column_op(chip, - mtd->writesize + aligned_pos, - &chip->oob_poi[aligned_pos], - aligned_len, false); - if (ret) - return ret; - } - - ret = mtd_ooblayout_get_eccbytes(mtd, chip->ecc.code_buf, - chip->oob_poi, index, eccfrag_len); - if (ret) - return ret; - - p = bufpoi + data_col_addr; - for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { - int stat; - - stat = chip->ecc.correct(mtd, p, &chip->ecc.code_buf[i], - &chip->ecc.calc_buf[i]); - if (stat == -EBADMSG && - (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { - /* check for empty pages with bitflips */ - stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, - &chip->ecc.code_buf[i], - chip->ecc.bytes, - NULL, 0, - chip->ecc.strength); - } - - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - return max_bitflips; -} - -/** - * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Not for syndrome calculating ECC controllers which need a special oob layout. - */ -static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size, ret; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->ecc.calc_buf; - uint8_t *ecc_code = chip->ecc.code_buf; - unsigned int max_bitflips = 0; - - ret = nand_read_page_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_READ); - - ret = nand_read_data_op(chip, p, eccsize, false); - if (ret) - return ret; - - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - } - - ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false); - if (ret) - return ret; - - ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - eccsteps = chip->ecc.steps; - p = buf; - - for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat == -EBADMSG && - (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { - /* check for empty pages with bitflips */ - stat = nand_check_erased_ecc_chunk(p, eccsize, - &ecc_code[i], eccbytes, - NULL, 0, - chip->ecc.strength); - } - - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - return max_bitflips; -} - -/** - * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Hardware ECC for large page chips, require OOB to be read first. For this - * ECC mode, the write_page method is re-used from ECC_HW. These methods - * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with - * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from - * the data area, by overwriting the NAND manufacturer bad block markings. - */ -static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size, ret; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_code = chip->ecc.code_buf; - uint8_t *ecc_calc = chip->ecc.calc_buf; - unsigned int max_bitflips = 0; - - /* Read the OOB area first */ - ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); - if (ret) - return ret; - - ret = nand_read_page_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - chip->ecc.hwctl(mtd, NAND_ECC_READ); - - ret = nand_read_data_op(chip, p, eccsize, false); - if (ret) - return ret; - - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); - if (stat == -EBADMSG && - (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { - /* check for empty pages with bitflips */ - stat = nand_check_erased_ecc_chunk(p, eccsize, - &ecc_code[i], eccbytes, - NULL, 0, - chip->ecc.strength); - } - - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - return max_bitflips; -} - -/** - * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * The hw generator calculates the error syndrome automatically. Therefore we - * need a special oob layout and handling. - */ -static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int ret, i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - unsigned int max_bitflips = 0; - - ret = nand_read_page_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - chip->ecc.hwctl(mtd, NAND_ECC_READ); - - ret = nand_read_data_op(chip, p, eccsize, false); - if (ret) - return ret; - - if (chip->ecc.prepad) { - ret = nand_read_data_op(chip, oob, chip->ecc.prepad, - false); - if (ret) - return ret; - - oob += chip->ecc.prepad; - } - - chip->ecc.hwctl(mtd, NAND_ECC_READSYN); - - ret = nand_read_data_op(chip, oob, eccbytes, false); - if (ret) - return ret; - - stat = chip->ecc.correct(mtd, p, oob, NULL); - - oob += eccbytes; - - if (chip->ecc.postpad) { - ret = nand_read_data_op(chip, oob, chip->ecc.postpad, - false); - if (ret) - return ret; - - oob += chip->ecc.postpad; - } - - if (stat == -EBADMSG && - (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { - /* check for empty pages with bitflips */ - stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, - oob - eccpadbytes, - eccpadbytes, - NULL, 0, - chip->ecc.strength); - } - - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - - /* Calculate remaining oob bytes */ - i = mtd->oobsize - (oob - chip->oob_poi); - if (i) { - ret = nand_read_data_op(chip, oob, i, false); - if (ret) - return ret; - } - - return max_bitflips; -} - -/** - * nand_transfer_oob - [INTERN] Transfer oob to client buffer - * @mtd: mtd info structure - * @oob: oob destination address - * @ops: oob ops structure - * @len: size of oob to transfer - */ -static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob, - struct mtd_oob_ops *ops, size_t len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - switch (ops->mode) { - - case MTD_OPS_PLACE_OOB: - case MTD_OPS_RAW: - memcpy(oob, chip->oob_poi + ops->ooboffs, len); - return oob + len; - - case MTD_OPS_AUTO_OOB: - ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi, - ops->ooboffs, len); - BUG_ON(ret); - return oob + len; - - default: - BUG(); - } - return NULL; -} - -/** - * nand_setup_read_retry - [INTERN] Set the READ RETRY mode - * @mtd: MTD device structure - * @retry_mode: the retry mode to use - * - * Some vendors supply a special command to shift the Vt threshold, to be used - * when there are too many bitflips in a page (i.e., ECC error). After setting - * a new threshold, the host should retry reading the page. - */ -static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - pr_debug("setting READ RETRY mode %d\n", retry_mode); - - if (retry_mode >= chip->read_retries) - return -EINVAL; - - if (!chip->setup_read_retry) - return -EOPNOTSUPP; - - return chip->setup_read_retry(mtd, retry_mode); -} - -/** - * nand_do_read_ops - [INTERN] Read data with ECC - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob ops structure - * - * Internal function. Called with chip held. - */ -static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - int chipnr, page, realpage, col, bytes, aligned, oob_required; - struct nand_chip *chip = mtd_to_nand(mtd); - int ret = 0; - uint32_t readlen = ops->len; - uint32_t oobreadlen = ops->ooblen; - uint32_t max_oobsize = mtd_oobavail(mtd, ops); - - uint8_t *bufpoi, *oob, *buf; - int use_bufpoi; - unsigned int max_bitflips = 0; - int retry_mode = 0; - bool ecc_fail = false; - - chipnr = (int)(from >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - realpage = (int)(from >> chip->page_shift); - page = realpage & chip->pagemask; - - col = (int)(from & (mtd->writesize - 1)); - - buf = ops->datbuf; - oob = ops->oobbuf; - oob_required = oob ? 1 : 0; - - while (1) { - unsigned int ecc_failures = mtd->ecc_stats.failed; - - bytes = min(mtd->writesize - col, readlen); - aligned = (bytes == mtd->writesize); - - if (!aligned) - use_bufpoi = 1; - else if (chip->options & NAND_USE_BOUNCE_BUFFER) - use_bufpoi = !virt_addr_valid(buf) || - !IS_ALIGNED((unsigned long)buf, - chip->buf_align); - else - use_bufpoi = 0; - - /* Is the current page in the buffer? */ - if (realpage != chip->pagebuf || oob) { - bufpoi = use_bufpoi ? chip->data_buf : buf; - - if (use_bufpoi && aligned) - pr_debug("%s: using read bounce buffer for buf@%p\n", - __func__, buf); - -read_retry: - /* - * Now read the page into the buffer. Absent an error, - * the read methods return max bitflips per ecc step. - */ - if (unlikely(ops->mode == MTD_OPS_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, - oob_required, - page); - else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && - !oob) - ret = chip->ecc.read_subpage(mtd, chip, - col, bytes, bufpoi, - page); - else - ret = chip->ecc.read_page(mtd, chip, bufpoi, - oob_required, page); - if (ret < 0) { - if (use_bufpoi) - /* Invalidate page cache */ - chip->pagebuf = -1; - break; - } - - /* Transfer not aligned data */ - if (use_bufpoi) { - if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && - !(mtd->ecc_stats.failed - ecc_failures) && - (ops->mode != MTD_OPS_RAW)) { - chip->pagebuf = realpage; - chip->pagebuf_bitflips = ret; - } else { - /* Invalidate page cache */ - chip->pagebuf = -1; - } - memcpy(buf, chip->data_buf + col, bytes); - } - - if (unlikely(oob)) { - int toread = min(oobreadlen, max_oobsize); - - if (toread) { - oob = nand_transfer_oob(mtd, - oob, ops, toread); - oobreadlen -= toread; - } - } - - if (chip->options & NAND_NEED_READRDY) { - /* Apply delay or wait for ready/busy pin */ - if (!chip->dev_ready) - udelay(chip->chip_delay); - else - nand_wait_ready(mtd); - } - - if (mtd->ecc_stats.failed - ecc_failures) { - if (retry_mode + 1 < chip->read_retries) { - retry_mode++; - ret = nand_setup_read_retry(mtd, - retry_mode); - if (ret < 0) - break; - - /* Reset failures; retry */ - mtd->ecc_stats.failed = ecc_failures; - goto read_retry; - } else { - /* No more retry modes; real failure */ - ecc_fail = true; - } - } - - buf += bytes; - max_bitflips = max_t(unsigned int, max_bitflips, ret); - } else { - memcpy(buf, chip->data_buf + col, bytes); - buf += bytes; - max_bitflips = max_t(unsigned int, max_bitflips, - chip->pagebuf_bitflips); - } - - readlen -= bytes; - - /* Reset to retry mode 0 */ - if (retry_mode) { - ret = nand_setup_read_retry(mtd, 0); - if (ret < 0) - break; - retry_mode = 0; - } - - if (!readlen) - break; - - /* For subsequent reads align to page boundary */ - col = 0; - /* Increment page address */ - realpage++; - - page = realpage & chip->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - chip->select_chip(mtd, -1); - - ops->retlen = ops->len - (size_t) readlen; - if (oob) - ops->oobretlen = ops->ooblen - oobreadlen; - - if (ret < 0) - return ret; - - if (ecc_fail) - return -EBADMSG; - - return max_bitflips; -} - -/** - * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - */ -int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) -{ - return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); -} -EXPORT_SYMBOL(nand_read_oob_std); - -/** - * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC - * with syndromes - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - */ -int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - int length = mtd->oobsize; - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size; - uint8_t *bufpoi = chip->oob_poi; - int i, toread, sndrnd = 0, pos, ret; - - ret = nand_read_page_op(chip, page, chip->ecc.size, NULL, 0); - if (ret) - return ret; - - for (i = 0; i < chip->ecc.steps; i++) { - if (sndrnd) { - int ret; - - pos = eccsize + i * (eccsize + chunk); - if (mtd->writesize > 512) - ret = nand_change_read_column_op(chip, pos, - NULL, 0, - false); - else - ret = nand_read_page_op(chip, page, pos, NULL, - 0); - - if (ret) - return ret; - } else - sndrnd = 1; - toread = min_t(int, length, chunk); - - ret = nand_read_data_op(chip, bufpoi, toread, false); - if (ret) - return ret; - - bufpoi += toread; - length -= toread; - } - if (length > 0) { - ret = nand_read_data_op(chip, bufpoi, length, false); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL(nand_read_oob_syndrome); - -/** - * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to write - */ -int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) -{ - return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, - mtd->oobsize); -} -EXPORT_SYMBOL(nand_write_oob_std); - -/** - * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC - * with syndrome - only for large page flash - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to write - */ -int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size, length = mtd->oobsize; - int ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps; - const uint8_t *bufpoi = chip->oob_poi; - - /* - * data-ecc-data-ecc ... ecc-oob - * or - * data-pad-ecc-pad-data-pad .... ecc-pad-oob - */ - if (!chip->ecc.prepad && !chip->ecc.postpad) { - pos = steps * (eccsize + chunk); - steps = 0; - } else - pos = eccsize; - - ret = nand_prog_page_begin_op(chip, page, pos, NULL, 0); - if (ret) - return ret; - - for (i = 0; i < steps; i++) { - if (sndcmd) { - if (mtd->writesize <= 512) { - uint32_t fill = 0xFFFFFFFF; - - len = eccsize; - while (len > 0) { - int num = min_t(int, len, 4); - - ret = nand_write_data_op(chip, &fill, - num, false); - if (ret) - return ret; - - len -= num; - } - } else { - pos = eccsize + i * (eccsize + chunk); - ret = nand_change_write_column_op(chip, pos, - NULL, 0, - false); - if (ret) - return ret; - } - } else - sndcmd = 1; - len = min_t(int, length, chunk); - - ret = nand_write_data_op(chip, bufpoi, len, false); - if (ret) - return ret; - - bufpoi += len; - length -= len; - } - if (length > 0) { - ret = nand_write_data_op(chip, bufpoi, length, false); - if (ret) - return ret; - } - - return nand_prog_page_end_op(chip); -} -EXPORT_SYMBOL(nand_write_oob_syndrome); - -/** - * nand_do_read_oob - [INTERN] NAND read out-of-band - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob operations description structure - * - * NAND read out-of-band data from the spare area. - */ -static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - unsigned int max_bitflips = 0; - int page, realpage, chipnr; - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtd_ecc_stats stats; - int readlen = ops->ooblen; - int len; - uint8_t *buf = ops->oobbuf; - int ret = 0; - - pr_debug("%s: from = 0x%08Lx, len = %i\n", - __func__, (unsigned long long)from, readlen); - - stats = mtd->ecc_stats; - - len = mtd_oobavail(mtd, ops); - - chipnr = (int)(from >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* Shift to get page */ - realpage = (int)(from >> chip->page_shift); - page = realpage & chip->pagemask; - - while (1) { - if (ops->mode == MTD_OPS_RAW) - ret = chip->ecc.read_oob_raw(mtd, chip, page); - else - ret = chip->ecc.read_oob(mtd, chip, page); - - if (ret < 0) - break; - - len = min(len, readlen); - buf = nand_transfer_oob(mtd, buf, ops, len); - - if (chip->options & NAND_NEED_READRDY) { - /* Apply delay or wait for ready/busy pin */ - if (!chip->dev_ready) - udelay(chip->chip_delay); - else - nand_wait_ready(mtd); - } - - max_bitflips = max_t(unsigned int, max_bitflips, ret); - - readlen -= len; - if (!readlen) - break; - - /* Increment page address */ - realpage++; - - page = realpage & chip->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - chip->select_chip(mtd, -1); - - ops->oobretlen = ops->ooblen - readlen; - - if (ret < 0) - return ret; - - if (mtd->ecc_stats.failed - stats.failed) - return -EBADMSG; - - return max_bitflips; -} - -/** - * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob operation description structure - * - * NAND read data and/or out-of-band data. - */ -static int nand_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - int ret; - - ops->retlen = 0; - - if (ops->mode != MTD_OPS_PLACE_OOB && - ops->mode != MTD_OPS_AUTO_OOB && - ops->mode != MTD_OPS_RAW) - return -ENOTSUPP; - - nand_get_device(mtd, FL_READING); - - if (!ops->datbuf) - ret = nand_do_read_oob(mtd, from, ops); - else - ret = nand_do_read_ops(mtd, from, ops); - - nand_release_device(mtd); - return ret; -} - - -/** - * nand_write_page_raw - [INTERN] raw page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * - * Not for syndrome calculating ECC controllers, which use a special oob layout. - */ -int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - int ret; - - ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - if (ret) - return ret; - - if (oob_required) { - ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, - false); - if (ret) - return ret; - } - - return nand_prog_page_end_op(chip); -} -EXPORT_SYMBOL(nand_write_page_raw); - -/** - * nand_write_page_raw_syndrome - [INTERN] raw page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * - * We need a special oob layout and handling even when ECC isn't checked. - */ -static int nand_write_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - uint8_t *oob = chip->oob_poi; - int steps, size, ret; - - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - for (steps = chip->ecc.steps; steps > 0; steps--) { - ret = nand_write_data_op(chip, buf, eccsize, false); - if (ret) - return ret; - - buf += eccsize; - - if (chip->ecc.prepad) { - ret = nand_write_data_op(chip, oob, chip->ecc.prepad, - false); - if (ret) - return ret; - - oob += chip->ecc.prepad; - } - - ret = nand_write_data_op(chip, oob, eccbytes, false); - if (ret) - return ret; - - oob += eccbytes; - - if (chip->ecc.postpad) { - ret = nand_write_data_op(chip, oob, chip->ecc.postpad, - false); - if (ret) - return ret; - - oob += chip->ecc.postpad; - } - } - - size = mtd->oobsize - (oob - chip->oob_poi); - if (size) { - ret = nand_write_data_op(chip, oob, size, false); - if (ret) - return ret; - } - - return nand_prog_page_end_op(chip); -} -/** - * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - */ -static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int i, eccsize = chip->ecc.size, ret; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->ecc.calc_buf; - const uint8_t *p = buf; - - /* Software ECC calculation */ - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); -} - -/** - * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - */ -static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int i, eccsize = chip->ecc.size, ret; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->ecc.calc_buf; - const uint8_t *p = buf; - - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - - ret = nand_write_data_op(chip, p, eccsize, false); - if (ret) - return ret; - - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - } - - ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); - if (ret) - return ret; - - return nand_prog_page_end_op(chip); -} - - -/** - * nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write - * @mtd: mtd info structure - * @chip: nand chip info structure - * @offset: column address of subpage within the page - * @data_len: data length - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - */ -static int nand_write_subpage_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint32_t offset, - uint32_t data_len, const uint8_t *buf, - int oob_required, int page) -{ - uint8_t *oob_buf = chip->oob_poi; - uint8_t *ecc_calc = chip->ecc.calc_buf; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - int ecc_steps = chip->ecc.steps; - uint32_t start_step = offset / ecc_size; - uint32_t end_step = (offset + data_len - 1) / ecc_size; - int oob_bytes = mtd->oobsize / ecc_steps; - int step, ret; - - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - for (step = 0; step < ecc_steps; step++) { - /* configure controller for WRITE access */ - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - - /* write data (untouched subpages already masked by 0xFF) */ - ret = nand_write_data_op(chip, buf, ecc_size, false); - if (ret) - return ret; - - /* mask ECC of un-touched subpages by padding 0xFF */ - if ((step < start_step) || (step > end_step)) - memset(ecc_calc, 0xff, ecc_bytes); - else - chip->ecc.calculate(mtd, buf, ecc_calc); - - /* mask OOB of un-touched subpages by padding 0xFF */ - /* if oob_required, preserve OOB metadata of written subpage */ - if (!oob_required || (step < start_step) || (step > end_step)) - memset(oob_buf, 0xff, oob_bytes); - - buf += ecc_size; - ecc_calc += ecc_bytes; - oob_buf += oob_bytes; - } - - /* copy calculated ECC for whole page to chip->buffer->oob */ - /* this include masked-value(0xFF) for unwritten subpages */ - ecc_calc = chip->ecc.calc_buf; - ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - /* write OOB buffer to NAND device */ - ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); - if (ret) - return ret; - - return nand_prog_page_end_op(chip); -} - - -/** - * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * - * The hw generator calculates the error syndrome automatically. Therefore we - * need a special oob layout and handling. - */ -static int nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - const uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - int ret; - - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - - ret = nand_write_data_op(chip, p, eccsize, false); - if (ret) - return ret; - - if (chip->ecc.prepad) { - ret = nand_write_data_op(chip, oob, chip->ecc.prepad, - false); - if (ret) - return ret; - - oob += chip->ecc.prepad; - } - - chip->ecc.calculate(mtd, p, oob); - - ret = nand_write_data_op(chip, oob, eccbytes, false); - if (ret) - return ret; - - oob += eccbytes; - - if (chip->ecc.postpad) { - ret = nand_write_data_op(chip, oob, chip->ecc.postpad, - false); - if (ret) - return ret; - - oob += chip->ecc.postpad; - } - } - - /* Calculate remaining oob bytes */ - i = mtd->oobsize - (oob - chip->oob_poi); - if (i) { - ret = nand_write_data_op(chip, oob, i, false); - if (ret) - return ret; - } - - return nand_prog_page_end_op(chip); -} - -/** - * nand_write_page - write one page - * @mtd: MTD device structure - * @chip: NAND chip descriptor - * @offset: address offset within the page - * @data_len: length of actual data to be written - * @buf: the data to write - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * @raw: use _raw version of write_page - */ -static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offset, int data_len, const uint8_t *buf, - int oob_required, int page, int raw) -{ - int status, subpage; - - if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && - chip->ecc.write_subpage) - subpage = offset || (data_len < mtd->writesize); - else - subpage = 0; - - if (unlikely(raw)) - status = chip->ecc.write_page_raw(mtd, chip, buf, - oob_required, page); - else if (subpage) - status = chip->ecc.write_subpage(mtd, chip, offset, data_len, - buf, oob_required, page); - else - status = chip->ecc.write_page(mtd, chip, buf, oob_required, - page); - - if (status < 0) - return status; - - return 0; -} - -/** - * nand_fill_oob - [INTERN] Transfer client buffer to oob - * @mtd: MTD device structure - * @oob: oob data buffer - * @len: oob data write length - * @ops: oob ops structure - */ -static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, - struct mtd_oob_ops *ops) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - /* - * Initialise to all 0xFF, to avoid the possibility of left over OOB - * data from a previous OOB read. - */ - memset(chip->oob_poi, 0xff, mtd->oobsize); - - switch (ops->mode) { - - case MTD_OPS_PLACE_OOB: - case MTD_OPS_RAW: - memcpy(chip->oob_poi + ops->ooboffs, oob, len); - return oob + len; - - case MTD_OPS_AUTO_OOB: - ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi, - ops->ooboffs, len); - BUG_ON(ret); - return oob + len; - - default: - BUG(); - } - return NULL; -} - -#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0) - -/** - * nand_do_write_ops - [INTERN] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operations description structure - * - * NAND write with ECC. - */ -static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int chipnr, realpage, page, column; - struct nand_chip *chip = mtd_to_nand(mtd); - uint32_t writelen = ops->len; - - uint32_t oobwritelen = ops->ooblen; - uint32_t oobmaxlen = mtd_oobavail(mtd, ops); - - uint8_t *oob = ops->oobbuf; - uint8_t *buf = ops->datbuf; - int ret; - int oob_required = oob ? 1 : 0; - - ops->retlen = 0; - if (!writelen) - return 0; - - /* Reject writes, which are not page aligned */ - if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { - pr_notice("%s: attempt to write non page aligned data\n", - __func__); - return -EINVAL; - } - - column = to & (mtd->writesize - 1); - - chipnr = (int)(to >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - ret = -EIO; - goto err_out; - } - - realpage = (int)(to >> chip->page_shift); - page = realpage & chip->pagemask; - - /* Invalidate the page cache, when we write to the cached page */ - if (to <= ((loff_t)chip->pagebuf << chip->page_shift) && - ((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len)) - chip->pagebuf = -1; - - /* Don't allow multipage oob writes with offset */ - if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) { - ret = -EINVAL; - goto err_out; - } - - while (1) { - int bytes = mtd->writesize; - uint8_t *wbuf = buf; - int use_bufpoi; - int part_pagewr = (column || writelen < mtd->writesize); - - if (part_pagewr) - use_bufpoi = 1; - else if (chip->options & NAND_USE_BOUNCE_BUFFER) - use_bufpoi = !virt_addr_valid(buf) || - !IS_ALIGNED((unsigned long)buf, - chip->buf_align); - else - use_bufpoi = 0; - - /* Partial page write?, or need to use bounce buffer */ - if (use_bufpoi) { - pr_debug("%s: using write bounce buffer for buf@%p\n", - __func__, buf); - if (part_pagewr) - bytes = min_t(int, bytes - column, writelen); - chip->pagebuf = -1; - memset(chip->data_buf, 0xff, mtd->writesize); - memcpy(&chip->data_buf[column], buf, bytes); - wbuf = chip->data_buf; - } - - if (unlikely(oob)) { - size_t len = min(oobwritelen, oobmaxlen); - oob = nand_fill_oob(mtd, oob, len, ops); - oobwritelen -= len; - } else { - /* We still need to erase leftover OOB data */ - memset(chip->oob_poi, 0xff, mtd->oobsize); - } - - ret = nand_write_page(mtd, chip, column, bytes, wbuf, - oob_required, page, - (ops->mode == MTD_OPS_RAW)); - if (ret) - break; - - writelen -= bytes; - if (!writelen) - break; - - column = 0; - buf += bytes; - realpage++; - - page = realpage & chip->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - - ops->retlen = ops->len - writelen; - if (unlikely(oob)) - ops->oobretlen = ops->ooblen; - -err_out: - chip->select_chip(mtd, -1); - return ret; -} - -/** - * panic_nand_write - [MTD Interface] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write - * - * NAND write with ECC. Used when performing writes in interrupt context, this - * may for example be called by mtdoops when writing an oops while in panic. - */ -static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int chipnr = (int)(to >> chip->chip_shift); - struct mtd_oob_ops ops; - int ret; - - /* Grab the device */ - panic_nand_get_device(chip, mtd, FL_WRITING); - - chip->select_chip(mtd, chipnr); - - /* Wait for the device to get ready */ - panic_nand_wait(mtd, chip, 400); - - memset(&ops, 0, sizeof(ops)); - ops.len = len; - ops.datbuf = (uint8_t *)buf; - ops.mode = MTD_OPS_PLACE_OOB; - - ret = nand_do_write_ops(mtd, to, &ops); - - *retlen = ops.retlen; - return ret; -} - -/** - * nand_do_write_oob - [MTD Interface] NAND write out-of-band - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operation description structure - * - * NAND write out-of-band. - */ -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int chipnr, page, status, len; - struct nand_chip *chip = mtd_to_nand(mtd); - - pr_debug("%s: to = 0x%08x, len = %i\n", - __func__, (unsigned int)to, (int)ops->ooblen); - - len = mtd_oobavail(mtd, ops); - - /* Do not allow write past end of page */ - if ((ops->ooboffs + ops->ooblen) > len) { - pr_debug("%s: attempt to write past end of page\n", - __func__); - return -EINVAL; - } - - chipnr = (int)(to >> chip->chip_shift); - - /* - * Reset the chip. Some chips (like the Toshiba TC5832DC found in one - * of my DiskOnChip 2000 test units) will clear the whole data page too - * if we don't do this. I have no clue why, but I seem to have 'fixed' - * it in the doc2000 driver in August 1999. dwmw2. - */ - nand_reset(chip, chipnr); - - chip->select_chip(mtd, chipnr); - - /* Shift to get page */ - page = (int)(to >> chip->page_shift); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - chip->select_chip(mtd, -1); - return -EROFS; - } - - /* Invalidate the page cache, if we write to the cached page */ - if (page == chip->pagebuf) - chip->pagebuf = -1; - - nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); - - if (ops->mode == MTD_OPS_RAW) - status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); - else - status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); - - chip->select_chip(mtd, -1); - - if (status) - return status; - - ops->oobretlen = ops->ooblen; - - return 0; -} - -/** - * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operation description structure - */ -static int nand_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int ret = -ENOTSUPP; - - ops->retlen = 0; - - nand_get_device(mtd, FL_WRITING); - - switch (ops->mode) { - case MTD_OPS_PLACE_OOB: - case MTD_OPS_AUTO_OOB: - case MTD_OPS_RAW: - break; - - default: - goto out; - } - - if (!ops->datbuf) - ret = nand_do_write_oob(mtd, to, ops); - else - ret = nand_do_write_ops(mtd, to, ops); - -out: - nand_release_device(mtd); - return ret; -} - -/** - * single_erase - [GENERIC] NAND standard block erase command function - * @mtd: MTD device structure - * @page: the page address of the block which will be erased - * - * Standard erase command for NAND chips. Returns NAND status. - */ -static int single_erase(struct mtd_info *mtd, int page) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - unsigned int eraseblock; - - /* Send commands to erase a block */ - eraseblock = page >> (chip->phys_erase_shift - chip->page_shift); - - return nand_erase_op(chip, eraseblock); -} - -/** - * nand_erase - [MTD Interface] erase block(s) - * @mtd: MTD device structure - * @instr: erase instruction - * - * Erase one ore more blocks. - */ -static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - return nand_erase_nand(mtd, instr, 0); -} - -/** - * nand_erase_nand - [INTERN] erase block(s) - * @mtd: MTD device structure - * @instr: erase instruction - * @allowbbt: allow erasing the bbt area - * - * Erase one ore more blocks. - */ -int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, - int allowbbt) -{ - int page, status, pages_per_block, ret, chipnr; - struct nand_chip *chip = mtd_to_nand(mtd); - loff_t len; - - pr_debug("%s: start = 0x%012llx, len = %llu\n", - __func__, (unsigned long long)instr->addr, - (unsigned long long)instr->len); - - if (check_offs_len(mtd, instr->addr, instr->len)) - return -EINVAL; - - /* Grab the lock and see if the device is available */ - nand_get_device(mtd, FL_ERASING); - - /* Shift to get first page */ - page = (int)(instr->addr >> chip->page_shift); - chipnr = (int)(instr->addr >> chip->chip_shift); - - /* Calculate pages in each block */ - pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); - - /* Select the NAND device */ - chip->select_chip(mtd, chipnr); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - pr_debug("%s: device is write protected!\n", - __func__); - instr->state = MTD_ERASE_FAILED; - goto erase_exit; - } - - /* Loop through the pages */ - len = instr->len; - - instr->state = MTD_ERASING; - - while (len) { - /* Check if we have a bad block, we do not erase bad blocks! */ - if (nand_block_checkbad(mtd, ((loff_t) page) << - chip->page_shift, allowbbt)) { - pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", - __func__, page); - instr->state = MTD_ERASE_FAILED; - goto erase_exit; - } - - /* - * Invalidate the page cache, if we erase the block which - * contains the current cached page. - */ - if (page <= chip->pagebuf && chip->pagebuf < - (page + pages_per_block)) - chip->pagebuf = -1; - - status = chip->erase(mtd, page & chip->pagemask); - - /* See if block erase succeeded */ - if (status) { - pr_debug("%s: failed erase, page 0x%08x\n", - __func__, page); - instr->state = MTD_ERASE_FAILED; - instr->fail_addr = - ((loff_t)page << chip->page_shift); - goto erase_exit; - } - - /* Increment page address and decrement length */ - len -= (1ULL << chip->phys_erase_shift); - page += pages_per_block; - - /* Check, if we cross a chip boundary */ - if (len && !(page & chip->pagemask)) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - instr->state = MTD_ERASE_DONE; - -erase_exit: - - ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; - - /* Deselect and wake up anyone waiting on the device */ - chip->select_chip(mtd, -1); - nand_release_device(mtd); - - /* Do call back function */ - if (!ret) - mtd_erase_callback(instr); - - /* Return more or less happy */ - return ret; -} - -/** - * nand_sync - [MTD Interface] sync - * @mtd: MTD device structure - * - * Sync is actually a wait for chip ready function. - */ -static void nand_sync(struct mtd_info *mtd) -{ - pr_debug("%s: called\n", __func__); - - /* Grab the lock and see if the device is available */ - nand_get_device(mtd, FL_SYNCING); - /* Release it and go back */ - nand_release_device(mtd); -} - -/** - * nand_block_isbad - [MTD Interface] Check if block at offset is bad - * @mtd: MTD device structure - * @offs: offset relative to mtd start - */ -static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int chipnr = (int)(offs >> chip->chip_shift); - int ret; - - /* Select the NAND device */ - nand_get_device(mtd, FL_READING); - chip->select_chip(mtd, chipnr); - - ret = nand_block_checkbad(mtd, offs, 0); - - chip->select_chip(mtd, -1); - nand_release_device(mtd); - - return ret; -} - -/** - * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad - * @mtd: MTD device structure - * @ofs: offset relative to mtd start - */ -static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - int ret; - - ret = nand_block_isbad(mtd, ofs); - if (ret) { - /* If it was bad already, return success and do nothing */ - if (ret > 0) - return 0; - return ret; - } - - return nand_block_markbad_lowlevel(mtd, ofs); -} - -/** - * nand_max_bad_blocks - [MTD Interface] Max number of bad blocks for an mtd - * @mtd: MTD device structure - * @ofs: offset relative to mtd start - * @len: length of mtd - */ -static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u32 part_start_block; - u32 part_end_block; - u32 part_start_die; - u32 part_end_die; - - /* - * max_bb_per_die and blocks_per_die used to determine - * the maximum bad block count. - */ - if (!chip->max_bb_per_die || !chip->blocks_per_die) - return -ENOTSUPP; - - /* Get the start and end of the partition in erase blocks. */ - part_start_block = mtd_div_by_eb(ofs, mtd); - part_end_block = mtd_div_by_eb(len, mtd) + part_start_block - 1; - - /* Get the start and end LUNs of the partition. */ - part_start_die = part_start_block / chip->blocks_per_die; - part_end_die = part_end_block / chip->blocks_per_die; - - /* - * Look up the bad blocks per unit and multiply by the number of units - * that the partition spans. - */ - return chip->max_bb_per_die * (part_end_die - part_start_die + 1); -} - -/** - * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand - * @mtd: MTD device structure - * @chip: nand chip info structure - * @addr: feature address. - * @subfeature_param: the subfeature parameters, a four bytes array. - */ -static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, - int addr, uint8_t *subfeature_param) -{ - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - - return nand_set_features_op(chip, addr, subfeature_param); -} - -/** - * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand - * @mtd: MTD device structure - * @chip: nand chip info structure - * @addr: feature address. - * @subfeature_param: the subfeature parameters, a four bytes array. - */ -static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, - int addr, uint8_t *subfeature_param) -{ - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - - return nand_get_features_op(chip, addr, subfeature_param); -} - -/** - * nand_onfi_get_set_features_notsupp - set/get features stub returning - * -ENOTSUPP - * @mtd: MTD device structure - * @chip: nand chip info structure - * @addr: feature address. - * @subfeature_param: the subfeature parameters, a four bytes array. - * - * Should be used by NAND controller drivers that do not support the SET/GET - * FEATURES operations. - */ -int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param) -{ - return -ENOTSUPP; -} -EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp); - -/** - * nand_suspend - [MTD Interface] Suspend the NAND flash - * @mtd: MTD device structure - */ -static int nand_suspend(struct mtd_info *mtd) -{ - return nand_get_device(mtd, FL_PM_SUSPENDED); -} - -/** - * nand_resume - [MTD Interface] Resume the NAND flash - * @mtd: MTD device structure - */ -static void nand_resume(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (chip->state == FL_PM_SUSPENDED) - nand_release_device(mtd); - else - pr_err("%s called for a chip which is not in suspended state\n", - __func__); -} - -/** - * nand_shutdown - [MTD Interface] Finish the current NAND operation and - * prevent further operations - * @mtd: MTD device structure - */ -static void nand_shutdown(struct mtd_info *mtd) -{ - nand_get_device(mtd, FL_PM_SUSPENDED); -} - -/* Set default functions */ -static void nand_set_defaults(struct nand_chip *chip) -{ - unsigned int busw = chip->options & NAND_BUSWIDTH_16; - - /* check for proper chip_delay setup, set 20us if not */ - if (!chip->chip_delay) - chip->chip_delay = 20; - - /* check, if a user supplied command function given */ - if (!chip->cmdfunc && !chip->exec_op) - chip->cmdfunc = nand_command; - - /* check, if a user supplied wait function given */ - if (chip->waitfunc == NULL) - chip->waitfunc = nand_wait; - - if (!chip->select_chip) - chip->select_chip = nand_select_chip; - - /* set for ONFI nand */ - if (!chip->onfi_set_features) - chip->onfi_set_features = nand_onfi_set_features; - if (!chip->onfi_get_features) - chip->onfi_get_features = nand_onfi_get_features; - - /* If called twice, pointers that depend on busw may need to be reset */ - if (!chip->read_byte || chip->read_byte == nand_read_byte) - chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; - if (!chip->read_word) - chip->read_word = nand_read_word; - if (!chip->block_bad) - chip->block_bad = nand_block_bad; - if (!chip->block_markbad) - chip->block_markbad = nand_default_block_markbad; - if (!chip->write_buf || chip->write_buf == nand_write_buf) - chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; - if (!chip->write_byte || chip->write_byte == nand_write_byte) - chip->write_byte = busw ? nand_write_byte16 : nand_write_byte; - if (!chip->read_buf || chip->read_buf == nand_read_buf) - chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; - if (!chip->scan_bbt) - chip->scan_bbt = nand_default_bbt; - - if (!chip->controller) { - chip->controller = &chip->hwcontrol; - nand_hw_control_init(chip->controller); - } - - if (!chip->buf_align) - chip->buf_align = 1; -} - -/* Sanitize ONFI strings so we can safely print them */ -static void sanitize_string(uint8_t *s, size_t len) -{ - ssize_t i; - - /* Null terminate */ - s[len - 1] = 0; - - /* Remove non printable chars */ - for (i = 0; i < len - 1; i++) { - if (s[i] < ' ' || s[i] > 127) - s[i] = '?'; - } - - /* Remove trailing spaces */ - strim(s); -} - -static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) -{ - int i; - while (len--) { - crc ^= *p++ << 8; - for (i = 0; i < 8; i++) - crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); - } - - return crc; -} - -/* Parse the Extended Parameter Page. */ -static int nand_flash_detect_ext_param_page(struct nand_chip *chip, - struct nand_onfi_params *p) -{ - struct onfi_ext_param_page *ep; - struct onfi_ext_section *s; - struct onfi_ext_ecc_info *ecc; - uint8_t *cursor; - int ret; - int len; - int i; - - len = le16_to_cpu(p->ext_param_page_length) * 16; - ep = kmalloc(len, GFP_KERNEL); - if (!ep) - return -ENOMEM; - - /* Send our own NAND_CMD_PARAM. */ - ret = nand_read_param_page_op(chip, 0, NULL, 0); - if (ret) - goto ext_out; - - /* Use the Change Read Column command to skip the ONFI param pages. */ - ret = nand_change_read_column_op(chip, - sizeof(*p) * p->num_of_param_pages, - ep, len, true); - if (ret) - goto ext_out; - - ret = -EINVAL; - if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) - != le16_to_cpu(ep->crc))) { - pr_debug("fail in the CRC.\n"); - goto ext_out; - } - - /* - * Check the signature. - * Do not strictly follow the ONFI spec, maybe changed in future. - */ - if (strncmp(ep->sig, "EPPS", 4)) { - pr_debug("The signature is invalid.\n"); - goto ext_out; - } - - /* find the ECC section. */ - cursor = (uint8_t *)(ep + 1); - for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { - s = ep->sections + i; - if (s->type == ONFI_SECTION_TYPE_2) - break; - cursor += s->length * 16; - } - if (i == ONFI_EXT_SECTION_MAX) { - pr_debug("We can not find the ECC section.\n"); - goto ext_out; - } - - /* get the info we want. */ - ecc = (struct onfi_ext_ecc_info *)cursor; - - if (!ecc->codeword_size) { - pr_debug("Invalid codeword size\n"); - goto ext_out; - } - - chip->ecc_strength_ds = ecc->ecc_bits; - chip->ecc_step_ds = 1 << ecc->codeword_size; - ret = 0; - -ext_out: - kfree(ep); - return ret; -} - -/* - * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. - */ -static int nand_flash_detect_onfi(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_onfi_params *p = &chip->onfi_params; - char id[4]; - int i, ret, val; - - /* Try ONFI for unknown chip or LP */ - ret = nand_readid_op(chip, 0x20, id, sizeof(id)); - if (ret || strncmp(id, "ONFI", 4)) - return 0; - - ret = nand_read_param_page_op(chip, 0, NULL, 0); - if (ret) - return 0; - - for (i = 0; i < 3; i++) { - ret = nand_read_data_op(chip, p, sizeof(*p), true); - if (ret) - return 0; - - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == - le16_to_cpu(p->crc)) { - break; - } - } - - if (i == 3) { - pr_err("Could not find valid ONFI parameter page; aborting\n"); - return 0; - } - - /* Check version */ - val = le16_to_cpu(p->revision); - if (val & (1 << 5)) - chip->onfi_version = 23; - else if (val & (1 << 4)) - chip->onfi_version = 22; - else if (val & (1 << 3)) - chip->onfi_version = 21; - else if (val & (1 << 2)) - chip->onfi_version = 20; - else if (val & (1 << 1)) - chip->onfi_version = 10; - - if (!chip->onfi_version) { - pr_info("unsupported ONFI version: %d\n", val); - return 0; - } - - sanitize_string(p->manufacturer, sizeof(p->manufacturer)); - sanitize_string(p->model, sizeof(p->model)); - if (!mtd->name) - mtd->name = p->model; - - mtd->writesize = le32_to_cpu(p->byte_per_page); - - /* - * pages_per_block and blocks_per_lun may not be a power-of-2 size - * (don't ask me who thought of this...). MTD assumes that these - * dimensions will be power-of-2, so just truncate the remaining area. - */ - mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); - mtd->erasesize *= mtd->writesize; - - mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - - /* See erasesize comment */ - chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); - chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; - chip->bits_per_cell = p->bits_per_cell; - - chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun); - chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun); - - if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) - chip->options |= NAND_BUSWIDTH_16; - - if (p->ecc_bits != 0xff) { - chip->ecc_strength_ds = p->ecc_bits; - chip->ecc_step_ds = 512; - } else if (chip->onfi_version >= 21 && - (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { - - /* - * The nand_flash_detect_ext_param_page() uses the - * Change Read Column command which maybe not supported - * by the chip->cmdfunc. So try to update the chip->cmdfunc - * now. We do not replace user supplied command function. - */ - if (mtd->writesize > 512 && chip->cmdfunc == nand_command) - chip->cmdfunc = nand_command_lp; - - /* The Extended Parameter Page is supported since ONFI 2.1. */ - if (nand_flash_detect_ext_param_page(chip, p)) - pr_warn("Failed to detect ONFI extended param page\n"); - } else { - pr_warn("Could not retrieve ONFI ECC requirements\n"); - } - - return 1; -} - -/* - * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise. - */ -static int nand_flash_detect_jedec(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_jedec_params *p = &chip->jedec_params; - struct jedec_ecc_info *ecc; - char id[5]; - int i, val, ret; - - /* Try JEDEC for unknown chip or LP */ - ret = nand_readid_op(chip, 0x40, id, sizeof(id)); - if (ret || strncmp(id, "JEDEC", sizeof(id))) - return 0; - - ret = nand_read_param_page_op(chip, 0x40, NULL, 0); - if (ret) - return 0; - - for (i = 0; i < 3; i++) { - ret = nand_read_data_op(chip, p, sizeof(*p), true); - if (ret) - return 0; - - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == - le16_to_cpu(p->crc)) - break; - } - - if (i == 3) { - pr_err("Could not find valid JEDEC parameter page; aborting\n"); - return 0; - } - - /* Check version */ - val = le16_to_cpu(p->revision); - if (val & (1 << 2)) - chip->jedec_version = 10; - else if (val & (1 << 1)) - chip->jedec_version = 1; /* vendor specific version */ - - if (!chip->jedec_version) { - pr_info("unsupported JEDEC version: %d\n", val); - return 0; - } - - sanitize_string(p->manufacturer, sizeof(p->manufacturer)); - sanitize_string(p->model, sizeof(p->model)); - if (!mtd->name) - mtd->name = p->model; - - mtd->writesize = le32_to_cpu(p->byte_per_page); - - /* Please reference to the comment for nand_flash_detect_onfi. */ - mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); - mtd->erasesize *= mtd->writesize; - - mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - - /* Please reference to the comment for nand_flash_detect_onfi. */ - chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); - chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; - chip->bits_per_cell = p->bits_per_cell; - - if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS) - chip->options |= NAND_BUSWIDTH_16; - - /* ECC info */ - ecc = &p->ecc_info[0]; - - if (ecc->codeword_size >= 9) { - chip->ecc_strength_ds = ecc->ecc_bits; - chip->ecc_step_ds = 1 << ecc->codeword_size; - } else { - pr_warn("Invalid codeword size\n"); - } - - return 1; -} - -/* - * nand_id_has_period - Check if an ID string has a given wraparound period - * @id_data: the ID string - * @arrlen: the length of the @id_data array - * @period: the period of repitition - * - * Check if an ID string is repeated within a given sequence of bytes at - * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a - * period of 3). This is a helper function for nand_id_len(). Returns non-zero - * if the repetition has a period of @period; otherwise, returns zero. - */ -static int nand_id_has_period(u8 *id_data, int arrlen, int period) -{ - int i, j; - for (i = 0; i < period; i++) - for (j = i + period; j < arrlen; j += period) - if (id_data[i] != id_data[j]) - return 0; - return 1; -} - -/* - * nand_id_len - Get the length of an ID string returned by CMD_READID - * @id_data: the ID string - * @arrlen: the length of the @id_data array - - * Returns the length of the ID string, according to known wraparound/trailing - * zero patterns. If no pattern exists, returns the length of the array. - */ -static int nand_id_len(u8 *id_data, int arrlen) -{ - int last_nonzero, period; - - /* Find last non-zero byte */ - for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--) - if (id_data[last_nonzero]) - break; - - /* All zeros */ - if (last_nonzero < 0) - return 0; - - /* Calculate wraparound period */ - for (period = 1; period < arrlen; period++) - if (nand_id_has_period(id_data, arrlen, period)) - break; - - /* There's a repeated pattern */ - if (period < arrlen) - return period; - - /* There are trailing zeros */ - if (last_nonzero < arrlen - 1) - return last_nonzero + 1; - - /* No pattern detected */ - return arrlen; -} - -/* Extract the bits of per cell from the 3rd byte of the extended ID */ -static int nand_get_bits_per_cell(u8 cellinfo) -{ - int bits; - - bits = cellinfo & NAND_CI_CELLTYPE_MSK; - bits >>= NAND_CI_CELLTYPE_SHIFT; - return bits + 1; -} - -/* - * Many new NAND share similar device ID codes, which represent the size of the - * chip. The rest of the parameters must be decoded according to generic or - * manufacturer-specific "extended ID" decoding patterns. - */ -void nand_decode_ext_id(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int extid; - u8 *id_data = chip->id.data; - /* The 3rd id byte holds MLC / multichip data */ - chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); - /* The 4th id byte is the important one */ - extid = id_data[3]; - - /* Calc pagesize */ - mtd->writesize = 1024 << (extid & 0x03); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - if (extid & 0x1) - chip->options |= NAND_BUSWIDTH_16; -} -EXPORT_SYMBOL_GPL(nand_decode_ext_id); - -/* - * Old devices have chip data hardcoded in the device ID table. nand_decode_id - * decodes a matching ID table entry and assigns the MTD size parameters for - * the chip. - */ -static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - mtd->erasesize = type->erasesize; - mtd->writesize = type->pagesize; - mtd->oobsize = mtd->writesize / 32; - - /* All legacy ID NAND are small-page, SLC */ - chip->bits_per_cell = 1; -} - -/* - * Set the bad block marker/indicator (BBM/BBI) patterns according to some - * heuristic patterns using various detected parameters (e.g., manufacturer, - * page size, cell-type information). - */ -static void nand_decode_bbm_options(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - /* Set the bad block position */ - if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16)) - chip->badblockpos = NAND_LARGE_BADBLOCK_POS; - else - chip->badblockpos = NAND_SMALL_BADBLOCK_POS; -} - -static inline bool is_full_id_nand(struct nand_flash_dev *type) -{ - return type->id_len; -} - -static bool find_full_id_nand(struct nand_chip *chip, - struct nand_flash_dev *type) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - u8 *id_data = chip->id.data; - - if (!strncmp(type->id, id_data, type->id_len)) { - mtd->writesize = type->pagesize; - mtd->erasesize = type->erasesize; - mtd->oobsize = type->oobsize; - - chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); - chip->chipsize = (uint64_t)type->chipsize << 20; - chip->options |= type->options; - chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); - chip->ecc_step_ds = NAND_ECC_STEP(type); - chip->onfi_timing_mode_default = - type->onfi_timing_mode_default; - - if (!mtd->name) - mtd->name = type->name; - - return true; - } - return false; -} - -/* - * Manufacturer detection. Only used when the NAND is not ONFI or JEDEC - * compliant and does not have a full-id or legacy-id entry in the nand_ids - * table. - */ -static void nand_manufacturer_detect(struct nand_chip *chip) -{ - /* - * Try manufacturer detection if available and use - * nand_decode_ext_id() otherwise. - */ - if (chip->manufacturer.desc && chip->manufacturer.desc->ops && - chip->manufacturer.desc->ops->detect) { - /* The 3rd id byte holds MLC / multichip data */ - chip->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]); - chip->manufacturer.desc->ops->detect(chip); - } else { - nand_decode_ext_id(chip); - } -} - -/* - * Manufacturer initialization. This function is called for all NANDs including - * ONFI and JEDEC compliant ones. - * Manufacturer drivers should put all their specific initialization code in - * their ->init() hook. - */ -static int nand_manufacturer_init(struct nand_chip *chip) -{ - if (!chip->manufacturer.desc || !chip->manufacturer.desc->ops || - !chip->manufacturer.desc->ops->init) - return 0; - - return chip->manufacturer.desc->ops->init(chip); -} - -/* - * Manufacturer cleanup. This function is called for all NANDs including - * ONFI and JEDEC compliant ones. - * Manufacturer drivers should put all their specific cleanup code in their - * ->cleanup() hook. - */ -static void nand_manufacturer_cleanup(struct nand_chip *chip) -{ - /* Release manufacturer private data */ - if (chip->manufacturer.desc && chip->manufacturer.desc->ops && - chip->manufacturer.desc->ops->cleanup) - chip->manufacturer.desc->ops->cleanup(chip); -} - -/* - * Get the flash and manufacturer id and lookup if the type is supported. - */ -static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) -{ - const struct nand_manufacturer *manufacturer; - struct mtd_info *mtd = nand_to_mtd(chip); - int busw, ret; - u8 *id_data = chip->id.data; - u8 maf_id, dev_id; - - /* - * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) - * after power-up. - */ - ret = nand_reset(chip, 0); - if (ret) - return ret; - - /* Select the device */ - chip->select_chip(mtd, 0); - - /* Send the command for reading device ID */ - ret = nand_readid_op(chip, 0, id_data, 2); - if (ret) - return ret; - - /* Read manufacturer and device IDs */ - maf_id = id_data[0]; - dev_id = id_data[1]; - - /* - * Try again to make sure, as some systems the bus-hold or other - * interface concerns can cause random data which looks like a - * possibly credible NAND flash to appear. If the two results do - * not match, ignore the device completely. - */ - - /* Read entire ID string */ - ret = nand_readid_op(chip, 0, id_data, sizeof(chip->id.data)); - if (ret) - return ret; - - if (id_data[0] != maf_id || id_data[1] != dev_id) { - pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", - maf_id, dev_id, id_data[0], id_data[1]); - return -ENODEV; - } - - chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data)); - - /* Try to identify manufacturer */ - manufacturer = nand_get_manufacturer(maf_id); - chip->manufacturer.desc = manufacturer; - - if (!type) - type = nand_flash_ids; - - /* - * Save the NAND_BUSWIDTH_16 flag before letting auto-detection logic - * override it. - * This is required to make sure initial NAND bus width set by the - * NAND controller driver is coherent with the real NAND bus width - * (extracted by auto-detection code). - */ - busw = chip->options & NAND_BUSWIDTH_16; - - /* - * The flag is only set (never cleared), reset it to its default value - * before starting auto-detection. - */ - chip->options &= ~NAND_BUSWIDTH_16; - - for (; type->name != NULL; type++) { - if (is_full_id_nand(type)) { - if (find_full_id_nand(chip, type)) - goto ident_done; - } else if (dev_id == type->dev_id) { - break; - } - } - - chip->onfi_version = 0; - if (!type->name || !type->pagesize) { - /* Check if the chip is ONFI compliant */ - if (nand_flash_detect_onfi(chip)) - goto ident_done; - - /* Check if the chip is JEDEC compliant */ - if (nand_flash_detect_jedec(chip)) - goto ident_done; - } - - if (!type->name) - return -ENODEV; - - if (!mtd->name) - mtd->name = type->name; - - chip->chipsize = (uint64_t)type->chipsize << 20; - - if (!type->pagesize) - nand_manufacturer_detect(chip); - else - nand_decode_id(chip, type); - - /* Get chip options */ - chip->options |= type->options; - -ident_done: - - if (chip->options & NAND_BUSWIDTH_AUTO) { - WARN_ON(busw & NAND_BUSWIDTH_16); - nand_set_defaults(chip); - } else if (busw != (chip->options & NAND_BUSWIDTH_16)) { - /* - * Check, if buswidth is correct. Hardware drivers should set - * chip correct! - */ - pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - maf_id, dev_id); - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - mtd->name); - pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8, - (chip->options & NAND_BUSWIDTH_16) ? 16 : 8); - return -EINVAL; - } - - nand_decode_bbm_options(chip); - - /* Calculate the address shift from the page size */ - chip->page_shift = ffs(mtd->writesize) - 1; - /* Convert chipsize to number of pages per chip -1 */ - chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; - - chip->bbt_erase_shift = chip->phys_erase_shift = - ffs(mtd->erasesize) - 1; - if (chip->chipsize & 0xffffffff) - chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; - else { - chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); - chip->chip_shift += 32 - 1; - } - - if (chip->chip_shift - chip->page_shift > 16) - chip->options |= NAND_ROW_ADDR_3; - - chip->badblockbits = 8; - chip->erase = single_erase; - - /* Do not replace user supplied command function! */ - if (mtd->writesize > 512 && chip->cmdfunc == nand_command) - chip->cmdfunc = nand_command_lp; - - pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - maf_id, dev_id); - - if (chip->onfi_version) - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - chip->onfi_params.model); - else if (chip->jedec_version) - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - chip->jedec_params.model); - else - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - type->name); - - pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", - (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", - mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); - return 0; -} - -static const char * const nand_ecc_modes[] = { - [NAND_ECC_NONE] = "none", - [NAND_ECC_SOFT] = "soft", - [NAND_ECC_HW] = "hw", - [NAND_ECC_HW_SYNDROME] = "hw_syndrome", - [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first", - [NAND_ECC_ON_DIE] = "on-die", -}; - -static int of_get_nand_ecc_mode(struct device_node *np) -{ - const char *pm; - int err, i; - - err = of_property_read_string(np, "nand-ecc-mode", &pm); - if (err < 0) - return err; - - for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++) - if (!strcasecmp(pm, nand_ecc_modes[i])) - return i; - - /* - * For backward compatibility we support few obsoleted values that don't - * have their mappings into nand_ecc_modes_t anymore (they were merged - * with other enums). - */ - if (!strcasecmp(pm, "soft_bch")) - return NAND_ECC_SOFT; - - return -ENODEV; -} - -static const char * const nand_ecc_algos[] = { - [NAND_ECC_HAMMING] = "hamming", - [NAND_ECC_BCH] = "bch", -}; - -static int of_get_nand_ecc_algo(struct device_node *np) -{ - const char *pm; - int err, i; - - err = of_property_read_string(np, "nand-ecc-algo", &pm); - if (!err) { - for (i = NAND_ECC_HAMMING; i < ARRAY_SIZE(nand_ecc_algos); i++) - if (!strcasecmp(pm, nand_ecc_algos[i])) - return i; - return -ENODEV; - } - - /* - * For backward compatibility we also read "nand-ecc-mode" checking - * for some obsoleted values that were specifying ECC algorithm. - */ - err = of_property_read_string(np, "nand-ecc-mode", &pm); - if (err < 0) - return err; - - if (!strcasecmp(pm, "soft")) - return NAND_ECC_HAMMING; - else if (!strcasecmp(pm, "soft_bch")) - return NAND_ECC_BCH; - - return -ENODEV; -} - -static int of_get_nand_ecc_step_size(struct device_node *np) -{ - int ret; - u32 val; - - ret = of_property_read_u32(np, "nand-ecc-step-size", &val); - return ret ? ret : val; -} - -static int of_get_nand_ecc_strength(struct device_node *np) -{ - int ret; - u32 val; - - ret = of_property_read_u32(np, "nand-ecc-strength", &val); - return ret ? ret : val; -} - -static int of_get_nand_bus_width(struct device_node *np) -{ - u32 val; - - if (of_property_read_u32(np, "nand-bus-width", &val)) - return 8; - - switch (val) { - case 8: - case 16: - return val; - default: - return -EIO; - } -} - -static bool of_get_nand_on_flash_bbt(struct device_node *np) -{ - return of_property_read_bool(np, "nand-on-flash-bbt"); -} - -static int nand_dt_init(struct nand_chip *chip) -{ - struct device_node *dn = nand_get_flash_node(chip); - int ecc_mode, ecc_algo, ecc_strength, ecc_step; - - if (!dn) - return 0; - - if (of_get_nand_bus_width(dn) == 16) - chip->options |= NAND_BUSWIDTH_16; - - if (of_get_nand_on_flash_bbt(dn)) - chip->bbt_options |= NAND_BBT_USE_FLASH; - - ecc_mode = of_get_nand_ecc_mode(dn); - ecc_algo = of_get_nand_ecc_algo(dn); - ecc_strength = of_get_nand_ecc_strength(dn); - ecc_step = of_get_nand_ecc_step_size(dn); - - if (ecc_mode >= 0) - chip->ecc.mode = ecc_mode; - - if (ecc_algo >= 0) - chip->ecc.algo = ecc_algo; - - if (ecc_strength >= 0) - chip->ecc.strength = ecc_strength; - - if (ecc_step > 0) - chip->ecc.size = ecc_step; - - if (of_property_read_bool(dn, "nand-ecc-maximize")) - chip->ecc.options |= NAND_ECC_MAXIMIZE; - - return 0; -} - -/** - * nand_scan_ident - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * @maxchips: number of chips to scan for - * @table: alternative NAND ID table - * - * This is the first phase of the normal nand_scan() function. It reads the - * flash ID and sets up MTD fields accordingly. - * - */ -int nand_scan_ident(struct mtd_info *mtd, int maxchips, - struct nand_flash_dev *table) -{ - int i, nand_maf_id, nand_dev_id; - struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - /* Enforce the right timings for reset/detection */ - onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); - - ret = nand_dt_init(chip); - if (ret) - return ret; - - if (!mtd->name && mtd->dev.parent) - mtd->name = dev_name(mtd->dev.parent); - - /* - * ->cmdfunc() is legacy and will only be used if ->exec_op() is not - * populated. - */ - if (!chip->exec_op) { - /* - * Default functions assigned for ->cmdfunc() and - * ->select_chip() both expect ->cmd_ctrl() to be populated. - */ - if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) { - pr_err("->cmd_ctrl() should be provided\n"); - return -EINVAL; - } - } - - /* Set the default functions */ - nand_set_defaults(chip); - - /* Read the flash type */ - ret = nand_detect(chip, table); - if (ret) { - if (!(chip->options & NAND_SCAN_SILENT_NODEV)) - pr_warn("No NAND device found\n"); - chip->select_chip(mtd, -1); - return ret; - } - - nand_maf_id = chip->id.data[0]; - nand_dev_id = chip->id.data[1]; - - chip->select_chip(mtd, -1); - - /* Check for a chip array */ - for (i = 1; i < maxchips; i++) { - u8 id[2]; - - /* See comment in nand_get_flash_type for reset */ - nand_reset(chip, i); - - chip->select_chip(mtd, i); - /* Send the command for reading device ID */ - nand_readid_op(chip, 0, id, sizeof(id)); - /* Read manufacturer and device IDs */ - if (nand_maf_id != id[0] || nand_dev_id != id[1]) { - chip->select_chip(mtd, -1); - break; - } - chip->select_chip(mtd, -1); - } - if (i > 1) - pr_info("%d chips detected\n", i); - - /* Store the number of chips and calc total size for mtd */ - chip->numchips = i; - mtd->size = i * chip->chipsize; - - return 0; -} -EXPORT_SYMBOL(nand_scan_ident); - -static int nand_set_ecc_soft_ops(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (WARN_ON(ecc->mode != NAND_ECC_SOFT)) - return -EINVAL; - - switch (ecc->algo) { - case NAND_ECC_HAMMING: - ecc->calculate = nand_calculate_ecc; - ecc->correct = nand_correct_data; - ecc->read_page = nand_read_page_swecc; - ecc->read_subpage = nand_read_subpage; - ecc->write_page = nand_write_page_swecc; - ecc->read_page_raw = nand_read_page_raw; - ecc->write_page_raw = nand_write_page_raw; - ecc->read_oob = nand_read_oob_std; - ecc->write_oob = nand_write_oob_std; - if (!ecc->size) - ecc->size = 256; - ecc->bytes = 3; - ecc->strength = 1; - return 0; - case NAND_ECC_BCH: - if (!mtd_nand_has_bch()) { - WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); - return -EINVAL; - } - ecc->calculate = nand_bch_calculate_ecc; - ecc->correct = nand_bch_correct_data; - ecc->read_page = nand_read_page_swecc; - ecc->read_subpage = nand_read_subpage; - ecc->write_page = nand_write_page_swecc; - ecc->read_page_raw = nand_read_page_raw; - ecc->write_page_raw = nand_write_page_raw; - ecc->read_oob = nand_read_oob_std; - ecc->write_oob = nand_write_oob_std; - - /* - * Board driver should supply ecc.size and ecc.strength - * values to select how many bits are correctable. - * Otherwise, default to 4 bits for large page devices. - */ - if (!ecc->size && (mtd->oobsize >= 64)) { - ecc->size = 512; - ecc->strength = 4; - } - - /* - * if no ecc placement scheme was provided pickup the default - * large page one. - */ - if (!mtd->ooblayout) { - /* handle large page devices only */ - if (mtd->oobsize < 64) { - WARN(1, "OOB layout is required when using software BCH on small pages\n"); - return -EINVAL; - } - - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); - - } - - /* - * We can only maximize ECC config when the default layout is - * used, otherwise we don't know how many bytes can really be - * used. - */ - if (mtd->ooblayout == &nand_ooblayout_lp_ops && - ecc->options & NAND_ECC_MAXIMIZE) { - int steps, bytes; - - /* Always prefer 1k blocks over 512bytes ones */ - ecc->size = 1024; - steps = mtd->writesize / ecc->size; - - /* Reserve 2 bytes for the BBM */ - bytes = (mtd->oobsize - 2) / steps; - ecc->strength = bytes * 8 / fls(8 * ecc->size); - } - - /* See nand_bch_init() for details. */ - ecc->bytes = 0; - ecc->priv = nand_bch_init(mtd); - if (!ecc->priv) { - WARN(1, "BCH ECC initialization failed!\n"); - return -EINVAL; - } - return 0; - default: - WARN(1, "Unsupported ECC algorithm!\n"); - return -EINVAL; - } -} - -/** - * nand_check_ecc_caps - check the sanity of preset ECC settings - * @chip: nand chip info structure - * @caps: ECC caps info structure - * @oobavail: OOB size that the ECC engine can use - * - * When ECC step size and strength are already set, check if they are supported - * by the controller and the calculated ECC bytes fit within the chip's OOB. - * On success, the calculated ECC bytes is set. - */ -int nand_check_ecc_caps(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_ecc_step_info *stepinfo; - int preset_step = chip->ecc.size; - int preset_strength = chip->ecc.strength; - int nsteps, ecc_bytes; - int i, j; - - if (WARN_ON(oobavail < 0)) - return -EINVAL; - - if (!preset_step || !preset_strength) - return -ENODATA; - - nsteps = mtd->writesize / preset_step; - - for (i = 0; i < caps->nstepinfos; i++) { - stepinfo = &caps->stepinfos[i]; - - if (stepinfo->stepsize != preset_step) - continue; - - for (j = 0; j < stepinfo->nstrengths; j++) { - if (stepinfo->strengths[j] != preset_strength) - continue; - - ecc_bytes = caps->calc_ecc_bytes(preset_step, - preset_strength); - if (WARN_ON_ONCE(ecc_bytes < 0)) - return ecc_bytes; - - if (ecc_bytes * nsteps > oobavail) { - pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB", - preset_step, preset_strength); - return -ENOSPC; - } - - chip->ecc.bytes = ecc_bytes; - - return 0; - } - } - - pr_err("ECC (step, strength) = (%d, %d) not supported on this controller", - preset_step, preset_strength); - - return -ENOTSUPP; -} -EXPORT_SYMBOL_GPL(nand_check_ecc_caps); - -/** - * nand_match_ecc_req - meet the chip's requirement with least ECC bytes - * @chip: nand chip info structure - * @caps: ECC engine caps info structure - * @oobavail: OOB size that the ECC engine can use - * - * If a chip's ECC requirement is provided, try to meet it with the least - * number of ECC bytes (i.e. with the largest number of OOB-free bytes). - * On success, the chosen ECC settings are set. - */ -int nand_match_ecc_req(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_ecc_step_info *stepinfo; - int req_step = chip->ecc_step_ds; - int req_strength = chip->ecc_strength_ds; - int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total; - int best_step, best_strength, best_ecc_bytes; - int best_ecc_bytes_total = INT_MAX; - int i, j; - - if (WARN_ON(oobavail < 0)) - return -EINVAL; - - /* No information provided by the NAND chip */ - if (!req_step || !req_strength) - return -ENOTSUPP; - - /* number of correctable bits the chip requires in a page */ - req_corr = mtd->writesize / req_step * req_strength; - - for (i = 0; i < caps->nstepinfos; i++) { - stepinfo = &caps->stepinfos[i]; - step_size = stepinfo->stepsize; - - for (j = 0; j < stepinfo->nstrengths; j++) { - strength = stepinfo->strengths[j]; - - /* - * If both step size and strength are smaller than the - * chip's requirement, it is not easy to compare the - * resulted reliability. - */ - if (step_size < req_step && strength < req_strength) - continue; - - if (mtd->writesize % step_size) - continue; - - nsteps = mtd->writesize / step_size; - - ecc_bytes = caps->calc_ecc_bytes(step_size, strength); - if (WARN_ON_ONCE(ecc_bytes < 0)) - continue; - ecc_bytes_total = ecc_bytes * nsteps; - - if (ecc_bytes_total > oobavail || - strength * nsteps < req_corr) - continue; - - /* - * We assume the best is to meet the chip's requrement - * with the least number of ECC bytes. - */ - if (ecc_bytes_total < best_ecc_bytes_total) { - best_ecc_bytes_total = ecc_bytes_total; - best_step = step_size; - best_strength = strength; - best_ecc_bytes = ecc_bytes; - } - } - } - - if (best_ecc_bytes_total == INT_MAX) - return -ENOTSUPP; - - chip->ecc.size = best_step; - chip->ecc.strength = best_strength; - chip->ecc.bytes = best_ecc_bytes; - - return 0; -} -EXPORT_SYMBOL_GPL(nand_match_ecc_req); - -/** - * nand_maximize_ecc - choose the max ECC strength available - * @chip: nand chip info structure - * @caps: ECC engine caps info structure - * @oobavail: OOB size that the ECC engine can use - * - * Choose the max ECC strength that is supported on the controller, and can fit - * within the chip's OOB. On success, the chosen ECC settings are set. - */ -int nand_maximize_ecc(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_ecc_step_info *stepinfo; - int step_size, strength, nsteps, ecc_bytes, corr; - int best_corr = 0; - int best_step = 0; - int best_strength, best_ecc_bytes; - int i, j; - - if (WARN_ON(oobavail < 0)) - return -EINVAL; - - for (i = 0; i < caps->nstepinfos; i++) { - stepinfo = &caps->stepinfos[i]; - step_size = stepinfo->stepsize; - - /* If chip->ecc.size is already set, respect it */ - if (chip->ecc.size && step_size != chip->ecc.size) - continue; - - for (j = 0; j < stepinfo->nstrengths; j++) { - strength = stepinfo->strengths[j]; - - if (mtd->writesize % step_size) - continue; - - nsteps = mtd->writesize / step_size; - - ecc_bytes = caps->calc_ecc_bytes(step_size, strength); - if (WARN_ON_ONCE(ecc_bytes < 0)) - continue; - - if (ecc_bytes * nsteps > oobavail) - continue; - - corr = strength * nsteps; - - /* - * If the number of correctable bits is the same, - * bigger step_size has more reliability. - */ - if (corr > best_corr || - (corr == best_corr && step_size > best_step)) { - best_corr = corr; - best_step = step_size; - best_strength = strength; - best_ecc_bytes = ecc_bytes; - } - } - } - - if (!best_corr) - return -ENOTSUPP; - - chip->ecc.size = best_step; - chip->ecc.strength = best_strength; - chip->ecc.bytes = best_ecc_bytes; - - return 0; -} -EXPORT_SYMBOL_GPL(nand_maximize_ecc); - -/* - * Check if the chip configuration meet the datasheet requirements. - - * If our configuration corrects A bits per B bytes and the minimum - * required correction level is X bits per Y bytes, then we must ensure - * both of the following are true: - * - * (1) A / B >= X / Y - * (2) A >= X - * - * Requirement (1) ensures we can correct for the required bitflip density. - * Requirement (2) ensures we can correct even when all bitflips are clumped - * in the same sector. - */ -static bool nand_ecc_strength_good(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int corr, ds_corr; - - if (ecc->size == 0 || chip->ecc_step_ds == 0) - /* Not enough information */ - return true; - - /* - * We get the number of corrected bits per page to compare - * the correction density. - */ - corr = (mtd->writesize * ecc->strength) / ecc->size; - ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds; - - return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; -} - -/** - * nand_scan_tail - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * - * This is the second phase of the normal nand_scan() function. It fills out - * all the uninitialized function pointers with the defaults and scans for a - * bad block table if appropriate. - */ -int nand_scan_tail(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i; - - /* New bad blocks should be marked in OOB, flash-based BBT, or both */ - if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && - !(chip->bbt_options & NAND_BBT_USE_FLASH))) { - return -EINVAL; - } - - chip->data_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); - if (!chip->data_buf) - return -ENOMEM; - - /* - * FIXME: some NAND manufacturer drivers expect the first die to be - * selected when manufacturer->init() is called. They should be fixed - * to explictly select the relevant die when interacting with the NAND - * chip. - */ - chip->select_chip(mtd, 0); - ret = nand_manufacturer_init(chip); - chip->select_chip(mtd, -1); - if (ret) - goto err_free_buf; - - /* Set the internal oob buffer location, just after the page data */ - chip->oob_poi = chip->data_buf + mtd->writesize; - - /* - * If no default placement scheme is given, select an appropriate one. - */ - if (!mtd->ooblayout && - !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) { - switch (mtd->oobsize) { - case 8: - case 16: - mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops); - break; - case 64: - case 128: - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops); - break; - default: - /* - * Expose the whole OOB area to users if ECC_NONE - * is passed. We could do that for all kind of - * ->oobsize, but we must keep the old large/small - * page with ECC layout when ->oobsize <= 128 for - * compatibility reasons. - */ - if (ecc->mode == NAND_ECC_NONE) { - mtd_set_ooblayout(mtd, - &nand_ooblayout_lp_ops); - break; - } - - WARN(1, "No oob scheme defined for oobsize %d\n", - mtd->oobsize); - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } - } - - /* - * Check ECC mode, default to software if 3byte/512byte hardware ECC is - * selected and we have 256 byte pagesize fallback to software ECC - */ - - switch (ecc->mode) { - case NAND_ECC_HW_OOB_FIRST: - /* Similar to NAND_ECC_HW, but a separate read_page handle */ - if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { - WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } - if (!ecc->read_page) - ecc->read_page = nand_read_page_hwecc_oob_first; - - case NAND_ECC_HW: - /* Use standard hwecc read page function? */ - if (!ecc->read_page) - ecc->read_page = nand_read_page_hwecc; - if (!ecc->write_page) - ecc->write_page = nand_write_page_hwecc; - if (!ecc->read_page_raw) - ecc->read_page_raw = nand_read_page_raw; - if (!ecc->write_page_raw) - ecc->write_page_raw = nand_write_page_raw; - if (!ecc->read_oob) - ecc->read_oob = nand_read_oob_std; - if (!ecc->write_oob) - ecc->write_oob = nand_write_oob_std; - if (!ecc->read_subpage) - ecc->read_subpage = nand_read_subpage; - if (!ecc->write_subpage && ecc->hwctl && ecc->calculate) - ecc->write_subpage = nand_write_subpage_hwecc; - - case NAND_ECC_HW_SYNDROME: - if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && - (!ecc->read_page || - ecc->read_page == nand_read_page_hwecc || - !ecc->write_page || - ecc->write_page == nand_write_page_hwecc)) { - WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } - /* Use standard syndrome read/write page function? */ - if (!ecc->read_page) - ecc->read_page = nand_read_page_syndrome; - if (!ecc->write_page) - ecc->write_page = nand_write_page_syndrome; - if (!ecc->read_page_raw) - ecc->read_page_raw = nand_read_page_raw_syndrome; - if (!ecc->write_page_raw) - ecc->write_page_raw = nand_write_page_raw_syndrome; - if (!ecc->read_oob) - ecc->read_oob = nand_read_oob_syndrome; - if (!ecc->write_oob) - ecc->write_oob = nand_write_oob_syndrome; - - if (mtd->writesize >= ecc->size) { - if (!ecc->strength) { - WARN(1, "Driver must set ecc.strength when using hardware ECC\n"); - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } - break; - } - pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", - ecc->size, mtd->writesize); - ecc->mode = NAND_ECC_SOFT; - ecc->algo = NAND_ECC_HAMMING; - - case NAND_ECC_SOFT: - ret = nand_set_ecc_soft_ops(mtd); - if (ret) { - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } - break; - - case NAND_ECC_ON_DIE: - if (!ecc->read_page || !ecc->write_page) { - WARN(1, "No ECC functions supplied; on-die ECC not possible\n"); - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } - if (!ecc->read_oob) - ecc->read_oob = nand_read_oob_std; - if (!ecc->write_oob) - ecc->write_oob = nand_write_oob_std; - break; - - case NAND_ECC_NONE: - pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n"); - ecc->read_page = nand_read_page_raw; - ecc->write_page = nand_write_page_raw; - ecc->read_oob = nand_read_oob_std; - ecc->read_page_raw = nand_read_page_raw; - ecc->write_page_raw = nand_write_page_raw; - ecc->write_oob = nand_write_oob_std; - ecc->size = mtd->writesize; - ecc->bytes = 0; - ecc->strength = 0; - break; - - default: - WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode); - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } - - if (ecc->correct || ecc->calculate) { - ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL); - ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL); - if (!ecc->calc_buf || !ecc->code_buf) { - ret = -ENOMEM; - goto err_nand_manuf_cleanup; - } - } - - /* For many systems, the standard OOB write also works for raw */ - if (!ecc->read_oob_raw) - ecc->read_oob_raw = ecc->read_oob; - if (!ecc->write_oob_raw) - ecc->write_oob_raw = ecc->write_oob; - - /* propagate ecc info to mtd_info */ - mtd->ecc_strength = ecc->strength; - mtd->ecc_step_size = ecc->size; - - /* - * Set the number of read / write steps for one page depending on ECC - * mode. - */ - ecc->steps = mtd->writesize / ecc->size; - if (ecc->steps * ecc->size != mtd->writesize) { - WARN(1, "Invalid ECC parameters\n"); - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } - ecc->total = ecc->steps * ecc->bytes; - if (ecc->total > mtd->oobsize) { - WARN(1, "Total number of ECC bytes exceeded oobsize\n"); - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } - - /* - * The number of bytes available for a client to place data into - * the out of band area. - */ - ret = mtd_ooblayout_count_freebytes(mtd); - if (ret < 0) - ret = 0; - - mtd->oobavail = ret; - - /* ECC sanity check: warn if it's too weak */ - if (!nand_ecc_strength_good(mtd)) - pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", - mtd->name); - - /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ - if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { - switch (ecc->steps) { - case 2: - mtd->subpage_sft = 1; - break; - case 4: - case 8: - case 16: - mtd->subpage_sft = 2; - break; - } - } - chip->subpagesize = mtd->writesize >> mtd->subpage_sft; - - /* Initialize state */ - chip->state = FL_READY; - - /* Invalidate the pagebuffer reference */ - chip->pagebuf = -1; - - /* Large page NAND with SOFT_ECC should support subpage reads */ - switch (ecc->mode) { - case NAND_ECC_SOFT: - if (chip->page_shift > 9) - chip->options |= NAND_SUBPAGE_READ; - break; - - default: - break; - } - - /* Fill in remaining MTD driver data */ - mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; - mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : - MTD_CAP_NANDFLASH; - mtd->_erase = nand_erase; - mtd->_point = NULL; - mtd->_unpoint = NULL; - mtd->_panic_write = panic_nand_write; - mtd->_read_oob = nand_read_oob; - mtd->_write_oob = nand_write_oob; - mtd->_sync = nand_sync; - mtd->_lock = NULL; - mtd->_unlock = NULL; - mtd->_suspend = nand_suspend; - mtd->_resume = nand_resume; - mtd->_reboot = nand_shutdown; - mtd->_block_isreserved = nand_block_isreserved; - mtd->_block_isbad = nand_block_isbad; - mtd->_block_markbad = nand_block_markbad; - mtd->_max_bad_blocks = nand_max_bad_blocks; - mtd->writebufsize = mtd->writesize; - - /* - * Initialize bitflip_threshold to its default prior scan_bbt() call. - * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be - * properly set. - */ - if (!mtd->bitflip_threshold) - mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); - - /* Initialize the ->data_interface field. */ - ret = nand_init_data_interface(chip); - if (ret) - goto err_nand_manuf_cleanup; - - /* Enter fastest possible mode on all dies. */ - for (i = 0; i < chip->numchips; i++) { - chip->select_chip(mtd, i); - ret = nand_setup_data_interface(chip, i); - chip->select_chip(mtd, -1); - - if (ret) - goto err_nand_manuf_cleanup; - } - - /* Check, if we should skip the bad block table scan */ - if (chip->options & NAND_SKIP_BBTSCAN) - return 0; - - /* Build bad block table */ - ret = chip->scan_bbt(mtd); - if (ret) - goto err_nand_manuf_cleanup; - - return 0; - - -err_nand_manuf_cleanup: - nand_manufacturer_cleanup(chip); - -err_free_buf: - kfree(chip->data_buf); - kfree(ecc->code_buf); - kfree(ecc->calc_buf); - - return ret; -} -EXPORT_SYMBOL(nand_scan_tail); - -/* - * is_module_text_address() isn't exported, and it's mostly a pointless - * test if this is a module _anyway_ -- they'd have to try _really_ hard - * to call us from in-kernel code if the core NAND support is modular. - */ -#ifdef MODULE -#define caller_is_module() (1) -#else -#define caller_is_module() \ - is_module_text_address((unsigned long)__builtin_return_address(0)) -#endif - -/** - * nand_scan - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * @maxchips: number of chips to scan for - * - * This fills out all the uninitialized function pointers with the defaults. - * The flash ID is read and the mtd/chip structures are filled with the - * appropriate values. - */ -int nand_scan(struct mtd_info *mtd, int maxchips) -{ - int ret; - - ret = nand_scan_ident(mtd, maxchips, NULL); - if (!ret) - ret = nand_scan_tail(mtd); - return ret; -} -EXPORT_SYMBOL(nand_scan); - -/** - * nand_cleanup - [NAND Interface] Free resources held by the NAND device - * @chip: NAND chip object - */ -void nand_cleanup(struct nand_chip *chip) -{ - if (chip->ecc.mode == NAND_ECC_SOFT && - chip->ecc.algo == NAND_ECC_BCH) - nand_bch_free((struct nand_bch_control *)chip->ecc.priv); - - /* Free bad block table memory */ - kfree(chip->bbt); - kfree(chip->data_buf); - kfree(chip->ecc.code_buf); - kfree(chip->ecc.calc_buf); - - /* Free bad block descriptor memory */ - if (chip->badblock_pattern && chip->badblock_pattern->options - & NAND_BBT_DYNAMICSTRUCT) - kfree(chip->badblock_pattern); - - /* Free manufacturer priv data. */ - nand_manufacturer_cleanup(chip); -} -EXPORT_SYMBOL_GPL(nand_cleanup); - -/** - * nand_release - [NAND Interface] Unregister the MTD device and free resources - * held by the NAND device - * @mtd: MTD device structure - */ -void nand_release(struct mtd_info *mtd) -{ - mtd_device_unregister(mtd); - nand_cleanup(mtd_to_nand(mtd)); -} -EXPORT_SYMBOL_GPL(nand_release); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Steven J. Hill "); -MODULE_AUTHOR("Thomas Gleixner "); -MODULE_DESCRIPTION("Generic NAND flash driver code"); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c deleted file mode 100644 index 36092850be2c..000000000000 --- a/drivers/mtd/nand/nand_bbt.c +++ /dev/null @@ -1,1452 +0,0 @@ -/* - * Overview: - * Bad block table support for the NAND driver - * - * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Description: - * - * When nand_scan_bbt is called, then it tries to find the bad block table - * depending on the options in the BBT descriptor(s). If no flash based BBT - * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory - * marked good / bad blocks. This information is used to create a memory BBT. - * Once a new bad block is discovered then the "factory" information is updated - * on the device. - * If a flash based BBT is specified then the function first tries to find the - * BBT on flash. If a BBT is found then the contents are read and the memory - * based BBT is created. If a mirrored BBT is selected then the mirror is - * searched too and the versions are compared. If the mirror has a greater - * version number, then the mirror BBT is used to build the memory based BBT. - * If the tables are not versioned, then we "or" the bad block information. - * If one of the BBTs is out of date or does not exist it is (re)created. - * If no BBT exists at all then the device is scanned for factory marked - * good / bad blocks and the bad block tables are created. - * - * For manufacturer created BBTs like the one found on M-SYS DOC devices - * the BBT is searched and read but never created - * - * The auto generated bad block table is located in the last good blocks - * of the device. The table is mirrored, so it can be updated eventually. - * The table is marked in the OOB area with an ident pattern and a version - * number which indicates which of both tables is more up to date. If the NAND - * controller needs the complete OOB area for the ECC information then the - * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of - * course): it moves the ident pattern and the version byte into the data area - * and the OOB area will remain untouched. - * - * The table uses 2 bits per block - * 11b: block is good - * 00b: block is factory marked bad - * 01b, 10b: block is marked bad due to wear - * - * The memory bad block table uses the following scheme: - * 00b: block is good - * 01b: block is marked bad due to wear - * 10b: block is reserved (to protect the bbt area) - * 11b: block is factory marked bad - * - * Multichip devices like DOC store the bad block info per floor. - * - * Following assumptions are made: - * - bbts start at a page boundary, if autolocated on a block boundary - * - the space necessary for a bbt in FLASH does not exceed a block boundary - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BBT_BLOCK_GOOD 0x00 -#define BBT_BLOCK_WORN 0x01 -#define BBT_BLOCK_RESERVED 0x02 -#define BBT_BLOCK_FACTORY_BAD 0x03 - -#define BBT_ENTRY_MASK 0x03 -#define BBT_ENTRY_SHIFT 2 - -static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); - -static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) -{ - uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; - entry >>= (block & BBT_ENTRY_MASK) * 2; - return entry & BBT_ENTRY_MASK; -} - -static inline void bbt_mark_entry(struct nand_chip *chip, int block, - uint8_t mark) -{ - uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); - chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; -} - -static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) -{ - if (memcmp(buf, td->pattern, td->len)) - return -1; - return 0; -} - -/** - * check_pattern - [GENERIC] check if a pattern is in the buffer - * @buf: the buffer to search - * @len: the length of buffer to search - * @paglen: the pagelength - * @td: search pattern descriptor - * - * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. - */ -static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) -{ - if (td->options & NAND_BBT_NO_OOB) - return check_pattern_no_oob(buf, td); - - /* Compare the pattern */ - if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) - return -1; - - return 0; -} - -/** - * check_short_pattern - [GENERIC] check if a pattern is in the buffer - * @buf: the buffer to search - * @td: search pattern descriptor - * - * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. Same as check_pattern, but no optional empty - * check. - */ -static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) -{ - /* Compare the pattern */ - if (memcmp(buf + td->offs, td->pattern, td->len)) - return -1; - return 0; -} - -/** - * add_marker_len - compute the length of the marker in data area - * @td: BBT descriptor used for computation - * - * The length will be 0 if the marker is located in OOB area. - */ -static u32 add_marker_len(struct nand_bbt_descr *td) -{ - u32 len; - - if (!(td->options & NAND_BBT_NO_OOB)) - return 0; - - len = td->len; - if (td->options & NAND_BBT_VERSION) - len++; - return len; -} - -/** - * read_bbt - [GENERIC] Read the bad block table starting from page - * @mtd: MTD device structure - * @buf: temporary buffer - * @page: the starting page - * @num: the number of bbt descriptors to read - * @td: the bbt describtion table - * @offs: block number offset in the table - * - * Read the bad block table starting from page. - */ -static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, - struct nand_bbt_descr *td, int offs) -{ - int res, ret = 0, i, j, act = 0; - struct nand_chip *this = mtd_to_nand(mtd); - size_t retlen, len, totlen; - loff_t from; - int bits = td->options & NAND_BBT_NRBITS_MSK; - uint8_t msk = (uint8_t)((1 << bits) - 1); - u32 marker_len; - int reserved_block_code = td->reserved_block_code; - - totlen = (num * bits) >> 3; - marker_len = add_marker_len(td); - from = ((loff_t)page) << this->page_shift; - - while (totlen) { - len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); - if (marker_len) { - /* - * In case the BBT marker is not in the OOB area it - * will be just in the first page. - */ - len -= marker_len; - from += marker_len; - marker_len = 0; - } - res = mtd_read(mtd, from, len, &retlen, buf); - if (res < 0) { - if (mtd_is_eccerr(res)) { - pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n", - from & ~mtd->writesize); - return res; - } else if (mtd_is_bitflip(res)) { - pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n", - from & ~mtd->writesize); - ret = res; - } else { - pr_info("nand_bbt: error reading BBT\n"); - return res; - } - } - - /* Analyse data */ - for (i = 0; i < len; i++) { - uint8_t dat = buf[i]; - for (j = 0; j < 8; j += bits, act++) { - uint8_t tmp = (dat >> j) & msk; - if (tmp == msk) - continue; - if (reserved_block_code && (tmp == reserved_block_code)) { - pr_info("nand_read_bbt: reserved block at 0x%012llx\n", - (loff_t)(offs + act) << - this->bbt_erase_shift); - bbt_mark_entry(this, offs + act, - BBT_BLOCK_RESERVED); - mtd->ecc_stats.bbtblocks++; - continue; - } - /* - * Leave it for now, if it's matured we can - * move this message to pr_debug. - */ - pr_info("nand_read_bbt: bad block at 0x%012llx\n", - (loff_t)(offs + act) << - this->bbt_erase_shift); - /* Factory marked bad or worn out? */ - if (tmp == 0) - bbt_mark_entry(this, offs + act, - BBT_BLOCK_FACTORY_BAD); - else - bbt_mark_entry(this, offs + act, - BBT_BLOCK_WORN); - mtd->ecc_stats.badblocks++; - } - } - totlen -= len; - from += len; - } - return ret; -} - -/** - * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @chip: read the table for a specific chip, -1 read all chips; applies only if - * NAND_BBT_PERCHIP option is set - * - * Read the bad block table for all chips starting at a given page. We assume - * that the bbt bits are in consecutive order. - */ -static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int res = 0, i; - - if (td->options & NAND_BBT_PERCHIP) { - int offs = 0; - for (i = 0; i < this->numchips; i++) { - if (chip == -1 || chip == i) - res = read_bbt(mtd, buf, td->pages[i], - this->chipsize >> this->bbt_erase_shift, - td, offs); - if (res) - return res; - offs += this->chipsize >> this->bbt_erase_shift; - } - } else { - res = read_bbt(mtd, buf, td->pages[0], - mtd->size >> this->bbt_erase_shift, td, 0); - if (res) - return res; - } - return 0; -} - -/* BBT marker is in the first page, no OOB */ -static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, - struct nand_bbt_descr *td) -{ - size_t retlen; - size_t len; - - len = td->len; - if (td->options & NAND_BBT_VERSION) - len++; - - return mtd_read(mtd, offs, len, &retlen, buf); -} - -/** - * scan_read_oob - [GENERIC] Scan data+OOB region to buffer - * @mtd: MTD device structure - * @buf: temporary buffer - * @offs: offset at which to scan - * @len: length of data region to read - * - * Scan read data from data+OOB. May traverse multiple pages, interleaving - * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" - * ECC condition (error or bitflip). May quit on the first (non-ECC) error. - */ -static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, - size_t len) -{ - struct mtd_oob_ops ops; - int res, ret = 0; - - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooboffs = 0; - ops.ooblen = mtd->oobsize; - - while (len > 0) { - ops.datbuf = buf; - ops.len = min(len, (size_t)mtd->writesize); - ops.oobbuf = buf + ops.len; - - res = mtd_read_oob(mtd, offs, &ops); - if (res) { - if (!mtd_is_bitflip_or_eccerr(res)) - return res; - else if (mtd_is_eccerr(res) || !ret) - ret = res; - } - - buf += mtd->oobsize + mtd->writesize; - len -= mtd->writesize; - offs += mtd->writesize; - } - return ret; -} - -static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs, - size_t len, struct nand_bbt_descr *td) -{ - if (td->options & NAND_BBT_NO_OOB) - return scan_read_data(mtd, buf, offs, td); - else - return scan_read_oob(mtd, buf, offs, len); -} - -/* Scan write data with oob to flash */ -static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, - uint8_t *buf, uint8_t *oob) -{ - struct mtd_oob_ops ops; - - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooboffs = 0; - ops.ooblen = mtd->oobsize; - ops.datbuf = buf; - ops.oobbuf = oob; - ops.len = len; - - return mtd_write_oob(mtd, offs, &ops); -} - -static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) -{ - u32 ver_offs = td->veroffs; - - if (!(td->options & NAND_BBT_NO_OOB)) - ver_offs += mtd->writesize; - return ver_offs; -} - -/** - * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @md: descriptor for the bad block table mirror - * - * Read the bad block table(s) for all chips starting at a given page. We - * assume that the bbt bits are in consecutive order. - */ -static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, struct nand_bbt_descr *md) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - /* Read the primary version, if available */ - if (td->options & NAND_BBT_VERSION) { - scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift, - mtd->writesize, td); - td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; - pr_info("Bad block table at page %d, version 0x%02X\n", - td->pages[0], td->version[0]); - } - - /* Read the mirror version, if available */ - if (md && (md->options & NAND_BBT_VERSION)) { - scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift, - mtd->writesize, md); - md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; - pr_info("Bad block table at page %d, version 0x%02X\n", - md->pages[0], md->version[0]); - } -} - -/* Scan a given block partially */ -static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, - loff_t offs, uint8_t *buf, int numpages) -{ - struct mtd_oob_ops ops; - int j, ret; - - ops.ooblen = mtd->oobsize; - ops.oobbuf = buf; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.mode = MTD_OPS_PLACE_OOB; - - for (j = 0; j < numpages; j++) { - /* - * Read the full oob until read_oob is fixed to handle single - * byte reads for 16 bit buswidth. - */ - ret = mtd_read_oob(mtd, offs, &ops); - /* Ignore ECC errors when checking for BBM */ - if (ret && !mtd_is_bitflip_or_eccerr(ret)) - return ret; - - if (check_short_pattern(buf, bd)) - return 1; - - offs += mtd->writesize; - } - return 0; -} - -/** - * create_bbt - [GENERIC] Create a bad block table by scanning the device - * @mtd: MTD device structure - * @buf: temporary buffer - * @bd: descriptor for the good/bad block search pattern - * @chip: create the table for a specific chip, -1 read all chips; applies only - * if NAND_BBT_PERCHIP option is set - * - * Create a bad block table by scanning the device for the given good/bad block - * identify pattern. - */ -static int create_bbt(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *bd, int chip) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int i, numblocks, numpages; - int startblock; - loff_t from; - - pr_info("Scanning device for bad blocks\n"); - - if (bd->options & NAND_BBT_SCAN2NDPAGE) - numpages = 2; - else - numpages = 1; - - if (chip == -1) { - numblocks = mtd->size >> this->bbt_erase_shift; - startblock = 0; - from = 0; - } else { - if (chip >= this->numchips) { - pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", - chip + 1, this->numchips); - return -EINVAL; - } - numblocks = this->chipsize >> this->bbt_erase_shift; - startblock = chip * numblocks; - numblocks += startblock; - from = (loff_t)startblock << this->bbt_erase_shift; - } - - if (this->bbt_options & NAND_BBT_SCANLASTPAGE) - from += mtd->erasesize - (mtd->writesize * numpages); - - for (i = startblock; i < numblocks; i++) { - int ret; - - BUG_ON(bd->options & NAND_BBT_NO_OOB); - - ret = scan_block_fast(mtd, bd, from, buf, numpages); - if (ret < 0) - return ret; - - if (ret) { - bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); - pr_warn("Bad eraseblock %d at 0x%012llx\n", - i, (unsigned long long)from); - mtd->ecc_stats.badblocks++; - } - - from += (1 << this->bbt_erase_shift); - } - return 0; -} - -/** - * search_bbt - [GENERIC] scan the device for a specific bad block table - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * - * Read the bad block table by searching for a given ident pattern. Search is - * preformed either from the beginning up or from the end of the device - * downwards. The search starts always at the start of a block. If the option - * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains - * the bad block information of this chip. This is necessary to provide support - * for certain DOC devices. - * - * The bbt ident pattern resides in the oob area of the first page in a block. - */ -static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int i, chips; - int startblock, block, dir; - int scanlen = mtd->writesize + mtd->oobsize; - int bbtblocks; - int blocktopage = this->bbt_erase_shift - this->page_shift; - - /* Search direction top -> down? */ - if (td->options & NAND_BBT_LASTBLOCK) { - startblock = (mtd->size >> this->bbt_erase_shift) - 1; - dir = -1; - } else { - startblock = 0; - dir = 1; - } - - /* Do we have a bbt per chip? */ - if (td->options & NAND_BBT_PERCHIP) { - chips = this->numchips; - bbtblocks = this->chipsize >> this->bbt_erase_shift; - startblock &= bbtblocks - 1; - } else { - chips = 1; - bbtblocks = mtd->size >> this->bbt_erase_shift; - } - - for (i = 0; i < chips; i++) { - /* Reset version information */ - td->version[i] = 0; - td->pages[i] = -1; - /* Scan the maximum number of blocks */ - for (block = 0; block < td->maxblocks; block++) { - - int actblock = startblock + dir * block; - loff_t offs = (loff_t)actblock << this->bbt_erase_shift; - - /* Read first page */ - scan_read(mtd, buf, offs, mtd->writesize, td); - if (!check_pattern(buf, scanlen, mtd->writesize, td)) { - td->pages[i] = actblock << blocktopage; - if (td->options & NAND_BBT_VERSION) { - offs = bbt_get_ver_offs(mtd, td); - td->version[i] = buf[offs]; - } - break; - } - } - startblock += this->chipsize >> this->bbt_erase_shift; - } - /* Check, if we found a bbt for each requested chip */ - for (i = 0; i < chips; i++) { - if (td->pages[i] == -1) - pr_warn("Bad block table not found for chip %d\n", i); - else - pr_info("Bad block table found at page %d, version 0x%02X\n", - td->pages[i], td->version[i]); - } - return 0; -} - -/** - * search_read_bbts - [GENERIC] scan the device for bad block table(s) - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @md: descriptor for the bad block table mirror - * - * Search and read the bad block table(s). - */ -static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, - struct nand_bbt_descr *md) -{ - /* Search the primary table */ - search_bbt(mtd, buf, td); - - /* Search the mirror table */ - if (md) - search_bbt(mtd, buf, md); -} - -/** - * get_bbt_block - Get the first valid eraseblock suitable to store a BBT - * @this: the NAND device - * @td: the BBT description - * @md: the mirror BBT descriptor - * @chip: the CHIP selector - * - * This functions returns a positive block number pointing a valid eraseblock - * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if - * all blocks are already used of marked bad. If td->pages[chip] was already - * pointing to a valid block we re-use it, otherwise we search for the next - * valid one. - */ -static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, - struct nand_bbt_descr *md, int chip) -{ - int startblock, dir, page, numblocks, i; - - /* - * There was already a version of the table, reuse the page. This - * applies for absolute placement too, as we have the page number in - * td->pages. - */ - if (td->pages[chip] != -1) - return td->pages[chip] >> - (this->bbt_erase_shift - this->page_shift); - - numblocks = (int)(this->chipsize >> this->bbt_erase_shift); - if (!(td->options & NAND_BBT_PERCHIP)) - numblocks *= this->numchips; - - /* - * Automatic placement of the bad block table. Search direction - * top -> down? - */ - if (td->options & NAND_BBT_LASTBLOCK) { - startblock = numblocks * (chip + 1) - 1; - dir = -1; - } else { - startblock = chip * numblocks; - dir = 1; - } - - for (i = 0; i < td->maxblocks; i++) { - int block = startblock + dir * i; - - /* Check, if the block is bad */ - switch (bbt_get_entry(this, block)) { - case BBT_BLOCK_WORN: - case BBT_BLOCK_FACTORY_BAD: - continue; - } - - page = block << (this->bbt_erase_shift - this->page_shift); - - /* Check, if the block is used by the mirror table */ - if (!md || md->pages[chip] != page) - return block; - } - - return -ENOSPC; -} - -/** - * mark_bbt_block_bad - Mark one of the block reserved for BBT bad - * @this: the NAND device - * @td: the BBT description - * @chip: the CHIP selector - * @block: the BBT block to mark - * - * Blocks reserved for BBT can become bad. This functions is an helper to mark - * such blocks as bad. It takes care of updating the in-memory BBT, marking the - * block as bad using a bad block marker and invalidating the associated - * td->pages[] entry. - */ -static void mark_bbt_block_bad(struct nand_chip *this, - struct nand_bbt_descr *td, - int chip, int block) -{ - struct mtd_info *mtd = nand_to_mtd(this); - loff_t to; - int res; - - bbt_mark_entry(this, block, BBT_BLOCK_WORN); - - to = (loff_t)block << this->bbt_erase_shift; - res = this->block_markbad(mtd, to); - if (res) - pr_warn("nand_bbt: error %d while marking block %d bad\n", - res, block); - - td->pages[chip] = -1; -} - -/** - * write_bbt - [GENERIC] (Re)write the bad block table - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @md: descriptor for the bad block table mirror - * @chipsel: selector for a specific chip, -1 for all - * - * (Re)write the bad block table. - */ -static int write_bbt(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, struct nand_bbt_descr *md, - int chipsel) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct erase_info einfo; - int i, res, chip = 0; - int bits, page, offs, numblocks, sft, sftmsk; - int nrchips, pageoffs, ooboffs; - uint8_t msk[4]; - uint8_t rcode = td->reserved_block_code; - size_t retlen, len = 0; - loff_t to; - struct mtd_oob_ops ops; - - ops.ooblen = mtd->oobsize; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.mode = MTD_OPS_PLACE_OOB; - - if (!rcode) - rcode = 0xff; - /* Write bad block table per chip rather than per device? */ - if (td->options & NAND_BBT_PERCHIP) { - numblocks = (int)(this->chipsize >> this->bbt_erase_shift); - /* Full device write or specific chip? */ - if (chipsel == -1) { - nrchips = this->numchips; - } else { - nrchips = chipsel + 1; - chip = chipsel; - } - } else { - numblocks = (int)(mtd->size >> this->bbt_erase_shift); - nrchips = 1; - } - - /* Loop through the chips */ - while (chip < nrchips) { - int block; - - block = get_bbt_block(this, td, md, chip); - if (block < 0) { - pr_err("No space left to write bad block table\n"); - res = block; - goto outerr; - } - - /* - * get_bbt_block() returns a block number, shift the value to - * get a page number. - */ - page = block << (this->bbt_erase_shift - this->page_shift); - - /* Set up shift count and masks for the flash table */ - bits = td->options & NAND_BBT_NRBITS_MSK; - msk[2] = ~rcode; - switch (bits) { - case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; - msk[3] = 0x01; - break; - case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; - msk[3] = 0x03; - break; - case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; - msk[3] = 0x0f; - break; - case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; - msk[3] = 0xff; - break; - default: return -EINVAL; - } - - to = ((loff_t)page) << this->page_shift; - - /* Must we save the block contents? */ - if (td->options & NAND_BBT_SAVECONTENT) { - /* Make it block aligned */ - to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1); - len = 1 << this->bbt_erase_shift; - res = mtd_read(mtd, to, len, &retlen, buf); - if (res < 0) { - if (retlen != len) { - pr_info("nand_bbt: error reading block for writing the bad block table\n"); - return res; - } - pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n"); - } - /* Read oob data */ - ops.ooblen = (len >> this->page_shift) * mtd->oobsize; - ops.oobbuf = &buf[len]; - res = mtd_read_oob(mtd, to + mtd->writesize, &ops); - if (res < 0 || ops.oobretlen != ops.ooblen) - goto outerr; - - /* Calc the byte offset in the buffer */ - pageoffs = page - (int)(to >> this->page_shift); - offs = pageoffs << this->page_shift; - /* Preset the bbt area with 0xff */ - memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); - ooboffs = len + (pageoffs * mtd->oobsize); - - } else if (td->options & NAND_BBT_NO_OOB) { - ooboffs = 0; - offs = td->len; - /* The version byte */ - if (td->options & NAND_BBT_VERSION) - offs++; - /* Calc length */ - len = (size_t)(numblocks >> sft); - len += offs; - /* Make it page aligned! */ - len = ALIGN(len, mtd->writesize); - /* Preset the buffer with 0xff */ - memset(buf, 0xff, len); - /* Pattern is located at the begin of first page */ - memcpy(buf, td->pattern, td->len); - } else { - /* Calc length */ - len = (size_t)(numblocks >> sft); - /* Make it page aligned! */ - len = ALIGN(len, mtd->writesize); - /* Preset the buffer with 0xff */ - memset(buf, 0xff, len + - (len >> this->page_shift)* mtd->oobsize); - offs = 0; - ooboffs = len; - /* Pattern is located in oob area of first page */ - memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); - } - - if (td->options & NAND_BBT_VERSION) - buf[ooboffs + td->veroffs] = td->version[chip]; - - /* Walk through the memory table */ - for (i = 0; i < numblocks; i++) { - uint8_t dat; - int sftcnt = (i << (3 - sft)) & sftmsk; - dat = bbt_get_entry(this, chip * numblocks + i); - /* Do not store the reserved bbt blocks! */ - buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); - } - - memset(&einfo, 0, sizeof(einfo)); - einfo.mtd = mtd; - einfo.addr = to; - einfo.len = 1 << this->bbt_erase_shift; - res = nand_erase_nand(mtd, &einfo, 1); - if (res < 0) { - pr_warn("nand_bbt: error while erasing BBT block %d\n", - res); - mark_bbt_block_bad(this, td, chip, block); - continue; - } - - res = scan_write_bbt(mtd, to, len, buf, - td->options & NAND_BBT_NO_OOB ? NULL : - &buf[len]); - if (res < 0) { - pr_warn("nand_bbt: error while writing BBT block %d\n", - res); - mark_bbt_block_bad(this, td, chip, block); - continue; - } - - pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", - (unsigned long long)to, td->version[chip]); - - /* Mark it as used */ - td->pages[chip++] = page; - } - return 0; - - outerr: - pr_warn("nand_bbt: error while writing bad block table %d\n", res); - return res; -} - -/** - * nand_memory_bbt - [GENERIC] create a memory based bad block table - * @mtd: MTD device structure - * @bd: descriptor for the good/bad block search pattern - * - * The function creates a memory based bbt by scanning the device for - * manufacturer / software marked good / bad blocks. - */ -static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - return create_bbt(mtd, this->data_buf, bd, -1); -} - -/** - * check_create - [GENERIC] create and write bbt(s) if necessary - * @mtd: MTD device structure - * @buf: temporary buffer - * @bd: descriptor for the good/bad block search pattern - * - * The function checks the results of the previous call to read_bbt and creates - * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found - * for the chip/device. Update is necessary if one of the tables is missing or - * the version nr. of one table is less than the other. - */ -static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) -{ - int i, chips, writeops, create, chipsel, res, res2; - struct nand_chip *this = mtd_to_nand(mtd); - struct nand_bbt_descr *td = this->bbt_td; - struct nand_bbt_descr *md = this->bbt_md; - struct nand_bbt_descr *rd, *rd2; - - /* Do we have a bbt per chip? */ - if (td->options & NAND_BBT_PERCHIP) - chips = this->numchips; - else - chips = 1; - - for (i = 0; i < chips; i++) { - writeops = 0; - create = 0; - rd = NULL; - rd2 = NULL; - res = res2 = 0; - /* Per chip or per device? */ - chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; - /* Mirrored table available? */ - if (md) { - if (td->pages[i] == -1 && md->pages[i] == -1) { - create = 1; - writeops = 0x03; - } else if (td->pages[i] == -1) { - rd = md; - writeops = 0x01; - } else if (md->pages[i] == -1) { - rd = td; - writeops = 0x02; - } else if (td->version[i] == md->version[i]) { - rd = td; - if (!(td->options & NAND_BBT_VERSION)) - rd2 = md; - } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { - rd = td; - writeops = 0x02; - } else { - rd = md; - writeops = 0x01; - } - } else { - if (td->pages[i] == -1) { - create = 1; - writeops = 0x01; - } else { - rd = td; - } - } - - if (create) { - /* Create the bad block table by scanning the device? */ - if (!(td->options & NAND_BBT_CREATE)) - continue; - - /* Create the table in memory by scanning the chip(s) */ - if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) - create_bbt(mtd, buf, bd, chipsel); - - td->version[i] = 1; - if (md) - md->version[i] = 1; - } - - /* Read back first? */ - if (rd) { - res = read_abs_bbt(mtd, buf, rd, chipsel); - if (mtd_is_eccerr(res)) { - /* Mark table as invalid */ - rd->pages[i] = -1; - rd->version[i] = 0; - i--; - continue; - } - } - /* If they weren't versioned, read both */ - if (rd2) { - res2 = read_abs_bbt(mtd, buf, rd2, chipsel); - if (mtd_is_eccerr(res2)) { - /* Mark table as invalid */ - rd2->pages[i] = -1; - rd2->version[i] = 0; - i--; - continue; - } - } - - /* Scrub the flash table(s)? */ - if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) - writeops = 0x03; - - /* Update version numbers before writing */ - if (md) { - td->version[i] = max(td->version[i], md->version[i]); - md->version[i] = td->version[i]; - } - - /* Write the bad block table to the device? */ - if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { - res = write_bbt(mtd, buf, td, md, chipsel); - if (res < 0) - return res; - } - - /* Write the mirror bad block table to the device? */ - if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { - res = write_bbt(mtd, buf, md, td, chipsel); - if (res < 0) - return res; - } - } - return 0; -} - -/** - * mark_bbt_regions - [GENERIC] mark the bad block table regions - * @mtd: MTD device structure - * @td: bad block table descriptor - * - * The bad block table regions are marked as "bad" to prevent accidental - * erasures / writes. The regions are identified by the mark 0x02. - */ -static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int i, j, chips, block, nrblocks, update; - uint8_t oldval; - - /* Do we have a bbt per chip? */ - if (td->options & NAND_BBT_PERCHIP) { - chips = this->numchips; - nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); - } else { - chips = 1; - nrblocks = (int)(mtd->size >> this->bbt_erase_shift); - } - - for (i = 0; i < chips; i++) { - if ((td->options & NAND_BBT_ABSPAGE) || - !(td->options & NAND_BBT_WRITE)) { - if (td->pages[i] == -1) - continue; - block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); - oldval = bbt_get_entry(this, block); - bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); - if ((oldval != BBT_BLOCK_RESERVED) && - td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)block << - this->bbt_erase_shift); - continue; - } - update = 0; - if (td->options & NAND_BBT_LASTBLOCK) - block = ((i + 1) * nrblocks) - td->maxblocks; - else - block = i * nrblocks; - for (j = 0; j < td->maxblocks; j++) { - oldval = bbt_get_entry(this, block); - bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); - if (oldval != BBT_BLOCK_RESERVED) - update = 1; - block++; - } - /* - * If we want reserved blocks to be recorded to flash, and some - * new ones have been marked, then we need to update the stored - * bbts. This should only happen once. - */ - if (update && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)(block - 1) << - this->bbt_erase_shift); - } -} - -/** - * verify_bbt_descr - verify the bad block description - * @mtd: MTD device structure - * @bd: the table to verify - * - * This functions performs a few sanity checks on the bad block description - * table. - */ -static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u32 pattern_len; - u32 bits; - u32 table_size; - - if (!bd) - return; - - pattern_len = bd->len; - bits = bd->options & NAND_BBT_NRBITS_MSK; - - BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && - !(this->bbt_options & NAND_BBT_USE_FLASH)); - BUG_ON(!bits); - - if (bd->options & NAND_BBT_VERSION) - pattern_len++; - - if (bd->options & NAND_BBT_NO_OOB) { - BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); - BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); - BUG_ON(bd->offs); - if (bd->options & NAND_BBT_VERSION) - BUG_ON(bd->veroffs != bd->len); - BUG_ON(bd->options & NAND_BBT_SAVECONTENT); - } - - if (bd->options & NAND_BBT_PERCHIP) - table_size = this->chipsize >> this->bbt_erase_shift; - else - table_size = mtd->size >> this->bbt_erase_shift; - table_size >>= 3; - table_size *= bits; - if (bd->options & NAND_BBT_NO_OOB) - table_size += pattern_len; - BUG_ON(table_size > (1 << this->bbt_erase_shift)); -} - -/** - * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) - * @mtd: MTD device structure - * @bd: descriptor for the good/bad block search pattern - * - * The function checks, if a bad block table(s) is/are already available. If - * not it scans the device for manufacturer marked good / bad blocks and writes - * the bad block table(s) to the selected place. - * - * The bad block table memory is allocated here. It must be freed by calling - * the nand_free_bbt function. - */ -static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int len, res; - uint8_t *buf; - struct nand_bbt_descr *td = this->bbt_td; - struct nand_bbt_descr *md = this->bbt_md; - - len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1; - /* - * Allocate memory (2bit per block) and clear the memory bad block - * table. - */ - this->bbt = kzalloc(len, GFP_KERNEL); - if (!this->bbt) - return -ENOMEM; - - /* - * If no primary table decriptor is given, scan the device to build a - * memory based bad block table. - */ - if (!td) { - if ((res = nand_memory_bbt(mtd, bd))) { - pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); - goto err; - } - return 0; - } - verify_bbt_descr(mtd, td); - verify_bbt_descr(mtd, md); - - /* Allocate a temporary buffer for one eraseblock incl. oob */ - len = (1 << this->bbt_erase_shift); - len += (len >> this->page_shift) * mtd->oobsize; - buf = vmalloc(len); - if (!buf) { - res = -ENOMEM; - goto err; - } - - /* Is the bbt at a given page? */ - if (td->options & NAND_BBT_ABSPAGE) { - read_abs_bbts(mtd, buf, td, md); - } else { - /* Search the bad block table using a pattern in oob */ - search_read_bbts(mtd, buf, td, md); - } - - res = check_create(mtd, buf, bd); - if (res) - goto err; - - /* Prevent the bbt regions from erasing / writing */ - mark_bbt_region(mtd, td); - if (md) - mark_bbt_region(mtd, md); - - vfree(buf); - return 0; - -err: - kfree(this->bbt); - this->bbt = NULL; - return res; -} - -/** - * nand_update_bbt - update bad block table(s) - * @mtd: MTD device structure - * @offs: the offset of the newly marked block - * - * The function updates the bad block table(s). - */ -static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int len, res = 0; - int chip, chipsel; - uint8_t *buf; - struct nand_bbt_descr *td = this->bbt_td; - struct nand_bbt_descr *md = this->bbt_md; - - if (!this->bbt || !td) - return -EINVAL; - - /* Allocate a temporary buffer for one eraseblock incl. oob */ - len = (1 << this->bbt_erase_shift); - len += (len >> this->page_shift) * mtd->oobsize; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* Do we have a bbt per chip? */ - if (td->options & NAND_BBT_PERCHIP) { - chip = (int)(offs >> this->chip_shift); - chipsel = chip; - } else { - chip = 0; - chipsel = -1; - } - - td->version[chip]++; - if (md) - md->version[chip]++; - - /* Write the bad block table to the device? */ - if (td->options & NAND_BBT_WRITE) { - res = write_bbt(mtd, buf, td, md, chipsel); - if (res < 0) - goto out; - } - /* Write the mirror bad block table to the device? */ - if (md && (md->options & NAND_BBT_WRITE)) { - res = write_bbt(mtd, buf, md, td, chipsel); - } - - out: - kfree(buf); - return res; -} - -/* - * Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks. - */ -static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; - -/* Generic flash bbt descriptors */ -static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; -static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, - .len = 4, - .veroffs = 12, - .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, - .len = 4, - .veroffs = 12, - .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, - .pattern = mirror_pattern -}; - -static struct nand_bbt_descr bbt_main_no_oob_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP - | NAND_BBT_NO_OOB, - .len = 4, - .veroffs = 4, - .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_no_oob_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP - | NAND_BBT_NO_OOB, - .len = 4, - .veroffs = 4, - .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, - .pattern = mirror_pattern -}; - -#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) -/** - * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure - * @this: NAND chip to create descriptor for - * - * This function allocates and initializes a nand_bbt_descr for BBM detection - * based on the properties of @this. The new descriptor is stored in - * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when - * passed to this function. - */ -static int nand_create_badblock_pattern(struct nand_chip *this) -{ - struct nand_bbt_descr *bd; - if (this->badblock_pattern) { - pr_warn("Bad block pattern already allocated; not replacing\n"); - return -EINVAL; - } - bd = kzalloc(sizeof(*bd), GFP_KERNEL); - if (!bd) - return -ENOMEM; - bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; - bd->offs = this->badblockpos; - bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; - bd->pattern = scan_ff_pattern; - bd->options |= NAND_BBT_DYNAMICSTRUCT; - this->badblock_pattern = bd; - return 0; -} - -/** - * nand_default_bbt - [NAND Interface] Select a default bad block table for the device - * @mtd: MTD device structure - * - * This function selects the default bad block table support for the device and - * calls the nand_scan_bbt function. - */ -int nand_default_bbt(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int ret; - - /* Is a flash based bad block table requested? */ - if (this->bbt_options & NAND_BBT_USE_FLASH) { - /* Use the default pattern descriptors */ - if (!this->bbt_td) { - if (this->bbt_options & NAND_BBT_NO_OOB) { - this->bbt_td = &bbt_main_no_oob_descr; - this->bbt_md = &bbt_mirror_no_oob_descr; - } else { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; - } - } - } else { - this->bbt_td = NULL; - this->bbt_md = NULL; - } - - if (!this->badblock_pattern) { - ret = nand_create_badblock_pattern(this); - if (ret) - return ret; - } - - return nand_scan_bbt(mtd, this->badblock_pattern); -} - -/** - * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved - * @mtd: MTD device structure - * @offs: offset in the device - */ -int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int block; - - block = (int)(offs >> this->bbt_erase_shift); - return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED; -} - -/** - * nand_isbad_bbt - [NAND Interface] Check if a block is bad - * @mtd: MTD device structure - * @offs: offset in the device - * @allowbbt: allow access to bad block table region - */ -int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int block, res; - - block = (int)(offs >> this->bbt_erase_shift); - res = bbt_get_entry(this, block); - - pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, block, res); - - switch (res) { - case BBT_BLOCK_GOOD: - return 0; - case BBT_BLOCK_WORN: - return 1; - case BBT_BLOCK_RESERVED: - return allowbbt ? 0 : 1; - } - return 1; -} - -/** - * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT - * @mtd: MTD device structure - * @offs: offset of the bad block - */ -int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int block, ret = 0; - - block = (int)(offs >> this->bbt_erase_shift); - - /* Mark bad block in memory */ - bbt_mark_entry(this, block, BBT_BLOCK_WORN); - - /* Update flash-based bad block table */ - if (this->bbt_options & NAND_BBT_USE_FLASH) - ret = nand_update_bbt(mtd, offs); - - return ret; -} diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c deleted file mode 100644 index 505441c9373b..000000000000 --- a/drivers/mtd/nand/nand_bch.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * This file provides ECC correction for more than 1 bit per block of data, - * using binary BCH codes. It relies on the generic BCH library lib/bch.c. - * - * Copyright © 2011 Ivan Djelic - * - * This file is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 or (at your option) any - * later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this file; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * struct nand_bch_control - private NAND BCH control structure - * @bch: BCH control structure - * @errloc: error location array - * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid - */ -struct nand_bch_control { - struct bch_control *bch; - unsigned int *errloc; - unsigned char *eccmask; -}; - -/** - * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block - * @mtd: MTD block structure - * @buf: input buffer with raw data - * @code: output buffer with ECC - */ -int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, - unsigned char *code) -{ - const struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_bch_control *nbc = chip->ecc.priv; - unsigned int i; - - memset(code, 0, chip->ecc.bytes); - encode_bch(nbc->bch, buf, chip->ecc.size, code); - - /* apply mask so that an erased page is a valid codeword */ - for (i = 0; i < chip->ecc.bytes; i++) - code[i] ^= nbc->eccmask[i]; - - return 0; -} -EXPORT_SYMBOL(nand_bch_calculate_ecc); - -/** - * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) - * @mtd: MTD block structure - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * - * Detect and correct bit errors for a data byte block - */ -int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - const struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_bch_control *nbc = chip->ecc.priv; - unsigned int *errloc = nbc->errloc; - int i, count; - - count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, - NULL, errloc); - if (count > 0) { - for (i = 0; i < count; i++) { - if (errloc[i] < (chip->ecc.size*8)) - /* error is located in data, correct it */ - buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); - /* else error in ecc, no action needed */ - - pr_debug("%s: corrected bitflip %u\n", __func__, - errloc[i]); - } - } else if (count < 0) { - printk(KERN_ERR "ecc unrecoverable error\n"); - count = -EBADMSG; - } - return count; -} -EXPORT_SYMBOL(nand_bch_correct_data); - -/** - * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction - * @mtd: MTD block structure - * - * Returns: - * a pointer to a new NAND BCH control structure, or NULL upon failure - * - * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes - * are used to compute BCH parameters m (Galois field order) and t (error - * correction capability). @eccbytes should be equal to the number of bytes - * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. - * - * Example: to configure 4 bit correction per 512 bytes, you should pass - * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) - * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) - */ -struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - unsigned int m, t, eccsteps, i; - struct nand_bch_control *nbc = NULL; - unsigned char *erased_page; - unsigned int eccsize = nand->ecc.size; - unsigned int eccbytes = nand->ecc.bytes; - unsigned int eccstrength = nand->ecc.strength; - - if (!eccbytes && eccstrength) { - eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); - nand->ecc.bytes = eccbytes; - } - - if (!eccsize || !eccbytes) { - printk(KERN_WARNING "ecc parameters not supplied\n"); - goto fail; - } - - m = fls(1+8*eccsize); - t = (eccbytes*8)/m; - - nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); - if (!nbc) - goto fail; - - nbc->bch = init_bch(m, t, 0); - if (!nbc->bch) - goto fail; - - /* verify that eccbytes has the expected value */ - if (nbc->bch->ecc_bytes != eccbytes) { - printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", - eccbytes, nbc->bch->ecc_bytes); - goto fail; - } - - eccsteps = mtd->writesize/eccsize; - - /* Check that we have an oob layout description. */ - if (!mtd->ooblayout) { - pr_warn("missing oob scheme"); - goto fail; - } - - /* sanity checks */ - if (8*(eccsize+eccbytes) >= (1 << m)) { - printk(KERN_WARNING "eccsize %u is too large\n", eccsize); - goto fail; - } - - /* - * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), - * which is called by mtd_ooblayout_count_eccbytes(). - * Make sure they are properly initialized before calling - * mtd_ooblayout_count_eccbytes(). - * FIXME: we should probably rework the sequencing in nand_scan_tail() - * to avoid setting those fields twice. - */ - nand->ecc.steps = eccsteps; - nand->ecc.total = eccsteps * eccbytes; - if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { - printk(KERN_WARNING "invalid ecc layout\n"); - goto fail; - } - - nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); - nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); - if (!nbc->eccmask || !nbc->errloc) - goto fail; - /* - * compute and store the inverted ecc of an erased ecc block - */ - erased_page = kmalloc(eccsize, GFP_KERNEL); - if (!erased_page) - goto fail; - - memset(erased_page, 0xff, eccsize); - memset(nbc->eccmask, 0, eccbytes); - encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); - kfree(erased_page); - - for (i = 0; i < eccbytes; i++) - nbc->eccmask[i] ^= 0xff; - - if (!eccstrength) - nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); - - return nbc; -fail: - nand_bch_free(nbc); - return NULL; -} -EXPORT_SYMBOL(nand_bch_init); - -/** - * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources - * @nbc: NAND BCH control structure - */ -void nand_bch_free(struct nand_bch_control *nbc) -{ - if (nbc) { - free_bch(nbc->bch); - kfree(nbc->errloc); - kfree(nbc->eccmask); - kfree(nbc); - } -} -EXPORT_SYMBOL(nand_bch_free); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ivan Djelic "); -MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c deleted file mode 100644 index 3630f0fe8fa4..000000000000 --- a/drivers/mtd/nand/nand_ecc.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * This file contains an ECC algorithm that detects and corrects 1 bit - * errors in a 256 byte block of data. - * - * Copyright © 2008 Koninklijke Philips Electronics NV. - * Author: Frans Meulenbroeks - * - * Completely replaces the previous ECC implementation which was written by: - * Steven J. Hill (sjhill@realitydiluted.com) - * Thomas Gleixner (tglx@linutronix.de) - * - * Information on how this algorithm works and how it was developed - * can be found in Documentation/mtd/nand_ecc.txt - * - * This file is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 or (at your option) any - * later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this file; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - */ - -/* - * The STANDALONE macro is useful when running the code outside the kernel - * e.g. when running the code in a testbed or a benchmark program. - * When STANDALONE is used, the module related macros are commented out - * as well as the linux include files. - * Instead a private definition of mtd_info is given to satisfy the compiler - * (the code does not use mtd_info, so the code does not care) - */ -#ifndef STANDALONE -#include -#include -#include -#include -#include -#include -#include -#else -#include -struct mtd_info; -#define EXPORT_SYMBOL(x) /* x */ - -#define MODULE_LICENSE(x) /* x */ -#define MODULE_AUTHOR(x) /* x */ -#define MODULE_DESCRIPTION(x) /* x */ - -#define pr_err printf -#endif - -/* - * invparity is a 256 byte table that contains the odd parity - * for each byte. So if the number of bits in a byte is even, - * the array element is 1, and when the number of bits is odd - * the array eleemnt is 0. - */ -static const char invparity[256] = { - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 -}; - -/* - * bitsperbyte contains the number of bits per byte - * this is only used for testing and repairing parity - * (a precalculated value slightly improves performance) - */ -static const char bitsperbyte[256] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, -}; - -/* - * addressbits is a lookup table to filter out the bits from the xor-ed - * ECC data that identify the faulty location. - * this is only used for repairing parity - * see the comments in nand_correct_data for more details - */ -static const char addressbits[256] = { - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, - 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, - 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, - 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, - 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, - 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, - 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, - 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, - 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, - 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, - 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, - 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, - 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, - 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, - 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, - 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, - 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f -}; - -/** - * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte - * block - * @buf: input buffer with raw data - * @eccsize: data bytes per ECC step (256 or 512) - * @code: output buffer with ECC - */ -void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, - unsigned char *code) -{ - int i; - const uint32_t *bp = (uint32_t *)buf; - /* 256 or 512 bytes/ecc */ - const uint32_t eccsize_mult = eccsize >> 8; - uint32_t cur; /* current value in buffer */ - /* rp0..rp15..rp17 are the various accumulated parities (per byte) */ - uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; - uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16; - uint32_t uninitialized_var(rp17); /* to make compiler happy */ - uint32_t par; /* the cumulative parity for all data */ - uint32_t tmppar; /* the cumulative parity for this iteration; - for rp12, rp14 and rp16 at the end of the - loop */ - - par = 0; - rp4 = 0; - rp6 = 0; - rp8 = 0; - rp10 = 0; - rp12 = 0; - rp14 = 0; - rp16 = 0; - - /* - * The loop is unrolled a number of times; - * This avoids if statements to decide on which rp value to update - * Also we process the data by longwords. - * Note: passing unaligned data might give a performance penalty. - * It is assumed that the buffers are aligned. - * tmppar is the cumulative sum of this iteration. - * needed for calculating rp12, rp14, rp16 and par - * also used as a performance improvement for rp6, rp8 and rp10 - */ - for (i = 0; i < eccsize_mult << 2; i++) { - cur = *bp++; - tmppar = cur; - rp4 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp6 ^= tmppar; - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp8 ^= tmppar; - - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - rp6 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp6 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp10 ^= tmppar; - - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - rp6 ^= cur; - rp8 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp6 ^= cur; - rp8 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - rp8 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp8 ^= cur; - - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - rp6 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp6 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - cur = *bp++; - tmppar ^= cur; - - par ^= tmppar; - if ((i & 0x1) == 0) - rp12 ^= tmppar; - if ((i & 0x2) == 0) - rp14 ^= tmppar; - if (eccsize_mult == 2 && (i & 0x4) == 0) - rp16 ^= tmppar; - } - - /* - * handle the fact that we use longword operations - * we'll bring rp4..rp14..rp16 back to single byte entities by - * shifting and xoring first fold the upper and lower 16 bits, - * then the upper and lower 8 bits. - */ - rp4 ^= (rp4 >> 16); - rp4 ^= (rp4 >> 8); - rp4 &= 0xff; - rp6 ^= (rp6 >> 16); - rp6 ^= (rp6 >> 8); - rp6 &= 0xff; - rp8 ^= (rp8 >> 16); - rp8 ^= (rp8 >> 8); - rp8 &= 0xff; - rp10 ^= (rp10 >> 16); - rp10 ^= (rp10 >> 8); - rp10 &= 0xff; - rp12 ^= (rp12 >> 16); - rp12 ^= (rp12 >> 8); - rp12 &= 0xff; - rp14 ^= (rp14 >> 16); - rp14 ^= (rp14 >> 8); - rp14 &= 0xff; - if (eccsize_mult == 2) { - rp16 ^= (rp16 >> 16); - rp16 ^= (rp16 >> 8); - rp16 &= 0xff; - } - - /* - * we also need to calculate the row parity for rp0..rp3 - * This is present in par, because par is now - * rp3 rp3 rp2 rp2 in little endian and - * rp2 rp2 rp3 rp3 in big endian - * as well as - * rp1 rp0 rp1 rp0 in little endian and - * rp0 rp1 rp0 rp1 in big endian - * First calculate rp2 and rp3 - */ -#ifdef __BIG_ENDIAN - rp2 = (par >> 16); - rp2 ^= (rp2 >> 8); - rp2 &= 0xff; - rp3 = par & 0xffff; - rp3 ^= (rp3 >> 8); - rp3 &= 0xff; -#else - rp3 = (par >> 16); - rp3 ^= (rp3 >> 8); - rp3 &= 0xff; - rp2 = par & 0xffff; - rp2 ^= (rp2 >> 8); - rp2 &= 0xff; -#endif - - /* reduce par to 16 bits then calculate rp1 and rp0 */ - par ^= (par >> 16); -#ifdef __BIG_ENDIAN - rp0 = (par >> 8) & 0xff; - rp1 = (par & 0xff); -#else - rp1 = (par >> 8) & 0xff; - rp0 = (par & 0xff); -#endif - - /* finally reduce par to 8 bits */ - par ^= (par >> 8); - par &= 0xff; - - /* - * and calculate rp5..rp15..rp17 - * note that par = rp4 ^ rp5 and due to the commutative property - * of the ^ operator we can say: - * rp5 = (par ^ rp4); - * The & 0xff seems superfluous, but benchmarking learned that - * leaving it out gives slightly worse results. No idea why, probably - * it has to do with the way the pipeline in pentium is organized. - */ - rp5 = (par ^ rp4) & 0xff; - rp7 = (par ^ rp6) & 0xff; - rp9 = (par ^ rp8) & 0xff; - rp11 = (par ^ rp10) & 0xff; - rp13 = (par ^ rp12) & 0xff; - rp15 = (par ^ rp14) & 0xff; - if (eccsize_mult == 2) - rp17 = (par ^ rp16) & 0xff; - - /* - * Finally calculate the ECC bits. - * Again here it might seem that there are performance optimisations - * possible, but benchmarks showed that on the system this is developed - * the code below is the fastest - */ -#ifdef CONFIG_MTD_NAND_ECC_SMC - code[0] = - (invparity[rp7] << 7) | - (invparity[rp6] << 6) | - (invparity[rp5] << 5) | - (invparity[rp4] << 4) | - (invparity[rp3] << 3) | - (invparity[rp2] << 2) | - (invparity[rp1] << 1) | - (invparity[rp0]); - code[1] = - (invparity[rp15] << 7) | - (invparity[rp14] << 6) | - (invparity[rp13] << 5) | - (invparity[rp12] << 4) | - (invparity[rp11] << 3) | - (invparity[rp10] << 2) | - (invparity[rp9] << 1) | - (invparity[rp8]); -#else - code[1] = - (invparity[rp7] << 7) | - (invparity[rp6] << 6) | - (invparity[rp5] << 5) | - (invparity[rp4] << 4) | - (invparity[rp3] << 3) | - (invparity[rp2] << 2) | - (invparity[rp1] << 1) | - (invparity[rp0]); - code[0] = - (invparity[rp15] << 7) | - (invparity[rp14] << 6) | - (invparity[rp13] << 5) | - (invparity[rp12] << 4) | - (invparity[rp11] << 3) | - (invparity[rp10] << 2) | - (invparity[rp9] << 1) | - (invparity[rp8]); -#endif - if (eccsize_mult == 1) - code[2] = - (invparity[par & 0xf0] << 7) | - (invparity[par & 0x0f] << 6) | - (invparity[par & 0xcc] << 5) | - (invparity[par & 0x33] << 4) | - (invparity[par & 0xaa] << 3) | - (invparity[par & 0x55] << 2) | - 3; - else - code[2] = - (invparity[par & 0xf0] << 7) | - (invparity[par & 0x0f] << 6) | - (invparity[par & 0xcc] << 5) | - (invparity[par & 0x33] << 4) | - (invparity[par & 0xaa] << 3) | - (invparity[par & 0x55] << 2) | - (invparity[rp17] << 1) | - (invparity[rp16] << 0); -} -EXPORT_SYMBOL(__nand_calculate_ecc); - -/** - * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte - * block - * @mtd: MTD block structure - * @buf: input buffer with raw data - * @code: output buffer with ECC - */ -int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, - unsigned char *code) -{ - __nand_calculate_ecc(buf, - mtd_to_nand(mtd)->ecc.size, code); - - return 0; -} -EXPORT_SYMBOL(nand_calculate_ecc); - -/** - * __nand_correct_data - [NAND Interface] Detect and correct bit error(s) - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * @eccsize: data bytes per ECC step (256 or 512) - * - * Detect and correct a 1 bit error for eccsize byte block - */ -int __nand_correct_data(unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc, - unsigned int eccsize) -{ - unsigned char b0, b1, b2, bit_addr; - unsigned int byte_addr; - /* 256 or 512 bytes/ecc */ - const uint32_t eccsize_mult = eccsize >> 8; - - /* - * b0 to b2 indicate which bit is faulty (if any) - * we might need the xor result more than once, - * so keep them in a local var - */ -#ifdef CONFIG_MTD_NAND_ECC_SMC - b0 = read_ecc[0] ^ calc_ecc[0]; - b1 = read_ecc[1] ^ calc_ecc[1]; -#else - b0 = read_ecc[1] ^ calc_ecc[1]; - b1 = read_ecc[0] ^ calc_ecc[0]; -#endif - b2 = read_ecc[2] ^ calc_ecc[2]; - - /* check if there are any bitfaults */ - - /* repeated if statements are slightly more efficient than switch ... */ - /* ordered in order of likelihood */ - - if ((b0 | b1 | b2) == 0) - return 0; /* no error */ - - if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) && - (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) && - ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) || - (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) { - /* single bit error */ - /* - * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty - * byte, cp 5/3/1 indicate the faulty bit. - * A lookup table (called addressbits) is used to filter - * the bits from the byte they are in. - * A marginal optimisation is possible by having three - * different lookup tables. - * One as we have now (for b0), one for b2 - * (that would avoid the >> 1), and one for b1 (with all values - * << 4). However it was felt that introducing two more tables - * hardly justify the gain. - * - * The b2 shift is there to get rid of the lowest two bits. - * We could also do addressbits[b2] >> 1 but for the - * performance it does not make any difference - */ - if (eccsize_mult == 1) - byte_addr = (addressbits[b1] << 4) + addressbits[b0]; - else - byte_addr = (addressbits[b2 & 0x3] << 8) + - (addressbits[b1] << 4) + addressbits[b0]; - bit_addr = addressbits[b2 >> 2]; - /* flip the bit */ - buf[byte_addr] ^= (1 << bit_addr); - return 1; - - } - /* count nr of bits; use table lookup, faster than calculating it */ - if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) - return 1; /* error in ECC data; no action needed */ - - pr_err("%s: uncorrectable ECC error\n", __func__); - return -EBADMSG; -} -EXPORT_SYMBOL(__nand_correct_data); - -/** - * nand_correct_data - [NAND Interface] Detect and correct bit error(s) - * @mtd: MTD block structure - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * - * Detect and correct a 1 bit error for 256/512 byte block - */ -int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - return __nand_correct_data(buf, read_ecc, calc_ecc, - mtd_to_nand(mtd)->ecc.size); -} -EXPORT_SYMBOL(nand_correct_data); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Frans Meulenbroeks "); -MODULE_DESCRIPTION("Generic NAND ECC support"); diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c deleted file mode 100644 index d542908a0ebb..000000000000 --- a/drivers/mtd/nand/nand_hynix.c +++ /dev/null @@ -1,676 +0,0 @@ -/* - * Copyright (C) 2017 Free Electrons - * Copyright (C) 2017 NextThing Co - * - * Author: Boris Brezillon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include - -#define NAND_HYNIX_CMD_SET_PARAMS 0x36 -#define NAND_HYNIX_CMD_APPLY_PARAMS 0x16 - -#define NAND_HYNIX_1XNM_RR_REPEAT 8 - -/** - * struct hynix_read_retry - read-retry data - * @nregs: number of register to set when applying a new read-retry mode - * @regs: register offsets (NAND chip dependent) - * @values: array of values to set in registers. The array size is equal to - * (nregs * nmodes) - */ -struct hynix_read_retry { - int nregs; - const u8 *regs; - u8 values[0]; -}; - -/** - * struct hynix_nand - private Hynix NAND struct - * @nand_technology: manufacturing process expressed in picometer - * @read_retry: read-retry information - */ -struct hynix_nand { - const struct hynix_read_retry *read_retry; -}; - -/** - * struct hynix_read_retry_otp - structure describing how the read-retry OTP - * area - * @nregs: number of hynix private registers to set before reading the reading - * the OTP area - * @regs: registers that should be configured - * @values: values that should be set in regs - * @page: the address to pass to the READ_PAGE command. Depends on the NAND - * chip - * @size: size of the read-retry OTP section - */ -struct hynix_read_retry_otp { - int nregs; - const u8 *regs; - const u8 *values; - int page; - int size; -}; - -static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) -{ - u8 jedecid[5] = { }; - int ret; - - ret = nand_readid_op(chip, 0x40, jedecid, sizeof(jedecid)); - if (ret) - return false; - - return !strncmp("JEDEC", jedecid, sizeof(jedecid)); -} - -static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (chip->exec_op) { - struct nand_op_instr instrs[] = { - NAND_OP_CMD(cmd, 0), - }; - struct nand_operation op = NAND_OPERATION(instrs); - - return nand_exec_op(chip, &op); - } - - chip->cmdfunc(mtd, cmd, -1, -1); - - return 0; -} - -static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - u16 column = ((u16)addr << 8) | addr; - - chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); - chip->write_byte(mtd, val); - - return 0; -} - -static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct hynix_nand *hynix = nand_get_manufacturer_data(chip); - const u8 *values; - int i, ret; - - values = hynix->read_retry->values + - (retry_mode * hynix->read_retry->nregs); - - /* Enter 'Set Hynix Parameters' mode */ - ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); - if (ret) - return ret; - - /* - * Configure the NAND in the requested read-retry mode. - * This is done by setting pre-defined values in internal NAND - * registers. - * - * The set of registers is NAND specific, and the values are either - * predefined or extracted from an OTP area on the NAND (values are - * probably tweaked at production in this case). - */ - for (i = 0; i < hynix->read_retry->nregs; i++) { - ret = hynix_nand_reg_write_op(chip, hynix->read_retry->regs[i], - values[i]); - if (ret) - return ret; - } - - /* Apply the new settings. */ - return hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); -} - -/** - * hynix_get_majority - get the value that is occurring the most in a given - * set of values - * @in: the array of values to test - * @repeat: the size of the in array - * @out: pointer used to store the output value - * - * This function implements the 'majority check' logic that is supposed to - * overcome the unreliability of MLC NANDs when reading the OTP area storing - * the read-retry parameters. - * - * It's based on a pretty simple assumption: if we repeat the same value - * several times and then take the one that is occurring the most, we should - * find the correct value. - * Let's hope this dummy algorithm prevents us from losing the read-retry - * parameters. - */ -static int hynix_get_majority(const u8 *in, int repeat, u8 *out) -{ - int i, j, half = repeat / 2; - - /* - * We only test the first half of the in array because we must ensure - * that the value is at least occurring repeat / 2 times. - * - * This loop is suboptimal since we may count the occurrences of the - * same value several time, but we are doing that on small sets, which - * makes it acceptable. - */ - for (i = 0; i < half; i++) { - int cnt = 0; - u8 val = in[i]; - - /* Count all values that are matching the one at index i. */ - for (j = i + 1; j < repeat; j++) { - if (in[j] == val) - cnt++; - } - - /* We found a value occurring more than repeat / 2. */ - if (cnt > half) { - *out = val; - return 0; - } - } - - return -EIO; -} - -static int hynix_read_rr_otp(struct nand_chip *chip, - const struct hynix_read_retry_otp *info, - void *buf) -{ - int i, ret; - - ret = nand_reset_op(chip); - if (ret) - return ret; - - ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); - if (ret) - return ret; - - for (i = 0; i < info->nregs; i++) { - ret = hynix_nand_reg_write_op(chip, info->regs[i], - info->values[i]); - if (ret) - return ret; - } - - ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); - if (ret) - return ret; - - /* Sequence to enter OTP mode? */ - ret = hynix_nand_cmd_op(chip, 0x17); - if (ret) - return ret; - - ret = hynix_nand_cmd_op(chip, 0x4); - if (ret) - return ret; - - ret = hynix_nand_cmd_op(chip, 0x19); - if (ret) - return ret; - - /* Now read the page */ - ret = nand_read_page_op(chip, info->page, 0, buf, info->size); - if (ret) - return ret; - - /* Put everything back to normal */ - ret = nand_reset_op(chip); - if (ret) - return ret; - - ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); - if (ret) - return ret; - - ret = hynix_nand_reg_write_op(chip, 0x38, 0); - if (ret) - return ret; - - ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); - if (ret) - return ret; - - return nand_read_page_op(chip, 0, 0, NULL, 0); -} - -#define NAND_HYNIX_1XNM_RR_COUNT_OFFS 0 -#define NAND_HYNIX_1XNM_RR_REG_COUNT_OFFS 8 -#define NAND_HYNIX_1XNM_RR_SET_OFFS(x, setsize, inv) \ - (16 + ((((x) * 2) + ((inv) ? 1 : 0)) * (setsize))) - -static int hynix_mlc_1xnm_rr_value(const u8 *buf, int nmodes, int nregs, - int mode, int reg, bool inv, u8 *val) -{ - u8 tmp[NAND_HYNIX_1XNM_RR_REPEAT]; - int val_offs = (mode * nregs) + reg; - int set_size = nmodes * nregs; - int i, ret; - - for (i = 0; i < NAND_HYNIX_1XNM_RR_REPEAT; i++) { - int set_offs = NAND_HYNIX_1XNM_RR_SET_OFFS(i, set_size, inv); - - tmp[i] = buf[val_offs + set_offs]; - } - - ret = hynix_get_majority(tmp, NAND_HYNIX_1XNM_RR_REPEAT, val); - if (ret) - return ret; - - if (inv) - *val = ~*val; - - return 0; -} - -static u8 hynix_1xnm_mlc_read_retry_regs[] = { - 0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf -}; - -static int hynix_mlc_1xnm_rr_init(struct nand_chip *chip, - const struct hynix_read_retry_otp *info) -{ - struct hynix_nand *hynix = nand_get_manufacturer_data(chip); - struct hynix_read_retry *rr = NULL; - int ret, i, j; - u8 nregs, nmodes; - u8 *buf; - - buf = kmalloc(info->size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = hynix_read_rr_otp(chip, info, buf); - if (ret) - goto out; - - ret = hynix_get_majority(buf, NAND_HYNIX_1XNM_RR_REPEAT, - &nmodes); - if (ret) - goto out; - - ret = hynix_get_majority(buf + NAND_HYNIX_1XNM_RR_REPEAT, - NAND_HYNIX_1XNM_RR_REPEAT, - &nregs); - if (ret) - goto out; - - rr = kzalloc(sizeof(*rr) + (nregs * nmodes), GFP_KERNEL); - if (!rr) { - ret = -ENOMEM; - goto out; - } - - for (i = 0; i < nmodes; i++) { - for (j = 0; j < nregs; j++) { - u8 *val = rr->values + (i * nregs); - - ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j, - false, val); - if (!ret) - continue; - - ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j, - true, val); - if (ret) - goto out; - } - } - - rr->nregs = nregs; - rr->regs = hynix_1xnm_mlc_read_retry_regs; - hynix->read_retry = rr; - chip->setup_read_retry = hynix_nand_setup_read_retry; - chip->read_retries = nmodes; - -out: - kfree(buf); - - if (ret) - kfree(rr); - - return ret; -} - -static const u8 hynix_mlc_1xnm_rr_otp_regs[] = { 0x38 }; -static const u8 hynix_mlc_1xnm_rr_otp_values[] = { 0x52 }; - -static const struct hynix_read_retry_otp hynix_mlc_1xnm_rr_otps[] = { - { - .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs), - .regs = hynix_mlc_1xnm_rr_otp_regs, - .values = hynix_mlc_1xnm_rr_otp_values, - .page = 0x21f, - .size = 784 - }, - { - .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs), - .regs = hynix_mlc_1xnm_rr_otp_regs, - .values = hynix_mlc_1xnm_rr_otp_values, - .page = 0x200, - .size = 528, - }, -}; - -static int hynix_nand_rr_init(struct nand_chip *chip) -{ - int i, ret = 0; - bool valid_jedecid; - - valid_jedecid = hynix_nand_has_valid_jedecid(chip); - - /* - * We only support read-retry for 1xnm NANDs, and those NANDs all - * expose a valid JEDEC ID. - */ - if (valid_jedecid) { - u8 nand_tech = chip->id.data[5] >> 4; - - /* 1xnm technology */ - if (nand_tech == 4) { - for (i = 0; i < ARRAY_SIZE(hynix_mlc_1xnm_rr_otps); - i++) { - /* - * FIXME: Hynix recommend to copy the - * read-retry OTP area into a normal page. - */ - ret = hynix_mlc_1xnm_rr_init(chip, - hynix_mlc_1xnm_rr_otps); - if (!ret) - break; - } - } - } - - if (ret) - pr_warn("failed to initialize read-retry infrastructure"); - - return 0; -} - -static void hynix_nand_extract_oobsize(struct nand_chip *chip, - bool valid_jedecid) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - u8 oobsize; - - oobsize = ((chip->id.data[3] >> 2) & 0x3) | - ((chip->id.data[3] >> 4) & 0x4); - - if (valid_jedecid) { - switch (oobsize) { - case 0: - mtd->oobsize = 2048; - break; - case 1: - mtd->oobsize = 1664; - break; - case 2: - mtd->oobsize = 1024; - break; - case 3: - mtd->oobsize = 640; - break; - default: - /* - * We should never reach this case, but if that - * happens, this probably means Hynix decided to use - * a different extended ID format, and we should find - * a way to support it. - */ - WARN(1, "Invalid OOB size"); - break; - } - } else { - switch (oobsize) { - case 0: - mtd->oobsize = 128; - break; - case 1: - mtd->oobsize = 224; - break; - case 2: - mtd->oobsize = 448; - break; - case 3: - mtd->oobsize = 64; - break; - case 4: - mtd->oobsize = 32; - break; - case 5: - mtd->oobsize = 16; - break; - case 6: - mtd->oobsize = 640; - break; - default: - /* - * We should never reach this case, but if that - * happens, this probably means Hynix decided to use - * a different extended ID format, and we should find - * a way to support it. - */ - WARN(1, "Invalid OOB size"); - break; - } - } -} - -static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip, - bool valid_jedecid) -{ - u8 ecc_level = (chip->id.data[4] >> 4) & 0x7; - - if (valid_jedecid) { - /* Reference: H27UCG8T2E datasheet */ - chip->ecc_step_ds = 1024; - - switch (ecc_level) { - case 0: - chip->ecc_step_ds = 0; - chip->ecc_strength_ds = 0; - break; - case 1: - chip->ecc_strength_ds = 4; - break; - case 2: - chip->ecc_strength_ds = 24; - break; - case 3: - chip->ecc_strength_ds = 32; - break; - case 4: - chip->ecc_strength_ds = 40; - break; - case 5: - chip->ecc_strength_ds = 50; - break; - case 6: - chip->ecc_strength_ds = 60; - break; - default: - /* - * We should never reach this case, but if that - * happens, this probably means Hynix decided to use - * a different extended ID format, and we should find - * a way to support it. - */ - WARN(1, "Invalid ECC requirements"); - } - } else { - /* - * The ECC requirements field meaning depends on the - * NAND technology. - */ - u8 nand_tech = chip->id.data[5] & 0x7; - - if (nand_tech < 3) { - /* > 26nm, reference: H27UBG8T2A datasheet */ - if (ecc_level < 5) { - chip->ecc_step_ds = 512; - chip->ecc_strength_ds = 1 << ecc_level; - } else if (ecc_level < 7) { - if (ecc_level == 5) - chip->ecc_step_ds = 2048; - else - chip->ecc_step_ds = 1024; - chip->ecc_strength_ds = 24; - } else { - /* - * We should never reach this case, but if that - * happens, this probably means Hynix decided - * to use a different extended ID format, and - * we should find a way to support it. - */ - WARN(1, "Invalid ECC requirements"); - } - } else { - /* <= 26nm, reference: H27UBG8T2B datasheet */ - if (!ecc_level) { - chip->ecc_step_ds = 0; - chip->ecc_strength_ds = 0; - } else if (ecc_level < 5) { - chip->ecc_step_ds = 512; - chip->ecc_strength_ds = 1 << (ecc_level - 1); - } else { - chip->ecc_step_ds = 1024; - chip->ecc_strength_ds = 24 + - (8 * (ecc_level - 5)); - } - } - } -} - -static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip, - bool valid_jedecid) -{ - u8 nand_tech; - - /* We need scrambling on all TLC NANDs*/ - if (chip->bits_per_cell > 2) - chip->options |= NAND_NEED_SCRAMBLING; - - /* And on MLC NANDs with sub-3xnm process */ - if (valid_jedecid) { - nand_tech = chip->id.data[5] >> 4; - - /* < 3xnm */ - if (nand_tech > 0) - chip->options |= NAND_NEED_SCRAMBLING; - } else { - nand_tech = chip->id.data[5] & 0x7; - - /* < 32nm */ - if (nand_tech > 2) - chip->options |= NAND_NEED_SCRAMBLING; - } -} - -static void hynix_nand_decode_id(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - bool valid_jedecid; - u8 tmp; - - /* - * Exclude all SLC NANDs from this advanced detection scheme. - * According to the ranges defined in several datasheets, it might - * appear that even SLC NANDs could fall in this extended ID scheme. - * If that the case rework the test to let SLC NANDs go through the - * detection process. - */ - if (chip->id.len < 6 || nand_is_slc(chip)) { - nand_decode_ext_id(chip); - return; - } - - /* Extract pagesize */ - mtd->writesize = 2048 << (chip->id.data[3] & 0x03); - - tmp = (chip->id.data[3] >> 4) & 0x3; - /* - * When bit7 is set that means we start counting at 1MiB, otherwise - * we start counting at 128KiB and shift this value the content of - * ID[3][4:5]. - * The only exception is when ID[3][4:5] == 3 and ID[3][7] == 0, in - * this case the erasesize is set to 768KiB. - */ - if (chip->id.data[3] & 0x80) - mtd->erasesize = SZ_1M << tmp; - else if (tmp == 3) - mtd->erasesize = SZ_512K + SZ_256K; - else - mtd->erasesize = SZ_128K << tmp; - - /* - * Modern Toggle DDR NANDs have a valid JEDECID even though they are - * not exposing a valid JEDEC parameter table. - * These NANDs use a different NAND ID scheme. - */ - valid_jedecid = hynix_nand_has_valid_jedecid(chip); - - hynix_nand_extract_oobsize(chip, valid_jedecid); - hynix_nand_extract_ecc_requirements(chip, valid_jedecid); - hynix_nand_extract_scrambling_requirements(chip, valid_jedecid); -} - -static void hynix_nand_cleanup(struct nand_chip *chip) -{ - struct hynix_nand *hynix = nand_get_manufacturer_data(chip); - - if (!hynix) - return; - - kfree(hynix->read_retry); - kfree(hynix); - nand_set_manufacturer_data(chip, NULL); -} - -static int hynix_nand_init(struct nand_chip *chip) -{ - struct hynix_nand *hynix; - int ret; - - if (!nand_is_slc(chip)) - chip->bbt_options |= NAND_BBT_SCANLASTPAGE; - else - chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; - - hynix = kzalloc(sizeof(*hynix), GFP_KERNEL); - if (!hynix) - return -ENOMEM; - - nand_set_manufacturer_data(chip, hynix); - - ret = hynix_nand_rr_init(chip); - if (ret) - hynix_nand_cleanup(chip); - - return ret; -} - -const struct nand_manufacturer_ops hynix_nand_manuf_ops = { - .detect = hynix_nand_decode_id, - .init = hynix_nand_init, - .cleanup = hynix_nand_cleanup, -}; diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c deleted file mode 100644 index 5423c3bb388e..000000000000 --- a/drivers/mtd/nand/nand_ids.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ -#include -#include - -#define LP_OPTIONS 0 -#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) - -#define SP_OPTIONS NAND_NEED_READRDY -#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16) - -/* - * The chip ID list: - * name, device ID, page size, chip size in MiB, eraseblock size, options - * - * If page size and eraseblock size are 0, the sizes are taken from the - * extended chip ID. - */ -struct nand_flash_dev nand_flash_ids[] = { - /* - * Some incompatible NAND chips share device ID's and so must be - * listed by full ID. We list them first so that we can easily identify - * the most specific match. - */ - {"TC58NVG0S3E 1G 3.3V 8-bit", - { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} }, - SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), - 2 }, - {"TC58NVG2S0F 4G 3.3V 8-bit", - { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, - SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, - {"TC58NVG2S0H 4G 3.3V 8-bit", - { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} }, - SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) }, - {"TC58NVG3S0F 8G 3.3V 8-bit", - { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, - SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, - {"TC58NVG5D2 32G 3.3V 8-bit", - { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} }, - SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, - {"TC58NVG6D2 64G 3.3V 8-bit", - { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, - SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, - {"SDTNRGAMA 64G 3.3V 8-bit", - { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} }, - SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, - {"H27UCG8T2ATR-BC 64G 3.3V 8-bit", - { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, - SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640, - NAND_ECC_INFO(40, SZ_1K), 4 }, - - LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS), - - LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16), - - LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16), - - LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16), - - LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16), - - LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS), - - /* - * These are the new chips with large page size. Their page size and - * eraseblock size are determined from the extended ID bytes. - */ - - /* 512 Megabit */ - EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16), - - /* 1 Gigabit */ - EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16), - - /* 2 Gigabit */ - EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16), - - /* 4 Gigabit */ - EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16), - - /* 8 Gigabit */ - EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16), - - /* 16 Gigabit */ - EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16), - - /* 32 Gigabit */ - EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16), - - /* 64 Gigabit */ - EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16), - - /* 128 Gigabit */ - EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16), - - /* 256 Gigabit */ - EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16), - - /* 512 Gigabit */ - EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16), - - {NULL} -}; - -/* Manufacturer IDs */ -static const struct nand_manufacturer nand_manufacturers[] = { - {NAND_MFR_TOSHIBA, "Toshiba", &toshiba_nand_manuf_ops}, - {NAND_MFR_ESMT, "ESMT"}, - {NAND_MFR_SAMSUNG, "Samsung", &samsung_nand_manuf_ops}, - {NAND_MFR_FUJITSU, "Fujitsu"}, - {NAND_MFR_NATIONAL, "National"}, - {NAND_MFR_RENESAS, "Renesas"}, - {NAND_MFR_STMICRO, "ST Micro"}, - {NAND_MFR_HYNIX, "Hynix", &hynix_nand_manuf_ops}, - {NAND_MFR_MICRON, "Micron", µn_nand_manuf_ops}, - {NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops}, - {NAND_MFR_MACRONIX, "Macronix", ¯onix_nand_manuf_ops}, - {NAND_MFR_EON, "Eon"}, - {NAND_MFR_SANDISK, "SanDisk"}, - {NAND_MFR_INTEL, "Intel"}, - {NAND_MFR_ATO, "ATO"}, - {NAND_MFR_WINBOND, "Winbond"}, -}; - -/** - * nand_get_manufacturer - Get manufacturer information from the manufacturer - * ID - * @id: manufacturer ID - * - * Returns a pointer a nand_manufacturer object if the manufacturer is defined - * in the NAND manufacturers database, NULL otherwise. - */ -const struct nand_manufacturer *nand_get_manufacturer(u8 id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(nand_manufacturers); i++) - if (nand_manufacturers[i].id == id) - return &nand_manufacturers[i]; - - return NULL; -} diff --git a/drivers/mtd/nand/nand_macronix.c b/drivers/mtd/nand/nand_macronix.c deleted file mode 100644 index d290ff2a6d2f..000000000000 --- a/drivers/mtd/nand/nand_macronix.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2017 Free Electrons - * Copyright (C) 2017 NextThing Co - * - * Author: Boris Brezillon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include - -static int macronix_nand_init(struct nand_chip *chip) -{ - if (nand_is_slc(chip)) - chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; - - return 0; -} - -const struct nand_manufacturer_ops macronix_nand_manuf_ops = { - .init = macronix_nand_init, -}; diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/nand_micron.c deleted file mode 100644 index 02e109ae73f1..000000000000 --- a/drivers/mtd/nand/nand_micron.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (C) 2017 Free Electrons - * Copyright (C) 2017 NextThing Co - * - * Author: Boris Brezillon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include - -/* - * Special Micron status bit that indicates when the block has been - * corrected by on-die ECC and should be rewritten - */ -#define NAND_STATUS_WRITE_RECOMMENDED BIT(3) - -struct nand_onfi_vendor_micron { - u8 two_plane_read; - u8 read_cache; - u8 read_unique_id; - u8 dq_imped; - u8 dq_imped_num_settings; - u8 dq_imped_feat_addr; - u8 rb_pulldown_strength; - u8 rb_pulldown_strength_feat_addr; - u8 rb_pulldown_strength_num_settings; - u8 otp_mode; - u8 otp_page_start; - u8 otp_data_prot_addr; - u8 otp_num_pages; - u8 otp_feat_addr; - u8 read_retry_options; - u8 reserved[72]; - u8 param_revision; -} __packed; - -static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; - - return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, - feature); -} - -/* - * Configure chip properties from Micron vendor-specific ONFI table - */ -static int micron_nand_onfi_init(struct nand_chip *chip) -{ - struct nand_onfi_params *p = &chip->onfi_params; - struct nand_onfi_vendor_micron *micron = (void *)p->vendor; - - if (!chip->onfi_version) - return 0; - - if (le16_to_cpu(p->vendor_revision) < 1) - return 0; - - chip->read_retries = micron->read_retry_options; - chip->setup_read_retry = micron_nand_setup_read_retry; - - return 0; -} - -static int micron_nand_on_die_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section >= 4) - return -ERANGE; - - oobregion->offset = (section * 16) + 8; - oobregion->length = 8; - - return 0; -} - -static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section >= 4) - return -ERANGE; - - oobregion->offset = (section * 16) + 2; - oobregion->length = 6; - - return 0; -} - -static const struct mtd_ooblayout_ops micron_nand_on_die_ooblayout_ops = { - .ecc = micron_nand_on_die_ooblayout_ecc, - .free = micron_nand_on_die_ooblayout_free, -}; - -static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) -{ - u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; - - if (enable) - feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN; - - return chip->onfi_set_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); -} - -static int -micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, - int page) -{ - u8 status; - int ret, max_bitflips = 0; - - ret = micron_nand_on_die_ecc_setup(chip, true); - if (ret) - return ret; - - ret = nand_read_page_op(chip, page, 0, NULL, 0); - if (ret) - goto out; - - ret = nand_status_op(chip, &status); - if (ret) - goto out; - - ret = nand_exit_status_op(chip); - if (ret) - goto out; - - if (status & NAND_STATUS_FAIL) - mtd->ecc_stats.failed++; - - /* - * The internal ECC doesn't tell us the number of bitflips - * that have been corrected, but tells us if it recommends to - * rewrite the block. If it's the case, then we pretend we had - * a number of bitflips equal to the ECC strength, which will - * hint the NAND core to rewrite the block. - */ - else if (status & NAND_STATUS_WRITE_RECOMMENDED) - max_bitflips = chip->ecc.strength; - - ret = nand_read_data_op(chip, buf, mtd->writesize, false); - if (!ret && oob_required) - ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, - false); - -out: - micron_nand_on_die_ecc_setup(chip, false); - - return ret ? ret : max_bitflips; -} - -static int -micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int ret; - - ret = micron_nand_on_die_ecc_setup(chip, true); - if (ret) - return ret; - - ret = nand_write_page_raw(mtd, chip, buf, oob_required, page); - micron_nand_on_die_ecc_setup(chip, false); - - return ret; -} - -enum { - /* The NAND flash doesn't support on-die ECC */ - MICRON_ON_DIE_UNSUPPORTED, - - /* - * The NAND flash supports on-die ECC and it can be - * enabled/disabled by a set features command. - */ - MICRON_ON_DIE_SUPPORTED, - - /* - * The NAND flash supports on-die ECC, and it cannot be - * disabled. - */ - MICRON_ON_DIE_MANDATORY, -}; - -/* - * Try to detect if the NAND support on-die ECC. To do this, we enable - * the feature, and read back if it has been enabled as expected. We - * also check if it can be disabled, because some Micron NANDs do not - * allow disabling the on-die ECC and we don't support such NANDs for - * now. - * - * This function also has the side effect of disabling on-die ECC if - * it had been left enabled by the firmware/bootloader. - */ -static int micron_supports_on_die_ecc(struct nand_chip *chip) -{ - u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; - int ret; - - if (chip->onfi_version == 0) - return MICRON_ON_DIE_UNSUPPORTED; - - if (chip->bits_per_cell != 1) - return MICRON_ON_DIE_UNSUPPORTED; - - ret = micron_nand_on_die_ecc_setup(chip, true); - if (ret) - return MICRON_ON_DIE_UNSUPPORTED; - - chip->onfi_get_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); - if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0) - return MICRON_ON_DIE_UNSUPPORTED; - - ret = micron_nand_on_die_ecc_setup(chip, false); - if (ret) - return MICRON_ON_DIE_UNSUPPORTED; - - chip->onfi_get_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); - if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) - return MICRON_ON_DIE_MANDATORY; - - /* - * Some Micron NANDs have an on-die ECC of 4/512, some other - * 8/512. We only support the former. - */ - if (chip->onfi_params.ecc_bits != 4) - return MICRON_ON_DIE_UNSUPPORTED; - - return MICRON_ON_DIE_SUPPORTED; -} - -static int micron_nand_init(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int ondie; - int ret; - - ret = micron_nand_onfi_init(chip); - if (ret) - return ret; - - if (mtd->writesize == 2048) - chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; - - ondie = micron_supports_on_die_ecc(chip); - - if (ondie == MICRON_ON_DIE_MANDATORY) { - pr_err("On-die ECC forcefully enabled, not supported\n"); - return -EINVAL; - } - - if (chip->ecc.mode == NAND_ECC_ON_DIE) { - if (ondie == MICRON_ON_DIE_UNSUPPORTED) { - pr_err("On-die ECC selected but not supported\n"); - return -EINVAL; - } - - chip->ecc.bytes = 8; - chip->ecc.size = 512; - chip->ecc.strength = 4; - chip->ecc.algo = NAND_ECC_BCH; - chip->ecc.read_page = micron_nand_read_page_on_die_ecc; - chip->ecc.write_page = micron_nand_write_page_on_die_ecc; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; - - mtd_set_ooblayout(mtd, µn_nand_on_die_ooblayout_ops); - } - - return 0; -} - -const struct nand_manufacturer_ops micron_nand_manuf_ops = { - .init = micron_nand_init, -}; diff --git a/drivers/mtd/nand/nand_samsung.c b/drivers/mtd/nand/nand_samsung.c deleted file mode 100644 index ef022f62f74c..000000000000 --- a/drivers/mtd/nand/nand_samsung.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2017 Free Electrons - * Copyright (C) 2017 NextThing Co - * - * Author: Boris Brezillon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include - -static void samsung_nand_decode_id(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - /* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */ - if (chip->id.len == 6 && !nand_is_slc(chip) && - chip->id.data[5] != 0x00) { - u8 extid = chip->id.data[3]; - - /* Get pagesize */ - mtd->writesize = 2048 << (extid & 0x03); - - extid >>= 2; - - /* Get oobsize */ - switch (((extid >> 2) & 0x4) | (extid & 0x3)) { - case 1: - mtd->oobsize = 128; - break; - case 2: - mtd->oobsize = 218; - break; - case 3: - mtd->oobsize = 400; - break; - case 4: - mtd->oobsize = 436; - break; - case 5: - mtd->oobsize = 512; - break; - case 6: - mtd->oobsize = 640; - break; - default: - /* - * We should never reach this case, but if that - * happens, this probably means Samsung decided to use - * a different extended ID format, and we should find - * a way to support it. - */ - WARN(1, "Invalid OOB size value"); - break; - } - - /* Get blocksize */ - extid >>= 2; - mtd->erasesize = (128 * 1024) << - (((extid >> 1) & 0x04) | (extid & 0x03)); - - /* Extract ECC requirements from 5th id byte*/ - extid = (chip->id.data[4] >> 4) & 0x07; - if (extid < 5) { - chip->ecc_step_ds = 512; - chip->ecc_strength_ds = 1 << extid; - } else { - chip->ecc_step_ds = 1024; - switch (extid) { - case 5: - chip->ecc_strength_ds = 24; - break; - case 6: - chip->ecc_strength_ds = 40; - break; - case 7: - chip->ecc_strength_ds = 60; - break; - default: - WARN(1, "Could not decode ECC info"); - chip->ecc_step_ds = 0; - } - } - } else { - nand_decode_ext_id(chip); - - if (nand_is_slc(chip)) { - switch (chip->id.data[1]) { - /* K9F4G08U0D-S[I|C]B0(T00) */ - case 0xDC: - chip->ecc_step_ds = 512; - chip->ecc_strength_ds = 1; - break; - - /* K9F1G08U0E 21nm chips do not support subpage write */ - case 0xF1: - if (chip->id.len > 4 && - (chip->id.data[4] & GENMASK(1, 0)) == 0x1) - chip->options |= NAND_NO_SUBPAGE_WRITE; - break; - default: - break; - } - } - } -} - -static int samsung_nand_init(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - if (mtd->writesize > 512) - chip->options |= NAND_SAMSUNG_LP_OPTIONS; - - if (!nand_is_slc(chip)) - chip->bbt_options |= NAND_BBT_SCANLASTPAGE; - else - chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; - - return 0; -} - -const struct nand_manufacturer_ops samsung_nand_manuf_ops = { - .detect = samsung_nand_decode_id, - .init = samsung_nand_init, -}; diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c deleted file mode 100644 index 9400d039ddbd..000000000000 --- a/drivers/mtd/nand/nand_timings.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (C) 2014 Free Electrons - * - * Author: Boris BREZILLON - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ -#include -#include -#include -#include - -static const struct nand_data_interface onfi_sdr_timings[] = { - /* Mode 0 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 20000, - .tALS_min = 50000, - .tAR_min = 25000, - .tCEA_max = 100000, - .tCEH_min = 20000, - .tCH_min = 20000, - .tCHZ_max = 100000, - .tCLH_min = 20000, - .tCLR_min = 20000, - .tCLS_min = 50000, - .tCOH_min = 0, - .tCS_min = 70000, - .tDH_min = 20000, - .tDS_min = 40000, - .tFEAT_max = 1000000, - .tIR_min = 10000, - .tITC_max = 1000000, - .tRC_min = 100000, - .tREA_max = 40000, - .tREH_min = 30000, - .tRHOH_min = 0, - .tRHW_min = 200000, - .tRHZ_max = 200000, - .tRLOH_min = 0, - .tRP_min = 50000, - .tRR_min = 40000, - .tRST_max = 250000000000ULL, - .tWB_max = 200000, - .tWC_min = 100000, - .tWH_min = 30000, - .tWHR_min = 120000, - .tWP_min = 50000, - .tWW_min = 100000, - }, - }, - /* Mode 1 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 10000, - .tALS_min = 25000, - .tAR_min = 10000, - .tCEA_max = 45000, - .tCEH_min = 20000, - .tCH_min = 10000, - .tCHZ_max = 50000, - .tCLH_min = 10000, - .tCLR_min = 10000, - .tCLS_min = 25000, - .tCOH_min = 15000, - .tCS_min = 35000, - .tDH_min = 10000, - .tDS_min = 20000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 50000, - .tREA_max = 30000, - .tREH_min = 15000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 0, - .tRP_min = 25000, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tWC_min = 45000, - .tWH_min = 15000, - .tWHR_min = 80000, - .tWP_min = 25000, - .tWW_min = 100000, - }, - }, - /* Mode 2 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 10000, - .tALS_min = 15000, - .tAR_min = 10000, - .tCEA_max = 30000, - .tCEH_min = 20000, - .tCH_min = 10000, - .tCHZ_max = 50000, - .tCLH_min = 10000, - .tCLR_min = 10000, - .tCLS_min = 15000, - .tCOH_min = 15000, - .tCS_min = 25000, - .tDH_min = 5000, - .tDS_min = 15000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 35000, - .tREA_max = 25000, - .tREH_min = 15000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 0, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tRP_min = 17000, - .tWC_min = 35000, - .tWH_min = 15000, - .tWHR_min = 80000, - .tWP_min = 17000, - .tWW_min = 100000, - }, - }, - /* Mode 3 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 5000, - .tALS_min = 10000, - .tAR_min = 10000, - .tCEA_max = 25000, - .tCEH_min = 20000, - .tCH_min = 5000, - .tCHZ_max = 50000, - .tCLH_min = 5000, - .tCLR_min = 10000, - .tCLS_min = 10000, - .tCOH_min = 15000, - .tCS_min = 25000, - .tDH_min = 5000, - .tDS_min = 10000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 30000, - .tREA_max = 20000, - .tREH_min = 10000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 0, - .tRP_min = 15000, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tWC_min = 30000, - .tWH_min = 10000, - .tWHR_min = 80000, - .tWP_min = 15000, - .tWW_min = 100000, - }, - }, - /* Mode 4 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 5000, - .tALS_min = 10000, - .tAR_min = 10000, - .tCEA_max = 25000, - .tCEH_min = 20000, - .tCH_min = 5000, - .tCHZ_max = 30000, - .tCLH_min = 5000, - .tCLR_min = 10000, - .tCLS_min = 10000, - .tCOH_min = 15000, - .tCS_min = 20000, - .tDH_min = 5000, - .tDS_min = 10000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 25000, - .tREA_max = 20000, - .tREH_min = 10000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 5000, - .tRP_min = 12000, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tWC_min = 25000, - .tWH_min = 10000, - .tWHR_min = 80000, - .tWP_min = 12000, - .tWW_min = 100000, - }, - }, - /* Mode 5 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 5000, - .tALS_min = 10000, - .tAR_min = 10000, - .tCEA_max = 25000, - .tCEH_min = 20000, - .tCH_min = 5000, - .tCHZ_max = 30000, - .tCLH_min = 5000, - .tCLR_min = 10000, - .tCLS_min = 10000, - .tCOH_min = 15000, - .tCS_min = 15000, - .tDH_min = 5000, - .tDS_min = 7000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 20000, - .tREA_max = 16000, - .tREH_min = 7000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 5000, - .tRP_min = 10000, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tWC_min = 20000, - .tWH_min = 7000, - .tWHR_min = 80000, - .tWP_min = 10000, - .tWW_min = 100000, - }, - }, -}; - -/** - * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND - * timings according to the given ONFI timing mode - * @mode: ONFI timing mode - */ -const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode) -{ - if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings)) - return ERR_PTR(-EINVAL); - - return &onfi_sdr_timings[mode].timings.sdr; -} -EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings); - -/** - * onfi_fill_data_interface - [NAND Interface] Initialize a data interface from - * given ONFI mode - * @mode: The ONFI timing mode - */ -int onfi_fill_data_interface(struct nand_chip *chip, - enum nand_data_interface_type type, - int timing_mode) -{ - struct nand_data_interface *iface = &chip->data_interface; - - if (type != NAND_SDR_IFACE) - return -EINVAL; - - if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings)) - return -EINVAL; - - *iface = onfi_sdr_timings[timing_mode]; - - /* - * Initialize timings that cannot be deduced from timing mode: - * tR, tPROG, tCCS, ... - * These information are part of the ONFI parameter page. - */ - if (chip->onfi_version) { - struct nand_onfi_params *params = &chip->onfi_params; - struct nand_sdr_timings *timings = &iface->timings.sdr; - - /* microseconds -> picoseconds */ - timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog); - timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers); - timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r); - - /* nanoseconds -> picoseconds */ - timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); - } - - return 0; -} -EXPORT_SYMBOL(onfi_fill_data_interface); diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/nand_toshiba.c deleted file mode 100644 index ab43f027cd23..000000000000 --- a/drivers/mtd/nand/nand_toshiba.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2017 Free Electrons - * Copyright (C) 2017 NextThing Co - * - * Author: Boris Brezillon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include - -static void toshiba_nand_decode_id(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - nand_decode_ext_id(chip); - - /* - * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per - * 512B page. For Toshiba SLC, we decode the 5th/6th byte as - * follows: - * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, - * 110b -> 24nm - * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC - */ - if (chip->id.len >= 6 && nand_is_slc(chip) && - (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ && - !(chip->id.data[4] & 0x80) /* !BENAND */) - mtd->oobsize = 32 * mtd->writesize >> 9; - - /* - * Extract ECC requirements from 6th id byte. - * For Toshiba SLC, ecc requrements are as follows: - * - 43nm: 1 bit ECC for each 512Byte is required. - * - 32nm: 4 bit ECC for each 512Byte is required. - * - 24nm: 8 bit ECC for each 512Byte is required. - */ - if (chip->id.len >= 6 && nand_is_slc(chip)) { - chip->ecc_step_ds = 512; - switch (chip->id.data[5] & 0x7) { - case 0x4: - chip->ecc_strength_ds = 1; - break; - case 0x5: - chip->ecc_strength_ds = 4; - break; - case 0x6: - chip->ecc_strength_ds = 8; - break; - default: - WARN(1, "Could not get ECC info"); - chip->ecc_step_ds = 0; - break; - } - } -} - -static int toshiba_nand_init(struct nand_chip *chip) -{ - if (nand_is_slc(chip)) - chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; - - return 0; -} - -const struct nand_manufacturer_ops toshiba_nand_manuf_ops = { - .detect = toshiba_nand_decode_id, - .init = toshiba_nand_init, -}; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c deleted file mode 100644 index 44322a363ba5..000000000000 --- a/drivers/mtd/nand/nandsim.c +++ /dev/null @@ -1,2392 +0,0 @@ -/* - * NAND flash simulator. - * - * Author: Artem B. Bityuckiy , - * - * Copyright (C) 2004 Nokia Corporation - * - * Note: NS means "NAND Simulator". - * Note: Input means input TO flash chip, output means output FROM chip. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Default simulator parameters values */ -#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ - !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \ - !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \ - !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE) -#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98 -#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39 -#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */ -#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */ -#endif - -#ifndef CONFIG_NANDSIM_ACCESS_DELAY -#define CONFIG_NANDSIM_ACCESS_DELAY 25 -#endif -#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY -#define CONFIG_NANDSIM_PROGRAMM_DELAY 200 -#endif -#ifndef CONFIG_NANDSIM_ERASE_DELAY -#define CONFIG_NANDSIM_ERASE_DELAY 2 -#endif -#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE -#define CONFIG_NANDSIM_OUTPUT_CYCLE 40 -#endif -#ifndef CONFIG_NANDSIM_INPUT_CYCLE -#define CONFIG_NANDSIM_INPUT_CYCLE 50 -#endif -#ifndef CONFIG_NANDSIM_BUS_WIDTH -#define CONFIG_NANDSIM_BUS_WIDTH 8 -#endif -#ifndef CONFIG_NANDSIM_DO_DELAYS -#define CONFIG_NANDSIM_DO_DELAYS 0 -#endif -#ifndef CONFIG_NANDSIM_LOG -#define CONFIG_NANDSIM_LOG 0 -#endif -#ifndef CONFIG_NANDSIM_DBG -#define CONFIG_NANDSIM_DBG 0 -#endif -#ifndef CONFIG_NANDSIM_MAX_PARTS -#define CONFIG_NANDSIM_MAX_PARTS 32 -#endif - -static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY; -static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY; -static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY; -static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE; -static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE; -static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; -static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; -static uint log = CONFIG_NANDSIM_LOG; -static uint dbg = CONFIG_NANDSIM_DBG; -static unsigned long parts[CONFIG_NANDSIM_MAX_PARTS]; -static unsigned int parts_num; -static char *badblocks = NULL; -static char *weakblocks = NULL; -static char *weakpages = NULL; -static unsigned int bitflips = 0; -static char *gravepages = NULL; -static unsigned int overridesize = 0; -static char *cache_file = NULL; -static unsigned int bbt; -static unsigned int bch; -static u_char id_bytes[8] = { - [0] = CONFIG_NANDSIM_FIRST_ID_BYTE, - [1] = CONFIG_NANDSIM_SECOND_ID_BYTE, - [2] = CONFIG_NANDSIM_THIRD_ID_BYTE, - [3] = CONFIG_NANDSIM_FOURTH_ID_BYTE, - [4 ... 7] = 0xFF, -}; - -module_param_array(id_bytes, byte, NULL, 0400); -module_param_named(first_id_byte, id_bytes[0], byte, 0400); -module_param_named(second_id_byte, id_bytes[1], byte, 0400); -module_param_named(third_id_byte, id_bytes[2], byte, 0400); -module_param_named(fourth_id_byte, id_bytes[3], byte, 0400); -module_param(access_delay, uint, 0400); -module_param(programm_delay, uint, 0400); -module_param(erase_delay, uint, 0400); -module_param(output_cycle, uint, 0400); -module_param(input_cycle, uint, 0400); -module_param(bus_width, uint, 0400); -module_param(do_delays, uint, 0400); -module_param(log, uint, 0400); -module_param(dbg, uint, 0400); -module_param_array(parts, ulong, &parts_num, 0400); -module_param(badblocks, charp, 0400); -module_param(weakblocks, charp, 0400); -module_param(weakpages, charp, 0400); -module_param(bitflips, uint, 0400); -module_param(gravepages, charp, 0400); -module_param(overridesize, uint, 0400); -module_param(cache_file, charp, 0400); -module_param(bbt, uint, 0400); -module_param(bch, uint, 0400); - -MODULE_PARM_DESC(id_bytes, "The ID bytes returned by NAND Flash 'read ID' command"); -MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID) (obsolete)"); -MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID) (obsolete)"); -MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command (obsolete)"); -MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command (obsolete)"); -MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)"); -MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); -MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); -MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanoseconds)"); -MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanoseconds)"); -MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); -MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); -MODULE_PARM_DESC(log, "Perform logging if not zero"); -MODULE_PARM_DESC(dbg, "Output debug information if not zero"); -MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas"); -/* Page and erase block positions for the following parameters are independent of any partitions */ -MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas"); -MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]" - " separated by commas e.g. 113:2 means eb 113" - " can be erased only twice before failing"); -MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]" - " separated by commas e.g. 1401:2 means page 1401" - " can be written only twice before failing"); -MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)"); -MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" - " separated by commas e.g. 1401:2 means page 1401" - " can be read only twice before failing"); -MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " - "The size is specified in erase blocks and as the exponent of a power of two" - " e.g. 5 means a size of 32 erase blocks"); -MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); -MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area"); -MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " - "be correctable in 512-byte blocks"); - -/* The largest possible page size */ -#define NS_LARGEST_PAGE_SIZE 4096 - -/* The prefix for simulator output */ -#define NS_OUTPUT_PREFIX "[nandsim]" - -/* Simulator's output macros (logging, debugging, warning, error) */ -#define NS_LOG(args...) \ - do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0) -#define NS_DBG(args...) \ - do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) -#define NS_WARN(args...) \ - do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0) -#define NS_ERR(args...) \ - do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0) -#define NS_INFO(args...) \ - do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0) - -/* Busy-wait delay macros (microseconds, milliseconds) */ -#define NS_UDELAY(us) \ - do { if (do_delays) udelay(us); } while(0) -#define NS_MDELAY(us) \ - do { if (do_delays) mdelay(us); } while(0) - -/* Is the nandsim structure initialized ? */ -#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0) - -/* Good operation completion status */ -#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0))) - -/* Operation failed completion status */ -#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) - -/* Calculate the page offset in flash RAM image by (row, column) address */ -#define NS_RAW_OFFSET(ns) \ - (((ns)->regs.row * (ns)->geom.pgszoob) + (ns)->regs.column) - -/* Calculate the OOB offset in flash RAM image by (row, column) address */ -#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) - -/* After a command is input, the simulator goes to one of the following states */ -#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */ -#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */ -#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */ -#define STATE_CMD_PAGEPROG 0x00000004 /* start page program */ -#define STATE_CMD_READOOB 0x00000005 /* read OOB area */ -#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */ -#define STATE_CMD_STATUS 0x00000007 /* read status */ -#define STATE_CMD_SEQIN 0x00000009 /* sequential data input */ -#define STATE_CMD_READID 0x0000000A /* read ID */ -#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */ -#define STATE_CMD_RESET 0x0000000C /* reset */ -#define STATE_CMD_RNDOUT 0x0000000D /* random output command */ -#define STATE_CMD_RNDOUTSTART 0x0000000E /* random output start command */ -#define STATE_CMD_MASK 0x0000000F /* command states mask */ - -/* After an address is input, the simulator goes to one of these states */ -#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */ -#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */ -#define STATE_ADDR_COLUMN 0x00000030 /* column address was accepted */ -#define STATE_ADDR_ZERO 0x00000040 /* one byte zero address was accepted */ -#define STATE_ADDR_MASK 0x00000070 /* address states mask */ - -/* During data input/output the simulator is in these states */ -#define STATE_DATAIN 0x00000100 /* waiting for data input */ -#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */ - -#define STATE_DATAOUT 0x00001000 /* waiting for page data output */ -#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ -#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ -#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ - -/* Previous operation is done, ready to accept new requests */ -#define STATE_READY 0x00000000 - -/* This state is used to mark that the next state isn't known yet */ -#define STATE_UNKNOWN 0x10000000 - -/* Simulator's actions bit masks */ -#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */ -#define ACTION_PRGPAGE 0x00200000 /* program the internal buffer to flash */ -#define ACTION_SECERASE 0x00300000 /* erase sector */ -#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */ -#define ACTION_HALFOFF 0x00500000 /* add to address half of page */ -#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */ -#define ACTION_MASK 0x00700000 /* action mask */ - -#define NS_OPER_NUM 13 /* Number of operations supported by the simulator */ -#define NS_OPER_STATES 6 /* Maximum number of states in operation */ - -#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ -#define OPT_PAGE512 0x00000002 /* 512-byte page chips */ -#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ -#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ -#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ -#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ -#define OPT_SMALLPAGE (OPT_PAGE512) /* 512-byte page chips */ - -/* Remove action bits from state */ -#define NS_STATE(x) ((x) & ~ACTION_MASK) - -/* - * Maximum previous states which need to be saved. Currently saving is - * only needed for page program operation with preceded read command - * (which is only valid for 512-byte pages). - */ -#define NS_MAX_PREVSTATES 1 - -/* Maximum page cache pages needed to read or write a NAND page to the cache_file */ -#define NS_MAX_HELD_PAGES 16 - -/* - * A union to represent flash memory contents and flash buffer. - */ -union ns_mem { - u_char *byte; /* for byte access */ - uint16_t *word; /* for 16-bit word access */ -}; - -/* - * The structure which describes all the internal simulator data. - */ -struct nandsim { - struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS]; - unsigned int nbparts; - - uint busw; /* flash chip bus width (8 or 16) */ - u_char ids[8]; /* chip's ID bytes */ - uint32_t options; /* chip's characteristic bits */ - uint32_t state; /* current chip state */ - uint32_t nxstate; /* next expected state */ - - uint32_t *op; /* current operation, NULL operations isn't known yet */ - uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */ - uint16_t npstates; /* number of previous states saved */ - uint16_t stateidx; /* current state index */ - - /* The simulated NAND flash pages array */ - union ns_mem *pages; - - /* Slab allocator for nand pages */ - struct kmem_cache *nand_pages_slab; - - /* Internal buffer of page + OOB size bytes */ - union ns_mem buf; - - /* NAND flash "geometry" */ - struct { - uint64_t totsz; /* total flash size, bytes */ - uint32_t secsz; /* flash sector (erase block) size, bytes */ - uint pgsz; /* NAND flash page size, bytes */ - uint oobsz; /* page OOB area size, bytes */ - uint64_t totszoob; /* total flash size including OOB, bytes */ - uint pgszoob; /* page size including OOB , bytes*/ - uint secszoob; /* sector size including OOB, bytes */ - uint pgnum; /* total number of pages */ - uint pgsec; /* number of pages per sector */ - uint secshift; /* bits number in sector size */ - uint pgshift; /* bits number in page size */ - uint pgaddrbytes; /* bytes per page address */ - uint secaddrbytes; /* bytes per sector address */ - uint idbytes; /* the number ID bytes that this chip outputs */ - } geom; - - /* NAND flash internal registers */ - struct { - unsigned command; /* the command register */ - u_char status; /* the status register */ - uint row; /* the page number */ - uint column; /* the offset within page */ - uint count; /* internal counter */ - uint num; /* number of bytes which must be processed */ - uint off; /* fixed page offset */ - } regs; - - /* NAND flash lines state */ - struct { - int ce; /* chip Enable */ - int cle; /* command Latch Enable */ - int ale; /* address Latch Enable */ - int wp; /* write Protect */ - } lines; - - /* Fields needed when using a cache file */ - struct file *cfile; /* Open file */ - unsigned long *pages_written; /* Which pages have been written */ - void *file_buf; - struct page *held_pages[NS_MAX_HELD_PAGES]; - int held_cnt; -}; - -/* - * Operations array. To perform any operation the simulator must pass - * through the correspondent states chain. - */ -static struct nandsim_operations { - uint32_t reqopts; /* options which are required to perform the operation */ - uint32_t states[NS_OPER_STATES]; /* operation's states */ -} ops[NS_OPER_NUM] = { - /* Read page + OOB from the beginning */ - {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY, - STATE_DATAOUT, STATE_READY}}, - /* Read page + OOB from the second half */ - {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY, - STATE_DATAOUT, STATE_READY}}, - /* Read OOB */ - {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY, - STATE_DATAOUT, STATE_READY}}, - /* Program page starting from the beginning */ - {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN, - STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, - /* Program page starting from the beginning */ - {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE, - STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, - /* Program page starting from the second half */ - {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE, - STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, - /* Program OOB */ - {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE, - STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, - /* Erase sector */ - {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}}, - /* Read status */ - {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, - /* Read ID */ - {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}}, - /* Large page devices read page */ - {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY, - STATE_DATAOUT, STATE_READY}}, - /* Large page devices random page read */ - {OPT_LARGEPAGE, {STATE_CMD_RNDOUT, STATE_ADDR_COLUMN, STATE_CMD_RNDOUTSTART | ACTION_CPY, - STATE_DATAOUT, STATE_READY}}, -}; - -struct weak_block { - struct list_head list; - unsigned int erase_block_no; - unsigned int max_erases; - unsigned int erases_done; -}; - -static LIST_HEAD(weak_blocks); - -struct weak_page { - struct list_head list; - unsigned int page_no; - unsigned int max_writes; - unsigned int writes_done; -}; - -static LIST_HEAD(weak_pages); - -struct grave_page { - struct list_head list; - unsigned int page_no; - unsigned int max_reads; - unsigned int reads_done; -}; - -static LIST_HEAD(grave_pages); - -static unsigned long *erase_block_wear = NULL; -static unsigned int wear_eb_count = 0; -static unsigned long total_wear = 0; - -/* MTD structure for NAND controller */ -static struct mtd_info *nsmtd; - -static int nandsim_debugfs_show(struct seq_file *m, void *private) -{ - unsigned long wmin = -1, wmax = 0, avg; - unsigned long deciles[10], decile_max[10], tot = 0; - unsigned int i; - - /* Calc wear stats */ - for (i = 0; i < wear_eb_count; ++i) { - unsigned long wear = erase_block_wear[i]; - if (wear < wmin) - wmin = wear; - if (wear > wmax) - wmax = wear; - tot += wear; - } - - for (i = 0; i < 9; ++i) { - deciles[i] = 0; - decile_max[i] = (wmax * (i + 1) + 5) / 10; - } - deciles[9] = 0; - decile_max[9] = wmax; - for (i = 0; i < wear_eb_count; ++i) { - int d; - unsigned long wear = erase_block_wear[i]; - for (d = 0; d < 10; ++d) - if (wear <= decile_max[d]) { - deciles[d] += 1; - break; - } - } - avg = tot / wear_eb_count; - - /* Output wear report */ - seq_printf(m, "Total numbers of erases: %lu\n", tot); - seq_printf(m, "Number of erase blocks: %u\n", wear_eb_count); - seq_printf(m, "Average number of erases: %lu\n", avg); - seq_printf(m, "Maximum number of erases: %lu\n", wmax); - seq_printf(m, "Minimum number of erases: %lu\n", wmin); - for (i = 0; i < 10; ++i) { - unsigned long from = (i ? decile_max[i - 1] + 1 : 0); - if (from > decile_max[i]) - continue; - seq_printf(m, "Number of ebs with erase counts from %lu to %lu : %lu\n", - from, - decile_max[i], - deciles[i]); - } - - return 0; -} - -static int nandsim_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, nandsim_debugfs_show, inode->i_private); -} - -static const struct file_operations dfs_fops = { - .open = nandsim_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * nandsim_debugfs_create - initialize debugfs - * @dev: nandsim device description object - * - * This function creates all debugfs files for UBI device @ubi. Returns zero in - * case of success and a negative error code in case of failure. - */ -static int nandsim_debugfs_create(struct nandsim *dev) -{ - struct dentry *root = nsmtd->dbg.dfs_dir; - struct dentry *dent; - - /* - * Just skip debugfs initialization when the debugfs directory is - * missing. - */ - if (IS_ERR_OR_NULL(root)) { - if (IS_ENABLED(CONFIG_DEBUG_FS) && - !IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) - NS_WARN("CONFIG_MTD_PARTITIONED_MASTER must be enabled to expose debugfs stuff\n"); - return 0; - } - - dent = debugfs_create_file("nandsim_wear_report", S_IRUSR, - root, dev, &dfs_fops); - if (IS_ERR_OR_NULL(dent)) { - NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n"); - return -1; - } - - return 0; -} - -/* - * Allocate array of page pointers, create slab allocation for an array - * and initialize the array by NULL pointers. - * - * RETURNS: 0 if success, -ENOMEM if memory alloc fails. - */ -static int __init alloc_device(struct nandsim *ns) -{ - struct file *cfile; - int i, err; - - if (cache_file) { - cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600); - if (IS_ERR(cfile)) - return PTR_ERR(cfile); - if (!(cfile->f_mode & FMODE_CAN_READ)) { - NS_ERR("alloc_device: cache file not readable\n"); - err = -EINVAL; - goto err_close; - } - if (!(cfile->f_mode & FMODE_CAN_WRITE)) { - NS_ERR("alloc_device: cache file not writeable\n"); - err = -EINVAL; - goto err_close; - } - ns->pages_written = vzalloc(BITS_TO_LONGS(ns->geom.pgnum) * - sizeof(unsigned long)); - if (!ns->pages_written) { - NS_ERR("alloc_device: unable to allocate pages written array\n"); - err = -ENOMEM; - goto err_close; - } - ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL); - if (!ns->file_buf) { - NS_ERR("alloc_device: unable to allocate file buf\n"); - err = -ENOMEM; - goto err_free; - } - ns->cfile = cfile; - return 0; - } - - ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); - if (!ns->pages) { - NS_ERR("alloc_device: unable to allocate page array\n"); - return -ENOMEM; - } - for (i = 0; i < ns->geom.pgnum; i++) { - ns->pages[i].byte = NULL; - } - ns->nand_pages_slab = kmem_cache_create("nandsim", - ns->geom.pgszoob, 0, 0, NULL); - if (!ns->nand_pages_slab) { - NS_ERR("cache_create: unable to create kmem_cache\n"); - return -ENOMEM; - } - - return 0; - -err_free: - vfree(ns->pages_written); -err_close: - filp_close(cfile, NULL); - return err; -} - -/* - * Free any allocated pages, and free the array of page pointers. - */ -static void free_device(struct nandsim *ns) -{ - int i; - - if (ns->cfile) { - kfree(ns->file_buf); - vfree(ns->pages_written); - filp_close(ns->cfile, NULL); - return; - } - - if (ns->pages) { - for (i = 0; i < ns->geom.pgnum; i++) { - if (ns->pages[i].byte) - kmem_cache_free(ns->nand_pages_slab, - ns->pages[i].byte); - } - kmem_cache_destroy(ns->nand_pages_slab); - vfree(ns->pages); - } -} - -static char __init *get_partition_name(int i) -{ - return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i); -} - -/* - * Initialize the nandsim structure. - * - * RETURNS: 0 if success, -ERRNO if failure. - */ -static int __init init_nandsim(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nandsim *ns = nand_get_controller_data(chip); - int i, ret = 0; - uint64_t remains; - uint64_t next_offset; - - if (NS_IS_INITIALIZED(ns)) { - NS_ERR("init_nandsim: nandsim is already initialized\n"); - return -EIO; - } - - /* Force mtd to not do delays */ - chip->chip_delay = 0; - - /* Initialize the NAND flash parameters */ - ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8; - ns->geom.totsz = mtd->size; - ns->geom.pgsz = mtd->writesize; - ns->geom.oobsz = mtd->oobsize; - ns->geom.secsz = mtd->erasesize; - ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; - ns->geom.pgnum = div_u64(ns->geom.totsz, ns->geom.pgsz); - ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; - ns->geom.secshift = ffs(ns->geom.secsz) - 1; - ns->geom.pgshift = chip->page_shift; - ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; - ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; - ns->options = 0; - - if (ns->geom.pgsz == 512) { - ns->options |= OPT_PAGE512; - if (ns->busw == 8) - ns->options |= OPT_PAGE512_8BIT; - } else if (ns->geom.pgsz == 2048) { - ns->options |= OPT_PAGE2048; - } else if (ns->geom.pgsz == 4096) { - ns->options |= OPT_PAGE4096; - } else { - NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); - return -EIO; - } - - if (ns->options & OPT_SMALLPAGE) { - if (ns->geom.totsz <= (32 << 20)) { - ns->geom.pgaddrbytes = 3; - ns->geom.secaddrbytes = 2; - } else { - ns->geom.pgaddrbytes = 4; - ns->geom.secaddrbytes = 3; - } - } else { - if (ns->geom.totsz <= (128 << 20)) { - ns->geom.pgaddrbytes = 4; - ns->geom.secaddrbytes = 2; - } else { - ns->geom.pgaddrbytes = 5; - ns->geom.secaddrbytes = 3; - } - } - - /* Fill the partition_info structure */ - if (parts_num > ARRAY_SIZE(ns->partitions)) { - NS_ERR("too many partitions.\n"); - return -EINVAL; - } - remains = ns->geom.totsz; - next_offset = 0; - for (i = 0; i < parts_num; ++i) { - uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz; - - if (!part_sz || part_sz > remains) { - NS_ERR("bad partition size.\n"); - return -EINVAL; - } - ns->partitions[i].name = get_partition_name(i); - if (!ns->partitions[i].name) { - NS_ERR("unable to allocate memory.\n"); - return -ENOMEM; - } - ns->partitions[i].offset = next_offset; - ns->partitions[i].size = part_sz; - next_offset += ns->partitions[i].size; - remains -= ns->partitions[i].size; - } - ns->nbparts = parts_num; - if (remains) { - if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) { - NS_ERR("too many partitions.\n"); - return -EINVAL; - } - ns->partitions[i].name = get_partition_name(i); - if (!ns->partitions[i].name) { - NS_ERR("unable to allocate memory.\n"); - return -ENOMEM; - } - ns->partitions[i].offset = next_offset; - ns->partitions[i].size = remains; - ns->nbparts += 1; - } - - if (ns->busw == 16) - NS_WARN("16-bit flashes support wasn't tested\n"); - - printk("flash size: %llu MiB\n", - (unsigned long long)ns->geom.totsz >> 20); - printk("page size: %u bytes\n", ns->geom.pgsz); - printk("OOB area size: %u bytes\n", ns->geom.oobsz); - printk("sector size: %u KiB\n", ns->geom.secsz >> 10); - printk("pages number: %u\n", ns->geom.pgnum); - printk("pages per sector: %u\n", ns->geom.pgsec); - printk("bus width: %u\n", ns->busw); - printk("bits in sector size: %u\n", ns->geom.secshift); - printk("bits in page size: %u\n", ns->geom.pgshift); - printk("bits in OOB size: %u\n", ffs(ns->geom.oobsz) - 1); - printk("flash size with OOB: %llu KiB\n", - (unsigned long long)ns->geom.totszoob >> 10); - printk("page address bytes: %u\n", ns->geom.pgaddrbytes); - printk("sector address bytes: %u\n", ns->geom.secaddrbytes); - printk("options: %#x\n", ns->options); - - if ((ret = alloc_device(ns)) != 0) - return ret; - - /* Allocate / initialize the internal buffer */ - ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); - if (!ns->buf.byte) { - NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", - ns->geom.pgszoob); - return -ENOMEM; - } - memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); - - return 0; -} - -/* - * Free the nandsim structure. - */ -static void free_nandsim(struct nandsim *ns) -{ - kfree(ns->buf.byte); - free_device(ns); - - return; -} - -static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd) -{ - char *w; - int zero_ok; - unsigned int erase_block_no; - loff_t offset; - - if (!badblocks) - return 0; - w = badblocks; - do { - zero_ok = (*w == '0' ? 1 : 0); - erase_block_no = simple_strtoul(w, &w, 0); - if (!zero_ok && !erase_block_no) { - NS_ERR("invalid badblocks.\n"); - return -EINVAL; - } - offset = (loff_t)erase_block_no * ns->geom.secsz; - if (mtd_block_markbad(mtd, offset)) { - NS_ERR("invalid badblocks.\n"); - return -EINVAL; - } - if (*w == ',') - w += 1; - } while (*w); - return 0; -} - -static int parse_weakblocks(void) -{ - char *w; - int zero_ok; - unsigned int erase_block_no; - unsigned int max_erases; - struct weak_block *wb; - - if (!weakblocks) - return 0; - w = weakblocks; - do { - zero_ok = (*w == '0' ? 1 : 0); - erase_block_no = simple_strtoul(w, &w, 0); - if (!zero_ok && !erase_block_no) { - NS_ERR("invalid weakblocks.\n"); - return -EINVAL; - } - max_erases = 3; - if (*w == ':') { - w += 1; - max_erases = simple_strtoul(w, &w, 0); - } - if (*w == ',') - w += 1; - wb = kzalloc(sizeof(*wb), GFP_KERNEL); - if (!wb) { - NS_ERR("unable to allocate memory.\n"); - return -ENOMEM; - } - wb->erase_block_no = erase_block_no; - wb->max_erases = max_erases; - list_add(&wb->list, &weak_blocks); - } while (*w); - return 0; -} - -static int erase_error(unsigned int erase_block_no) -{ - struct weak_block *wb; - - list_for_each_entry(wb, &weak_blocks, list) - if (wb->erase_block_no == erase_block_no) { - if (wb->erases_done >= wb->max_erases) - return 1; - wb->erases_done += 1; - return 0; - } - return 0; -} - -static int parse_weakpages(void) -{ - char *w; - int zero_ok; - unsigned int page_no; - unsigned int max_writes; - struct weak_page *wp; - - if (!weakpages) - return 0; - w = weakpages; - do { - zero_ok = (*w == '0' ? 1 : 0); - page_no = simple_strtoul(w, &w, 0); - if (!zero_ok && !page_no) { - NS_ERR("invalid weakpages.\n"); - return -EINVAL; - } - max_writes = 3; - if (*w == ':') { - w += 1; - max_writes = simple_strtoul(w, &w, 0); - } - if (*w == ',') - w += 1; - wp = kzalloc(sizeof(*wp), GFP_KERNEL); - if (!wp) { - NS_ERR("unable to allocate memory.\n"); - return -ENOMEM; - } - wp->page_no = page_no; - wp->max_writes = max_writes; - list_add(&wp->list, &weak_pages); - } while (*w); - return 0; -} - -static int write_error(unsigned int page_no) -{ - struct weak_page *wp; - - list_for_each_entry(wp, &weak_pages, list) - if (wp->page_no == page_no) { - if (wp->writes_done >= wp->max_writes) - return 1; - wp->writes_done += 1; - return 0; - } - return 0; -} - -static int parse_gravepages(void) -{ - char *g; - int zero_ok; - unsigned int page_no; - unsigned int max_reads; - struct grave_page *gp; - - if (!gravepages) - return 0; - g = gravepages; - do { - zero_ok = (*g == '0' ? 1 : 0); - page_no = simple_strtoul(g, &g, 0); - if (!zero_ok && !page_no) { - NS_ERR("invalid gravepagess.\n"); - return -EINVAL; - } - max_reads = 3; - if (*g == ':') { - g += 1; - max_reads = simple_strtoul(g, &g, 0); - } - if (*g == ',') - g += 1; - gp = kzalloc(sizeof(*gp), GFP_KERNEL); - if (!gp) { - NS_ERR("unable to allocate memory.\n"); - return -ENOMEM; - } - gp->page_no = page_no; - gp->max_reads = max_reads; - list_add(&gp->list, &grave_pages); - } while (*g); - return 0; -} - -static int read_error(unsigned int page_no) -{ - struct grave_page *gp; - - list_for_each_entry(gp, &grave_pages, list) - if (gp->page_no == page_no) { - if (gp->reads_done >= gp->max_reads) - return 1; - gp->reads_done += 1; - return 0; - } - return 0; -} - -static void free_lists(void) -{ - struct list_head *pos, *n; - list_for_each_safe(pos, n, &weak_blocks) { - list_del(pos); - kfree(list_entry(pos, struct weak_block, list)); - } - list_for_each_safe(pos, n, &weak_pages) { - list_del(pos); - kfree(list_entry(pos, struct weak_page, list)); - } - list_for_each_safe(pos, n, &grave_pages) { - list_del(pos); - kfree(list_entry(pos, struct grave_page, list)); - } - kfree(erase_block_wear); -} - -static int setup_wear_reporting(struct mtd_info *mtd) -{ - size_t mem; - - wear_eb_count = div_u64(mtd->size, mtd->erasesize); - mem = wear_eb_count * sizeof(unsigned long); - if (mem / sizeof(unsigned long) != wear_eb_count) { - NS_ERR("Too many erase blocks for wear reporting\n"); - return -ENOMEM; - } - erase_block_wear = kzalloc(mem, GFP_KERNEL); - if (!erase_block_wear) { - NS_ERR("Too many erase blocks for wear reporting\n"); - return -ENOMEM; - } - return 0; -} - -static void update_wear(unsigned int erase_block_no) -{ - if (!erase_block_wear) - return; - total_wear += 1; - /* - * TODO: Notify this through a debugfs entry, - * instead of showing an error message. - */ - if (total_wear == 0) - NS_ERR("Erase counter total overflow\n"); - erase_block_wear[erase_block_no] += 1; - if (erase_block_wear[erase_block_no] == 0) - NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); -} - -/* - * Returns the string representation of 'state' state. - */ -static char *get_state_name(uint32_t state) -{ - switch (NS_STATE(state)) { - case STATE_CMD_READ0: - return "STATE_CMD_READ0"; - case STATE_CMD_READ1: - return "STATE_CMD_READ1"; - case STATE_CMD_PAGEPROG: - return "STATE_CMD_PAGEPROG"; - case STATE_CMD_READOOB: - return "STATE_CMD_READOOB"; - case STATE_CMD_READSTART: - return "STATE_CMD_READSTART"; - case STATE_CMD_ERASE1: - return "STATE_CMD_ERASE1"; - case STATE_CMD_STATUS: - return "STATE_CMD_STATUS"; - case STATE_CMD_SEQIN: - return "STATE_CMD_SEQIN"; - case STATE_CMD_READID: - return "STATE_CMD_READID"; - case STATE_CMD_ERASE2: - return "STATE_CMD_ERASE2"; - case STATE_CMD_RESET: - return "STATE_CMD_RESET"; - case STATE_CMD_RNDOUT: - return "STATE_CMD_RNDOUT"; - case STATE_CMD_RNDOUTSTART: - return "STATE_CMD_RNDOUTSTART"; - case STATE_ADDR_PAGE: - return "STATE_ADDR_PAGE"; - case STATE_ADDR_SEC: - return "STATE_ADDR_SEC"; - case STATE_ADDR_ZERO: - return "STATE_ADDR_ZERO"; - case STATE_ADDR_COLUMN: - return "STATE_ADDR_COLUMN"; - case STATE_DATAIN: - return "STATE_DATAIN"; - case STATE_DATAOUT: - return "STATE_DATAOUT"; - case STATE_DATAOUT_ID: - return "STATE_DATAOUT_ID"; - case STATE_DATAOUT_STATUS: - return "STATE_DATAOUT_STATUS"; - case STATE_READY: - return "STATE_READY"; - case STATE_UNKNOWN: - return "STATE_UNKNOWN"; - } - - NS_ERR("get_state_name: unknown state, BUG\n"); - return NULL; -} - -/* - * Check if command is valid. - * - * RETURNS: 1 if wrong command, 0 if right. - */ -static int check_command(int cmd) -{ - switch (cmd) { - - case NAND_CMD_READ0: - case NAND_CMD_READ1: - case NAND_CMD_READSTART: - case NAND_CMD_PAGEPROG: - case NAND_CMD_READOOB: - case NAND_CMD_ERASE1: - case NAND_CMD_STATUS: - case NAND_CMD_SEQIN: - case NAND_CMD_READID: - case NAND_CMD_ERASE2: - case NAND_CMD_RESET: - case NAND_CMD_RNDOUT: - case NAND_CMD_RNDOUTSTART: - return 0; - - default: - return 1; - } -} - -/* - * Returns state after command is accepted by command number. - */ -static uint32_t get_state_by_command(unsigned command) -{ - switch (command) { - case NAND_CMD_READ0: - return STATE_CMD_READ0; - case NAND_CMD_READ1: - return STATE_CMD_READ1; - case NAND_CMD_PAGEPROG: - return STATE_CMD_PAGEPROG; - case NAND_CMD_READSTART: - return STATE_CMD_READSTART; - case NAND_CMD_READOOB: - return STATE_CMD_READOOB; - case NAND_CMD_ERASE1: - return STATE_CMD_ERASE1; - case NAND_CMD_STATUS: - return STATE_CMD_STATUS; - case NAND_CMD_SEQIN: - return STATE_CMD_SEQIN; - case NAND_CMD_READID: - return STATE_CMD_READID; - case NAND_CMD_ERASE2: - return STATE_CMD_ERASE2; - case NAND_CMD_RESET: - return STATE_CMD_RESET; - case NAND_CMD_RNDOUT: - return STATE_CMD_RNDOUT; - case NAND_CMD_RNDOUTSTART: - return STATE_CMD_RNDOUTSTART; - } - - NS_ERR("get_state_by_command: unknown command, BUG\n"); - return 0; -} - -/* - * Move an address byte to the correspondent internal register. - */ -static inline void accept_addr_byte(struct nandsim *ns, u_char bt) -{ - uint byte = (uint)bt; - - if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) - ns->regs.column |= (byte << 8 * ns->regs.count); - else { - ns->regs.row |= (byte << 8 * (ns->regs.count - - ns->geom.pgaddrbytes + - ns->geom.secaddrbytes)); - } - - return; -} - -/* - * Switch to STATE_READY state. - */ -static inline void switch_to_ready_state(struct nandsim *ns, u_char status) -{ - NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY)); - - ns->state = STATE_READY; - ns->nxstate = STATE_UNKNOWN; - ns->op = NULL; - ns->npstates = 0; - ns->stateidx = 0; - ns->regs.num = 0; - ns->regs.count = 0; - ns->regs.off = 0; - ns->regs.row = 0; - ns->regs.column = 0; - ns->regs.status = status; -} - -/* - * If the operation isn't known yet, try to find it in the global array - * of supported operations. - * - * Operation can be unknown because of the following. - * 1. New command was accepted and this is the first call to find the - * correspondent states chain. In this case ns->npstates = 0; - * 2. There are several operations which begin with the same command(s) - * (for example program from the second half and read from the - * second half operations both begin with the READ1 command). In this - * case the ns->pstates[] array contains previous states. - * - * Thus, the function tries to find operation containing the following - * states (if the 'flag' parameter is 0): - * ns->pstates[0], ... ns->pstates[ns->npstates], ns->state - * - * If (one and only one) matching operation is found, it is accepted ( - * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is - * zeroed). - * - * If there are several matches, the current state is pushed to the - * ns->pstates. - * - * The operation can be unknown only while commands are input to the chip. - * As soon as address command is accepted, the operation must be known. - * In such situation the function is called with 'flag' != 0, and the - * operation is searched using the following pattern: - * ns->pstates[0], ... ns->pstates[ns->npstates],
- * - * It is supposed that this pattern must either match one operation or - * none. There can't be ambiguity in that case. - * - * If no matches found, the function does the following: - * 1. if there are saved states present, try to ignore them and search - * again only using the last command. If nothing was found, switch - * to the STATE_READY state. - * 2. if there are no saved states, switch to the STATE_READY state. - * - * RETURNS: -2 - no matched operations found. - * -1 - several matches. - * 0 - operation is found. - */ -static int find_operation(struct nandsim *ns, uint32_t flag) -{ - int opsfound = 0; - int i, j, idx = 0; - - for (i = 0; i < NS_OPER_NUM; i++) { - - int found = 1; - - if (!(ns->options & ops[i].reqopts)) - /* Ignore operations we can't perform */ - continue; - - if (flag) { - if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK)) - continue; - } else { - if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates])) - continue; - } - - for (j = 0; j < ns->npstates; j++) - if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j]) - && (ns->options & ops[idx].reqopts)) { - found = 0; - break; - } - - if (found) { - idx = i; - opsfound += 1; - } - } - - if (opsfound == 1) { - /* Exact match */ - ns->op = &ops[idx].states[0]; - if (flag) { - /* - * In this case the find_operation function was - * called when address has just began input. But it isn't - * yet fully input and the current state must - * not be one of STATE_ADDR_*, but the STATE_ADDR_* - * state must be the next state (ns->nxstate). - */ - ns->stateidx = ns->npstates - 1; - } else { - ns->stateidx = ns->npstates; - } - ns->npstates = 0; - ns->state = ns->op[ns->stateidx]; - ns->nxstate = ns->op[ns->stateidx + 1]; - NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n", - idx, get_state_name(ns->state), get_state_name(ns->nxstate)); - return 0; - } - - if (opsfound == 0) { - /* Nothing was found. Try to ignore previous commands (if any) and search again */ - if (ns->npstates != 0) { - NS_DBG("find_operation: no operation found, try again with state %s\n", - get_state_name(ns->state)); - ns->npstates = 0; - return find_operation(ns, 0); - - } - NS_DBG("find_operation: no operations found\n"); - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return -2; - } - - if (flag) { - /* This shouldn't happen */ - NS_DBG("find_operation: BUG, operation must be known if address is input\n"); - return -2; - } - - NS_DBG("find_operation: there is still ambiguity\n"); - - ns->pstates[ns->npstates++] = ns->state; - - return -1; -} - -static void put_pages(struct nandsim *ns) -{ - int i; - - for (i = 0; i < ns->held_cnt; i++) - put_page(ns->held_pages[i]); -} - -/* Get page cache pages in advance to provide NOFS memory allocation */ -static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos) -{ - pgoff_t index, start_index, end_index; - struct page *page; - struct address_space *mapping = file->f_mapping; - - start_index = pos >> PAGE_SHIFT; - end_index = (pos + count - 1) >> PAGE_SHIFT; - if (end_index - start_index + 1 > NS_MAX_HELD_PAGES) - return -EINVAL; - ns->held_cnt = 0; - for (index = start_index; index <= end_index; index++) { - page = find_get_page(mapping, index); - if (page == NULL) { - page = find_or_create_page(mapping, index, GFP_NOFS); - if (page == NULL) { - write_inode_now(mapping->host, 1); - page = find_or_create_page(mapping, index, GFP_NOFS); - } - if (page == NULL) { - put_pages(ns); - return -ENOMEM; - } - unlock_page(page); - } - ns->held_pages[ns->held_cnt++] = page; - } - return 0; -} - -static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) -{ - ssize_t tx; - int err; - unsigned int noreclaim_flag; - - err = get_pages(ns, file, count, pos); - if (err) - return err; - noreclaim_flag = memalloc_noreclaim_save(); - tx = kernel_read(file, buf, count, &pos); - memalloc_noreclaim_restore(noreclaim_flag); - put_pages(ns); - return tx; -} - -static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) -{ - ssize_t tx; - int err; - unsigned int noreclaim_flag; - - err = get_pages(ns, file, count, pos); - if (err) - return err; - noreclaim_flag = memalloc_noreclaim_save(); - tx = kernel_write(file, buf, count, &pos); - memalloc_noreclaim_restore(noreclaim_flag); - put_pages(ns); - return tx; -} - -/* - * Returns a pointer to the current page. - */ -static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns) -{ - return &(ns->pages[ns->regs.row]); -} - -/* - * Retuns a pointer to the current byte, within the current page. - */ -static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) -{ - return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; -} - -static int do_read_error(struct nandsim *ns, int num) -{ - unsigned int page_no = ns->regs.row; - - if (read_error(page_no)) { - prandom_bytes(ns->buf.byte, num); - NS_WARN("simulating read error in page %u\n", page_no); - return 1; - } - return 0; -} - -static void do_bit_flips(struct nandsim *ns, int num) -{ - if (bitflips && prandom_u32() < (1 << 22)) { - int flips = 1; - if (bitflips > 1) - flips = (prandom_u32() % (int) bitflips) + 1; - while (flips--) { - int pos = prandom_u32() % (num * 8); - ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); - NS_WARN("read_page: flipping bit %d in page %d " - "reading from %d ecc: corrected=%u failed=%u\n", - pos, ns->regs.row, ns->regs.column + ns->regs.off, - nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); - } - } -} - -/* - * Fill the NAND buffer with data read from the specified page. - */ -static void read_page(struct nandsim *ns, int num) -{ - union ns_mem *mypage; - - if (ns->cfile) { - if (!test_bit(ns->regs.row, ns->pages_written)) { - NS_DBG("read_page: page %d not written\n", ns->regs.row); - memset(ns->buf.byte, 0xFF, num); - } else { - loff_t pos; - ssize_t tx; - - NS_DBG("read_page: page %d written, reading from %d\n", - ns->regs.row, ns->regs.column + ns->regs.off); - if (do_read_error(ns, num)) - return; - pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; - tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos); - if (tx != num) { - NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); - return; - } - do_bit_flips(ns, num); - } - return; - } - - mypage = NS_GET_PAGE(ns); - if (mypage->byte == NULL) { - NS_DBG("read_page: page %d not allocated\n", ns->regs.row); - memset(ns->buf.byte, 0xFF, num); - } else { - NS_DBG("read_page: page %d allocated, reading from %d\n", - ns->regs.row, ns->regs.column + ns->regs.off); - if (do_read_error(ns, num)) - return; - memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); - do_bit_flips(ns, num); - } -} - -/* - * Erase all pages in the specified sector. - */ -static void erase_sector(struct nandsim *ns) -{ - union ns_mem *mypage; - int i; - - if (ns->cfile) { - for (i = 0; i < ns->geom.pgsec; i++) - if (__test_and_clear_bit(ns->regs.row + i, - ns->pages_written)) { - NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i); - } - return; - } - - mypage = NS_GET_PAGE(ns); - for (i = 0; i < ns->geom.pgsec; i++) { - if (mypage->byte != NULL) { - NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); - kmem_cache_free(ns->nand_pages_slab, mypage->byte); - mypage->byte = NULL; - } - mypage++; - } -} - -/* - * Program the specified page with the contents from the NAND buffer. - */ -static int prog_page(struct nandsim *ns, int num) -{ - int i; - union ns_mem *mypage; - u_char *pg_off; - - if (ns->cfile) { - loff_t off; - ssize_t tx; - int all; - - NS_DBG("prog_page: writing page %d\n", ns->regs.row); - pg_off = ns->file_buf + ns->regs.column + ns->regs.off; - off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; - if (!test_bit(ns->regs.row, ns->pages_written)) { - all = 1; - memset(ns->file_buf, 0xff, ns->geom.pgszoob); - } else { - all = 0; - tx = read_file(ns, ns->cfile, pg_off, num, off); - if (tx != num) { - NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); - return -1; - } - } - for (i = 0; i < num; i++) - pg_off[i] &= ns->buf.byte[i]; - if (all) { - loff_t pos = (loff_t)ns->regs.row * ns->geom.pgszoob; - tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, pos); - if (tx != ns->geom.pgszoob) { - NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); - return -1; - } - __set_bit(ns->regs.row, ns->pages_written); - } else { - tx = write_file(ns, ns->cfile, pg_off, num, off); - if (tx != num) { - NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); - return -1; - } - } - return 0; - } - - mypage = NS_GET_PAGE(ns); - if (mypage->byte == NULL) { - NS_DBG("prog_page: allocating page %d\n", ns->regs.row); - /* - * We allocate memory with GFP_NOFS because a flash FS may - * utilize this. If it is holding an FS lock, then gets here, - * then kernel memory alloc runs writeback which goes to the FS - * again and deadlocks. This was seen in practice. - */ - mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS); - if (mypage->byte == NULL) { - NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); - return -1; - } - memset(mypage->byte, 0xFF, ns->geom.pgszoob); - } - - pg_off = NS_PAGE_BYTE_OFF(ns); - for (i = 0; i < num; i++) - pg_off[i] &= ns->buf.byte[i]; - - return 0; -} - -/* - * If state has any action bit, perform this action. - * - * RETURNS: 0 if success, -1 if error. - */ -static int do_state_action(struct nandsim *ns, uint32_t action) -{ - int num; - int busdiv = ns->busw == 8 ? 1 : 2; - unsigned int erase_block_no, page_no; - - action &= ACTION_MASK; - - /* Check that page address input is correct */ - if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) { - NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row); - return -1; - } - - switch (action) { - - case ACTION_CPY: - /* - * Copy page data to the internal buffer. - */ - - /* Column shouldn't be very large */ - if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) { - NS_ERR("do_state_action: column number is too large\n"); - break; - } - num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; - read_page(ns, num); - - NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", - num, NS_RAW_OFFSET(ns) + ns->regs.off); - - if (ns->regs.off == 0) - NS_LOG("read page %d\n", ns->regs.row); - else if (ns->regs.off < ns->geom.pgsz) - NS_LOG("read page %d (second half)\n", ns->regs.row); - else - NS_LOG("read OOB of page %d\n", ns->regs.row); - - NS_UDELAY(access_delay); - NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv); - - break; - - case ACTION_SECERASE: - /* - * Erase sector. - */ - - if (ns->lines.wp) { - NS_ERR("do_state_action: device is write-protected, ignore sector erase\n"); - return -1; - } - - if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec - || (ns->regs.row & ~(ns->geom.secsz - 1))) { - NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row); - return -1; - } - - ns->regs.row = (ns->regs.row << - 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; - ns->regs.column = 0; - - erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift); - - NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", - ns->regs.row, NS_RAW_OFFSET(ns)); - NS_LOG("erase sector %u\n", erase_block_no); - - erase_sector(ns); - - NS_MDELAY(erase_delay); - - if (erase_block_wear) - update_wear(erase_block_no); - - if (erase_error(erase_block_no)) { - NS_WARN("simulating erase failure in erase block %u\n", erase_block_no); - return -1; - } - - break; - - case ACTION_PRGPAGE: - /* - * Program page - move internal buffer data to the page. - */ - - if (ns->lines.wp) { - NS_WARN("do_state_action: device is write-protected, programm\n"); - return -1; - } - - num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; - if (num != ns->regs.count) { - NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n", - ns->regs.count, num); - return -1; - } - - if (prog_page(ns, num) == -1) - return -1; - - page_no = ns->regs.row; - - NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", - num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); - NS_LOG("programm page %d\n", ns->regs.row); - - NS_UDELAY(programm_delay); - NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); - - if (write_error(page_no)) { - NS_WARN("simulating write failure in page %u\n", page_no); - return -1; - } - - break; - - case ACTION_ZEROOFF: - NS_DBG("do_state_action: set internal offset to 0\n"); - ns->regs.off = 0; - break; - - case ACTION_HALFOFF: - if (!(ns->options & OPT_PAGE512_8BIT)) { - NS_ERR("do_state_action: BUG! can't skip half of page for non-512" - "byte page size 8x chips\n"); - return -1; - } - NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2); - ns->regs.off = ns->geom.pgsz/2; - break; - - case ACTION_OOBOFF: - NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz); - ns->regs.off = ns->geom.pgsz; - break; - - default: - NS_DBG("do_state_action: BUG! unknown action\n"); - } - - return 0; -} - -/* - * Switch simulator's state. - */ -static void switch_state(struct nandsim *ns) -{ - if (ns->op) { - /* - * The current operation have already been identified. - * Just follow the states chain. - */ - - ns->stateidx += 1; - ns->state = ns->nxstate; - ns->nxstate = ns->op[ns->stateidx + 1]; - - NS_DBG("switch_state: operation is known, switch to the next state, " - "state: %s, nxstate: %s\n", - get_state_name(ns->state), get_state_name(ns->nxstate)); - - /* See, whether we need to do some action */ - if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - - } else { - /* - * We don't yet know which operation we perform. - * Try to identify it. - */ - - /* - * The only event causing the switch_state function to - * be called with yet unknown operation is new command. - */ - ns->state = get_state_by_command(ns->regs.command); - - NS_DBG("switch_state: operation is unknown, try to find it\n"); - - if (find_operation(ns, 0) != 0) - return; - - if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - } - - /* For 16x devices column means the page offset in words */ - if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) { - NS_DBG("switch_state: double the column number for 16x device\n"); - ns->regs.column <<= 1; - } - - if (NS_STATE(ns->nxstate) == STATE_READY) { - /* - * The current state is the last. Return to STATE_READY - */ - - u_char status = NS_STATUS_OK(ns); - - /* In case of data states, see if all bytes were input/output */ - if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) - && ns->regs.count != ns->regs.num) { - NS_WARN("switch_state: not all bytes were processed, %d left\n", - ns->regs.num - ns->regs.count); - status = NS_STATUS_FAILED(ns); - } - - NS_DBG("switch_state: operation complete, switch to STATE_READY state\n"); - - switch_to_ready_state(ns, status); - - return; - } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) { - /* - * If the next state is data input/output, switch to it now - */ - - ns->state = ns->nxstate; - ns->nxstate = ns->op[++ns->stateidx + 1]; - ns->regs.num = ns->regs.count = 0; - - NS_DBG("switch_state: the next state is data I/O, switch, " - "state: %s, nxstate: %s\n", - get_state_name(ns->state), get_state_name(ns->nxstate)); - - /* - * Set the internal register to the count of bytes which - * are expected to be input or output - */ - switch (NS_STATE(ns->state)) { - case STATE_DATAIN: - case STATE_DATAOUT: - ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; - break; - - case STATE_DATAOUT_ID: - ns->regs.num = ns->geom.idbytes; - break; - - case STATE_DATAOUT_STATUS: - ns->regs.count = ns->regs.num = 0; - break; - - default: - NS_ERR("switch_state: BUG! unknown data state\n"); - } - - } else if (ns->nxstate & STATE_ADDR_MASK) { - /* - * If the next state is address input, set the internal - * register to the number of expected address bytes - */ - - ns->regs.count = 0; - - switch (NS_STATE(ns->nxstate)) { - case STATE_ADDR_PAGE: - ns->regs.num = ns->geom.pgaddrbytes; - - break; - case STATE_ADDR_SEC: - ns->regs.num = ns->geom.secaddrbytes; - break; - - case STATE_ADDR_ZERO: - ns->regs.num = 1; - break; - - case STATE_ADDR_COLUMN: - /* Column address is always 2 bytes */ - ns->regs.num = ns->geom.pgaddrbytes - ns->geom.secaddrbytes; - break; - - default: - NS_ERR("switch_state: BUG! unknown address state\n"); - } - } else { - /* - * Just reset internal counters. - */ - - ns->regs.num = 0; - ns->regs.count = 0; - } -} - -static u_char ns_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nandsim *ns = nand_get_controller_data(chip); - u_char outb = 0x00; - - /* Sanity and correctness checks */ - if (!ns->lines.ce) { - NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb); - return outb; - } - if (ns->lines.ale || ns->lines.cle) { - NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb); - return outb; - } - if (!(ns->state & STATE_DATAOUT_MASK)) { - NS_WARN("read_byte: unexpected data output cycle, state is %s " - "return %#x\n", get_state_name(ns->state), (uint)outb); - return outb; - } - - /* Status register may be read as many times as it is wanted */ - if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) { - NS_DBG("read_byte: return %#x status\n", ns->regs.status); - return ns->regs.status; - } - - /* Check if there is any data in the internal buffer which may be read */ - if (ns->regs.count == ns->regs.num) { - NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb); - return outb; - } - - switch (NS_STATE(ns->state)) { - case STATE_DATAOUT: - if (ns->busw == 8) { - outb = ns->buf.byte[ns->regs.count]; - ns->regs.count += 1; - } else { - outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]); - ns->regs.count += 2; - } - break; - case STATE_DATAOUT_ID: - NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num); - outb = ns->ids[ns->regs.count]; - ns->regs.count += 1; - break; - default: - BUG(); - } - - if (ns->regs.count == ns->regs.num) { - NS_DBG("read_byte: all bytes were read\n"); - - if (NS_STATE(ns->nxstate) == STATE_READY) - switch_state(ns); - } - - return outb; -} - -static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nandsim *ns = nand_get_controller_data(chip); - - /* Sanity and correctness checks */ - if (!ns->lines.ce) { - NS_ERR("write_byte: chip is disabled, ignore write\n"); - return; - } - if (ns->lines.ale && ns->lines.cle) { - NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n"); - return; - } - - if (ns->lines.cle == 1) { - /* - * The byte written is a command. - */ - - if (byte == NAND_CMD_RESET) { - NS_LOG("reset chip\n"); - switch_to_ready_state(ns, NS_STATUS_OK(ns)); - return; - } - - /* Check that the command byte is correct */ - if (check_command(byte)) { - NS_ERR("write_byte: unknown command %#x\n", (uint)byte); - return; - } - - if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS - || NS_STATE(ns->state) == STATE_DATAOUT) { - int row = ns->regs.row; - - switch_state(ns); - if (byte == NAND_CMD_RNDOUT) - ns->regs.row = row; - } - - /* Check if chip is expecting command */ - if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { - /* Do not warn if only 2 id bytes are read */ - if (!(ns->regs.command == NAND_CMD_READID && - NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) { - /* - * We are in situation when something else (not command) - * was expected but command was input. In this case ignore - * previous command(s)/state(s) and accept the last one. - */ - NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " - "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); - } - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - } - - NS_DBG("command byte corresponding to %s state accepted\n", - get_state_name(get_state_by_command(byte))); - ns->regs.command = byte; - switch_state(ns); - - } else if (ns->lines.ale == 1) { - /* - * The byte written is an address. - */ - - if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) { - - NS_DBG("write_byte: operation isn't known yet, identify it\n"); - - if (find_operation(ns, 1) < 0) - return; - - if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - - ns->regs.count = 0; - switch (NS_STATE(ns->nxstate)) { - case STATE_ADDR_PAGE: - ns->regs.num = ns->geom.pgaddrbytes; - break; - case STATE_ADDR_SEC: - ns->regs.num = ns->geom.secaddrbytes; - break; - case STATE_ADDR_ZERO: - ns->regs.num = 1; - break; - default: - BUG(); - } - } - - /* Check that chip is expecting address */ - if (!(ns->nxstate & STATE_ADDR_MASK)) { - NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, " - "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate)); - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - - /* Check if this is expected byte */ - if (ns->regs.count == ns->regs.num) { - NS_ERR("write_byte: no more address bytes expected\n"); - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - - accept_addr_byte(ns, byte); - - ns->regs.count += 1; - - NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n", - (uint)byte, ns->regs.count, ns->regs.num); - - if (ns->regs.count == ns->regs.num) { - NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column); - switch_state(ns); - } - - } else { - /* - * The byte written is an input data. - */ - - /* Check that chip is expecting data input */ - if (!(ns->state & STATE_DATAIN_MASK)) { - NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, " - "switch to %s\n", (uint)byte, - get_state_name(ns->state), get_state_name(STATE_READY)); - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - - /* Check if this is expected byte */ - if (ns->regs.count == ns->regs.num) { - NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n", - ns->regs.num); - return; - } - - if (ns->busw == 8) { - ns->buf.byte[ns->regs.count] = byte; - ns->regs.count += 1; - } else { - ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte); - ns->regs.count += 2; - } - } - - return; -} - -static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nandsim *ns = nand_get_controller_data(chip); - - ns->lines.cle = bitmask & NAND_CLE ? 1 : 0; - ns->lines.ale = bitmask & NAND_ALE ? 1 : 0; - ns->lines.ce = bitmask & NAND_NCE ? 1 : 0; - - if (cmd != NAND_CMD_NONE) - ns_nand_write_byte(mtd, cmd); -} - -static int ns_device_ready(struct mtd_info *mtd) -{ - NS_DBG("device_ready\n"); - return 1; -} - -static uint16_t ns_nand_read_word(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - NS_DBG("read_word\n"); - - return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); -} - -static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nandsim *ns = nand_get_controller_data(chip); - - /* Check that chip is expecting data input */ - if (!(ns->state & STATE_DATAIN_MASK)) { - NS_ERR("write_buf: data input isn't expected, state is %s, " - "switch to STATE_READY\n", get_state_name(ns->state)); - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - - /* Check if these are expected bytes */ - if (ns->regs.count + len > ns->regs.num) { - NS_ERR("write_buf: too many input bytes\n"); - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - - memcpy(ns->buf.byte + ns->regs.count, buf, len); - ns->regs.count += len; - - if (ns->regs.count == ns->regs.num) { - NS_DBG("write_buf: %d bytes were written\n", ns->regs.count); - } -} - -static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nandsim *ns = nand_get_controller_data(chip); - - /* Sanity and correctness checks */ - if (!ns->lines.ce) { - NS_ERR("read_buf: chip is disabled\n"); - return; - } - if (ns->lines.ale || ns->lines.cle) { - NS_ERR("read_buf: ALE or CLE pin is high\n"); - return; - } - if (!(ns->state & STATE_DATAOUT_MASK)) { - NS_WARN("read_buf: unexpected data output cycle, current state is %s\n", - get_state_name(ns->state)); - return; - } - - if (NS_STATE(ns->state) != STATE_DATAOUT) { - int i; - - for (i = 0; i < len; i++) - buf[i] = mtd_to_nand(mtd)->read_byte(mtd); - - return; - } - - /* Check if these are expected bytes */ - if (ns->regs.count + len > ns->regs.num) { - NS_ERR("read_buf: too many bytes to read\n"); - switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - - memcpy(buf, ns->buf.byte + ns->regs.count, len); - ns->regs.count += len; - - if (ns->regs.count == ns->regs.num) { - if (NS_STATE(ns->nxstate) == STATE_READY) - switch_state(ns); - } - - return; -} - -/* - * Module initialization function - */ -static int __init ns_init_module(void) -{ - struct nand_chip *chip; - struct nandsim *nand; - int retval = -ENOMEM, i; - - if (bus_width != 8 && bus_width != 16) { - NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width); - return -EINVAL; - } - - /* Allocate and initialize mtd_info, nand_chip and nandsim structures */ - chip = kzalloc(sizeof(struct nand_chip) + sizeof(struct nandsim), - GFP_KERNEL); - if (!chip) { - NS_ERR("unable to allocate core structures.\n"); - return -ENOMEM; - } - nsmtd = nand_to_mtd(chip); - nand = (struct nandsim *)(chip + 1); - nand_set_controller_data(chip, (void *)nand); - - /* - * Register simulator's callbacks. - */ - chip->cmd_ctrl = ns_hwcontrol; - chip->read_byte = ns_nand_read_byte; - chip->dev_ready = ns_device_ready; - chip->write_buf = ns_nand_write_buf; - chip->read_buf = ns_nand_read_buf; - chip->read_word = ns_nand_read_word; - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */ - /* and 'badblocks' parameters to work */ - chip->options |= NAND_SKIP_BBTSCAN; - - switch (bbt) { - case 2: - chip->bbt_options |= NAND_BBT_NO_OOB; - case 1: - chip->bbt_options |= NAND_BBT_USE_FLASH; - case 0: - break; - default: - NS_ERR("bbt has to be 0..2\n"); - retval = -EINVAL; - goto error; - } - /* - * Perform minimum nandsim structure initialization to handle - * the initial ID read command correctly - */ - if (id_bytes[6] != 0xFF || id_bytes[7] != 0xFF) - nand->geom.idbytes = 8; - else if (id_bytes[4] != 0xFF || id_bytes[5] != 0xFF) - nand->geom.idbytes = 6; - else if (id_bytes[2] != 0xFF || id_bytes[3] != 0xFF) - nand->geom.idbytes = 4; - else - nand->geom.idbytes = 2; - nand->regs.status = NS_STATUS_OK(nand); - nand->nxstate = STATE_UNKNOWN; - nand->options |= OPT_PAGE512; /* temporary value */ - memcpy(nand->ids, id_bytes, sizeof(nand->ids)); - if (bus_width == 16) { - nand->busw = 16; - chip->options |= NAND_BUSWIDTH_16; - } - - nsmtd->owner = THIS_MODULE; - - if ((retval = parse_weakblocks()) != 0) - goto error; - - if ((retval = parse_weakpages()) != 0) - goto error; - - if ((retval = parse_gravepages()) != 0) - goto error; - - retval = nand_scan_ident(nsmtd, 1, NULL); - if (retval) { - NS_ERR("cannot scan NAND Simulator device\n"); - goto error; - } - - if (bch) { - unsigned int eccsteps, eccbytes; - if (!mtd_nand_has_bch()) { - NS_ERR("BCH ECC support is disabled\n"); - retval = -EINVAL; - goto error; - } - /* use 512-byte ecc blocks */ - eccsteps = nsmtd->writesize/512; - eccbytes = (bch*13+7)/8; - /* do not bother supporting small page devices */ - if ((nsmtd->oobsize < 64) || !eccsteps) { - NS_ERR("bch not available on small page devices\n"); - retval = -EINVAL; - goto error; - } - if ((eccbytes*eccsteps+2) > nsmtd->oobsize) { - NS_ERR("invalid bch value %u\n", bch); - retval = -EINVAL; - goto error; - } - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_BCH; - chip->ecc.size = 512; - chip->ecc.strength = bch; - chip->ecc.bytes = eccbytes; - NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size); - } - - retval = nand_scan_tail(nsmtd); - if (retval) { - NS_ERR("can't register NAND Simulator\n"); - goto error; - } - - if (overridesize) { - uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize; - if (new_size >> overridesize != nsmtd->erasesize) { - NS_ERR("overridesize is too big\n"); - retval = -EINVAL; - goto err_exit; - } - /* N.B. This relies on nand_scan not doing anything with the size before we change it */ - nsmtd->size = new_size; - chip->chipsize = new_size; - chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1; - chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; - } - - if ((retval = setup_wear_reporting(nsmtd)) != 0) - goto err_exit; - - if ((retval = init_nandsim(nsmtd)) != 0) - goto err_exit; - - if ((retval = chip->scan_bbt(nsmtd)) != 0) - goto err_exit; - - if ((retval = parse_badblocks(nand, nsmtd)) != 0) - goto err_exit; - - /* Register NAND partitions */ - retval = mtd_device_register(nsmtd, &nand->partitions[0], - nand->nbparts); - if (retval != 0) - goto err_exit; - - if ((retval = nandsim_debugfs_create(nand)) != 0) - goto err_exit; - - return 0; - -err_exit: - free_nandsim(nand); - nand_release(nsmtd); - for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i) - kfree(nand->partitions[i].name); -error: - kfree(chip); - free_lists(); - - return retval; -} - -module_init(ns_init_module); - -/* - * Module clean-up function - */ -static void __exit ns_cleanup_module(void) -{ - struct nand_chip *chip = mtd_to_nand(nsmtd); - struct nandsim *ns = nand_get_controller_data(chip); - int i; - - free_nandsim(ns); /* Free nandsim private resources */ - nand_release(nsmtd); /* Unregister driver */ - for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i) - kfree(ns->partitions[i].name); - kfree(mtd_to_nand(nsmtd)); /* Free other structures */ - free_lists(); -} - -module_exit(ns_cleanup_module); - -MODULE_LICENSE ("GPL"); -MODULE_AUTHOR ("Artem B. Bityuckiy"); -MODULE_DESCRIPTION ("The NAND flash simulator"); diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c deleted file mode 100644 index d8a806894937..000000000000 --- a/drivers/mtd/nand/ndfc.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Overview: - * Platform independent driver for NDFC (NanD Flash Controller) - * integrated into EP440 cores - * - * Ported to an OF platform driver by Sean MacLennan - * - * The NDFC supports multiple chips, but this driver only supports a - * single chip since I do not have access to any boards with - * multiple chips. - * - * Author: Thomas Gleixner - * - * Copyright 2006 IBM - * Copyright 2008 PIKA Technologies - * Sean MacLennan - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NDFC_MAX_CS 4 - -struct ndfc_controller { - struct platform_device *ofdev; - void __iomem *ndfcbase; - struct nand_chip chip; - int chip_select; - struct nand_hw_control ndfc_control; -}; - -static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS]; - -static void ndfc_select_chip(struct mtd_info *mtd, int chip) -{ - uint32_t ccr; - struct nand_chip *nchip = mtd_to_nand(mtd); - struct ndfc_controller *ndfc = nand_get_controller_data(nchip); - - ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); - if (chip >= 0) { - ccr &= ~NDFC_CCR_BS_MASK; - ccr |= NDFC_CCR_BS(chip + ndfc->chip_select); - } else - ccr |= NDFC_CCR_RESET_CE; - out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); -} - -static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct ndfc_controller *ndfc = nand_get_controller_data(chip); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_CMD); - else - writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_ALE); -} - -static int ndfc_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct ndfc_controller *ndfc = nand_get_controller_data(chip); - - return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; -} - -static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) -{ - uint32_t ccr; - struct nand_chip *chip = mtd_to_nand(mtd); - struct ndfc_controller *ndfc = nand_get_controller_data(chip); - - ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); - ccr |= NDFC_CCR_RESET_ECC; - out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); - wmb(); -} - -static int ndfc_calculate_ecc(struct mtd_info *mtd, - const u_char *dat, u_char *ecc_code) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct ndfc_controller *ndfc = nand_get_controller_data(chip); - uint32_t ecc; - uint8_t *p = (uint8_t *)&ecc; - - wmb(); - ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); - /* The NDFC uses Smart Media (SMC) bytes order */ - ecc_code[0] = p[1]; - ecc_code[1] = p[2]; - ecc_code[2] = p[3]; - - return 0; -} - -/* - * Speedups for buffer read/write/verify - * - * NDFC allows 32bit read/write of data. So we can speed up the buffer - * functions. No further checking, as nand_base will always read/write - * page aligned. - */ -static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct ndfc_controller *ndfc = nand_get_controller_data(chip); - uint32_t *p = (uint32_t *) buf; - - for(;len > 0; len -= 4) - *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA); -} - -static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct ndfc_controller *ndfc = nand_get_controller_data(chip); - uint32_t *p = (uint32_t *) buf; - - for(;len > 0; len -= 4) - out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); -} - -/* - * Initialize chip structure - */ -static int ndfc_chip_init(struct ndfc_controller *ndfc, - struct device_node *node) -{ - struct device_node *flash_np; - struct nand_chip *chip = &ndfc->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; - chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; - chip->cmd_ctrl = ndfc_hwcontrol; - chip->dev_ready = ndfc_ready; - chip->select_chip = ndfc_select_chip; - chip->chip_delay = 50; - chip->controller = &ndfc->ndfc_control; - chip->read_buf = ndfc_read_buf; - chip->write_buf = ndfc_write_buf; - chip->ecc.correct = nand_correct_data; - chip->ecc.hwctl = ndfc_enable_hwecc; - chip->ecc.calculate = ndfc_calculate_ecc; - chip->ecc.mode = NAND_ECC_HW; - chip->ecc.size = 256; - chip->ecc.bytes = 3; - chip->ecc.strength = 1; - nand_set_controller_data(chip, ndfc); - - mtd->dev.parent = &ndfc->ofdev->dev; - - flash_np = of_get_next_child(node, NULL); - if (!flash_np) - return -ENODEV; - nand_set_flash_node(chip, flash_np); - - mtd->name = kasprintf(GFP_KERNEL, "%s.%s", dev_name(&ndfc->ofdev->dev), - flash_np->name); - if (!mtd->name) { - ret = -ENOMEM; - goto err; - } - - ret = nand_scan(mtd, 1); - if (ret) - goto err; - - ret = mtd_device_register(mtd, NULL, 0); - -err: - of_node_put(flash_np); - if (ret) - kfree(mtd->name); - return ret; -} - -static int ndfc_probe(struct platform_device *ofdev) -{ - struct ndfc_controller *ndfc; - const __be32 *reg; - u32 ccr; - u32 cs; - int err, len; - - /* Read the reg property to get the chip select */ - reg = of_get_property(ofdev->dev.of_node, "reg", &len); - if (reg == NULL || len != 12) { - dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); - return -ENOENT; - } - - cs = be32_to_cpu(reg[0]); - if (cs >= NDFC_MAX_CS) { - dev_err(&ofdev->dev, "invalid CS number (%d)\n", cs); - return -EINVAL; - } - - ndfc = &ndfc_ctrl[cs]; - ndfc->chip_select = cs; - - nand_hw_control_init(&ndfc->ndfc_control); - ndfc->ofdev = ofdev; - dev_set_drvdata(&ofdev->dev, ndfc); - - ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0); - if (!ndfc->ndfcbase) { - dev_err(&ofdev->dev, "failed to get memory\n"); - return -EIO; - } - - ccr = NDFC_CCR_BS(ndfc->chip_select); - - /* It is ok if ccr does not exist - just default to 0 */ - reg = of_get_property(ofdev->dev.of_node, "ccr", NULL); - if (reg) - ccr |= be32_to_cpup(reg); - - out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); - - /* Set the bank settings if given */ - reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL); - if (reg) { - int offset = NDFC_BCFG0 + (ndfc->chip_select << 2); - out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg)); - } - - err = ndfc_chip_init(ndfc, ofdev->dev.of_node); - if (err) { - iounmap(ndfc->ndfcbase); - return err; - } - - return 0; -} - -static int ndfc_remove(struct platform_device *ofdev) -{ - struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); - struct mtd_info *mtd = nand_to_mtd(&ndfc->chip); - - nand_release(mtd); - kfree(mtd->name); - - return 0; -} - -static const struct of_device_id ndfc_match[] = { - { .compatible = "ibm,ndfc", }, - {} -}; -MODULE_DEVICE_TABLE(of, ndfc_match); - -static struct platform_driver ndfc_driver = { - .driver = { - .name = "ndfc", - .of_match_table = ndfc_match, - }, - .probe = ndfc_probe, - .remove = ndfc_remove, -}; - -module_platform_driver(ndfc_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Thomas Gleixner "); -MODULE_DESCRIPTION("OF Platform driver for NDFC"); diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c deleted file mode 100644 index af5b32c9a791..000000000000 --- a/drivers/mtd/nand/nuc900_nand.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright © 2009 Nuvoton technology corporation. - * - * Wan ZongShun - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation;version 2 of the License. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define REG_FMICSR 0x00 -#define REG_SMCSR 0xa0 -#define REG_SMISR 0xac -#define REG_SMCMD 0xb0 -#define REG_SMADDR 0xb4 -#define REG_SMDATA 0xb8 - -#define RESET_FMI 0x01 -#define NAND_EN 0x08 -#define READYBUSY (0x01 << 18) - -#define SWRST 0x01 -#define PSIZE (0x01 << 3) -#define DMARWEN (0x03 << 1) -#define BUSWID (0x01 << 4) -#define ECC4EN (0x01 << 5) -#define WP (0x01 << 24) -#define NANDCS (0x01 << 25) -#define ENDADDR (0x01 << 31) - -#define read_data_reg(dev) \ - __raw_readl((dev)->reg + REG_SMDATA) - -#define write_data_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMDATA) - -#define write_cmd_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMCMD) - -#define write_addr_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMADDR) - -struct nuc900_nand { - struct nand_chip chip; - void __iomem *reg; - struct clk *clk; - spinlock_t lock; -}; - -static inline struct nuc900_nand *mtd_to_nuc900(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct nuc900_nand, chip); -} - -static const struct mtd_partition partitions[] = { - { - .name = "NAND FS 0", - .offset = 0, - .size = 8 * 1024 * 1024 - }, - { - .name = "NAND FS 1", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL - } -}; - -static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd) -{ - unsigned char ret; - struct nuc900_nand *nand = mtd_to_nuc900(mtd); - - ret = (unsigned char)read_data_reg(nand); - - return ret; -} - -static void nuc900_nand_read_buf(struct mtd_info *mtd, - unsigned char *buf, int len) -{ - int i; - struct nuc900_nand *nand = mtd_to_nuc900(mtd); - - for (i = 0; i < len; i++) - buf[i] = (unsigned char)read_data_reg(nand); -} - -static void nuc900_nand_write_buf(struct mtd_info *mtd, - const unsigned char *buf, int len) -{ - int i; - struct nuc900_nand *nand = mtd_to_nuc900(mtd); - - for (i = 0; i < len; i++) - write_data_reg(nand, buf[i]); -} - -static int nuc900_check_rb(struct nuc900_nand *nand) -{ - unsigned int val; - spin_lock(&nand->lock); - val = __raw_readl(nand->reg + REG_SMISR); - val &= READYBUSY; - spin_unlock(&nand->lock); - - return val; -} - -static int nuc900_nand_devready(struct mtd_info *mtd) -{ - struct nuc900_nand *nand = mtd_to_nuc900(mtd); - int ready; - - ready = (nuc900_check_rb(nand)) ? 1 : 0; - return ready; -} - -static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - struct nuc900_nand *nand = mtd_to_nuc900(mtd); - - if (command == NAND_CMD_READOOB) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - write_cmd_reg(nand, command & 0xff); - - if (column != -1 || page_addr != -1) { - - if (column != -1) { - if (chip->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - write_addr_reg(nand, column); - write_addr_reg(nand, column >> 8 | ENDADDR); - } - if (page_addr != -1) { - write_addr_reg(nand, page_addr); - - if (chip->options & NAND_ROW_ADDR_3) { - write_addr_reg(nand, page_addr >> 8); - write_addr_reg(nand, page_addr >> 16 | ENDADDR); - } else { - write_addr_reg(nand, page_addr >> 8 | ENDADDR); - } - } - } - - switch (command) { - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: - case NAND_CMD_STATUS: - return; - - case NAND_CMD_RESET: - if (chip->dev_ready) - break; - udelay(chip->chip_delay); - - write_cmd_reg(nand, NAND_CMD_STATUS); - write_cmd_reg(nand, command); - - while (!nuc900_check_rb(nand)) - ; - - return; - - case NAND_CMD_RNDOUT: - write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); - return; - - case NAND_CMD_READ0: - - write_cmd_reg(nand, NAND_CMD_READSTART); - default: - - if (!chip->dev_ready) { - udelay(chip->chip_delay); - return; - } - } - - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ - ndelay(100); - - while (!chip->dev_ready(mtd)) - ; -} - - -static void nuc900_nand_enable(struct nuc900_nand *nand) -{ - unsigned int val; - spin_lock(&nand->lock); - __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); - - val = __raw_readl(nand->reg + REG_FMICSR); - - if (!(val & NAND_EN)) - __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); - - val = __raw_readl(nand->reg + REG_SMCSR); - - val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); - val |= WP; - - __raw_writel(val, nand->reg + REG_SMCSR); - - spin_unlock(&nand->lock); -} - -static int nuc900_nand_probe(struct platform_device *pdev) -{ - struct nuc900_nand *nuc900_nand; - struct nand_chip *chip; - struct mtd_info *mtd; - struct resource *res; - - nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand), - GFP_KERNEL); - if (!nuc900_nand) - return -ENOMEM; - chip = &(nuc900_nand->chip); - mtd = nand_to_mtd(chip); - - mtd->dev.parent = &pdev->dev; - spin_lock_init(&nuc900_nand->lock); - - nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(nuc900_nand->clk)) - return -ENOENT; - clk_enable(nuc900_nand->clk); - - chip->cmdfunc = nuc900_nand_command_lp; - chip->dev_ready = nuc900_nand_devready; - chip->read_byte = nuc900_nand_read_byte; - chip->write_buf = nuc900_nand_write_buf; - chip->read_buf = nuc900_nand_read_buf; - chip->chip_delay = 50; - chip->options = 0; - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nuc900_nand->reg)) - return PTR_ERR(nuc900_nand->reg); - - nuc900_nand_enable(nuc900_nand); - - if (nand_scan(mtd, 1)) - return -ENXIO; - - mtd_device_register(mtd, partitions, ARRAY_SIZE(partitions)); - - platform_set_drvdata(pdev, nuc900_nand); - - return 0; -} - -static int nuc900_nand_remove(struct platform_device *pdev) -{ - struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); - - nand_release(nand_to_mtd(&nuc900_nand->chip)); - clk_disable(nuc900_nand->clk); - - return 0; -} - -static struct platform_driver nuc900_nand_driver = { - .probe = nuc900_nand_probe, - .remove = nuc900_nand_remove, - .driver = { - .name = "nuc900-fmi", - }, -}; - -module_platform_driver(nuc900_nand_driver); - -MODULE_AUTHOR("Wan ZongShun "); -MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-fmi"); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c deleted file mode 100644 index 8cdf7d3d8fa7..000000000000 --- a/drivers/mtd/nand/omap2.c +++ /dev/null @@ -1,2316 +0,0 @@ -/* - * Copyright © 2004 Texas Instruments, Jian Zhang - * Copyright © 2004 Micron Technology Inc. - * Copyright © 2004 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#define DRIVER_NAME "omap2-nand" -#define OMAP_NAND_TIMEOUT_MS 5000 - -#define NAND_Ecc_P1e (1 << 0) -#define NAND_Ecc_P2e (1 << 1) -#define NAND_Ecc_P4e (1 << 2) -#define NAND_Ecc_P8e (1 << 3) -#define NAND_Ecc_P16e (1 << 4) -#define NAND_Ecc_P32e (1 << 5) -#define NAND_Ecc_P64e (1 << 6) -#define NAND_Ecc_P128e (1 << 7) -#define NAND_Ecc_P256e (1 << 8) -#define NAND_Ecc_P512e (1 << 9) -#define NAND_Ecc_P1024e (1 << 10) -#define NAND_Ecc_P2048e (1 << 11) - -#define NAND_Ecc_P1o (1 << 16) -#define NAND_Ecc_P2o (1 << 17) -#define NAND_Ecc_P4o (1 << 18) -#define NAND_Ecc_P8o (1 << 19) -#define NAND_Ecc_P16o (1 << 20) -#define NAND_Ecc_P32o (1 << 21) -#define NAND_Ecc_P64o (1 << 22) -#define NAND_Ecc_P128o (1 << 23) -#define NAND_Ecc_P256o (1 << 24) -#define NAND_Ecc_P512o (1 << 25) -#define NAND_Ecc_P1024o (1 << 26) -#define NAND_Ecc_P2048o (1 << 27) - -#define TF(value) (value ? 1 : 0) - -#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0) -#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1) -#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2) -#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3) -#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4) -#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5) -#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6) -#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7) - -#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0) -#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1) -#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2) -#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3) -#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4) -#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5) -#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6) -#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7) - -#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0) -#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1) -#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2) -#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3) -#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4) -#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5) -#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6) -#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7) - -#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0) -#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1) -#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2) -#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3) -#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4) -#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5) -#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6) -#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7) - -#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) -#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) - -#define PREFETCH_CONFIG1_CS_SHIFT 24 -#define ECC_CONFIG_CS_SHIFT 1 -#define CS_MASK 0x7 -#define ENABLE_PREFETCH (0x1 << 7) -#define DMA_MPU_MODE_SHIFT 2 -#define ECCSIZE0_SHIFT 12 -#define ECCSIZE1_SHIFT 22 -#define ECC1RESULTSIZE 0x1 -#define ECCCLEAR 0x100 -#define ECC1 0x1 -#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 -#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) -#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) -#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) -#define STATUS_BUFF_EMPTY 0x00000001 - -#define SECTOR_BYTES 512 -/* 4 bit padding to make byte aligned, 56 = 52 + 4 */ -#define BCH4_BIT_PAD 4 - -/* GPMC ecc engine settings for read */ -#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ -#define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */ -#define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */ -#define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */ -#define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */ - -/* GPMC ecc engine settings for write */ -#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */ -#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ -#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ - -#define BADBLOCK_MARKER_LENGTH 2 - -static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55, - 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78, - 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93, - 0x07, 0x0e}; -static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, - 0xac, 0x6b, 0xff, 0x99, 0x7b}; -static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; - -/* Shared among all NAND instances to synchronize access to the ECC Engine */ -static struct nand_hw_control omap_gpmc_controller = { - .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock), - .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq), -}; - -struct omap_nand_info { - struct nand_chip nand; - struct platform_device *pdev; - - int gpmc_cs; - bool dev_ready; - enum nand_io xfer_type; - int devsize; - enum omap_ecc ecc_opt; - struct device_node *elm_of_node; - - unsigned long phys_base; - struct completion comp; - struct dma_chan *dma; - int gpmc_irq_fifo; - int gpmc_irq_count; - enum { - OMAP_NAND_IO_READ = 0, /* read */ - OMAP_NAND_IO_WRITE, /* write */ - } iomode; - u_char *buf; - int buf_len; - /* Interface to GPMC */ - struct gpmc_nand_regs reg; - struct gpmc_nand_ops *ops; - bool flash_bbt; - /* fields specific for BCHx_HW ECC scheme */ - struct device *elm_dev; - /* NAND ready gpio */ - struct gpio_desc *ready_gpiod; -}; - -static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand); -} - -/** - * omap_prefetch_enable - configures and starts prefetch transfer - * @cs: cs (chip select) number - * @fifo_th: fifo threshold to be used for read/ write - * @dma_mode: dma mode enable (1) or disable (0) - * @u32_count: number of bytes to be transferred - * @is_write: prefetch read(0) or write post(1) mode - */ -static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode, - unsigned int u32_count, int is_write, struct omap_nand_info *info) -{ - u32 val; - - if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) - return -1; - - if (readl(info->reg.gpmc_prefetch_control)) - return -EBUSY; - - /* Set the amount of bytes to be prefetched */ - writel(u32_count, info->reg.gpmc_prefetch_config2); - - /* Set dma/mpu mode, the prefetch read / post write and - * enable the engine. Set which cs is has requested for. - */ - val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) | - PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH | - (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1)); - writel(val, info->reg.gpmc_prefetch_config1); - - /* Start the prefetch engine */ - writel(0x1, info->reg.gpmc_prefetch_control); - - return 0; -} - -/** - * omap_prefetch_reset - disables and stops the prefetch engine - */ -static int omap_prefetch_reset(int cs, struct omap_nand_info *info) -{ - u32 config1; - - /* check if the same module/cs is trying to reset */ - config1 = readl(info->reg.gpmc_prefetch_config1); - if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs) - return -EINVAL; - - /* Stop the PFPW engine */ - writel(0x0, info->reg.gpmc_prefetch_control); - - /* Reset/disable the PFPW engine */ - writel(0x0, info->reg.gpmc_prefetch_config1); - - return 0; -} - -/** - * omap_hwcontrol - hardware specific access to control-lines - * @mtd: MTD device structure - * @cmd: command to device - * @ctrl: - * NAND_NCE: bit 0 -> don't care - * NAND_CLE: bit 1 -> Command Latch - * NAND_ALE: bit 2 -> Address Latch - * - * NOTE: boards may use different bits for these!! - */ -static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - - if (cmd != NAND_CMD_NONE) { - if (ctrl & NAND_CLE) - writeb(cmd, info->reg.gpmc_nand_command); - - else if (ctrl & NAND_ALE) - writeb(cmd, info->reg.gpmc_nand_address); - - else /* NAND_NCE */ - writeb(cmd, info->reg.gpmc_nand_data); - } -} - -/** - * omap_read_buf8 - read data from NAND controller into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - - ioread8_rep(nand->IO_ADDR_R, buf, len); -} - -/** - * omap_write_buf8 - write buffer to NAND controller - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - u_char *p = (u_char *)buf; - bool status; - - while (len--) { - iowrite8(*p++, info->nand.IO_ADDR_W); - /* wait until buffer is available for write */ - do { - status = info->ops->nand_writebuffer_empty(); - } while (!status); - } -} - -/** - * omap_read_buf16 - read data from NAND controller into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - - ioread16_rep(nand->IO_ADDR_R, buf, len / 2); -} - -/** - * omap_write_buf16 - write buffer to NAND controller - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - u16 *p = (u16 *) buf; - bool status; - /* FIXME try bursts of writesw() or DMA ... */ - len >>= 1; - - while (len--) { - iowrite16(*p++, info->nand.IO_ADDR_W); - /* wait until buffer is available for write */ - do { - status = info->ops->nand_writebuffer_empty(); - } while (!status); - } -} - -/** - * omap_read_buf_pref - read data from NAND controller into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - uint32_t r_count = 0; - int ret = 0; - u32 *p = (u32 *)buf; - - /* take care of subpage reads */ - if (len % 4) { - if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, buf, len % 4); - else - omap_read_buf8(mtd, buf, len % 4); - p = (u32 *) (buf + len % 4); - len -= len % 4; - } - - /* configure and start prefetch transfer */ - ret = omap_prefetch_enable(info->gpmc_cs, - PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info); - if (ret) { - /* PFPW engine is busy, use cpu copy method */ - if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, (u_char *)p, len); - else - omap_read_buf8(mtd, (u_char *)p, len); - } else { - do { - r_count = readl(info->reg.gpmc_prefetch_status); - r_count = PREFETCH_STATUS_FIFO_CNT(r_count); - r_count = r_count >> 2; - ioread32_rep(info->nand.IO_ADDR_R, p, r_count); - p += r_count; - len -= r_count << 2; - } while (len); - /* disable and stop the PFPW engine */ - omap_prefetch_reset(info->gpmc_cs, info); - } -} - -/** - * omap_write_buf_pref - write buffer to NAND controller - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void omap_write_buf_pref(struct mtd_info *mtd, - const u_char *buf, int len) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - uint32_t w_count = 0; - int i = 0, ret = 0; - u16 *p = (u16 *)buf; - unsigned long tim, limit; - u32 val; - - /* take care of subpage writes */ - if (len % 2 != 0) { - writeb(*buf, info->nand.IO_ADDR_W); - p = (u16 *)(buf + 1); - len--; - } - - /* configure and start prefetch transfer */ - ret = omap_prefetch_enable(info->gpmc_cs, - PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info); - if (ret) { - /* PFPW engine is busy, use cpu copy method */ - if (info->nand.options & NAND_BUSWIDTH_16) - omap_write_buf16(mtd, (u_char *)p, len); - else - omap_write_buf8(mtd, (u_char *)p, len); - } else { - while (len) { - w_count = readl(info->reg.gpmc_prefetch_status); - w_count = PREFETCH_STATUS_FIFO_CNT(w_count); - w_count = w_count >> 1; - for (i = 0; (i < w_count) && len; i++, len -= 2) - iowrite16(*p++, info->nand.IO_ADDR_W); - } - /* wait for data to flushed-out before reset the prefetch */ - tim = 0; - limit = (loops_per_jiffy * - msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); - do { - cpu_relax(); - val = readl(info->reg.gpmc_prefetch_status); - val = PREFETCH_STATUS_COUNT(val); - } while (val && (tim++ < limit)); - - /* disable and stop the PFPW engine */ - omap_prefetch_reset(info->gpmc_cs, info); - } -} - -/* - * omap_nand_dma_callback: callback on the completion of dma transfer - * @data: pointer to completion data structure - */ -static void omap_nand_dma_callback(void *data) -{ - complete((struct completion *) data); -} - -/* - * omap_nand_dma_transfer: configure and start dma transfer - * @mtd: MTD device structure - * @addr: virtual address in RAM of source/destination - * @len: number of data bytes to be transferred - * @is_write: flag for read/write operation - */ -static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, - unsigned int len, int is_write) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - struct dma_async_tx_descriptor *tx; - enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : - DMA_FROM_DEVICE; - struct scatterlist sg; - unsigned long tim, limit; - unsigned n; - int ret; - u32 val; - - if (!virt_addr_valid(addr)) - goto out_copy; - - sg_init_one(&sg, addr, len); - n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); - if (n == 0) { - dev_err(&info->pdev->dev, - "Couldn't DMA map a %d byte buffer\n", len); - goto out_copy; - } - - tx = dmaengine_prep_slave_sg(info->dma, &sg, n, - is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!tx) - goto out_copy_unmap; - - tx->callback = omap_nand_dma_callback; - tx->callback_param = &info->comp; - dmaengine_submit(tx); - - init_completion(&info->comp); - - /* setup and start DMA using dma_addr */ - dma_async_issue_pending(info->dma); - - /* configure and start prefetch transfer */ - ret = omap_prefetch_enable(info->gpmc_cs, - PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info); - if (ret) - /* PFPW engine is busy, use cpu copy method */ - goto out_copy_unmap; - - wait_for_completion(&info->comp); - tim = 0; - limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); - - do { - cpu_relax(); - val = readl(info->reg.gpmc_prefetch_status); - val = PREFETCH_STATUS_COUNT(val); - } while (val && (tim++ < limit)); - - /* disable and stop the PFPW engine */ - omap_prefetch_reset(info->gpmc_cs, info); - - dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); - return 0; - -out_copy_unmap: - dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); -out_copy: - if (info->nand.options & NAND_BUSWIDTH_16) - is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) - : omap_write_buf16(mtd, (u_char *) addr, len); - else - is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) - : omap_write_buf8(mtd, (u_char *) addr, len); - return 0; -} - -/** - * omap_read_buf_dma_pref - read data from NAND controller into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len) -{ - if (len <= mtd->oobsize) - omap_read_buf_pref(mtd, buf, len); - else - /* start transfer in DMA mode */ - omap_nand_dma_transfer(mtd, buf, len, 0x0); -} - -/** - * omap_write_buf_dma_pref - write buffer to NAND controller - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void omap_write_buf_dma_pref(struct mtd_info *mtd, - const u_char *buf, int len) -{ - if (len <= mtd->oobsize) - omap_write_buf_pref(mtd, buf, len); - else - /* start transfer in DMA mode */ - omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1); -} - -/* - * omap_nand_irq - GPMC irq handler - * @this_irq: gpmc irq number - * @dev: omap_nand_info structure pointer is passed here - */ -static irqreturn_t omap_nand_irq(int this_irq, void *dev) -{ - struct omap_nand_info *info = (struct omap_nand_info *) dev; - u32 bytes; - - bytes = readl(info->reg.gpmc_prefetch_status); - bytes = PREFETCH_STATUS_FIFO_CNT(bytes); - bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */ - if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */ - if (this_irq == info->gpmc_irq_count) - goto done; - - if (info->buf_len && (info->buf_len < bytes)) - bytes = info->buf_len; - else if (!info->buf_len) - bytes = 0; - iowrite32_rep(info->nand.IO_ADDR_W, - (u32 *)info->buf, bytes >> 2); - info->buf = info->buf + bytes; - info->buf_len -= bytes; - - } else { - ioread32_rep(info->nand.IO_ADDR_R, - (u32 *)info->buf, bytes >> 2); - info->buf = info->buf + bytes; - - if (this_irq == info->gpmc_irq_count) - goto done; - } - - return IRQ_HANDLED; - -done: - complete(&info->comp); - - disable_irq_nosync(info->gpmc_irq_fifo); - disable_irq_nosync(info->gpmc_irq_count); - - return IRQ_HANDLED; -} - -/* - * omap_read_buf_irq_pref - read data from NAND controller into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - int ret = 0; - - if (len <= mtd->oobsize) { - omap_read_buf_pref(mtd, buf, len); - return; - } - - info->iomode = OMAP_NAND_IO_READ; - info->buf = buf; - init_completion(&info->comp); - - /* configure and start prefetch transfer */ - ret = omap_prefetch_enable(info->gpmc_cs, - PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info); - if (ret) - /* PFPW engine is busy, use cpu copy method */ - goto out_copy; - - info->buf_len = len; - - enable_irq(info->gpmc_irq_count); - enable_irq(info->gpmc_irq_fifo); - - /* waiting for read to complete */ - wait_for_completion(&info->comp); - - /* disable and stop the PFPW engine */ - omap_prefetch_reset(info->gpmc_cs, info); - return; - -out_copy: - if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, buf, len); - else - omap_read_buf8(mtd, buf, len); -} - -/* - * omap_write_buf_irq_pref - write buffer to NAND controller - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void omap_write_buf_irq_pref(struct mtd_info *mtd, - const u_char *buf, int len) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - int ret = 0; - unsigned long tim, limit; - u32 val; - - if (len <= mtd->oobsize) { - omap_write_buf_pref(mtd, buf, len); - return; - } - - info->iomode = OMAP_NAND_IO_WRITE; - info->buf = (u_char *) buf; - init_completion(&info->comp); - - /* configure and start prefetch transfer : size=24 */ - ret = omap_prefetch_enable(info->gpmc_cs, - (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info); - if (ret) - /* PFPW engine is busy, use cpu copy method */ - goto out_copy; - - info->buf_len = len; - - enable_irq(info->gpmc_irq_count); - enable_irq(info->gpmc_irq_fifo); - - /* waiting for write to complete */ - wait_for_completion(&info->comp); - - /* wait for data to flushed-out before reset the prefetch */ - tim = 0; - limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); - do { - val = readl(info->reg.gpmc_prefetch_status); - val = PREFETCH_STATUS_COUNT(val); - cpu_relax(); - } while (val && (tim++ < limit)); - - /* disable and stop the PFPW engine */ - omap_prefetch_reset(info->gpmc_cs, info); - return; - -out_copy: - if (info->nand.options & NAND_BUSWIDTH_16) - omap_write_buf16(mtd, buf, len); - else - omap_write_buf8(mtd, buf, len); -} - -/** - * gen_true_ecc - This function will generate true ECC value - * @ecc_buf: buffer to store ecc code - * - * This generated true ECC value can be used when correcting - * data read from NAND flash memory core - */ -static void gen_true_ecc(u8 *ecc_buf) -{ - u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | - ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); - - ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | - P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp)); - ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | - P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp)); - ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | - P1e(tmp) | P2048o(tmp) | P2048e(tmp)); -} - -/** - * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data - * @ecc_data1: ecc code from nand spare area - * @ecc_data2: ecc code from hardware register obtained from hardware ecc - * @page_data: page data - * - * This function compares two ECC's and indicates if there is an error. - * If the error can be corrected it will be corrected to the buffer. - * If there is no error, %0 is returned. If there is an error but it - * was corrected, %1 is returned. Otherwise, %-1 is returned. - */ -static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ - u8 *ecc_data2, /* read from register */ - u8 *page_data) -{ - uint i; - u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; - u8 comp0_bit[8], comp1_bit[8], comp2_bit[8]; - u8 ecc_bit[24]; - u8 ecc_sum = 0; - u8 find_bit = 0; - uint find_byte = 0; - int isEccFF; - - isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF); - - gen_true_ecc(ecc_data1); - gen_true_ecc(ecc_data2); - - for (i = 0; i <= 2; i++) { - *(ecc_data1 + i) = ~(*(ecc_data1 + i)); - *(ecc_data2 + i) = ~(*(ecc_data2 + i)); - } - - for (i = 0; i < 8; i++) { - tmp0_bit[i] = *ecc_data1 % 2; - *ecc_data1 = *ecc_data1 / 2; - } - - for (i = 0; i < 8; i++) { - tmp1_bit[i] = *(ecc_data1 + 1) % 2; - *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2; - } - - for (i = 0; i < 8; i++) { - tmp2_bit[i] = *(ecc_data1 + 2) % 2; - *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2; - } - - for (i = 0; i < 8; i++) { - comp0_bit[i] = *ecc_data2 % 2; - *ecc_data2 = *ecc_data2 / 2; - } - - for (i = 0; i < 8; i++) { - comp1_bit[i] = *(ecc_data2 + 1) % 2; - *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2; - } - - for (i = 0; i < 8; i++) { - comp2_bit[i] = *(ecc_data2 + 2) % 2; - *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2; - } - - for (i = 0; i < 6; i++) - ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2]; - - for (i = 0; i < 8; i++) - ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i]; - - for (i = 0; i < 8; i++) - ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i]; - - ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0]; - ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1]; - - for (i = 0; i < 24; i++) - ecc_sum += ecc_bit[i]; - - switch (ecc_sum) { - case 0: - /* Not reached because this function is not called if - * ECC values are equal - */ - return 0; - - case 1: - /* Uncorrectable error */ - pr_debug("ECC UNCORRECTED_ERROR 1\n"); - return -EBADMSG; - - case 11: - /* UN-Correctable error */ - pr_debug("ECC UNCORRECTED_ERROR B\n"); - return -EBADMSG; - - case 12: - /* Correctable error */ - find_byte = (ecc_bit[23] << 8) + - (ecc_bit[21] << 7) + - (ecc_bit[19] << 6) + - (ecc_bit[17] << 5) + - (ecc_bit[15] << 4) + - (ecc_bit[13] << 3) + - (ecc_bit[11] << 2) + - (ecc_bit[9] << 1) + - ecc_bit[7]; - - find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; - - pr_debug("Correcting single bit ECC error at offset: " - "%d, bit: %d\n", find_byte, find_bit); - - page_data[find_byte] ^= (1 << find_bit); - - return 1; - default: - if (isEccFF) { - if (ecc_data2[0] == 0 && - ecc_data2[1] == 0 && - ecc_data2[2] == 0) - return 0; - } - pr_debug("UNCORRECTED_ERROR default\n"); - return -EBADMSG; - } -} - -/** - * omap_correct_data - Compares the ECC read with HW generated ECC - * @mtd: MTD device structure - * @dat: page data - * @read_ecc: ecc read from nand flash - * @calc_ecc: ecc read from HW ECC registers - * - * Compares the ecc read from nand spare area with ECC registers values - * and if ECC's mismatched, it will call 'omap_compare_ecc' for error - * detection and correction. If there are no errors, %0 is returned. If - * there were errors and all of the errors were corrected, the number of - * corrected errors is returned. If uncorrectable errors exist, %-1 is - * returned. - */ -static int omap_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - int blockCnt = 0, i = 0, ret = 0; - int stat = 0; - - /* Ex NAND_ECC_HW12_2048 */ - if ((info->nand.ecc.mode == NAND_ECC_HW) && - (info->nand.ecc.size == 2048)) - blockCnt = 4; - else - blockCnt = 1; - - for (i = 0; i < blockCnt; i++) { - if (memcmp(read_ecc, calc_ecc, 3) != 0) { - ret = omap_compare_ecc(read_ecc, calc_ecc, dat); - if (ret < 0) - return ret; - /* keep track of the number of corrected errors */ - stat += ret; - } - read_ecc += 3; - calc_ecc += 3; - dat += 512; - } - return stat; -} - -/** - * omap_calcuate_ecc - Generate non-inverted ECC bytes. - * @mtd: MTD device structure - * @dat: The pointer to data on which ecc is computed - * @ecc_code: The ecc_code buffer - * - * Using noninverted ECC can be considered ugly since writing a blank - * page ie. padding will clear the ECC bytes. This is no problem as long - * nobody is trying to write data on the seemingly unused page. Reading - * an erased page will produce an ECC mismatch between generated and read - * ECC bytes that has to be dealt with separately. - */ -static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - u32 val; - - val = readl(info->reg.gpmc_ecc_config); - if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs) - return -EINVAL; - - /* read ecc result */ - val = readl(info->reg.gpmc_ecc1_result); - *ecc_code++ = val; /* P128e, ..., P1e */ - *ecc_code++ = val >> 16; /* P128o, ..., P1o */ - /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ - *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); - - return 0; -} - -/** - * omap_enable_hwecc - This function enables the hardware ecc functionality - * @mtd: MTD device structure - * @mode: Read/Write mode - */ -static void omap_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; - u32 val; - - /* clear ecc and enable bits */ - val = ECCCLEAR | ECC1; - writel(val, info->reg.gpmc_ecc_control); - - /* program ecc and result sizes */ - val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) | - ECC1RESULTSIZE); - writel(val, info->reg.gpmc_ecc_size_config); - - switch (mode) { - case NAND_ECC_READ: - case NAND_ECC_WRITE: - writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); - break; - case NAND_ECC_READSYN: - writel(ECCCLEAR, info->reg.gpmc_ecc_control); - break; - default: - dev_info(&info->pdev->dev, - "error: unrecognized Mode[%d]!\n", mode); - break; - } - - /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ - val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); - writel(val, info->reg.gpmc_ecc_config); -} - -/** - * omap_wait - wait until the command is done - * @mtd: MTD device structure - * @chip: NAND Chip structure - * - * Wait function is called during Program and erase operations and - * the way it is called from MTD layer, we should wait till the NAND - * chip is ready after the programming/erase operation has completed. - * - * Erase can take up to 400ms and program up to 20ms according to - * general NAND and SmartMedia specs - */ -static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct omap_nand_info *info = mtd_to_omap(mtd); - unsigned long timeo = jiffies; - int status, state = this->state; - - if (state == FL_ERASING) - timeo += msecs_to_jiffies(400); - else - timeo += msecs_to_jiffies(20); - - writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command); - while (time_before(jiffies, timeo)) { - status = readb(info->reg.gpmc_nand_data); - if (status & NAND_STATUS_READY) - break; - cond_resched(); - } - - status = readb(info->reg.gpmc_nand_data); - return status; -} - -/** - * omap_dev_ready - checks the NAND Ready GPIO line - * @mtd: MTD device structure - * - * Returns true if ready and false if busy. - */ -static int omap_dev_ready(struct mtd_info *mtd) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - - return gpiod_get_value(info->ready_gpiod); -} - -/** - * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation - * @mtd: MTD device structure - * @mode: Read/Write mode - * - * When using BCH with SW correction (i.e. no ELM), sector size is set - * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode - * for both reading and writing with: - * eccsize0 = 0 (no additional protected byte in spare area) - * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) - */ -static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) -{ - unsigned int bch_type; - unsigned int dev_width, nsectors; - struct omap_nand_info *info = mtd_to_omap(mtd); - enum omap_ecc ecc_opt = info->ecc_opt; - struct nand_chip *chip = mtd_to_nand(mtd); - u32 val, wr_mode; - unsigned int ecc_size1, ecc_size0; - - /* GPMC configurations for calculating ECC */ - switch (ecc_opt) { - case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: - bch_type = 0; - nsectors = 1; - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - break; - case OMAP_ECC_BCH4_CODE_HW: - bch_type = 0; - nsectors = chip->ecc.steps; - if (mode == NAND_ECC_READ) { - wr_mode = BCH_WRAPMODE_1; - ecc_size0 = BCH4R_ECC_SIZE0; - ecc_size1 = BCH4R_ECC_SIZE1; - } else { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } - break; - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - bch_type = 1; - nsectors = 1; - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - break; - case OMAP_ECC_BCH8_CODE_HW: - bch_type = 1; - nsectors = chip->ecc.steps; - if (mode == NAND_ECC_READ) { - wr_mode = BCH_WRAPMODE_1; - ecc_size0 = BCH8R_ECC_SIZE0; - ecc_size1 = BCH8R_ECC_SIZE1; - } else { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } - break; - case OMAP_ECC_BCH16_CODE_HW: - bch_type = 0x2; - nsectors = chip->ecc.steps; - if (mode == NAND_ECC_READ) { - wr_mode = 0x01; - ecc_size0 = 52; /* ECC bits in nibbles per sector */ - ecc_size1 = 0; /* non-ECC bits in nibbles per sector */ - } else { - wr_mode = 0x01; - ecc_size0 = 0; /* extra bits in nibbles per sector */ - ecc_size1 = 52; /* OOB bits in nibbles per sector */ - } - break; - default: - return; - } - - writel(ECC1, info->reg.gpmc_ecc_control); - - /* Configure ecc size for BCH */ - val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT); - writel(val, info->reg.gpmc_ecc_size_config); - - dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; - - /* BCH configuration */ - val = ((1 << 16) | /* enable BCH */ - (bch_type << 12) | /* BCH4/BCH8/BCH16 */ - (wr_mode << 8) | /* wrap mode */ - (dev_width << 7) | /* bus width */ - (((nsectors-1) & 0x7) << 4) | /* number of sectors */ - (info->gpmc_cs << 1) | /* ECC CS */ - (0x1)); /* enable ECC */ - - writel(val, info->reg.gpmc_ecc_config); - - /* Clear ecc and enable bits */ - writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); -} - -static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; -static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, - 0x97, 0x79, 0xe5, 0x24, 0xb5}; - -/** - * _omap_calculate_ecc_bch - Generate ECC bytes for one sector - * @mtd: MTD device structure - * @dat: The pointer to data on which ecc is computed - * @ecc_code: The ecc_code buffer - * @i: The sector number (for a multi sector page) - * - * Support calculating of BCH4/8/16 ECC vectors for one sector - * within a page. Sector number is in @i. - */ -static int _omap_calculate_ecc_bch(struct mtd_info *mtd, - const u_char *dat, u_char *ecc_calc, int i) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - int eccbytes = info->nand.ecc.bytes; - struct gpmc_nand_regs *gpmc_regs = &info->reg; - u8 *ecc_code; - unsigned long bch_val1, bch_val2, bch_val3, bch_val4; - u32 val; - int j; - - ecc_code = ecc_calc; - switch (info->ecc_opt) { - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - case OMAP_ECC_BCH8_CODE_HW: - bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); - bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); - bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]); - bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]); - *ecc_code++ = (bch_val4 & 0xFF); - *ecc_code++ = ((bch_val3 >> 24) & 0xFF); - *ecc_code++ = ((bch_val3 >> 16) & 0xFF); - *ecc_code++ = ((bch_val3 >> 8) & 0xFF); - *ecc_code++ = (bch_val3 & 0xFF); - *ecc_code++ = ((bch_val2 >> 24) & 0xFF); - *ecc_code++ = ((bch_val2 >> 16) & 0xFF); - *ecc_code++ = ((bch_val2 >> 8) & 0xFF); - *ecc_code++ = (bch_val2 & 0xFF); - *ecc_code++ = ((bch_val1 >> 24) & 0xFF); - *ecc_code++ = ((bch_val1 >> 16) & 0xFF); - *ecc_code++ = ((bch_val1 >> 8) & 0xFF); - *ecc_code++ = (bch_val1 & 0xFF); - break; - case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: - case OMAP_ECC_BCH4_CODE_HW: - bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); - bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); - *ecc_code++ = ((bch_val2 >> 12) & 0xFF); - *ecc_code++ = ((bch_val2 >> 4) & 0xFF); - *ecc_code++ = ((bch_val2 & 0xF) << 4) | - ((bch_val1 >> 28) & 0xF); - *ecc_code++ = ((bch_val1 >> 20) & 0xFF); - *ecc_code++ = ((bch_val1 >> 12) & 0xFF); - *ecc_code++ = ((bch_val1 >> 4) & 0xFF); - *ecc_code++ = ((bch_val1 & 0xF) << 4); - break; - case OMAP_ECC_BCH16_CODE_HW: - val = readl(gpmc_regs->gpmc_bch_result6[i]); - ecc_code[0] = ((val >> 8) & 0xFF); - ecc_code[1] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result5[i]); - ecc_code[2] = ((val >> 24) & 0xFF); - ecc_code[3] = ((val >> 16) & 0xFF); - ecc_code[4] = ((val >> 8) & 0xFF); - ecc_code[5] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result4[i]); - ecc_code[6] = ((val >> 24) & 0xFF); - ecc_code[7] = ((val >> 16) & 0xFF); - ecc_code[8] = ((val >> 8) & 0xFF); - ecc_code[9] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result3[i]); - ecc_code[10] = ((val >> 24) & 0xFF); - ecc_code[11] = ((val >> 16) & 0xFF); - ecc_code[12] = ((val >> 8) & 0xFF); - ecc_code[13] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result2[i]); - ecc_code[14] = ((val >> 24) & 0xFF); - ecc_code[15] = ((val >> 16) & 0xFF); - ecc_code[16] = ((val >> 8) & 0xFF); - ecc_code[17] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result1[i]); - ecc_code[18] = ((val >> 24) & 0xFF); - ecc_code[19] = ((val >> 16) & 0xFF); - ecc_code[20] = ((val >> 8) & 0xFF); - ecc_code[21] = ((val >> 0) & 0xFF); - val = readl(gpmc_regs->gpmc_bch_result0[i]); - ecc_code[22] = ((val >> 24) & 0xFF); - ecc_code[23] = ((val >> 16) & 0xFF); - ecc_code[24] = ((val >> 8) & 0xFF); - ecc_code[25] = ((val >> 0) & 0xFF); - break; - default: - return -EINVAL; - } - - /* ECC scheme specific syndrome customizations */ - switch (info->ecc_opt) { - case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: - /* Add constant polynomial to remainder, so that - * ECC of blank pages results in 0x0 on reading back - */ - for (j = 0; j < eccbytes; j++) - ecc_calc[j] ^= bch4_polynomial[j]; - break; - case OMAP_ECC_BCH4_CODE_HW: - /* Set 8th ECC byte as 0x0 for ROM compatibility */ - ecc_calc[eccbytes - 1] = 0x0; - break; - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - /* Add constant polynomial to remainder, so that - * ECC of blank pages results in 0x0 on reading back - */ - for (j = 0; j < eccbytes; j++) - ecc_calc[j] ^= bch8_polynomial[j]; - break; - case OMAP_ECC_BCH8_CODE_HW: - /* Set 14th ECC byte as 0x0 for ROM compatibility */ - ecc_calc[eccbytes - 1] = 0x0; - break; - case OMAP_ECC_BCH16_CODE_HW: - break; - default: - return -EINVAL; - } - - return 0; -} - -/** - * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction - * @mtd: MTD device structure - * @dat: The pointer to data on which ecc is computed - * @ecc_code: The ecc_code buffer - * - * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used - * when SW based correction is required as ECC is required for one sector - * at a time. - */ -static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd, - const u_char *dat, u_char *ecc_calc) -{ - return _omap_calculate_ecc_bch(mtd, dat, ecc_calc, 0); -} - -/** - * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors - * @mtd: MTD device structure - * @dat: The pointer to data on which ecc is computed - * @ecc_code: The ecc_code buffer - * - * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go. - */ -static int omap_calculate_ecc_bch_multi(struct mtd_info *mtd, - const u_char *dat, u_char *ecc_calc) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - int eccbytes = info->nand.ecc.bytes; - unsigned long nsectors; - int i, ret; - - nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; - for (i = 0; i < nsectors; i++) { - ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i); - if (ret) - return ret; - - ecc_calc += eccbytes; - } - - return 0; -} - -/** - * erased_sector_bitflips - count bit flips - * @data: data sector buffer - * @oob: oob buffer - * @info: omap_nand_info - * - * Check the bit flips in erased page falls below correctable level. - * If falls below, report the page as erased with correctable bit - * flip, else report as uncorrectable page. - */ -static int erased_sector_bitflips(u_char *data, u_char *oob, - struct omap_nand_info *info) -{ - int flip_bits = 0, i; - - for (i = 0; i < info->nand.ecc.size; i++) { - flip_bits += hweight8(~data[i]); - if (flip_bits > info->nand.ecc.strength) - return 0; - } - - for (i = 0; i < info->nand.ecc.bytes - 1; i++) { - flip_bits += hweight8(~oob[i]); - if (flip_bits > info->nand.ecc.strength) - return 0; - } - - /* - * Bit flips falls in correctable level. - * Fill data area with 0xFF - */ - if (flip_bits) { - memset(data, 0xFF, info->nand.ecc.size); - memset(oob, 0xFF, info->nand.ecc.bytes); - } - - return flip_bits; -} - -/** - * omap_elm_correct_data - corrects page data area in case error reported - * @mtd: MTD device structure - * @data: page data - * @read_ecc: ecc read from nand flash - * @calc_ecc: ecc read from HW ECC registers - * - * Calculated ecc vector reported as zero in case of non-error pages. - * In case of non-zero ecc vector, first filter out erased-pages, and - * then process data via ELM to detect bit-flips. - */ -static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, - u_char *read_ecc, u_char *calc_ecc) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - struct nand_ecc_ctrl *ecc = &info->nand.ecc; - int eccsteps = info->nand.ecc.steps; - int i , j, stat = 0; - int eccflag, actual_eccbytes; - struct elm_errorvec err_vec[ERROR_VECTOR_MAX]; - u_char *ecc_vec = calc_ecc; - u_char *spare_ecc = read_ecc; - u_char *erased_ecc_vec; - u_char *buf; - int bitflip_count; - bool is_error_reported = false; - u32 bit_pos, byte_pos, error_max, pos; - int err; - - switch (info->ecc_opt) { - case OMAP_ECC_BCH4_CODE_HW: - /* omit 7th ECC byte reserved for ROM code compatibility */ - actual_eccbytes = ecc->bytes - 1; - erased_ecc_vec = bch4_vector; - break; - case OMAP_ECC_BCH8_CODE_HW: - /* omit 14th ECC byte reserved for ROM code compatibility */ - actual_eccbytes = ecc->bytes - 1; - erased_ecc_vec = bch8_vector; - break; - case OMAP_ECC_BCH16_CODE_HW: - actual_eccbytes = ecc->bytes; - erased_ecc_vec = bch16_vector; - break; - default: - dev_err(&info->pdev->dev, "invalid driver configuration\n"); - return -EINVAL; - } - - /* Initialize elm error vector to zero */ - memset(err_vec, 0, sizeof(err_vec)); - - for (i = 0; i < eccsteps ; i++) { - eccflag = 0; /* initialize eccflag */ - - /* - * Check any error reported, - * In case of error, non zero ecc reported. - */ - for (j = 0; j < actual_eccbytes; j++) { - if (calc_ecc[j] != 0) { - eccflag = 1; /* non zero ecc, error present */ - break; - } - } - - if (eccflag == 1) { - if (memcmp(calc_ecc, erased_ecc_vec, - actual_eccbytes) == 0) { - /* - * calc_ecc[] matches pattern for ECC(all 0xff) - * so this is definitely an erased-page - */ - } else { - buf = &data[info->nand.ecc.size * i]; - /* - * count number of 0-bits in read_buf. - * This check can be removed once a similar - * check is introduced in generic NAND driver - */ - bitflip_count = erased_sector_bitflips( - buf, read_ecc, info); - if (bitflip_count) { - /* - * number of 0-bits within ECC limits - * So this may be an erased-page - */ - stat += bitflip_count; - } else { - /* - * Too many 0-bits. It may be a - * - programmed-page, OR - * - erased-page with many bit-flips - * So this page requires check by ELM - */ - err_vec[i].error_reported = true; - is_error_reported = true; - } - } - } - - /* Update the ecc vector */ - calc_ecc += ecc->bytes; - read_ecc += ecc->bytes; - } - - /* Check if any error reported */ - if (!is_error_reported) - return stat; - - /* Decode BCH error using ELM module */ - elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); - - err = 0; - for (i = 0; i < eccsteps; i++) { - if (err_vec[i].error_uncorrectable) { - dev_err(&info->pdev->dev, - "uncorrectable bit-flips found\n"); - err = -EBADMSG; - } else if (err_vec[i].error_reported) { - for (j = 0; j < err_vec[i].error_count; j++) { - switch (info->ecc_opt) { - case OMAP_ECC_BCH4_CODE_HW: - /* Add 4 bits to take care of padding */ - pos = err_vec[i].error_loc[j] + - BCH4_BIT_PAD; - break; - case OMAP_ECC_BCH8_CODE_HW: - case OMAP_ECC_BCH16_CODE_HW: - pos = err_vec[i].error_loc[j]; - break; - default: - return -EINVAL; - } - error_max = (ecc->size + actual_eccbytes) * 8; - /* Calculate bit position of error */ - bit_pos = pos % 8; - - /* Calculate byte position of error */ - byte_pos = (error_max - pos - 1) / 8; - - if (pos < error_max) { - if (byte_pos < 512) { - pr_debug("bitflip@dat[%d]=%x\n", - byte_pos, data[byte_pos]); - data[byte_pos] ^= 1 << bit_pos; - } else { - pr_debug("bitflip@oob[%d]=%x\n", - (byte_pos - 512), - spare_ecc[byte_pos - 512]); - spare_ecc[byte_pos - 512] ^= - 1 << bit_pos; - } - } else { - dev_err(&info->pdev->dev, - "invalid bit-flip @ %d:%d\n", - byte_pos, bit_pos); - err = -EBADMSG; - } - } - } - - /* Update number of correctable errors */ - stat += err_vec[i].error_count; - - /* Update page data with sector size */ - data += ecc->size; - spare_ecc += ecc->bytes; - } - - return (err) ? err : stat; -} - -/** - * omap_write_page_bch - BCH ecc based write page function for entire page - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page - * - * Custom write page method evolved to support multi sector writing in one shot - */ -static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - int ret; - uint8_t *ecc_calc = chip->ecc.calc_buf; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - /* Enable GPMC ecc engine */ - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - - /* Write data */ - chip->write_buf(mtd, buf, mtd->writesize); - - /* Update ecc vector from GPMC result registers */ - omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]); - - ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - /* Write ecc vector to OOB area */ - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -/** - * omap_write_subpage_bch - BCH hardware ECC based subpage write - * @mtd: mtd info structure - * @chip: nand chip info structure - * @offset: column address of subpage within the page - * @data_len: data length - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * - * OMAP optimized subpage write method. - */ -static int omap_write_subpage_bch(struct mtd_info *mtd, - struct nand_chip *chip, u32 offset, - u32 data_len, const u8 *buf, - int oob_required, int page) -{ - u8 *ecc_calc = chip->ecc.calc_buf; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - int ecc_steps = chip->ecc.steps; - u32 start_step = offset / ecc_size; - u32 end_step = (offset + data_len - 1) / ecc_size; - int step, ret = 0; - - /* - * Write entire page at one go as it would be optimal - * as ECC is calculated by hardware. - * ECC is calculated for all subpages but we choose - * only what we want. - */ - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - /* Enable GPMC ECC engine */ - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - - /* Write data */ - chip->write_buf(mtd, buf, mtd->writesize); - - for (step = 0; step < ecc_steps; step++) { - /* mask ECC of un-touched subpages by padding 0xFF */ - if (step < start_step || step > end_step) - memset(ecc_calc, 0xff, ecc_bytes); - else - ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step); - - if (ret) - return ret; - - buf += ecc_size; - ecc_calc += ecc_bytes; - } - - /* copy calculated ECC for whole page to chip->buffer->oob */ - /* this include masked-value(0xFF) for unwritten subpages */ - ecc_calc = chip->ecc.calc_buf; - ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - /* write OOB buffer to NAND device */ - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -/** - * omap_read_page_bch - BCH ecc based page read function for entire page - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module - * used for error correction. - * Custom method evolved to support ELM error correction & multi sector - * reading. On reading page data area is read along with OOB data with - * ecc engine enabled. ecc vector updated after read of OOB data. - * For non error pages ecc vector reported as zero. - */ -static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - uint8_t *ecc_calc = chip->ecc.calc_buf; - uint8_t *ecc_code = chip->ecc.code_buf; - int stat, ret; - unsigned int max_bitflips = 0; - - nand_read_page_op(chip, page, 0, NULL, 0); - - /* Enable GPMC ecc engine */ - chip->ecc.hwctl(mtd, NAND_ECC_READ); - - /* Read data */ - chip->read_buf(mtd, buf, mtd->writesize); - - /* Read oob bytes */ - nand_change_read_column_op(chip, - mtd->writesize + BADBLOCK_MARKER_LENGTH, - chip->oob_poi + BADBLOCK_MARKER_LENGTH, - chip->ecc.total, false); - - /* Calculate ecc bytes */ - omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc); - - ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc); - - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - - return max_bitflips; -} - -/** - * is_elm_present - checks for presence of ELM module by scanning DT nodes - * @omap_nand_info: NAND device structure containing platform data - */ -static bool is_elm_present(struct omap_nand_info *info, - struct device_node *elm_node) -{ - struct platform_device *pdev; - - /* check whether elm-id is passed via DT */ - if (!elm_node) { - dev_err(&info->pdev->dev, "ELM devicetree node not found\n"); - return false; - } - pdev = of_find_device_by_node(elm_node); - /* check whether ELM device is registered */ - if (!pdev) { - dev_err(&info->pdev->dev, "ELM device not found\n"); - return false; - } - /* ELM module available, now configure it */ - info->elm_dev = &pdev->dev; - return true; -} - -static bool omap2_nand_ecc_check(struct omap_nand_info *info) -{ - bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm; - - switch (info->ecc_opt) { - case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - ecc_needs_omap_bch = false; - ecc_needs_bch = true; - ecc_needs_elm = false; - break; - case OMAP_ECC_BCH4_CODE_HW: - case OMAP_ECC_BCH8_CODE_HW: - case OMAP_ECC_BCH16_CODE_HW: - ecc_needs_omap_bch = true; - ecc_needs_bch = false; - ecc_needs_elm = true; - break; - default: - ecc_needs_omap_bch = false; - ecc_needs_bch = false; - ecc_needs_elm = false; - break; - } - - if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_BCH)) { - dev_err(&info->pdev->dev, - "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); - return false; - } - if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) { - dev_err(&info->pdev->dev, - "CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); - return false; - } - if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) { - dev_err(&info->pdev->dev, "ELM not available\n"); - return false; - } - - return true; -} - -static const char * const nand_xfer_types[] = { - [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", - [NAND_OMAP_POLLED] = "polled", - [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", - [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", -}; - -static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) -{ - struct device_node *child = dev->of_node; - int i; - const char *s; - u32 cs; - - if (of_property_read_u32(child, "reg", &cs) < 0) { - dev_err(dev, "reg not found in DT\n"); - return -EINVAL; - } - - info->gpmc_cs = cs; - - /* detect availability of ELM module. Won't be present pre-OMAP4 */ - info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); - if (!info->elm_of_node) { - info->elm_of_node = of_parse_phandle(child, "elm_id", 0); - if (!info->elm_of_node) - dev_dbg(dev, "ti,elm-id not in DT\n"); - } - - /* select ecc-scheme for NAND */ - if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { - dev_err(dev, "ti,nand-ecc-opt not found\n"); - return -EINVAL; - } - - if (!strcmp(s, "sw")) { - info->ecc_opt = OMAP_ECC_HAM1_CODE_SW; - } else if (!strcmp(s, "ham1") || - !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) { - info->ecc_opt = OMAP_ECC_HAM1_CODE_HW; - } else if (!strcmp(s, "bch4")) { - if (info->elm_of_node) - info->ecc_opt = OMAP_ECC_BCH4_CODE_HW; - else - info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; - } else if (!strcmp(s, "bch8")) { - if (info->elm_of_node) - info->ecc_opt = OMAP_ECC_BCH8_CODE_HW; - else - info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; - } else if (!strcmp(s, "bch16")) { - info->ecc_opt = OMAP_ECC_BCH16_CODE_HW; - } else { - dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n"); - return -EINVAL; - } - - /* select data transfer mode */ - if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) { - for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { - if (!strcasecmp(s, nand_xfer_types[i])) { - info->xfer_type = i; - return 0; - } - } - - dev_err(dev, "unrecognized value for ti,nand-xfer-type\n"); - return -EINVAL; - } - - return 0; -} - -static int omap_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - struct nand_chip *chip = &info->nand; - int off = BADBLOCK_MARKER_LENGTH; - - if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && - !(chip->options & NAND_BUSWIDTH_16)) - off = 1; - - if (section) - return -ERANGE; - - oobregion->offset = off; - oobregion->length = chip->ecc.total; - - return 0; -} - -static int omap_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct omap_nand_info *info = mtd_to_omap(mtd); - struct nand_chip *chip = &info->nand; - int off = BADBLOCK_MARKER_LENGTH; - - if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && - !(chip->options & NAND_BUSWIDTH_16)) - off = 1; - - if (section) - return -ERANGE; - - off += chip->ecc.total; - if (off >= mtd->oobsize) - return -ERANGE; - - oobregion->offset = off; - oobregion->length = mtd->oobsize - off; - - return 0; -} - -static const struct mtd_ooblayout_ops omap_ooblayout_ops = { - .ecc = omap_ooblayout_ecc, - .free = omap_ooblayout_free, -}; - -static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int off = BADBLOCK_MARKER_LENGTH; - - if (section >= chip->ecc.steps) - return -ERANGE; - - /* - * When SW correction is employed, one OMAP specific marker byte is - * reserved after each ECC step. - */ - oobregion->offset = off + (section * (chip->ecc.bytes + 1)); - oobregion->length = chip->ecc.bytes; - - return 0; -} - -static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int off = BADBLOCK_MARKER_LENGTH; - - if (section) - return -ERANGE; - - /* - * When SW correction is employed, one OMAP specific marker byte is - * reserved after each ECC step. - */ - off += ((chip->ecc.bytes + 1) * chip->ecc.steps); - if (off >= mtd->oobsize) - return -ERANGE; - - oobregion->offset = off; - oobregion->length = mtd->oobsize - off; - - return 0; -} - -static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = { - .ecc = omap_sw_ooblayout_ecc, - .free = omap_sw_ooblayout_free, -}; - -static int omap_nand_probe(struct platform_device *pdev) -{ - struct omap_nand_info *info; - struct mtd_info *mtd; - struct nand_chip *nand_chip; - int err; - dma_cap_mask_t mask; - struct resource *res; - struct device *dev = &pdev->dev; - int min_oobbytes = BADBLOCK_MARKER_LENGTH; - int oobbytes_per_step; - - info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->pdev = pdev; - - err = omap_get_dt_info(dev, info); - if (err) - return err; - - info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); - if (!info->ops) { - dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); - return -ENODEV; - } - - nand_chip = &info->nand; - mtd = nand_to_mtd(nand_chip); - mtd->dev.parent = &pdev->dev; - nand_chip->ecc.priv = NULL; - nand_set_flash_node(nand_chip, dev->of_node); - - if (!mtd->name) { - mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "omap2-nand.%d", info->gpmc_cs); - if (!mtd->name) { - dev_err(&pdev->dev, "Failed to set MTD name\n"); - return -ENOMEM; - } - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nand_chip->IO_ADDR_R)) - return PTR_ERR(nand_chip->IO_ADDR_R); - - info->phys_base = res->start; - - nand_chip->controller = &omap_gpmc_controller; - - nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; - nand_chip->cmd_ctrl = omap_hwcontrol; - - info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", - GPIOD_IN); - if (IS_ERR(info->ready_gpiod)) { - dev_err(dev, "failed to get ready gpio\n"); - return PTR_ERR(info->ready_gpiod); - } - - /* - * If RDY/BSY line is connected to OMAP then use the omap ready - * function and the generic nand_wait function which reads the status - * register after monitoring the RDY/BSY line. Otherwise use a standard - * chip delay which is slightly more than tR (AC Timing) of the NAND - * device and read status register until you get a failure or success - */ - if (info->ready_gpiod) { - nand_chip->dev_ready = omap_dev_ready; - nand_chip->chip_delay = 0; - } else { - nand_chip->waitfunc = omap_wait; - nand_chip->chip_delay = 50; - } - - if (info->flash_bbt) - nand_chip->bbt_options |= NAND_BBT_USE_FLASH; - - /* scan NAND device connected to chip controller */ - nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; - err = nand_scan_ident(mtd, 1, NULL); - if (err) { - dev_err(&info->pdev->dev, - "scan failed, may be bus-width mismatch\n"); - goto return_error; - } - - if (nand_chip->bbt_options & NAND_BBT_USE_FLASH) - nand_chip->bbt_options |= NAND_BBT_NO_OOB; - else - nand_chip->options |= NAND_SKIP_BBTSCAN; - - /* re-populate low-level callbacks based on xfer modes */ - switch (info->xfer_type) { - case NAND_OMAP_PREFETCH_POLLED: - nand_chip->read_buf = omap_read_buf_pref; - nand_chip->write_buf = omap_write_buf_pref; - break; - - case NAND_OMAP_POLLED: - /* Use nand_base defaults for {read,write}_buf */ - break; - - case NAND_OMAP_PREFETCH_DMA: - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - info->dma = dma_request_chan(pdev->dev.parent, "rxtx"); - - if (IS_ERR(info->dma)) { - dev_err(&pdev->dev, "DMA engine request failed\n"); - err = PTR_ERR(info->dma); - goto return_error; - } else { - struct dma_slave_config cfg; - - memset(&cfg, 0, sizeof(cfg)); - cfg.src_addr = info->phys_base; - cfg.dst_addr = info->phys_base; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.src_maxburst = 16; - cfg.dst_maxburst = 16; - err = dmaengine_slave_config(info->dma, &cfg); - if (err) { - dev_err(&pdev->dev, "DMA engine slave config failed: %d\n", - err); - goto return_error; - } - nand_chip->read_buf = omap_read_buf_dma_pref; - nand_chip->write_buf = omap_write_buf_dma_pref; - } - break; - - case NAND_OMAP_PREFETCH_IRQ: - info->gpmc_irq_fifo = platform_get_irq(pdev, 0); - if (info->gpmc_irq_fifo <= 0) { - dev_err(&pdev->dev, "error getting fifo irq\n"); - err = -ENODEV; - goto return_error; - } - err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo, - omap_nand_irq, IRQF_SHARED, - "gpmc-nand-fifo", info); - if (err) { - dev_err(&pdev->dev, "requesting irq(%d) error:%d", - info->gpmc_irq_fifo, err); - info->gpmc_irq_fifo = 0; - goto return_error; - } - - info->gpmc_irq_count = platform_get_irq(pdev, 1); - if (info->gpmc_irq_count <= 0) { - dev_err(&pdev->dev, "error getting count irq\n"); - err = -ENODEV; - goto return_error; - } - err = devm_request_irq(&pdev->dev, info->gpmc_irq_count, - omap_nand_irq, IRQF_SHARED, - "gpmc-nand-count", info); - if (err) { - dev_err(&pdev->dev, "requesting irq(%d) error:%d", - info->gpmc_irq_count, err); - info->gpmc_irq_count = 0; - goto return_error; - } - - nand_chip->read_buf = omap_read_buf_irq_pref; - nand_chip->write_buf = omap_write_buf_irq_pref; - - break; - - default: - dev_err(&pdev->dev, - "xfer_type(%d) not supported!\n", info->xfer_type); - err = -EINVAL; - goto return_error; - } - - if (!omap2_nand_ecc_check(info)) { - err = -EINVAL; - goto return_error; - } - - /* - * Bail out earlier to let NAND_ECC_SOFT code create its own - * ooblayout instead of using ours. - */ - if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { - nand_chip->ecc.mode = NAND_ECC_SOFT; - nand_chip->ecc.algo = NAND_ECC_HAMMING; - goto scan_tail; - } - - /* populate MTD interface based on ECC scheme */ - switch (info->ecc_opt) { - case OMAP_ECC_HAM1_CODE_HW: - pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.bytes = 3; - nand_chip->ecc.size = 512; - nand_chip->ecc.strength = 1; - nand_chip->ecc.calculate = omap_calculate_ecc; - nand_chip->ecc.hwctl = omap_enable_hwecc; - nand_chip->ecc.correct = omap_correct_data; - mtd_set_ooblayout(mtd, &omap_ooblayout_ops); - oobbytes_per_step = nand_chip->ecc.bytes; - - if (!(nand_chip->options & NAND_BUSWIDTH_16)) - min_oobbytes = 1; - - break; - - case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: - pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - nand_chip->ecc.bytes = 7; - nand_chip->ecc.strength = 4; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = nand_bch_correct_data; - nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; - mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); - /* Reserve one byte for the OMAP marker */ - oobbytes_per_step = nand_chip->ecc.bytes + 1; - /* software bch library is used for locating errors */ - nand_chip->ecc.priv = nand_bch_init(mtd); - if (!nand_chip->ecc.priv) { - dev_err(&info->pdev->dev, "unable to use BCH library\n"); - err = -EINVAL; - goto return_error; - } - break; - - case OMAP_ECC_BCH4_CODE_HW: - pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - /* 14th bit is kept reserved for ROM-code compatibility */ - nand_chip->ecc.bytes = 7 + 1; - nand_chip->ecc.strength = 4; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.read_page = omap_read_page_bch; - nand_chip->ecc.write_page = omap_write_page_bch; - nand_chip->ecc.write_subpage = omap_write_subpage_bch; - mtd_set_ooblayout(mtd, &omap_ooblayout_ops); - oobbytes_per_step = nand_chip->ecc.bytes; - - err = elm_config(info->elm_dev, BCH4_ECC, - mtd->writesize / nand_chip->ecc.size, - nand_chip->ecc.size, nand_chip->ecc.bytes); - if (err < 0) - goto return_error; - break; - - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - nand_chip->ecc.bytes = 13; - nand_chip->ecc.strength = 8; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = nand_bch_correct_data; - nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; - mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); - /* Reserve one byte for the OMAP marker */ - oobbytes_per_step = nand_chip->ecc.bytes + 1; - /* software bch library is used for locating errors */ - nand_chip->ecc.priv = nand_bch_init(mtd); - if (!nand_chip->ecc.priv) { - dev_err(&info->pdev->dev, "unable to use BCH library\n"); - err = -EINVAL; - goto return_error; - } - break; - - case OMAP_ECC_BCH8_CODE_HW: - pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - /* 14th bit is kept reserved for ROM-code compatibility */ - nand_chip->ecc.bytes = 13 + 1; - nand_chip->ecc.strength = 8; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.read_page = omap_read_page_bch; - nand_chip->ecc.write_page = omap_write_page_bch; - nand_chip->ecc.write_subpage = omap_write_subpage_bch; - mtd_set_ooblayout(mtd, &omap_ooblayout_ops); - oobbytes_per_step = nand_chip->ecc.bytes; - - err = elm_config(info->elm_dev, BCH8_ECC, - mtd->writesize / nand_chip->ecc.size, - nand_chip->ecc.size, nand_chip->ecc.bytes); - if (err < 0) - goto return_error; - - break; - - case OMAP_ECC_BCH16_CODE_HW: - pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - nand_chip->ecc.bytes = 26; - nand_chip->ecc.strength = 16; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.read_page = omap_read_page_bch; - nand_chip->ecc.write_page = omap_write_page_bch; - nand_chip->ecc.write_subpage = omap_write_subpage_bch; - mtd_set_ooblayout(mtd, &omap_ooblayout_ops); - oobbytes_per_step = nand_chip->ecc.bytes; - - err = elm_config(info->elm_dev, BCH16_ECC, - mtd->writesize / nand_chip->ecc.size, - nand_chip->ecc.size, nand_chip->ecc.bytes); - if (err < 0) - goto return_error; - - break; - default: - dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n"); - err = -EINVAL; - goto return_error; - } - - /* check if NAND device's OOB is enough to store ECC signatures */ - min_oobbytes += (oobbytes_per_step * - (mtd->writesize / nand_chip->ecc.size)); - if (mtd->oobsize < min_oobbytes) { - dev_err(&info->pdev->dev, - "not enough OOB bytes required = %d, available=%d\n", - min_oobbytes, mtd->oobsize); - err = -EINVAL; - goto return_error; - } - -scan_tail: - /* second phase scan */ - err = nand_scan_tail(mtd); - if (err) - goto return_error; - - err = mtd_device_register(mtd, NULL, 0); - if (err) - goto return_error; - - platform_set_drvdata(pdev, mtd); - - return 0; - -return_error: - if (!IS_ERR_OR_NULL(info->dma)) - dma_release_channel(info->dma); - if (nand_chip->ecc.priv) { - nand_bch_free(nand_chip->ecc.priv); - nand_chip->ecc.priv = NULL; - } - return err; -} - -static int omap_nand_remove(struct platform_device *pdev) -{ - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct omap_nand_info *info = mtd_to_omap(mtd); - if (nand_chip->ecc.priv) { - nand_bch_free(nand_chip->ecc.priv); - nand_chip->ecc.priv = NULL; - } - if (info->dma) - dma_release_channel(info->dma); - nand_release(mtd); - return 0; -} - -static const struct of_device_id omap_nand_ids[] = { - { .compatible = "ti,omap2-nand", }, - {}, -}; -MODULE_DEVICE_TABLE(of, omap_nand_ids); - -static struct platform_driver omap_nand_driver = { - .probe = omap_nand_probe, - .remove = omap_nand_remove, - .driver = { - .name = DRIVER_NAME, - .of_match_table = of_match_ptr(omap_nand_ids), - }, -}; - -module_platform_driver(omap_nand_driver); - -MODULE_ALIAS("platform:" DRIVER_NAME); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/omap_elm.c deleted file mode 100644 index a3f32f939cc1..000000000000 --- a/drivers/mtd/nand/omap_elm.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * Error Location Module - * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#define DRIVER_NAME "omap-elm" - -#include -#include -#include -#include -#include -#include -#include -#include - -#define ELM_SYSCONFIG 0x010 -#define ELM_IRQSTATUS 0x018 -#define ELM_IRQENABLE 0x01c -#define ELM_LOCATION_CONFIG 0x020 -#define ELM_PAGE_CTRL 0x080 -#define ELM_SYNDROME_FRAGMENT_0 0x400 -#define ELM_SYNDROME_FRAGMENT_1 0x404 -#define ELM_SYNDROME_FRAGMENT_2 0x408 -#define ELM_SYNDROME_FRAGMENT_3 0x40c -#define ELM_SYNDROME_FRAGMENT_4 0x410 -#define ELM_SYNDROME_FRAGMENT_5 0x414 -#define ELM_SYNDROME_FRAGMENT_6 0x418 -#define ELM_LOCATION_STATUS 0x800 -#define ELM_ERROR_LOCATION_0 0x880 - -/* ELM Interrupt Status Register */ -#define INTR_STATUS_PAGE_VALID BIT(8) - -/* ELM Interrupt Enable Register */ -#define INTR_EN_PAGE_MASK BIT(8) - -/* ELM Location Configuration Register */ -#define ECC_BCH_LEVEL_MASK 0x3 - -/* ELM syndrome */ -#define ELM_SYNDROME_VALID BIT(16) - -/* ELM_LOCATION_STATUS Register */ -#define ECC_CORRECTABLE_MASK BIT(8) -#define ECC_NB_ERRORS_MASK 0x1f - -/* ELM_ERROR_LOCATION_0-15 Registers */ -#define ECC_ERROR_LOCATION_MASK 0x1fff - -#define ELM_ECC_SIZE 0x7ff - -#define SYNDROME_FRAGMENT_REG_SIZE 0x40 -#define ERROR_LOCATION_SIZE 0x100 - -struct elm_registers { - u32 elm_irqenable; - u32 elm_sysconfig; - u32 elm_location_config; - u32 elm_page_ctrl; - u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX]; - u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX]; - u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX]; - u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX]; - u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX]; - u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX]; - u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX]; -}; - -struct elm_info { - struct device *dev; - void __iomem *elm_base; - struct completion elm_completion; - struct list_head list; - enum bch_ecc bch_type; - struct elm_registers elm_regs; - int ecc_steps; - int ecc_syndrome_size; -}; - -static LIST_HEAD(elm_devices); - -static void elm_write_reg(struct elm_info *info, int offset, u32 val) -{ - writel(val, info->elm_base + offset); -} - -static u32 elm_read_reg(struct elm_info *info, int offset) -{ - return readl(info->elm_base + offset); -} - -/** - * elm_config - Configure ELM module - * @dev: ELM device - * @bch_type: Type of BCH ecc - */ -int elm_config(struct device *dev, enum bch_ecc bch_type, - int ecc_steps, int ecc_step_size, int ecc_syndrome_size) -{ - u32 reg_val; - struct elm_info *info = dev_get_drvdata(dev); - - if (!info) { - dev_err(dev, "Unable to configure elm - device not probed?\n"); - return -EPROBE_DEFER; - } - /* ELM cannot detect ECC errors for chunks > 1KB */ - if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) { - dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size); - return -EINVAL; - } - /* ELM support 8 error syndrome process */ - if (ecc_steps > ERROR_VECTOR_MAX) { - dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps); - return -EINVAL; - } - - reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16); - elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val); - info->bch_type = bch_type; - info->ecc_steps = ecc_steps; - info->ecc_syndrome_size = ecc_syndrome_size; - - return 0; -} -EXPORT_SYMBOL(elm_config); - -/** - * elm_configure_page_mode - Enable/Disable page mode - * @info: elm info - * @index: index number of syndrome fragment vector - * @enable: enable/disable flag for page mode - * - * Enable page mode for syndrome fragment index - */ -static void elm_configure_page_mode(struct elm_info *info, int index, - bool enable) -{ - u32 reg_val; - - reg_val = elm_read_reg(info, ELM_PAGE_CTRL); - if (enable) - reg_val |= BIT(index); /* enable page mode */ - else - reg_val &= ~BIT(index); /* disable page mode */ - - elm_write_reg(info, ELM_PAGE_CTRL, reg_val); -} - -/** - * elm_load_syndrome - Load ELM syndrome reg - * @info: elm info - * @err_vec: elm error vectors - * @ecc: buffer with calculated ecc - * - * Load syndrome fragment registers with calculated ecc in reverse order. - */ -static void elm_load_syndrome(struct elm_info *info, - struct elm_errorvec *err_vec, u8 *ecc) -{ - int i, offset; - u32 val; - - for (i = 0; i < info->ecc_steps; i++) { - - /* Check error reported */ - if (err_vec[i].error_reported) { - elm_configure_page_mode(info, i, true); - offset = ELM_SYNDROME_FRAGMENT_0 + - SYNDROME_FRAGMENT_REG_SIZE * i; - switch (info->bch_type) { - case BCH8_ECC: - /* syndrome fragment 0 = ecc[9-12B] */ - val = cpu_to_be32(*(u32 *) &ecc[9]); - elm_write_reg(info, offset, val); - - /* syndrome fragment 1 = ecc[5-8B] */ - offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[5]); - elm_write_reg(info, offset, val); - - /* syndrome fragment 2 = ecc[1-4B] */ - offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[1]); - elm_write_reg(info, offset, val); - - /* syndrome fragment 3 = ecc[0B] */ - offset += 4; - val = ecc[0]; - elm_write_reg(info, offset, val); - break; - case BCH4_ECC: - /* syndrome fragment 0 = ecc[20-52b] bits */ - val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) | - ((ecc[2] & 0xf) << 28); - elm_write_reg(info, offset, val); - - /* syndrome fragment 1 = ecc[0-20b] bits */ - offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; - elm_write_reg(info, offset, val); - break; - case BCH16_ECC: - val = cpu_to_be32(*(u32 *) &ecc[22]); - elm_write_reg(info, offset, val); - offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[18]); - elm_write_reg(info, offset, val); - offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[14]); - elm_write_reg(info, offset, val); - offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[10]); - elm_write_reg(info, offset, val); - offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[6]); - elm_write_reg(info, offset, val); - offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[2]); - elm_write_reg(info, offset, val); - offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16; - elm_write_reg(info, offset, val); - break; - default: - pr_err("invalid config bch_type\n"); - } - } - - /* Update ecc pointer with ecc byte size */ - ecc += info->ecc_syndrome_size; - } -} - -/** - * elm_start_processing - start elm syndrome processing - * @info: elm info - * @err_vec: elm error vectors - * - * Set syndrome valid bit for syndrome fragment registers for which - * elm syndrome fragment registers are loaded. This enables elm module - * to start processing syndrome vectors. - */ -static void elm_start_processing(struct elm_info *info, - struct elm_errorvec *err_vec) -{ - int i, offset; - u32 reg_val; - - /* - * Set syndrome vector valid, so that ELM module - * will process it for vectors error is reported - */ - for (i = 0; i < info->ecc_steps; i++) { - if (err_vec[i].error_reported) { - offset = ELM_SYNDROME_FRAGMENT_6 + - SYNDROME_FRAGMENT_REG_SIZE * i; - reg_val = elm_read_reg(info, offset); - reg_val |= ELM_SYNDROME_VALID; - elm_write_reg(info, offset, reg_val); - } - } -} - -/** - * elm_error_correction - locate correctable error position - * @info: elm info - * @err_vec: elm error vectors - * - * On completion of processing by elm module, error location status - * register updated with correctable/uncorrectable error information. - * In case of correctable errors, number of errors located from - * elm location status register & read the positions from - * elm error location register. - */ -static void elm_error_correction(struct elm_info *info, - struct elm_errorvec *err_vec) -{ - int i, j, errors = 0; - int offset; - u32 reg_val; - - for (i = 0; i < info->ecc_steps; i++) { - - /* Check error reported */ - if (err_vec[i].error_reported) { - offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i; - reg_val = elm_read_reg(info, offset); - - /* Check correctable error or not */ - if (reg_val & ECC_CORRECTABLE_MASK) { - offset = ELM_ERROR_LOCATION_0 + - ERROR_LOCATION_SIZE * i; - - /* Read count of correctable errors */ - err_vec[i].error_count = reg_val & - ECC_NB_ERRORS_MASK; - - /* Update the error locations in error vector */ - for (j = 0; j < err_vec[i].error_count; j++) { - - reg_val = elm_read_reg(info, offset); - err_vec[i].error_loc[j] = reg_val & - ECC_ERROR_LOCATION_MASK; - - /* Update error location register */ - offset += 4; - } - - errors += err_vec[i].error_count; - } else { - err_vec[i].error_uncorrectable = true; - } - - /* Clearing interrupts for processed error vectors */ - elm_write_reg(info, ELM_IRQSTATUS, BIT(i)); - - /* Disable page mode */ - elm_configure_page_mode(info, i, false); - } - } -} - -/** - * elm_decode_bch_error_page - Locate error position - * @dev: device pointer - * @ecc_calc: calculated ECC bytes from GPMC - * @err_vec: elm error vectors - * - * Called with one or more error reported vectors & vectors with - * error reported is updated in err_vec[].error_reported - */ -void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, - struct elm_errorvec *err_vec) -{ - struct elm_info *info = dev_get_drvdata(dev); - u32 reg_val; - - /* Enable page mode interrupt */ - reg_val = elm_read_reg(info, ELM_IRQSTATUS); - elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID); - elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK); - - /* Load valid ecc byte to syndrome fragment register */ - elm_load_syndrome(info, err_vec, ecc_calc); - - /* Enable syndrome processing for which syndrome fragment is updated */ - elm_start_processing(info, err_vec); - - /* Wait for ELM module to finish locating error correction */ - wait_for_completion(&info->elm_completion); - - /* Disable page mode interrupt */ - reg_val = elm_read_reg(info, ELM_IRQENABLE); - elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK); - elm_error_correction(info, err_vec); -} -EXPORT_SYMBOL(elm_decode_bch_error_page); - -static irqreturn_t elm_isr(int this_irq, void *dev_id) -{ - u32 reg_val; - struct elm_info *info = dev_id; - - reg_val = elm_read_reg(info, ELM_IRQSTATUS); - - /* All error vectors processed */ - if (reg_val & INTR_STATUS_PAGE_VALID) { - elm_write_reg(info, ELM_IRQSTATUS, - reg_val & INTR_STATUS_PAGE_VALID); - complete(&info->elm_completion); - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -static int elm_probe(struct platform_device *pdev) -{ - int ret = 0; - struct resource *res, *irq; - struct elm_info *info; - - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->dev = &pdev->dev; - - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) { - dev_err(&pdev->dev, "no irq resource defined\n"); - return -ENODEV; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->elm_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(info->elm_base)) - return PTR_ERR(info->elm_base); - - ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, - pdev->name, info); - if (ret) { - dev_err(&pdev->dev, "failure requesting %pr\n", irq); - return ret; - } - - pm_runtime_enable(&pdev->dev); - if (pm_runtime_get_sync(&pdev->dev) < 0) { - ret = -EINVAL; - pm_runtime_disable(&pdev->dev); - dev_err(&pdev->dev, "can't enable clock\n"); - return ret; - } - - init_completion(&info->elm_completion); - INIT_LIST_HEAD(&info->list); - list_add(&info->list, &elm_devices); - platform_set_drvdata(pdev, info); - return ret; -} - -static int elm_remove(struct platform_device *pdev) -{ - pm_runtime_put_sync(&pdev->dev); - pm_runtime_disable(&pdev->dev); - return 0; -} - -#ifdef CONFIG_PM_SLEEP -/** - * elm_context_save - * saves ELM configurations to preserve them across Hardware powered-down - */ -static int elm_context_save(struct elm_info *info) -{ - struct elm_registers *regs = &info->elm_regs; - enum bch_ecc bch_type = info->bch_type; - u32 offset = 0, i; - - regs->elm_irqenable = elm_read_reg(info, ELM_IRQENABLE); - regs->elm_sysconfig = elm_read_reg(info, ELM_SYSCONFIG); - regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG); - regs->elm_page_ctrl = elm_read_reg(info, ELM_PAGE_CTRL); - for (i = 0; i < ERROR_VECTOR_MAX; i++) { - offset = i * SYNDROME_FRAGMENT_REG_SIZE; - switch (bch_type) { - case BCH16_ECC: - regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, - ELM_SYNDROME_FRAGMENT_6 + offset); - regs->elm_syndrome_fragment_5[i] = elm_read_reg(info, - ELM_SYNDROME_FRAGMENT_5 + offset); - regs->elm_syndrome_fragment_4[i] = elm_read_reg(info, - ELM_SYNDROME_FRAGMENT_4 + offset); - case BCH8_ECC: - regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, - ELM_SYNDROME_FRAGMENT_3 + offset); - regs->elm_syndrome_fragment_2[i] = elm_read_reg(info, - ELM_SYNDROME_FRAGMENT_2 + offset); - case BCH4_ECC: - regs->elm_syndrome_fragment_1[i] = elm_read_reg(info, - ELM_SYNDROME_FRAGMENT_1 + offset); - regs->elm_syndrome_fragment_0[i] = elm_read_reg(info, - ELM_SYNDROME_FRAGMENT_0 + offset); - break; - default: - return -EINVAL; - } - /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs - * to be saved for all BCH schemes*/ - regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, - ELM_SYNDROME_FRAGMENT_6 + offset); - } - return 0; -} - -/** - * elm_context_restore - * writes configurations saved duing power-down back into ELM registers - */ -static int elm_context_restore(struct elm_info *info) -{ - struct elm_registers *regs = &info->elm_regs; - enum bch_ecc bch_type = info->bch_type; - u32 offset = 0, i; - - elm_write_reg(info, ELM_IRQENABLE, regs->elm_irqenable); - elm_write_reg(info, ELM_SYSCONFIG, regs->elm_sysconfig); - elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config); - elm_write_reg(info, ELM_PAGE_CTRL, regs->elm_page_ctrl); - for (i = 0; i < ERROR_VECTOR_MAX; i++) { - offset = i * SYNDROME_FRAGMENT_REG_SIZE; - switch (bch_type) { - case BCH16_ECC: - elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, - regs->elm_syndrome_fragment_6[i]); - elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset, - regs->elm_syndrome_fragment_5[i]); - elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset, - regs->elm_syndrome_fragment_4[i]); - case BCH8_ECC: - elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, - regs->elm_syndrome_fragment_3[i]); - elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset, - regs->elm_syndrome_fragment_2[i]); - case BCH4_ECC: - elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset, - regs->elm_syndrome_fragment_1[i]); - elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset, - regs->elm_syndrome_fragment_0[i]); - break; - default: - return -EINVAL; - } - /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */ - elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, - regs->elm_syndrome_fragment_6[i] & - ELM_SYNDROME_VALID); - } - return 0; -} - -static int elm_suspend(struct device *dev) -{ - struct elm_info *info = dev_get_drvdata(dev); - elm_context_save(info); - pm_runtime_put_sync(dev); - return 0; -} - -static int elm_resume(struct device *dev) -{ - struct elm_info *info = dev_get_drvdata(dev); - pm_runtime_get_sync(dev); - elm_context_restore(info); - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); - -#ifdef CONFIG_OF -static const struct of_device_id elm_of_match[] = { - { .compatible = "ti,am3352-elm" }, - {}, -}; -MODULE_DEVICE_TABLE(of, elm_of_match); -#endif - -static struct platform_driver elm_driver = { - .driver = { - .name = DRIVER_NAME, - .of_match_table = of_match_ptr(elm_of_match), - .pm = &elm_pm_ops, - }, - .probe = elm_probe, - .remove = elm_remove, -}; - -module_platform_driver(elm_driver); - -MODULE_DESCRIPTION("ELM driver for BCH error correction"); -MODULE_AUTHOR("Texas Instruments"); -MODULE_ALIAS("platform:" DRIVER_NAME); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c deleted file mode 100644 index 7825fd3ce66b..000000000000 --- a/drivers/mtd/nand/orion_nand.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * NAND support for Marvell Orion SoC platforms - * - * Tzachi Perelstein - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct orion_nand_info { - struct nand_chip chip; - struct clk *clk; -}; - -static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *nc = mtd_to_nand(mtd); - struct orion_nand_data *board = nand_get_controller_data(nc); - u32 offs; - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - offs = (1 << board->cle); - else if (ctrl & NAND_ALE) - offs = (1 << board->ale); - else - return; - - if (nc->options & NAND_BUSWIDTH_16) - offs <<= 1; - - writeb(cmd, nc->IO_ADDR_W + offs); -} - -static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - void __iomem *io_base = chip->IO_ADDR_R; -#if __LINUX_ARM_ARCH__ >= 5 - uint64_t *buf64; -#endif - int i = 0; - - while (len && (unsigned long)buf & 7) { - *buf++ = readb(io_base); - len--; - } -#if __LINUX_ARM_ARCH__ >= 5 - buf64 = (uint64_t *)buf; - while (i < len/8) { - /* - * Since GCC has no proper constraint (PR 43518) - * force x variable to r2/r3 registers as ldrd instruction - * requires first register to be even. - */ - register uint64_t x asm ("r2"); - - asm volatile ("ldrd\t%0, [%1]" : "=&r" (x) : "r" (io_base)); - buf64[i++] = x; - } - i *= 8; -#else - readsl(io_base, buf, len/4); - i = len / 4 * 4; -#endif - while (i < len) - buf[i++] = readb(io_base); -} - -static int __init orion_nand_probe(struct platform_device *pdev) -{ - struct orion_nand_info *info; - struct mtd_info *mtd; - struct nand_chip *nc; - struct orion_nand_data *board; - struct resource *res; - void __iomem *io_base; - int ret = 0; - u32 val = 0; - - info = devm_kzalloc(&pdev->dev, - sizeof(struct orion_nand_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - nc = &info->chip; - mtd = nand_to_mtd(nc); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - io_base = devm_ioremap_resource(&pdev->dev, res); - - if (IS_ERR(io_base)) - return PTR_ERR(io_base); - - if (pdev->dev.of_node) { - board = devm_kzalloc(&pdev->dev, sizeof(struct orion_nand_data), - GFP_KERNEL); - if (!board) - return -ENOMEM; - if (!of_property_read_u32(pdev->dev.of_node, "cle", &val)) - board->cle = (u8)val; - else - board->cle = 0; - if (!of_property_read_u32(pdev->dev.of_node, "ale", &val)) - board->ale = (u8)val; - else - board->ale = 1; - if (!of_property_read_u32(pdev->dev.of_node, - "bank-width", &val)) - board->width = (u8)val * 8; - else - board->width = 8; - if (!of_property_read_u32(pdev->dev.of_node, - "chip-delay", &val)) - board->chip_delay = (u8)val; - } else { - board = dev_get_platdata(&pdev->dev); - } - - mtd->dev.parent = &pdev->dev; - - nand_set_controller_data(nc, board); - nand_set_flash_node(nc, pdev->dev.of_node); - nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; - nc->cmd_ctrl = orion_nand_cmd_ctrl; - nc->read_buf = orion_nand_read_buf; - nc->ecc.mode = NAND_ECC_SOFT; - nc->ecc.algo = NAND_ECC_HAMMING; - - if (board->chip_delay) - nc->chip_delay = board->chip_delay; - - WARN(board->width > 16, - "%d bit bus width out of range", - board->width); - - if (board->width == 16) - nc->options |= NAND_BUSWIDTH_16; - - if (board->dev_ready) - nc->dev_ready = board->dev_ready; - - platform_set_drvdata(pdev, info); - - /* Not all platforms can gate the clock, so it is not - an error if the clock does not exists. */ - info->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - if (ret == -ENOENT) { - info->clk = NULL; - } else { - dev_err(&pdev->dev, "failed to get clock!\n"); - return ret; - } - } - - ret = clk_prepare_enable(info->clk); - if (ret) { - dev_err(&pdev->dev, "failed to prepare clock!\n"); - return ret; - } - - ret = nand_scan(mtd, 1); - if (ret) - goto no_dev; - - mtd->name = "orion_nand"; - ret = mtd_device_register(mtd, board->parts, board->nr_parts); - if (ret) { - nand_release(mtd); - goto no_dev; - } - - return 0; - -no_dev: - clk_disable_unprepare(info->clk); - return ret; -} - -static int orion_nand_remove(struct platform_device *pdev) -{ - struct orion_nand_info *info = platform_get_drvdata(pdev); - struct nand_chip *chip = &info->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - - nand_release(mtd); - - clk_disable_unprepare(info->clk); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id orion_nand_of_match_table[] = { - { .compatible = "marvell,orion-nand", }, - {}, -}; -MODULE_DEVICE_TABLE(of, orion_nand_of_match_table); -#endif - -static struct platform_driver orion_nand_driver = { - .remove = orion_nand_remove, - .driver = { - .name = "orion_nand", - .of_match_table = of_match_ptr(orion_nand_of_match_table), - }, -}; - -module_platform_driver_probe(orion_nand_driver, orion_nand_probe); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Tzachi Perelstein"); -MODULE_DESCRIPTION("NAND glue for Orion platforms"); -MODULE_ALIAS("platform:orion_nand"); diff --git a/drivers/mtd/nand/oxnas_nand.c b/drivers/mtd/nand/oxnas_nand.c deleted file mode 100644 index d649d5944826..000000000000 --- a/drivers/mtd/nand/oxnas_nand.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Oxford Semiconductor OXNAS NAND driver - - * Copyright (C) 2016 Neil Armstrong - * Heavily based on plat_nand.c : - * Author: Vitaly Wool - * Copyright (C) 2013 Ma Haijun - * Copyright (C) 2012 John Crispin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Nand commands */ -#define OXNAS_NAND_CMD_ALE BIT(18) -#define OXNAS_NAND_CMD_CLE BIT(19) - -#define OXNAS_NAND_MAX_CHIPS 1 - -struct oxnas_nand_ctrl { - struct nand_hw_control base; - void __iomem *io_base; - struct clk *clk; - struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS]; -}; - -static uint8_t oxnas_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); - - return readb(oxnas->io_base); -} - -static void oxnas_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); - - ioread8_rep(oxnas->io_base, buf, len); -} - -static void oxnas_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); - - iowrite8_rep(oxnas->io_base, buf, len); -} - -/* Single CS command control */ -static void oxnas_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); - - if (ctrl & NAND_CLE) - writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_CLE); - else if (ctrl & NAND_ALE) - writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_ALE); -} - -/* - * Probe for the NAND device. - */ -static int oxnas_nand_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct device_node *nand_np; - struct oxnas_nand_ctrl *oxnas; - struct nand_chip *chip; - struct mtd_info *mtd; - struct resource *res; - int nchips = 0; - int count = 0; - int err = 0; - - /* Allocate memory for the device structure (and zero it) */ - oxnas = devm_kzalloc(&pdev->dev, sizeof(*oxnas), - GFP_KERNEL); - if (!oxnas) - return -ENOMEM; - - nand_hw_control_init(&oxnas->base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - oxnas->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(oxnas->io_base)) - return PTR_ERR(oxnas->io_base); - - oxnas->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(oxnas->clk)) - oxnas->clk = NULL; - - /* Only a single chip node is supported */ - count = of_get_child_count(np); - if (count > 1) - return -EINVAL; - - err = clk_prepare_enable(oxnas->clk); - if (err) - return err; - - device_reset_optional(&pdev->dev); - - for_each_child_of_node(np, nand_np) { - chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), - GFP_KERNEL); - if (!chip) { - err = -ENOMEM; - goto err_clk_unprepare; - } - - chip->controller = &oxnas->base; - - nand_set_flash_node(chip, nand_np); - nand_set_controller_data(chip, oxnas); - - mtd = nand_to_mtd(chip); - mtd->dev.parent = &pdev->dev; - mtd->priv = chip; - - chip->cmd_ctrl = oxnas_nand_cmd_ctrl; - chip->read_buf = oxnas_nand_read_buf; - chip->read_byte = oxnas_nand_read_byte; - chip->write_buf = oxnas_nand_write_buf; - chip->chip_delay = 30; - - /* Scan to find existence of the device */ - err = nand_scan(mtd, 1); - if (err) - goto err_clk_unprepare; - - err = mtd_device_register(mtd, NULL, 0); - if (err) { - nand_release(mtd); - goto err_clk_unprepare; - } - - oxnas->chips[nchips] = chip; - ++nchips; - } - - /* Exit if no chips found */ - if (!nchips) { - err = -ENODEV; - goto err_clk_unprepare; - } - - platform_set_drvdata(pdev, oxnas); - - return 0; - -err_clk_unprepare: - clk_disable_unprepare(oxnas->clk); - return err; -} - -static int oxnas_nand_remove(struct platform_device *pdev) -{ - struct oxnas_nand_ctrl *oxnas = platform_get_drvdata(pdev); - - if (oxnas->chips[0]) - nand_release(nand_to_mtd(oxnas->chips[0])); - - clk_disable_unprepare(oxnas->clk); - - return 0; -} - -static const struct of_device_id oxnas_nand_match[] = { - { .compatible = "oxsemi,ox820-nand" }, - {}, -}; -MODULE_DEVICE_TABLE(of, oxnas_nand_match); - -static struct platform_driver oxnas_nand_driver = { - .probe = oxnas_nand_probe, - .remove = oxnas_nand_remove, - .driver = { - .name = "oxnas_nand", - .of_match_table = oxnas_nand_match, - }, -}; - -module_platform_driver(oxnas_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Neil Armstrong "); -MODULE_DESCRIPTION("Oxnas NAND driver"); -MODULE_ALIAS("platform:oxnas_nand"); diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c deleted file mode 100644 index a47a7e4bd25a..000000000000 --- a/drivers/mtd/nand/pasemi_nand.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2006-2007 PA Semi, Inc - * - * Author: Egor Martovetsky - * Maintained by: Olof Johansson - * - * Driver for the PWRficient onchip NAND flash interface - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define LBICTRL_LPCCTL_NR 0x00004000 -#define CLE_PIN_CTL 15 -#define ALE_PIN_CTL 14 - -static unsigned int lpcctl; -static struct mtd_info *pasemi_nand_mtd; -static const char driver_name[] = "pasemi-nand"; - -static void pasemi_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - while (len > 0x800) { - memcpy_fromio(buf, chip->IO_ADDR_R, 0x800); - buf += 0x800; - len -= 0x800; - } - memcpy_fromio(buf, chip->IO_ADDR_R, len); -} - -static void pasemi_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - while (len > 0x800) { - memcpy_toio(chip->IO_ADDR_R, buf, 0x800); - buf += 0x800; - len -= 0x800; - } - memcpy_toio(chip->IO_ADDR_R, buf, len); -} - -static void pasemi_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - out_8(chip->IO_ADDR_W + (1 << CLE_PIN_CTL), cmd); - else - out_8(chip->IO_ADDR_W + (1 << ALE_PIN_CTL), cmd); - - /* Push out posted writes */ - eieio(); - inl(lpcctl); -} - -int pasemi_device_ready(struct mtd_info *mtd) -{ - return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); -} - -static int pasemi_nand_probe(struct platform_device *ofdev) -{ - struct device *dev = &ofdev->dev; - struct pci_dev *pdev; - struct device_node *np = dev->of_node; - struct resource res; - struct nand_chip *chip; - int err = 0; - - err = of_address_to_resource(np, 0, &res); - - if (err) - return -EINVAL; - - /* We only support one device at the moment */ - if (pasemi_nand_mtd) - return -ENODEV; - - dev_dbg(dev, "pasemi_nand at %pR\n", &res); - - /* Allocate memory for MTD device structure and private data */ - chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); - if (!chip) { - err = -ENOMEM; - goto out; - } - - pasemi_nand_mtd = nand_to_mtd(chip); - - /* Link the private data with the MTD structure */ - pasemi_nand_mtd->dev.parent = dev; - - chip->IO_ADDR_R = of_iomap(np, 0); - chip->IO_ADDR_W = chip->IO_ADDR_R; - - if (!chip->IO_ADDR_R) { - err = -EIO; - goto out_mtd; - } - - pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa008, NULL); - if (!pdev) { - err = -ENODEV; - goto out_ior; - } - - lpcctl = pci_resource_start(pdev, 0); - pci_dev_put(pdev); - - if (!request_region(lpcctl, 4, driver_name)) { - err = -EBUSY; - goto out_ior; - } - - chip->cmd_ctrl = pasemi_hwcontrol; - chip->dev_ready = pasemi_device_ready; - chip->read_buf = pasemi_read_buf; - chip->write_buf = pasemi_write_buf; - chip->chip_delay = 0; - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - - /* Enable the following for a flash based bad block table */ - chip->bbt_options = NAND_BBT_USE_FLASH; - - /* Scan to find existence of the device */ - err = nand_scan(pasemi_nand_mtd, 1); - if (err) - goto out_lpc; - - if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) { - dev_err(dev, "Unable to register MTD device\n"); - err = -ENODEV; - goto out_lpc; - } - - dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res, - lpcctl); - - return 0; - - out_lpc: - release_region(lpcctl, 4); - out_ior: - iounmap(chip->IO_ADDR_R); - out_mtd: - kfree(chip); - out: - return err; -} - -static int pasemi_nand_remove(struct platform_device *ofdev) -{ - struct nand_chip *chip; - - if (!pasemi_nand_mtd) - return 0; - - chip = mtd_to_nand(pasemi_nand_mtd); - - /* Release resources, unregister device */ - nand_release(pasemi_nand_mtd); - - release_region(lpcctl, 4); - - iounmap(chip->IO_ADDR_R); - - /* Free the MTD device structure */ - kfree(chip); - - pasemi_nand_mtd = NULL; - - return 0; -} - -static const struct of_device_id pasemi_nand_match[] = -{ - { - .compatible = "pasemi,localbus-nand", - }, - {}, -}; - -MODULE_DEVICE_TABLE(of, pasemi_nand_match); - -static struct platform_driver pasemi_nand_driver = -{ - .driver = { - .name = driver_name, - .of_match_table = pasemi_nand_match, - }, - .probe = pasemi_nand_probe, - .remove = pasemi_nand_remove, -}; - -module_platform_driver(pasemi_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Egor Martovetsky "); -MODULE_DESCRIPTION("NAND flash interface driver for PA Semi PWRficient"); diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c deleted file mode 100644 index 925a1323604d..000000000000 --- a/drivers/mtd/nand/plat_nand.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Generic NAND driver - * - * Author: Vitaly Wool - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -struct plat_nand_data { - struct nand_chip chip; - void __iomem *io_base; -}; - -/* - * Probe for the NAND device. - */ -static int plat_nand_probe(struct platform_device *pdev) -{ - struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); - struct plat_nand_data *data; - struct mtd_info *mtd; - struct resource *res; - const char **part_types; - int err = 0; - - if (!pdata) { - dev_err(&pdev->dev, "platform_nand_data is missing\n"); - return -EINVAL; - } - - if (pdata->chip.nr_chips < 1) { - dev_err(&pdev->dev, "invalid number of chips specified\n"); - return -EINVAL; - } - - /* Allocate memory for the device structure (and zero it) */ - data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(data->io_base)) - return PTR_ERR(data->io_base); - - nand_set_flash_node(&data->chip, pdev->dev.of_node); - mtd = nand_to_mtd(&data->chip); - mtd->dev.parent = &pdev->dev; - - data->chip.IO_ADDR_R = data->io_base; - data->chip.IO_ADDR_W = data->io_base; - data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; - data->chip.dev_ready = pdata->ctrl.dev_ready; - data->chip.select_chip = pdata->ctrl.select_chip; - data->chip.write_buf = pdata->ctrl.write_buf; - data->chip.read_buf = pdata->ctrl.read_buf; - data->chip.read_byte = pdata->ctrl.read_byte; - data->chip.chip_delay = pdata->chip.chip_delay; - data->chip.options |= pdata->chip.options; - data->chip.bbt_options |= pdata->chip.bbt_options; - - data->chip.ecc.hwctl = pdata->ctrl.hwcontrol; - data->chip.ecc.mode = NAND_ECC_SOFT; - data->chip.ecc.algo = NAND_ECC_HAMMING; - - platform_set_drvdata(pdev, data); - - /* Handle any platform specific setup */ - if (pdata->ctrl.probe) { - err = pdata->ctrl.probe(pdev); - if (err) - goto out; - } - - /* Scan to find existence of the device */ - err = nand_scan(mtd, pdata->chip.nr_chips); - if (err) - goto out; - - part_types = pdata->chip.part_probe_types; - - err = mtd_device_parse_register(mtd, part_types, NULL, - pdata->chip.partitions, - pdata->chip.nr_partitions); - - if (!err) - return err; - - nand_release(mtd); -out: - if (pdata->ctrl.remove) - pdata->ctrl.remove(pdev); - return err; -} - -/* - * Remove a NAND device. - */ -static int plat_nand_remove(struct platform_device *pdev) -{ - struct plat_nand_data *data = platform_get_drvdata(pdev); - struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); - - nand_release(nand_to_mtd(&data->chip)); - if (pdata->ctrl.remove) - pdata->ctrl.remove(pdev); - - return 0; -} - -static const struct of_device_id plat_nand_match[] = { - { .compatible = "gen_nand" }, - {}, -}; -MODULE_DEVICE_TABLE(of, plat_nand_match); - -static struct platform_driver plat_nand_driver = { - .probe = plat_nand_probe, - .remove = plat_nand_remove, - .driver = { - .name = "gen_nand", - .of_match_table = plat_nand_match, - }, -}; - -module_platform_driver(plat_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Vitaly Wool"); -MODULE_DESCRIPTION("Simple generic NAND driver"); -MODULE_ALIAS("platform:gen_nand"); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c deleted file mode 100644 index d75f30263d21..000000000000 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ /dev/null @@ -1,2103 +0,0 @@ -/* - * Copyright © 2005 Intel Corporation - * Copyright © 2006 Marvell International Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * See Documentation/mtd/nand/pxa3xx-nand.txt for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200) -#define NAND_STOP_DELAY msecs_to_jiffies(40) -#define PAGE_CHUNK_SIZE (2048) - -/* - * Define a buffer size for the initial command that detects the flash device: - * STATUS, READID and PARAM. - * ONFI param page is 256 bytes, and there are three redundant copies - * to be read. JEDEC param page is 512 bytes, and there are also three - * redundant copies to be read. - * Hence this buffer should be at least 512 x 3. Let's pick 2048. - */ -#define INIT_BUFFER_SIZE 2048 - -/* System control register and bit to enable NAND on some SoCs */ -#define GENCONF_SOC_DEVICE_MUX 0x208 -#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0) - -/* registers and bit definitions */ -#define NDCR (0x00) /* Control register */ -#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ -#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ -#define NDSR (0x14) /* Status Register */ -#define NDPCR (0x18) /* Page Count Register */ -#define NDBDR0 (0x1C) /* Bad Block Register 0 */ -#define NDBDR1 (0x20) /* Bad Block Register 1 */ -#define NDECCCTRL (0x28) /* ECC control */ -#define NDDB (0x40) /* Data Buffer */ -#define NDCB0 (0x48) /* Command Buffer0 */ -#define NDCB1 (0x4C) /* Command Buffer1 */ -#define NDCB2 (0x50) /* Command Buffer2 */ - -#define NDCR_SPARE_EN (0x1 << 31) -#define NDCR_ECC_EN (0x1 << 30) -#define NDCR_DMA_EN (0x1 << 29) -#define NDCR_ND_RUN (0x1 << 28) -#define NDCR_DWIDTH_C (0x1 << 27) -#define NDCR_DWIDTH_M (0x1 << 26) -#define NDCR_PAGE_SZ (0x1 << 24) -#define NDCR_NCSX (0x1 << 23) -#define NDCR_ND_MODE (0x3 << 21) -#define NDCR_NAND_MODE (0x0) -#define NDCR_CLR_PG_CNT (0x1 << 20) -#define NFCV1_NDCR_ARB_CNTL (0x1 << 19) -#define NFCV2_NDCR_STOP_ON_UNCOR (0x1 << 19) -#define NDCR_RD_ID_CNT_MASK (0x7 << 16) -#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) - -#define NDCR_RA_START (0x1 << 15) -#define NDCR_PG_PER_BLK (0x1 << 14) -#define NDCR_ND_ARB_EN (0x1 << 12) -#define NDCR_INT_MASK (0xFFF) - -#define NDSR_MASK (0xfff) -#define NDSR_ERR_CNT_OFF (16) -#define NDSR_ERR_CNT_MASK (0x1f) -#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK) -#define NDSR_RDY (0x1 << 12) -#define NDSR_FLASH_RDY (0x1 << 11) -#define NDSR_CS0_PAGED (0x1 << 10) -#define NDSR_CS1_PAGED (0x1 << 9) -#define NDSR_CS0_CMDD (0x1 << 8) -#define NDSR_CS1_CMDD (0x1 << 7) -#define NDSR_CS0_BBD (0x1 << 6) -#define NDSR_CS1_BBD (0x1 << 5) -#define NDSR_UNCORERR (0x1 << 4) -#define NDSR_CORERR (0x1 << 3) -#define NDSR_WRDREQ (0x1 << 2) -#define NDSR_RDDREQ (0x1 << 1) -#define NDSR_WRCMDREQ (0x1) - -#define NDCB0_LEN_OVRD (0x1 << 28) -#define NDCB0_ST_ROW_EN (0x1 << 26) -#define NDCB0_AUTO_RS (0x1 << 25) -#define NDCB0_CSEL (0x1 << 24) -#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29) -#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK) -#define NDCB0_CMD_TYPE_MASK (0x7 << 21) -#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) -#define NDCB0_NC (0x1 << 20) -#define NDCB0_DBC (0x1 << 19) -#define NDCB0_ADDR_CYC_MASK (0x7 << 16) -#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) -#define NDCB0_CMD2_MASK (0xff << 8) -#define NDCB0_CMD1_MASK (0xff) -#define NDCB0_ADDR_CYC_SHIFT (16) - -#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */ -#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */ -#define EXT_CMD_TYPE_READ 4 /* Read */ -#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */ -#define EXT_CMD_TYPE_FINAL 3 /* Final command */ -#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ -#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */ - -/* - * This should be large enough to read 'ONFI' and 'JEDEC'. - * Let's use 7 bytes, which is the maximum ID count supported - * by the controller (see NDCR_RD_ID_CNT_MASK). - */ -#define READ_ID_BYTES 7 - -/* macros for registers read/write */ -#define nand_writel(info, off, val) \ - do { \ - dev_vdbg(&info->pdev->dev, \ - "%s():%d nand_writel(0x%x, 0x%04x)\n", \ - __func__, __LINE__, (val), (off)); \ - writel_relaxed((val), (info)->mmio_base + (off)); \ - } while (0) - -#define nand_readl(info, off) \ - ({ \ - unsigned int _v; \ - _v = readl_relaxed((info)->mmio_base + (off)); \ - dev_vdbg(&info->pdev->dev, \ - "%s():%d nand_readl(0x%04x) = 0x%x\n", \ - __func__, __LINE__, (off), _v); \ - _v; \ - }) - -/* error code and state */ -enum { - ERR_NONE = 0, - ERR_DMABUSERR = -1, - ERR_SENDCMD = -2, - ERR_UNCORERR = -3, - ERR_BBERR = -4, - ERR_CORERR = -5, -}; - -enum { - STATE_IDLE = 0, - STATE_PREPARED, - STATE_CMD_HANDLE, - STATE_DMA_READING, - STATE_DMA_WRITING, - STATE_DMA_DONE, - STATE_PIO_READING, - STATE_PIO_WRITING, - STATE_CMD_DONE, - STATE_READY, -}; - -enum pxa3xx_nand_variant { - PXA3XX_NAND_VARIANT_PXA, - PXA3XX_NAND_VARIANT_ARMADA370, - PXA3XX_NAND_VARIANT_ARMADA_8K, -}; - -struct pxa3xx_nand_host { - struct nand_chip chip; - void *info_data; - - /* page size of attached chip */ - int use_ecc; - int cs; - - /* calculated from pxa3xx_nand_flash data */ - unsigned int col_addr_cycles; - unsigned int row_addr_cycles; -}; - -struct pxa3xx_nand_info { - struct nand_hw_control controller; - struct platform_device *pdev; - - struct clk *clk; - void __iomem *mmio_base; - unsigned long mmio_phys; - struct completion cmd_complete, dev_ready; - - unsigned int buf_start; - unsigned int buf_count; - unsigned int buf_size; - unsigned int data_buff_pos; - unsigned int oob_buff_pos; - - /* DMA information */ - struct scatterlist sg; - enum dma_data_direction dma_dir; - struct dma_chan *dma_chan; - dma_cookie_t dma_cookie; - int drcmr_dat; - - unsigned char *data_buff; - unsigned char *oob_buff; - dma_addr_t data_buff_phys; - int data_dma_ch; - - struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; - unsigned int state; - - /* - * This driver supports NFCv1 (as found in PXA SoC) - * and NFCv2 (as found in Armada 370/XP SoC). - */ - enum pxa3xx_nand_variant variant; - - int cs; - int use_ecc; /* use HW ECC ? */ - int ecc_bch; /* using BCH ECC? */ - int use_dma; /* use DMA ? */ - int use_spare; /* use spare ? */ - int need_wait; - - /* Amount of real data per full chunk */ - unsigned int chunk_size; - - /* Amount of spare data per full chunk */ - unsigned int spare_size; - - /* Number of full chunks (i.e chunk_size + spare_size) */ - unsigned int nfullchunks; - - /* - * Total number of chunks. If equal to nfullchunks, then there - * are only full chunks. Otherwise, there is one last chunk of - * size (last_chunk_size + last_spare_size) - */ - unsigned int ntotalchunks; - - /* Amount of real data in the last chunk */ - unsigned int last_chunk_size; - - /* Amount of spare data in the last chunk */ - unsigned int last_spare_size; - - unsigned int ecc_size; - unsigned int ecc_err_cnt; - unsigned int max_bitflips; - int retcode; - - /* - * Variables only valid during command - * execution. step_chunk_size and step_spare_size is the - * amount of real data and spare data in the current - * chunk. cur_chunk is the current chunk being - * read/programmed. - */ - unsigned int step_chunk_size; - unsigned int step_spare_size; - unsigned int cur_chunk; - - /* cached register value */ - uint32_t reg_ndcr; - uint32_t ndtr0cs0; - uint32_t ndtr1cs0; - - /* generated NDCBx register values */ - uint32_t ndcb0; - uint32_t ndcb1; - uint32_t ndcb2; - uint32_t ndcb3; -}; - -static bool use_dma = 1; -module_param(use_dma, bool, 0444); -MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW"); - -struct pxa3xx_nand_timing { - unsigned int tCH; /* Enable signal hold time */ - unsigned int tCS; /* Enable signal setup time */ - unsigned int tWH; /* ND_nWE high duration */ - unsigned int tWP; /* ND_nWE pulse time */ - unsigned int tRH; /* ND_nRE high duration */ - unsigned int tRP; /* ND_nRE pulse width */ - unsigned int tR; /* ND_nWE high to ND_nRE low for read */ - unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ - unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ -}; - -struct pxa3xx_nand_flash { - uint32_t chip_id; - unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ - unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ - struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ -}; - -static struct pxa3xx_nand_timing timing[] = { - { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, - { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, - { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, - { 10, 35, 15, 25, 15, 25, 25000, 60, 10, }, -}; - -static struct pxa3xx_nand_flash builtin_flash_types[] = { - { 0x46ec, 16, 16, &timing[1] }, - { 0xdaec, 8, 8, &timing[1] }, - { 0xd7ec, 8, 8, &timing[1] }, - { 0xa12c, 8, 8, &timing[2] }, - { 0xb12c, 16, 16, &timing[2] }, - { 0xdc2c, 8, 8, &timing[2] }, - { 0xcc2c, 16, 16, &timing[2] }, - { 0xba20, 16, 16, &timing[3] }, -}; - -static int pxa3xx_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int nchunks = mtd->writesize / info->chunk_size; - - if (section >= nchunks) - return -ERANGE; - - oobregion->offset = ((info->ecc_size + info->spare_size) * section) + - info->spare_size; - oobregion->length = info->ecc_size; - - return 0; -} - -static int pxa3xx_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int nchunks = mtd->writesize / info->chunk_size; - - if (section >= nchunks) - return -ERANGE; - - if (!info->spare_size) - return 0; - - oobregion->offset = section * (info->ecc_size + info->spare_size); - oobregion->length = info->spare_size; - if (!section) { - /* - * Bootrom looks in bytes 0 & 5 for bad blocks for the - * 4KB page / 4bit BCH combination. - */ - if (mtd->writesize == 4096 && info->chunk_size == 2048) { - oobregion->offset += 6; - oobregion->length -= 6; - } else { - oobregion->offset += 2; - oobregion->length -= 2; - } - } - - return 0; -} - -static const struct mtd_ooblayout_ops pxa3xx_ooblayout_ops = { - .ecc = pxa3xx_ooblayout_ecc, - .free = pxa3xx_ooblayout_free, -}; - -static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; -static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 8, - .len = 6, - .veroffs = 14, - .maxblocks = 8, /* Last 8 blocks in each chip */ - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 8, - .len = 6, - .veroffs = 14, - .maxblocks = 8, /* Last 8 blocks in each chip */ - .pattern = bbt_mirror_pattern -}; - -#define NDTR0_tCH(c) (min((c), 7) << 19) -#define NDTR0_tCS(c) (min((c), 7) << 16) -#define NDTR0_tWH(c) (min((c), 7) << 11) -#define NDTR0_tWP(c) (min((c), 7) << 8) -#define NDTR0_tRH(c) (min((c), 7) << 3) -#define NDTR0_tRP(c) (min((c), 7) << 0) - -#define NDTR1_tR(c) (min((c), 65535) << 16) -#define NDTR1_tWHR(c) (min((c), 15) << 4) -#define NDTR1_tAR(c) (min((c), 15) << 0) - -/* convert nano-seconds to nand flash controller clock cycles */ -#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000) - -static const struct of_device_id pxa3xx_nand_dt_ids[] = { - { - .compatible = "marvell,pxa3xx-nand", - .data = (void *)PXA3XX_NAND_VARIANT_PXA, - }, - { - .compatible = "marvell,armada370-nand", - .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370, - }, - { - .compatible = "marvell,armada-8k-nand", - .data = (void *)PXA3XX_NAND_VARIANT_ARMADA_8K, - }, - {} -}; -MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); - -static enum pxa3xx_nand_variant -pxa3xx_nand_get_variant(struct platform_device *pdev) -{ - const struct of_device_id *of_id = - of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); - if (!of_id) - return PXA3XX_NAND_VARIANT_PXA; - return (enum pxa3xx_nand_variant)of_id->data; -} - -static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, - const struct pxa3xx_nand_timing *t) -{ - struct pxa3xx_nand_info *info = host->info_data; - unsigned long nand_clk = clk_get_rate(info->clk); - uint32_t ndtr0, ndtr1; - - ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | - NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | - NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | - NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | - NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | - NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); - - ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | - NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | - NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); - - info->ndtr0cs0 = ndtr0; - info->ndtr1cs0 = ndtr1; - nand_writel(info, NDTR0CS0, ndtr0); - nand_writel(info, NDTR1CS0, ndtr1); -} - -static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host, - const struct nand_sdr_timings *t) -{ - struct pxa3xx_nand_info *info = host->info_data; - struct nand_chip *chip = &host->chip; - unsigned long nand_clk = clk_get_rate(info->clk); - uint32_t ndtr0, ndtr1; - - u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000); - u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000); - u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000); - u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000); - u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000); - u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000); - u32 tR = chip->chip_delay * 1000; - u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000); - u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000); - - /* fallback to a default value if tR = 0 */ - if (!tR) - tR = 20000; - - ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) | - NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) | - NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) | - NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) | - NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) | - NDTR0_tRP(ns2cycle(tRP_min, nand_clk)); - - ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) | - NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) | - NDTR1_tAR(ns2cycle(tAR_min, nand_clk)); - - info->ndtr0cs0 = ndtr0; - info->ndtr1cs0 = ndtr1; - nand_writel(info, NDTR0CS0, ndtr0); - nand_writel(info, NDTR1CS0, ndtr1); -} - -static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host, - unsigned int *flash_width, - unsigned int *dfc_width) -{ - struct nand_chip *chip = &host->chip; - struct pxa3xx_nand_info *info = host->info_data; - const struct pxa3xx_nand_flash *f = NULL; - int i, id, ntypes; - u8 idbuf[2]; - - ntypes = ARRAY_SIZE(builtin_flash_types); - - nand_readid_op(chip, 0, idbuf, sizeof(idbuf)); - id = idbuf[0] | (idbuf[1] << 8); - - for (i = 0; i < ntypes; i++) { - f = &builtin_flash_types[i]; - - if (f->chip_id == id) - break; - } - - if (i == ntypes) { - dev_err(&info->pdev->dev, "Error: timings not found\n"); - return -EINVAL; - } - - pxa3xx_nand_set_timing(host, f->timing); - - *flash_width = f->flash_width; - *dfc_width = f->dfc_width; - - return 0; -} - -static int pxa3xx_nand_init_timings_onfi(struct pxa3xx_nand_host *host, - int mode) -{ - const struct nand_sdr_timings *timings; - - mode = fls(mode) - 1; - if (mode < 0) - mode = 0; - - timings = onfi_async_timing_mode_to_sdr_timings(mode); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - pxa3xx_nand_set_sdr_timing(host, timings); - - return 0; -} - -static int pxa3xx_nand_init(struct pxa3xx_nand_host *host) -{ - struct nand_chip *chip = &host->chip; - struct pxa3xx_nand_info *info = host->info_data; - unsigned int flash_width = 0, dfc_width = 0; - int mode, err; - - mode = onfi_get_async_timing_mode(chip); - if (mode == ONFI_TIMING_MODE_UNKNOWN) { - err = pxa3xx_nand_init_timings_compat(host, &flash_width, - &dfc_width); - if (err) - return err; - - if (flash_width == 16) { - info->reg_ndcr |= NDCR_DWIDTH_M; - chip->options |= NAND_BUSWIDTH_16; - } - - info->reg_ndcr |= (dfc_width == 16) ? NDCR_DWIDTH_C : 0; - } else { - err = pxa3xx_nand_init_timings_onfi(host, mode); - if (err) - return err; - } - - return 0; -} - -/** - * NOTE: it is a must to set ND_RUN firstly, then write - * command buffer, otherwise, it does not work. - * We enable all the interrupt at the same time, and - * let pxa3xx_nand_irq to handle all logic. - */ -static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) -{ - uint32_t ndcr; - - ndcr = info->reg_ndcr; - - if (info->use_ecc) { - ndcr |= NDCR_ECC_EN; - if (info->ecc_bch) - nand_writel(info, NDECCCTRL, 0x1); - } else { - ndcr &= ~NDCR_ECC_EN; - if (info->ecc_bch) - nand_writel(info, NDECCCTRL, 0x0); - } - - if (info->use_dma) - ndcr |= NDCR_DMA_EN; - else - ndcr &= ~NDCR_DMA_EN; - - if (info->use_spare) - ndcr |= NDCR_SPARE_EN; - else - ndcr &= ~NDCR_SPARE_EN; - - ndcr |= NDCR_ND_RUN; - - /* clear status bits and run */ - nand_writel(info, NDSR, NDSR_MASK); - nand_writel(info, NDCR, 0); - nand_writel(info, NDCR, ndcr); -} - -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) -{ - uint32_t ndcr; - int timeout = NAND_STOP_DELAY; - - /* wait RUN bit in NDCR become 0 */ - ndcr = nand_readl(info, NDCR); - while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) { - ndcr = nand_readl(info, NDCR); - udelay(1); - } - - if (timeout <= 0) { - ndcr &= ~NDCR_ND_RUN; - nand_writel(info, NDCR, ndcr); - } - if (info->dma_chan) - dmaengine_terminate_all(info->dma_chan); - - /* clear status bits */ - nand_writel(info, NDSR, NDSR_MASK); -} - -static void __maybe_unused -enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) -{ - uint32_t ndcr; - - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~int_mask); -} - -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) -{ - uint32_t ndcr; - - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr | int_mask); -} - -static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) -{ - if (info->ecc_bch) { - u32 val; - int ret; - - /* - * According to the datasheet, when reading from NDDB - * with BCH enabled, after each 32 bytes reads, we - * have to make sure that the NDSR.RDDREQ bit is set. - * - * Drain the FIFO 8 32 bits reads at a time, and skip - * the polling on the last read. - */ - while (len > 8) { - ioread32_rep(info->mmio_base + NDDB, data, 8); - - ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val, - val & NDSR_RDDREQ, 1000, 5000); - if (ret) { - dev_err(&info->pdev->dev, - "Timeout on RDDREQ while draining the FIFO\n"); - return; - } - - data += 32; - len -= 8; - } - } - - ioread32_rep(info->mmio_base + NDDB, data, len); -} - -static void handle_data_pio(struct pxa3xx_nand_info *info) -{ - switch (info->state) { - case STATE_PIO_WRITING: - if (info->step_chunk_size) - writesl(info->mmio_base + NDDB, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(info->step_chunk_size, 4)); - - if (info->step_spare_size) - writesl(info->mmio_base + NDDB, - info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->step_spare_size, 4)); - break; - case STATE_PIO_READING: - if (info->step_chunk_size) - drain_fifo(info, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(info->step_chunk_size, 4)); - - if (info->step_spare_size) - drain_fifo(info, - info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->step_spare_size, 4)); - break; - default: - dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, - info->state); - BUG(); - } - - /* Update buffer pointers for multi-page read/write */ - info->data_buff_pos += info->step_chunk_size; - info->oob_buff_pos += info->step_spare_size; -} - -static void pxa3xx_nand_data_dma_irq(void *data) -{ - struct pxa3xx_nand_info *info = data; - struct dma_tx_state state; - enum dma_status status; - - status = dmaengine_tx_status(info->dma_chan, info->dma_cookie, &state); - if (likely(status == DMA_COMPLETE)) { - info->state = STATE_DMA_DONE; - } else { - dev_err(&info->pdev->dev, "DMA error on data channel\n"); - info->retcode = ERR_DMABUSERR; - } - dma_unmap_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); - - nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); - enable_int(info, NDCR_INT_MASK); -} - -static void start_data_dma(struct pxa3xx_nand_info *info) -{ - enum dma_transfer_direction direction; - struct dma_async_tx_descriptor *tx; - - switch (info->state) { - case STATE_DMA_WRITING: - info->dma_dir = DMA_TO_DEVICE; - direction = DMA_MEM_TO_DEV; - break; - case STATE_DMA_READING: - info->dma_dir = DMA_FROM_DEVICE; - direction = DMA_DEV_TO_MEM; - break; - default: - dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, - info->state); - BUG(); - } - info->sg.length = info->chunk_size; - if (info->use_spare) - info->sg.length += info->spare_size + info->ecc_size; - dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); - - tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction, - DMA_PREP_INTERRUPT); - if (!tx) { - dev_err(&info->pdev->dev, "prep_slave_sg() failed\n"); - return; - } - tx->callback = pxa3xx_nand_data_dma_irq; - tx->callback_param = info; - info->dma_cookie = dmaengine_submit(tx); - dma_async_issue_pending(info->dma_chan); - dev_dbg(&info->pdev->dev, "%s(dir=%d cookie=%x size=%u)\n", - __func__, direction, info->dma_cookie, info->sg.length); -} - -static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data) -{ - struct pxa3xx_nand_info *info = data; - - handle_data_pio(info); - - info->state = STATE_CMD_DONE; - nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); - - return IRQ_HANDLED; -} - -static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) -{ - struct pxa3xx_nand_info *info = devid; - unsigned int status, is_completed = 0, is_ready = 0; - unsigned int ready, cmd_done; - irqreturn_t ret = IRQ_HANDLED; - - if (info->cs == 0) { - ready = NDSR_FLASH_RDY; - cmd_done = NDSR_CS0_CMDD; - } else { - ready = NDSR_RDY; - cmd_done = NDSR_CS1_CMDD; - } - - status = nand_readl(info, NDSR); - - if (status & NDSR_UNCORERR) - info->retcode = ERR_UNCORERR; - if (status & NDSR_CORERR) { - info->retcode = ERR_CORERR; - if ((info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || - info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) && - info->ecc_bch) - info->ecc_err_cnt = NDSR_ERR_CNT(status); - else - info->ecc_err_cnt = 1; - - /* - * Each chunk composing a page is corrected independently, - * and we need to store maximum number of corrected bitflips - * to return it to the MTD layer in ecc.read_page(). - */ - info->max_bitflips = max_t(unsigned int, - info->max_bitflips, - info->ecc_err_cnt); - } - if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { - /* whether use dma to transfer data */ - if (info->use_dma) { - disable_int(info, NDCR_INT_MASK); - info->state = (status & NDSR_RDDREQ) ? - STATE_DMA_READING : STATE_DMA_WRITING; - start_data_dma(info); - goto NORMAL_IRQ_EXIT; - } else { - info->state = (status & NDSR_RDDREQ) ? - STATE_PIO_READING : STATE_PIO_WRITING; - ret = IRQ_WAKE_THREAD; - goto NORMAL_IRQ_EXIT; - } - } - if (status & cmd_done) { - info->state = STATE_CMD_DONE; - is_completed = 1; - } - if (status & ready) { - info->state = STATE_READY; - is_ready = 1; - } - - /* - * Clear all status bit before issuing the next command, which - * can and will alter the status bits and will deserve a new - * interrupt on its own. This lets the controller exit the IRQ - */ - nand_writel(info, NDSR, status); - - if (status & NDSR_WRCMDREQ) { - status &= ~NDSR_WRCMDREQ; - info->state = STATE_CMD_HANDLE; - - /* - * Command buffer registers NDCB{0-2} (and optionally NDCB3) - * must be loaded by writing directly either 12 or 16 - * bytes directly to NDCB0, four bytes at a time. - * - * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored - * but each NDCBx register can be read. - */ - nand_writel(info, NDCB0, info->ndcb0); - nand_writel(info, NDCB0, info->ndcb1); - nand_writel(info, NDCB0, info->ndcb2); - - /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || - info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) - nand_writel(info, NDCB0, info->ndcb3); - } - - if (is_completed) - complete(&info->cmd_complete); - if (is_ready) - complete(&info->dev_ready); -NORMAL_IRQ_EXIT: - return ret; -} - -static inline int is_buf_blank(uint8_t *buf, size_t len) -{ - for (; len > 0; len--) - if (*buf++ != 0xff) - return 0; - return 1; -} - -static void set_command_address(struct pxa3xx_nand_info *info, - unsigned int page_size, uint16_t column, int page_addr) -{ - /* small page addr setting */ - if (page_size < PAGE_CHUNK_SIZE) { - info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) - | (column & 0xFF); - - info->ndcb2 = 0; - } else { - info->ndcb1 = ((page_addr & 0xFFFF) << 16) - | (column & 0xFFFF); - - if (page_addr & 0xFF0000) - info->ndcb2 = (page_addr & 0xFF0000) >> 16; - else - info->ndcb2 = 0; - } -} - -static void prepare_start_command(struct pxa3xx_nand_info *info, int command) -{ - struct pxa3xx_nand_host *host = info->host[info->cs]; - struct mtd_info *mtd = nand_to_mtd(&host->chip); - - /* reset data and oob column point to handle data */ - info->buf_start = 0; - info->buf_count = 0; - info->data_buff_pos = 0; - info->oob_buff_pos = 0; - info->step_chunk_size = 0; - info->step_spare_size = 0; - info->cur_chunk = 0; - info->use_ecc = 0; - info->use_spare = 1; - info->retcode = ERR_NONE; - info->ecc_err_cnt = 0; - info->ndcb3 = 0; - info->need_wait = 0; - - switch (command) { - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - case NAND_CMD_PAGEPROG: - info->use_ecc = 1; - break; - case NAND_CMD_PARAM: - info->use_spare = 0; - break; - default: - info->ndcb1 = 0; - info->ndcb2 = 0; - break; - } - - /* - * If we are about to issue a read command, or about to set - * the write address, then clean the data buffer. - */ - if (command == NAND_CMD_READ0 || - command == NAND_CMD_READOOB || - command == NAND_CMD_SEQIN) { - - info->buf_count = mtd->writesize + mtd->oobsize; - memset(info->data_buff, 0xFF, info->buf_count); - } - -} - -static int prepare_set_command(struct pxa3xx_nand_info *info, int command, - int ext_cmd_type, uint16_t column, int page_addr) -{ - int addr_cycle, exec_cmd; - struct pxa3xx_nand_host *host; - struct mtd_info *mtd; - - host = info->host[info->cs]; - mtd = nand_to_mtd(&host->chip); - addr_cycle = 0; - exec_cmd = 1; - - if (info->cs != 0) - info->ndcb0 = NDCB0_CSEL; - else - info->ndcb0 = 0; - - if (command == NAND_CMD_SEQIN) - exec_cmd = 0; - - addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles - + host->col_addr_cycles); - - switch (command) { - case NAND_CMD_READOOB: - case NAND_CMD_READ0: - info->buf_start = column; - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | addr_cycle - | NAND_CMD_READ0; - - if (command == NAND_CMD_READOOB) - info->buf_start += mtd->writesize; - - if (info->cur_chunk < info->nfullchunks) { - info->step_chunk_size = info->chunk_size; - info->step_spare_size = info->spare_size; - } else { - info->step_chunk_size = info->last_chunk_size; - info->step_spare_size = info->last_spare_size; - } - - /* - * Multiple page read needs an 'extended command type' field, - * which is either naked-read or last-read according to the - * state. - */ - if (mtd->writesize == PAGE_CHUNK_SIZE) { - info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); - } else if (mtd->writesize > PAGE_CHUNK_SIZE) { - info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) - | NDCB0_LEN_OVRD - | NDCB0_EXT_CMD_TYPE(ext_cmd_type); - info->ndcb3 = info->step_chunk_size + - info->step_spare_size; - } - - set_command_address(info, mtd->writesize, column, page_addr); - break; - - case NAND_CMD_SEQIN: - - info->buf_start = column; - set_command_address(info, mtd->writesize, 0, page_addr); - - /* - * Multiple page programming needs to execute the initial - * SEQIN command that sets the page address. - */ - if (mtd->writesize > PAGE_CHUNK_SIZE) { - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_EXT_CMD_TYPE(ext_cmd_type) - | addr_cycle - | command; - exec_cmd = 1; - } - break; - - case NAND_CMD_PAGEPROG: - if (is_buf_blank(info->data_buff, - (mtd->writesize + mtd->oobsize))) { - exec_cmd = 0; - break; - } - - if (info->cur_chunk < info->nfullchunks) { - info->step_chunk_size = info->chunk_size; - info->step_spare_size = info->spare_size; - } else { - info->step_chunk_size = info->last_chunk_size; - info->step_spare_size = info->last_spare_size; - } - - /* Second command setting for large pages */ - if (mtd->writesize > PAGE_CHUNK_SIZE) { - /* - * Multiple page write uses the 'extended command' - * field. This can be used to issue a command dispatch - * or a naked-write depending on the current stage. - */ - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_LEN_OVRD - | NDCB0_EXT_CMD_TYPE(ext_cmd_type); - info->ndcb3 = info->step_chunk_size + - info->step_spare_size; - - /* - * This is the command dispatch that completes a chunked - * page program operation. - */ - if (info->cur_chunk == info->ntotalchunks) { - info->ndcb0 = NDCB0_CMD_TYPE(0x1) - | NDCB0_EXT_CMD_TYPE(ext_cmd_type) - | command; - info->ndcb1 = 0; - info->ndcb2 = 0; - info->ndcb3 = 0; - } - } else { - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_AUTO_RS - | NDCB0_ST_ROW_EN - | NDCB0_DBC - | (NAND_CMD_PAGEPROG << 8) - | NAND_CMD_SEQIN - | addr_cycle; - } - break; - - case NAND_CMD_PARAM: - info->buf_count = INIT_BUFFER_SIZE; - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | NDCB0_ADDR_CYC(1) - | NDCB0_LEN_OVRD - | command; - info->ndcb1 = (column & 0xFF); - info->ndcb3 = INIT_BUFFER_SIZE; - info->step_chunk_size = INIT_BUFFER_SIZE; - break; - - case NAND_CMD_READID: - info->buf_count = READ_ID_BYTES; - info->ndcb0 |= NDCB0_CMD_TYPE(3) - | NDCB0_ADDR_CYC(1) - | command; - info->ndcb1 = (column & 0xFF); - - info->step_chunk_size = 8; - break; - case NAND_CMD_STATUS: - info->buf_count = 1; - info->ndcb0 |= NDCB0_CMD_TYPE(4) - | NDCB0_ADDR_CYC(1) - | command; - - info->step_chunk_size = 8; - break; - - case NAND_CMD_ERASE1: - info->ndcb0 |= NDCB0_CMD_TYPE(2) - | NDCB0_AUTO_RS - | NDCB0_ADDR_CYC(3) - | NDCB0_DBC - | (NAND_CMD_ERASE2 << 8) - | NAND_CMD_ERASE1; - info->ndcb1 = page_addr; - info->ndcb2 = 0; - - break; - case NAND_CMD_RESET: - info->ndcb0 |= NDCB0_CMD_TYPE(5) - | command; - - break; - - case NAND_CMD_ERASE2: - exec_cmd = 0; - break; - - default: - exec_cmd = 0; - dev_err(&info->pdev->dev, "non-supported command %x\n", - command); - break; - } - - return exec_cmd; -} - -static void nand_cmdfunc(struct mtd_info *mtd, unsigned command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int exec_cmd; - - /* - * if this is a x16 device ,then convert the input - * "byte" address into a "word" address appropriate - * for indexing a word-oriented device - */ - if (info->reg_ndcr & NDCR_DWIDTH_M) - column /= 2; - - /* - * There may be different NAND chip hooked to - * different chip select, so check whether - * chip select has been changed, if yes, reset the timing - */ - if (info->cs != host->cs) { - info->cs = host->cs; - nand_writel(info, NDTR0CS0, info->ndtr0cs0); - nand_writel(info, NDTR1CS0, info->ndtr1cs0); - } - - prepare_start_command(info, command); - - info->state = STATE_PREPARED; - exec_cmd = prepare_set_command(info, command, 0, column, page_addr); - - if (exec_cmd) { - init_completion(&info->cmd_complete); - init_completion(&info->dev_ready); - info->need_wait = 1; - pxa3xx_nand_start(info); - - if (!wait_for_completion_timeout(&info->cmd_complete, - CHIP_DELAY_TIMEOUT)) { - dev_err(&info->pdev->dev, "Wait time out!!!\n"); - /* Stop State Machine for next command cycle */ - pxa3xx_nand_stop(info); - } - } - info->state = STATE_IDLE; -} - -static void nand_cmdfunc_extended(struct mtd_info *mtd, - const unsigned command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int exec_cmd, ext_cmd_type; - - /* - * if this is a x16 device then convert the input - * "byte" address into a "word" address appropriate - * for indexing a word-oriented device - */ - if (info->reg_ndcr & NDCR_DWIDTH_M) - column /= 2; - - /* - * There may be different NAND chip hooked to - * different chip select, so check whether - * chip select has been changed, if yes, reset the timing - */ - if (info->cs != host->cs) { - info->cs = host->cs; - nand_writel(info, NDTR0CS0, info->ndtr0cs0); - nand_writel(info, NDTR1CS0, info->ndtr1cs0); - } - - /* Select the extended command for the first command */ - switch (command) { - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - ext_cmd_type = EXT_CMD_TYPE_MONO; - break; - case NAND_CMD_SEQIN: - ext_cmd_type = EXT_CMD_TYPE_DISPATCH; - break; - case NAND_CMD_PAGEPROG: - ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; - break; - default: - ext_cmd_type = 0; - break; - } - - prepare_start_command(info, command); - - /* - * Prepare the "is ready" completion before starting a command - * transaction sequence. If the command is not executed the - * completion will be completed, see below. - * - * We can do that inside the loop because the command variable - * is invariant and thus so is the exec_cmd. - */ - info->need_wait = 1; - init_completion(&info->dev_ready); - do { - info->state = STATE_PREPARED; - - exec_cmd = prepare_set_command(info, command, ext_cmd_type, - column, page_addr); - if (!exec_cmd) { - info->need_wait = 0; - complete(&info->dev_ready); - break; - } - - init_completion(&info->cmd_complete); - pxa3xx_nand_start(info); - - if (!wait_for_completion_timeout(&info->cmd_complete, - CHIP_DELAY_TIMEOUT)) { - dev_err(&info->pdev->dev, "Wait time out!!!\n"); - /* Stop State Machine for next command cycle */ - pxa3xx_nand_stop(info); - break; - } - - /* Only a few commands need several steps */ - if (command != NAND_CMD_PAGEPROG && - command != NAND_CMD_READ0 && - command != NAND_CMD_READOOB) - break; - - info->cur_chunk++; - - /* Check if the sequence is complete */ - if (info->cur_chunk == info->ntotalchunks && command != NAND_CMD_PAGEPROG) - break; - - /* - * After a splitted program command sequence has issued - * the command dispatch, the command sequence is complete. - */ - if (info->cur_chunk == (info->ntotalchunks + 1) && - command == NAND_CMD_PAGEPROG && - ext_cmd_type == EXT_CMD_TYPE_DISPATCH) - break; - - if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { - /* Last read: issue a 'last naked read' */ - if (info->cur_chunk == info->ntotalchunks - 1) - ext_cmd_type = EXT_CMD_TYPE_LAST_RW; - else - ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; - - /* - * If a splitted program command has no more data to transfer, - * the command dispatch must be issued to complete. - */ - } else if (command == NAND_CMD_PAGEPROG && - info->cur_chunk == info->ntotalchunks) { - ext_cmd_type = EXT_CMD_TYPE_DISPATCH; - } - } while (1); - - info->state = STATE_IDLE; -} - -static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required, - int page) -{ - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, - int page) -{ - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - - nand_read_page_op(chip, page, 0, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (info->retcode == ERR_CORERR && info->use_ecc) { - mtd->ecc_stats.corrected += info->ecc_err_cnt; - - } else if (info->retcode == ERR_UNCORERR) { - /* - * for blank page (all 0xff), HW will calculate its ECC as - * 0, which is different from the ECC information within - * OOB, ignore such uncorrectable errors - */ - if (is_buf_blank(buf, mtd->writesize)) - info->retcode = ERR_NONE; - else - mtd->ecc_stats.failed++; - } - - return info->max_bitflips; -} - -static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - char retval = 0xFF; - - if (info->buf_start < info->buf_count) - /* Has just send a new command? */ - retval = info->data_buff[info->buf_start++]; - - return retval; -} - -static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - u16 retval = 0xFFFF; - - if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { - retval = *((u16 *)(info->data_buff+info->buf_start)); - info->buf_start += 2; - } - return retval; -} - -static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int real_len = min_t(size_t, len, info->buf_count - info->buf_start); - - memcpy(buf, info->data_buff + info->buf_start, real_len); - info->buf_start += real_len; -} - -static void pxa3xx_nand_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int real_len = min_t(size_t, len, info->buf_count - info->buf_start); - - memcpy(info->data_buff + info->buf_start, buf, real_len); - info->buf_start += real_len; -} - -static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) -{ - return; -} - -static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - - if (info->need_wait) { - info->need_wait = 0; - if (!wait_for_completion_timeout(&info->dev_ready, - CHIP_DELAY_TIMEOUT)) { - dev_err(&info->pdev->dev, "Ready time out!!!\n"); - return NAND_STATUS_FAIL; - } - } - - /* pxa3xx_nand_send_command has waited for command complete */ - if (this->state == FL_WRITING || this->state == FL_ERASING) { - if (info->retcode == ERR_NONE) - return 0; - else - return NAND_STATUS_FAIL; - } - - return NAND_STATUS_READY; -} - -static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_host *host = info->host[info->cs]; - struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); - const struct nand_sdr_timings *timings; - - /* Configure default flash values */ - info->chunk_size = PAGE_CHUNK_SIZE; - info->reg_ndcr = 0x0; /* enable all interrupts */ - info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); - info->reg_ndcr |= NDCR_SPARE_EN; - - /* use the common timing to make a try */ - timings = onfi_async_timing_mode_to_sdr_timings(0); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - pxa3xx_nand_set_sdr_timing(host, timings); - return 0; -} - -static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_host *host = info->host[info->cs]; - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - - info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; - info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0; - info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; -} - -static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) -{ - struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); - uint32_t ndcr = nand_readl(info, NDCR); - - /* Set an initial chunk size */ - info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; - info->reg_ndcr = ndcr & - ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL); - info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - info->ndtr0cs0 = nand_readl(info, NDTR0CS0); - info->ndtr1cs0 = nand_readl(info, NDTR1CS0); -} - -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) -{ - struct platform_device *pdev = info->pdev; - struct dma_slave_config config; - dma_cap_mask_t mask; - struct pxad_param param; - int ret; - - info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); - if (info->data_buff == NULL) - return -ENOMEM; - if (use_dma == 0) - return 0; - - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - sg_init_one(&info->sg, info->data_buff, info->buf_size); - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - param.prio = PXAD_PRIO_LOWEST; - param.drcmr = info->drcmr_dat; - info->dma_chan = dma_request_slave_channel_compat(mask, pxad_filter_fn, - ¶m, &pdev->dev, - "data"); - if (!info->dma_chan) { - dev_err(&pdev->dev, "unable to request data dma channel\n"); - return -ENODEV; - } - - memset(&config, 0, sizeof(config)); - config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.src_addr = info->mmio_phys + NDDB; - config.dst_addr = info->mmio_phys + NDDB; - config.src_maxburst = 32; - config.dst_maxburst = 32; - ret = dmaengine_slave_config(info->dma_chan, &config); - if (ret < 0) { - dev_err(&info->pdev->dev, - "dma channel configuration failed: %d\n", - ret); - return ret; - } - - /* - * Now that DMA buffers are allocated we turn on - * DMA proper for I/O operations. - */ - info->use_dma = 1; - return 0; -} - -static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) -{ - if (info->use_dma) { - dmaengine_terminate_all(info->dma_chan); - dma_release_channel(info->dma_chan); - } - kfree(info->data_buff); -} - -static int pxa_ecc_init(struct pxa3xx_nand_info *info, - struct mtd_info *mtd, - int strength, int ecc_stepsize, int page_size) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { - info->nfullchunks = 1; - info->ntotalchunks = 1; - info->chunk_size = 2048; - info->spare_size = 40; - info->ecc_size = 24; - ecc->mode = NAND_ECC_HW; - ecc->size = 512; - ecc->strength = 1; - - } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { - info->nfullchunks = 1; - info->ntotalchunks = 1; - info->chunk_size = 512; - info->spare_size = 8; - info->ecc_size = 8; - ecc->mode = NAND_ECC_HW; - ecc->size = 512; - ecc->strength = 1; - - /* - * Required ECC: 4-bit correction per 512 bytes - * Select: 16-bit correction per 2048 bytes - */ - } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { - info->ecc_bch = 1; - info->nfullchunks = 1; - info->ntotalchunks = 1; - info->chunk_size = 2048; - info->spare_size = 32; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); - ecc->strength = 16; - - } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { - info->ecc_bch = 1; - info->nfullchunks = 2; - info->ntotalchunks = 2; - info->chunk_size = 2048; - info->spare_size = 32; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); - ecc->strength = 16; - - /* - * Required ECC: 8-bit correction per 512 bytes - * Select: 16-bit correction per 1024 bytes - */ - } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { - info->ecc_bch = 1; - info->nfullchunks = 4; - info->ntotalchunks = 5; - info->chunk_size = 1024; - info->spare_size = 0; - info->last_chunk_size = 0; - info->last_spare_size = 64; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); - ecc->strength = 16; - } else { - dev_err(&info->pdev->dev, - "ECC strength %d at page size %d is not supported\n", - strength, page_size); - return -ENODEV; - } - - dev_info(&info->pdev->dev, "ECC strength %d, ECC step size %d\n", - ecc->strength, ecc->size); - return 0; -} - -static int pxa3xx_nand_scan(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); - int ret; - uint16_t ecc_strength, ecc_step; - - if (pdata->keep_config) { - pxa3xx_nand_detect_config(info); - } else { - ret = pxa3xx_nand_config_ident(info); - if (ret) - return ret; - } - - if (info->reg_ndcr & NDCR_DWIDTH_M) - chip->options |= NAND_BUSWIDTH_16; - - /* Device detection must be done with ECC disabled */ - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || - info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) - nand_writel(info, NDECCCTRL, 0x0); - - if (pdata->flash_bbt) - chip->bbt_options |= NAND_BBT_USE_FLASH; - - chip->ecc.strength = pdata->ecc_strength; - chip->ecc.size = pdata->ecc_step_size; - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - if (!pdata->keep_config) { - ret = pxa3xx_nand_init(host); - if (ret) { - dev_err(&info->pdev->dev, "Failed to init nand: %d\n", - ret); - return ret; - } - } - - if (chip->bbt_options & NAND_BBT_USE_FLASH) { - /* - * We'll use a bad block table stored in-flash and don't - * allow writing the bad block marker to the flash. - */ - chip->bbt_options |= NAND_BBT_NO_OOB_BBM; - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; - } - - /* - * If the page size is bigger than the FIFO size, let's check - * we are given the right variant and then switch to the extended - * (aka splitted) command handling, - */ - if (mtd->writesize > PAGE_CHUNK_SIZE) { - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || - info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) { - chip->cmdfunc = nand_cmdfunc_extended; - } else { - dev_err(&info->pdev->dev, - "unsupported page size on this variant\n"); - return -ENODEV; - } - } - - ecc_strength = chip->ecc.strength; - ecc_step = chip->ecc.size; - if (!ecc_strength || !ecc_step) { - ecc_strength = chip->ecc_strength_ds; - ecc_step = chip->ecc_step_ds; - } - - /* Set default ECC strength requirements on non-ONFI devices */ - if (ecc_strength < 1 && ecc_step < 1) { - ecc_strength = 1; - ecc_step = 512; - } - - ret = pxa_ecc_init(info, mtd, ecc_strength, - ecc_step, mtd->writesize); - if (ret) - return ret; - - /* calculate addressing information */ - if (mtd->writesize >= 2048) - host->col_addr_cycles = 2; - else - host->col_addr_cycles = 1; - - /* release the initial buffer */ - kfree(info->data_buff); - - /* allocate the real data + oob buffer */ - info->buf_size = mtd->writesize + mtd->oobsize; - ret = pxa3xx_nand_init_buff(info); - if (ret) - return ret; - info->oob_buff = info->data_buff + mtd->writesize; - - if ((mtd->size >> chip->page_shift) > 65536) - host->row_addr_cycles = 3; - else - host->row_addr_cycles = 2; - - if (!pdata->keep_config) - pxa3xx_nand_config_tail(info); - - return nand_scan_tail(mtd); -} - -static int alloc_nand_resource(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct pxa3xx_nand_platform_data *pdata; - struct pxa3xx_nand_info *info; - struct pxa3xx_nand_host *host; - struct nand_chip *chip = NULL; - struct mtd_info *mtd; - struct resource *r; - int ret, irq, cs; - - pdata = dev_get_platdata(&pdev->dev); - if (pdata->num_cs <= 0) { - dev_err(&pdev->dev, "invalid number of chip selects\n"); - return -ENODEV; - } - - info = devm_kzalloc(&pdev->dev, - sizeof(*info) + sizeof(*host) * pdata->num_cs, - GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->pdev = pdev; - info->variant = pxa3xx_nand_get_variant(pdev); - for (cs = 0; cs < pdata->num_cs; cs++) { - host = (void *)&info[1] + sizeof(*host) * cs; - chip = &host->chip; - nand_set_controller_data(chip, host); - mtd = nand_to_mtd(chip); - info->host[cs] = host; - host->cs = cs; - host->info_data = info; - mtd->dev.parent = &pdev->dev; - /* FIXME: all chips use the same device tree partitions */ - nand_set_flash_node(chip, np); - - nand_set_controller_data(chip, host); - chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; - chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; - chip->controller = &info->controller; - chip->waitfunc = pxa3xx_nand_waitfunc; - chip->select_chip = pxa3xx_nand_select_chip; - chip->read_word = pxa3xx_nand_read_word; - chip->read_byte = pxa3xx_nand_read_byte; - chip->read_buf = pxa3xx_nand_read_buf; - chip->write_buf = pxa3xx_nand_write_buf; - chip->options |= NAND_NO_SUBPAGE_WRITE; - chip->cmdfunc = nand_cmdfunc; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; - } - - nand_hw_control_init(chip->controller); - info->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - dev_err(&pdev->dev, "failed to get nand clock: %d\n", ret); - return ret; - } - ret = clk_prepare_enable(info->clk); - if (ret < 0) - return ret; - - if (!np && use_dma) { - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r == NULL) { - dev_err(&pdev->dev, - "no resource defined for data DMA\n"); - ret = -ENXIO; - goto fail_disable_clk; - } - info->drcmr_dat = r->start; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); - ret = -ENXIO; - goto fail_disable_clk; - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->mmio_base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(info->mmio_base)) { - ret = PTR_ERR(info->mmio_base); - dev_err(&pdev->dev, "failed to map register space: %d\n", ret); - goto fail_disable_clk; - } - info->mmio_phys = r->start; - - /* Allocate a buffer to allow flash detection */ - info->buf_size = INIT_BUFFER_SIZE; - info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); - if (info->data_buff == NULL) { - ret = -ENOMEM; - goto fail_disable_clk; - } - - /* initialize all interrupts to be disabled */ - disable_int(info, NDSR_MASK); - - ret = request_threaded_irq(irq, pxa3xx_nand_irq, - pxa3xx_nand_irq_thread, IRQF_ONESHOT, - pdev->name, info); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); - goto fail_free_buf; - } - - platform_set_drvdata(pdev, info); - - return 0; - -fail_free_buf: - free_irq(irq, info); - kfree(info->data_buff); -fail_disable_clk: - clk_disable_unprepare(info->clk); - return ret; -} - -static int pxa3xx_nand_remove(struct platform_device *pdev) -{ - struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); - struct pxa3xx_nand_platform_data *pdata; - int irq, cs; - - if (!info) - return 0; - - pdata = dev_get_platdata(&pdev->dev); - - irq = platform_get_irq(pdev, 0); - if (irq >= 0) - free_irq(irq, info); - pxa3xx_nand_free_buff(info); - - /* - * In the pxa3xx case, the DFI bus is shared between the SMC and NFC. - * In order to prevent a lockup of the system bus, the DFI bus - * arbitration is granted to SMC upon driver removal. This is done by - * setting the x_ARB_CNTL bit, which also prevents the NAND to have - * access to the bus anymore. - */ - nand_writel(info, NDCR, - (nand_readl(info, NDCR) & ~NDCR_ND_ARB_EN) | - NFCV1_NDCR_ARB_CNTL); - clk_disable_unprepare(info->clk); - - for (cs = 0; cs < pdata->num_cs; cs++) - nand_release(nand_to_mtd(&info->host[cs]->chip)); - return 0; -} - -static int pxa3xx_nand_probe_dt(struct platform_device *pdev) -{ - struct pxa3xx_nand_platform_data *pdata; - struct device_node *np = pdev->dev.of_node; - const struct of_device_id *of_id = - of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); - - if (!of_id) - return 0; - - /* - * Some SoCs like A7k/A8k need to enable manually the NAND - * controller to avoid being bootloader dependent. This is done - * through the use of a single bit in the System Functions registers. - */ - if (pxa3xx_nand_get_variant(pdev) == PXA3XX_NAND_VARIANT_ARMADA_8K) { - struct regmap *sysctrl_base = syscon_regmap_lookup_by_phandle( - pdev->dev.of_node, "marvell,system-controller"); - u32 reg; - - if (IS_ERR(sysctrl_base)) - return PTR_ERR(sysctrl_base); - - regmap_read(sysctrl_base, GENCONF_SOC_DEVICE_MUX, ®); - reg |= GENCONF_SOC_DEVICE_MUX_NFC_EN; - regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg); - } - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - if (of_get_property(np, "marvell,nand-enable-arbiter", NULL)) - pdata->enable_arbiter = 1; - if (of_get_property(np, "marvell,nand-keep-config", NULL)) - pdata->keep_config = 1; - of_property_read_u32(np, "num-cs", &pdata->num_cs); - - pdev->dev.platform_data = pdata; - - return 0; -} - -static int pxa3xx_nand_probe(struct platform_device *pdev) -{ - struct pxa3xx_nand_platform_data *pdata; - struct pxa3xx_nand_info *info; - int ret, cs, probe_success, dma_available; - - dma_available = IS_ENABLED(CONFIG_ARM) && - (IS_ENABLED(CONFIG_ARCH_PXA) || IS_ENABLED(CONFIG_ARCH_MMP)); - if (use_dma && !dma_available) { - use_dma = 0; - dev_warn(&pdev->dev, - "This platform can't do DMA on this device\n"); - } - - ret = pxa3xx_nand_probe_dt(pdev); - if (ret) - return ret; - - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -ENODEV; - } - - ret = alloc_nand_resource(pdev); - if (ret) - return ret; - - info = platform_get_drvdata(pdev); - probe_success = 0; - for (cs = 0; cs < pdata->num_cs; cs++) { - struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip); - - /* - * The mtd name matches the one used in 'mtdparts' kernel - * parameter. This name cannot be changed or otherwise - * user's mtd partitions configuration would get broken. - */ - mtd->name = "pxa3xx_nand-0"; - info->cs = cs; - ret = pxa3xx_nand_scan(mtd); - if (ret) { - dev_warn(&pdev->dev, "failed to scan nand at cs %d\n", - cs); - continue; - } - - ret = mtd_device_register(mtd, pdata->parts[cs], - pdata->nr_parts[cs]); - if (!ret) - probe_success = 1; - } - - if (!probe_success) { - pxa3xx_nand_remove(pdev); - return -ENODEV; - } - - return 0; -} - -#ifdef CONFIG_PM -static int pxa3xx_nand_suspend(struct device *dev) -{ - struct pxa3xx_nand_info *info = dev_get_drvdata(dev); - - if (info->state) { - dev_err(dev, "driver busy, state = %d\n", info->state); - return -EAGAIN; - } - - clk_disable(info->clk); - return 0; -} - -static int pxa3xx_nand_resume(struct device *dev) -{ - struct pxa3xx_nand_info *info = dev_get_drvdata(dev); - int ret; - - ret = clk_enable(info->clk); - if (ret < 0) - return ret; - - /* We don't want to handle interrupt without calling mtd routine */ - disable_int(info, NDCR_INT_MASK); - - /* - * Directly set the chip select to a invalid value, - * then the driver would reset the timing according - * to current chip select at the beginning of cmdfunc - */ - info->cs = 0xff; - - /* - * As the spec says, the NDSR would be updated to 0x1800 when - * doing the nand_clk disable/enable. - * To prevent it damaging state machine of the driver, clear - * all status before resume - */ - nand_writel(info, NDSR, NDSR_MASK); - - return 0; -} -#else -#define pxa3xx_nand_suspend NULL -#define pxa3xx_nand_resume NULL -#endif - -static const struct dev_pm_ops pxa3xx_nand_pm_ops = { - .suspend = pxa3xx_nand_suspend, - .resume = pxa3xx_nand_resume, -}; - -static struct platform_driver pxa3xx_nand_driver = { - .driver = { - .name = "pxa3xx-nand", - .of_match_table = pxa3xx_nand_dt_ids, - .pm = &pxa3xx_nand_pm_ops, - }, - .probe = pxa3xx_nand_probe, - .remove = pxa3xx_nand_remove, -}; - -module_platform_driver(pxa3xx_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("PXA3xx NAND controller driver"); diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c deleted file mode 100644 index 563b759ffca6..000000000000 --- a/drivers/mtd/nand/qcom_nandc.c +++ /dev/null @@ -1,2921 +0,0 @@ -/* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* XXX: drivers shall never use this directly! */ - -/* NANDc reg offsets */ -#define NAND_FLASH_CMD 0x00 -#define NAND_ADDR0 0x04 -#define NAND_ADDR1 0x08 -#define NAND_FLASH_CHIP_SELECT 0x0c -#define NAND_EXEC_CMD 0x10 -#define NAND_FLASH_STATUS 0x14 -#define NAND_BUFFER_STATUS 0x18 -#define NAND_DEV0_CFG0 0x20 -#define NAND_DEV0_CFG1 0x24 -#define NAND_DEV0_ECC_CFG 0x28 -#define NAND_DEV1_ECC_CFG 0x2c -#define NAND_DEV1_CFG0 0x30 -#define NAND_DEV1_CFG1 0x34 -#define NAND_READ_ID 0x40 -#define NAND_READ_STATUS 0x44 -#define NAND_DEV_CMD0 0xa0 -#define NAND_DEV_CMD1 0xa4 -#define NAND_DEV_CMD2 0xa8 -#define NAND_DEV_CMD_VLD 0xac -#define SFLASHC_BURST_CFG 0xe0 -#define NAND_ERASED_CW_DETECT_CFG 0xe8 -#define NAND_ERASED_CW_DETECT_STATUS 0xec -#define NAND_EBI2_ECC_BUF_CFG 0xf0 -#define FLASH_BUF_ACC 0x100 - -#define NAND_CTRL 0xf00 -#define NAND_VERSION 0xf08 -#define NAND_READ_LOCATION_0 0xf20 -#define NAND_READ_LOCATION_1 0xf24 -#define NAND_READ_LOCATION_2 0xf28 -#define NAND_READ_LOCATION_3 0xf2c - -/* dummy register offsets, used by write_reg_dma */ -#define NAND_DEV_CMD1_RESTORE 0xdead -#define NAND_DEV_CMD_VLD_RESTORE 0xbeef - -/* NAND_FLASH_CMD bits */ -#define PAGE_ACC BIT(4) -#define LAST_PAGE BIT(5) - -/* NAND_FLASH_CHIP_SELECT bits */ -#define NAND_DEV_SEL 0 -#define DM_EN BIT(2) - -/* NAND_FLASH_STATUS bits */ -#define FS_OP_ERR BIT(4) -#define FS_READY_BSY_N BIT(5) -#define FS_MPU_ERR BIT(8) -#define FS_DEVICE_STS_ERR BIT(16) -#define FS_DEVICE_WP BIT(23) - -/* NAND_BUFFER_STATUS bits */ -#define BS_UNCORRECTABLE_BIT BIT(8) -#define BS_CORRECTABLE_ERR_MSK 0x1f - -/* NAND_DEVn_CFG0 bits */ -#define DISABLE_STATUS_AFTER_WRITE 4 -#define CW_PER_PAGE 6 -#define UD_SIZE_BYTES 9 -#define ECC_PARITY_SIZE_BYTES_RS 19 -#define SPARE_SIZE_BYTES 23 -#define NUM_ADDR_CYCLES 27 -#define STATUS_BFR_READ 30 -#define SET_RD_MODE_AFTER_STATUS 31 - -/* NAND_DEVn_CFG0 bits */ -#define DEV0_CFG1_ECC_DISABLE 0 -#define WIDE_FLASH 1 -#define NAND_RECOVERY_CYCLES 2 -#define CS_ACTIVE_BSY 5 -#define BAD_BLOCK_BYTE_NUM 6 -#define BAD_BLOCK_IN_SPARE_AREA 16 -#define WR_RD_BSY_GAP 17 -#define ENABLE_BCH_ECC 27 - -/* NAND_DEV0_ECC_CFG bits */ -#define ECC_CFG_ECC_DISABLE 0 -#define ECC_SW_RESET 1 -#define ECC_MODE 4 -#define ECC_PARITY_SIZE_BYTES_BCH 8 -#define ECC_NUM_DATA_BYTES 16 -#define ECC_FORCE_CLK_OPEN 30 - -/* NAND_DEV_CMD1 bits */ -#define READ_ADDR 0 - -/* NAND_DEV_CMD_VLD bits */ -#define READ_START_VLD BIT(0) -#define READ_STOP_VLD BIT(1) -#define WRITE_START_VLD BIT(2) -#define ERASE_START_VLD BIT(3) -#define SEQ_READ_START_VLD BIT(4) - -/* NAND_EBI2_ECC_BUF_CFG bits */ -#define NUM_STEPS 0 - -/* NAND_ERASED_CW_DETECT_CFG bits */ -#define ERASED_CW_ECC_MASK 1 -#define AUTO_DETECT_RES 0 -#define MASK_ECC (1 << ERASED_CW_ECC_MASK) -#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) -#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) -#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) -#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) - -/* NAND_ERASED_CW_DETECT_STATUS bits */ -#define PAGE_ALL_ERASED BIT(7) -#define CODEWORD_ALL_ERASED BIT(6) -#define PAGE_ERASED BIT(5) -#define CODEWORD_ERASED BIT(4) -#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) -#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) - -/* NAND_READ_LOCATION_n bits */ -#define READ_LOCATION_OFFSET 0 -#define READ_LOCATION_SIZE 16 -#define READ_LOCATION_LAST 31 - -/* Version Mask */ -#define NAND_VERSION_MAJOR_MASK 0xf0000000 -#define NAND_VERSION_MAJOR_SHIFT 28 -#define NAND_VERSION_MINOR_MASK 0x0fff0000 -#define NAND_VERSION_MINOR_SHIFT 16 - -/* NAND OP_CMDs */ -#define PAGE_READ 0x2 -#define PAGE_READ_WITH_ECC 0x3 -#define PAGE_READ_WITH_ECC_SPARE 0x4 -#define PROGRAM_PAGE 0x6 -#define PAGE_PROGRAM_WITH_ECC 0x7 -#define PROGRAM_PAGE_SPARE 0x9 -#define BLOCK_ERASE 0xa -#define FETCH_ID 0xb -#define RESET_DEVICE 0xd - -/* Default Value for NAND_DEV_CMD_VLD */ -#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ - ERASE_START_VLD | SEQ_READ_START_VLD) - -/* NAND_CTRL bits */ -#define BAM_MODE_EN BIT(0) - -/* - * the NAND controller performs reads/writes with ECC in 516 byte chunks. - * the driver calls the chunks 'step' or 'codeword' interchangeably - */ -#define NANDC_STEP_SIZE 512 - -/* - * the largest page size we support is 8K, this will have 16 steps/codewords - * of 512 bytes each - */ -#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) - -/* we read at most 3 registers per codeword scan */ -#define MAX_REG_RD (3 * MAX_NUM_STEPS) - -/* ECC modes supported by the controller */ -#define ECC_NONE BIT(0) -#define ECC_RS_4BIT BIT(1) -#define ECC_BCH_4BIT BIT(2) -#define ECC_BCH_8BIT BIT(3) - -#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \ -nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ - ((offset) << READ_LOCATION_OFFSET) | \ - ((size) << READ_LOCATION_SIZE) | \ - ((is_last) << READ_LOCATION_LAST)) - -/* - * Returns the actual register address for all NAND_DEV_ registers - * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) - */ -#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) - -/* Returns the NAND register physical address */ -#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) - -/* Returns the dma address for reg read buffer */ -#define reg_buf_dma_addr(chip, vaddr) \ - ((chip)->reg_read_dma + \ - ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf)) - -#define QPIC_PER_CW_CMD_ELEMENTS 32 -#define QPIC_PER_CW_CMD_SGL 32 -#define QPIC_PER_CW_DATA_SGL 8 - -/* - * Flags used in DMA descriptor preparation helper functions - * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) - */ -/* Don't set the EOT in current tx BAM sgl */ -#define NAND_BAM_NO_EOT BIT(0) -/* Set the NWD flag in current BAM sgl */ -#define NAND_BAM_NWD BIT(1) -/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ -#define NAND_BAM_NEXT_SGL BIT(2) -/* - * Erased codeword status is being used two times in single transfer so this - * flag will determine the current value of erased codeword status register - */ -#define NAND_ERASED_CW_SET BIT(4) - -/* - * This data type corresponds to the BAM transaction which will be used for all - * NAND transfers. - * @bam_ce - the array of BAM command elements - * @cmd_sgl - sgl for NAND BAM command pipe - * @data_sgl - sgl for NAND BAM consumer/producer pipe - * @bam_ce_pos - the index in bam_ce which is available for next sgl - * @bam_ce_start - the index in bam_ce which marks the start position ce - * for current sgl. It will be used for size calculation - * for current sgl - * @cmd_sgl_pos - current index in command sgl. - * @cmd_sgl_start - start index in command sgl. - * @tx_sgl_pos - current index in data sgl for tx. - * @tx_sgl_start - start index in data sgl for tx. - * @rx_sgl_pos - current index in data sgl for rx. - * @rx_sgl_start - start index in data sgl for rx. - */ -struct bam_transaction { - struct bam_cmd_element *bam_ce; - struct scatterlist *cmd_sgl; - struct scatterlist *data_sgl; - u32 bam_ce_pos; - u32 bam_ce_start; - u32 cmd_sgl_pos; - u32 cmd_sgl_start; - u32 tx_sgl_pos; - u32 tx_sgl_start; - u32 rx_sgl_pos; - u32 rx_sgl_start; -}; - -/* - * This data type corresponds to the nand dma descriptor - * @list - list for desc_info - * @dir - DMA transfer direction - * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by - * ADM - * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM - * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM - * @dma_desc - low level DMA engine descriptor - */ -struct desc_info { - struct list_head node; - - enum dma_data_direction dir; - union { - struct scatterlist adm_sgl; - struct { - struct scatterlist *bam_sgl; - int sgl_cnt; - }; - }; - struct dma_async_tx_descriptor *dma_desc; -}; - -/* - * holds the current register values that we want to write. acts as a contiguous - * chunk of memory which we use to write the controller registers through DMA. - */ -struct nandc_regs { - __le32 cmd; - __le32 addr0; - __le32 addr1; - __le32 chip_sel; - __le32 exec; - - __le32 cfg0; - __le32 cfg1; - __le32 ecc_bch_cfg; - - __le32 clrflashstatus; - __le32 clrreadstatus; - - __le32 cmd1; - __le32 vld; - - __le32 orig_cmd1; - __le32 orig_vld; - - __le32 ecc_buf_cfg; - __le32 read_location0; - __le32 read_location1; - __le32 read_location2; - __le32 read_location3; - - __le32 erased_cw_detect_cfg_clr; - __le32 erased_cw_detect_cfg_set; -}; - -/* - * NAND controller data struct - * - * @controller: base controller structure - * @host_list: list containing all the chips attached to the - * controller - * @dev: parent device - * @base: MMIO base - * @base_phys: physical base address of controller registers - * @base_dma: dma base address of controller registers - * @core_clk: controller clock - * @aon_clk: another controller clock - * - * @chan: dma channel - * @cmd_crci: ADM DMA CRCI for command flow control - * @data_crci: ADM DMA CRCI for data flow control - * @desc_list: DMA descriptor list (list of desc_infos) - * - * @data_buffer: our local DMA buffer for page read/writes, - * used when we can't use the buffer provided - * by upper layers directly - * @buf_size/count/start: markers for chip->read_buf/write_buf functions - * @reg_read_buf: local buffer for reading back registers via DMA - * @reg_read_dma: contains dma address for register read buffer - * @reg_read_pos: marker for data read in reg_read_buf - * - * @regs: a contiguous chunk of memory for DMA register - * writes. contains the register values to be - * written to controller - * @cmd1/vld: some fixed controller register values - * @props: properties of current NAND controller, - * initialized via DT match data - * @max_cwperpage: maximum QPIC codewords required. calculated - * from all connected NAND devices pagesize - */ -struct qcom_nand_controller { - struct nand_hw_control controller; - struct list_head host_list; - - struct device *dev; - - void __iomem *base; - phys_addr_t base_phys; - dma_addr_t base_dma; - - struct clk *core_clk; - struct clk *aon_clk; - - union { - /* will be used only by QPIC for BAM DMA */ - struct { - struct dma_chan *tx_chan; - struct dma_chan *rx_chan; - struct dma_chan *cmd_chan; - }; - - /* will be used only by EBI2 for ADM DMA */ - struct { - struct dma_chan *chan; - unsigned int cmd_crci; - unsigned int data_crci; - }; - }; - - struct list_head desc_list; - struct bam_transaction *bam_txn; - - u8 *data_buffer; - int buf_size; - int buf_count; - int buf_start; - unsigned int max_cwperpage; - - __le32 *reg_read_buf; - dma_addr_t reg_read_dma; - int reg_read_pos; - - struct nandc_regs *regs; - - u32 cmd1, vld; - const struct qcom_nandc_props *props; -}; - -/* - * NAND chip structure - * - * @chip: base NAND chip structure - * @node: list node to add itself to host_list in - * qcom_nand_controller - * - * @cs: chip select value for this chip - * @cw_size: the number of bytes in a single step/codeword - * of a page, consisting of all data, ecc, spare - * and reserved bytes - * @cw_data: the number of bytes within a codeword protected - * by ECC - * @use_ecc: request the controller to use ECC for the - * upcoming read/write - * @bch_enabled: flag to tell whether BCH ECC mode is used - * @ecc_bytes_hw: ECC bytes used by controller hardware for this - * chip - * @status: value to be returned if NAND_CMD_STATUS command - * is executed - * @last_command: keeps track of last command on this chip. used - * for reading correct status - * - * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for - * ecc/non-ecc mode for the current nand flash - * device - */ -struct qcom_nand_host { - struct nand_chip chip; - struct list_head node; - - int cs; - int cw_size; - int cw_data; - bool use_ecc; - bool bch_enabled; - int ecc_bytes_hw; - int spare_bytes; - int bbm_size; - u8 status; - int last_command; - - u32 cfg0, cfg1; - u32 cfg0_raw, cfg1_raw; - u32 ecc_buf_cfg; - u32 ecc_bch_cfg; - u32 clrflashstatus; - u32 clrreadstatus; -}; - -/* - * This data type corresponds to the NAND controller properties which varies - * among different NAND controllers. - * @ecc_modes - ecc mode for NAND - * @is_bam - whether NAND controller is using BAM - * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset - */ -struct qcom_nandc_props { - u32 ecc_modes; - bool is_bam; - u32 dev_cmd_reg_start; -}; - -/* Frees the BAM transaction memory */ -static void free_bam_transaction(struct qcom_nand_controller *nandc) -{ - struct bam_transaction *bam_txn = nandc->bam_txn; - - devm_kfree(nandc->dev, bam_txn); -} - -/* Allocates and Initializes the BAM transaction */ -static struct bam_transaction * -alloc_bam_transaction(struct qcom_nand_controller *nandc) -{ - struct bam_transaction *bam_txn; - size_t bam_txn_size; - unsigned int num_cw = nandc->max_cwperpage; - void *bam_txn_buf; - - bam_txn_size = - sizeof(*bam_txn) + num_cw * - ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + - (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + - (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); - - bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); - if (!bam_txn_buf) - return NULL; - - bam_txn = bam_txn_buf; - bam_txn_buf += sizeof(*bam_txn); - - bam_txn->bam_ce = bam_txn_buf; - bam_txn_buf += - sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; - - bam_txn->cmd_sgl = bam_txn_buf; - bam_txn_buf += - sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; - - bam_txn->data_sgl = bam_txn_buf; - - return bam_txn; -} - -/* Clears the BAM transaction indexes */ -static void clear_bam_transaction(struct qcom_nand_controller *nandc) -{ - struct bam_transaction *bam_txn = nandc->bam_txn; - - if (!nandc->props->is_bam) - return; - - bam_txn->bam_ce_pos = 0; - bam_txn->bam_ce_start = 0; - bam_txn->cmd_sgl_pos = 0; - bam_txn->cmd_sgl_start = 0; - bam_txn->tx_sgl_pos = 0; - bam_txn->tx_sgl_start = 0; - bam_txn->rx_sgl_pos = 0; - bam_txn->rx_sgl_start = 0; - - sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * - QPIC_PER_CW_CMD_SGL); - sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * - QPIC_PER_CW_DATA_SGL); -} - -static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) -{ - return container_of(chip, struct qcom_nand_host, chip); -} - -static inline struct qcom_nand_controller * -get_qcom_nand_controller(struct nand_chip *chip) -{ - return container_of(chip->controller, struct qcom_nand_controller, - controller); -} - -static inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset) -{ - return ioread32(nandc->base + offset); -} - -static inline void nandc_write(struct qcom_nand_controller *nandc, int offset, - u32 val) -{ - iowrite32(val, nandc->base + offset); -} - -static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, - bool is_cpu) -{ - if (!nandc->props->is_bam) - return; - - if (is_cpu) - dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, - MAX_REG_RD * - sizeof(*nandc->reg_read_buf), - DMA_FROM_DEVICE); - else - dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, - MAX_REG_RD * - sizeof(*nandc->reg_read_buf), - DMA_FROM_DEVICE); -} - -static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) -{ - switch (offset) { - case NAND_FLASH_CMD: - return ®s->cmd; - case NAND_ADDR0: - return ®s->addr0; - case NAND_ADDR1: - return ®s->addr1; - case NAND_FLASH_CHIP_SELECT: - return ®s->chip_sel; - case NAND_EXEC_CMD: - return ®s->exec; - case NAND_FLASH_STATUS: - return ®s->clrflashstatus; - case NAND_DEV0_CFG0: - return ®s->cfg0; - case NAND_DEV0_CFG1: - return ®s->cfg1; - case NAND_DEV0_ECC_CFG: - return ®s->ecc_bch_cfg; - case NAND_READ_STATUS: - return ®s->clrreadstatus; - case NAND_DEV_CMD1: - return ®s->cmd1; - case NAND_DEV_CMD1_RESTORE: - return ®s->orig_cmd1; - case NAND_DEV_CMD_VLD: - return ®s->vld; - case NAND_DEV_CMD_VLD_RESTORE: - return ®s->orig_vld; - case NAND_EBI2_ECC_BUF_CFG: - return ®s->ecc_buf_cfg; - case NAND_READ_LOCATION_0: - return ®s->read_location0; - case NAND_READ_LOCATION_1: - return ®s->read_location1; - case NAND_READ_LOCATION_2: - return ®s->read_location2; - case NAND_READ_LOCATION_3: - return ®s->read_location3; - default: - return NULL; - } -} - -static void nandc_set_reg(struct qcom_nand_controller *nandc, int offset, - u32 val) -{ - struct nandc_regs *regs = nandc->regs; - __le32 *reg; - - reg = offset_to_nandc_reg(regs, offset); - - if (reg) - *reg = cpu_to_le32(val); -} - -/* helper to configure address register values */ -static void set_address(struct qcom_nand_host *host, u16 column, int page) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - if (chip->options & NAND_BUSWIDTH_16) - column >>= 1; - - nandc_set_reg(nandc, NAND_ADDR0, page << 16 | column); - nandc_set_reg(nandc, NAND_ADDR1, page >> 16 & 0xff); -} - -/* - * update_rw_regs: set up read/write register values, these will be - * written to the NAND controller registers via DMA - * - * @num_cw: number of steps for the read/write operation - * @read: read or write operation - */ -static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - u32 cmd, cfg0, cfg1, ecc_bch_cfg; - - if (read) { - if (host->use_ecc) - cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; - else - cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; - } else { - cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; - } - - if (host->use_ecc) { - cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) | - (num_cw - 1) << CW_PER_PAGE; - - cfg1 = host->cfg1; - ecc_bch_cfg = host->ecc_bch_cfg; - } else { - cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) | - (num_cw - 1) << CW_PER_PAGE; - - cfg1 = host->cfg1_raw; - ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; - } - - nandc_set_reg(nandc, NAND_FLASH_CMD, cmd); - nandc_set_reg(nandc, NAND_DEV0_CFG0, cfg0); - nandc_set_reg(nandc, NAND_DEV0_CFG1, cfg1); - nandc_set_reg(nandc, NAND_DEV0_ECC_CFG, ecc_bch_cfg); - nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg); - nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); - nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - - if (read) - nandc_set_read_loc(nandc, 0, 0, host->use_ecc ? - host->cw_data : host->cw_size, 1); -} - -/* - * Maps the scatter gather list for DMA transfer and forms the DMA descriptor - * for BAM. This descriptor will be added in the NAND DMA descriptor queue - * which will be submitted to DMA engine. - */ -static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, - struct dma_chan *chan, - unsigned long flags) -{ - struct desc_info *desc; - struct scatterlist *sgl; - unsigned int sgl_cnt; - int ret; - struct bam_transaction *bam_txn = nandc->bam_txn; - enum dma_transfer_direction dir_eng; - struct dma_async_tx_descriptor *dma_desc; - - desc = kzalloc(sizeof(*desc), GFP_KERNEL); - if (!desc) - return -ENOMEM; - - if (chan == nandc->cmd_chan) { - sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; - sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; - bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; - dir_eng = DMA_MEM_TO_DEV; - desc->dir = DMA_TO_DEVICE; - } else if (chan == nandc->tx_chan) { - sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; - sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; - bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; - dir_eng = DMA_MEM_TO_DEV; - desc->dir = DMA_TO_DEVICE; - } else { - sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; - sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; - bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; - dir_eng = DMA_DEV_TO_MEM; - desc->dir = DMA_FROM_DEVICE; - } - - sg_mark_end(sgl + sgl_cnt - 1); - ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); - if (ret == 0) { - dev_err(nandc->dev, "failure in mapping desc\n"); - kfree(desc); - return -ENOMEM; - } - - desc->sgl_cnt = sgl_cnt; - desc->bam_sgl = sgl; - - dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, - flags); - - if (!dma_desc) { - dev_err(nandc->dev, "failure in prep desc\n"); - dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); - kfree(desc); - return -EINVAL; - } - - desc->dma_desc = dma_desc; - - list_add_tail(&desc->node, &nandc->desc_list); - - return 0; -} - -/* - * Prepares the command descriptor for BAM DMA which will be used for NAND - * register reads and writes. The command descriptor requires the command - * to be formed in command element type so this function uses the command - * element from bam transaction ce array and fills the same with required - * data. A single SGL can contain multiple command elements so - * NAND_BAM_NEXT_SGL will be used for starting the separate SGL - * after the current command element. - */ -static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, - int reg_off, const void *vaddr, - int size, unsigned int flags) -{ - int bam_ce_size; - int i, ret; - struct bam_cmd_element *bam_ce_buffer; - struct bam_transaction *bam_txn = nandc->bam_txn; - - bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; - - /* fill the command desc */ - for (i = 0; i < size; i++) { - if (read) - bam_prep_ce(&bam_ce_buffer[i], - nandc_reg_phys(nandc, reg_off + 4 * i), - BAM_READ_COMMAND, - reg_buf_dma_addr(nandc, - (__le32 *)vaddr + i)); - else - bam_prep_ce_le32(&bam_ce_buffer[i], - nandc_reg_phys(nandc, reg_off + 4 * i), - BAM_WRITE_COMMAND, - *((__le32 *)vaddr + i)); - } - - bam_txn->bam_ce_pos += size; - - /* use the separate sgl after this command */ - if (flags & NAND_BAM_NEXT_SGL) { - bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; - bam_ce_size = (bam_txn->bam_ce_pos - - bam_txn->bam_ce_start) * - sizeof(struct bam_cmd_element); - sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], - bam_ce_buffer, bam_ce_size); - bam_txn->cmd_sgl_pos++; - bam_txn->bam_ce_start = bam_txn->bam_ce_pos; - - if (flags & NAND_BAM_NWD) { - ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, - DMA_PREP_FENCE | - DMA_PREP_CMD); - if (ret) - return ret; - } - } - - return 0; -} - -/* - * Prepares the data descriptor for BAM DMA which will be used for NAND - * data reads and writes. - */ -static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, - const void *vaddr, - int size, unsigned int flags) -{ - int ret; - struct bam_transaction *bam_txn = nandc->bam_txn; - - if (read) { - sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], - vaddr, size); - bam_txn->rx_sgl_pos++; - } else { - sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], - vaddr, size); - bam_txn->tx_sgl_pos++; - - /* - * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag - * is not set, form the DMA descriptor - */ - if (!(flags & NAND_BAM_NO_EOT)) { - ret = prepare_bam_async_desc(nandc, nandc->tx_chan, - DMA_PREP_INTERRUPT); - if (ret) - return ret; - } - } - - return 0; -} - -static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, - int reg_off, const void *vaddr, int size, - bool flow_control) -{ - struct desc_info *desc; - struct dma_async_tx_descriptor *dma_desc; - struct scatterlist *sgl; - struct dma_slave_config slave_conf; - enum dma_transfer_direction dir_eng; - int ret; - - desc = kzalloc(sizeof(*desc), GFP_KERNEL); - if (!desc) - return -ENOMEM; - - sgl = &desc->adm_sgl; - - sg_init_one(sgl, vaddr, size); - - if (read) { - dir_eng = DMA_DEV_TO_MEM; - desc->dir = DMA_FROM_DEVICE; - } else { - dir_eng = DMA_MEM_TO_DEV; - desc->dir = DMA_TO_DEVICE; - } - - ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); - if (ret == 0) { - ret = -ENOMEM; - goto err; - } - - memset(&slave_conf, 0x00, sizeof(slave_conf)); - - slave_conf.device_fc = flow_control; - if (read) { - slave_conf.src_maxburst = 16; - slave_conf.src_addr = nandc->base_dma + reg_off; - slave_conf.slave_id = nandc->data_crci; - } else { - slave_conf.dst_maxburst = 16; - slave_conf.dst_addr = nandc->base_dma + reg_off; - slave_conf.slave_id = nandc->cmd_crci; - } - - ret = dmaengine_slave_config(nandc->chan, &slave_conf); - if (ret) { - dev_err(nandc->dev, "failed to configure dma channel\n"); - goto err; - } - - dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); - if (!dma_desc) { - dev_err(nandc->dev, "failed to prepare desc\n"); - ret = -EINVAL; - goto err; - } - - desc->dma_desc = dma_desc; - - list_add_tail(&desc->node, &nandc->desc_list); - - return 0; -err: - kfree(desc); - - return ret; -} - -/* - * read_reg_dma: prepares a descriptor to read a given number of - * contiguous registers to the reg_read_buf pointer - * - * @first: offset of the first register in the contiguous block - * @num_regs: number of registers to read - * @flags: flags to control DMA descriptor preparation - */ -static int read_reg_dma(struct qcom_nand_controller *nandc, int first, - int num_regs, unsigned int flags) -{ - bool flow_control = false; - void *vaddr; - - vaddr = nandc->reg_read_buf + nandc->reg_read_pos; - nandc->reg_read_pos += num_regs; - - if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) - first = dev_cmd_reg_addr(nandc, first); - - if (nandc->props->is_bam) - return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, - num_regs, flags); - - if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) - flow_control = true; - - return prep_adm_dma_desc(nandc, true, first, vaddr, - num_regs * sizeof(u32), flow_control); -} - -/* - * write_reg_dma: prepares a descriptor to write a given number of - * contiguous registers - * - * @first: offset of the first register in the contiguous block - * @num_regs: number of registers to write - * @flags: flags to control DMA descriptor preparation - */ -static int write_reg_dma(struct qcom_nand_controller *nandc, int first, - int num_regs, unsigned int flags) -{ - bool flow_control = false; - struct nandc_regs *regs = nandc->regs; - void *vaddr; - - vaddr = offset_to_nandc_reg(regs, first); - - if (first == NAND_ERASED_CW_DETECT_CFG) { - if (flags & NAND_ERASED_CW_SET) - vaddr = ®s->erased_cw_detect_cfg_set; - else - vaddr = ®s->erased_cw_detect_cfg_clr; - } - - if (first == NAND_EXEC_CMD) - flags |= NAND_BAM_NWD; - - if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) - first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); - - if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) - first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); - - if (nandc->props->is_bam) - return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, - num_regs, flags); - - if (first == NAND_FLASH_CMD) - flow_control = true; - - return prep_adm_dma_desc(nandc, false, first, vaddr, - num_regs * sizeof(u32), flow_control); -} - -/* - * read_data_dma: prepares a DMA descriptor to transfer data from the - * controller's internal buffer to the buffer 'vaddr' - * - * @reg_off: offset within the controller's data buffer - * @vaddr: virtual address of the buffer we want to write to - * @size: DMA transaction size in bytes - * @flags: flags to control DMA descriptor preparation - */ -static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, - const u8 *vaddr, int size, unsigned int flags) -{ - if (nandc->props->is_bam) - return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); - - return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); -} - -/* - * write_data_dma: prepares a DMA descriptor to transfer data from - * 'vaddr' to the controller's internal buffer - * - * @reg_off: offset within the controller's data buffer - * @vaddr: virtual address of the buffer we want to read from - * @size: DMA transaction size in bytes - * @flags: flags to control DMA descriptor preparation - */ -static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, - const u8 *vaddr, int size, unsigned int flags) -{ - if (nandc->props->is_bam) - return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); - - return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); -} - -/* - * Helper to prepare DMA descriptors for configuring registers - * before reading a NAND page. - */ -static void config_nand_page_read(struct qcom_nand_controller *nandc) -{ - write_reg_dma(nandc, NAND_ADDR0, 2, 0); - write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); - write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); - write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); - write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, - NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); -} - -/* - * Helper to prepare DMA descriptors for configuring registers - * before reading each codeword in NAND page. - */ -static void config_nand_cw_read(struct qcom_nand_controller *nandc) -{ - if (nandc->props->is_bam) - write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, - NAND_BAM_NEXT_SGL); - - write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); - read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, - NAND_BAM_NEXT_SGL); -} - -/* - * Helper to prepare dma descriptors to configure registers needed for reading a - * single codeword in page - */ -static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc) -{ - config_nand_page_read(nandc); - config_nand_cw_read(nandc); -} - -/* - * Helper to prepare DMA descriptors used to configure registers needed for - * before writing a NAND page. - */ -static void config_nand_page_write(struct qcom_nand_controller *nandc) -{ - write_reg_dma(nandc, NAND_ADDR0, 2, 0); - write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); - write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, - NAND_BAM_NEXT_SGL); -} - -/* - * Helper to prepare DMA descriptors for configuring registers - * before writing each codeword in NAND page. - */ -static void config_nand_cw_write(struct qcom_nand_controller *nandc) -{ - write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); - - write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); - write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); -} - -/* - * the following functions are used within chip->cmdfunc() to perform different - * NAND_CMD_* commands - */ - -/* sets up descriptors for NAND_CMD_PARAM */ -static int nandc_param(struct qcom_nand_host *host) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - /* - * NAND_CMD_PARAM is called before we know much about the FLASH chip - * in use. we configure the controller to perform a raw read of 512 - * bytes to read onfi params - */ - nandc_set_reg(nandc, NAND_FLASH_CMD, PAGE_READ | PAGE_ACC | LAST_PAGE); - nandc_set_reg(nandc, NAND_ADDR0, 0); - nandc_set_reg(nandc, NAND_ADDR1, 0); - nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE - | 512 << UD_SIZE_BYTES - | 5 << NUM_ADDR_CYCLES - | 0 << SPARE_SIZE_BYTES); - nandc_set_reg(nandc, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES - | 0 << CS_ACTIVE_BSY - | 17 << BAD_BLOCK_BYTE_NUM - | 1 << BAD_BLOCK_IN_SPARE_AREA - | 2 << WR_RD_BSY_GAP - | 0 << WIDE_FLASH - | 1 << DEV0_CFG1_ECC_DISABLE); - nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); - - /* configure CMD1 and VLD for ONFI param probing */ - nandc_set_reg(nandc, NAND_DEV_CMD_VLD, - (nandc->vld & ~READ_START_VLD)); - nandc_set_reg(nandc, NAND_DEV_CMD1, - (nandc->cmd1 & ~(0xFF << READ_ADDR)) - | NAND_CMD_PARAM << READ_ADDR); - - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - - nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); - nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); - nandc_set_read_loc(nandc, 0, 0, 512, 1); - - write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); - write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); - - nandc->buf_count = 512; - memset(nandc->data_buffer, 0xff, nandc->buf_count); - - config_nand_single_cw_page_read(nandc); - - read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, - nandc->buf_count, 0); - - /* restore CMD1 and VLD regs */ - write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); - write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); - - return 0; -} - -/* sets up descriptors for NAND_CMD_ERASE1 */ -static int erase_block(struct qcom_nand_host *host, int page_addr) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - nandc_set_reg(nandc, NAND_FLASH_CMD, - BLOCK_ERASE | PAGE_ACC | LAST_PAGE); - nandc_set_reg(nandc, NAND_ADDR0, page_addr); - nandc_set_reg(nandc, NAND_ADDR1, 0); - nandc_set_reg(nandc, NAND_DEV0_CFG0, - host->cfg0_raw & ~(7 << CW_PER_PAGE)); - nandc_set_reg(nandc, NAND_DEV0_CFG1, host->cfg1_raw); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); - nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); - - write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); - - write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); - write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); - - return 0; -} - -/* sets up descriptors for NAND_CMD_READID */ -static int read_id(struct qcom_nand_host *host, int column) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - if (column == -1) - return 0; - - nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID); - nandc_set_reg(nandc, NAND_ADDR0, column); - nandc_set_reg(nandc, NAND_ADDR1, 0); - nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, - nandc->props->is_bam ? 0 : DM_EN); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - - write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); - - return 0; -} - -/* sets up descriptors for NAND_CMD_RESET */ -static int reset(struct qcom_nand_host *host) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - - write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); - - return 0; -} - -/* helpers to submit/free our list of dma descriptors */ -static int submit_descs(struct qcom_nand_controller *nandc) -{ - struct desc_info *desc; - dma_cookie_t cookie = 0; - struct bam_transaction *bam_txn = nandc->bam_txn; - int r; - - if (nandc->props->is_bam) { - if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { - r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); - if (r) - return r; - } - - if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { - r = prepare_bam_async_desc(nandc, nandc->tx_chan, - DMA_PREP_INTERRUPT); - if (r) - return r; - } - - if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { - r = prepare_bam_async_desc(nandc, nandc->cmd_chan, - DMA_PREP_CMD); - if (r) - return r; - } - } - - list_for_each_entry(desc, &nandc->desc_list, node) - cookie = dmaengine_submit(desc->dma_desc); - - if (nandc->props->is_bam) { - dma_async_issue_pending(nandc->tx_chan); - dma_async_issue_pending(nandc->rx_chan); - - if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE) - return -ETIMEDOUT; - } else { - if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) - return -ETIMEDOUT; - } - - return 0; -} - -static void free_descs(struct qcom_nand_controller *nandc) -{ - struct desc_info *desc, *n; - - list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { - list_del(&desc->node); - - if (nandc->props->is_bam) - dma_unmap_sg(nandc->dev, desc->bam_sgl, - desc->sgl_cnt, desc->dir); - else - dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, - desc->dir); - - kfree(desc); - } -} - -/* reset the register read buffer for next NAND operation */ -static void clear_read_regs(struct qcom_nand_controller *nandc) -{ - nandc->reg_read_pos = 0; - nandc_read_buffer_sync(nandc, false); -} - -static void pre_command(struct qcom_nand_host *host, int command) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - nandc->buf_count = 0; - nandc->buf_start = 0; - host->use_ecc = false; - host->last_command = command; - - clear_read_regs(nandc); - - if (command == NAND_CMD_RESET || command == NAND_CMD_READID || - command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) - clear_bam_transaction(nandc); -} - -/* - * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our - * privately maintained status byte, this status byte can be read after - * NAND_CMD_STATUS is called - */ -static void parse_erase_write_errors(struct qcom_nand_host *host, int command) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int num_cw; - int i; - - num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; - nandc_read_buffer_sync(nandc, true); - - for (i = 0; i < num_cw; i++) { - u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); - - if (flash_status & FS_MPU_ERR) - host->status &= ~NAND_STATUS_WP; - - if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && - (flash_status & - FS_DEVICE_STS_ERR))) - host->status |= NAND_STATUS_FAIL; - } -} - -static void post_command(struct qcom_nand_host *host, int command) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - switch (command) { - case NAND_CMD_READID: - nandc_read_buffer_sync(nandc, true); - memcpy(nandc->data_buffer, nandc->reg_read_buf, - nandc->buf_count); - break; - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - parse_erase_write_errors(host, command); - break; - default: - break; - } -} - -/* - * Implements chip->cmdfunc. It's only used for a limited set of commands. - * The rest of the commands wouldn't be called by upper layers. For example, - * NAND_CMD_READOOB would never be called because we have our own versions - * of read_oob ops for nand_ecc_ctrl. - */ -static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - bool wait = false; - int ret = 0; - - pre_command(host, command); - - switch (command) { - case NAND_CMD_RESET: - ret = reset(host); - wait = true; - break; - - case NAND_CMD_READID: - nandc->buf_count = 4; - ret = read_id(host, column); - wait = true; - break; - - case NAND_CMD_PARAM: - ret = nandc_param(host); - wait = true; - break; - - case NAND_CMD_ERASE1: - ret = erase_block(host, page_addr); - wait = true; - break; - - case NAND_CMD_READ0: - /* we read the entire page for now */ - WARN_ON(column != 0); - - host->use_ecc = true; - set_address(host, 0, page_addr); - update_rw_regs(host, ecc->steps, true); - break; - - case NAND_CMD_SEQIN: - WARN_ON(column != 0); - set_address(host, 0, page_addr); - break; - - case NAND_CMD_PAGEPROG: - case NAND_CMD_STATUS: - case NAND_CMD_NONE: - default: - break; - } - - if (ret) { - dev_err(nandc->dev, "failure executing command %d\n", - command); - free_descs(nandc); - return; - } - - if (wait) { - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, - "failure submitting descs for command %d\n", - command); - } - - free_descs(nandc); - - post_command(host, command); -} - -/* - * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read - * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS. - * - * when using RS ECC, the HW reports the same erros when reading an erased CW, - * but it notifies that it is an erased CW by placing special characters at - * certain offsets in the buffer. - * - * verify if the page is erased or not, and fix up the page for RS ECC by - * replacing the special characters with 0xff. - */ -static bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len) -{ - u8 empty1, empty2; - - /* - * an erased page flags an error in NAND_FLASH_STATUS, check if the page - * is erased by looking for 0x54s at offsets 3 and 175 from the - * beginning of each codeword - */ - - empty1 = data_buf[3]; - empty2 = data_buf[175]; - - /* - * if the erased codework markers, if they exist override them with - * 0xffs - */ - if ((empty1 == 0x54 && empty2 == 0xff) || - (empty1 == 0xff && empty2 == 0x54)) { - data_buf[3] = 0xff; - data_buf[175] = 0xff; - } - - /* - * check if the entire chunk contains 0xffs or not. if it doesn't, then - * restore the original values at the special offsets - */ - if (memchr_inv(data_buf, 0xff, data_len)) { - data_buf[3] = empty1; - data_buf[175] = empty2; - - return false; - } - - return true; -} - -struct read_stats { - __le32 flash; - __le32 buffer; - __le32 erased_cw; -}; - -/* - * reads back status registers set by the controller to notify page read - * errors. this is equivalent to what 'ecc->correct()' would do. - */ -static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, - u8 *oob_buf) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - unsigned int max_bitflips = 0; - struct read_stats *buf; - int i; - - buf = (struct read_stats *)nandc->reg_read_buf; - nandc_read_buffer_sync(nandc, true); - - for (i = 0; i < ecc->steps; i++, buf++) { - u32 flash, buffer, erased_cw; - int data_len, oob_len; - - if (i == (ecc->steps - 1)) { - data_len = ecc->size - ((ecc->steps - 1) << 2); - oob_len = ecc->steps << 2; - } else { - data_len = host->cw_data; - oob_len = 0; - } - - flash = le32_to_cpu(buf->flash); - buffer = le32_to_cpu(buf->buffer); - erased_cw = le32_to_cpu(buf->erased_cw); - - if (flash & (FS_OP_ERR | FS_MPU_ERR)) { - bool erased; - - /* ignore erased codeword errors */ - if (host->bch_enabled) { - erased = (erased_cw & ERASED_CW) == ERASED_CW ? - true : false; - } else { - erased = erased_chunk_check_and_fixup(data_buf, - data_len); - } - - if (erased) { - data_buf += data_len; - if (oob_buf) - oob_buf += oob_len + ecc->bytes; - continue; - } - - if (buffer & BS_UNCORRECTABLE_BIT) { - int ret, ecclen, extraooblen; - void *eccbuf; - - eccbuf = oob_buf ? oob_buf + oob_len : NULL; - ecclen = oob_buf ? host->ecc_bytes_hw : 0; - extraooblen = oob_buf ? oob_len : 0; - - /* - * make sure it isn't an erased page reported - * as not-erased by HW because of a few bitflips - */ - ret = nand_check_erased_ecc_chunk(data_buf, - data_len, eccbuf, ecclen, oob_buf, - extraooblen, ecc->strength); - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - max_bitflips = - max_t(unsigned int, max_bitflips, ret); - } - } - } else { - unsigned int stat; - - stat = buffer & BS_CORRECTABLE_ERR_MSK; - mtd->ecc_stats.corrected += stat; - max_bitflips = max(max_bitflips, stat); - } - - data_buf += data_len; - if (oob_buf) - oob_buf += oob_len + ecc->bytes; - } - - return max_bitflips; -} - -/* - * helper to perform the actual page read operation, used by ecc->read_page(), - * ecc->read_oob() - */ -static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, - u8 *oob_buf) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int i, ret; - - config_nand_page_read(nandc); - - /* queue cmd descs for each codeword */ - for (i = 0; i < ecc->steps; i++) { - int data_size, oob_size; - - if (i == (ecc->steps - 1)) { - data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + - host->spare_bytes; - } else { - data_size = host->cw_data; - oob_size = host->ecc_bytes_hw + host->spare_bytes; - } - - if (nandc->props->is_bam) { - if (data_buf && oob_buf) { - nandc_set_read_loc(nandc, 0, 0, data_size, 0); - nandc_set_read_loc(nandc, 1, data_size, - oob_size, 1); - } else if (data_buf) { - nandc_set_read_loc(nandc, 0, 0, data_size, 1); - } else { - nandc_set_read_loc(nandc, 0, data_size, - oob_size, 1); - } - } - - config_nand_cw_read(nandc); - - if (data_buf) - read_data_dma(nandc, FLASH_BUF_ACC, data_buf, - data_size, 0); - - /* - * when ecc is enabled, the controller doesn't read the real - * or dummy bad block markers in each chunk. To maintain a - * consistent layout across RAW and ECC reads, we just - * leave the real/dummy BBM offsets empty (i.e, filled with - * 0xffs) - */ - if (oob_buf) { - int j; - - for (j = 0; j < host->bbm_size; j++) - *oob_buf++ = 0xff; - - read_data_dma(nandc, FLASH_BUF_ACC + data_size, - oob_buf, oob_size, 0); - } - - if (data_buf) - data_buf += data_size; - if (oob_buf) - oob_buf += oob_size; - } - - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failure to read page/oob\n"); - - free_descs(nandc); - - return ret; -} - -/* - * a helper that copies the last step/codeword of a page (containing free oob) - * into our local buffer - */ -static int copy_last_cw(struct qcom_nand_host *host, int page) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int size; - int ret; - - clear_read_regs(nandc); - - size = host->use_ecc ? host->cw_data : host->cw_size; - - /* prepare a clean read buffer */ - memset(nandc->data_buffer, 0xff, size); - - set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, true); - - config_nand_single_cw_page_read(nandc); - - read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); - - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failed to copy last codeword\n"); - - free_descs(nandc); - - return ret; -} - -/* implements ecc->read_page() */ -static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - u8 *data_buf, *oob_buf = NULL; - int ret; - - nand_read_page_op(chip, page, 0, NULL, 0); - data_buf = buf; - oob_buf = oob_required ? chip->oob_poi : NULL; - - clear_bam_transaction(nandc); - ret = read_page_ecc(host, data_buf, oob_buf); - if (ret) { - dev_err(nandc->dev, "failure to read page\n"); - return ret; - } - - return parse_read_errors(host, data_buf, oob_buf); -} - -/* implements ecc->read_page_raw() */ -static int qcom_nandc_read_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - u8 *data_buf, *oob_buf; - struct nand_ecc_ctrl *ecc = &chip->ecc; - int i, ret; - int read_loc; - - nand_read_page_op(chip, page, 0, NULL, 0); - data_buf = buf; - oob_buf = chip->oob_poi; - - host->use_ecc = false; - - clear_bam_transaction(nandc); - update_rw_regs(host, ecc->steps, true); - config_nand_page_read(nandc); - - for (i = 0; i < ecc->steps; i++) { - int data_size1, data_size2, oob_size1, oob_size2; - int reg_off = FLASH_BUF_ACC; - - data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); - oob_size1 = host->bbm_size; - - if (i == (ecc->steps - 1)) { - data_size2 = ecc->size - data_size1 - - ((ecc->steps - 1) << 2); - oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + - host->spare_bytes; - } else { - data_size2 = host->cw_data - data_size1; - oob_size2 = host->ecc_bytes_hw + host->spare_bytes; - } - - if (nandc->props->is_bam) { - read_loc = 0; - nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); - read_loc += data_size1; - - nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); - read_loc += oob_size1; - - nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); - read_loc += data_size2; - - nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); - } - - config_nand_cw_read(nandc); - - read_data_dma(nandc, reg_off, data_buf, data_size1, 0); - reg_off += data_size1; - data_buf += data_size1; - - read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); - reg_off += oob_size1; - oob_buf += oob_size1; - - read_data_dma(nandc, reg_off, data_buf, data_size2, 0); - reg_off += data_size2; - data_buf += data_size2; - - read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); - oob_buf += oob_size2; - } - - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failure to read raw page\n"); - - free_descs(nandc); - - return 0; -} - -/* implements ecc->read_oob() */ -static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret; - - clear_read_regs(nandc); - clear_bam_transaction(nandc); - - host->use_ecc = true; - set_address(host, 0, page); - update_rw_regs(host, ecc->steps, true); - - ret = read_page_ecc(host, NULL, chip->oob_poi); - if (ret) - dev_err(nandc->dev, "failure to read oob\n"); - - return ret; -} - -/* implements ecc->write_page() */ -static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - u8 *data_buf, *oob_buf; - int i, ret; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - clear_read_regs(nandc); - clear_bam_transaction(nandc); - - data_buf = (u8 *)buf; - oob_buf = chip->oob_poi; - - host->use_ecc = true; - update_rw_regs(host, ecc->steps, false); - config_nand_page_write(nandc); - - for (i = 0; i < ecc->steps; i++) { - int data_size, oob_size; - - if (i == (ecc->steps - 1)) { - data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + - host->spare_bytes; - } else { - data_size = host->cw_data; - oob_size = ecc->bytes; - } - - - write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, - i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); - - /* - * when ECC is enabled, we don't really need to write anything - * to oob for the first n - 1 codewords since these oob regions - * just contain ECC bytes that's written by the controller - * itself. For the last codeword, we skip the bbm positions and - * write to the free oob area. - */ - if (i == (ecc->steps - 1)) { - oob_buf += host->bbm_size; - - write_data_dma(nandc, FLASH_BUF_ACC + data_size, - oob_buf, oob_size, 0); - } - - config_nand_cw_write(nandc); - - data_buf += data_size; - oob_buf += oob_size; - } - - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failure to write page\n"); - - free_descs(nandc); - - if (!ret) - ret = nand_prog_page_end_op(chip); - - return ret; -} - -/* implements ecc->write_page_raw() */ -static int qcom_nandc_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, - int oob_required, int page) -{ - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - u8 *data_buf, *oob_buf; - int i, ret; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - clear_read_regs(nandc); - clear_bam_transaction(nandc); - - data_buf = (u8 *)buf; - oob_buf = chip->oob_poi; - - host->use_ecc = false; - update_rw_regs(host, ecc->steps, false); - config_nand_page_write(nandc); - - for (i = 0; i < ecc->steps; i++) { - int data_size1, data_size2, oob_size1, oob_size2; - int reg_off = FLASH_BUF_ACC; - - data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); - oob_size1 = host->bbm_size; - - if (i == (ecc->steps - 1)) { - data_size2 = ecc->size - data_size1 - - ((ecc->steps - 1) << 2); - oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + - host->spare_bytes; - } else { - data_size2 = host->cw_data - data_size1; - oob_size2 = host->ecc_bytes_hw + host->spare_bytes; - } - - write_data_dma(nandc, reg_off, data_buf, data_size1, - NAND_BAM_NO_EOT); - reg_off += data_size1; - data_buf += data_size1; - - write_data_dma(nandc, reg_off, oob_buf, oob_size1, - NAND_BAM_NO_EOT); - reg_off += oob_size1; - oob_buf += oob_size1; - - write_data_dma(nandc, reg_off, data_buf, data_size2, - NAND_BAM_NO_EOT); - reg_off += data_size2; - data_buf += data_size2; - - write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); - oob_buf += oob_size2; - - config_nand_cw_write(nandc); - } - - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failure to write raw page\n"); - - free_descs(nandc); - - if (!ret) - ret = nand_prog_page_end_op(chip); - - return ret; -} - -/* - * implements ecc->write_oob() - * - * the NAND controller cannot write only data or only oob within a codeword, - * since ecc is calculated for the combined codeword. we first copy the - * entire contents for the last codeword(data + oob), replace the old oob - * with the new one in chip->oob_poi, and then write the entire codeword. - * this read-copy-write operation results in a slight performance loss. - */ -static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - u8 *oob = chip->oob_poi; - int data_size, oob_size; - int ret; - - host->use_ecc = true; - - clear_bam_transaction(nandc); - ret = copy_last_cw(host, page); - if (ret) - return ret; - - clear_read_regs(nandc); - clear_bam_transaction(nandc); - - /* calculate the data and oob size for the last codeword/step */ - data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = mtd->oobavail; - - /* override new oob content to last codeword */ - mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, - 0, mtd->oobavail); - - set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, false); - - config_nand_page_write(nandc); - write_data_dma(nandc, FLASH_BUF_ACC, - nandc->data_buffer, data_size + oob_size, 0); - config_nand_cw_write(nandc); - - ret = submit_descs(nandc); - - free_descs(nandc); - - if (ret) { - dev_err(nandc->dev, "failure to write oob\n"); - return -EIO; - } - - return nand_prog_page_end_op(chip); -} - -static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int page, ret, bbpos, bad = 0; - u32 flash_status; - - page = (int)(ofs >> chip->page_shift) & chip->pagemask; - - /* - * configure registers for a raw sub page read, the address is set to - * the beginning of the last codeword, we don't care about reading ecc - * portion of oob. we just want the first few bytes from this codeword - * that contains the BBM - */ - host->use_ecc = false; - - clear_bam_transaction(nandc); - ret = copy_last_cw(host, page); - if (ret) - goto err; - - flash_status = le32_to_cpu(nandc->reg_read_buf[0]); - - if (flash_status & (FS_OP_ERR | FS_MPU_ERR)) { - dev_warn(nandc->dev, "error when trying to read BBM\n"); - goto err; - } - - bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); - - bad = nandc->data_buffer[bbpos] != 0xff; - - if (chip->options & NAND_BUSWIDTH_16) - bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); -err: - return bad; -} - -static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int page, ret; - - clear_read_regs(nandc); - clear_bam_transaction(nandc); - - /* - * to mark the BBM as bad, we flash the entire last codeword with 0s. - * we don't care about the rest of the content in the codeword since - * we aren't going to use this block again - */ - memset(nandc->data_buffer, 0x00, host->cw_size); - - page = (int)(ofs >> chip->page_shift) & chip->pagemask; - - /* prepare write */ - host->use_ecc = false; - set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, false); - - config_nand_page_write(nandc); - write_data_dma(nandc, FLASH_BUF_ACC, - nandc->data_buffer, host->cw_size, 0); - config_nand_cw_write(nandc); - - ret = submit_descs(nandc); - - free_descs(nandc); - - if (ret) { - dev_err(nandc->dev, "failure to update BBM\n"); - return -EIO; - } - - return nand_prog_page_end_op(chip); -} - -/* - * the three functions below implement chip->read_byte(), chip->read_buf() - * and chip->write_buf() respectively. these aren't used for - * reading/writing page data, they are used for smaller data like reading - * id, status etc - */ -static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - u8 *buf = nandc->data_buffer; - u8 ret = 0x0; - - if (host->last_command == NAND_CMD_STATUS) { - ret = host->status; - - host->status = NAND_STATUS_READY | NAND_STATUS_WP; - - return ret; - } - - if (nandc->buf_start < nandc->buf_count) - ret = buf[nandc->buf_start++]; - - return ret; -} - -static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); - - memcpy(buf, nandc->data_buffer + nandc->buf_start, real_len); - nandc->buf_start += real_len; -} - -static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); - - memcpy(nandc->data_buffer + nandc->buf_start, buf, real_len); - - nandc->buf_start += real_len; -} - -/* we support only one external chip for now */ -static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - if (chipnr <= 0) - return; - - dev_warn(nandc->dev, "invalid chip select\n"); -} - -/* - * NAND controller page layout info - * - * Layout with ECC enabled: - * - * |----------------------| |---------------------------------| - * | xx.......yy| | *********xx.......yy| - * | DATA xx..ECC..yy| | DATA **SPARE**xx..ECC..yy| - * | (516) xx.......yy| | (516-n*4) **(n*4)**xx.......yy| - * | xx.......yy| | *********xx.......yy| - * |----------------------| |---------------------------------| - * codeword 1,2..n-1 codeword n - * <---(528/532 Bytes)--> <-------(528/532 Bytes)---------> - * - * n = Number of codewords in the page - * . = ECC bytes - * * = Spare/free bytes - * x = Unused byte(s) - * y = Reserved byte(s) - * - * 2K page: n = 4, spare = 16 bytes - * 4K page: n = 8, spare = 32 bytes - * 8K page: n = 16, spare = 64 bytes - * - * the qcom nand controller operates at a sub page/codeword level. each - * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively. - * the number of ECC bytes vary based on the ECC strength and the bus width. - * - * the first n - 1 codewords contains 516 bytes of user data, the remaining - * 12/16 bytes consist of ECC and reserved data. The nth codeword contains - * both user data and spare(oobavail) bytes that sum up to 516 bytes. - * - * When we access a page with ECC enabled, the reserved bytes(s) are not - * accessible at all. When reading, we fill up these unreadable positions - * with 0xffs. When writing, the controller skips writing the inaccessible - * bytes. - * - * Layout with ECC disabled: - * - * |------------------------------| |---------------------------------------| - * | yy xx.......| | bb *********xx.......| - * | DATA1 yy DATA2 xx..ECC..| | DATA1 bb DATA2 **SPARE**xx..ECC..| - * | (size1) yy (size2) xx.......| | (size1) bb (size2) **(n*4)**xx.......| - * | yy xx.......| | bb *********xx.......| - * |------------------------------| |---------------------------------------| - * codeword 1,2..n-1 codeword n - * <-------(528/532 Bytes)------> <-----------(528/532 Bytes)-----------> - * - * n = Number of codewords in the page - * . = ECC bytes - * * = Spare/free bytes - * x = Unused byte(s) - * y = Dummy Bad Bock byte(s) - * b = Real Bad Block byte(s) - * size1/size2 = function of codeword size and 'n' - * - * when the ECC block is disabled, one reserved byte (or two for 16 bit bus - * width) is now accessible. For the first n - 1 codewords, these are dummy Bad - * Block Markers. In the last codeword, this position contains the real BBM - * - * In order to have a consistent layout between RAW and ECC modes, we assume - * the following OOB layout arrangement: - * - * |-----------| |--------------------| - * |yyxx.......| |bb*********xx.......| - * |yyxx..ECC..| |bb*FREEOOB*xx..ECC..| - * |yyxx.......| |bb*********xx.......| - * |yyxx.......| |bb*********xx.......| - * |-----------| |--------------------| - * first n - 1 nth OOB region - * OOB regions - * - * n = Number of codewords in the page - * . = ECC bytes - * * = FREE OOB bytes - * y = Dummy bad block byte(s) (inaccessible when ECC enabled) - * x = Unused byte(s) - * b = Real bad block byte(s) (inaccessible when ECC enabled) - * - * This layout is read as is when ECC is disabled. When ECC is enabled, the - * inaccessible Bad Block byte(s) are ignored when we write to a page/oob, - * and assumed as 0xffs when we read a page/oob. The ECC, unused and - * dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is - * the sum of the three). - */ -static int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section > 1) - return -ERANGE; - - if (!section) { - oobregion->length = (ecc->bytes * (ecc->steps - 1)) + - host->bbm_size; - oobregion->offset = 0; - } else { - oobregion->length = host->ecc_bytes_hw + host->spare_bytes; - oobregion->offset = mtd->oobsize - oobregion->length; - } - - return 0; -} - -static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section) - return -ERANGE; - - oobregion->length = ecc->steps * 4; - oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size; - - return 0; -} - -static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = { - .ecc = qcom_nand_ooblayout_ecc, - .free = qcom_nand_ooblayout_free, -}; - -static int qcom_nand_host_setup(struct qcom_nand_host *host) -{ - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int cwperpage, bad_block_byte; - bool wide_bus; - int ecc_mode = 1; - - /* - * the controller requires each step consists of 512 bytes of data. - * bail out if DT has populated a wrong step size. - */ - if (ecc->size != NANDC_STEP_SIZE) { - dev_err(nandc->dev, "invalid ecc size\n"); - return -EINVAL; - } - - wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; - - if (ecc->strength >= 8) { - /* 8 bit ECC defaults to BCH ECC on all platforms */ - host->bch_enabled = true; - ecc_mode = 1; - - if (wide_bus) { - host->ecc_bytes_hw = 14; - host->spare_bytes = 0; - host->bbm_size = 2; - } else { - host->ecc_bytes_hw = 13; - host->spare_bytes = 2; - host->bbm_size = 1; - } - } else { - /* - * if the controller supports BCH for 4 bit ECC, the controller - * uses lesser bytes for ECC. If RS is used, the ECC bytes is - * always 10 bytes - */ - if (nandc->props->ecc_modes & ECC_BCH_4BIT) { - /* BCH */ - host->bch_enabled = true; - ecc_mode = 0; - - if (wide_bus) { - host->ecc_bytes_hw = 8; - host->spare_bytes = 2; - host->bbm_size = 2; - } else { - host->ecc_bytes_hw = 7; - host->spare_bytes = 4; - host->bbm_size = 1; - } - } else { - /* RS */ - host->ecc_bytes_hw = 10; - - if (wide_bus) { - host->spare_bytes = 0; - host->bbm_size = 2; - } else { - host->spare_bytes = 1; - host->bbm_size = 1; - } - } - } - - /* - * we consider ecc->bytes as the sum of all the non-data content in a - * step. It gives us a clean representation of the oob area (even if - * all the bytes aren't used for ECC).It is always 16 bytes for 8 bit - * ECC and 12 bytes for 4 bit ECC - */ - ecc->bytes = host->ecc_bytes_hw + host->spare_bytes + host->bbm_size; - - ecc->read_page = qcom_nandc_read_page; - ecc->read_page_raw = qcom_nandc_read_page_raw; - ecc->read_oob = qcom_nandc_read_oob; - ecc->write_page = qcom_nandc_write_page; - ecc->write_page_raw = qcom_nandc_write_page_raw; - ecc->write_oob = qcom_nandc_write_oob; - - ecc->mode = NAND_ECC_HW; - - mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); - - cwperpage = mtd->writesize / ecc->size; - nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, - cwperpage); - - /* - * DATA_UD_BYTES varies based on whether the read/write command protects - * spare data with ECC too. We protect spare data by default, so we set - * it to main + spare data, which are 512 and 4 bytes respectively. - */ - host->cw_data = 516; - - /* - * total bytes in a step, either 528 bytes for 4 bit ECC, or 532 bytes - * for 8 bit ECC - */ - host->cw_size = host->cw_data + ecc->bytes; - - if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) { - dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n"); - return -EINVAL; - } - - bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; - - host->cfg0 = (cwperpage - 1) << CW_PER_PAGE - | host->cw_data << UD_SIZE_BYTES - | 0 << DISABLE_STATUS_AFTER_WRITE - | 5 << NUM_ADDR_CYCLES - | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS - | 0 << STATUS_BFR_READ - | 1 << SET_RD_MODE_AFTER_STATUS - | host->spare_bytes << SPARE_SIZE_BYTES; - - host->cfg1 = 7 << NAND_RECOVERY_CYCLES - | 0 << CS_ACTIVE_BSY - | bad_block_byte << BAD_BLOCK_BYTE_NUM - | 0 << BAD_BLOCK_IN_SPARE_AREA - | 2 << WR_RD_BSY_GAP - | wide_bus << WIDE_FLASH - | host->bch_enabled << ENABLE_BCH_ECC; - - host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE - | host->cw_size << UD_SIZE_BYTES - | 5 << NUM_ADDR_CYCLES - | 0 << SPARE_SIZE_BYTES; - - host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES - | 0 << CS_ACTIVE_BSY - | 17 << BAD_BLOCK_BYTE_NUM - | 1 << BAD_BLOCK_IN_SPARE_AREA - | 2 << WR_RD_BSY_GAP - | wide_bus << WIDE_FLASH - | 1 << DEV0_CFG1_ECC_DISABLE; - - host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE - | 0 << ECC_SW_RESET - | host->cw_data << ECC_NUM_DATA_BYTES - | 1 << ECC_FORCE_CLK_OPEN - | ecc_mode << ECC_MODE - | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH; - - host->ecc_buf_cfg = 0x203 << NUM_STEPS; - - host->clrflashstatus = FS_READY_BSY_N; - host->clrreadstatus = 0xc0; - nandc->regs->erased_cw_detect_cfg_clr = - cpu_to_le32(CLR_ERASED_PAGE_DET); - nandc->regs->erased_cw_detect_cfg_set = - cpu_to_le32(SET_ERASED_PAGE_DET); - - dev_dbg(nandc->dev, - "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", - host->cfg0, host->cfg1, host->ecc_buf_cfg, host->ecc_bch_cfg, - host->cw_size, host->cw_data, ecc->strength, ecc->bytes, - cwperpage); - - return 0; -} - -static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) -{ - int ret; - - ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(nandc->dev, "failed to set DMA mask\n"); - return ret; - } - - /* - * we use the internal buffer for reading ONFI params, reading small - * data like ID and status, and preforming read-copy-write operations - * when writing to a codeword partially. 532 is the maximum possible - * size of a codeword for our nand controller - */ - nandc->buf_size = 532; - - nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, - GFP_KERNEL); - if (!nandc->data_buffer) - return -ENOMEM; - - nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), - GFP_KERNEL); - if (!nandc->regs) - return -ENOMEM; - - nandc->reg_read_buf = devm_kzalloc(nandc->dev, - MAX_REG_RD * sizeof(*nandc->reg_read_buf), - GFP_KERNEL); - if (!nandc->reg_read_buf) - return -ENOMEM; - - if (nandc->props->is_bam) { - nandc->reg_read_dma = - dma_map_single(nandc->dev, nandc->reg_read_buf, - MAX_REG_RD * - sizeof(*nandc->reg_read_buf), - DMA_FROM_DEVICE); - if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { - dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); - return -EIO; - } - - nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx"); - if (!nandc->tx_chan) { - dev_err(nandc->dev, "failed to request tx channel\n"); - return -ENODEV; - } - - nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx"); - if (!nandc->rx_chan) { - dev_err(nandc->dev, "failed to request rx channel\n"); - return -ENODEV; - } - - nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd"); - if (!nandc->cmd_chan) { - dev_err(nandc->dev, "failed to request cmd channel\n"); - return -ENODEV; - } - - /* - * Initially allocate BAM transaction to read ONFI param page. - * After detecting all the devices, this BAM transaction will - * be freed and the next BAM tranasction will be allocated with - * maximum codeword size - */ - nandc->max_cwperpage = 1; - nandc->bam_txn = alloc_bam_transaction(nandc); - if (!nandc->bam_txn) { - dev_err(nandc->dev, - "failed to allocate bam transaction\n"); - return -ENOMEM; - } - } else { - nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); - if (!nandc->chan) { - dev_err(nandc->dev, - "failed to request slave channel\n"); - return -ENODEV; - } - } - - INIT_LIST_HEAD(&nandc->desc_list); - INIT_LIST_HEAD(&nandc->host_list); - - nand_hw_control_init(&nandc->controller); - - return 0; -} - -static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) -{ - if (nandc->props->is_bam) { - if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) - dma_unmap_single(nandc->dev, nandc->reg_read_dma, - MAX_REG_RD * - sizeof(*nandc->reg_read_buf), - DMA_FROM_DEVICE); - - if (nandc->tx_chan) - dma_release_channel(nandc->tx_chan); - - if (nandc->rx_chan) - dma_release_channel(nandc->rx_chan); - - if (nandc->cmd_chan) - dma_release_channel(nandc->cmd_chan); - } else { - if (nandc->chan) - dma_release_channel(nandc->chan); - } -} - -/* one time setup of a few nand controller registers */ -static int qcom_nandc_setup(struct qcom_nand_controller *nandc) -{ - u32 nand_ctrl; - - /* kill onenand */ - nandc_write(nandc, SFLASHC_BURST_CFG, 0); - nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), - NAND_DEV_CMD_VLD_VAL); - - /* enable ADM or BAM DMA */ - if (nandc->props->is_bam) { - nand_ctrl = nandc_read(nandc, NAND_CTRL); - nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); - } else { - nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); - } - - /* save the original values of these registers */ - nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1)); - nandc->vld = NAND_DEV_CMD_VLD_VAL; - - return 0; -} - -static int qcom_nand_host_init(struct qcom_nand_controller *nandc, - struct qcom_nand_host *host, - struct device_node *dn) -{ - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - struct device *dev = nandc->dev; - int ret; - - ret = of_property_read_u32(dn, "reg", &host->cs); - if (ret) { - dev_err(dev, "can't get chip-select\n"); - return -ENXIO; - } - - nand_set_flash_node(chip, dn); - mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs); - if (!mtd->name) - return -ENOMEM; - - mtd->owner = THIS_MODULE; - mtd->dev.parent = dev; - - chip->cmdfunc = qcom_nandc_command; - chip->select_chip = qcom_nandc_select_chip; - chip->read_byte = qcom_nandc_read_byte; - chip->read_buf = qcom_nandc_read_buf; - chip->write_buf = qcom_nandc_write_buf; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; - - /* - * the bad block marker is readable only when we read the last codeword - * of a page with ECC disabled. currently, the nand_base and nand_bbt - * helpers don't allow us to read BB from a nand chip with ECC - * disabled (MTD_OPS_PLACE_OOB is set by default). use the block_bad - * and block_markbad helpers until we permanently switch to using - * MTD_OPS_RAW for all drivers (with the help of badblockbits) - */ - chip->block_bad = qcom_nandc_block_bad; - chip->block_markbad = qcom_nandc_block_markbad; - - chip->controller = &nandc->controller; - chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER | - NAND_SKIP_BBTSCAN; - - /* set up initial status value */ - host->status = NAND_STATUS_READY | NAND_STATUS_WP; - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - ret = qcom_nand_host_setup(host); - - return ret; -} - -static int qcom_nand_mtd_register(struct qcom_nand_controller *nandc, - struct qcom_nand_host *host, - struct device_node *dn) -{ - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - ret = nand_scan_tail(mtd); - if (ret) - return ret; - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) - nand_cleanup(mtd_to_nand(mtd)); - - return ret; -} - -static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc) -{ - struct device *dev = nandc->dev; - struct device_node *dn = dev->of_node, *child; - struct qcom_nand_host *host, *tmp; - int ret; - - for_each_available_child_of_node(dn, child) { - host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); - if (!host) { - of_node_put(child); - return -ENOMEM; - } - - ret = qcom_nand_host_init(nandc, host, child); - if (ret) { - devm_kfree(dev, host); - continue; - } - - list_add_tail(&host->node, &nandc->host_list); - } - - if (list_empty(&nandc->host_list)) - return -ENODEV; - - if (nandc->props->is_bam) { - free_bam_transaction(nandc); - nandc->bam_txn = alloc_bam_transaction(nandc); - if (!nandc->bam_txn) { - dev_err(nandc->dev, - "failed to allocate bam transaction\n"); - return -ENOMEM; - } - } - - list_for_each_entry_safe(host, tmp, &nandc->host_list, node) { - ret = qcom_nand_mtd_register(nandc, host, child); - if (ret) { - list_del(&host->node); - devm_kfree(dev, host); - } - } - - if (list_empty(&nandc->host_list)) - return -ENODEV; - - return 0; -} - -/* parse custom DT properties here */ -static int qcom_nandc_parse_dt(struct platform_device *pdev) -{ - struct qcom_nand_controller *nandc = platform_get_drvdata(pdev); - struct device_node *np = nandc->dev->of_node; - int ret; - - if (!nandc->props->is_bam) { - ret = of_property_read_u32(np, "qcom,cmd-crci", - &nandc->cmd_crci); - if (ret) { - dev_err(nandc->dev, "command CRCI unspecified\n"); - return ret; - } - - ret = of_property_read_u32(np, "qcom,data-crci", - &nandc->data_crci); - if (ret) { - dev_err(nandc->dev, "data CRCI unspecified\n"); - return ret; - } - } - - return 0; -} - -static int qcom_nandc_probe(struct platform_device *pdev) -{ - struct qcom_nand_controller *nandc; - const void *dev_data; - struct device *dev = &pdev->dev; - struct resource *res; - int ret; - - nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL); - if (!nandc) - return -ENOMEM; - - platform_set_drvdata(pdev, nandc); - nandc->dev = dev; - - dev_data = of_device_get_match_data(dev); - if (!dev_data) { - dev_err(&pdev->dev, "failed to get device data\n"); - return -ENODEV; - } - - nandc->props = dev_data; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nandc->base = devm_ioremap_resource(dev, res); - if (IS_ERR(nandc->base)) - return PTR_ERR(nandc->base); - - nandc->base_phys = res->start; - nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start); - - nandc->core_clk = devm_clk_get(dev, "core"); - if (IS_ERR(nandc->core_clk)) - return PTR_ERR(nandc->core_clk); - - nandc->aon_clk = devm_clk_get(dev, "aon"); - if (IS_ERR(nandc->aon_clk)) - return PTR_ERR(nandc->aon_clk); - - ret = qcom_nandc_parse_dt(pdev); - if (ret) - return ret; - - ret = qcom_nandc_alloc(nandc); - if (ret) - goto err_core_clk; - - ret = clk_prepare_enable(nandc->core_clk); - if (ret) - goto err_core_clk; - - ret = clk_prepare_enable(nandc->aon_clk); - if (ret) - goto err_aon_clk; - - ret = qcom_nandc_setup(nandc); - if (ret) - goto err_setup; - - ret = qcom_probe_nand_devices(nandc); - if (ret) - goto err_setup; - - return 0; - -err_setup: - clk_disable_unprepare(nandc->aon_clk); -err_aon_clk: - clk_disable_unprepare(nandc->core_clk); -err_core_clk: - qcom_nandc_unalloc(nandc); - - return ret; -} - -static int qcom_nandc_remove(struct platform_device *pdev) -{ - struct qcom_nand_controller *nandc = platform_get_drvdata(pdev); - struct qcom_nand_host *host; - - list_for_each_entry(host, &nandc->host_list, node) - nand_release(nand_to_mtd(&host->chip)); - - qcom_nandc_unalloc(nandc); - - clk_disable_unprepare(nandc->aon_clk); - clk_disable_unprepare(nandc->core_clk); - - return 0; -} - -static const struct qcom_nandc_props ipq806x_nandc_props = { - .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), - .is_bam = false, - .dev_cmd_reg_start = 0x0, -}; - -static const struct qcom_nandc_props ipq4019_nandc_props = { - .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), - .is_bam = true, - .dev_cmd_reg_start = 0x0, -}; - -static const struct qcom_nandc_props ipq8074_nandc_props = { - .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), - .is_bam = true, - .dev_cmd_reg_start = 0x7000, -}; - -/* - * data will hold a struct pointer containing more differences once we support - * more controller variants - */ -static const struct of_device_id qcom_nandc_of_match[] = { - { - .compatible = "qcom,ipq806x-nand", - .data = &ipq806x_nandc_props, - }, - { - .compatible = "qcom,ipq4019-nand", - .data = &ipq4019_nandc_props, - }, - { - .compatible = "qcom,ipq8074-nand", - .data = &ipq8074_nandc_props, - }, - {} -}; -MODULE_DEVICE_TABLE(of, qcom_nandc_of_match); - -static struct platform_driver qcom_nandc_driver = { - .driver = { - .name = "qcom-nandc", - .of_match_table = qcom_nandc_of_match, - }, - .probe = qcom_nandc_probe, - .remove = qcom_nandc_remove, -}; -module_platform_driver(qcom_nandc_driver); - -MODULE_AUTHOR("Archit Taneja "); -MODULE_DESCRIPTION("Qualcomm NAND Controller driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c deleted file mode 100644 index 595635b9e9de..000000000000 --- a/drivers/mtd/nand/r852.c +++ /dev/null @@ -1,1079 +0,0 @@ -/* - * Copyright © 2009 - Maxim Levitsky - * driver for Ricoh xD readers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sm_common.h" -#include "r852.h" - - -static bool r852_enable_dma = 1; -module_param(r852_enable_dma, bool, S_IRUGO); -MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)"); - -static int debug; -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -/* read register */ -static inline uint8_t r852_read_reg(struct r852_device *dev, int address) -{ - uint8_t reg = readb(dev->mmio + address); - return reg; -} - -/* write register */ -static inline void r852_write_reg(struct r852_device *dev, - int address, uint8_t value) -{ - writeb(value, dev->mmio + address); - mmiowb(); -} - - -/* read dword sized register */ -static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address) -{ - uint32_t reg = le32_to_cpu(readl(dev->mmio + address)); - return reg; -} - -/* write dword sized register */ -static inline void r852_write_reg_dword(struct r852_device *dev, - int address, uint32_t value) -{ - writel(cpu_to_le32(value), dev->mmio + address); - mmiowb(); -} - -/* returns pointer to our private structure */ -static inline struct r852_device *r852_get_dev(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - return nand_get_controller_data(chip); -} - - -/* check if controller supports dma */ -static void r852_dma_test(struct r852_device *dev) -{ - dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) & - (R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2); - - if (!dev->dma_usable) - message("Non dma capable device detected, dma disabled"); - - if (!r852_enable_dma) { - message("disabling dma on user request"); - dev->dma_usable = 0; - } -} - -/* - * Enable dma. Enables ether first or second stage of the DMA, - * Expects dev->dma_dir and dev->dma_state be set - */ -static void r852_dma_enable(struct r852_device *dev) -{ - uint8_t dma_reg, dma_irq_reg; - - /* Set up dma settings */ - dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS); - dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY); - - if (dev->dma_dir) - dma_reg |= R852_DMA_READ; - - if (dev->dma_state == DMA_INTERNAL) { - dma_reg |= R852_DMA_INTERNAL; - /* Precaution to make sure HW doesn't write */ - /* to random kernel memory */ - r852_write_reg_dword(dev, R852_DMA_ADDR, - cpu_to_le32(dev->phys_bounce_buffer)); - } else { - dma_reg |= R852_DMA_MEMORY; - r852_write_reg_dword(dev, R852_DMA_ADDR, - cpu_to_le32(dev->phys_dma_addr)); - } - - /* Precaution: make sure write reached the device */ - r852_read_reg_dword(dev, R852_DMA_ADDR); - - r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg); - - /* Set dma irq */ - dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); - r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, - dma_irq_reg | - R852_DMA_IRQ_INTERNAL | - R852_DMA_IRQ_ERROR | - R852_DMA_IRQ_MEMORY); -} - -/* - * Disable dma, called from the interrupt handler, which specifies - * success of the operation via 'error' argument - */ -static void r852_dma_done(struct r852_device *dev, int error) -{ - WARN_ON(dev->dma_stage == 0); - - r852_write_reg_dword(dev, R852_DMA_IRQ_STA, - r852_read_reg_dword(dev, R852_DMA_IRQ_STA)); - - r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0); - r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0); - - /* Precaution to make sure HW doesn't write to random kernel memory */ - r852_write_reg_dword(dev, R852_DMA_ADDR, - cpu_to_le32(dev->phys_bounce_buffer)); - r852_read_reg_dword(dev, R852_DMA_ADDR); - - dev->dma_error = error; - dev->dma_stage = 0; - - if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer) - pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN, - dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); -} - -/* - * Wait, till dma is done, which includes both phases of it - */ -static int r852_dma_wait(struct r852_device *dev) -{ - long timeout = wait_for_completion_timeout(&dev->dma_done, - msecs_to_jiffies(1000)); - if (!timeout) { - dbg("timeout waiting for DMA interrupt"); - return -ETIMEDOUT; - } - - return 0; -} - -/* - * Read/Write one page using dma. Only pages can be read (512 bytes) -*/ -static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) -{ - int bounce = 0; - unsigned long flags; - int error; - - dev->dma_error = 0; - - /* Set dma direction */ - dev->dma_dir = do_read; - dev->dma_stage = 1; - reinit_completion(&dev->dma_done); - - dbg_verbose("doing dma %s ", do_read ? "read" : "write"); - - /* Set initial dma state: for reading first fill on board buffer, - from device, for writes first fill the buffer from memory*/ - dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY; - - /* if incoming buffer is not page aligned, we should do bounce */ - if ((unsigned long)buf & (R852_DMA_LEN-1)) - bounce = 1; - - if (!bounce) { - dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf, - R852_DMA_LEN, - (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE)); - - if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr)) - bounce = 1; - } - - if (bounce) { - dbg_verbose("dma: using bounce buffer"); - dev->phys_dma_addr = dev->phys_bounce_buffer; - if (!do_read) - memcpy(dev->bounce_buffer, buf, R852_DMA_LEN); - } - - /* Enable DMA */ - spin_lock_irqsave(&dev->irqlock, flags); - r852_dma_enable(dev); - spin_unlock_irqrestore(&dev->irqlock, flags); - - /* Wait till complete */ - error = r852_dma_wait(dev); - - if (error) { - r852_dma_done(dev, error); - return; - } - - if (do_read && bounce) - memcpy((void *)buf, dev->bounce_buffer, R852_DMA_LEN); -} - -/* - * Program data lines of the nand chip to send data to it - */ -static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct r852_device *dev = r852_get_dev(mtd); - uint32_t reg; - - /* Don't allow any access to hardware if we suspect card removal */ - if (dev->card_unstable) - return; - - /* Special case for whole sector read */ - if (len == R852_DMA_LEN && dev->dma_usable) { - r852_do_dma(dev, (uint8_t *)buf, 0); - return; - } - - /* write DWORD chinks - faster */ - while (len >= 4) { - reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; - r852_write_reg_dword(dev, R852_DATALINE, reg); - buf += 4; - len -= 4; - - } - - /* write rest */ - while (len > 0) { - r852_write_reg(dev, R852_DATALINE, *buf++); - len--; - } -} - -/* - * Read data lines of the nand chip to retrieve data - */ -static void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct r852_device *dev = r852_get_dev(mtd); - uint32_t reg; - - if (dev->card_unstable) { - /* since we can't signal error here, at least, return - predictable buffer */ - memset(buf, 0, len); - return; - } - - /* special case for whole sector read */ - if (len == R852_DMA_LEN && dev->dma_usable) { - r852_do_dma(dev, buf, 1); - return; - } - - /* read in dword sized chunks */ - while (len >= 4) { - - reg = r852_read_reg_dword(dev, R852_DATALINE); - *buf++ = reg & 0xFF; - *buf++ = (reg >> 8) & 0xFF; - *buf++ = (reg >> 16) & 0xFF; - *buf++ = (reg >> 24) & 0xFF; - len -= 4; - } - - /* read the reset by bytes */ - while (len--) - *buf++ = r852_read_reg(dev, R852_DATALINE); -} - -/* - * Read one byte from nand chip - */ -static uint8_t r852_read_byte(struct mtd_info *mtd) -{ - struct r852_device *dev = r852_get_dev(mtd); - - /* Same problem as in r852_read_buf.... */ - if (dev->card_unstable) - return 0; - - return r852_read_reg(dev, R852_DATALINE); -} - -/* - * Control several chip lines & send commands - */ -static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) -{ - struct r852_device *dev = r852_get_dev(mtd); - - if (dev->card_unstable) - return; - - if (ctrl & NAND_CTRL_CHANGE) { - - dev->ctlreg &= ~(R852_CTL_DATA | R852_CTL_COMMAND | - R852_CTL_ON | R852_CTL_CARDENABLE); - - if (ctrl & NAND_ALE) - dev->ctlreg |= R852_CTL_DATA; - - if (ctrl & NAND_CLE) - dev->ctlreg |= R852_CTL_COMMAND; - - if (ctrl & NAND_NCE) - dev->ctlreg |= (R852_CTL_CARDENABLE | R852_CTL_ON); - else - dev->ctlreg &= ~R852_CTL_WRITE; - - /* when write is stareted, enable write access */ - if (dat == NAND_CMD_ERASE1) - dev->ctlreg |= R852_CTL_WRITE; - - r852_write_reg(dev, R852_CTL, dev->ctlreg); - } - - /* HACK: NAND_CMD_SEQIN is called without NAND_CTRL_CHANGE, but we need - to set write mode */ - if (dat == NAND_CMD_SEQIN && (dev->ctlreg & R852_CTL_COMMAND)) { - dev->ctlreg |= R852_CTL_WRITE; - r852_write_reg(dev, R852_CTL, dev->ctlreg); - } - - if (dat != NAND_CMD_NONE) - r852_write_reg(dev, R852_DATALINE, dat); -} - -/* - * Wait till card is ready. - * based on nand_wait, but returns errors on DMA error - */ -static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) -{ - struct r852_device *dev = nand_get_controller_data(chip); - - unsigned long timeout; - u8 status; - - timeout = jiffies + (chip->state == FL_ERASING ? - msecs_to_jiffies(400) : msecs_to_jiffies(20)); - - while (time_before(jiffies, timeout)) - if (chip->dev_ready(mtd)) - break; - - nand_status_op(chip, &status); - - /* Unfortunelly, no way to send detailed error status... */ - if (dev->dma_error) { - status |= NAND_STATUS_FAIL; - dev->dma_error = 0; - } - return status; -} - -/* - * Check if card is ready - */ - -static int r852_ready(struct mtd_info *mtd) -{ - struct r852_device *dev = r852_get_dev(mtd); - return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY); -} - - -/* - * Set ECC engine mode -*/ - -static void r852_ecc_hwctl(struct mtd_info *mtd, int mode) -{ - struct r852_device *dev = r852_get_dev(mtd); - - if (dev->card_unstable) - return; - - switch (mode) { - case NAND_ECC_READ: - case NAND_ECC_WRITE: - /* enable ecc generation/check*/ - dev->ctlreg |= R852_CTL_ECC_ENABLE; - - /* flush ecc buffer */ - r852_write_reg(dev, R852_CTL, - dev->ctlreg | R852_CTL_ECC_ACCESS); - - r852_read_reg_dword(dev, R852_DATALINE); - r852_write_reg(dev, R852_CTL, dev->ctlreg); - return; - - case NAND_ECC_READSYN: - /* disable ecc generation */ - dev->ctlreg &= ~R852_CTL_ECC_ENABLE; - r852_write_reg(dev, R852_CTL, dev->ctlreg); - } -} - -/* - * Calculate ECC, only used for writes - */ - -static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, - uint8_t *ecc_code) -{ - struct r852_device *dev = r852_get_dev(mtd); - struct sm_oob *oob = (struct sm_oob *)ecc_code; - uint32_t ecc1, ecc2; - - if (dev->card_unstable) - return 0; - - dev->ctlreg &= ~R852_CTL_ECC_ENABLE; - r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); - - ecc1 = r852_read_reg_dword(dev, R852_DATALINE); - ecc2 = r852_read_reg_dword(dev, R852_DATALINE); - - oob->ecc1[0] = (ecc1) & 0xFF; - oob->ecc1[1] = (ecc1 >> 8) & 0xFF; - oob->ecc1[2] = (ecc1 >> 16) & 0xFF; - - oob->ecc2[0] = (ecc2) & 0xFF; - oob->ecc2[1] = (ecc2 >> 8) & 0xFF; - oob->ecc2[2] = (ecc2 >> 16) & 0xFF; - - r852_write_reg(dev, R852_CTL, dev->ctlreg); - return 0; -} - -/* - * Correct the data using ECC, hw did almost everything for us - */ - -static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - uint32_t ecc_reg; - uint8_t ecc_status, err_byte; - int i, error = 0; - - struct r852_device *dev = r852_get_dev(mtd); - - if (dev->card_unstable) - return 0; - - if (dev->dma_error) { - dev->dma_error = 0; - return -EIO; - } - - r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); - ecc_reg = r852_read_reg_dword(dev, R852_DATALINE); - r852_write_reg(dev, R852_CTL, dev->ctlreg); - - for (i = 0 ; i <= 1 ; i++) { - - ecc_status = (ecc_reg >> 8) & 0xFF; - - /* ecc uncorrectable error */ - if (ecc_status & R852_ECC_FAIL) { - dbg("ecc: unrecoverable error, in half %d", i); - error = -EBADMSG; - goto exit; - } - - /* correctable error */ - if (ecc_status & R852_ECC_CORRECTABLE) { - - err_byte = ecc_reg & 0xFF; - dbg("ecc: recoverable error, " - "in half %d, byte %d, bit %d", i, - err_byte, ecc_status & R852_ECC_ERR_BIT_MSK); - - dat[err_byte] ^= - 1 << (ecc_status & R852_ECC_ERR_BIT_MSK); - error++; - } - - dat += 256; - ecc_reg >>= 16; - } -exit: - return error; -} - -/* - * This is copy of nand_read_oob_std - * nand_read_oob_syndrome assumes we can send column address - we can't - */ -static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); -} - -/* - * Start the nand engine - */ - -static void r852_engine_enable(struct r852_device *dev) -{ - if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) { - r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); - r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); - } else { - r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); - r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); - } - msleep(300); - r852_write_reg(dev, R852_CTL, 0); -} - - -/* - * Stop the nand engine - */ - -static void r852_engine_disable(struct r852_device *dev) -{ - r852_write_reg_dword(dev, R852_HW, 0); - r852_write_reg(dev, R852_CTL, R852_CTL_RESET); -} - -/* - * Test if card is present - */ - -static void r852_card_update_present(struct r852_device *dev) -{ - unsigned long flags; - uint8_t reg; - - spin_lock_irqsave(&dev->irqlock, flags); - reg = r852_read_reg(dev, R852_CARD_STA); - dev->card_detected = !!(reg & R852_CARD_STA_PRESENT); - spin_unlock_irqrestore(&dev->irqlock, flags); -} - -/* - * Update card detection IRQ state according to current card state - * which is read in r852_card_update_present - */ -static void r852_update_card_detect(struct r852_device *dev) -{ - int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); - dev->card_unstable = 0; - - card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT); - card_detect_reg |= R852_CARD_IRQ_GENABLE; - - card_detect_reg |= dev->card_detected ? - R852_CARD_IRQ_REMOVE : R852_CARD_IRQ_INSERT; - - r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg); -} - -static ssize_t r852_media_type_show(struct device *sys_dev, - struct device_attribute *attr, char *buf) -{ - struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev); - struct r852_device *dev = r852_get_dev(mtd); - char *data = dev->sm ? "smartmedia" : "xd"; - - strcpy(buf, data); - return strlen(data); -} - -static DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); - - -/* Detect properties of card in slot */ -static void r852_update_media_status(struct r852_device *dev) -{ - uint8_t reg; - unsigned long flags; - int readonly; - - spin_lock_irqsave(&dev->irqlock, flags); - if (!dev->card_detected) { - message("card removed"); - spin_unlock_irqrestore(&dev->irqlock, flags); - return ; - } - - readonly = r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_RO; - reg = r852_read_reg(dev, R852_DMA_CAP); - dev->sm = (reg & (R852_DMA1 | R852_DMA2)) && (reg & R852_SMBIT); - - message("detected %s %s card in slot", - dev->sm ? "SmartMedia" : "xD", - readonly ? "readonly" : "writeable"); - - dev->readonly = readonly; - spin_unlock_irqrestore(&dev->irqlock, flags); -} - -/* - * Register the nand device - * Called when the card is detected - */ -static int r852_register_nand_device(struct r852_device *dev) -{ - struct mtd_info *mtd = nand_to_mtd(dev->chip); - - WARN_ON(dev->card_registred); - - mtd->dev.parent = &dev->pci_dev->dev; - - if (dev->readonly) - dev->chip->options |= NAND_ROM; - - r852_engine_enable(dev); - - if (sm_register_device(mtd, dev->sm)) - goto error1; - - if (device_create_file(&mtd->dev, &dev_attr_media_type)) { - message("can't create media type sysfs attribute"); - goto error3; - } - - dev->card_registred = 1; - return 0; -error3: - nand_release(mtd); -error1: - /* Force card redetect */ - dev->card_detected = 0; - return -1; -} - -/* - * Unregister the card - */ - -static void r852_unregister_nand_device(struct r852_device *dev) -{ - struct mtd_info *mtd = nand_to_mtd(dev->chip); - - if (!dev->card_registred) - return; - - device_remove_file(&mtd->dev, &dev_attr_media_type); - nand_release(mtd); - r852_engine_disable(dev); - dev->card_registred = 0; -} - -/* Card state updater */ -static void r852_card_detect_work(struct work_struct *work) -{ - struct r852_device *dev = - container_of(work, struct r852_device, card_detect_work.work); - - r852_card_update_present(dev); - r852_update_card_detect(dev); - dev->card_unstable = 0; - - /* False alarm */ - if (dev->card_detected == dev->card_registred) - goto exit; - - /* Read media properties */ - r852_update_media_status(dev); - - /* Register the card */ - if (dev->card_detected) - r852_register_nand_device(dev); - else - r852_unregister_nand_device(dev); -exit: - r852_update_card_detect(dev); -} - -/* Ack + disable IRQ generation */ -static void r852_disable_irqs(struct r852_device *dev) -{ - uint8_t reg; - reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); - r852_write_reg(dev, R852_CARD_IRQ_ENABLE, reg & ~R852_CARD_IRQ_MASK); - - reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); - r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, - reg & ~R852_DMA_IRQ_MASK); - - r852_write_reg(dev, R852_CARD_IRQ_STA, R852_CARD_IRQ_MASK); - r852_write_reg_dword(dev, R852_DMA_IRQ_STA, R852_DMA_IRQ_MASK); -} - -/* Interrupt handler */ -static irqreturn_t r852_irq(int irq, void *data) -{ - struct r852_device *dev = (struct r852_device *)data; - - uint8_t card_status, dma_status; - unsigned long flags; - irqreturn_t ret = IRQ_NONE; - - spin_lock_irqsave(&dev->irqlock, flags); - - /* handle card detection interrupts first */ - card_status = r852_read_reg(dev, R852_CARD_IRQ_STA); - r852_write_reg(dev, R852_CARD_IRQ_STA, card_status); - - if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) { - - ret = IRQ_HANDLED; - dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT); - - /* we shouldn't receive any interrupts if we wait for card - to settle */ - WARN_ON(dev->card_unstable); - - /* disable irqs while card is unstable */ - /* this will timeout DMA if active, but better that garbage */ - r852_disable_irqs(dev); - - if (dev->card_unstable) - goto out; - - /* let, card state to settle a bit, and then do the work */ - dev->card_unstable = 1; - queue_delayed_work(dev->card_workqueue, - &dev->card_detect_work, msecs_to_jiffies(100)); - goto out; - } - - - /* Handle dma interrupts */ - dma_status = r852_read_reg_dword(dev, R852_DMA_IRQ_STA); - r852_write_reg_dword(dev, R852_DMA_IRQ_STA, dma_status); - - if (dma_status & R852_DMA_IRQ_MASK) { - - ret = IRQ_HANDLED; - - if (dma_status & R852_DMA_IRQ_ERROR) { - dbg("received dma error IRQ"); - r852_dma_done(dev, -EIO); - complete(&dev->dma_done); - goto out; - } - - /* received DMA interrupt out of nowhere? */ - WARN_ON_ONCE(dev->dma_stage == 0); - - if (dev->dma_stage == 0) - goto out; - - /* done device access */ - if (dev->dma_state == DMA_INTERNAL && - (dma_status & R852_DMA_IRQ_INTERNAL)) { - - dev->dma_state = DMA_MEMORY; - dev->dma_stage++; - } - - /* done memory DMA */ - if (dev->dma_state == DMA_MEMORY && - (dma_status & R852_DMA_IRQ_MEMORY)) { - dev->dma_state = DMA_INTERNAL; - dev->dma_stage++; - } - - /* Enable 2nd half of dma dance */ - if (dev->dma_stage == 2) - r852_dma_enable(dev); - - /* Operation done */ - if (dev->dma_stage == 3) { - r852_dma_done(dev, 0); - complete(&dev->dma_done); - } - goto out; - } - - /* Handle unknown interrupts */ - if (dma_status) - dbg("bad dma IRQ status = %x", dma_status); - - if (card_status & ~R852_CARD_STA_CD) - dbg("strange card status = %x", card_status); - -out: - spin_unlock_irqrestore(&dev->irqlock, flags); - return ret; -} - -static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) -{ - int error; - struct nand_chip *chip; - struct r852_device *dev; - - /* pci initialization */ - error = pci_enable_device(pci_dev); - - if (error) - goto error1; - - pci_set_master(pci_dev); - - error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); - if (error) - goto error2; - - error = pci_request_regions(pci_dev, DRV_NAME); - - if (error) - goto error3; - - error = -ENOMEM; - - /* init nand chip, but register it only on card insert */ - chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); - - if (!chip) - goto error4; - - /* commands */ - chip->cmd_ctrl = r852_cmdctl; - chip->waitfunc = r852_wait; - chip->dev_ready = r852_ready; - - /* I/O */ - chip->read_byte = r852_read_byte; - chip->read_buf = r852_read_buf; - chip->write_buf = r852_write_buf; - - /* ecc */ - chip->ecc.mode = NAND_ECC_HW_SYNDROME; - chip->ecc.size = R852_DMA_LEN; - chip->ecc.bytes = SM_OOB_SIZE; - chip->ecc.strength = 2; - chip->ecc.hwctl = r852_ecc_hwctl; - chip->ecc.calculate = r852_ecc_calculate; - chip->ecc.correct = r852_ecc_correct; - - /* TODO: hack */ - chip->ecc.read_oob = r852_read_oob; - - /* init our device structure */ - dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL); - - if (!dev) - goto error5; - - nand_set_controller_data(chip, dev); - dev->chip = chip; - dev->pci_dev = pci_dev; - pci_set_drvdata(pci_dev, dev); - - dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN, - &dev->phys_bounce_buffer); - - if (!dev->bounce_buffer) - goto error6; - - - error = -ENODEV; - dev->mmio = pci_ioremap_bar(pci_dev, 0); - - if (!dev->mmio) - goto error7; - - error = -ENOMEM; - dev->tmp_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); - - if (!dev->tmp_buffer) - goto error8; - - init_completion(&dev->dma_done); - - dev->card_workqueue = create_freezable_workqueue(DRV_NAME); - - if (!dev->card_workqueue) - goto error9; - - INIT_DELAYED_WORK(&dev->card_detect_work, r852_card_detect_work); - - /* shutdown everything - precation */ - r852_engine_disable(dev); - r852_disable_irqs(dev); - - r852_dma_test(dev); - - dev->irq = pci_dev->irq; - spin_lock_init(&dev->irqlock); - - dev->card_detected = 0; - r852_card_update_present(dev); - - /*register irq handler*/ - error = -ENODEV; - if (request_irq(pci_dev->irq, &r852_irq, IRQF_SHARED, - DRV_NAME, dev)) - goto error10; - - /* kick initial present test */ - queue_delayed_work(dev->card_workqueue, - &dev->card_detect_work, 0); - - - printk(KERN_NOTICE DRV_NAME ": driver loaded successfully\n"); - return 0; - -error10: - destroy_workqueue(dev->card_workqueue); -error9: - kfree(dev->tmp_buffer); -error8: - pci_iounmap(pci_dev, dev->mmio); -error7: - pci_free_consistent(pci_dev, R852_DMA_LEN, - dev->bounce_buffer, dev->phys_bounce_buffer); -error6: - kfree(dev); -error5: - kfree(chip); -error4: - pci_release_regions(pci_dev); -error3: -error2: - pci_disable_device(pci_dev); -error1: - return error; -} - -static void r852_remove(struct pci_dev *pci_dev) -{ - struct r852_device *dev = pci_get_drvdata(pci_dev); - - /* Stop detect workqueue - - we are going to unregister the device anyway*/ - cancel_delayed_work_sync(&dev->card_detect_work); - destroy_workqueue(dev->card_workqueue); - - /* Unregister the device, this might make more IO */ - r852_unregister_nand_device(dev); - - /* Stop interrupts */ - r852_disable_irqs(dev); - free_irq(dev->irq, dev); - - /* Cleanup */ - kfree(dev->tmp_buffer); - pci_iounmap(pci_dev, dev->mmio); - pci_free_consistent(pci_dev, R852_DMA_LEN, - dev->bounce_buffer, dev->phys_bounce_buffer); - - kfree(dev->chip); - kfree(dev); - - /* Shutdown the PCI device */ - pci_release_regions(pci_dev); - pci_disable_device(pci_dev); -} - -static void r852_shutdown(struct pci_dev *pci_dev) -{ - struct r852_device *dev = pci_get_drvdata(pci_dev); - - cancel_delayed_work_sync(&dev->card_detect_work); - r852_disable_irqs(dev); - synchronize_irq(dev->irq); - pci_disable_device(pci_dev); -} - -#ifdef CONFIG_PM_SLEEP -static int r852_suspend(struct device *device) -{ - struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); - - if (dev->ctlreg & R852_CTL_CARDENABLE) - return -EBUSY; - - /* First make sure the detect work is gone */ - cancel_delayed_work_sync(&dev->card_detect_work); - - /* Turn off the interrupts and stop the device */ - r852_disable_irqs(dev); - r852_engine_disable(dev); - - /* If card was pulled off just during the suspend, which is very - unlikely, we will remove it on resume, it too late now - anyway... */ - dev->card_unstable = 0; - return 0; -} - -static int r852_resume(struct device *device) -{ - struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); - struct mtd_info *mtd = nand_to_mtd(dev->chip); - - r852_disable_irqs(dev); - r852_card_update_present(dev); - r852_engine_disable(dev); - - - /* If card status changed, just do the work */ - if (dev->card_detected != dev->card_registred) { - dbg("card was %s during low power state", - dev->card_detected ? "added" : "removed"); - - queue_delayed_work(dev->card_workqueue, - &dev->card_detect_work, msecs_to_jiffies(1000)); - return 0; - } - - /* Otherwise, initialize the card */ - if (dev->card_registred) { - r852_engine_enable(dev); - dev->chip->select_chip(mtd, 0); - nand_reset_op(dev->chip); - dev->chip->select_chip(mtd, -1); - } - - /* Program card detection IRQ */ - r852_update_card_detect(dev); - return 0; -} -#endif - -static const struct pci_device_id r852_pci_id_tbl[] = { - - { PCI_VDEVICE(RICOH, 0x0852), }, - { }, -}; - -MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl); - -static SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume); - -static struct pci_driver r852_pci_driver = { - .name = DRV_NAME, - .id_table = r852_pci_id_tbl, - .probe = r852_probe, - .remove = r852_remove, - .shutdown = r852_shutdown, - .driver.pm = &r852_pm_ops, -}; - -module_pci_driver(r852_pci_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Maxim Levitsky "); -MODULE_DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver"); diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/r852.h deleted file mode 100644 index 8713c57f6207..000000000000 --- a/drivers/mtd/nand/r852.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright © 2009 - Maxim Levitsky - * driver for Ricoh xD readers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - - -/* nand interface + ecc - byte write/read does one cycle on nand data lines. - dword write/read does 4 cycles - if R852_CTL_ECC_ACCESS is set in R852_CTL, then dword read reads - results of ecc correction, if DMA read was done before. - If write was done two dword reads read generated ecc checksums -*/ -#define R852_DATALINE 0x00 - -/* control register */ -#define R852_CTL 0x04 -#define R852_CTL_COMMAND 0x01 /* send command (#CLE)*/ -#define R852_CTL_DATA 0x02 /* read/write data (#ALE)*/ -#define R852_CTL_ON 0x04 /* only seem to controls the hd led, */ - /* but has to be set on start...*/ -#define R852_CTL_RESET 0x08 /* unknown, set only on start once*/ -#define R852_CTL_CARDENABLE 0x10 /* probably (#CE) - always set*/ -#define R852_CTL_ECC_ENABLE 0x20 /* enable ecc engine */ -#define R852_CTL_ECC_ACCESS 0x40 /* read/write ecc via reg #0*/ -#define R852_CTL_WRITE 0x80 /* set when performing writes (#WP) */ - -/* card detection status */ -#define R852_CARD_STA 0x05 - -#define R852_CARD_STA_CD 0x01 /* state of #CD line, same as 0x04 */ -#define R852_CARD_STA_RO 0x02 /* card is readonly */ -#define R852_CARD_STA_PRESENT 0x04 /* card is present (#CD) */ -#define R852_CARD_STA_ABSENT 0x08 /* card is absent */ -#define R852_CARD_STA_BUSY 0x80 /* card is busy - (#R/B) */ - -/* card detection irq status & enable*/ -#define R852_CARD_IRQ_STA 0x06 /* IRQ status */ -#define R852_CARD_IRQ_ENABLE 0x07 /* IRQ enable */ - -#define R852_CARD_IRQ_CD 0x01 /* fire when #CD lights, same as 0x04*/ -#define R852_CARD_IRQ_REMOVE 0x04 /* detect card removal */ -#define R852_CARD_IRQ_INSERT 0x08 /* detect card insert */ -#define R852_CARD_IRQ_UNK1 0x10 /* unknown */ -#define R852_CARD_IRQ_GENABLE 0x80 /* general enable */ -#define R852_CARD_IRQ_MASK 0x1D - - - -/* hardware enable */ -#define R852_HW 0x08 -#define R852_HW_ENABLED 0x01 /* hw enabled */ -#define R852_HW_UNKNOWN 0x80 - - -/* dma capabilities */ -#define R852_DMA_CAP 0x09 -#define R852_SMBIT 0x20 /* if set with bit #6 or bit #7, then */ - /* hw is smartmedia */ -#define R852_DMA1 0x40 /* if set w/bit #7, dma is supported */ -#define R852_DMA2 0x80 /* if set w/bit #6, dma is supported */ - - -/* physical DMA address - 32 bit value*/ -#define R852_DMA_ADDR 0x0C - - -/* dma settings */ -#define R852_DMA_SETTINGS 0x10 -#define R852_DMA_MEMORY 0x01 /* (memory <-> internal hw buffer) */ -#define R852_DMA_READ 0x02 /* 0 = write, 1 = read */ -#define R852_DMA_INTERNAL 0x04 /* (internal hw buffer <-> card) */ - -/* dma IRQ status */ -#define R852_DMA_IRQ_STA 0x14 - -/* dma IRQ enable */ -#define R852_DMA_IRQ_ENABLE 0x18 - -#define R852_DMA_IRQ_MEMORY 0x01 /* (memory <-> internal hw buffer) */ -#define R852_DMA_IRQ_ERROR 0x02 /* error did happen */ -#define R852_DMA_IRQ_INTERNAL 0x04 /* (internal hw buffer <-> card) */ -#define R852_DMA_IRQ_MASK 0x07 /* mask of all IRQ bits */ - - -/* ECC syndrome format - read from reg #0 will return two copies of these for - each half of the page. - first byte is error byte location, and second, bit location + flags */ -#define R852_ECC_ERR_BIT_MSK 0x07 /* error bit location */ -#define R852_ECC_CORRECT 0x10 /* no errors - (guessed) */ -#define R852_ECC_CORRECTABLE 0x20 /* correctable error exist */ -#define R852_ECC_FAIL 0x40 /* non correctable error detected */ - -#define R852_DMA_LEN 512 - -#define DMA_INTERNAL 0 -#define DMA_MEMORY 1 - -struct r852_device { - void __iomem *mmio; /* mmio */ - struct nand_chip *chip; /* nand chip backpointer */ - struct pci_dev *pci_dev; /* pci backpointer */ - - /* dma area */ - dma_addr_t phys_dma_addr; /* bus address of buffer*/ - struct completion dma_done; /* data transfer done */ - - dma_addr_t phys_bounce_buffer; /* bus address of bounce buffer */ - uint8_t *bounce_buffer; /* virtual address of bounce buffer */ - - int dma_dir; /* 1 = read, 0 = write */ - int dma_stage; /* 0 - idle, 1 - first step, - 2 - second step */ - - int dma_state; /* 0 = internal, 1 = memory */ - int dma_error; /* dma errors */ - int dma_usable; /* is it possible to use dma */ - - /* card status area */ - struct delayed_work card_detect_work; - struct workqueue_struct *card_workqueue; - int card_registred; /* card registered with mtd */ - int card_detected; /* card detected in slot */ - int card_unstable; /* whenever the card is inserted, - is not known yet */ - int readonly; /* card is readonly */ - int sm; /* Is card smartmedia */ - - /* interrupt handling */ - spinlock_t irqlock; /* IRQ protecting lock */ - int irq; /* irq num */ - /* misc */ - void *tmp_buffer; /* temporary buffer */ - uint8_t ctlreg; /* cached contents of control reg */ -}; - -#define DRV_NAME "r852" - - -#define dbg(format, ...) \ - if (debug) \ - printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) - -#define dbg_verbose(format, ...) \ - if (debug > 1) \ - printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) - - -#define message(format, ...) \ - printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig new file mode 100644 index 000000000000..e6b8c59f2c0d --- /dev/null +++ b/drivers/mtd/nand/raw/Kconfig @@ -0,0 +1,580 @@ +config MTD_NAND_ECC + tristate + +config MTD_NAND_ECC_SMC + bool "NAND ECC Smart Media byte order" + depends on MTD_NAND_ECC + default n + help + Software ECC according to the Smart Media Specification. + The original Linux implementation had byte 0 and 1 swapped. + + +menuconfig MTD_NAND + tristate "NAND Device Support" + depends on MTD + select MTD_NAND_ECC + help + This enables support for accessing all type of NAND flash + devices. For further information see + . + +if MTD_NAND + +config MTD_NAND_BCH + tristate + select BCH + depends on MTD_NAND_ECC_BCH + default MTD_NAND + +config MTD_NAND_ECC_BCH + bool "Support software BCH ECC" + default n + help + This enables support for software BCH error correction. Binary BCH + codes are more powerful and cpu intensive than traditional Hamming + ECC codes. They are used with NAND devices requiring more than 1 bit + of error correction. + +config MTD_SM_COMMON + tristate + default n + +config MTD_NAND_DENALI + tristate + +config MTD_NAND_DENALI_PCI + tristate "Support Denali NAND controller on Intel Moorestown" + select MTD_NAND_DENALI + depends on HAS_DMA && PCI + help + Enable the driver for NAND flash on Intel Moorestown, using the + Denali NAND controller core. + +config MTD_NAND_DENALI_DT + tristate "Support Denali NAND controller as a DT device" + select MTD_NAND_DENALI + depends on HAS_DMA && HAVE_CLK && OF + help + Enable the driver for NAND flash on platforms using a Denali NAND + controller as a DT device. + +config MTD_NAND_GPIO + tristate "GPIO assisted NAND Flash driver" + depends on GPIOLIB || COMPILE_TEST + depends on HAS_IOMEM + help + This enables a NAND flash driver where control signals are + connected to GPIO pins, and commands and data are communicated + via a memory mapped interface. + +config MTD_NAND_AMS_DELTA + tristate "NAND Flash device on Amstrad E3" + depends on MACH_AMS_DELTA + default y + help + Support for NAND flash on Amstrad E3 (Delta). + +config MTD_NAND_OMAP2 + tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone" + depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE) + help + Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4 + and Keystone platforms. + +config MTD_NAND_OMAP_BCH + depends on MTD_NAND_OMAP2 + bool "Support hardware based BCH error correction" + default n + select BCH + help + This config enables the ELM hardware engine, which can be used to + locate and correct errors when using BCH ECC scheme. This offloads + the cpu from doing ECC error searching and correction. However some + legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine + so this is optional for them. + +config MTD_NAND_OMAP_BCH_BUILD + def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH + +config MTD_NAND_RICOH + tristate "Ricoh xD card reader" + default n + depends on PCI + select MTD_SM_COMMON + help + Enable support for Ricoh R5C852 xD card reader + You also need to enable ether + NAND SSFDC (SmartMedia) read only translation layer' or new + expermental, readwrite + 'SmartMedia/xD new translation layer' + +config MTD_NAND_AU1550 + tristate "Au1550/1200 NAND support" + depends on MIPS_ALCHEMY + help + This enables the driver for the NAND flash controller on the + AMD/Alchemy 1550 SOC. + +config MTD_NAND_BF5XX + tristate "Blackfin on-chip NAND Flash Controller driver" + depends on BF54x || BF52x + help + This enables the Blackfin on-chip NAND flash controller + + No board specific support is done by this driver, each board + must advertise a platform_device for the driver to attach. + + This driver can also be built as a module. If so, the module + will be called bf5xx-nand. + +config MTD_NAND_BF5XX_HWECC + bool "BF5XX NAND Hardware ECC" + default y + depends on MTD_NAND_BF5XX + help + Enable the use of the BF5XX's internal ECC generator when + using NAND. + +config MTD_NAND_BF5XX_BOOTROM_ECC + bool "Use Blackfin BootROM ECC Layout" + default n + depends on MTD_NAND_BF5XX_HWECC + help + If you wish to modify NAND pages and allow the Blackfin on-chip + BootROM to boot from them, say Y here. This is only necessary + if you are booting U-Boot out of NAND and you wish to update + U-Boot from Linux' userspace. Otherwise, you should say N here. + + If unsure, say N. + +config MTD_NAND_S3C2410 + tristate "NAND Flash support for Samsung S3C SoCs" + depends on ARCH_S3C24XX || ARCH_S3C64XX + help + This enables the NAND flash controller on the S3C24xx and S3C64xx + SoCs + + No board specific support is done by this driver, each board + must advertise a platform_device for the driver to attach. + +config MTD_NAND_S3C2410_DEBUG + bool "Samsung S3C NAND driver debug" + depends on MTD_NAND_S3C2410 + help + Enable debugging of the S3C NAND driver + +config MTD_NAND_NDFC + tristate "NDFC NanD Flash Controller" + depends on 4xx + select MTD_NAND_ECC_SMC + help + NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs + +config MTD_NAND_S3C2410_CLKSTOP + bool "Samsung S3C NAND IDLE clock stop" + depends on MTD_NAND_S3C2410 + default n + help + Stop the clock to the NAND controller when there is no chip + selected to save power. This will mean there is a small delay + when the is NAND chip selected or released, but will save + approximately 5mA of power when there is nothing happening. + +config MTD_NAND_TANGO + tristate "NAND Flash support for Tango chips" + depends on ARCH_TANGO || COMPILE_TEST + depends on HAS_DMA + help + Enables the NAND Flash controller on Tango chips. + +config MTD_NAND_DISKONCHIP + tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)" + depends on HAS_IOMEM + select REED_SOLOMON + select REED_SOLOMON_DEC16 + help + This is a reimplementation of M-Systems DiskOnChip 2000, + Millennium and Millennium Plus as a standard NAND device driver, + as opposed to the earlier self-contained MTD device drivers. + This should enable, among other things, proper JFFS2 operation on + these devices. + +config MTD_NAND_DISKONCHIP_PROBE_ADVANCED + bool "Advanced detection options for DiskOnChip" + depends on MTD_NAND_DISKONCHIP + help + This option allows you to specify nonstandard address at which to + probe for a DiskOnChip, or to change the detection options. You + are unlikely to need any of this unless you are using LinuxBIOS. + Say 'N'. + +config MTD_NAND_DISKONCHIP_PROBE_ADDRESS + hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED + depends on MTD_NAND_DISKONCHIP + default "0" + ---help--- + By default, the probe for DiskOnChip devices will look for a + DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. + This option allows you to specify a single address at which to probe + for the device, which is useful if you have other devices in that + range which get upset when they are probed. + + (Note that on PowerPC, the normal probe will only check at + 0xE4000000.) + + Normally, you should leave this set to zero, to allow the probe at + the normal addresses. + +config MTD_NAND_DISKONCHIP_PROBE_HIGH + bool "Probe high addresses" + depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED + help + By default, the probe for DiskOnChip devices will look for a + DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. + This option changes to make it probe between 0xFFFC8000 and + 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be + useful to you. Say 'N'. + +config MTD_NAND_DISKONCHIP_BBTWRITE + bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP" + depends on MTD_NAND_DISKONCHIP + help + On DiskOnChip devices shipped with the INFTL filesystem (Millennium + and 2000 TSOP/Alon), Linux reserves some space at the end of the + device for the Bad Block Table (BBT). If you have existing INFTL + data on your device (created by non-Linux tools such as M-Systems' + DOS drivers), your data might overlap the area Linux wants to use for + the BBT. If this is a concern for you, leave this option disabled and + Linux will not write BBT data into this area. + The downside of leaving this option disabled is that if bad blocks + are detected by Linux, they will not be recorded in the BBT, which + could cause future problems. + Once you enable this option, new filesystems (INFTL or others, created + in Linux or other operating systems) will not use the reserved area. + The only reason not to enable this option is to prevent damage to + preexisting filesystems. + Even if you leave this disabled, you can enable BBT writes at module + load time (assuming you build diskonchip as a module) with the module + parameter "inftl_bbt_write=1". + +config MTD_NAND_DOCG4 + tristate "Support for DiskOnChip G4" + depends on HAS_IOMEM + select BCH + select BITREVERSE + help + Support for diskonchip G4 nand flash, found in various smartphones and + PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba + Portege G900, Asus P526, and O2 XDA Zinc. + + With this driver you will be able to use UBI and create a ubifs on the + device, so you may wish to consider enabling UBI and UBIFS as well. + + These devices ship with the Mys/Sandisk SAFTL formatting, for which + there is currently no mtd parser, so you may want to use command line + partitioning to segregate write-protected blocks. On the Treo680, the + first five erase blocks (256KiB each) are write-protected, followed + by the block containing the saftl partition table. This is probably + typical. + +config MTD_NAND_SHARPSL + tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" + depends on ARCH_PXA + +config MTD_NAND_CAFE + tristate "NAND support for OLPC CAFÉ chip" + depends on PCI + select REED_SOLOMON + select REED_SOLOMON_DEC16 + help + Use NAND flash attached to the CAFÉ chip designed for the OLPC + laptop. + +config MTD_NAND_CS553X + tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" + depends on X86_32 + depends on !UML && HAS_IOMEM + help + The CS553x companion chips for the AMD Geode processor + include NAND flash controllers with built-in hardware ECC + capabilities; enabling this option will allow you to use + these. The driver will check the MSRs to verify that the + controller is enabled for NAND, and currently requires that + the controller be in MMIO mode. + + If you say "m", the module will be called cs553x_nand. + +config MTD_NAND_ATMEL + tristate "Support for NAND Flash / SmartMedia on AT91" + depends on ARCH_AT91 + select MFD_ATMEL_SMC + help + Enables support for NAND Flash / Smart Media Card interface + on Atmel AT91 processors. + +config MTD_NAND_PXA3xx + tristate "NAND support on PXA3xx and Armada 370/XP" + depends on !MTD_NAND_MARVELL + depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU + help + + This enables the driver for the NAND flash device found on + PXA3xx processors (NFCv1) and also on 32-bit Armada + platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada + platforms (7K, 8K) (NFCv2). + +config MTD_NAND_MARVELL + tristate "NAND controller support on Marvell boards" + depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \ + COMPILE_TEST + depends on HAS_IOMEM + help + This enables the NAND flash controller driver for Marvell boards, + including: + - PXA3xx processors (NFCv1) + - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2) + - 64-bit Aramda platforms (7k, 8k) (NFCv2) + +config MTD_NAND_SLC_LPC32XX + tristate "NXP LPC32xx SLC Controller" + depends on ARCH_LPC32XX + help + Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell + chips) NAND controller. This is the default for the PHYTEC 3250 + reference board which contains a NAND256R3A2CZA6 chip. + + Please check the actual NAND chip connected and its support + by the SLC NAND controller. + +config MTD_NAND_MLC_LPC32XX + tristate "NXP LPC32xx MLC Controller" + depends on ARCH_LPC32XX + help + Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND + controller. This is the default for the WORK92105 controller + board. + + Please check the actual NAND chip connected and its support + by the MLC NAND controller. + +config MTD_NAND_CM_X270 + tristate "Support for NAND Flash on CM-X270 modules" + depends on MACH_ARMCORE + +config MTD_NAND_PASEMI + tristate "NAND support for PA Semi PWRficient" + depends on PPC_PASEMI + help + Enables support for NAND Flash interface on PA Semi PWRficient + based boards + +config MTD_NAND_TMIO + tristate "NAND Flash device on Toshiba Mobile IO Controller" + depends on MFD_TMIO + help + Support for NAND flash connected to a Toshiba Mobile IO + Controller in some PDAs, including the Sharp SL6000x. + +config MTD_NAND_NANDSIM + tristate "Support for NAND Flash Simulator" + help + The simulator may simulate various NAND flash chips for the + MTD nand layer. + +config MTD_NAND_GPMI_NAND + tristate "GPMI NAND Flash Controller driver" + depends on MTD_NAND && MXS_DMA + help + Enables NAND Flash support for IMX23, IMX28 or IMX6. + The GPMI controller is very powerful, with the help of BCH + module, it can do the hardware ECC. The GPMI supports several + NAND flashs at the same time. + +config MTD_NAND_BRCMNAND + tristate "Broadcom STB NAND controller" + depends on ARM || ARM64 || MIPS + help + Enables the Broadcom NAND controller driver. The controller was + originally designed for Set-Top Box but is used on various BCM7xxx, + BCM3xxx, BCM63xxx, iProc/Cygnus and more. + +config MTD_NAND_BCM47XXNFLASH + tristate "Support for NAND flash on BCM4706 BCMA bus" + depends on BCMA_NFLASH + help + BCMA bus can have various flash memories attached, they are + registered by bcma as platform devices. This enables driver for + NAND flash memories. For now only BCM4706 is supported. + +config MTD_NAND_PLATFORM + tristate "Support for generic platform NAND driver" + depends on HAS_IOMEM + help + This implements a generic NAND driver for on-SOC platform + devices. You will need to provide platform-specific functions + via platform_data. + +config MTD_NAND_ORION + tristate "NAND Flash support for Marvell Orion SoC" + depends on PLAT_ORION + help + This enables the NAND flash controller on Orion machines. + + No board specific support is done by this driver, each board + must advertise a platform_device for the driver to attach. + +config MTD_NAND_OXNAS + tristate "NAND Flash support for Oxford Semiconductor SoC" + depends on ARCH_OXNAS || COMPILE_TEST + depends on HAS_IOMEM + help + This enables the NAND flash controller on Oxford Semiconductor SoCs. + +config MTD_NAND_FSL_ELBC + tristate "NAND support for Freescale eLBC controllers" + depends on FSL_SOC + select FSL_LBC + help + Various Freescale chips, including the 8313, include a NAND Flash + Controller Module with built-in hardware ECC capabilities. + Enabling this option will enable you to use this to control + external NAND devices. + +config MTD_NAND_FSL_IFC + tristate "NAND support for Freescale IFC controller" + depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A + select FSL_IFC + select MEMORY + help + Various Freescale chips e.g P1010, include a NAND Flash machine + with built-in hardware ECC capabilities. + Enabling this option will enable you to use this to control + external NAND devices. + +config MTD_NAND_FSL_UPM + tristate "Support for NAND on Freescale UPM" + depends on PPC_83xx || PPC_85xx + select FSL_LBC + help + Enables support for NAND Flash chips wired onto Freescale PowerPC + processor localbus with User-Programmable Machine support. + +config MTD_NAND_MPC5121_NFC + tristate "MPC5121 built-in NAND Flash Controller support" + depends on PPC_MPC512x + help + This enables the driver for the NAND flash controller on the + MPC5121 SoC. + +config MTD_NAND_VF610_NFC + tristate "Support for Freescale NFC for VF610/MPC5125" + depends on (SOC_VF610 || COMPILE_TEST) + depends on HAS_IOMEM + help + Enables support for NAND Flash Controller on some Freescale + processors like the VF610, MPC5125, MCF54418 or Kinetis K70. + The driver supports a maximum 2k page size. With 2k pages and + 64 bytes or more of OOB, hardware ECC with up to 32-bit error + correction is supported. Hardware ECC is only enabled through + device tree. + +config MTD_NAND_MXC + tristate "MXC NAND support" + depends on ARCH_MXC + help + This enables the driver for the NAND flash controller on the + MXC processors. + +config MTD_NAND_SH_FLCTL + tristate "Support for NAND on Renesas SuperH FLCTL" + depends on SUPERH || COMPILE_TEST + depends on HAS_IOMEM + depends on HAS_DMA + help + Several Renesas SuperH CPU has FLCTL. This option enables support + for NAND Flash using FLCTL. + +config MTD_NAND_DAVINCI + tristate "Support NAND on DaVinci/Keystone SoC" + depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) + help + Enable the driver for NAND flash chips on Texas Instruments + DaVinci/Keystone processors. + +config MTD_NAND_TXX9NDFMC + tristate "NAND Flash support for TXx9 SoC" + depends on SOC_TX4938 || SOC_TX4939 + help + This enables the NAND flash controller on the TXx9 SoCs. + +config MTD_NAND_SOCRATES + tristate "Support for NAND on Socrates board" + depends on SOCRATES + help + Enables support for NAND Flash chips wired onto Socrates board. + +config MTD_NAND_NUC900 + tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." + depends on ARCH_W90X900 + help + This enables the driver for the NAND Flash on evaluation board based + on w90p910 / NUC9xx. + +config MTD_NAND_JZ4740 + tristate "Support for JZ4740 SoC NAND controller" + depends on MACH_JZ4740 + help + Enables support for NAND Flash on JZ4740 SoC based boards. + +config MTD_NAND_JZ4780 + tristate "Support for NAND on JZ4780 SoC" + depends on MACH_JZ4780 && JZ4780_NEMC + help + Enables support for NAND Flash connected to the NEMC on JZ4780 SoC + based boards, using the BCH controller for hardware error correction. + +config MTD_NAND_FSMC + tristate "Support for NAND on ST Micros FSMC" + depends on OF + depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 + help + Enables support for NAND Flash chips on the ST Microelectronics + Flexible Static Memory Controller (FSMC) + +config MTD_NAND_XWAY + bool "Support for NAND on Lantiq XWAY SoC" + depends on LANTIQ && SOC_TYPE_XWAY + help + Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached + to the External Bus Unit (EBU). + +config MTD_NAND_SUNXI + tristate "Support for NAND on Allwinner SoCs" + depends on ARCH_SUNXI + help + Enables support for NAND Flash chips on Allwinner SoCs. + +config MTD_NAND_HISI504 + tristate "Support for NAND controller on Hisilicon SoC Hip04" + depends on ARCH_HISI || COMPILE_TEST + depends on HAS_DMA + help + Enables support for NAND controller on Hisilicon SoC Hip04. + +config MTD_NAND_QCOM + tristate "Support for NAND on QCOM SoCs" + depends on ARCH_QCOM + help + Enables support for NAND flash chips on SoCs containing the EBI2 NAND + controller. This controller is found on IPQ806x SoC. + +config MTD_NAND_MTK + tristate "Support for NAND controller on MTK SoCs" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on HAS_DMA + help + Enables support for NAND controller on MTK SoCs. + This controller is found on mt27xx, mt81xx, mt65xx SoCs. + +endif # MTD_NAND diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile new file mode 100644 index 000000000000..4e0982476267 --- /dev/null +++ b/drivers/mtd/nand/raw/Makefile @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_MTD_NAND) += nand.o +obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o +obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o +obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o + +obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o +obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o +obj-$(CONFIG_MTD_NAND_DENALI) += denali.o +obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o +obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o +obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o +obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o +obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o +obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o +obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o +obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o +obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o +obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o +obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o +obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o +obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o +obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o +obj-$(CONFIG_MTD_NAND_ATMEL) += atmel/ +obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o +omap2_nand-objs := omap2.o +obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o +obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o +obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o +obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o +obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o +obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o +obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o +obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o +obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o +obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o +obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o +obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o +obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o +obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o +obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o +obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o +obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o +obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o +obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o +obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o +obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o +obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o +obj-$(CONFIG_MTD_NAND_RICOH) += r852.o +obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o +obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o +obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ +obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o +obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ +obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o +obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o +obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ +obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o +obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o + +nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o +nand-objs += nand_amd.o +nand-objs += nand_hynix.o +nand-objs += nand_macronix.o +nand-objs += nand_micron.o +nand-objs += nand_samsung.o +nand-objs += nand_toshiba.o diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c new file mode 100644 index 000000000000..35f80523e52e --- /dev/null +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2006 Jonathan McDowell + * + * Derived from drivers/mtd/nand/toto.c (removed in v2.6.28) + * Copyright (c) 2003 Texas Instruments + * Copyright (c) 2002 Thomas Gleixner + * + * Converted to platform driver by Janusz Krzysztofik + * Partially stolen from plat_nand.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * Amstrad E3 (Delta). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/* + * MTD structure for E3 (Delta) + */ +static struct mtd_info *ams_delta_mtd = NULL; + +/* + * Define partitions for flash devices + */ + +static const struct mtd_partition partition_info[] = { + { .name = "Kernel", + .offset = 0, + .size = 3 * SZ_1M + SZ_512K }, + { .name = "u-boot", + .offset = 3 * SZ_1M + SZ_512K, + .size = SZ_256K }, + { .name = "u-boot params", + .offset = 3 * SZ_1M + SZ_512K + SZ_256K, + .size = SZ_256K }, + { .name = "Amstrad LDR", + .offset = 4 * SZ_1M, + .size = SZ_256K }, + { .name = "File system", + .offset = 4 * SZ_1M + 1 * SZ_256K, + .size = 27 * SZ_1M }, + { .name = "PBL reserved", + .offset = 32 * SZ_1M - 3 * SZ_256K, + .size = 3 * SZ_256K }, +}; + +static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd_to_nand(mtd); + void __iomem *io_base = (void __iomem *)nand_get_controller_data(this); + + writew(0, io_base + OMAP_MPUIO_IO_CNTL); + writew(byte, this->IO_ADDR_W); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0); + ndelay(40); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1); +} + +static u_char ams_delta_read_byte(struct mtd_info *mtd) +{ + u_char res; + struct nand_chip *this = mtd_to_nand(mtd); + void __iomem *io_base = (void __iomem *)nand_get_controller_data(this); + + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0); + ndelay(40); + writew(~0, io_base + OMAP_MPUIO_IO_CNTL); + res = readw(this->IO_ADDR_R); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1); + + return res; +} + +static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf, + int len) +{ + int i; + + for (i=0; i bit 2 + * NAND_CLE: bit 1 -> bit 7 + * NAND_ALE: bit 2 -> bit 6 + */ +static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + + if (ctrl & NAND_CTRL_CHANGE) { + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE, + (ctrl & NAND_NCE) == 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE, + (ctrl & NAND_CLE) != 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE, + (ctrl & NAND_ALE) != 0); + } + + if (cmd != NAND_CMD_NONE) + ams_delta_write_byte(mtd, cmd); +} + +static int ams_delta_nand_ready(struct mtd_info *mtd) +{ + return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB); +} + +static const struct gpio _mandatory_gpio[] = { + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nce", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nre", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nwp", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nwe", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE, + .flags = GPIOF_OUT_INIT_LOW, + .label = "nand_ale", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE, + .flags = GPIOF_OUT_INIT_LOW, + .label = "nand_cle", + }, +}; + +/* + * Main initialization routine + */ +static int ams_delta_init(struct platform_device *pdev) +{ + struct nand_chip *this; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + void __iomem *io_base; + int err = 0; + + if (!res) + return -ENXIO; + + /* Allocate memory for MTD device structure and private data */ + this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); + if (!this) { + printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n"); + err = -ENOMEM; + goto out; + } + + ams_delta_mtd = nand_to_mtd(this); + ams_delta_mtd->owner = THIS_MODULE; + + /* + * Don't try to request the memory region from here, + * it should have been already requested from the + * gpio-omap driver and requesting it again would fail. + */ + + io_base = ioremap(res->start, resource_size(res)); + if (io_base == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + err = -EIO; + goto out_free; + } + + nand_set_controller_data(this, (void *)io_base); + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH; + this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT; + this->read_byte = ams_delta_read_byte; + this->write_buf = ams_delta_write_buf; + this->read_buf = ams_delta_read_buf; + this->cmd_ctrl = ams_delta_hwcontrol; + if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) { + this->dev_ready = ams_delta_nand_ready; + } else { + this->dev_ready = NULL; + printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n"); + } + /* 25 us command delay time */ + this->chip_delay = 30; + this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; + + platform_set_drvdata(pdev, io_base); + + /* Set chip enabled, but */ + err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); + if (err) + goto out_gpio; + + /* Scan to find existence of the device */ + err = nand_scan(ams_delta_mtd, 1); + if (err) + goto out_mtd; + + /* Register the partitions */ + mtd_device_register(ams_delta_mtd, partition_info, + ARRAY_SIZE(partition_info)); + + goto out; + + out_mtd: + gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); +out_gpio: + gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); + iounmap(io_base); +out_free: + kfree(this); + out: + return err; +} + +/* + * Clean up routine + */ +static int ams_delta_cleanup(struct platform_device *pdev) +{ + void __iomem *io_base = platform_get_drvdata(pdev); + + /* Release resources, unregister device */ + nand_release(ams_delta_mtd); + + gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); + gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); + iounmap(io_base); + + /* Free the MTD device structure */ + kfree(mtd_to_nand(ams_delta_mtd)); + + return 0; +} + +static struct platform_driver ams_delta_nand_driver = { + .probe = ams_delta_init, + .remove = ams_delta_cleanup, + .driver = { + .name = "ams-delta-nand", + }, +}; + +module_platform_driver(ams_delta_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jonathan McDowell "); +MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)"); diff --git a/drivers/mtd/nand/raw/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile new file mode 100644 index 000000000000..288db4f38a8f --- /dev/null +++ b/drivers/mtd/nand/raw/atmel/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MTD_NAND_ATMEL) += atmel-nand-controller.o atmel-pmecc.o + +atmel-nand-controller-objs := nand-controller.o +atmel-pmecc-objs := pmecc.o diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c new file mode 100644 index 000000000000..12f6753d47ae --- /dev/null +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -0,0 +1,2565 @@ +/* + * Copyright 2017 ATMEL + * Copyright 2017 Free Electrons + * + * Author: Boris Brezillon + * + * Derived from the atmel_nand.c driver which contained the following + * copyrights: + * + * Copyright 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) + * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c (removed in v3.8) + * Copyright 2000 Steven J. Hill (sjhill@cotw.com) + * + * + * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 + * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 + * + * Derived from Das U-Boot source code + * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) + * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * Add Programmable Multibit ECC support for various AT91 SoC + * Copyright 2012 ATMEL, Hong Xu + * + * Add Nand Flash Controller support for SAMA5 SoC + * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * A few words about the naming convention in this file. This convention + * applies to structure and function names. + * + * Prefixes: + * + * - atmel_nand_: all generic structures/functions + * - atmel_smc_nand_: all structures/functions specific to the SMC interface + * (at91sam9 and avr32 SoCs) + * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface + * (sama5 SoCs and later) + * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block + * that is available in the HSMC block + * - _nand_: all SoC specific structures/functions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmecc.h" + +#define ATMEL_HSMC_NFC_CFG 0x0 +#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24) +#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24) +#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20)) +#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16) +#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13) +#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12) +#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9) +#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8) +#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0) +#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1) + +#define ATMEL_HSMC_NFC_CTRL 0x4 +#define ATMEL_HSMC_NFC_CTRL_EN BIT(0) +#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1) + +#define ATMEL_HSMC_NFC_SR 0x8 +#define ATMEL_HSMC_NFC_IER 0xc +#define ATMEL_HSMC_NFC_IDR 0x10 +#define ATMEL_HSMC_NFC_IMR 0x14 +#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1) +#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4) +#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5) +#define ATMEL_HSMC_NFC_SR_BUSY BIT(8) +#define ATMEL_HSMC_NFC_SR_WR BIT(11) +#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12) +#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16) +#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17) +#define ATMEL_HSMC_NFC_SR_DTOE BIT(20) +#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21) +#define ATMEL_HSMC_NFC_SR_AWB BIT(22) +#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23) +#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \ + ATMEL_HSMC_NFC_SR_UNDEF | \ + ATMEL_HSMC_NFC_SR_AWB | \ + ATMEL_HSMC_NFC_SR_NFCASE) +#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24) + +#define ATMEL_HSMC_NFC_ADDR 0x18 +#define ATMEL_HSMC_NFC_BANK 0x1c + +#define ATMEL_NFC_MAX_RB_ID 7 + +#define ATMEL_NFC_SRAM_SIZE 0x2400 + +#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2)) +#define ATMEL_NFC_VCMD2 BIT(18) +#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19) +#define ATMEL_NFC_CSID(cs) ((cs) << 22) +#define ATMEL_NFC_DATAEN BIT(25) +#define ATMEL_NFC_NFCWR BIT(26) + +#define ATMEL_NFC_MAX_ADDR_CYCLES 5 + +#define ATMEL_NAND_ALE_OFFSET BIT(21) +#define ATMEL_NAND_CLE_OFFSET BIT(22) + +#define DEFAULT_TIMEOUT_MS 1000 +#define MIN_DMA_LEN 128 + +enum atmel_nand_rb_type { + ATMEL_NAND_NO_RB, + ATMEL_NAND_NATIVE_RB, + ATMEL_NAND_GPIO_RB, +}; + +struct atmel_nand_rb { + enum atmel_nand_rb_type type; + union { + struct gpio_desc *gpio; + int id; + }; +}; + +struct atmel_nand_cs { + int id; + struct atmel_nand_rb rb; + struct gpio_desc *csgpio; + struct { + void __iomem *virt; + dma_addr_t dma; + } io; + + struct atmel_smc_cs_conf smcconf; +}; + +struct atmel_nand { + struct list_head node; + struct device *dev; + struct nand_chip base; + struct atmel_nand_cs *activecs; + struct atmel_pmecc_user *pmecc; + struct gpio_desc *cdgpio; + int numcs; + struct atmel_nand_cs cs[]; +}; + +static inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip) +{ + return container_of(chip, struct atmel_nand, base); +} + +enum atmel_nfc_data_xfer { + ATMEL_NFC_NO_DATA, + ATMEL_NFC_READ_DATA, + ATMEL_NFC_WRITE_DATA, +}; + +struct atmel_nfc_op { + u8 cs; + u8 ncmds; + u8 cmds[2]; + u8 naddrs; + u8 addrs[5]; + enum atmel_nfc_data_xfer data; + u32 wait; + u32 errors; +}; + +struct atmel_nand_controller; +struct atmel_nand_controller_caps; + +struct atmel_nand_controller_ops { + int (*probe)(struct platform_device *pdev, + const struct atmel_nand_controller_caps *caps); + int (*remove)(struct atmel_nand_controller *nc); + void (*nand_init)(struct atmel_nand_controller *nc, + struct atmel_nand *nand); + int (*ecc_init)(struct atmel_nand *nand); + int (*setup_data_interface)(struct atmel_nand *nand, int csline, + const struct nand_data_interface *conf); +}; + +struct atmel_nand_controller_caps { + bool has_dma; + bool legacy_of_bindings; + u32 ale_offs; + u32 cle_offs; + const struct atmel_nand_controller_ops *ops; +}; + +struct atmel_nand_controller { + struct nand_hw_control base; + const struct atmel_nand_controller_caps *caps; + struct device *dev; + struct regmap *smc; + struct dma_chan *dmac; + struct atmel_pmecc *pmecc; + struct list_head chips; + struct clk *mck; +}; + +static inline struct atmel_nand_controller * +to_nand_controller(struct nand_hw_control *ctl) +{ + return container_of(ctl, struct atmel_nand_controller, base); +} + +struct atmel_smc_nand_controller { + struct atmel_nand_controller base; + struct regmap *matrix; + unsigned int ebi_csa_offs; +}; + +static inline struct atmel_smc_nand_controller * +to_smc_nand_controller(struct nand_hw_control *ctl) +{ + return container_of(to_nand_controller(ctl), + struct atmel_smc_nand_controller, base); +} + +struct atmel_hsmc_nand_controller { + struct atmel_nand_controller base; + struct { + struct gen_pool *pool; + void __iomem *virt; + dma_addr_t dma; + } sram; + const struct atmel_hsmc_reg_layout *hsmc_layout; + struct regmap *io; + struct atmel_nfc_op op; + struct completion complete; + int irq; + + /* Only used when instantiating from legacy DT bindings. */ + struct clk *clk; +}; + +static inline struct atmel_hsmc_nand_controller * +to_hsmc_nand_controller(struct nand_hw_control *ctl) +{ + return container_of(to_nand_controller(ctl), + struct atmel_hsmc_nand_controller, base); +} + +static bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status) +{ + op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS; + op->wait ^= status & op->wait; + + return !op->wait || op->errors; +} + +static irqreturn_t atmel_nfc_interrupt(int irq, void *data) +{ + struct atmel_hsmc_nand_controller *nc = data; + u32 sr, rcvd; + bool done; + + regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr); + + rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); + done = atmel_nfc_op_done(&nc->op, sr); + + if (rcvd) + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd); + + if (done) + complete(&nc->complete); + + return rcvd ? IRQ_HANDLED : IRQ_NONE; +} + +static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll, + unsigned int timeout_ms) +{ + int ret; + + if (!timeout_ms) + timeout_ms = DEFAULT_TIMEOUT_MS; + + if (poll) { + u32 status; + + ret = regmap_read_poll_timeout(nc->base.smc, + ATMEL_HSMC_NFC_SR, status, + atmel_nfc_op_done(&nc->op, + status), + 0, timeout_ms * 1000); + } else { + init_completion(&nc->complete); + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER, + nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); + ret = wait_for_completion_timeout(&nc->complete, + msecs_to_jiffies(timeout_ms)); + if (!ret) + ret = -ETIMEDOUT; + else + ret = 0; + + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); + } + + if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) { + dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n"); + ret = -ETIMEDOUT; + } + + if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) { + dev_err(nc->base.dev, "Access to an undefined area\n"); + ret = -EIO; + } + + if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) { + dev_err(nc->base.dev, "Access while busy\n"); + ret = -EIO; + } + + if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) { + dev_err(nc->base.dev, "Wrong access size\n"); + ret = -EIO; + } + + return ret; +} + +static void atmel_nand_dma_transfer_finished(void *data) +{ + struct completion *finished = data; + + complete(finished); +} + +static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc, + void *buf, dma_addr_t dev_dma, size_t len, + enum dma_data_direction dir) +{ + DECLARE_COMPLETION_ONSTACK(finished); + dma_addr_t src_dma, dst_dma, buf_dma; + struct dma_async_tx_descriptor *tx; + dma_cookie_t cookie; + + buf_dma = dma_map_single(nc->dev, buf, len, dir); + if (dma_mapping_error(nc->dev, dev_dma)) { + dev_err(nc->dev, + "Failed to prepare a buffer for DMA access\n"); + goto err; + } + + if (dir == DMA_FROM_DEVICE) { + src_dma = dev_dma; + dst_dma = buf_dma; + } else { + src_dma = buf_dma; + dst_dma = dev_dma; + } + + tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(nc->dev, "Failed to prepare DMA memcpy\n"); + goto err_unmap; + } + + tx->callback = atmel_nand_dma_transfer_finished; + tx->callback_param = &finished; + + cookie = dmaengine_submit(tx); + if (dma_submit_error(cookie)) { + dev_err(nc->dev, "Failed to do DMA tx_submit\n"); + goto err_unmap; + } + + dma_async_issue_pending(nc->dmac); + wait_for_completion(&finished); + + return 0; + +err_unmap: + dma_unmap_single(nc->dev, buf_dma, len, dir); + +err: + dev_dbg(nc->dev, "Fall back to CPU I/O\n"); + + return -EIO; +} + +static u8 atmel_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + + return ioread8(nand->activecs->io.virt); +} + +static u16 atmel_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + + return ioread16(nand->activecs->io.virt); +} + +static void atmel_nand_write_byte(struct mtd_info *mtd, u8 byte) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + + if (chip->options & NAND_BUSWIDTH_16) + iowrite16(byte | (byte << 8), nand->activecs->io.virt); + else + iowrite8(byte, nand->activecs->io.virt); +} + +static void atmel_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_nand_controller *nc; + + nc = to_nand_controller(chip->controller); + + /* + * If the controller supports DMA, the buffer address is DMA-able and + * len is long enough to make DMA transfers profitable, let's trigger + * a DMA transfer. If it fails, fallback to PIO mode. + */ + if (nc->dmac && virt_addr_valid(buf) && + len >= MIN_DMA_LEN && + !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len, + DMA_FROM_DEVICE)) + return; + + if (chip->options & NAND_BUSWIDTH_16) + ioread16_rep(nand->activecs->io.virt, buf, len / 2); + else + ioread8_rep(nand->activecs->io.virt, buf, len); +} + +static void atmel_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_nand_controller *nc; + + nc = to_nand_controller(chip->controller); + + /* + * If the controller supports DMA, the buffer address is DMA-able and + * len is long enough to make DMA transfers profitable, let's trigger + * a DMA transfer. If it fails, fallback to PIO mode. + */ + if (nc->dmac && virt_addr_valid(buf) && + len >= MIN_DMA_LEN && + !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma, + len, DMA_TO_DEVICE)) + return; + + if (chip->options & NAND_BUSWIDTH_16) + iowrite16_rep(nand->activecs->io.virt, buf, len / 2); + else + iowrite8_rep(nand->activecs->io.virt, buf, len); +} + +static int atmel_nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + + return gpiod_get_value(nand->activecs->rb.gpio); +} + +static void atmel_nand_select_chip(struct mtd_info *mtd, int cs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + + if (cs < 0 || cs >= nand->numcs) { + nand->activecs = NULL; + chip->dev_ready = NULL; + return; + } + + nand->activecs = &nand->cs[cs]; + + if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB) + chip->dev_ready = atmel_nand_dev_ready; +} + +static int atmel_hsmc_nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_hsmc_nand_controller *nc; + u32 status; + + nc = to_hsmc_nand_controller(chip->controller); + + regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status); + + return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id); +} + +static void atmel_hsmc_nand_select_chip(struct mtd_info *mtd, int cs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_hsmc_nand_controller *nc; + + nc = to_hsmc_nand_controller(chip->controller); + + atmel_nand_select_chip(mtd, cs); + + if (!nand->activecs) { + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, + ATMEL_HSMC_NFC_CTRL_DIS); + return; + } + + if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB) + chip->dev_ready = atmel_hsmc_nand_dev_ready; + + regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG, + ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK | + ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK | + ATMEL_HSMC_NFC_CFG_RSPARE | + ATMEL_HSMC_NFC_CFG_WSPARE, + ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) | + ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) | + ATMEL_HSMC_NFC_CFG_RSPARE); + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, + ATMEL_HSMC_NFC_CTRL_EN); +} + +static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll) +{ + u8 *addrs = nc->op.addrs; + unsigned int op = 0; + u32 addr, val; + int i, ret; + + nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE; + + for (i = 0; i < nc->op.ncmds; i++) + op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]); + + if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++); + + op |= ATMEL_NFC_CSID(nc->op.cs) | + ATMEL_NFC_ACYCLE(nc->op.naddrs); + + if (nc->op.ncmds > 1) + op |= ATMEL_NFC_VCMD2; + + addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) | + (addrs[3] << 24); + + if (nc->op.data != ATMEL_NFC_NO_DATA) { + op |= ATMEL_NFC_DATAEN; + nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE; + + if (nc->op.data == ATMEL_NFC_WRITE_DATA) + op |= ATMEL_NFC_NFCWR; + } + + /* Clear all flags. */ + regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val); + + /* Send the command. */ + regmap_write(nc->io, op, addr); + + ret = atmel_nfc_wait(nc, poll, 0); + if (ret) + dev_err(nc->base.dev, + "Failed to send NAND command (err = %d)!", + ret); + + /* Reset the op state. */ + memset(&nc->op, 0, sizeof(nc->op)); + + return ret; +} + +static void atmel_hsmc_nand_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_hsmc_nand_controller *nc; + + nc = to_hsmc_nand_controller(chip->controller); + + if (ctrl & NAND_ALE) { + if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) + return; + + nc->op.addrs[nc->op.naddrs++] = dat; + } else if (ctrl & NAND_CLE) { + if (nc->op.ncmds > 1) + return; + + nc->op.cmds[nc->op.ncmds++] = dat; + } + + if (dat == NAND_CMD_NONE) { + nc->op.cs = nand->activecs->id; + atmel_nfc_exec_op(nc, true); + } +} + +static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_nand_controller *nc; + + nc = to_nand_controller(chip->controller); + + if ((ctrl & NAND_CTRL_CHANGE) && nand->activecs->csgpio) { + if (ctrl & NAND_NCE) + gpiod_set_value(nand->activecs->csgpio, 0); + else + gpiod_set_value(nand->activecs->csgpio, 1); + } + + if (ctrl & NAND_ALE) + writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs); + else if (ctrl & NAND_CLE) + writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs); +} + +static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf, + bool oob_required) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_hsmc_nand_controller *nc; + int ret = -EIO; + + nc = to_hsmc_nand_controller(chip->controller); + + if (nc->base.dmac) + ret = atmel_nand_dma_transfer(&nc->base, (void *)buf, + nc->sram.dma, mtd->writesize, + DMA_TO_DEVICE); + + /* Falling back to CPU copy. */ + if (ret) + memcpy_toio(nc->sram.virt, buf, mtd->writesize); + + if (oob_required) + memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi, + mtd->oobsize); +} + +static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf, + bool oob_required) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_hsmc_nand_controller *nc; + int ret = -EIO; + + nc = to_hsmc_nand_controller(chip->controller); + + if (nc->base.dmac) + ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma, + mtd->writesize, DMA_FROM_DEVICE); + + /* Falling back to CPU copy. */ + if (ret) + memcpy_fromio(buf, nc->sram.virt, mtd->writesize); + + if (oob_required) + memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize, + mtd->oobsize); +} + +static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_hsmc_nand_controller *nc; + + nc = to_hsmc_nand_controller(chip->controller); + + if (column >= 0) { + nc->op.addrs[nc->op.naddrs++] = column; + + /* + * 2 address cycles for the column offset on large page NANDs. + */ + if (mtd->writesize > 512) + nc->op.addrs[nc->op.naddrs++] = column >> 8; + } + + if (page >= 0) { + nc->op.addrs[nc->op.naddrs++] = page; + nc->op.addrs[nc->op.naddrs++] = page >> 8; + + if (chip->options & NAND_ROW_ADDR_3) + nc->op.addrs[nc->op.naddrs++] = page >> 16; + } +} + +static int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_nand_controller *nc; + int ret; + + nc = to_nand_controller(chip->controller); + + if (raw) + return 0; + + ret = atmel_pmecc_enable(nand->pmecc, op); + if (ret) + dev_err(nc->dev, + "Failed to enable ECC engine (err = %d)\n", ret); + + return ret; +} + +static void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + + if (!raw) + atmel_pmecc_disable(nand->pmecc); +} + +static int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand_controller *nc; + struct mtd_oob_region oobregion; + void *eccbuf; + int ret, i; + + nc = to_nand_controller(chip->controller); + + if (raw) + return 0; + + ret = atmel_pmecc_wait_rdy(nand->pmecc); + if (ret) { + dev_err(nc->dev, + "Failed to transfer NAND page data (err = %d)\n", + ret); + return ret; + } + + mtd_ooblayout_ecc(mtd, 0, &oobregion); + eccbuf = chip->oob_poi + oobregion.offset; + + for (i = 0; i < chip->ecc.steps; i++) { + atmel_pmecc_get_generated_eccbytes(nand->pmecc, i, + eccbuf); + eccbuf += chip->ecc.bytes; + } + + return 0; +} + +static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf, + bool raw) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand_controller *nc; + struct mtd_oob_region oobregion; + int ret, i, max_bitflips = 0; + void *databuf, *eccbuf; + + nc = to_nand_controller(chip->controller); + + if (raw) + return 0; + + ret = atmel_pmecc_wait_rdy(nand->pmecc); + if (ret) { + dev_err(nc->dev, + "Failed to read NAND page data (err = %d)\n", + ret); + return ret; + } + + mtd_ooblayout_ecc(mtd, 0, &oobregion); + eccbuf = chip->oob_poi + oobregion.offset; + databuf = buf; + + for (i = 0; i < chip->ecc.steps; i++) { + ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf, + eccbuf); + if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc)) + ret = nand_check_erased_ecc_chunk(databuf, + chip->ecc.size, + eccbuf, + chip->ecc.bytes, + NULL, 0, + chip->ecc.strength); + + if (ret >= 0) + max_bitflips = max(ret, max_bitflips); + else + mtd->ecc_stats.failed++; + + databuf += chip->ecc.size; + eccbuf += chip->ecc.bytes; + } + + return max_bitflips; +} + +static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, + bool oob_required, int page, bool raw) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand *nand = to_atmel_nand(chip); + int ret; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); + if (ret) + return ret; + + atmel_nand_write_buf(mtd, buf, mtd->writesize); + + ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); + if (ret) { + atmel_pmecc_disable(nand->pmecc); + return ret; + } + + atmel_nand_pmecc_disable(chip, raw); + + atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, + int oob_required, int page) +{ + return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false); +} + +static int atmel_nand_pmecc_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, int oob_required, + int page) +{ + return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true); +} + +static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, + bool oob_required, int page, bool raw) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + nand_read_page_op(chip, page, 0, NULL, 0); + + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); + if (ret) + return ret; + + atmel_nand_read_buf(mtd, buf, mtd->writesize); + atmel_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + ret = atmel_nand_pmecc_correct_data(chip, buf, raw); + + atmel_nand_pmecc_disable(chip, raw); + + return ret; +} + +static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false); +} + +static int atmel_nand_pmecc_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true); +} + +static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip, + const u8 *buf, bool oob_required, + int page, bool raw) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_hsmc_nand_controller *nc; + int ret, status; + + nc = to_hsmc_nand_controller(chip->controller); + + atmel_nfc_copy_to_sram(chip, buf, false); + + nc->op.cmds[0] = NAND_CMD_SEQIN; + nc->op.ncmds = 1; + atmel_nfc_set_op_addr(chip, page, 0x0); + nc->op.cs = nand->activecs->id; + nc->op.data = ATMEL_NFC_WRITE_DATA; + + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); + if (ret) + return ret; + + ret = atmel_nfc_exec_op(nc, false); + if (ret) { + atmel_nand_pmecc_disable(chip, raw); + dev_err(nc->base.dev, + "Failed to transfer NAND page data (err = %d)\n", + ret); + return ret; + } + + ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); + + atmel_nand_pmecc_disable(chip, raw); + + if (ret) + return ret; + + atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + nc->op.cmds[0] = NAND_CMD_PAGEPROG; + nc->op.ncmds = 1; + nc->op.cs = nand->activecs->id; + ret = atmel_nfc_exec_op(nc, false); + if (ret) + dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n", + ret); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return ret; +} + +static int atmel_hsmc_nand_pmecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, int oob_required, + int page) +{ + return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, + false); +} + +static int atmel_hsmc_nand_pmecc_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, + int oob_required, int page) +{ + return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, + true); +} + +static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, + bool oob_required, int page, + bool raw) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_hsmc_nand_controller *nc; + int ret; + + nc = to_hsmc_nand_controller(chip->controller); + + /* + * Optimized read page accessors only work when the NAND R/B pin is + * connected to a native SoC R/B pin. If that's not the case, fallback + * to the non-optimized one. + */ + if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) { + nand_read_page_op(chip, page, 0, NULL, 0); + + return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, + raw); + } + + nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0; + + if (mtd->writesize > 512) + nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART; + + atmel_nfc_set_op_addr(chip, page, 0x0); + nc->op.cs = nand->activecs->id; + nc->op.data = ATMEL_NFC_READ_DATA; + + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); + if (ret) + return ret; + + ret = atmel_nfc_exec_op(nc, false); + if (ret) { + atmel_nand_pmecc_disable(chip, raw); + dev_err(nc->base.dev, + "Failed to load NAND page data (err = %d)\n", + ret); + return ret; + } + + atmel_nfc_copy_from_sram(chip, buf, true); + + ret = atmel_nand_pmecc_correct_data(chip, buf, raw); + + atmel_nand_pmecc_disable(chip, raw); + + return ret; +} + +static int atmel_hsmc_nand_pmecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, + false); +} + +static int atmel_hsmc_nand_pmecc_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + u8 *buf, int oob_required, + int page) +{ + return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, + true); +} + +static int atmel_nand_pmecc_init(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_nand_controller *nc; + struct atmel_pmecc_user_req req; + + nc = to_nand_controller(chip->controller); + + if (!nc->pmecc) { + dev_err(nc->dev, "HW ECC not supported\n"); + return -ENOTSUPP; + } + + if (nc->caps->legacy_of_bindings) { + u32 val; + + if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap", + &val)) + chip->ecc.strength = val; + + if (!of_property_read_u32(nc->dev->of_node, + "atmel,pmecc-sector-size", + &val)) + chip->ecc.size = val; + } + + if (chip->ecc.options & NAND_ECC_MAXIMIZE) + req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; + else if (chip->ecc.strength) + req.ecc.strength = chip->ecc.strength; + else if (chip->ecc_strength_ds) + req.ecc.strength = chip->ecc_strength_ds; + else + req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; + + if (chip->ecc.size) + req.ecc.sectorsize = chip->ecc.size; + else if (chip->ecc_step_ds) + req.ecc.sectorsize = chip->ecc_step_ds; + else + req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO; + + req.pagesize = mtd->writesize; + req.oobsize = mtd->oobsize; + + if (mtd->writesize <= 512) { + req.ecc.bytes = 4; + req.ecc.ooboffset = 0; + } else { + req.ecc.bytes = mtd->oobsize - 2; + req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO; + } + + nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req); + if (IS_ERR(nand->pmecc)) + return PTR_ERR(nand->pmecc); + + chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.size = req.ecc.sectorsize; + chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors; + chip->ecc.strength = req.ecc.strength; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + + return 0; +} + +static int atmel_nand_ecc_init(struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + struct atmel_nand_controller *nc; + int ret; + + nc = to_nand_controller(chip->controller); + + switch (chip->ecc.mode) { + case NAND_ECC_NONE: + case NAND_ECC_SOFT: + /* + * Nothing to do, the core will initialize everything for us. + */ + break; + + case NAND_ECC_HW: + ret = atmel_nand_pmecc_init(chip); + if (ret) + return ret; + + chip->ecc.read_page = atmel_nand_pmecc_read_page; + chip->ecc.write_page = atmel_nand_pmecc_write_page; + chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw; + chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw; + break; + + default: + /* Other modes are not supported. */ + dev_err(nc->dev, "Unsupported ECC mode: %d\n", + chip->ecc.mode); + return -ENOTSUPP; + } + + return 0; +} + +static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + int ret; + + ret = atmel_nand_ecc_init(nand); + if (ret) + return ret; + + if (chip->ecc.mode != NAND_ECC_HW) + return 0; + + /* Adjust the ECC operations for the HSMC IP. */ + chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page; + chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page; + chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw; + chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw; + + return 0; +} + +static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, + const struct nand_data_interface *conf, + struct atmel_smc_cs_conf *smcconf) +{ + u32 ncycles, totalcycles, timeps, mckperiodps; + struct atmel_nand_controller *nc; + int ret; + + nc = to_nand_controller(nand->base.controller); + + /* DDR interface not supported. */ + if (conf->type != NAND_SDR_IFACE) + return -ENOTSUPP; + + /* + * tRC < 30ns implies EDO mode. This controller does not support this + * mode. + */ + if (conf->timings.sdr.tRC_min < 30000) + return -ENOTSUPP; + + atmel_smc_cs_conf_init(smcconf); + + mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck); + mckperiodps *= 1000; + + /* + * Set write pulse timing. This one is easy to extract: + * + * NWE_PULSE = tWP + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps); + totalcycles = ncycles; + ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * The write setup timing depends on the operation done on the NAND. + * All operations goes through the same data bus, but the operation + * type depends on the address we are writing to (ALE/CLE address + * lines). + * Since we have no way to differentiate the different operations at + * the SMC level, we must consider the worst case (the biggest setup + * time among all operation types): + * + * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE + */ + timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min, + conf->timings.sdr.tALS_min); + timeps = max(timeps, conf->timings.sdr.tDS_min); + ncycles = DIV_ROUND_UP(timeps, mckperiodps); + ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0; + totalcycles += ncycles; + ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * As for the write setup timing, the write hold timing depends on the + * operation done on the NAND: + * + * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH) + */ + timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min, + conf->timings.sdr.tALH_min); + timeps = max3(timeps, conf->timings.sdr.tDH_min, + conf->timings.sdr.tWH_min); + ncycles = DIV_ROUND_UP(timeps, mckperiodps); + totalcycles += ncycles; + + /* + * The write cycle timing is directly matching tWC, but is also + * dependent on the other timings on the setup and hold timings we + * calculated earlier, which gives: + * + * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD) + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps); + ncycles = max(totalcycles, ncycles); + ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * We don't want the CS line to be toggled between each byte/word + * transfer to the NAND. The only way to guarantee that is to have the + * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: + * + * NCS_WR_PULSE = NWE_CYCLE + */ + ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * As for the write setup timing, the read hold timing depends on the + * operation done on the NAND: + * + * NRD_HOLD = max(tREH, tRHOH) + */ + timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min); + ncycles = DIV_ROUND_UP(timeps, mckperiodps); + totalcycles = ncycles; + + /* + * TDF = tRHZ - NRD_HOLD + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps); + ncycles -= totalcycles; + + /* + * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and + * we might end up with a config that does not fit in the TDF field. + * Just take the max value in this case and hope that the NAND is more + * tolerant than advertised. + */ + if (ncycles > ATMEL_SMC_MODE_TDF_MAX) + ncycles = ATMEL_SMC_MODE_TDF_MAX; + else if (ncycles < ATMEL_SMC_MODE_TDF_MIN) + ncycles = ATMEL_SMC_MODE_TDF_MIN; + + smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) | + ATMEL_SMC_MODE_TDFMODE_OPTIMIZED; + + /* + * Read pulse timing directly matches tRP: + * + * NRD_PULSE = tRP + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps); + totalcycles += ncycles; + ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * The write cycle timing is directly matching tWC, but is also + * dependent on the setup and hold timings we calculated earlier, + * which gives: + * + * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD) + * + * NRD_SETUP is always 0. + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps); + ncycles = max(totalcycles, ncycles); + ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * We don't want the CS line to be toggled between each byte/word + * transfer from the NAND. The only way to guarantee that is to have + * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: + * + * NCS_RD_PULSE = NRD_CYCLE + */ + ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT, + ncycles); + if (ret) + return ret; + + /* Txxx timings are directly matching tXXX ones. */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TCLR_SHIFT, + ncycles); + if (ret) + return ret; + + ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TADL_SHIFT, + ncycles); + /* + * Version 4 of the ONFI spec mandates that tADL be at least 400 + * nanoseconds, but, depending on the master clock rate, 400 ns may not + * fit in the tADL field of the SMC reg. We need to relax the check and + * accept the -ERANGE return code. + * + * Note that previous versions of the ONFI spec had a lower tADL_min + * (100 or 200 ns). It's not clear why this timing constraint got + * increased but it seems most NANDs are fine with values lower than + * 400ns, so we should be safe. + */ + if (ret && ret != -ERANGE) + return ret; + + ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TAR_SHIFT, + ncycles); + if (ret) + return ret; + + ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TRR_SHIFT, + ncycles); + if (ret) + return ret; + + ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TWB_SHIFT, + ncycles); + if (ret) + return ret; + + /* Attach the CS line to the NFC logic. */ + smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL; + + /* Set the appropriate data bus width. */ + if (nand->base.options & NAND_BUSWIDTH_16) + smcconf->mode |= ATMEL_SMC_MODE_DBW_16; + + /* Operate in NRD/NWE READ/WRITEMODE. */ + smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD | + ATMEL_SMC_MODE_WRITEMODE_NWE; + + return 0; +} + +static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand, + int csline, + const struct nand_data_interface *conf) +{ + struct atmel_nand_controller *nc; + struct atmel_smc_cs_conf smcconf; + struct atmel_nand_cs *cs; + int ret; + + nc = to_nand_controller(nand->base.controller); + + ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); + if (ret) + return ret; + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + cs = &nand->cs[csline]; + cs->smcconf = smcconf; + atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf); + + return 0; +} + +static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand, + int csline, + const struct nand_data_interface *conf) +{ + struct atmel_hsmc_nand_controller *nc; + struct atmel_smc_cs_conf smcconf; + struct atmel_nand_cs *cs; + int ret; + + nc = to_hsmc_nand_controller(nand->base.controller); + + ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); + if (ret) + return ret; + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + cs = &nand->cs[csline]; + cs->smcconf = smcconf; + + if (cs->rb.type == ATMEL_NAND_NATIVE_RB) + cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id); + + atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id, + &cs->smcconf); + + return 0; +} + +static int atmel_nand_setup_data_interface(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_nand_controller *nc; + + nc = to_nand_controller(nand->base.controller); + + if (csline >= nand->numcs || + (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY)) + return -EINVAL; + + return nc->caps->ops->setup_data_interface(nand, csline, conf); +} + +static void atmel_nand_init(struct atmel_nand_controller *nc, + struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + struct mtd_info *mtd = nand_to_mtd(chip); + + mtd->dev.parent = nc->dev; + nand->base.controller = &nc->base; + + chip->cmd_ctrl = atmel_nand_cmd_ctrl; + chip->read_byte = atmel_nand_read_byte; + chip->read_word = atmel_nand_read_word; + chip->write_byte = atmel_nand_write_byte; + chip->read_buf = atmel_nand_read_buf; + chip->write_buf = atmel_nand_write_buf; + chip->select_chip = atmel_nand_select_chip; + + if (nc->mck && nc->caps->ops->setup_data_interface) + chip->setup_data_interface = atmel_nand_setup_data_interface; + + /* Some NANDs require a longer delay than the default one (20us). */ + chip->chip_delay = 40; + + /* + * Use a bounce buffer when the buffer passed by the MTD user is not + * suitable for DMA. + */ + if (nc->dmac) + chip->options |= NAND_USE_BOUNCE_BUFFER; + + /* Default to HW ECC if pmecc is available. */ + if (nc->pmecc) + chip->ecc.mode = NAND_ECC_HW; +} + +static void atmel_smc_nand_init(struct atmel_nand_controller *nc, + struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + struct atmel_smc_nand_controller *smc_nc; + int i; + + atmel_nand_init(nc, nand); + + smc_nc = to_smc_nand_controller(chip->controller); + if (!smc_nc->matrix) + return; + + /* Attach the CS to the NAND Flash logic. */ + for (i = 0; i < nand->numcs; i++) + regmap_update_bits(smc_nc->matrix, smc_nc->ebi_csa_offs, + BIT(nand->cs[i].id), BIT(nand->cs[i].id)); +} + +static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc, + struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + + atmel_nand_init(nc, nand); + + /* Overload some methods for the HSMC controller. */ + chip->cmd_ctrl = atmel_hsmc_nand_cmd_ctrl; + chip->select_chip = atmel_hsmc_nand_select_chip; +} + +static int atmel_nand_detect(struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand_controller *nc; + int ret; + + nc = to_nand_controller(chip->controller); + + ret = nand_scan_ident(mtd, nand->numcs, NULL); + if (ret) + dev_err(nc->dev, "nand_scan_ident() failed: %d\n", ret); + + return ret; +} + +static int atmel_nand_unregister(struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + ret = mtd_device_unregister(mtd); + if (ret) + return ret; + + nand_cleanup(chip); + list_del(&nand->node); + + return 0; +} + +static int atmel_nand_register(struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand_controller *nc; + int ret; + + nc = to_nand_controller(chip->controller); + + if (nc->caps->legacy_of_bindings || !nc->dev->of_node) { + /* + * We keep the MTD name unchanged to avoid breaking platforms + * where the MTD cmdline parser is used and the bootloader + * has not been updated to use the new naming scheme. + */ + mtd->name = "atmel_nand"; + } else if (!mtd->name) { + /* + * If the new bindings are used and the bootloader has not been + * updated to pass a new mtdparts parameter on the cmdline, you + * should define the following property in your nand node: + * + * label = "atmel_nand"; + * + * This way, mtd->name will be set by the core when + * nand_set_flash_node() is called. + */ + mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL, + "%s:nand.%d", dev_name(nc->dev), + nand->cs[0].id); + if (!mtd->name) { + dev_err(nc->dev, "Failed to allocate mtd->name\n"); + return -ENOMEM; + } + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret); + return ret; + } + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(nc->dev, "Failed to register mtd device: %d\n", ret); + nand_cleanup(chip); + return ret; + } + + list_add_tail(&nand->node, &nc->chips); + + return 0; +} + +static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc, + struct device_node *np, + int reg_cells) +{ + struct atmel_nand *nand; + struct gpio_desc *gpio; + int numcs, ret, i; + + numcs = of_property_count_elems_of_size(np, "reg", + reg_cells * sizeof(u32)); + if (numcs < 1) { + dev_err(nc->dev, "Missing or invalid reg property\n"); + return ERR_PTR(-EINVAL); + } + + nand = devm_kzalloc(nc->dev, + sizeof(*nand) + (numcs * sizeof(*nand->cs)), + GFP_KERNEL); + if (!nand) { + dev_err(nc->dev, "Failed to allocate NAND object\n"); + return ERR_PTR(-ENOMEM); + } + + nand->numcs = numcs; + + gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "det", 0, + &np->fwnode, GPIOD_IN, + "nand-det"); + if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { + dev_err(nc->dev, + "Failed to get detect gpio (err = %ld)\n", + PTR_ERR(gpio)); + return ERR_CAST(gpio); + } + + if (!IS_ERR(gpio)) + nand->cdgpio = gpio; + + for (i = 0; i < numcs; i++) { + struct resource res; + u32 val; + + ret = of_address_to_resource(np, 0, &res); + if (ret) { + dev_err(nc->dev, "Invalid reg property (err = %d)\n", + ret); + return ERR_PTR(ret); + } + + ret = of_property_read_u32_index(np, "reg", i * reg_cells, + &val); + if (ret) { + dev_err(nc->dev, "Invalid reg property (err = %d)\n", + ret); + return ERR_PTR(ret); + } + + nand->cs[i].id = val; + + nand->cs[i].io.dma = res.start; + nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res); + if (IS_ERR(nand->cs[i].io.virt)) + return ERR_CAST(nand->cs[i].io.virt); + + if (!of_property_read_u32(np, "atmel,rb", &val)) { + if (val > ATMEL_NFC_MAX_RB_ID) + return ERR_PTR(-EINVAL); + + nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB; + nand->cs[i].rb.id = val; + } else { + gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, + "rb", i, &np->fwnode, + GPIOD_IN, "nand-rb"); + if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { + dev_err(nc->dev, + "Failed to get R/B gpio (err = %ld)\n", + PTR_ERR(gpio)); + return ERR_CAST(gpio); + } + + if (!IS_ERR(gpio)) { + nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB; + nand->cs[i].rb.gpio = gpio; + } + } + + gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "cs", + i, &np->fwnode, + GPIOD_OUT_HIGH, + "nand-cs"); + if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { + dev_err(nc->dev, + "Failed to get CS gpio (err = %ld)\n", + PTR_ERR(gpio)); + return ERR_CAST(gpio); + } + + if (!IS_ERR(gpio)) + nand->cs[i].csgpio = gpio; + } + + nand_set_flash_node(&nand->base, np); + + return nand; +} + +static int +atmel_nand_controller_add_nand(struct atmel_nand_controller *nc, + struct atmel_nand *nand) +{ + int ret; + + /* No card inserted, skip this NAND. */ + if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) { + dev_info(nc->dev, "No SmartMedia card inserted.\n"); + return 0; + } + + nc->caps->ops->nand_init(nc, nand); + + ret = atmel_nand_detect(nand); + if (ret) + return ret; + + ret = nc->caps->ops->ecc_init(nand); + if (ret) + return ret; + + return atmel_nand_register(nand); +} + +static int +atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc) +{ + struct atmel_nand *nand, *tmp; + int ret; + + list_for_each_entry_safe(nand, tmp, &nc->chips, node) { + ret = atmel_nand_unregister(nand); + if (ret) + return ret; + } + + return 0; +} + +static int +atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc) +{ + struct device *dev = nc->dev; + struct platform_device *pdev = to_platform_device(dev); + struct atmel_nand *nand; + struct gpio_desc *gpio; + struct resource *res; + + /* + * Legacy bindings only allow connecting a single NAND with a unique CS + * line to the controller. + */ + nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs), + GFP_KERNEL); + if (!nand) + return -ENOMEM; + + nand->numcs = 1; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nand->cs[0].io.virt = devm_ioremap_resource(dev, res); + if (IS_ERR(nand->cs[0].io.virt)) + return PTR_ERR(nand->cs[0].io.virt); + + nand->cs[0].io.dma = res->start; + + /* + * The old driver was hardcoding the CS id to 3 for all sama5 + * controllers. Since this id is only meaningful for the sama5 + * controller we can safely assign this id to 3 no matter the + * controller. + * If one wants to connect a NAND to a different CS line, he will + * have to use the new bindings. + */ + nand->cs[0].id = 3; + + /* R/B GPIO. */ + gpio = devm_gpiod_get_index_optional(dev, NULL, 0, GPIOD_IN); + if (IS_ERR(gpio)) { + dev_err(dev, "Failed to get R/B gpio (err = %ld)\n", + PTR_ERR(gpio)); + return PTR_ERR(gpio); + } + + if (gpio) { + nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB; + nand->cs[0].rb.gpio = gpio; + } + + /* CS GPIO. */ + gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) { + dev_err(dev, "Failed to get CS gpio (err = %ld)\n", + PTR_ERR(gpio)); + return PTR_ERR(gpio); + } + + nand->cs[0].csgpio = gpio; + + /* Card detect GPIO. */ + gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN); + if (IS_ERR(gpio)) { + dev_err(dev, + "Failed to get detect gpio (err = %ld)\n", + PTR_ERR(gpio)); + return PTR_ERR(gpio); + } + + nand->cdgpio = gpio; + + nand_set_flash_node(&nand->base, nc->dev->of_node); + + return atmel_nand_controller_add_nand(nc, nand); +} + +static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc) +{ + struct device_node *np, *nand_np; + struct device *dev = nc->dev; + int ret, reg_cells; + u32 val; + + /* We do not retrieve the SMC syscon when parsing old DTs. */ + if (nc->caps->legacy_of_bindings) + return atmel_nand_controller_legacy_add_nands(nc); + + np = dev->of_node; + + ret = of_property_read_u32(np, "#address-cells", &val); + if (ret) { + dev_err(dev, "missing #address-cells property\n"); + return ret; + } + + reg_cells = val; + + ret = of_property_read_u32(np, "#size-cells", &val); + if (ret) { + dev_err(dev, "missing #address-cells property\n"); + return ret; + } + + reg_cells += val; + + for_each_child_of_node(np, nand_np) { + struct atmel_nand *nand; + + nand = atmel_nand_create(nc, nand_np, reg_cells); + if (IS_ERR(nand)) { + ret = PTR_ERR(nand); + goto err; + } + + ret = atmel_nand_controller_add_nand(nc, nand); + if (ret) + goto err; + } + + return 0; + +err: + atmel_nand_controller_remove_nands(nc); + + return ret; +} + +static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc) +{ + if (nc->dmac) + dma_release_channel(nc->dmac); + + clk_put(nc->mck); +} + +static const struct of_device_id atmel_matrix_of_ids[] = { + { + .compatible = "atmel,at91sam9260-matrix", + .data = (void *)AT91SAM9260_MATRIX_EBICSA, + }, + { + .compatible = "atmel,at91sam9261-matrix", + .data = (void *)AT91SAM9261_MATRIX_EBICSA, + }, + { + .compatible = "atmel,at91sam9263-matrix", + .data = (void *)AT91SAM9263_MATRIX_EBI0CSA, + }, + { + .compatible = "atmel,at91sam9rl-matrix", + .data = (void *)AT91SAM9RL_MATRIX_EBICSA, + }, + { + .compatible = "atmel,at91sam9g45-matrix", + .data = (void *)AT91SAM9G45_MATRIX_EBICSA, + }, + { + .compatible = "atmel,at91sam9n12-matrix", + .data = (void *)AT91SAM9N12_MATRIX_EBICSA, + }, + { + .compatible = "atmel,at91sam9x5-matrix", + .data = (void *)AT91SAM9X5_MATRIX_EBICSA, + }, + { /* sentinel */ }, +}; + +static int atmel_nand_controller_init(struct atmel_nand_controller *nc, + struct platform_device *pdev, + const struct atmel_nand_controller_caps *caps) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret; + + nand_hw_control_init(&nc->base); + INIT_LIST_HEAD(&nc->chips); + nc->dev = dev; + nc->caps = caps; + + platform_set_drvdata(pdev, nc); + + nc->pmecc = devm_atmel_pmecc_get(dev); + if (IS_ERR(nc->pmecc)) { + ret = PTR_ERR(nc->pmecc); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Could not get PMECC object (err = %d)\n", + ret); + return ret; + } + + if (nc->caps->has_dma) { + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + nc->dmac = dma_request_channel(mask, NULL, NULL); + if (!nc->dmac) + dev_err(nc->dev, "Failed to request DMA channel\n"); + } + + /* We do not retrieve the SMC syscon when parsing old DTs. */ + if (nc->caps->legacy_of_bindings) + return 0; + + nc->mck = of_clk_get(dev->parent->of_node, 0); + if (IS_ERR(nc->mck)) { + dev_err(dev, "Failed to retrieve MCK clk\n"); + return PTR_ERR(nc->mck); + } + + np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); + if (!np) { + dev_err(dev, "Missing or invalid atmel,smc property\n"); + return -EINVAL; + } + + nc->smc = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(nc->smc)) { + ret = PTR_ERR(nc->smc); + dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret); + return ret; + } + + return 0; +} + +static int +atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc) +{ + struct device *dev = nc->base.dev; + const struct of_device_id *match; + struct device_node *np; + int ret; + + /* We do not retrieve the matrix syscon when parsing old DTs. */ + if (nc->base.caps->legacy_of_bindings) + return 0; + + np = of_parse_phandle(dev->parent->of_node, "atmel,matrix", 0); + if (!np) + return 0; + + match = of_match_node(atmel_matrix_of_ids, np); + if (!match) { + of_node_put(np); + return 0; + } + + nc->matrix = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(nc->matrix)) { + ret = PTR_ERR(nc->matrix); + dev_err(dev, "Could not get Matrix regmap (err = %d)\n", ret); + return ret; + } + + nc->ebi_csa_offs = (unsigned int)match->data; + + /* + * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1 + * add 4 to ->ebi_csa_offs. + */ + if (of_device_is_compatible(dev->parent->of_node, + "atmel,at91sam9263-ebi1")) + nc->ebi_csa_offs += 4; + + return 0; +} + +static int +atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc) +{ + struct regmap_config regmap_conf = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + + struct device *dev = nc->base.dev; + struct device_node *nand_np, *nfc_np; + void __iomem *iomem; + struct resource res; + int ret; + + nand_np = dev->of_node; + nfc_np = of_find_compatible_node(dev->of_node, NULL, + "atmel,sama5d3-nfc"); + + nc->clk = of_clk_get(nfc_np, 0); + if (IS_ERR(nc->clk)) { + ret = PTR_ERR(nc->clk); + dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n", + ret); + goto out; + } + + ret = clk_prepare_enable(nc->clk); + if (ret) { + dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n", + ret); + goto out; + } + + nc->irq = of_irq_get(nand_np, 0); + if (nc->irq <= 0) { + ret = nc->irq ?: -ENXIO; + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get IRQ number (err = %d)\n", + ret); + goto out; + } + + ret = of_address_to_resource(nfc_np, 0, &res); + if (ret) { + dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n", + ret); + goto out; + } + + iomem = devm_ioremap_resource(dev, &res); + if (IS_ERR(iomem)) { + ret = PTR_ERR(iomem); + goto out; + } + + regmap_conf.name = "nfc-io"; + regmap_conf.max_register = resource_size(&res) - 4; + nc->io = devm_regmap_init_mmio(dev, iomem, ®map_conf); + if (IS_ERR(nc->io)) { + ret = PTR_ERR(nc->io); + dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", + ret); + goto out; + } + + ret = of_address_to_resource(nfc_np, 1, &res); + if (ret) { + dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n", + ret); + goto out; + } + + iomem = devm_ioremap_resource(dev, &res); + if (IS_ERR(iomem)) { + ret = PTR_ERR(iomem); + goto out; + } + + regmap_conf.name = "smc"; + regmap_conf.max_register = resource_size(&res) - 4; + nc->base.smc = devm_regmap_init_mmio(dev, iomem, ®map_conf); + if (IS_ERR(nc->base.smc)) { + ret = PTR_ERR(nc->base.smc); + dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", + ret); + goto out; + } + + ret = of_address_to_resource(nfc_np, 2, &res); + if (ret) { + dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n", + ret); + goto out; + } + + nc->sram.virt = devm_ioremap_resource(dev, &res); + if (IS_ERR(nc->sram.virt)) { + ret = PTR_ERR(nc->sram.virt); + goto out; + } + + nc->sram.dma = res.start; + +out: + of_node_put(nfc_np); + + return ret; +} + +static int +atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc) +{ + struct device *dev = nc->base.dev; + struct device_node *np; + int ret; + + np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); + if (!np) { + dev_err(dev, "Missing or invalid atmel,smc property\n"); + return -EINVAL; + } + + nc->hsmc_layout = atmel_hsmc_get_reg_layout(np); + + nc->irq = of_irq_get(np, 0); + of_node_put(np); + if (nc->irq <= 0) { + ret = nc->irq ?: -ENXIO; + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get IRQ number (err = %d)\n", + ret); + return ret; + } + + np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0); + if (!np) { + dev_err(dev, "Missing or invalid atmel,nfc-io property\n"); + return -EINVAL; + } + + nc->io = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(nc->io)) { + ret = PTR_ERR(nc->io); + dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret); + return ret; + } + + nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node, + "atmel,nfc-sram", 0); + if (!nc->sram.pool) { + dev_err(nc->base.dev, "Missing SRAM\n"); + return -ENOMEM; + } + + nc->sram.virt = gen_pool_dma_alloc(nc->sram.pool, + ATMEL_NFC_SRAM_SIZE, + &nc->sram.dma); + if (!nc->sram.virt) { + dev_err(nc->base.dev, + "Could not allocate memory from the NFC SRAM pool\n"); + return -ENOMEM; + } + + return 0; +} + +static int +atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc) +{ + struct atmel_hsmc_nand_controller *hsmc_nc; + int ret; + + ret = atmel_nand_controller_remove_nands(nc); + if (ret) + return ret; + + hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base); + if (hsmc_nc->sram.pool) + gen_pool_free(hsmc_nc->sram.pool, + (unsigned long)hsmc_nc->sram.virt, + ATMEL_NFC_SRAM_SIZE); + + if (hsmc_nc->clk) { + clk_disable_unprepare(hsmc_nc->clk); + clk_put(hsmc_nc->clk); + } + + atmel_nand_controller_cleanup(nc); + + return 0; +} + +static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev, + const struct atmel_nand_controller_caps *caps) +{ + struct device *dev = &pdev->dev; + struct atmel_hsmc_nand_controller *nc; + int ret; + + nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); + if (!nc) + return -ENOMEM; + + ret = atmel_nand_controller_init(&nc->base, pdev, caps); + if (ret) + return ret; + + if (caps->legacy_of_bindings) + ret = atmel_hsmc_nand_controller_legacy_init(nc); + else + ret = atmel_hsmc_nand_controller_init(nc); + + if (ret) + return ret; + + /* Make sure all irqs are masked before registering our IRQ handler. */ + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); + ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt, + IRQF_SHARED, "nfc", nc); + if (ret) { + dev_err(dev, + "Could not get register NFC interrupt handler (err = %d)\n", + ret); + goto err; + } + + /* Initial NFC configuration. */ + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG, + ATMEL_HSMC_NFC_CFG_DTO_MAX); + + ret = atmel_nand_controller_add_nands(&nc->base); + if (ret) + goto err; + + return 0; + +err: + atmel_hsmc_nand_controller_remove(&nc->base); + + return ret; +} + +static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = { + .probe = atmel_hsmc_nand_controller_probe, + .remove = atmel_hsmc_nand_controller_remove, + .ecc_init = atmel_hsmc_nand_ecc_init, + .nand_init = atmel_hsmc_nand_init, + .setup_data_interface = atmel_hsmc_nand_setup_data_interface, +}; + +static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = { + .has_dma = true, + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ops = &atmel_hsmc_nc_ops, +}; + +/* Only used to parse old bindings. */ +static const struct atmel_nand_controller_caps atmel_sama5_nand_caps = { + .has_dma = true, + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ops = &atmel_hsmc_nc_ops, + .legacy_of_bindings = true, +}; + +static int atmel_smc_nand_controller_probe(struct platform_device *pdev, + const struct atmel_nand_controller_caps *caps) +{ + struct device *dev = &pdev->dev; + struct atmel_smc_nand_controller *nc; + int ret; + + nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); + if (!nc) + return -ENOMEM; + + ret = atmel_nand_controller_init(&nc->base, pdev, caps); + if (ret) + return ret; + + ret = atmel_smc_nand_controller_init(nc); + if (ret) + return ret; + + return atmel_nand_controller_add_nands(&nc->base); +} + +static int +atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc) +{ + int ret; + + ret = atmel_nand_controller_remove_nands(nc); + if (ret) + return ret; + + atmel_nand_controller_cleanup(nc); + + return 0; +} + +/* + * The SMC reg layout of at91rm9200 is completely different which prevents us + * from re-using atmel_smc_nand_setup_data_interface() for the + * ->setup_data_interface() hook. + * At this point, there's no support for the at91rm9200 SMC IP, so we leave + * ->setup_data_interface() unassigned. + */ +static const struct atmel_nand_controller_ops at91rm9200_nc_ops = { + .probe = atmel_smc_nand_controller_probe, + .remove = atmel_smc_nand_controller_remove, + .ecc_init = atmel_nand_ecc_init, + .nand_init = atmel_smc_nand_init, +}; + +static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = { + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ops = &at91rm9200_nc_ops, +}; + +static const struct atmel_nand_controller_ops atmel_smc_nc_ops = { + .probe = atmel_smc_nand_controller_probe, + .remove = atmel_smc_nand_controller_remove, + .ecc_init = atmel_nand_ecc_init, + .nand_init = atmel_smc_nand_init, + .setup_data_interface = atmel_smc_nand_setup_data_interface, +}; + +static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = { + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ops = &atmel_smc_nc_ops, +}; + +static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = { + .ale_offs = BIT(22), + .cle_offs = BIT(21), + .ops = &atmel_smc_nc_ops, +}; + +static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = { + .has_dma = true, + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ops = &atmel_smc_nc_ops, +}; + +/* Only used to parse old bindings. */ +static const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = { + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ops = &atmel_smc_nc_ops, + .legacy_of_bindings = true, +}; + +static const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = { + .ale_offs = BIT(22), + .cle_offs = BIT(21), + .ops = &atmel_smc_nc_ops, + .legacy_of_bindings = true, +}; + +static const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = { + .has_dma = true, + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ops = &atmel_smc_nc_ops, + .legacy_of_bindings = true, +}; + +static const struct of_device_id atmel_nand_controller_of_ids[] = { + { + .compatible = "atmel,at91rm9200-nand-controller", + .data = &atmel_rm9200_nc_caps, + }, + { + .compatible = "atmel,at91sam9260-nand-controller", + .data = &atmel_sam9260_nc_caps, + }, + { + .compatible = "atmel,at91sam9261-nand-controller", + .data = &atmel_sam9261_nc_caps, + }, + { + .compatible = "atmel,at91sam9g45-nand-controller", + .data = &atmel_sam9g45_nc_caps, + }, + { + .compatible = "atmel,sama5d3-nand-controller", + .data = &atmel_sama5_nc_caps, + }, + /* Support for old/deprecated bindings: */ + { + .compatible = "atmel,at91rm9200-nand", + .data = &atmel_rm9200_nand_caps, + }, + { + .compatible = "atmel,sama5d4-nand", + .data = &atmel_rm9200_nand_caps, + }, + { + .compatible = "atmel,sama5d2-nand", + .data = &atmel_rm9200_nand_caps, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids); + +static int atmel_nand_controller_probe(struct platform_device *pdev) +{ + const struct atmel_nand_controller_caps *caps; + + if (pdev->id_entry) + caps = (void *)pdev->id_entry->driver_data; + else + caps = of_device_get_match_data(&pdev->dev); + + if (!caps) { + dev_err(&pdev->dev, "Could not retrieve NFC caps\n"); + return -EINVAL; + } + + if (caps->legacy_of_bindings) { + u32 ale_offs = 21; + + /* + * If we are parsing legacy DT props and the DT contains a + * valid NFC node, forward the request to the sama5 logic. + */ + if (of_find_compatible_node(pdev->dev.of_node, NULL, + "atmel,sama5d3-nfc")) + caps = &atmel_sama5_nand_caps; + + /* + * Even if the compatible says we are dealing with an + * at91rm9200 controller, the atmel,nand-has-dma specify that + * this controller supports DMA, which means we are in fact + * dealing with an at91sam9g45+ controller. + */ + if (!caps->has_dma && + of_property_read_bool(pdev->dev.of_node, + "atmel,nand-has-dma")) + caps = &atmel_sam9g45_nand_caps; + + /* + * All SoCs except the at91sam9261 are assigning ALE to A21 and + * CLE to A22. If atmel,nand-addr-offset != 21 this means we're + * actually dealing with an at91sam9261 controller. + */ + of_property_read_u32(pdev->dev.of_node, + "atmel,nand-addr-offset", &ale_offs); + if (ale_offs != 21) + caps = &atmel_sam9261_nand_caps; + } + + return caps->ops->probe(pdev, caps); +} + +static int atmel_nand_controller_remove(struct platform_device *pdev) +{ + struct atmel_nand_controller *nc = platform_get_drvdata(pdev); + + return nc->caps->ops->remove(nc); +} + +static __maybe_unused int atmel_nand_controller_resume(struct device *dev) +{ + struct atmel_nand_controller *nc = dev_get_drvdata(dev); + struct atmel_nand *nand; + + if (nc->pmecc) + atmel_pmecc_reset(nc->pmecc); + + list_for_each_entry(nand, &nc->chips, node) { + int i; + + for (i = 0; i < nand->numcs; i++) + nand_reset(&nand->base, i); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL, + atmel_nand_controller_resume); + +static struct platform_driver atmel_nand_controller_driver = { + .driver = { + .name = "atmel-nand-controller", + .of_match_table = of_match_ptr(atmel_nand_controller_of_ids), + .pm = &atmel_nand_controller_pm_ops, + }, + .probe = atmel_nand_controller_probe, + .remove = atmel_nand_controller_remove, +}; +module_platform_driver(atmel_nand_controller_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Boris Brezillon "); +MODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs"); +MODULE_ALIAS("platform:atmel-nand-controller"); diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c new file mode 100644 index 000000000000..9de29c9afb0c --- /dev/null +++ b/drivers/mtd/nand/raw/atmel/pmecc.c @@ -0,0 +1,1012 @@ +/* + * Copyright 2017 ATMEL + * Copyright 2017 Free Electrons + * + * Author: Boris Brezillon + * + * Derived from the atmel_nand.c driver which contained the following + * copyrights: + * + * Copyright 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) + * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c (removed in v3.8) + * Copyright 2000 Steven J. Hill (sjhill@cotw.com) + * + * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 + * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 + * + * Derived from Das U-Boot source code + * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) + * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * Add Programmable Multibit ECC support for various AT91 SoC + * Copyright 2012 ATMEL, Hong Xu + * + * Add Nand Flash Controller support for SAMA5 SoC + * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The PMECC is an hardware assisted BCH engine, which means part of the + * ECC algorithm is left to the software. The hardware/software repartition + * is explained in the "PMECC Controller Functional Description" chapter in + * Atmel datasheets, and some of the functions in this file are directly + * implementing the algorithms described in the "Software Implementation" + * sub-section. + * + * TODO: it seems that the software BCH implementation in lib/bch.c is already + * providing some of the logic we are implementing here. It would be smart + * to expose the needed lib/bch.c helpers/functions and re-use them here. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmecc.h" + +/* Galois field dimension */ +#define PMECC_GF_DIMENSION_13 13 +#define PMECC_GF_DIMENSION_14 14 + +/* Primitive Polynomial used by PMECC */ +#define PMECC_GF_13_PRIMITIVE_POLY 0x201b +#define PMECC_GF_14_PRIMITIVE_POLY 0x4443 + +#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000 +#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000 + +/* Time out value for reading PMECC status register */ +#define PMECC_MAX_TIMEOUT_MS 100 + +/* PMECC Register Definitions */ +#define ATMEL_PMECC_CFG 0x0 +#define PMECC_CFG_BCH_STRENGTH(x) (x) +#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0) +#define PMECC_CFG_SECTOR512 (0 << 4) +#define PMECC_CFG_SECTOR1024 (1 << 4) +#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8) +#define PMECC_CFG_READ_OP (0 << 12) +#define PMECC_CFG_WRITE_OP (1 << 12) +#define PMECC_CFG_SPARE_ENABLE BIT(16) +#define PMECC_CFG_AUTO_ENABLE BIT(20) + +#define ATMEL_PMECC_SAREA 0x4 +#define ATMEL_PMECC_SADDR 0x8 +#define ATMEL_PMECC_EADDR 0xc + +#define ATMEL_PMECC_CLK 0x10 +#define PMECC_CLK_133MHZ (2 << 0) + +#define ATMEL_PMECC_CTRL 0x14 +#define PMECC_CTRL_RST BIT(0) +#define PMECC_CTRL_DATA BIT(1) +#define PMECC_CTRL_USER BIT(2) +#define PMECC_CTRL_ENABLE BIT(4) +#define PMECC_CTRL_DISABLE BIT(5) + +#define ATMEL_PMECC_SR 0x18 +#define PMECC_SR_BUSY BIT(0) +#define PMECC_SR_ENABLE BIT(4) + +#define ATMEL_PMECC_IER 0x1c +#define ATMEL_PMECC_IDR 0x20 +#define ATMEL_PMECC_IMR 0x24 +#define ATMEL_PMECC_ISR 0x28 +#define PMECC_ERROR_INT BIT(0) + +#define ATMEL_PMECC_ECC(sector, n) \ + ((((sector) + 1) * 0x40) + (n)) + +#define ATMEL_PMECC_REM(sector, n) \ + ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200) + +/* PMERRLOC Register Definitions */ +#define ATMEL_PMERRLOC_ELCFG 0x0 +#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0) +#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0) +#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16) + +#define ATMEL_PMERRLOC_ELPRIM 0x4 +#define ATMEL_PMERRLOC_ELEN 0x8 +#define ATMEL_PMERRLOC_ELDIS 0xc +#define PMERRLOC_DISABLE BIT(0) + +#define ATMEL_PMERRLOC_ELSR 0x10 +#define PMERRLOC_ELSR_BUSY BIT(0) + +#define ATMEL_PMERRLOC_ELIER 0x14 +#define ATMEL_PMERRLOC_ELIDR 0x18 +#define ATMEL_PMERRLOC_ELIMR 0x1c +#define ATMEL_PMERRLOC_ELISR 0x20 +#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8) +#define PMERRLOC_CALC_DONE BIT(0) + +#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28) + +#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs)) + +struct atmel_pmecc_gf_tables { + u16 *alpha_to; + u16 *index_of; +}; + +struct atmel_pmecc_caps { + const int *strengths; + int nstrengths; + int el_offset; + bool correct_erased_chunks; +}; + +struct atmel_pmecc { + struct device *dev; + const struct atmel_pmecc_caps *caps; + + struct { + void __iomem *base; + void __iomem *errloc; + } regs; + + struct mutex lock; +}; + +struct atmel_pmecc_user_conf_cache { + u32 cfg; + u32 sarea; + u32 saddr; + u32 eaddr; +}; + +struct atmel_pmecc_user { + struct atmel_pmecc_user_conf_cache cache; + struct atmel_pmecc *pmecc; + const struct atmel_pmecc_gf_tables *gf_tables; + int eccbytes; + s16 *partial_syn; + s16 *si; + s16 *lmu; + s16 *smu; + s32 *mu; + s32 *dmu; + s32 *delta; + u32 isr; +}; + +static DEFINE_MUTEX(pmecc_gf_tables_lock); +static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512; +static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024; + +static inline int deg(unsigned int poly) +{ + /* polynomial degree is the most-significant bit index */ + return fls(poly) - 1; +} + +static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly, + struct atmel_pmecc_gf_tables *gf_tables) +{ + unsigned int i, x = 1; + const unsigned int k = BIT(deg(poly)); + unsigned int nn = BIT(mm) - 1; + + /* primitive polynomial must be of degree m */ + if (k != (1u << mm)) + return -EINVAL; + + for (i = 0; i < nn; i++) { + gf_tables->alpha_to[i] = x; + gf_tables->index_of[x] = i; + if (i && (x == 1)) + /* polynomial is not primitive (a^i=1 with 0alpha_to[nn] = 1; + gf_tables->index_of[0] = 0; + + return 0; +} + +static const struct atmel_pmecc_gf_tables * +atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req) +{ + struct atmel_pmecc_gf_tables *gf_tables; + unsigned int poly, degree, table_size; + int ret; + + if (req->ecc.sectorsize == 512) { + degree = PMECC_GF_DIMENSION_13; + poly = PMECC_GF_13_PRIMITIVE_POLY; + table_size = PMECC_LOOKUP_TABLE_SIZE_512; + } else { + degree = PMECC_GF_DIMENSION_14; + poly = PMECC_GF_14_PRIMITIVE_POLY; + table_size = PMECC_LOOKUP_TABLE_SIZE_1024; + } + + gf_tables = kzalloc(sizeof(*gf_tables) + + (2 * table_size * sizeof(u16)), + GFP_KERNEL); + if (!gf_tables) + return ERR_PTR(-ENOMEM); + + gf_tables->alpha_to = (void *)(gf_tables + 1); + gf_tables->index_of = gf_tables->alpha_to + table_size; + + ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables); + if (ret) { + kfree(gf_tables); + return ERR_PTR(ret); + } + + return gf_tables; +} + +static const struct atmel_pmecc_gf_tables * +atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req) +{ + const struct atmel_pmecc_gf_tables **gf_tables, *ret; + + mutex_lock(&pmecc_gf_tables_lock); + if (req->ecc.sectorsize == 512) + gf_tables = &pmecc_gf_tables_512; + else + gf_tables = &pmecc_gf_tables_1024; + + ret = *gf_tables; + + if (!ret) { + ret = atmel_pmecc_create_gf_tables(req); + if (!IS_ERR(ret)) + *gf_tables = ret; + } + mutex_unlock(&pmecc_gf_tables_lock); + + return ret; +} + +static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc, + struct atmel_pmecc_user_req *req) +{ + int i, max_eccbytes, eccbytes = 0, eccstrength = 0; + + if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0) + return -EINVAL; + + if (req->ecc.ooboffset >= 0 && + req->ecc.ooboffset + req->ecc.bytes > req->oobsize) + return -EINVAL; + + if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) { + if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) + return -EINVAL; + + if (req->pagesize > 512) + req->ecc.sectorsize = 1024; + else + req->ecc.sectorsize = 512; + } + + if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024) + return -EINVAL; + + if (req->pagesize % req->ecc.sectorsize) + return -EINVAL; + + req->ecc.nsectors = req->pagesize / req->ecc.sectorsize; + + max_eccbytes = req->ecc.bytes; + + for (i = 0; i < pmecc->caps->nstrengths; i++) { + int nbytes, strength = pmecc->caps->strengths[i]; + + if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH && + strength < req->ecc.strength) + continue; + + nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize), + 8); + nbytes *= req->ecc.nsectors; + + if (nbytes > max_eccbytes) + break; + + eccstrength = strength; + eccbytes = nbytes; + + if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) + break; + } + + if (!eccstrength) + return -EINVAL; + + req->ecc.bytes = eccbytes; + req->ecc.strength = eccstrength; + + if (req->ecc.ooboffset < 0) + req->ecc.ooboffset = req->oobsize - eccbytes; + + return 0; +} + +struct atmel_pmecc_user * +atmel_pmecc_create_user(struct atmel_pmecc *pmecc, + struct atmel_pmecc_user_req *req) +{ + struct atmel_pmecc_user *user; + const struct atmel_pmecc_gf_tables *gf_tables; + int strength, size, ret; + + ret = atmel_pmecc_prepare_user_req(pmecc, req); + if (ret) + return ERR_PTR(ret); + + size = sizeof(*user); + size = ALIGN(size, sizeof(u16)); + /* Reserve space for partial_syn, si and smu */ + size += ((2 * req->ecc.strength) + 1) * sizeof(u16) * + (2 + req->ecc.strength + 2); + /* Reserve space for lmu. */ + size += (req->ecc.strength + 1) * sizeof(u16); + /* Reserve space for mu, dmu and delta. */ + size = ALIGN(size, sizeof(s32)); + size += (req->ecc.strength + 1) * sizeof(s32) * 3; + + user = kzalloc(size, GFP_KERNEL); + if (!user) + return ERR_PTR(-ENOMEM); + + user->pmecc = pmecc; + + user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16)); + user->si = user->partial_syn + ((2 * req->ecc.strength) + 1); + user->lmu = user->si + ((2 * req->ecc.strength) + 1); + user->smu = user->lmu + (req->ecc.strength + 1); + user->mu = (s32 *)PTR_ALIGN(user->smu + + (((2 * req->ecc.strength) + 1) * + (req->ecc.strength + 2)), + sizeof(s32)); + user->dmu = user->mu + req->ecc.strength + 1; + user->delta = user->dmu + req->ecc.strength + 1; + + gf_tables = atmel_pmecc_get_gf_tables(req); + if (IS_ERR(gf_tables)) { + kfree(user); + return ERR_CAST(gf_tables); + } + + user->gf_tables = gf_tables; + + user->eccbytes = req->ecc.bytes / req->ecc.nsectors; + + for (strength = 0; strength < pmecc->caps->nstrengths; strength++) { + if (pmecc->caps->strengths[strength] == req->ecc.strength) + break; + } + + user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) | + PMECC_CFG_NSECTORS(req->ecc.nsectors); + + if (req->ecc.sectorsize == 1024) + user->cache.cfg |= PMECC_CFG_SECTOR1024; + + user->cache.sarea = req->oobsize - 1; + user->cache.saddr = req->ecc.ooboffset; + user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1; + + return user; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_create_user); + +void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user) +{ + kfree(user); +} +EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user); + +static int get_strength(struct atmel_pmecc_user *user) +{ + const int *strengths = user->pmecc->caps->strengths; + + return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK]; +} + +static int get_sectorsize(struct atmel_pmecc_user *user) +{ + return user->cache.cfg & PMECC_LOOKUP_TABLE_SIZE_1024 ? 1024 : 512; +} + +static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector) +{ + int strength = get_strength(user); + u32 value; + int i; + + /* Fill odd syndromes */ + for (i = 0; i < strength; i++) { + value = readl_relaxed(user->pmecc->regs.base + + ATMEL_PMECC_REM(sector, i / 2)); + if (i & 1) + value >>= 16; + + user->partial_syn[(2 * i) + 1] = value; + } +} + +static void atmel_pmecc_substitute(struct atmel_pmecc_user *user) +{ + int degree = get_sectorsize(user) == 512 ? 13 : 14; + int cw_len = BIT(degree) - 1; + int strength = get_strength(user); + s16 *alpha_to = user->gf_tables->alpha_to; + s16 *index_of = user->gf_tables->index_of; + s16 *partial_syn = user->partial_syn; + s16 *si; + int i, j; + + /* + * si[] is a table that holds the current syndrome value, + * an element of that table belongs to the field + */ + si = user->si; + + memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1)); + + /* Computation 2t syndromes based on S(x) */ + /* Odd syndromes */ + for (i = 1; i < 2 * strength; i += 2) { + for (j = 0; j < degree; j++) { + if (partial_syn[i] & BIT(j)) + si[i] = alpha_to[i * j] ^ si[i]; + } + } + /* Even syndrome = (Odd syndrome) ** 2 */ + for (i = 2, j = 1; j <= strength; i = ++j << 1) { + if (si[j] == 0) { + si[i] = 0; + } else { + s16 tmp; + + tmp = index_of[si[j]]; + tmp = (tmp * 2) % cw_len; + si[i] = alpha_to[tmp]; + } + } +} + +static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user) +{ + s16 *lmu = user->lmu; + s16 *si = user->si; + s32 *mu = user->mu; + s32 *dmu = user->dmu; + s32 *delta = user->delta; + int degree = get_sectorsize(user) == 512 ? 13 : 14; + int cw_len = BIT(degree) - 1; + int strength = get_strength(user); + int num = 2 * strength + 1; + s16 *index_of = user->gf_tables->index_of; + s16 *alpha_to = user->gf_tables->alpha_to; + int i, j, k; + u32 dmu_0_count, tmp; + s16 *smu = user->smu; + + /* index of largest delta */ + int ro; + int largest; + int diff; + + dmu_0_count = 0; + + /* First Row */ + + /* Mu */ + mu[0] = -1; + + memset(smu, 0, sizeof(s16) * num); + smu[0] = 1; + + /* discrepancy set to 1 */ + dmu[0] = 1; + /* polynom order set to 0 */ + lmu[0] = 0; + delta[0] = (mu[0] * 2 - lmu[0]) >> 1; + + /* Second Row */ + + /* Mu */ + mu[1] = 0; + /* Sigma(x) set to 1 */ + memset(&smu[num], 0, sizeof(s16) * num); + smu[num] = 1; + + /* discrepancy set to S1 */ + dmu[1] = si[1]; + + /* polynom order set to 0 */ + lmu[1] = 0; + + delta[1] = (mu[1] * 2 - lmu[1]) >> 1; + + /* Init the Sigma(x) last row */ + memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num); + + for (i = 1; i <= strength; i++) { + mu[i + 1] = i << 1; + /* Begin Computing Sigma (Mu+1) and L(mu) */ + /* check if discrepancy is set to 0 */ + if (dmu[i] == 0) { + dmu_0_count++; + + tmp = ((strength - (lmu[i] >> 1) - 1) / 2); + if ((strength - (lmu[i] >> 1) - 1) & 0x1) + tmp += 2; + else + tmp += 1; + + if (dmu_0_count == tmp) { + for (j = 0; j <= (lmu[i] >> 1) + 1; j++) + smu[(strength + 1) * num + j] = + smu[i * num + j]; + + lmu[strength + 1] = lmu[i]; + return; + } + + /* copy polynom */ + for (j = 0; j <= lmu[i] >> 1; j++) + smu[(i + 1) * num + j] = smu[i * num + j]; + + /* copy previous polynom order to the next */ + lmu[i + 1] = lmu[i]; + } else { + ro = 0; + largest = -1; + /* find largest delta with dmu != 0 */ + for (j = 0; j < i; j++) { + if ((dmu[j]) && (delta[j] > largest)) { + largest = delta[j]; + ro = j; + } + } + + /* compute difference */ + diff = (mu[i] - mu[ro]); + + /* Compute degree of the new smu polynomial */ + if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) + lmu[i + 1] = lmu[i]; + else + lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; + + /* Init smu[i+1] with 0 */ + for (k = 0; k < num; k++) + smu[(i + 1) * num + k] = 0; + + /* Compute smu[i+1] */ + for (k = 0; k <= lmu[ro] >> 1; k++) { + s16 a, b, c; + + if (!(smu[ro * num + k] && dmu[i])) + continue; + + a = index_of[dmu[i]]; + b = index_of[dmu[ro]]; + c = index_of[smu[ro * num + k]]; + tmp = a + (cw_len - b) + c; + a = alpha_to[tmp % cw_len]; + smu[(i + 1) * num + (k + diff)] = a; + } + + for (k = 0; k <= lmu[i] >> 1; k++) + smu[(i + 1) * num + k] ^= smu[i * num + k]; + } + + /* End Computing Sigma (Mu+1) and L(mu) */ + /* In either case compute delta */ + delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; + + /* Do not compute discrepancy for the last iteration */ + if (i >= strength) + continue; + + for (k = 0; k <= (lmu[i + 1] >> 1); k++) { + tmp = 2 * (i - 1); + if (k == 0) { + dmu[i + 1] = si[tmp + 3]; + } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { + s16 a, b, c; + + a = index_of[smu[(i + 1) * num + k]]; + b = si[2 * (i - 1) + 3 - k]; + c = index_of[b]; + tmp = a + c; + tmp %= cw_len; + dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1]; + } + } + } +} + +static int atmel_pmecc_err_location(struct atmel_pmecc_user *user) +{ + int sector_size = get_sectorsize(user); + int degree = sector_size == 512 ? 13 : 14; + struct atmel_pmecc *pmecc = user->pmecc; + int strength = get_strength(user); + int ret, roots_nbr, i, err_nbr = 0; + int num = (2 * strength) + 1; + s16 *smu = user->smu; + u32 val; + + writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS); + + for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) { + writel_relaxed(smu[(strength + 1) * num + i], + pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i)); + err_nbr++; + } + + val = (err_nbr - 1) << 16; + if (sector_size == 1024) + val |= 1; + + writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG); + writel((sector_size * 8) + (degree * strength), + pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN); + + ret = readl_relaxed_poll_timeout(pmecc->regs.errloc + + ATMEL_PMERRLOC_ELISR, + val, val & PMERRLOC_CALC_DONE, 0, + PMECC_MAX_TIMEOUT_MS * 1000); + if (ret) { + dev_err(pmecc->dev, + "PMECC: Timeout to calculate error location.\n"); + return ret; + } + + roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8; + /* Number of roots == degree of smu hence <= cap */ + if (roots_nbr == user->lmu[strength + 1] >> 1) + return err_nbr - 1; + + /* + * Number of roots does not match the degree of smu + * unable to correct error. + */ + return -EBADMSG; +} + +int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector, + void *data, void *ecc) +{ + struct atmel_pmecc *pmecc = user->pmecc; + int sectorsize = get_sectorsize(user); + int eccbytes = user->eccbytes; + int i, nerrors; + + if (!(user->isr & BIT(sector))) + return 0; + + atmel_pmecc_gen_syndrome(user, sector); + atmel_pmecc_substitute(user); + atmel_pmecc_get_sigma(user); + + nerrors = atmel_pmecc_err_location(user); + if (nerrors < 0) + return nerrors; + + for (i = 0; i < nerrors; i++) { + const char *area; + int byte, bit; + u32 errpos; + u8 *ptr; + + errpos = readl_relaxed(pmecc->regs.errloc + + ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i)); + errpos--; + + byte = errpos / 8; + bit = errpos % 8; + + if (byte < sectorsize) { + ptr = data + byte; + area = "data"; + } else if (byte < sectorsize + eccbytes) { + ptr = ecc + byte - sectorsize; + area = "ECC"; + } else { + dev_dbg(pmecc->dev, + "Invalid errpos value (%d, max is %d)\n", + errpos, (sectorsize + eccbytes) * 8); + return -EINVAL; + } + + dev_dbg(pmecc->dev, + "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n", + area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit))); + + *ptr ^= BIT(bit); + } + + return nerrors; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector); + +bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user) +{ + return user->pmecc->caps->correct_erased_chunks; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks); + +void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, + int sector, void *ecc) +{ + struct atmel_pmecc *pmecc = user->pmecc; + u8 *ptr = ecc; + int i; + + for (i = 0; i < user->eccbytes; i++) + ptr[i] = readb_relaxed(pmecc->regs.base + + ATMEL_PMECC_ECC(sector, i)); +} +EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes); + +void atmel_pmecc_reset(struct atmel_pmecc *pmecc) +{ + writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); + writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); +} +EXPORT_SYMBOL_GPL(atmel_pmecc_reset); + +int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op) +{ + struct atmel_pmecc *pmecc = user->pmecc; + u32 cfg; + + if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) { + dev_err(pmecc->dev, "Bad ECC operation!"); + return -EINVAL; + } + + mutex_lock(&user->pmecc->lock); + + cfg = user->cache.cfg; + if (op == NAND_ECC_WRITE) + cfg |= PMECC_CFG_WRITE_OP; + else + cfg |= PMECC_CFG_AUTO_ENABLE; + + writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG); + writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA); + writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR); + writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR); + + writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); + writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL); + + return 0; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_enable); + +void atmel_pmecc_disable(struct atmel_pmecc_user *user) +{ + atmel_pmecc_reset(user->pmecc); + mutex_unlock(&user->pmecc->lock); +} +EXPORT_SYMBOL_GPL(atmel_pmecc_disable); + +int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user) +{ + struct atmel_pmecc *pmecc = user->pmecc; + u32 status; + int ret; + + ret = readl_relaxed_poll_timeout(pmecc->regs.base + + ATMEL_PMECC_SR, + status, !(status & PMECC_SR_BUSY), 0, + PMECC_MAX_TIMEOUT_MS * 1000); + if (ret) { + dev_err(pmecc->dev, + "Timeout while waiting for PMECC ready.\n"); + return ret; + } + + user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR); + + return 0; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy); + +static struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev, + const struct atmel_pmecc_caps *caps, + int pmecc_res_idx, int errloc_res_idx) +{ + struct device *dev = &pdev->dev; + struct atmel_pmecc *pmecc; + struct resource *res; + + pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL); + if (!pmecc) + return ERR_PTR(-ENOMEM); + + pmecc->caps = caps; + pmecc->dev = dev; + mutex_init(&pmecc->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, pmecc_res_idx); + pmecc->regs.base = devm_ioremap_resource(dev, res); + if (IS_ERR(pmecc->regs.base)) + return ERR_CAST(pmecc->regs.base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, errloc_res_idx); + pmecc->regs.errloc = devm_ioremap_resource(dev, res); + if (IS_ERR(pmecc->regs.errloc)) + return ERR_CAST(pmecc->regs.errloc); + + /* Disable all interrupts before registering the PMECC handler. */ + writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR); + atmel_pmecc_reset(pmecc); + + return pmecc; +} + +static void devm_atmel_pmecc_put(struct device *dev, void *res) +{ + struct atmel_pmecc **pmecc = res; + + put_device((*pmecc)->dev); +} + +static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev, + struct device_node *np) +{ + struct platform_device *pdev; + struct atmel_pmecc *pmecc, **ptr; + + pdev = of_find_device_by_node(np); + if (!pdev || !platform_get_drvdata(pdev)) + return ERR_PTR(-EPROBE_DEFER); + + ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + get_device(&pdev->dev); + pmecc = platform_get_drvdata(pdev); + + *ptr = pmecc; + + devres_add(userdev, ptr); + + return pmecc; +} + +static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 }; + +static struct atmel_pmecc_caps at91sam9g45_caps = { + .strengths = atmel_pmecc_strengths, + .nstrengths = 5, + .el_offset = 0x8c, +}; + +static struct atmel_pmecc_caps sama5d4_caps = { + .strengths = atmel_pmecc_strengths, + .nstrengths = 5, + .el_offset = 0x8c, + .correct_erased_chunks = true, +}; + +static struct atmel_pmecc_caps sama5d2_caps = { + .strengths = atmel_pmecc_strengths, + .nstrengths = 6, + .el_offset = 0xac, + .correct_erased_chunks = true, +}; + +static const struct of_device_id atmel_pmecc_legacy_match[] = { + { .compatible = "atmel,sama5d4-nand", &sama5d4_caps }, + { .compatible = "atmel,sama5d2-nand", &sama5d2_caps }, + { /* sentinel */ } +}; + +struct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev) +{ + struct atmel_pmecc *pmecc; + struct device_node *np; + + if (!userdev) + return ERR_PTR(-EINVAL); + + if (!userdev->of_node) + return NULL; + + np = of_parse_phandle(userdev->of_node, "ecc-engine", 0); + if (np) { + pmecc = atmel_pmecc_get_by_node(userdev, np); + of_node_put(np); + } else { + /* + * Support old DT bindings: in this case the PMECC iomem + * resources are directly defined in the user pdev at position + * 1 and 2. Extract all relevant information from there. + */ + struct platform_device *pdev = to_platform_device(userdev); + const struct atmel_pmecc_caps *caps; + const struct of_device_id *match; + + /* No PMECC engine available. */ + if (!of_property_read_bool(userdev->of_node, + "atmel,has-pmecc")) + return NULL; + + caps = &at91sam9g45_caps; + + /* Find the caps associated to the NAND dev node. */ + match = of_match_node(atmel_pmecc_legacy_match, + userdev->of_node); + if (match && match->data) + caps = match->data; + + pmecc = atmel_pmecc_create(pdev, caps, 1, 2); + } + + return pmecc; +} +EXPORT_SYMBOL(devm_atmel_pmecc_get); + +static const struct of_device_id atmel_pmecc_match[] = { + { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps }, + { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps }, + { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, atmel_pmecc_match); + +static int atmel_pmecc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct atmel_pmecc_caps *caps; + struct atmel_pmecc *pmecc; + + caps = of_device_get_match_data(&pdev->dev); + if (!caps) { + dev_err(dev, "Invalid caps\n"); + return -EINVAL; + } + + pmecc = atmel_pmecc_create(pdev, caps, 0, 1); + if (IS_ERR(pmecc)) + return PTR_ERR(pmecc); + + platform_set_drvdata(pdev, pmecc); + + return 0; +} + +static struct platform_driver atmel_pmecc_driver = { + .driver = { + .name = "atmel-pmecc", + .of_match_table = of_match_ptr(atmel_pmecc_match), + }, + .probe = atmel_pmecc_probe, +}; +module_platform_driver(atmel_pmecc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Boris Brezillon "); +MODULE_DESCRIPTION("PMECC engine driver"); +MODULE_ALIAS("platform:atmel_pmecc"); diff --git a/drivers/mtd/nand/raw/atmel/pmecc.h b/drivers/mtd/nand/raw/atmel/pmecc.h new file mode 100644 index 000000000000..808f1be0d6ad --- /dev/null +++ b/drivers/mtd/nand/raw/atmel/pmecc.h @@ -0,0 +1,74 @@ +/* + * © Copyright 2016 ATMEL + * © Copyright 2016 Free Electrons + * + * Author: Boris Brezillon + * + * Derived from the atmel_nand.c driver which contained the following + * copyrights: + * + * Copyright © 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) + * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c (removed in v3.8) + * Copyright © 2000 Steven J. Hill (sjhill@cotw.com) + * + * + * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 + * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007 + * + * Derived from Das U-Boot source code + * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) + * © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * Add Programmable Multibit ECC support for various AT91 SoC + * © Copyright 2012 ATMEL, Hong Xu + * + * Add Nand Flash Controller support for SAMA5 SoC + * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef ATMEL_PMECC_H +#define ATMEL_PMECC_H + +#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0 +#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0 +#define ATMEL_PMECC_OOBOFFSET_AUTO -1 + +struct atmel_pmecc_user_req { + int pagesize; + int oobsize; + struct { + int strength; + int bytes; + int sectorsize; + int nsectors; + int ooboffset; + } ecc; +}; + +struct atmel_pmecc *devm_atmel_pmecc_get(struct device *dev); + +struct atmel_pmecc_user * +atmel_pmecc_create_user(struct atmel_pmecc *pmecc, + struct atmel_pmecc_user_req *req); +void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user); + +void atmel_pmecc_reset(struct atmel_pmecc *pmecc); +int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op); +void atmel_pmecc_disable(struct atmel_pmecc_user *user); +int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user); +int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector, + void *data, void *ecc); +bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user); +void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, + int sector, void *ecc); + +#endif /* ATMEL_PMECC_H */ diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c new file mode 100644 index 000000000000..df0ef1f1e2f5 --- /dev/null +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2004 Embedded Edge, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct au1550nd_ctx { + struct nand_chip chip; + + int cs; + void __iomem *base; + void (*write_byte)(struct mtd_info *, u_char); +}; + +/** + * au_read_byte - read one byte from the chip + * @mtd: MTD device structure + * + * read function for 8bit buswidth + */ +static u_char au_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u_char ret = readb(this->IO_ADDR_R); + wmb(); /* drain writebuffer */ + return ret; +} + +/** + * au_write_byte - write one byte to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * write function for 8it buswidth + */ +static void au_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd_to_nand(mtd); + writeb(byte, this->IO_ADDR_W); + wmb(); /* drain writebuffer */ +} + +/** + * au_read_byte16 - read one byte endianness aware from the chip + * @mtd: MTD device structure + * + * read function for 16bit buswidth with endianness conversion + */ +static u_char au_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); + wmb(); /* drain writebuffer */ + return ret; +} + +/** + * au_write_byte16 - write one byte endianness aware to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * write function for 16bit buswidth with endianness conversion + */ +static void au_write_byte16(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd_to_nand(mtd); + writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); + wmb(); /* drain writebuffer */ +} + +/** + * au_read_word - read one word from the chip + * @mtd: MTD device structure + * + * read function for 16bit buswidth without endianness conversion + */ +static u16 au_read_word(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u16 ret = readw(this->IO_ADDR_R); + wmb(); /* drain writebuffer */ + return ret; +} + +/** + * au_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * write function for 8bit buswidth + */ +static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd_to_nand(mtd); + + for (i = 0; i < len; i++) { + writeb(buf[i], this->IO_ADDR_W); + wmb(); /* drain writebuffer */ + } +} + +/** + * au_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * read function for 8bit buswidth + */ +static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd_to_nand(mtd); + + for (i = 0; i < len; i++) { + buf[i] = readb(this->IO_ADDR_R); + wmb(); /* drain writebuffer */ + } +} + +/** + * au_write_buf16 - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * write function for 16bit buswidth + */ +static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd_to_nand(mtd); + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) { + writew(p[i], this->IO_ADDR_W); + wmb(); /* drain writebuffer */ + } + +} + +/** + * au_read_buf16 - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * read function for 16bit buswidth + */ +static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd_to_nand(mtd); + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) { + p[i] = readw(this->IO_ADDR_R); + wmb(); /* drain writebuffer */ + } +} + +/* Select the chip by setting nCE to low */ +#define NAND_CTL_SETNCE 1 +/* Deselect the chip by setting nCE to high */ +#define NAND_CTL_CLRNCE 2 +/* Select the command latch by setting CLE to high */ +#define NAND_CTL_SETCLE 3 +/* Deselect the command latch by setting CLE to low */ +#define NAND_CTL_CLRCLE 4 +/* Select the address latch by setting ALE to high */ +#define NAND_CTL_SETALE 5 +/* Deselect the address latch by setting ALE to low */ +#define NAND_CTL_CLRALE 6 + +static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx, + chip); + + switch (cmd) { + + case NAND_CTL_SETCLE: + this->IO_ADDR_W = ctx->base + MEM_STNAND_CMD; + break; + + case NAND_CTL_CLRCLE: + this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA; + break; + + case NAND_CTL_SETALE: + this->IO_ADDR_W = ctx->base + MEM_STNAND_ADDR; + break; + + case NAND_CTL_CLRALE: + this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA; + /* FIXME: Nobody knows why this is necessary, + * but it works only that way */ + udelay(1); + break; + + case NAND_CTL_SETNCE: + /* assert (force assert) chip enable */ + alchemy_wrsmem((1 << (4 + ctx->cs)), AU1000_MEM_STNDCTL); + break; + + case NAND_CTL_CLRNCE: + /* deassert chip enable */ + alchemy_wrsmem(0, AU1000_MEM_STNDCTL); + break; + } + + this->IO_ADDR_R = this->IO_ADDR_W; + + wmb(); /* Drain the writebuffer */ +} + +int au1550_device_ready(struct mtd_info *mtd) +{ + return (alchemy_rdsmem(AU1000_MEM_STSTAT) & 0x1) ? 1 : 0; +} + +/** + * au1550_select_chip - control -CE line + * Forbid driving -CE manually permitting the NAND controller to do this. + * Keeping -CE asserted during the whole sector reads interferes with the + * NOR flash and PCMCIA drivers as it causes contention on the static bus. + * We only have to hold -CE low for the NAND read commands since the flash + * chip needs it to be asserted during chip not ready time but the NAND + * controller keeps it released. + * + * @mtd: MTD device structure + * @chip: chipnumber to select, -1 for deselect + */ +static void au1550_select_chip(struct mtd_info *mtd, int chip) +{ +} + +/** + * au1550_command - Send command to NAND device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + */ +static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx, + chip); + int ce_override = 0, i; + unsigned long flags = 0; + + /* Begin command latch cycle */ + au1550_hwcontrol(mtd, NAND_CTL_SETCLE); + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + ctx->write_byte(mtd, readcmd); + } + ctx->write_byte(mtd, command); + + /* Set ALE and clear CLE to start address cycle */ + au1550_hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + au1550_hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + ctx->write_byte(mtd, column); + } + if (page_addr != -1) { + ctx->write_byte(mtd, (u8)(page_addr & 0xff)); + + if (command == NAND_CMD_READ0 || + command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB) { + /* + * NAND controller will release -CE after + * the last address byte is written, so we'll + * have to forcibly assert it. No interrupts + * are allowed while we do this as we don't + * want the NOR flash or PCMCIA drivers to + * steal our precious bytes of data... + */ + ce_override = 1; + local_irq_save(flags); + au1550_hwcontrol(mtd, NAND_CTL_SETNCE); + } + + ctx->write_byte(mtd, (u8)(page_addr >> 8)); + + if (this->options & NAND_ROW_ADDR_3) + ctx->write_byte(mtd, + ((page_addr >> 16) & 0x0f)); + } + /* Latch in address */ + au1550_hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * Program and erase have their own busy handlers. + * Status and sequential in need no delay. + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + break; + + case NAND_CMD_READ0: + case NAND_CMD_READ1: + case NAND_CMD_READOOB: + /* Check if we're really driving -CE low (just in case) */ + if (unlikely(!ce_override)) + break; + + /* Apply a short delay always to ensure that we do wait tWB. */ + ndelay(100); + /* Wait for a chip to become ready... */ + for (i = this->chip_delay; !this->dev_ready(mtd) && i > 0; --i) + udelay(1); + + /* Release -CE and re-enable interrupts. */ + au1550_hwcontrol(mtd, NAND_CTL_CLRNCE); + local_irq_restore(flags); + return; + } + /* Apply this short delay always to ensure that we do wait tWB. */ + ndelay(100); + + while(!this->dev_ready(mtd)); +} + +static int find_nand_cs(unsigned long nand_base) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR); + unsigned long addr, staddr, start, mask, end; + int i; + + for (i = 0; i < 4; i++) { + addr = 0x1000 + (i * 0x10); /* CSx */ + staddr = __raw_readl(base + addr + 0x08); /* STADDRx */ + /* figure out the decoded range of this CS */ + start = (staddr << 4) & 0xfffc0000; + mask = (staddr << 18) & 0xfffc0000; + end = (start | (start - 1)) & ~(start ^ mask); + if ((nand_base >= start) && (nand_base < end)) + return i; + } + + return -ENODEV; +} + +static int au1550nd_probe(struct platform_device *pdev) +{ + struct au1550nd_platdata *pd; + struct au1550nd_ctx *ctx; + struct nand_chip *this; + struct mtd_info *mtd; + struct resource *r; + int ret, cs; + + pd = dev_get_platdata(&pdev->dev); + if (!pd) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no NAND memory resource\n"); + ret = -ENODEV; + goto out1; + } + if (request_mem_region(r->start, resource_size(r), "au1550-nand")) { + dev_err(&pdev->dev, "cannot claim NAND memory area\n"); + ret = -ENOMEM; + goto out1; + } + + ctx->base = ioremap_nocache(r->start, 0x1000); + if (!ctx->base) { + dev_err(&pdev->dev, "cannot remap NAND memory area\n"); + ret = -ENODEV; + goto out2; + } + + this = &ctx->chip; + mtd = nand_to_mtd(this); + mtd->dev.parent = &pdev->dev; + + /* figure out which CS# r->start belongs to */ + cs = find_nand_cs(r->start); + if (cs < 0) { + dev_err(&pdev->dev, "cannot detect NAND chipselect\n"); + ret = -ENODEV; + goto out3; + } + ctx->cs = cs; + + this->dev_ready = au1550_device_ready; + this->select_chip = au1550_select_chip; + this->cmdfunc = au1550_command; + + /* 30 us command delay time */ + this->chip_delay = 30; + this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; + + if (pd->devwidth) + this->options |= NAND_BUSWIDTH_16; + + this->read_byte = (pd->devwidth) ? au_read_byte16 : au_read_byte; + ctx->write_byte = (pd->devwidth) ? au_write_byte16 : au_write_byte; + this->read_word = au_read_word; + this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf; + this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf; + + ret = nand_scan(mtd, 1); + if (ret) { + dev_err(&pdev->dev, "NAND scan failed with %d\n", ret); + goto out3; + } + + mtd_device_register(mtd, pd->parts, pd->num_parts); + + platform_set_drvdata(pdev, ctx); + + return 0; + +out3: + iounmap(ctx->base); +out2: + release_mem_region(r->start, resource_size(r)); +out1: + kfree(ctx); + return ret; +} + +static int au1550nd_remove(struct platform_device *pdev) +{ + struct au1550nd_ctx *ctx = platform_get_drvdata(pdev); + struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + nand_release(nand_to_mtd(&ctx->chip)); + iounmap(ctx->base); + release_mem_region(r->start, 0x1000); + kfree(ctx); + return 0; +} + +static struct platform_driver au1550nd_driver = { + .driver = { + .name = "au1550-nand", + }, + .probe = au1550nd_probe, + .remove = au1550nd_remove, +}; + +module_platform_driver(au1550nd_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Embedded Edge, LLC"); +MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board"); diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/Makefile b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile new file mode 100644 index 000000000000..f05b119e134b --- /dev/null +++ b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile @@ -0,0 +1,4 @@ +bcm47xxnflash-y += main.o +bcm47xxnflash-y += ops_bcm4706.o + +obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash.o diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h new file mode 100644 index 000000000000..201b9baa52a0 --- /dev/null +++ b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __BCM47XXNFLASH_H +#define __BCM47XXNFLASH_H + +#ifndef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + +#include +#include + +struct bcm47xxnflash { + struct bcma_drv_cc *cc; + + struct nand_chip nand_chip; + + unsigned curr_command; + int curr_page_addr; + int curr_column; + + u8 id_data[8]; +}; + +int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n); + +#endif /* BCM47XXNFLASH */ diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/main.c b/drivers/mtd/nand/raw/bcm47xxnflash/main.c new file mode 100644 index 000000000000..fb31429b70a9 --- /dev/null +++ b/drivers/mtd/nand/raw/bcm47xxnflash/main.c @@ -0,0 +1,81 @@ +/* + * BCM47XX NAND flash driver + * + * Copyright (C) 2012 Rafał Miłecki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "bcm47xxnflash.h" + +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("NAND flash driver for BCMA bus"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rafał Miłecki"); + +static const char *probes[] = { "bcm47xxpart", NULL }; + +static int bcm47xxnflash_probe(struct platform_device *pdev) +{ + struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev); + struct bcm47xxnflash *b47n; + struct mtd_info *mtd; + int err = 0; + + b47n = devm_kzalloc(&pdev->dev, sizeof(*b47n), GFP_KERNEL); + if (!b47n) + return -ENOMEM; + + nand_set_controller_data(&b47n->nand_chip, b47n); + mtd = nand_to_mtd(&b47n->nand_chip); + mtd->dev.parent = &pdev->dev; + b47n->cc = container_of(nflash, struct bcma_drv_cc, nflash); + + if (b47n->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { + err = bcm47xxnflash_ops_bcm4706_init(b47n); + } else { + pr_err("Device not supported\n"); + err = -ENOTSUPP; + } + if (err) { + pr_err("Initialization failed: %d\n", err); + return err; + } + + platform_set_drvdata(pdev, b47n); + + err = mtd_device_parse_register(mtd, probes, NULL, NULL, 0); + if (err) { + pr_err("Failed to register MTD device: %d\n", err); + return err; + } + + return 0; +} + +static int bcm47xxnflash_remove(struct platform_device *pdev) +{ + struct bcm47xxnflash *nflash = platform_get_drvdata(pdev); + + nand_release(nand_to_mtd(&nflash->nand_chip)); + + return 0; +} + +static struct platform_driver bcm47xxnflash_driver = { + .probe = bcm47xxnflash_probe, + .remove = bcm47xxnflash_remove, + .driver = { + .name = "bcma_nflash", + }, +}; + +module_platform_driver(bcm47xxnflash_driver); diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c new file mode 100644 index 000000000000..54bac5b73f0a --- /dev/null +++ b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c @@ -0,0 +1,456 @@ +/* + * BCM47XX NAND flash driver + * + * Copyright (C) 2012 Rafał Miłecki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "bcm47xxnflash.h" + +#include +#include +#include +#include +#include + +/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has + * shown ~1000 retries as maxiumum. */ +#define NFLASH_READY_RETRIES 10000 + +#define NFLASH_SECTOR_SIZE 512 + +#define NCTL_CMD0 0x00010000 +#define NCTL_COL 0x00020000 /* Update column with value from BCMA_CC_NFLASH_COL_ADDR */ +#define NCTL_ROW 0x00040000 /* Update row (page) with value from BCMA_CC_NFLASH_ROW_ADDR */ +#define NCTL_CMD1W 0x00080000 +#define NCTL_READ 0x00100000 +#define NCTL_WRITE 0x00200000 +#define NCTL_SPECADDR 0x01000000 +#define NCTL_READY 0x04000000 +#define NCTL_ERR 0x08000000 +#define NCTL_CSA 0x40000000 +#define NCTL_START 0x80000000 + +/************************************************** + * Various helpers + **************************************************/ + +static inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock) +{ + return ((ns * 1000 * clock) / 1000000) + 1; +} + +static int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code) +{ + int i = 0; + + bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, NCTL_START | code); + for (i = 0; i < NFLASH_READY_RETRIES; i++) { + if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_START)) { + i = 0; + break; + } + } + if (i) { + pr_err("NFLASH control command not ready!\n"); + return -EBUSY; + } + return 0; +} + +static int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc) +{ + int i; + + for (i = 0; i < NFLASH_READY_RETRIES; i++) { + if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_READY) { + if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & + BCMA_CC_NFLASH_CTL_ERR) { + pr_err("Error on polling\n"); + return -EBUSY; + } else { + return 0; + } + } + } + + pr_err("Polling timeout!\n"); + return -EBUSY; +} + +/************************************************** + * R/W + **************************************************/ + +static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf, + int len) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); + + u32 ctlcode; + u32 *dest = (u32 *)buf; + int i; + int toread; + + BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); + /* Don't validate column using nand_chip->page_shift, it may be bigger + * when accessing OOB */ + + while (len) { + /* We can read maximum of 0x200 bytes at once */ + toread = min(len, 0x200); + + /* Set page and column */ + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR, + b47n->curr_column); + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR, + b47n->curr_page_addr); + + /* Prepare to read */ + ctlcode = NCTL_CSA | NCTL_CMD1W | NCTL_ROW | NCTL_COL | + NCTL_CMD0; + ctlcode |= NAND_CMD_READSTART << 8; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) + return; + if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc)) + return; + + /* Eventually read some data :) */ + for (i = 0; i < toread; i += 4, dest++) { + ctlcode = NCTL_CSA | 0x30000000 | NCTL_READ; + if (i == toread - 4) /* Last read goes without that */ + ctlcode &= ~NCTL_CSA; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, + ctlcode)) + return; + *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA); + } + + b47n->curr_column += toread; + len -= toread; + } +} + +static void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); + struct bcma_drv_cc *cc = b47n->cc; + + u32 ctlcode; + const u32 *data = (u32 *)buf; + int i; + + BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); + /* Don't validate column using nand_chip->page_shift, it may be bigger + * when accessing OOB */ + + for (i = 0; i < len; i += 4, data++) { + bcma_cc_write32(cc, BCMA_CC_NFLASH_DATA, *data); + + ctlcode = NCTL_CSA | 0x30000000 | NCTL_WRITE; + if (i == len - 4) /* Last read goes without that */ + ctlcode &= ~NCTL_CSA; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) { + pr_err("%s ctl_cmd didn't work!\n", __func__); + return; + } + } + + b47n->curr_column += len; +} + +/************************************************** + * NAND chip ops + **************************************************/ + +static void bcm47xxnflash_ops_bcm4706_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); + u32 code = 0; + + if (cmd == NAND_CMD_NONE) + return; + + if (cmd & NAND_CTRL_CLE) + code = cmd | NCTL_CMD0; + + /* nCS is not needed for reset command */ + if (cmd != NAND_CMD_RESET) + code |= NCTL_CSA; + + bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, code); +} + +/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */ +static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd, + int chip) +{ + return; +} + +static int bcm47xxnflash_ops_bcm4706_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); + + return !!(bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_CTL) & NCTL_READY); +} + +/* + * Default nand_command and nand_command_lp don't match BCM4706 hardware layout. + * For example, reading chip id is performed in a non-standard way. + * Setting column and page is also handled differently, we use a special + * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert + * standard commands would be much more complicated. + */ +static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd, + unsigned command, int column, + int page_addr) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); + struct bcma_drv_cc *cc = b47n->cc; + u32 ctlcode; + int i; + + if (column != -1) + b47n->curr_column = column; + if (page_addr != -1) + b47n->curr_page_addr = page_addr; + + switch (command) { + case NAND_CMD_RESET: + nand_chip->cmd_ctrl(mtd, command, NAND_CTRL_CLE); + + ndelay(100); + nand_wait_ready(mtd); + break; + case NAND_CMD_READID: + ctlcode = NCTL_CSA | 0x01000000 | NCTL_CMD1W | NCTL_CMD0; + ctlcode |= NAND_CMD_READID; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) { + pr_err("READID error\n"); + break; + } + + /* + * Reading is specific, last one has to go without NCTL_CSA + * bit. We don't know how many reads NAND subsystem is going + * to perform, so cache everything. + */ + for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) { + ctlcode = NCTL_CSA | NCTL_READ; + if (i == ARRAY_SIZE(b47n->id_data) - 1) + ctlcode &= ~NCTL_CSA; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, + ctlcode)) { + pr_err("READID error\n"); + break; + } + b47n->id_data[i] = + bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA) + & 0xFF; + } + + break; + case NAND_CMD_STATUS: + ctlcode = NCTL_CSA | NCTL_CMD0 | NAND_CMD_STATUS; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) + pr_err("STATUS command error\n"); + break; + case NAND_CMD_READ0: + break; + case NAND_CMD_READOOB: + if (page_addr != -1) + b47n->curr_column += mtd->writesize; + break; + case NAND_CMD_ERASE1: + bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, + b47n->curr_page_addr); + ctlcode = NCTL_ROW | NCTL_CMD1W | NCTL_CMD0 | + NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8); + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) + pr_err("ERASE1 failed\n"); + break; + case NAND_CMD_ERASE2: + break; + case NAND_CMD_SEQIN: + /* Set page and column */ + bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR, + b47n->curr_column); + bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, + b47n->curr_page_addr); + + /* Prepare to write */ + ctlcode = 0x40000000 | NCTL_ROW | NCTL_COL | NCTL_CMD0; + ctlcode |= NAND_CMD_SEQIN; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) + pr_err("SEQIN failed\n"); + break; + case NAND_CMD_PAGEPROG: + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_CMD0 | + NAND_CMD_PAGEPROG)) + pr_err("PAGEPROG failed\n"); + if (bcm47xxnflash_ops_bcm4706_poll(cc)) + pr_err("PAGEPROG not ready\n"); + break; + default: + pr_err("Command 0x%X unsupported\n", command); + break; + } + b47n->curr_command = command; +} + +static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); + struct bcma_drv_cc *cc = b47n->cc; + u32 tmp = 0; + + switch (b47n->curr_command) { + case NAND_CMD_READID: + if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) { + pr_err("Requested invalid id_data: %d\n", + b47n->curr_column); + return 0; + } + return b47n->id_data[b47n->curr_column++]; + case NAND_CMD_STATUS: + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_READ)) + return 0; + return bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xff; + case NAND_CMD_READOOB: + bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4); + return tmp & 0xFF; + } + + pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command); + return 0; +} + +static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd, + uint8_t *buf, int len) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); + + switch (b47n->curr_command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + bcm47xxnflash_ops_bcm4706_read(mtd, buf, len); + return; + } + + pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command); +} + +static void bcm47xxnflash_ops_bcm4706_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); + + switch (b47n->curr_command) { + case NAND_CMD_SEQIN: + bcm47xxnflash_ops_bcm4706_write(mtd, buf, len); + return; + } + + pr_err("Invalid command for buf write: 0x%X\n", b47n->curr_command); +} + +/************************************************** + * Init + **************************************************/ + +int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) +{ + struct nand_chip *nand_chip = (struct nand_chip *)&b47n->nand_chip; + int err; + u32 freq; + u16 clock; + u8 w0, w1, w2, w3, w4; + + unsigned long chipsize; /* MiB */ + u8 tbits, col_bits, col_size, row_bits, row_bsize; + u32 val; + + b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; + nand_chip->cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl; + nand_chip->dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready; + b47n->nand_chip.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc; + b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte; + b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf; + b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf; + b47n->nand_chip.onfi_set_features = nand_onfi_get_set_features_notsupp; + b47n->nand_chip.onfi_get_features = nand_onfi_get_set_features_notsupp; + + nand_chip->chip_delay = 50; + b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; + b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */ + + /* Enable NAND flash access */ + bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG, + BCMA_CC_4706_FLASHSCFG_NF1); + + /* Configure wait counters */ + if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) { + /* 400 MHz */ + freq = 400000000 / 4; + } else { + freq = bcma_chipco_pll_read(b47n->cc, 4); + freq = (freq & 0xFFF) >> 3; + /* Fixed reference clock 25 MHz and m = 2 */ + freq = (freq * 25000000 / 2) / 4; + } + clock = freq / 1000000; + w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock); + w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock); + w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); + w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); + w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock); + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0, + (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0)); + + /* Scan NAND */ + err = nand_scan(nand_to_mtd(&b47n->nand_chip), 1); + if (err) { + pr_err("Could not scan NAND flash: %d\n", err); + goto exit; + } + + /* Configure FLASH */ + chipsize = b47n->nand_chip.chipsize >> 20; + tbits = ffs(chipsize); /* find first bit set */ + if (!tbits || tbits != fls(chipsize)) { + pr_err("Invalid flash size: 0x%lX\n", chipsize); + err = -ENOTSUPP; + goto exit; + } + tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */ + + col_bits = b47n->nand_chip.page_shift + 1; + col_size = (col_bits + 7) / 8; + + row_bits = tbits - col_bits + 1; + row_bsize = (row_bits + 7) / 8; + + val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2; + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val); + +exit: + if (err) + bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG, + ~BCMA_CC_4706_FLASHSCFG_NF1); + return err; +} diff --git a/drivers/mtd/nand/raw/bf5xx_nand.c b/drivers/mtd/nand/raw/bf5xx_nand.c new file mode 100644 index 000000000000..9a1d8d104570 --- /dev/null +++ b/drivers/mtd/nand/raw/bf5xx_nand.c @@ -0,0 +1,861 @@ +/* + * Copyright 2006-2008 Analog Devices Inc. + * http://blackfin.uclinux.org/ + * Bryan Wu + * + * Blackfin BF5xx on-chip NAND flash controller driver + * + * Derived from s3c2410.c + * Copyright (c) 2007 Ben Dooks + * + * Derived from cafe.c + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2006 David Woodhouse + * + * Changelog: + * 12-Jun-2007 Bryan Wu: Initial version + * 18-Jul-2007 Bryan Wu: + * - ECC_HW and ECC_SW supported + * - DMA supported in ECC_HW + * - YAFFS tested as rootfs in both ECC_HW and ECC_SW + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRV_NAME "bf5xx-nand" +#define DRV_VERSION "1.2" +#define DRV_AUTHOR "Bryan Wu " +#define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver" + +/* NFC_STAT Masks */ +#define NBUSY 0x01 /* Not Busy */ +#define WB_FULL 0x02 /* Write Buffer Full */ +#define PG_WR_STAT 0x04 /* Page Write Pending */ +#define PG_RD_STAT 0x08 /* Page Read Pending */ +#define WB_EMPTY 0x10 /* Write Buffer Empty */ + +/* NFC_IRQSTAT Masks */ +#define NBUSYIRQ 0x01 /* Not Busy IRQ */ +#define WB_OVF 0x02 /* Write Buffer Overflow */ +#define WB_EDGE 0x04 /* Write Buffer Edge Detect */ +#define RD_RDY 0x08 /* Read Data Ready */ +#define WR_DONE 0x10 /* Page Write Done */ + +/* NFC_RST Masks */ +#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */ + +/* NFC_PGCTL Masks */ +#define PG_RD_START 0x01 /* Page Read Start */ +#define PG_WR_START 0x02 /* Page Write Start */ + +#ifdef CONFIG_MTD_NAND_BF5XX_HWECC +static int hardware_ecc = 1; +#else +static int hardware_ecc; +#endif + +static const unsigned short bfin_nfc_pin_req[] = + {P_NAND_CE, + P_NAND_RB, + P_NAND_D0, + P_NAND_D1, + P_NAND_D2, + P_NAND_D3, + P_NAND_D4, + P_NAND_D5, + P_NAND_D6, + P_NAND_D7, + P_NAND_WE, + P_NAND_RE, + P_NAND_CLE, + P_NAND_ALE, + 0}; + +#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC +static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = section * 8; + oobregion->length = 3; + + return 0; +} + +static int bootrom_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = (section * 8) + 3; + oobregion->length = 5; + + return 0; +} + +static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = { + .ecc = bootrom_ooblayout_ecc, + .free = bootrom_ooblayout_free, +}; +#endif + +/* + * Data structures for bf5xx nand flash controller driver + */ + +/* bf5xx nand info */ +struct bf5xx_nand_info { + /* mtd info */ + struct nand_hw_control controller; + struct nand_chip chip; + + /* platform info */ + struct bf5xx_nand_platform *platform; + + /* device info */ + struct device *device; + + /* DMA stuff */ + struct completion dma_completion; +}; + +/* + * Conversion functions + */ +static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct bf5xx_nand_info, + chip); +} + +static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev) +{ + return platform_get_drvdata(pdev); +} + +static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev) +{ + return dev_get_platdata(&pdev->dev); +} + +/* + * struct nand_chip interface function pointers + */ + +/* + * bf5xx_nand_hwcontrol + * + * Issue command and address cycles to the chip + */ +static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + if (cmd == NAND_CMD_NONE) + return; + + while (bfin_read_NFC_STAT() & WB_FULL) + cpu_relax(); + + if (ctrl & NAND_CLE) + bfin_write_NFC_CMD(cmd); + else if (ctrl & NAND_ALE) + bfin_write_NFC_ADDR(cmd); + SSYNC(); +} + +/* + * bf5xx_nand_devready() + * + * returns 0 if the nand is busy, 1 if it is ready + */ +static int bf5xx_nand_devready(struct mtd_info *mtd) +{ + unsigned short val = bfin_read_NFC_STAT(); + + if ((val & NBUSY) == NBUSY) + return 1; + else + return 0; +} + +/* + * ECC functions + * These allow the bf5xx to use the controller's ECC + * generator block to ECC the data as it passes through + */ + +/* + * ECC error correction function + */ +static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + u32 syndrome[5]; + u32 calced, stored; + int i; + unsigned short failing_bit, failing_byte; + u_char data; + + calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16); + stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16); + + syndrome[0] = (calced ^ stored); + + /* + * syndrome 0: all zero + * No error in data + * No action + */ + if (!syndrome[0] || !calced || !stored) + return 0; + + /* + * sysdrome 0: only one bit is one + * ECC data was incorrect + * No action + */ + if (hweight32(syndrome[0]) == 1) { + dev_err(info->device, "ECC data was incorrect!\n"); + return -EBADMSG; + } + + syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF); + syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF); + syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF); + syndrome[4] = syndrome[2] ^ syndrome[3]; + + for (i = 0; i < 5; i++) + dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]); + + dev_info(info->device, + "calced[0x%08x], stored[0x%08x]\n", + calced, stored); + + /* + * sysdrome 0: exactly 11 bits are one, each parity + * and parity' pair is 1 & 0 or 0 & 1. + * 1-bit correctable error + * Correct the error + */ + if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) { + dev_info(info->device, + "1-bit correctable error, correct it.\n"); + dev_info(info->device, + "syndrome[1] 0x%08x\n", syndrome[1]); + + failing_bit = syndrome[1] & 0x7; + failing_byte = syndrome[1] >> 0x3; + data = *(dat + failing_byte); + data = data ^ (0x1 << failing_bit); + *(dat + failing_byte) = data; + + return 1; + } + + /* + * sysdrome 0: random data + * More than 1-bit error, non-correctable error + * Discard data, mark bad block + */ + dev_err(info->device, + "More than 1-bit error, non-correctable error.\n"); + dev_err(info->device, + "Please discard data, mark bad block\n"); + + return -EBADMSG; +} + +static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret, bitflips = 0; + + ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); + if (ret < 0) + return ret; + + bitflips = ret; + + /* If ecc size is 512, correct second 256 bytes */ + if (chip->ecc.size == 512) { + dat += 256; + read_ecc += 3; + calc_ecc += 3; + ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); + if (ret < 0) + return ret; + + bitflips += ret; + } + + return bitflips; +} + +static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + return; +} + +static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + u16 ecc0, ecc1; + u32 code[2]; + u8 *p; + + /* first 3 bytes ECC code for 256 page size */ + ecc0 = bfin_read_NFC_ECC0(); + ecc1 = bfin_read_NFC_ECC1(); + + code[0] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11); + + dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]); + + p = (u8 *) code; + memcpy(ecc_code, p, 3); + + /* second 3 bytes ECC code for 512 ecc size */ + if (chip->ecc.size == 512) { + ecc0 = bfin_read_NFC_ECC2(); + ecc1 = bfin_read_NFC_ECC3(); + code[1] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11); + + /* second 3 bytes in ecc_code for second 256 + * bytes of 512 page size + */ + p = (u8 *) (code + 1); + memcpy((ecc_code + 3), p, 3); + dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]); + } + + return 0; +} + +/* + * PIO mode for buffer writing and reading + */ +static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + unsigned short val; + + /* + * Data reads are requested by first writing to NFC_DATA_RD + * and then reading back from NFC_READ. + */ + for (i = 0; i < len; i++) { + while (bfin_read_NFC_STAT() & WB_FULL) + cpu_relax(); + + /* Contents do not matter */ + bfin_write_NFC_DATA_RD(0x0000); + SSYNC(); + + while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY) + cpu_relax(); + + buf[i] = bfin_read_NFC_READ(); + + val = bfin_read_NFC_IRQSTAT(); + val |= RD_RDY; + bfin_write_NFC_IRQSTAT(val); + SSYNC(); + } +} + +static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd) +{ + uint8_t val; + + bf5xx_nand_read_buf(mtd, &val, 1); + + return val; +} + +static void bf5xx_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + while (bfin_read_NFC_STAT() & WB_FULL) + cpu_relax(); + + bfin_write_NFC_DATA_WR(buf[i]); + SSYNC(); + } +} + +static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + u16 *p = (u16 *) buf; + len >>= 1; + + /* + * Data reads are requested by first writing to NFC_DATA_RD + * and then reading back from NFC_READ. + */ + bfin_write_NFC_DATA_RD(0x5555); + + SSYNC(); + + for (i = 0; i < len; i++) + p[i] = bfin_read_NFC_READ(); +} + +static void bf5xx_nand_write_buf16(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + int i; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + bfin_write_NFC_DATA_WR(p[i]); + + SSYNC(); +} + +/* + * DMA functions for buffer writing and reading + */ +static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id) +{ + struct bf5xx_nand_info *info = dev_id; + + clear_dma_irqstat(CH_NFC); + disable_dma(CH_NFC); + complete(&info->dma_completion); + + return IRQ_HANDLED; +} + +static void bf5xx_nand_dma_rw(struct mtd_info *mtd, + uint8_t *buf, int is_read) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + unsigned short val; + + dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n", + mtd, buf, is_read); + + /* + * Before starting a dma transfer, be sure to invalidate/flush + * the cache over the address range of your DMA buffer to + * prevent cache coherency problems. Otherwise very subtle bugs + * can be introduced to your driver. + */ + if (is_read) + invalidate_dcache_range((unsigned int)buf, + (unsigned int)(buf + chip->ecc.size)); + else + flush_dcache_range((unsigned int)buf, + (unsigned int)(buf + chip->ecc.size)); + + /* + * This register must be written before each page is + * transferred to generate the correct ECC register + * values. + */ + bfin_write_NFC_RST(ECC_RST); + SSYNC(); + while (bfin_read_NFC_RST() & ECC_RST) + cpu_relax(); + + disable_dma(CH_NFC); + clear_dma_irqstat(CH_NFC); + + /* setup DMA register with Blackfin DMA API */ + set_dma_config(CH_NFC, 0x0); + set_dma_start_addr(CH_NFC, (unsigned long) buf); + + /* The DMAs have different size on BF52x and BF54x */ +#ifdef CONFIG_BF52x + set_dma_x_count(CH_NFC, (chip->ecc.size >> 1)); + set_dma_x_modify(CH_NFC, 2); + val = DI_EN | WDSIZE_16; +#endif + +#ifdef CONFIG_BF54x + set_dma_x_count(CH_NFC, (chip->ecc.size >> 2)); + set_dma_x_modify(CH_NFC, 4); + val = DI_EN | WDSIZE_32; +#endif + /* setup write or read operation */ + if (is_read) + val |= WNR; + set_dma_config(CH_NFC, val); + enable_dma(CH_NFC); + + /* Start PAGE read/write operation */ + if (is_read) + bfin_write_NFC_PGCTL(PG_RD_START); + else + bfin_write_NFC_PGCTL(PG_WR_START); + wait_for_completion(&info->dma_completion); +} + +static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, + uint8_t *buf, int len) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len); + + if (len == chip->ecc.size) + bf5xx_nand_dma_rw(mtd, buf, 1); + else + bf5xx_nand_read_buf(mtd, buf, len); +} + +static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len); + + if (len == chip->ecc.size) + bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0); + else + bf5xx_nand_write_buf(mtd, buf, len); +} + +static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + nand_read_page_op(chip, page, 0, NULL, 0); + + bf5xx_nand_read_buf(mtd, buf, mtd->writesize); + bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static int bf5xx_nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) +{ + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +/* + * System initialization functions + */ +static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) +{ + int ret; + + /* Do not use dma */ + if (!hardware_ecc) + return 0; + + init_completion(&info->dma_completion); + + /* Request NFC DMA channel */ + ret = request_dma(CH_NFC, "BF5XX NFC driver"); + if (ret < 0) { + dev_err(info->device, " unable to get DMA channel\n"); + return ret; + } + +#ifdef CONFIG_BF54x + /* Setup DMAC1 channel mux for NFC which shared with SDH */ + bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1); + SSYNC(); +#endif + + set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info); + + /* Turn off the DMA channel first */ + disable_dma(CH_NFC); + return 0; +} + +static void bf5xx_nand_dma_remove(struct bf5xx_nand_info *info) +{ + /* Free NFC DMA channel */ + if (hardware_ecc) + free_dma(CH_NFC); +} + +/* + * BF5XX NFC hardware initialization + * - pin mux setup + * - clear interrupt status + */ +static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) +{ + int err = 0; + unsigned short val; + struct bf5xx_nand_platform *plat = info->platform; + + /* setup NFC_CTL register */ + dev_info(info->device, + "data_width=%d, wr_dly=%d, rd_dly=%d\n", + (plat->data_width ? 16 : 8), + plat->wr_dly, plat->rd_dly); + + val = (1 << NFC_PG_SIZE_OFFSET) | + (plat->data_width << NFC_NWIDTH_OFFSET) | + (plat->rd_dly << NFC_RDDLY_OFFSET) | + (plat->wr_dly << NFC_WRDLY_OFFSET); + dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val); + + bfin_write_NFC_CTL(val); + SSYNC(); + + /* clear interrupt status */ + bfin_write_NFC_IRQMASK(0x0); + SSYNC(); + val = bfin_read_NFC_IRQSTAT(); + bfin_write_NFC_IRQSTAT(val); + SSYNC(); + + /* DMA initialization */ + if (bf5xx_nand_dma_init(info)) + err = -ENXIO; + + return err; +} + +/* + * Device management interface + */ +static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info) +{ + struct mtd_info *mtd = nand_to_mtd(&info->chip); + struct mtd_partition *parts = info->platform->partitions; + int nr = info->platform->nr_partitions; + + return mtd_device_register(mtd, parts, nr); +} + +static int bf5xx_nand_remove(struct platform_device *pdev) +{ + struct bf5xx_nand_info *info = to_nand_info(pdev); + + /* first thing we need to do is release all our mtds + * and their partitions, then go through freeing the + * resources used + */ + nand_release(nand_to_mtd(&info->chip)); + + peripheral_free_list(bfin_nfc_pin_req); + bf5xx_nand_dma_remove(info); + + return 0; +} + +static int bf5xx_nand_scan(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret; + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + if (hardware_ecc) { + /* + * for nand with page size > 512B, think it as several sections with 512B + */ + if (likely(mtd->writesize >= 512)) { + chip->ecc.size = 512; + chip->ecc.bytes = 6; + chip->ecc.strength = 2; + } else { + chip->ecc.size = 256; + chip->ecc.bytes = 3; + chip->ecc.strength = 1; + bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET)); + SSYNC(); + } + } + + return nand_scan_tail(mtd); +} + +/* + * bf5xx_nand_probe + * + * called by device layer when it finds a device matching + * one our driver can handled. This code checks to see if + * it can allocate all necessary resources then calls the + * nand layer to look for devices + */ +static int bf5xx_nand_probe(struct platform_device *pdev) +{ + struct bf5xx_nand_platform *plat = to_nand_plat(pdev); + struct bf5xx_nand_info *info = NULL; + struct nand_chip *chip = NULL; + struct mtd_info *mtd = NULL; + int err = 0; + + dev_dbg(&pdev->dev, "(%p)\n", pdev); + + if (!plat) { + dev_err(&pdev->dev, "no platform specific information\n"); + return -EINVAL; + } + + if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) { + dev_err(&pdev->dev, "requesting Peripherals failed\n"); + return -EFAULT; + } + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto out_err; + } + + platform_set_drvdata(pdev, info); + + nand_hw_control_init(&info->controller); + + info->device = &pdev->dev; + info->platform = plat; + + /* initialise chip data struct */ + chip = &info->chip; + mtd = nand_to_mtd(&info->chip); + + if (plat->data_width) + chip->options |= NAND_BUSWIDTH_16; + + chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN; + + chip->read_buf = (plat->data_width) ? + bf5xx_nand_read_buf16 : bf5xx_nand_read_buf; + chip->write_buf = (plat->data_width) ? + bf5xx_nand_write_buf16 : bf5xx_nand_write_buf; + + chip->read_byte = bf5xx_nand_read_byte; + + chip->cmd_ctrl = bf5xx_nand_hwcontrol; + chip->dev_ready = bf5xx_nand_devready; + + nand_set_controller_data(chip, mtd); + chip->controller = &info->controller; + + chip->IO_ADDR_R = (void __iomem *) NFC_READ; + chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR; + + chip->chip_delay = 0; + + /* initialise mtd info data struct */ + mtd->dev.parent = &pdev->dev; + + /* initialise the hardware */ + err = bf5xx_nand_hw_init(info); + if (err) + goto out_err; + + /* setup hardware ECC data struct */ + if (hardware_ecc) { +#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC + mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops); +#endif + chip->read_buf = bf5xx_nand_dma_read_buf; + chip->write_buf = bf5xx_nand_dma_write_buf; + chip->ecc.calculate = bf5xx_nand_calculate_ecc; + chip->ecc.correct = bf5xx_nand_correct_data; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.hwctl = bf5xx_nand_enable_hwecc; + chip->ecc.read_page_raw = bf5xx_nand_read_page_raw; + chip->ecc.write_page_raw = bf5xx_nand_write_page_raw; + } else { + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + } + + /* scan hardware nand chip and setup mtd info data struct */ + if (bf5xx_nand_scan(mtd)) { + err = -ENXIO; + goto out_err_nand_scan; + } + +#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC + chip->badblockpos = 63; +#endif + + /* add NAND partition */ + bf5xx_nand_add_partition(info); + + dev_dbg(&pdev->dev, "initialised ok\n"); + return 0; + +out_err_nand_scan: + bf5xx_nand_dma_remove(info); +out_err: + peripheral_free_list(bfin_nfc_pin_req); + + return err; +} + +/* driver device registration */ +static struct platform_driver bf5xx_nand_driver = { + .probe = bf5xx_nand_probe, + .remove = bf5xx_nand_remove, + .driver = { + .name = DRV_NAME, + }, +}; + +module_platform_driver(bf5xx_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRV_AUTHOR); +MODULE_DESCRIPTION(DRV_DESC); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/mtd/nand/raw/brcmnand/Makefile b/drivers/mtd/nand/raw/brcmnand/Makefile new file mode 100644 index 000000000000..195b845e48b8 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# link order matters; don't link the more generic brcmstb_nand.o before the +# more specific iproc_nand.o, for instance +obj-$(CONFIG_MTD_NAND_BRCMNAND) += iproc_nand.o +obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63138_nand.o +obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm6368_nand.o +obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o +obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o diff --git a/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c new file mode 100644 index 000000000000..59444b3a697d --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c @@ -0,0 +1,109 @@ +/* + * Copyright © 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "brcmnand.h" + +struct bcm63138_nand_soc { + struct brcmnand_soc soc; + void __iomem *base; +}; + +#define BCM63138_NAND_INT_STATUS 0x00 +#define BCM63138_NAND_INT_EN 0x04 + +enum { + BCM63138_CTLRDY = BIT(4), +}; + +static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc) +{ + struct bcm63138_nand_soc *priv = + container_of(soc, struct bcm63138_nand_soc, soc); + void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS; + u32 val = brcmnand_readl(mmio); + + if (val & BCM63138_CTLRDY) { + brcmnand_writel(val & ~BCM63138_CTLRDY, mmio); + return true; + } + + return false; +} + +static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en) +{ + struct bcm63138_nand_soc *priv = + container_of(soc, struct bcm63138_nand_soc, soc); + void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN; + u32 val = brcmnand_readl(mmio); + + if (en) + val |= BCM63138_CTLRDY; + else + val &= ~BCM63138_CTLRDY; + + brcmnand_writel(val, mmio); +} + +static int bcm63138_nand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm63138_nand_soc *priv; + struct brcmnand_soc *soc; + struct resource *res; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + soc = &priv->soc; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base"); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + soc->ctlrdy_ack = bcm63138_nand_intc_ack; + soc->ctlrdy_set_enabled = bcm63138_nand_intc_set; + + return brcmnand_probe(pdev, soc); +} + +static const struct of_device_id bcm63138_nand_of_match[] = { + { .compatible = "brcm,nand-bcm63138" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match); + +static struct platform_driver bcm63138_nand_driver = { + .probe = bcm63138_nand_probe, + .remove = brcmnand_remove, + .driver = { + .name = "bcm63138_nand", + .pm = &brcmnand_pm_ops, + .of_match_table = bcm63138_nand_of_match, + } +}; +module_platform_driver(bcm63138_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("NAND driver for BCM63138"); diff --git a/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c new file mode 100644 index 000000000000..34c91b0e1e69 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c @@ -0,0 +1,142 @@ +/* + * Copyright 2015 Simon Arlott + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Derived from bcm63138_nand.c: + * Copyright © 2015 Broadcom Corporation + * + * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/include/bcm963xx/63268_map_part.h: + * Copyright 2000-2010 Broadcom Corporation + * + * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/flash/nandflash.c: + * Copyright 2000-2010 Broadcom Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "brcmnand.h" + +struct bcm6368_nand_soc { + struct brcmnand_soc soc; + void __iomem *base; +}; + +#define BCM6368_NAND_INT 0x00 +#define BCM6368_NAND_STATUS_SHIFT 0 +#define BCM6368_NAND_STATUS_MASK (0xfff << BCM6368_NAND_STATUS_SHIFT) +#define BCM6368_NAND_ENABLE_SHIFT 16 +#define BCM6368_NAND_ENABLE_MASK (0xffff << BCM6368_NAND_ENABLE_SHIFT) +#define BCM6368_NAND_BASE_ADDR0 0x04 +#define BCM6368_NAND_BASE_ADDR1 0x0c + +enum { + BCM6368_NP_READ = BIT(0), + BCM6368_BLOCK_ERASE = BIT(1), + BCM6368_COPY_BACK = BIT(2), + BCM6368_PAGE_PGM = BIT(3), + BCM6368_CTRL_READY = BIT(4), + BCM6368_DEV_RBPIN = BIT(5), + BCM6368_ECC_ERR_UNC = BIT(6), + BCM6368_ECC_ERR_CORR = BIT(7), +}; + +static bool bcm6368_nand_intc_ack(struct brcmnand_soc *soc) +{ + struct bcm6368_nand_soc *priv = + container_of(soc, struct bcm6368_nand_soc, soc); + void __iomem *mmio = priv->base + BCM6368_NAND_INT; + u32 val = brcmnand_readl(mmio); + + if (val & (BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT)) { + /* Ack interrupt */ + val &= ~BCM6368_NAND_STATUS_MASK; + val |= BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT; + brcmnand_writel(val, mmio); + return true; + } + + return false; +} + +static void bcm6368_nand_intc_set(struct brcmnand_soc *soc, bool en) +{ + struct bcm6368_nand_soc *priv = + container_of(soc, struct bcm6368_nand_soc, soc); + void __iomem *mmio = priv->base + BCM6368_NAND_INT; + u32 val = brcmnand_readl(mmio); + + /* Don't ack any interrupts */ + val &= ~BCM6368_NAND_STATUS_MASK; + + if (en) + val |= BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT; + else + val &= ~(BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT); + + brcmnand_writel(val, mmio); +} + +static int bcm6368_nand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm6368_nand_soc *priv; + struct brcmnand_soc *soc; + struct resource *res; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + soc = &priv->soc; + + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "nand-int-base"); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + soc->ctlrdy_ack = bcm6368_nand_intc_ack; + soc->ctlrdy_set_enabled = bcm6368_nand_intc_set; + + /* Disable and ack all interrupts */ + brcmnand_writel(0, priv->base + BCM6368_NAND_INT); + brcmnand_writel(BCM6368_NAND_STATUS_MASK, + priv->base + BCM6368_NAND_INT); + + return brcmnand_probe(pdev, soc); +} + +static const struct of_device_id bcm6368_nand_of_match[] = { + { .compatible = "brcm,nand-bcm6368" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm6368_nand_of_match); + +static struct platform_driver bcm6368_nand_driver = { + .probe = bcm6368_nand_probe, + .remove = brcmnand_remove, + .driver = { + .name = "bcm6368_nand", + .pm = &brcmnand_pm_ops, + .of_match_table = bcm6368_nand_of_match, + } +}; +module_platform_driver(bcm6368_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Arlott"); +MODULE_DESCRIPTION("NAND driver for BCM6368"); diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c new file mode 100644 index 000000000000..c28fd2bc1a84 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -0,0 +1,2620 @@ +/* + * Copyright © 2010-2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "brcmnand.h" + +/* + * This flag controls if WP stays on between erase/write commands to mitigate + * flash corruption due to power glitches. Values: + * 0: NAND_WP is not used or not available + * 1: NAND_WP is set by default, cleared for erase/write operations + * 2: NAND_WP is always cleared + */ +static int wp_on = 1; +module_param(wp_on, int, 0444); + +/*********************************************************************** + * Definitions + ***********************************************************************/ + +#define DRV_NAME "brcmnand" + +#define CMD_NULL 0x00 +#define CMD_PAGE_READ 0x01 +#define CMD_SPARE_AREA_READ 0x02 +#define CMD_STATUS_READ 0x03 +#define CMD_PROGRAM_PAGE 0x04 +#define CMD_PROGRAM_SPARE_AREA 0x05 +#define CMD_COPY_BACK 0x06 +#define CMD_DEVICE_ID_READ 0x07 +#define CMD_BLOCK_ERASE 0x08 +#define CMD_FLASH_RESET 0x09 +#define CMD_BLOCKS_LOCK 0x0a +#define CMD_BLOCKS_LOCK_DOWN 0x0b +#define CMD_BLOCKS_UNLOCK 0x0c +#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d +#define CMD_PARAMETER_READ 0x0e +#define CMD_PARAMETER_CHANGE_COL 0x0f +#define CMD_LOW_LEVEL_OP 0x10 + +struct brcm_nand_dma_desc { + u32 next_desc; + u32 next_desc_ext; + u32 cmd_irq; + u32 dram_addr; + u32 dram_addr_ext; + u32 tfr_len; + u32 total_len; + u32 flash_addr; + u32 flash_addr_ext; + u32 cs; + u32 pad2[5]; + u32 status_valid; +} __packed; + +/* Bitfields for brcm_nand_dma_desc::status_valid */ +#define FLASH_DMA_ECC_ERROR (1 << 8) +#define FLASH_DMA_CORR_ERROR (1 << 9) + +/* 512B flash cache in the NAND controller HW */ +#define FC_SHIFT 9U +#define FC_BYTES 512U +#define FC_WORDS (FC_BYTES >> 2) + +#define BRCMNAND_MIN_PAGESIZE 512 +#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024) +#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024) + +#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY) +#define NAND_POLL_STATUS_TIMEOUT_MS 100 + +/* Controller feature flags */ +enum { + BRCMNAND_HAS_1K_SECTORS = BIT(0), + BRCMNAND_HAS_PREFETCH = BIT(1), + BRCMNAND_HAS_CACHE_MODE = BIT(2), + BRCMNAND_HAS_WP = BIT(3), +}; + +struct brcmnand_controller { + struct device *dev; + struct nand_hw_control controller; + void __iomem *nand_base; + void __iomem *nand_fc; /* flash cache */ + void __iomem *flash_dma_base; + unsigned int irq; + unsigned int dma_irq; + int nand_version; + + /* Some SoCs provide custom interrupt status register(s) */ + struct brcmnand_soc *soc; + + /* Some SoCs have a gateable clock for the controller */ + struct clk *clk; + + int cmd_pending; + bool dma_pending; + struct completion done; + struct completion dma_done; + + /* List of NAND hosts (one for each chip-select) */ + struct list_head host_list; + + struct brcm_nand_dma_desc *dma_desc; + dma_addr_t dma_pa; + + /* in-memory cache of the FLASH_CACHE, used only for some commands */ + u8 flash_cache[FC_BYTES]; + + /* Controller revision details */ + const u16 *reg_offsets; + unsigned int reg_spacing; /* between CS1, CS2, ... regs */ + const u8 *cs_offsets; /* within each chip-select */ + const u8 *cs0_offsets; /* within CS0, if different */ + unsigned int max_block_size; + const unsigned int *block_sizes; + unsigned int max_page_size; + const unsigned int *page_sizes; + unsigned int max_oob; + u32 features; + + /* for low-power standby/resume only */ + u32 nand_cs_nand_select; + u32 nand_cs_nand_xor; + u32 corr_stat_threshold; + u32 flash_dma_mode; +}; + +struct brcmnand_cfg { + u64 device_size; + unsigned int block_size; + unsigned int page_size; + unsigned int spare_area_size; + unsigned int device_width; + unsigned int col_adr_bytes; + unsigned int blk_adr_bytes; + unsigned int ful_adr_bytes; + unsigned int sector_size_1k; + unsigned int ecc_level; + /* use for low-power standby/resume only */ + u32 acc_control; + u32 config; + u32 config_ext; + u32 timing_1; + u32 timing_2; +}; + +struct brcmnand_host { + struct list_head node; + + struct nand_chip chip; + struct platform_device *pdev; + int cs; + + unsigned int last_cmd; + unsigned int last_byte; + u64 last_addr; + struct brcmnand_cfg hwcfg; + struct brcmnand_controller *ctrl; +}; + +enum brcmnand_reg { + BRCMNAND_CMD_START = 0, + BRCMNAND_CMD_EXT_ADDRESS, + BRCMNAND_CMD_ADDRESS, + BRCMNAND_INTFC_STATUS, + BRCMNAND_CS_SELECT, + BRCMNAND_CS_XOR, + BRCMNAND_LL_OP, + BRCMNAND_CS0_BASE, + BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */ + BRCMNAND_CORR_THRESHOLD, + BRCMNAND_CORR_THRESHOLD_EXT, + BRCMNAND_UNCORR_COUNT, + BRCMNAND_CORR_COUNT, + BRCMNAND_CORR_EXT_ADDR, + BRCMNAND_CORR_ADDR, + BRCMNAND_UNCORR_EXT_ADDR, + BRCMNAND_UNCORR_ADDR, + BRCMNAND_SEMAPHORE, + BRCMNAND_ID, + BRCMNAND_ID_EXT, + BRCMNAND_LL_RDATA, + BRCMNAND_OOB_READ_BASE, + BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_OOB_WRITE_BASE, + BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_FC_BASE, +}; + +/* BRCMNAND v4.0 */ +static const u16 brcmnand_regs_v40[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v5.0 */ +static const u16 brcmnand_regs_v50[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0x140, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v6.0 - v7.1 */ +static const u16 brcmnand_regs_v60[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xc0, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x280, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x400, +}; + +/* BRCMNAND v7.1 */ +static const u16 brcmnand_regs_v71[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xdc, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x280, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x400, +}; + +/* BRCMNAND v7.2 */ +static const u16 brcmnand_regs_v72[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xdc, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x400, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x600, +}; + +enum brcmnand_cs_reg { + BRCMNAND_CS_CFG_EXT = 0, + BRCMNAND_CS_CFG, + BRCMNAND_CS_ACC_CONTROL, + BRCMNAND_CS_TIMING1, + BRCMNAND_CS_TIMING2, +}; + +/* Per chip-select offsets for v7.1 */ +static const u8 brcmnand_cs_offsets_v71[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x0c, + [BRCMNAND_CS_TIMING2] = 0x10, +}; + +/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */ +static const u8 brcmnand_cs_offsets[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x04, + [BRCMNAND_CS_TIMING1] = 0x08, + [BRCMNAND_CS_TIMING2] = 0x0c, +}; + +/* Per chip-select offset for <= v5.0 on CS0 only */ +static const u8 brcmnand_cs_offsets_cs0[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x08, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x10, + [BRCMNAND_CS_TIMING2] = 0x14, +}; + +/* + * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had + * one config register, but once the bitfields overflowed, newer controllers + * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around. + */ +enum { + CFG_BLK_ADR_BYTES_SHIFT = 8, + CFG_COL_ADR_BYTES_SHIFT = 12, + CFG_FUL_ADR_BYTES_SHIFT = 16, + CFG_BUS_WIDTH_SHIFT = 23, + CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT), + CFG_DEVICE_SIZE_SHIFT = 24, + + /* Only for pre-v7.1 (with no CFG_EXT register) */ + CFG_PAGE_SIZE_SHIFT = 20, + CFG_BLK_SIZE_SHIFT = 28, + + /* Only for v7.1+ (with CFG_EXT register) */ + CFG_EXT_PAGE_SIZE_SHIFT = 0, + CFG_EXT_BLK_SIZE_SHIFT = 4, +}; + +/* BRCMNAND_INTFC_STATUS */ +enum { + INTFC_FLASH_STATUS = GENMASK(7, 0), + + INTFC_ERASED = BIT(27), + INTFC_OOB_VALID = BIT(28), + INTFC_CACHE_VALID = BIT(29), + INTFC_FLASH_READY = BIT(30), + INTFC_CTLR_READY = BIT(31), +}; + +static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) +{ + return brcmnand_readl(ctrl->nand_base + offs); +} + +static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs, + u32 val) +{ + brcmnand_writel(val, ctrl->nand_base + offs); +} + +static int brcmnand_revision_init(struct brcmnand_controller *ctrl) +{ + static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 }; + static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 }; + static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 }; + + ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff; + + /* Only support v4.0+? */ + if (ctrl->nand_version < 0x0400) { + dev_err(ctrl->dev, "version %#x not supported\n", + ctrl->nand_version); + return -ENODEV; + } + + /* Register offsets */ + if (ctrl->nand_version >= 0x0702) + ctrl->reg_offsets = brcmnand_regs_v72; + else if (ctrl->nand_version >= 0x0701) + ctrl->reg_offsets = brcmnand_regs_v71; + else if (ctrl->nand_version >= 0x0600) + ctrl->reg_offsets = brcmnand_regs_v60; + else if (ctrl->nand_version >= 0x0500) + ctrl->reg_offsets = brcmnand_regs_v50; + else if (ctrl->nand_version >= 0x0400) + ctrl->reg_offsets = brcmnand_regs_v40; + + /* Chip-select stride */ + if (ctrl->nand_version >= 0x0701) + ctrl->reg_spacing = 0x14; + else + ctrl->reg_spacing = 0x10; + + /* Per chip-select registers */ + if (ctrl->nand_version >= 0x0701) { + ctrl->cs_offsets = brcmnand_cs_offsets_v71; + } else { + ctrl->cs_offsets = brcmnand_cs_offsets; + + /* v5.0 and earlier has a different CS0 offset layout */ + if (ctrl->nand_version <= 0x0500) + ctrl->cs0_offsets = brcmnand_cs_offsets_cs0; + } + + /* Page / block sizes */ + if (ctrl->nand_version >= 0x0701) { + /* >= v7.1 use nice power-of-2 values! */ + ctrl->max_page_size = 16 * 1024; + ctrl->max_block_size = 2 * 1024 * 1024; + } else { + ctrl->page_sizes = page_sizes; + if (ctrl->nand_version >= 0x0600) + ctrl->block_sizes = block_sizes_v6; + else + ctrl->block_sizes = block_sizes_v4; + + if (ctrl->nand_version < 0x0400) { + ctrl->max_page_size = 4096; + ctrl->max_block_size = 512 * 1024; + } + } + + /* Maximum spare area sector size (per 512B) */ + if (ctrl->nand_version >= 0x0702) + ctrl->max_oob = 128; + else if (ctrl->nand_version >= 0x0600) + ctrl->max_oob = 64; + else if (ctrl->nand_version >= 0x0500) + ctrl->max_oob = 32; + else + ctrl->max_oob = 16; + + /* v6.0 and newer (except v6.1) have prefetch support */ + if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601) + ctrl->features |= BRCMNAND_HAS_PREFETCH; + + /* + * v6.x has cache mode, but it's implemented differently. Ignore it for + * now. + */ + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_CACHE_MODE; + + if (ctrl->nand_version >= 0x0500) + ctrl->features |= BRCMNAND_HAS_1K_SECTORS; + + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_WP; + else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp")) + ctrl->features |= BRCMNAND_HAS_WP; + + return 0; +} + +static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + return nand_readreg(ctrl, offs); + else + return 0; +} + +static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 val) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + nand_writereg(ctrl, offs, val); +} + +static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 mask, unsigned + int shift, u32 val) +{ + u32 tmp = brcmnand_read_reg(ctrl, reg); + + tmp &= ~mask; + tmp |= val << shift; + brcmnand_write_reg(ctrl, reg, tmp); +} + +static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word) +{ + return __raw_readl(ctrl->nand_fc + word * 4); +} + +static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, + int word, u32 val) +{ + __raw_writel(val, ctrl->nand_fc + word * 4); +} + +static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, + enum brcmnand_cs_reg reg) +{ + u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE]; + u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE]; + u8 cs_offs; + + if (cs == 0 && ctrl->cs0_offsets) + cs_offs = ctrl->cs0_offsets[reg]; + else + cs_offs = ctrl->cs_offsets[reg]; + + if (cs && offs_cs1) + return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs; + + return offs_cs0 + cs * ctrl->reg_spacing + cs_offs; +} + +static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0600) + return 1; + return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT); +} + +static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int shift = 0, bits; + enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; + int cs = host->cs; + + if (ctrl->nand_version >= 0x0702) + bits = 7; + else if (ctrl->nand_version >= 0x0600) + bits = 6; + else if (ctrl->nand_version >= 0x0500) + bits = 5; + else + bits = 4; + + if (ctrl->nand_version >= 0x0702) { + if (cs >= 4) + reg = BRCMNAND_CORR_THRESHOLD_EXT; + shift = (cs % 4) * bits; + } else if (ctrl->nand_version >= 0x0600) { + if (cs >= 5) + reg = BRCMNAND_CORR_THRESHOLD_EXT; + shift = (cs % 5) * bits; + } + brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val); +} + +static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0602) + return 24; + return 0; +} + +/*********************************************************************** + * NAND ACC CONTROL bitfield + * + * Some bits have remained constant throughout hardware revision, while + * others have shifted around. + ***********************************************************************/ + +/* Constant for all versions (where supported) */ +enum { + /* See BRCMNAND_HAS_CACHE_MODE */ + ACC_CONTROL_CACHE_MODE = BIT(22), + + /* See BRCMNAND_HAS_PREFETCH */ + ACC_CONTROL_PREFETCH = BIT(23), + + ACC_CONTROL_PAGE_HIT = BIT(24), + ACC_CONTROL_WR_PREEMPT = BIT(25), + ACC_CONTROL_PARTIAL_PAGE = BIT(26), + ACC_CONTROL_RD_ERASED = BIT(27), + ACC_CONTROL_FAST_PGM_RDIN = BIT(28), + ACC_CONTROL_WR_ECC = BIT(30), + ACC_CONTROL_RD_ECC = BIT(31), +}; + +static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0702) + return GENMASK(7, 0); + else if (ctrl->nand_version >= 0x0600) + return GENMASK(6, 0); + else + return GENMASK(5, 0); +} + +#define NAND_ACC_CONTROL_ECC_SHIFT 16 +#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13 + +static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl) +{ + u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f; + + mask <<= NAND_ACC_CONTROL_ECC_SHIFT; + + /* v7.2 includes additional ECC levels */ + if (ctrl->nand_version >= 0x0702) + mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT; + + return mask; +} + +static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + u32 acc_control = nand_readreg(ctrl, offs); + u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC; + + if (en) { + acc_control |= ecc_flags; /* enable RD/WR ECC */ + acc_control |= host->hwcfg.ecc_level + << NAND_ACC_CONTROL_ECC_SHIFT; + } else { + acc_control &= ~ecc_flags; /* disable RD/WR ECC */ + acc_control &= ~brcmnand_ecc_level_mask(ctrl); + } + + nand_writereg(ctrl, offs, acc_control); +} + +static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0702) + return 9; + else if (ctrl->nand_version >= 0x0600) + return 7; + else if (ctrl->nand_version >= 0x0500) + return 6; + else + return -1; +} + +static int brcmnand_get_sector_size_1k(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + + if (shift < 0) + return 0; + + return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1; +} + +static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u32 tmp; + + if (shift < 0) + return; + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~(1 << shift); + tmp |= (!!val) << shift; + nand_writereg(ctrl, acc_control_offs, tmp); +} + +/*********************************************************************** + * CS_NAND_SELECT + ***********************************************************************/ + +enum { + CS_SELECT_NAND_WP = BIT(29), + CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30), +}; + +static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl, + u32 mask, u32 expected_val, + unsigned long timeout_ms) +{ + unsigned long limit; + u32 val; + + if (!timeout_ms) + timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS; + + limit = jiffies + msecs_to_jiffies(timeout_ms); + do { + val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); + if ((val & mask) == expected_val) + return 0; + + cpu_relax(); + } while (time_after(limit, jiffies)); + + dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n", + expected_val, val & mask); + + return -ETIMEDOUT; +} + +static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) +{ + u32 val = en ? CS_SELECT_NAND_WP : 0; + + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val); +} + +/*********************************************************************** + * Flash DMA + ***********************************************************************/ + +enum flash_dma_reg { + FLASH_DMA_REVISION = 0x00, + FLASH_DMA_FIRST_DESC = 0x04, + FLASH_DMA_FIRST_DESC_EXT = 0x08, + FLASH_DMA_CTRL = 0x0c, + FLASH_DMA_MODE = 0x10, + FLASH_DMA_STATUS = 0x14, + FLASH_DMA_INTERRUPT_DESC = 0x18, + FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c, + FLASH_DMA_ERROR_STATUS = 0x20, + FLASH_DMA_CURRENT_DESC = 0x24, + FLASH_DMA_CURRENT_DESC_EXT = 0x28, +}; + +static inline bool has_flash_dma(struct brcmnand_controller *ctrl) +{ + return ctrl->flash_dma_base; +} + +static inline bool flash_dma_buf_ok(const void *buf) +{ + return buf && !is_vmalloc_addr(buf) && + likely(IS_ALIGNED((uintptr_t)buf, 4)); +} + +static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs, + u32 val) +{ + brcmnand_writel(val, ctrl->flash_dma_base + offs); +} + +static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs) +{ + return brcmnand_readl(ctrl->flash_dma_base + offs); +} + +/* Low-level operation types: command, address, write, or read */ +enum brcmnand_llop_type { + LL_OP_CMD, + LL_OP_ADDR, + LL_OP_WR, + LL_OP_RD, +}; + +/*********************************************************************** + * Internal support functions + ***********************************************************************/ + +static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl, + struct brcmnand_cfg *cfg) +{ + if (ctrl->nand_version <= 0x0701) + return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 && + cfg->ecc_level == 15; + else + return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 && + cfg->ecc_level == 15) || + (cfg->spare_area_size == 28 && cfg->ecc_level == 16)); +} + +/* + * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given + * the layout/configuration. + * Returns -ERRCODE on failure. + */ +static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + oobregion->offset = (section * sas) + 6; + oobregion->length = 3; + + return 0; +} + +static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors * 2) + return -ERANGE; + + oobregion->offset = (section / 2) * sas; + + if (section & 1) { + oobregion->offset += 9; + oobregion->length = 7; + } else { + oobregion->length = 6; + + /* First sector of each page may have BBI */ + if (!section) { + /* + * Small-page NAND use byte 6 for BBI while large-page + * NAND use byte 0. + */ + if (cfg->page_size > 512) + oobregion->offset++; + oobregion->length--; + } + } + + return 0; +} + +static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = { + .ecc = brcmnand_hamming_ooblayout_ecc, + .free = brcmnand_hamming_ooblayout_free, +}; + +static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + if (sas <= chip->ecc.bytes) + return 0; + + oobregion->offset = section * sas; + oobregion->length = sas - chip->ecc.bytes; + + if (!section) { + oobregion->offset++; + oobregion->length--; + } + + return 0; +} + +static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + + if (section > 1 || sas - chip->ecc.bytes < 6 || + (section && sas - chip->ecc.bytes == 6)) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 6; + oobregion->length = sas - chip->ecc.bytes - 6; + } + + return 0; +} + +static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .free = brcmnand_bch_ooblayout_free_lp, +}; + +static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .free = brcmnand_bch_ooblayout_free_sp, +}; + +static int brcmstb_choose_ecc_layout(struct brcmnand_host *host) +{ + struct brcmnand_cfg *p = &host->hwcfg; + struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct nand_ecc_ctrl *ecc = &host->chip.ecc; + unsigned int ecc_level = p->ecc_level; + int sas = p->spare_area_size << p->sector_size_1k; + int sectors = p->page_size / (512 << p->sector_size_1k); + + if (p->sector_size_1k) + ecc_level <<= 1; + + if (is_hamming_ecc(host->ctrl, p)) { + ecc->bytes = 3 * sectors; + mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops); + return 0; + } + + /* + * CONTROLLER_VERSION: + * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) + * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) + * But we will just be conservative. + */ + ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8); + if (p->page_size == 512) + mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops); + else + mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops); + + if (ecc->bytes >= sas) { + dev_err(&host->pdev->dev, + "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", + ecc->bytes, sas); + return -EINVAL; + } + + return 0; +} + +static void brcmnand_wp(struct mtd_info *mtd, int wp) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + + if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) { + static int old_wp = -1; + int ret; + + if (old_wp != wp) { + dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off"); + old_wp = wp; + } + + /* + * make sure ctrl/flash ready before and after + * changing state of #WP pin + */ + ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY | + NAND_STATUS_READY, + NAND_CTRL_RDY | + NAND_STATUS_READY, 0); + if (ret) + return; + + brcmnand_set_wp(ctrl, wp); + nand_status_op(chip, NULL); + /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */ + ret = bcmnand_ctrl_poll_status(ctrl, + NAND_CTRL_RDY | + NAND_STATUS_READY | + NAND_STATUS_WP, + NAND_CTRL_RDY | + NAND_STATUS_READY | + (wp ? 0 : NAND_STATUS_WP), 0); + + if (ret) + dev_err_ratelimited(&host->pdev->dev, + "nand #WP expected %s\n", + wp ? "on" : "off"); + } +} + +/* Helper functions for reading and writing OOB registers */ +static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE]; + + if (offs >= ctrl->max_oob) + return 0x77; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3)); +} + +static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs, + u32 data) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE]; + + if (offs >= ctrl->max_oob) + return; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + nand_writereg(ctrl, reg_offs, data); +} + +/* + * read_oob_from_regs - read data from OOB registers + * @ctrl: NAND controller + * @i: sub-page sector index + * @oob: buffer to read to + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob, + int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; j < tbytes; j++) + oob[j] = oob_reg_read(ctrl, j); + return tbytes; +} + +/* + * write_oob_to_regs - write data to OOB registers + * @i: sub-page sector index + * @oob: buffer to write from + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, + const u8 *oob, int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; j < tbytes; j += 4) + oob_reg_write(ctrl, j, + (oob[j + 0] << 24) | + (oob[j + 1] << 16) | + (oob[j + 2] << 8) | + (oob[j + 3] << 0)); + return tbytes; +} + +static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + /* Discard all NAND_CTLRDY interrupts during DMA */ + if (ctrl->dma_pending) + return IRQ_HANDLED; + + complete(&ctrl->done); + return IRQ_HANDLED; +} + +/* Handle SoC-specific interrupt hardware */ +static irqreturn_t brcmnand_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + if (ctrl->soc->ctlrdy_ack(ctrl->soc)) + return brcmnand_ctlrdy_irq(irq, data); + + return IRQ_NONE; +} + +static irqreturn_t brcmnand_dma_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + complete(&ctrl->dma_done); + + return IRQ_HANDLED; +} + +static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int ret; + + dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, + brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); + BUG_ON(ctrl->cmd_pending != 0); + ctrl->cmd_pending = cmd; + + ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); + WARN_ON(ret); + + mb(); /* flush previous writes */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_START, + cmd << brcmnand_cmd_shift(ctrl)); +} + +/*********************************************************************** + * NAND MTD API: read/program/erase + ***********************************************************************/ + +static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + /* intentionally left blank */ +} + +static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(100); + + dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); + if (ctrl->cmd_pending && + wait_for_completion_timeout(&ctrl->done, timeo) <= 0) { + u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) + >> brcmnand_cmd_shift(ctrl); + + dev_err_ratelimited(ctrl->dev, + "timeout waiting for command %#02x\n", cmd); + dev_err_ratelimited(ctrl->dev, "intfc status %08x\n", + brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)); + } + ctrl->cmd_pending = 0; + return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; +} + +enum { + LLOP_RE = BIT(16), + LLOP_WE = BIT(17), + LLOP_ALE = BIT(18), + LLOP_CLE = BIT(19), + LLOP_RETURN_IDLE = BIT(31), + + LLOP_DATA_MASK = GENMASK(15, 0), +}; + +static int brcmnand_low_level_op(struct brcmnand_host *host, + enum brcmnand_llop_type type, u32 data, + bool last_op) +{ + struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + u32 tmp; + + tmp = data & LLOP_DATA_MASK; + switch (type) { + case LL_OP_CMD: + tmp |= LLOP_WE | LLOP_CLE; + break; + case LL_OP_ADDR: + /* WE | ALE */ + tmp |= LLOP_WE | LLOP_ALE; + break; + case LL_OP_WR: + /* WE */ + tmp |= LLOP_WE; + break; + case LL_OP_RD: + /* RE */ + tmp |= LLOP_RE; + break; + } + if (last_op) + /* RETURN_IDLE */ + tmp |= LLOP_RETURN_IDLE; + + dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp); + + brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp); + (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP); + + brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP); + return brcmnand_waitfunc(mtd, chip); +} + +static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + u64 addr = (u64)page_addr << chip->page_shift; + int native_cmd = 0; + + if (command == NAND_CMD_READID || command == NAND_CMD_PARAM || + command == NAND_CMD_RNDOUT) + addr = (u64)column; + /* Avoid propagating a negative, don't-care address */ + else if (page_addr < 0) + addr = 0; + + dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command, + (unsigned long long)addr); + + host->last_cmd = command; + host->last_byte = 0; + host->last_addr = addr; + + switch (command) { + case NAND_CMD_RESET: + native_cmd = CMD_FLASH_RESET; + break; + case NAND_CMD_STATUS: + native_cmd = CMD_STATUS_READ; + break; + case NAND_CMD_READID: + native_cmd = CMD_DEVICE_ID_READ; + break; + case NAND_CMD_READOOB: + native_cmd = CMD_SPARE_AREA_READ; + break; + case NAND_CMD_ERASE1: + native_cmd = CMD_BLOCK_ERASE; + brcmnand_wp(mtd, 0); + break; + case NAND_CMD_PARAM: + native_cmd = CMD_PARAMETER_READ; + break; + case NAND_CMD_SET_FEATURES: + case NAND_CMD_GET_FEATURES: + brcmnand_low_level_op(host, LL_OP_CMD, command, false); + brcmnand_low_level_op(host, LL_OP_ADDR, column, false); + break; + case NAND_CMD_RNDOUT: + native_cmd = CMD_PARAMETER_CHANGE_COL; + addr &= ~((u64)(FC_BYTES - 1)); + /* + * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0 + * NB: hwcfg.sector_size_1k may not be initialized yet + */ + if (brcmnand_get_sector_size_1k(host)) { + host->hwcfg.sector_size_1k = + brcmnand_get_sector_size_1k(host); + brcmnand_set_sector_size_1k(host, 0); + } + break; + } + + if (!native_cmd) + return; + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + brcmnand_send_cmd(host, native_cmd); + brcmnand_waitfunc(mtd, chip); + + if (native_cmd == CMD_PARAMETER_READ || + native_cmd == CMD_PARAMETER_CHANGE_COL) { + /* Copy flash cache word-wise */ + u32 *flash_cache = (u32 *)ctrl->flash_cache; + int i; + + brcmnand_soc_data_bus_prepare(ctrl->soc, true); + + /* + * Must cache the FLASH_CACHE now, since changes in + * SECTOR_SIZE_1K may invalidate it + */ + for (i = 0; i < FC_WORDS; i++) + /* + * Flash cache is big endian for parameter pages, at + * least on STB SoCs + */ + flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i)); + + brcmnand_soc_data_bus_unprepare(ctrl->soc, true); + + /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ + if (host->hwcfg.sector_size_1k) + brcmnand_set_sector_size_1k(host, + host->hwcfg.sector_size_1k); + } + + /* Re-enable protection is necessary only after erase */ + if (command == NAND_CMD_ERASE1) + brcmnand_wp(mtd, 1); +} + +static uint8_t brcmnand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + uint8_t ret = 0; + int addr, offs; + + switch (host->last_cmd) { + case NAND_CMD_READID: + if (host->last_byte < 4) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >> + (24 - (host->last_byte << 3)); + else if (host->last_byte < 8) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >> + (56 - (host->last_byte << 3)); + break; + + case NAND_CMD_READOOB: + ret = oob_reg_read(ctrl, host->last_byte); + break; + + case NAND_CMD_STATUS: + ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; + if (wp_on) /* hide WP status */ + ret |= NAND_STATUS_WP; + break; + + case NAND_CMD_PARAM: + case NAND_CMD_RNDOUT: + addr = host->last_addr + host->last_byte; + offs = addr & (FC_BYTES - 1); + + /* At FC_BYTES boundary, switch to next column */ + if (host->last_byte > 0 && offs == 0) + nand_change_read_column_op(chip, addr, NULL, 0, false); + + ret = ctrl->flash_cache[offs]; + break; + case NAND_CMD_GET_FEATURES: + if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) { + ret = 0; + } else { + bool last = host->last_byte == + ONFI_SUBFEATURE_PARAM_LEN - 1; + brcmnand_low_level_op(host, LL_OP_RD, 0, last); + ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff; + } + } + + dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret); + host->last_byte++; + + return ret; +} + +static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++, buf++) + *buf = brcmnand_read_byte(mtd); +} + +static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + int i; + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + + switch (host->last_cmd) { + case NAND_CMD_SET_FEATURES: + for (i = 0; i < len; i++) + brcmnand_low_level_op(host, LL_OP_WR, buf[i], + (i + 1) == len); + break; + default: + BUG(); + break; + } +} + +/** + * Construct a FLASH_DMA descriptor as part of a linked list. You must know the + * following ahead of time: + * - Is this descriptor the beginning or end of a linked list? + * - What is the (DMA) address of the next descriptor in the linked list? + */ +static int brcmnand_fill_dma_desc(struct brcmnand_host *host, + struct brcm_nand_dma_desc *desc, u64 addr, + dma_addr_t buf, u32 len, u8 dma_cmd, + bool begin, bool end, + dma_addr_t next_desc) +{ + memset(desc, 0, sizeof(*desc)); + /* Descriptors are written in native byte order (wordwise) */ + desc->next_desc = lower_32_bits(next_desc); + desc->next_desc_ext = upper_32_bits(next_desc); + desc->cmd_irq = (dma_cmd << 24) | + (end ? (0x03 << 8) : 0) | /* IRQ | STOP */ + (!!begin) | ((!!end) << 1); /* head, tail */ +#ifdef CONFIG_CPU_BIG_ENDIAN + desc->cmd_irq |= 0x01 << 12; +#endif + desc->dram_addr = lower_32_bits(buf); + desc->dram_addr_ext = upper_32_bits(buf); + desc->tfr_len = len; + desc->total_len = len; + desc->flash_addr = lower_32_bits(addr); + desc->flash_addr_ext = upper_32_bits(addr); + desc->cs = host->cs; + desc->status_valid = 0x01; + return 0; +} + +/** + * Kick the FLASH_DMA engine, with a given DMA descriptor + */ +static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(100); + + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + + /* Start FLASH_DMA engine */ + ctrl->dma_pending = true; + mb(); /* flush previous writes */ + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */ + + if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) { + dev_err(ctrl->dev, + "timeout waiting for DMA; status %#x, error status %#x\n", + flash_dma_readl(ctrl, FLASH_DMA_STATUS), + flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS)); + } + ctrl->dma_pending = false; + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */ +} + +static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf, + u32 len, u8 dma_cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + dma_addr_t buf_pa; + int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + buf_pa = dma_map_single(ctrl->dev, buf, len, dir); + if (dma_mapping_error(ctrl->dev, buf_pa)) { + dev_err(ctrl->dev, "unable to map buffer for DMA\n"); + return -ENOMEM; + } + + brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len, + dma_cmd, true, true, 0); + + brcmnand_dma_run(host, ctrl->dma_pa); + + dma_unmap_single(ctrl->dev, buf_pa, len, dir); + + if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR) + return -EBADMSG; + else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR) + return -EUCLEAN; + + return 0; +} + +/* + * Assumes proper CS is already set + */ +static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, + u8 *oob, u64 *err_addr) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + int i, j, ret = 0; + + /* Clear error addresses */ + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0); + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ + brcmnand_send_cmd(host, CMD_PAGE_READ); + brcmnand_waitfunc(mtd, chip); + + if (likely(buf)) { + brcmnand_soc_data_bus_prepare(ctrl->soc, false); + + for (j = 0; j < FC_WORDS; j++, buf++) + *buf = brcmnand_read_fc(ctrl, j); + + brcmnand_soc_data_bus_unprepare(ctrl->soc, false); + } + + if (oob) + oob += read_oob_from_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EBADMSG; + } + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_CORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_CORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EUCLEAN; + } + } + + return ret; +} + +/* + * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC + * error + * + * Because the HW ECC signals an ECC error if an erase paged has even a single + * bitflip, we must check each ECC error to see if it is actually an erased + * page with bitflips, not a truly corrupted page. + * + * On a real error, return a negative error code (-EBADMSG for ECC error), and + * buf will contain raw data. + * Otherwise, buf gets filled with 0xffs and return the maximum number of + * bitflips-per-ECC-sector to the caller. + * + */ +static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, + struct nand_chip *chip, void *buf, u64 addr) +{ + int i, sas; + void *oob = chip->oob_poi; + int bitflips = 0; + int page = addr >> chip->page_shift; + int ret; + + if (!buf) { + buf = chip->data_buf; + /* Invalidate page cache */ + chip->pagebuf = -1; + } + + sas = mtd->oobsize / chip->ecc.steps; + + /* read without ecc for verification */ + ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page); + if (ret) + return ret; + + for (i = 0; i < chip->ecc.steps; i++, oob += sas) { + ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, + oob, sas, NULL, 0, + chip->ecc.strength); + if (ret < 0) + return ret; + + bitflips = max(bitflips, ret); + } + + return bitflips; +} + +static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + u64 err_addr = 0; + int err; + bool retry = true; + + dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); + +try_dmaread: + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0); + + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, + CMD_PAGE_READ); + if (err) { + if (mtd_is_bitflip_or_eccerr(err)) + err_addr = addr; + else + return -EIO; + } + } else { + if (oob) + memset(oob, 0x99, mtd->oobsize); + + err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, + oob, &err_addr); + } + + if (mtd_is_eccerr(err)) { + /* + * On controller version and 7.0, 7.1 , DMA read after a + * prior PIO read that reported uncorrectable error, + * the DMA engine captures this error following DMA read + * cleared only on subsequent DMA read, so just retry once + * to clear a possible false error reported for current DMA + * read + */ + if ((ctrl->nand_version == 0x0700) || + (ctrl->nand_version == 0x0701)) { + if (retry) { + retry = false; + goto try_dmaread; + } + } + + /* + * Controller version 7.2 has hw encoder to detect erased page + * bitflips, apply sw verification for older controllers only + */ + if (ctrl->nand_version < 0x0702) { + err = brcmstb_nand_verify_erased_page(mtd, chip, buf, + addr); + /* erased page bitflips corrected */ + if (err >= 0) + return err; + } + + dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.failed++; + /* NAND layer expects zero on ECC errors */ + return 0; + } + + if (mtd_is_bitflip(err)) { + unsigned int corrected = brcmnand_count_corrected(ctrl); + + dev_dbg(ctrl->dev, "corrected error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.corrected += corrected; + /* Always exceed the software-imposed threshold */ + return max(mtd->bitflip_threshold, corrected); + } + + return 0; +} + +static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + + nand_read_page_op(chip, page, 0, NULL, 0); + + return brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); +} + +static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + int ret; + + nand_read_page_op(chip, page, 0, NULL, 0); + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + return ret; +} + +static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); +} + +static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + + brcmnand_set_ecc_enabled(host, 0); + brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + return 0; +} + +static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, const u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int i, j, trans = mtd->writesize >> FC_SHIFT; + int status, ret = 0; + + dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf); + + if (unlikely((unsigned long)buf & 0x03)) { + dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf); + buf = (u32 *)((unsigned long)buf & ~0x03); + } + + brcmnand_wp(mtd, 0); + + for (i = 0; i < ctrl->max_oob; i += 4) + oob_reg_write(ctrl, i, 0xffffffff); + + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + if (brcmnand_dma_trans(host, addr, (u32 *)buf, + mtd->writesize, CMD_PROGRAM_PAGE)) + ret = -EIO; + goto out; + } + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + /* full address MUST be set before populating FC */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + if (buf) { + brcmnand_soc_data_bus_prepare(ctrl->soc, false); + + for (j = 0; j < FC_WORDS; j++, buf++) + brcmnand_write_fc(ctrl, j, *buf); + + brcmnand_soc_data_bus_unprepare(ctrl->soc, false); + } else if (oob) { + for (j = 0; j < FC_WORDS; j++) + brcmnand_write_fc(ctrl, j, 0xffffffff); + } + + if (oob) { + oob += write_oob_to_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + } + + /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */ + brcmnand_send_cmd(host, CMD_PROGRAM_PAGE); + status = brcmnand_waitfunc(mtd, chip); + + if (status & NAND_STATUS_FAIL) { + dev_info(ctrl->dev, "program failed at %llx\n", + (unsigned long long)addr); + ret = -EIO; + goto out; + } + } +out: + brcmnand_wp(mtd, 1); + return ret; +} + +static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + void *oob = oob_required ? chip->oob_poi : NULL; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + + return nand_prog_page_end_op(chip); +} + +static int brcmnand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required, int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + void *oob = oob_required ? chip->oob_poi : NULL; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + brcmnand_set_ecc_enabled(host, 0); + brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + + return nand_prog_page_end_op(chip); +} + +static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_write(mtd, chip, (u64)page << chip->page_shift, + NULL, chip->oob_poi); +} + +static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + int ret; + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL, + (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + + return ret; +} + +/*********************************************************************** + * Per-CS setup (1 NAND device) + ***********************************************************************/ + +static int brcmnand_set_cfg(struct brcmnand_host *host, + struct brcmnand_cfg *cfg) +{ + struct brcmnand_controller *ctrl = host->ctrl; + struct nand_chip *chip = &host->chip; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u8 block_size = 0, page_size = 0, device_size = 0; + u32 tmp; + + if (ctrl->block_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->block_sizes[i]; i++) + if (ctrl->block_sizes[i] * 1024 == cfg->block_size) { + block_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + return -EINVAL; + } + } else { + block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE); + } + + if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size && + cfg->block_size > ctrl->max_block_size)) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + block_size = 0; + } + + if (ctrl->page_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->page_sizes[i]; i++) + if (ctrl->page_sizes[i] == cfg->page_size) { + page_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid page size %u\n", + cfg->page_size); + return -EINVAL; + } + } else { + page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE); + } + + if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size && + cfg->page_size > ctrl->max_page_size)) { + dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size); + return -EINVAL; + } + + if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) { + dev_warn(ctrl->dev, "invalid device size 0x%llx\n", + (unsigned long long)cfg->device_size); + return -EINVAL; + } + device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE); + + tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) | + (cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) | + (cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) | + (!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) | + (device_size << CFG_DEVICE_SIZE_SHIFT); + if (cfg_offs == cfg_ext_offs) { + tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) | + (block_size << CFG_BLK_SIZE_SHIFT); + nand_writereg(ctrl, cfg_offs, tmp); + } else { + nand_writereg(ctrl, cfg_offs, tmp); + tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) | + (block_size << CFG_EXT_BLK_SIZE_SHIFT); + nand_writereg(ctrl, cfg_ext_offs, tmp); + } + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~brcmnand_ecc_level_mask(ctrl); + tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; + tmp &= ~brcmnand_spare_area_mask(ctrl); + tmp |= cfg->spare_area_size; + nand_writereg(ctrl, acc_control_offs, tmp); + + brcmnand_set_sector_size_1k(host, cfg->sector_size_1k); + + /* threshold = ceil(BCH-level * 0.75) */ + brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4)); + + return 0; +} + +static void brcmnand_print_cfg(struct brcmnand_host *host, + char *buf, struct brcmnand_cfg *cfg) +{ + buf += sprintf(buf, + "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit", + (unsigned long long)cfg->device_size >> 20, + cfg->block_size >> 10, + cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size, + cfg->page_size >= 1024 ? "KiB" : "B", + cfg->spare_area_size, cfg->device_width); + + /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */ + if (is_hamming_ecc(host->ctrl, cfg)) + sprintf(buf, ", Hamming ECC"); + else if (cfg->sector_size_1k) + sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1); + else + sprintf(buf, ", BCH-%u", cfg->ecc_level); +} + +/* + * Minimum number of bytes to address a page. Calculated as: + * roundup(log2(size / page-size) / 8) + * + * NB: the following does not "round up" for non-power-of-2 'size'; but this is + * OK because many other things will break if 'size' is irregular... + */ +static inline int get_blk_adr_bytes(u64 size, u32 writesize) +{ + return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3; +} + +static int brcmnand_setup_dev(struct brcmnand_host *host) +{ + struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + struct brcmnand_cfg *cfg = &host->hwcfg; + char msg[128]; + u32 offs, tmp, oob_sector; + int ret; + + memset(cfg, 0, sizeof(*cfg)); + + ret = of_property_read_u32(nand_get_flash_node(chip), + "brcm,nand-oob-sector-size", + &oob_sector); + if (ret) { + /* Use detected size */ + cfg->spare_area_size = mtd->oobsize / + (mtd->writesize >> FC_SHIFT); + } else { + cfg->spare_area_size = oob_sector; + } + if (cfg->spare_area_size > ctrl->max_oob) + cfg->spare_area_size = ctrl->max_oob; + /* + * Set oobsize to be consistent with controller's spare_area_size, as + * the rest is inaccessible. + */ + mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT); + + cfg->device_size = mtd->size; + cfg->block_size = mtd->erasesize; + cfg->page_size = mtd->writesize; + cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8; + cfg->col_adr_bytes = 2; + cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize); + + if (chip->ecc.mode != NAND_ECC_HW) { + dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n", + chip->ecc.mode); + return -EINVAL; + } + + if (chip->ecc.algo == NAND_ECC_UNKNOWN) { + if (chip->ecc.strength == 1 && chip->ecc.size == 512) + /* Default to Hamming for 1-bit ECC, if unspecified */ + chip->ecc.algo = NAND_ECC_HAMMING; + else + /* Otherwise, BCH */ + chip->ecc.algo = NAND_ECC_BCH; + } + + if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 || + chip->ecc.size != 512)) { + dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n", + chip->ecc.strength, chip->ecc.size); + return -EINVAL; + } + + switch (chip->ecc.size) { + case 512: + if (chip->ecc.algo == NAND_ECC_HAMMING) + cfg->ecc_level = 15; + else + cfg->ecc_level = chip->ecc.strength; + cfg->sector_size_1k = 0; + break; + case 1024: + if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) { + dev_err(ctrl->dev, "1KB sectors not supported\n"); + return -EINVAL; + } + if (chip->ecc.strength & 0x1) { + dev_err(ctrl->dev, + "odd ECC not supported with 1KB sectors\n"); + return -EINVAL; + } + + cfg->ecc_level = chip->ecc.strength >> 1; + cfg->sector_size_1k = 1; + break; + default: + dev_err(ctrl->dev, "unsupported ECC size: %d\n", + chip->ecc.size); + return -EINVAL; + } + + cfg->ful_adr_bytes = cfg->blk_adr_bytes; + if (mtd->writesize > 512) + cfg->ful_adr_bytes += cfg->col_adr_bytes; + else + cfg->ful_adr_bytes += 1; + + ret = brcmnand_set_cfg(host, cfg); + if (ret) + return ret; + + brcmnand_set_ecc_enabled(host, 1); + + brcmnand_print_cfg(host, msg, cfg); + dev_info(ctrl->dev, "detected %s\n", msg); + + /* Configure ACC_CONTROL */ + offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + tmp = nand_readreg(ctrl, offs); + tmp &= ~ACC_CONTROL_PARTIAL_PAGE; + tmp &= ~ACC_CONTROL_RD_ERASED; + + /* We need to turn on Read from erased paged protected by ECC */ + if (ctrl->nand_version >= 0x0702) + tmp |= ACC_CONTROL_RD_ERASED; + tmp &= ~ACC_CONTROL_FAST_PGM_RDIN; + if (ctrl->features & BRCMNAND_HAS_PREFETCH) + tmp &= ~ACC_CONTROL_PREFETCH; + + nand_writereg(ctrl, offs, tmp); + + return 0; +} + +static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) +{ + struct brcmnand_controller *ctrl = host->ctrl; + struct platform_device *pdev = host->pdev; + struct mtd_info *mtd; + struct nand_chip *chip; + int ret; + u16 cfg_offs; + + ret = of_property_read_u32(dn, "reg", &host->cs); + if (ret) { + dev_err(&pdev->dev, "can't get chip-select\n"); + return -ENXIO; + } + + mtd = nand_to_mtd(&host->chip); + chip = &host->chip; + + nand_set_flash_node(chip, dn); + nand_set_controller_data(chip, host); + mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d", + host->cs); + if (!mtd->name) + return -ENOMEM; + + mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; + + chip->IO_ADDR_R = (void __iomem *)0xdeadbeef; + chip->IO_ADDR_W = (void __iomem *)0xdeadbeef; + + chip->cmd_ctrl = brcmnand_cmd_ctrl; + chip->cmdfunc = brcmnand_cmdfunc; + chip->waitfunc = brcmnand_waitfunc; + chip->read_byte = brcmnand_read_byte; + chip->read_buf = brcmnand_read_buf; + chip->write_buf = brcmnand_write_buf; + + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.read_page = brcmnand_read_page; + chip->ecc.write_page = brcmnand_write_page; + chip->ecc.read_page_raw = brcmnand_read_page_raw; + chip->ecc.write_page_raw = brcmnand_write_page_raw; + chip->ecc.write_oob_raw = brcmnand_write_oob_raw; + chip->ecc.read_oob_raw = brcmnand_read_oob_raw; + chip->ecc.read_oob = brcmnand_read_oob; + chip->ecc.write_oob = brcmnand_write_oob; + + chip->controller = &ctrl->controller; + + /* + * The bootloader might have configured 16bit mode but + * NAND READID command only works in 8bit mode. We force + * 8bit mode here to ensure that NAND READID commands works. + */ + cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + nand_writereg(ctrl, cfg_offs, + nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH); + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + /* + * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA + * to/from, and have nand_base pass us a bounce buffer instead, as + * needed. + */ + chip->options |= NAND_USE_BOUNCE_BUFFER; + + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + if (brcmnand_setup_dev(host)) + return -ENXIO; + + chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; + /* only use our internal HW threshold */ + mtd->bitflip_threshold = 1; + + ret = brcmstb_choose_ecc_layout(host); + if (ret) + return ret; + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + return mtd_device_register(mtd, NULL, 0); +} + +static void brcmnand_save_restore_cs_config(struct brcmnand_host *host, + int restore) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1); + u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2); + + if (restore) { + nand_writereg(ctrl, cfg_offs, host->hwcfg.config); + if (cfg_offs != cfg_ext_offs) + nand_writereg(ctrl, cfg_ext_offs, + host->hwcfg.config_ext); + nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control); + nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1); + nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2); + } else { + host->hwcfg.config = nand_readreg(ctrl, cfg_offs); + if (cfg_offs != cfg_ext_offs) + host->hwcfg.config_ext = + nand_readreg(ctrl, cfg_ext_offs); + host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs); + host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs); + host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs); + } +} + +static int brcmnand_suspend(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + brcmnand_save_restore_cs_config(host, 0); + + ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT); + ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR); + ctrl->corr_stat_threshold = + brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD); + + if (has_flash_dma(ctrl)) + ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE); + + return 0; +} + +static int brcmnand_resume(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + if (has_flash_dma(ctrl)) { + flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode); + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + } + + brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select); + brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, + ctrl->corr_stat_threshold); + if (ctrl->soc) { + /* Clear/re-enable interrupt */ + ctrl->soc->ctlrdy_ack(ctrl->soc); + ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); + } + + list_for_each_entry(host, &ctrl->host_list, node) { + struct nand_chip *chip = &host->chip; + + brcmnand_save_restore_cs_config(host, 1); + + /* Reset the chip, required by some chips after power-up */ + nand_reset_op(chip); + } + + return 0; +} + +const struct dev_pm_ops brcmnand_pm_ops = { + .suspend = brcmnand_suspend, + .resume = brcmnand_resume, +}; +EXPORT_SYMBOL_GPL(brcmnand_pm_ops); + +static const struct of_device_id brcmnand_of_match[] = { + { .compatible = "brcm,brcmnand-v4.0" }, + { .compatible = "brcm,brcmnand-v5.0" }, + { .compatible = "brcm,brcmnand-v6.0" }, + { .compatible = "brcm,brcmnand-v6.1" }, + { .compatible = "brcm,brcmnand-v6.2" }, + { .compatible = "brcm,brcmnand-v7.0" }, + { .compatible = "brcm,brcmnand-v7.1" }, + { .compatible = "brcm,brcmnand-v7.2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmnand_of_match); + +/*********************************************************************** + * Platform driver setup (per controller) + ***********************************************************************/ + +int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; + struct brcmnand_controller *ctrl; + struct resource *res; + int ret; + + /* We only support device-tree instantiation */ + if (!dn) + return -ENODEV; + + if (!of_match_node(brcmnand_of_match, dn)) + return -ENODEV; + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + dev_set_drvdata(dev, ctrl); + ctrl->dev = dev; + + init_completion(&ctrl->done); + init_completion(&ctrl->dma_done); + nand_hw_control_init(&ctrl->controller); + INIT_LIST_HEAD(&ctrl->host_list); + + /* NAND register range */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctrl->nand_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->nand_base)) + return PTR_ERR(ctrl->nand_base); + + /* Enable clock before using NAND registers */ + ctrl->clk = devm_clk_get(dev, "nand"); + if (!IS_ERR(ctrl->clk)) { + ret = clk_prepare_enable(ctrl->clk); + if (ret) + return ret; + } else { + ret = PTR_ERR(ctrl->clk); + if (ret == -EPROBE_DEFER) + return ret; + + ctrl->clk = NULL; + } + + /* Initialize NAND revision */ + ret = brcmnand_revision_init(ctrl); + if (ret) + goto err; + + /* + * Most chips have this cache at a fixed offset within 'nand' block. + * Some must specify this region separately. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache"); + if (res) { + ctrl->nand_fc = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->nand_fc)) { + ret = PTR_ERR(ctrl->nand_fc); + goto err; + } + } else { + ctrl->nand_fc = ctrl->nand_base + + ctrl->reg_offsets[BRCMNAND_FC_BASE]; + } + + /* FLASH_DMA */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma"); + if (res) { + ctrl->flash_dma_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->flash_dma_base)) { + ret = PTR_ERR(ctrl->flash_dma_base); + goto err; + } + + flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */ + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + + /* Allocate descriptor(s) */ + ctrl->dma_desc = dmam_alloc_coherent(dev, + sizeof(*ctrl->dma_desc), + &ctrl->dma_pa, GFP_KERNEL); + if (!ctrl->dma_desc) { + ret = -ENOMEM; + goto err; + } + + ctrl->dma_irq = platform_get_irq(pdev, 1); + if ((int)ctrl->dma_irq < 0) { + dev_err(dev, "missing FLASH_DMA IRQ\n"); + ret = -ENODEV; + goto err; + } + + ret = devm_request_irq(dev, ctrl->dma_irq, + brcmnand_dma_irq, 0, DRV_NAME, + ctrl); + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->dma_irq, ret); + goto err; + } + + dev_info(dev, "enabling FLASH_DMA\n"); + } + + /* Disable automatic device ID config, direct addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, + CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0); + /* Disable XOR addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); + + if (ctrl->features & BRCMNAND_HAS_WP) { + /* Permanently disable write protection */ + if (wp_on == 2) + brcmnand_set_wp(ctrl, false); + } else { + wp_on = 0; + } + + /* IRQ */ + ctrl->irq = platform_get_irq(pdev, 0); + if ((int)ctrl->irq < 0) { + dev_err(dev, "no IRQ defined\n"); + ret = -ENODEV; + goto err; + } + + /* + * Some SoCs integrate this controller (e.g., its interrupt bits) in + * interesting ways + */ + if (soc) { + ctrl->soc = soc; + + ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0, + DRV_NAME, ctrl); + + /* Enable interrupt */ + ctrl->soc->ctlrdy_ack(ctrl->soc); + ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); + } else { + /* Use standard interrupt infrastructure */ + ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, + DRV_NAME, ctrl); + } + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->irq, ret); + goto err; + } + + for_each_available_child_of_node(dn, child) { + if (of_device_is_compatible(child, "brcm,nandcs")) { + struct brcmnand_host *host; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) { + of_node_put(child); + ret = -ENOMEM; + goto err; + } + host->pdev = pdev; + host->ctrl = ctrl; + + ret = brcmnand_init_cs(host, child); + if (ret) { + devm_kfree(dev, host); + continue; /* Try all chip-selects */ + } + + list_add_tail(&host->node, &ctrl->host_list); + } + } + + /* No chip-selects could initialize properly */ + if (list_empty(&ctrl->host_list)) { + ret = -ENODEV; + goto err; + } + + return 0; + +err: + clk_disable_unprepare(ctrl->clk); + return ret; + +} +EXPORT_SYMBOL_GPL(brcmnand_probe); + +int brcmnand_remove(struct platform_device *pdev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + nand_release(nand_to_mtd(&host->chip)); + + clk_disable_unprepare(ctrl->clk); + + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} +EXPORT_SYMBOL_GPL(brcmnand_remove); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kevin Cernekee"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("NAND driver for Broadcom chips"); +MODULE_ALIAS("platform:brcmnand"); diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h new file mode 100644 index 000000000000..5c44cd4aba87 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h @@ -0,0 +1,74 @@ +/* + * Copyright © 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __BRCMNAND_H__ +#define __BRCMNAND_H__ + +#include +#include + +struct platform_device; +struct dev_pm_ops; + +struct brcmnand_soc { + bool (*ctlrdy_ack)(struct brcmnand_soc *soc); + void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); + void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare, + bool is_param); +}; + +static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc, + bool is_param) +{ + if (soc && soc->prepare_data_bus) + soc->prepare_data_bus(soc, true, is_param); +} + +static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc, + bool is_param) +{ + if (soc && soc->prepare_data_bus) + soc->prepare_data_bus(soc, false, is_param); +} + +static inline u32 brcmnand_readl(void __iomem *addr) +{ + /* + * MIPS endianness is configured by boot strap, which also reverses all + * bus endianness (i.e., big-endian CPU + big endian bus ==> native + * endian I/O). + * + * Other architectures (e.g., ARM) either do not support big endian, or + * else leave I/O in little endian mode. + */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + return __raw_readl(addr); + else + return readl_relaxed(addr); +} + +static inline void brcmnand_writel(u32 val, void __iomem *addr) +{ + /* See brcmnand_readl() comments */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + __raw_writel(val, addr); + else + writel_relaxed(val, addr); +} + +int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc); +int brcmnand_remove(struct platform_device *pdev); + +extern const struct dev_pm_ops brcmnand_pm_ops; + +#endif /* __BRCMNAND_H__ */ diff --git a/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c new file mode 100644 index 000000000000..5c271077ac87 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c @@ -0,0 +1,44 @@ +/* + * Copyright © 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "brcmnand.h" + +static const struct of_device_id brcmstb_nand_of_match[] = { + { .compatible = "brcm,brcmnand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmstb_nand_of_match); + +static int brcmstb_nand_probe(struct platform_device *pdev) +{ + return brcmnand_probe(pdev, NULL); +} + +static struct platform_driver brcmstb_nand_driver = { + .probe = brcmstb_nand_probe, + .remove = brcmnand_remove, + .driver = { + .name = "brcmstb_nand", + .pm = &brcmnand_pm_ops, + .of_match_table = brcmstb_nand_of_match, + } +}; +module_platform_driver(brcmstb_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("NAND driver for Broadcom STB chips"); diff --git a/drivers/mtd/nand/raw/brcmnand/iproc_nand.c b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c new file mode 100644 index 000000000000..4c6ae113664d --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c @@ -0,0 +1,160 @@ +/* + * Copyright © 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "brcmnand.h" + +struct iproc_nand_soc { + struct brcmnand_soc soc; + + void __iomem *idm_base; + void __iomem *ext_base; + spinlock_t idm_lock; +}; + +#define IPROC_NAND_CTLR_READY_OFFSET 0x10 +#define IPROC_NAND_CTLR_READY BIT(0) + +#define IPROC_NAND_IO_CTRL_OFFSET 0x00 +#define IPROC_NAND_APB_LE_MODE BIT(24) +#define IPROC_NAND_INT_CTRL_READ_ENABLE BIT(6) + +static bool iproc_nand_intc_ack(struct brcmnand_soc *soc) +{ + struct iproc_nand_soc *priv = + container_of(soc, struct iproc_nand_soc, soc); + void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET; + u32 val = brcmnand_readl(mmio); + + if (val & IPROC_NAND_CTLR_READY) { + brcmnand_writel(IPROC_NAND_CTLR_READY, mmio); + return true; + } + + return false; +} + +static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en) +{ + struct iproc_nand_soc *priv = + container_of(soc, struct iproc_nand_soc, soc); + void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&priv->idm_lock, flags); + + val = brcmnand_readl(mmio); + + if (en) + val |= IPROC_NAND_INT_CTRL_READ_ENABLE; + else + val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE; + + brcmnand_writel(val, mmio); + + spin_unlock_irqrestore(&priv->idm_lock, flags); +} + +static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare, + bool is_param) +{ + struct iproc_nand_soc *priv = + container_of(soc, struct iproc_nand_soc, soc); + void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&priv->idm_lock, flags); + + val = brcmnand_readl(mmio); + + /* + * In the case of BE or when dealing with NAND data, alway configure + * the APB bus to LE mode before accessing the FIFO and back to BE mode + * after the access is done + */ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) || !is_param) { + if (prepare) + val |= IPROC_NAND_APB_LE_MODE; + else + val &= ~IPROC_NAND_APB_LE_MODE; + } else { /* when in LE accessing the parameter page, keep APB in BE */ + val &= ~IPROC_NAND_APB_LE_MODE; + } + + brcmnand_writel(val, mmio); + + spin_unlock_irqrestore(&priv->idm_lock, flags); +} + +static int iproc_nand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iproc_nand_soc *priv; + struct brcmnand_soc *soc; + struct resource *res; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + soc = &priv->soc; + + spin_lock_init(&priv->idm_lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm"); + priv->idm_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->idm_base)) + return PTR_ERR(priv->idm_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext"); + priv->ext_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->ext_base)) + return PTR_ERR(priv->ext_base); + + soc->ctlrdy_ack = iproc_nand_intc_ack; + soc->ctlrdy_set_enabled = iproc_nand_intc_set; + soc->prepare_data_bus = iproc_nand_apb_access; + + return brcmnand_probe(pdev, soc); +} + +static const struct of_device_id iproc_nand_of_match[] = { + { .compatible = "brcm,nand-iproc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, iproc_nand_of_match); + +static struct platform_driver iproc_nand_driver = { + .probe = iproc_nand_probe, + .remove = brcmnand_remove, + .driver = { + .name = "iproc_nand", + .pm = &brcmnand_pm_ops, + .of_match_table = iproc_nand_of_match, + } +}; +module_platform_driver(iproc_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_AUTHOR("Ray Jui"); +MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs"); diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c new file mode 100644 index 000000000000..567ff972d5fc --- /dev/null +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -0,0 +1,871 @@ +/* + * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 + * + * The data sheet for this device can be found at: + * http://wiki.laptop.org/go/Datasheets + * + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2006 David Woodhouse + */ + +#define DEBUG + +#include +#undef DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CAFE_NAND_CTRL1 0x00 +#define CAFE_NAND_CTRL2 0x04 +#define CAFE_NAND_CTRL3 0x08 +#define CAFE_NAND_STATUS 0x0c +#define CAFE_NAND_IRQ 0x10 +#define CAFE_NAND_IRQ_MASK 0x14 +#define CAFE_NAND_DATA_LEN 0x18 +#define CAFE_NAND_ADDR1 0x1c +#define CAFE_NAND_ADDR2 0x20 +#define CAFE_NAND_TIMING1 0x24 +#define CAFE_NAND_TIMING2 0x28 +#define CAFE_NAND_TIMING3 0x2c +#define CAFE_NAND_NONMEM 0x30 +#define CAFE_NAND_ECC_RESULT 0x3C +#define CAFE_NAND_DMA_CTRL 0x40 +#define CAFE_NAND_DMA_ADDR0 0x44 +#define CAFE_NAND_DMA_ADDR1 0x48 +#define CAFE_NAND_ECC_SYN01 0x50 +#define CAFE_NAND_ECC_SYN23 0x54 +#define CAFE_NAND_ECC_SYN45 0x58 +#define CAFE_NAND_ECC_SYN67 0x5c +#define CAFE_NAND_READ_DATA 0x1000 +#define CAFE_NAND_WRITE_DATA 0x2000 + +#define CAFE_GLOBAL_CTRL 0x3004 +#define CAFE_GLOBAL_IRQ 0x3008 +#define CAFE_GLOBAL_IRQ_MASK 0x300c +#define CAFE_NAND_RESET 0x3034 + +/* Missing from the datasheet: bit 19 of CTRL1 sets CE0 vs. CE1 */ +#define CTRL1_CHIPSELECT (1<<19) + +struct cafe_priv { + struct nand_chip nand; + struct pci_dev *pdev; + void __iomem *mmio; + struct rs_control *rs; + uint32_t ctl1; + uint32_t ctl2; + int datalen; + int nr_data; + int data_pos; + int page_addr; + dma_addr_t dmaaddr; + unsigned char *dmabuf; +}; + +static int usedma = 1; +module_param(usedma, int, 0644); + +static int skipbbt = 0; +module_param(skipbbt, int, 0644); + +static int debug = 0; +module_param(debug, int, 0644); + +static int regdebug = 0; +module_param(regdebug, int, 0644); + +static int checkecc = 1; +module_param(checkecc, int, 0644); + +static unsigned int numtimings; +static int timing[3]; +module_param_array(timing, int, &numtimings, 0644); + +static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; + +/* Hrm. Why isn't this already conditional on something in the struct device? */ +#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) + +/* Make it easier to switch to PIO if we need to */ +#define cafe_readl(cafe, addr) readl((cafe)->mmio + CAFE_##addr) +#define cafe_writel(cafe, datum, addr) writel(datum, (cafe)->mmio + CAFE_##addr) + +static int cafe_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cafe_priv *cafe = nand_get_controller_data(chip); + int result = !!(cafe_readl(cafe, NAND_STATUS) & 0x40000000); + uint32_t irqs = cafe_readl(cafe, NAND_IRQ); + + cafe_writel(cafe, irqs, NAND_IRQ); + + cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", + result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ), + cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK)); + + return result; +} + + +static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cafe_priv *cafe = nand_get_controller_data(chip); + + if (usedma) + memcpy(cafe->dmabuf + cafe->datalen, buf, len); + else + memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); + + cafe->datalen += len; + + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", + len, cafe->datalen); +} + +static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cafe_priv *cafe = nand_get_controller_data(chip); + + if (usedma) + memcpy(buf, cafe->dmabuf + cafe->datalen, len); + else + memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); + + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", + len, cafe->datalen); + cafe->datalen += len; +} + +static uint8_t cafe_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cafe_priv *cafe = nand_get_controller_data(chip); + uint8_t d; + + cafe_read_buf(mtd, &d, 1); + cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); + + return d; +} + +static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cafe_priv *cafe = nand_get_controller_data(chip); + int adrbytes = 0; + uint32_t ctl1; + uint32_t doneint = 0x80000000; + + cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", + command, column, page_addr); + + if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { + /* Second half of a command we already calculated */ + cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2); + ctl1 = cafe->ctl1; + cafe->ctl2 &= ~(1<<30); + cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", + cafe->ctl1, cafe->nr_data); + goto do_command; + } + /* Reset ECC engine */ + cafe_writel(cafe, 0, NAND_CTRL2); + + /* Emulate NAND_CMD_READOOB on large-page chips */ + if (mtd->writesize > 512 && + command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* FIXME: Do we need to send read command before sending data + for small-page chips, to position the buffer correctly? */ + + if (column != -1) { + cafe_writel(cafe, column, NAND_ADDR1); + adrbytes = 2; + if (page_addr != -1) + goto write_adr2; + } else if (page_addr != -1) { + cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1); + page_addr >>= 16; + write_adr2: + cafe_writel(cafe, page_addr, NAND_ADDR2); + adrbytes += 2; + if (mtd->size > mtd->writesize << 16) + adrbytes++; + } + + cafe->data_pos = cafe->datalen = 0; + + /* Set command valid bit, mask in the chip select bit */ + ctl1 = 0x80000000 | command | (cafe->ctl1 & CTRL1_CHIPSELECT); + + /* Set RD or WR bits as appropriate */ + if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { + ctl1 |= (1<<26); /* rd */ + /* Always 5 bytes, for now */ + cafe->datalen = 4; + /* And one address cycle -- even for STATUS, since the controller doesn't work without */ + adrbytes = 1; + } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { + ctl1 |= 1<<26; /* rd */ + /* For now, assume just read to end of page */ + cafe->datalen = mtd->writesize + mtd->oobsize - column; + } else if (command == NAND_CMD_SEQIN) + ctl1 |= 1<<25; /* wr */ + + /* Set number of address bytes */ + if (adrbytes) + ctl1 |= ((adrbytes-1)|8) << 27; + + if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { + /* Ignore the first command of a pair; the hardware + deals with them both at once, later */ + cafe->ctl1 = ctl1; + cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", + cafe->ctl1, cafe->datalen); + return; + } + /* RNDOUT and READ0 commands need a following byte */ + if (command == NAND_CMD_RNDOUT) + cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2); + else if (command == NAND_CMD_READ0 && mtd->writesize > 512) + cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2); + + do_command: + cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", + cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2)); + + /* NB: The datasheet lies -- we really should be subtracting 1 here */ + cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN); + cafe_writel(cafe, 0x90000000, NAND_IRQ); + if (usedma && (ctl1 & (3<<25))) { + uint32_t dmactl = 0xc0000000 + cafe->datalen; + /* If WR or RD bits set, set up DMA */ + if (ctl1 & (1<<26)) { + /* It's a read */ + dmactl |= (1<<29); + /* ... so it's done when the DMA is done, not just + the command. */ + doneint = 0x10000000; + } + cafe_writel(cafe, dmactl, NAND_DMA_CTRL); + } + cafe->datalen = 0; + + if (unlikely(regdebug)) { + int i; + printk("About to write command %08x to register 0\n", ctl1); + for (i=4; i< 0x5c; i+=4) + printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); + } + + cafe_writel(cafe, ctl1, NAND_CTRL1); + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + if (1) { + int c; + uint32_t irqs; + + for (c = 500000; c != 0; c--) { + irqs = cafe_readl(cafe, NAND_IRQ); + if (irqs & doneint) + break; + udelay(1); + if (!(c % 100000)) + cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); + cpu_relax(); + } + cafe_writel(cafe, doneint, NAND_IRQ); + cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", + command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ)); + } + + WARN_ON(cafe->ctl2 & (1<<30)); + + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_RNDOUT: + cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); + return; + } + nand_wait_ready(mtd); + cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); +} + +static void cafe_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cafe_priv *cafe = nand_get_controller_data(chip); + + cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); + + /* Mask the appropriate bit into the stored value of ctl1 + which will be used by cafe_nand_cmdfunc() */ + if (chipnr) + cafe->ctl1 |= CTRL1_CHIPSELECT; + else + cafe->ctl1 &= ~CTRL1_CHIPSELECT; +} + +static irqreturn_t cafe_nand_interrupt(int irq, void *id) +{ + struct mtd_info *mtd = id; + struct nand_chip *chip = mtd_to_nand(mtd); + struct cafe_priv *cafe = nand_get_controller_data(chip); + uint32_t irqs = cafe_readl(cafe, NAND_IRQ); + cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ); + if (!irqs) + return IRQ_NONE; + + cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ)); + return IRQ_HANDLED; +} + +static void cafe_nand_bug(struct mtd_info *mtd) +{ + BUG(); +} + +static int cafe_nand_write_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); +} + +/* Don't use -- use nand_read_oob_std for now */ +static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); +} +/** + * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi + * + * The hw generator calculates the error syndrome automatically. Therefore + * we need a special oob layout and handling. + */ +static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct cafe_priv *cafe = nand_get_controller_data(chip); + unsigned int max_bitflips = 0; + + cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", + cafe_readl(cafe, NAND_ECC_RESULT), + cafe_readl(cafe, NAND_ECC_SYN01)); + + nand_read_page_op(chip, page, 0, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { + unsigned short syn[8], pat[4]; + int pos[4]; + u8 *oob = chip->oob_poi; + int i, n; + + for (i=0; i<8; i+=2) { + uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); + syn[i] = cafe->rs->index_of[tmp & 0xfff]; + syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff]; + } + + n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0, + pat); + + for (i = 0; i < n; i++) { + int p = pos[i]; + + /* The 12-bit symbols are mapped to bytes here */ + + if (p > 1374) { + /* out of range */ + n = -1374; + } else if (p == 0) { + /* high four bits do not correspond to data */ + if (pat[i] > 0xff) + n = -2048; + else + buf[0] ^= pat[i]; + } else if (p == 1365) { + buf[2047] ^= pat[i] >> 4; + oob[0] ^= pat[i] << 4; + } else if (p > 1365) { + if ((p & 1) == 1) { + oob[3*p/2 - 2048] ^= pat[i] >> 4; + oob[3*p/2 - 2047] ^= pat[i] << 4; + } else { + oob[3*p/2 - 2049] ^= pat[i] >> 8; + oob[3*p/2 - 2048] ^= pat[i]; + } + } else if ((p & 1) == 1) { + buf[3*p/2] ^= pat[i] >> 4; + buf[3*p/2 + 1] ^= pat[i] << 4; + } else { + buf[3*p/2 - 1] ^= pat[i] >> 8; + buf[3*p/2] ^= pat[i]; + } + } + + if (n < 0) { + dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", + cafe_readl(cafe, NAND_ADDR2) * 2048); + for (i = 0; i < 0x5c; i += 4) + printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); + mtd->ecc_stats.failed++; + } else { + dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); + mtd->ecc_stats.corrected += n; + max_bitflips = max_t(unsigned int, max_bitflips, n); + } + } + + return max_bitflips; +} + +static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int cafe_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = chip->ecc.total; + oobregion->length = mtd->oobsize - chip->ecc.total; + + return 0; +} + +static const struct mtd_ooblayout_ops cafe_ooblayout_ops = { + .ecc = cafe_ooblayout_ecc, + .free = cafe_ooblayout_free, +}; + +/* Ick. The BBT code really ought to be able to work this bit out + for itself from the above, at least for the 2KiB case */ +static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' }; +static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' }; + +static uint8_t cafe_bbt_pattern_512[] = { 0xBB }; +static uint8_t cafe_mirror_pattern_512[] = { 0xBC }; + + +static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_bbt_pattern_2048 +}; + +static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_mirror_pattern_2048 +}; + +static struct nand_bbt_descr cafe_bbt_main_descr_512 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 14, + .len = 1, + .veroffs = 15, + .maxblocks = 4, + .pattern = cafe_bbt_pattern_512 +}; + +static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 14, + .len = 1, + .veroffs = 15, + .maxblocks = 4, + .pattern = cafe_mirror_pattern_512 +}; + + +static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + struct cafe_priv *cafe = nand_get_controller_data(chip); + + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Set up ECC autogeneration */ + cafe->ctl2 |= (1<<30); + + return nand_prog_page_end_op(chip); +} + +static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs) +{ + return 0; +} + +/* F_2[X]/(X**6+X+1) */ +static unsigned short gf64_mul(u8 a, u8 b) +{ + u8 c; + unsigned int i; + + c = 0; + for (i = 0; i < 6; i++) { + if (a & 1) + c ^= b; + a >>= 1; + b <<= 1; + if ((b & 0x40) != 0) + b ^= 0x43; + } + + return c; +} + +/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */ +static u16 gf4096_mul(u16 a, u16 b) +{ + u8 ah, al, bh, bl, ch, cl; + + ah = a >> 6; + al = a & 0x3f; + bh = b >> 6; + bl = b & 0x3f; + + ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl); + cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl); + + return (ch << 6) ^ cl; +} + +static int cafe_mul(int x) +{ + if (x == 0) + return 1; + return gf4096_mul(x, 0xe01); +} + +static int cafe_nand_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct mtd_info *mtd; + struct cafe_priv *cafe; + uint32_t ctrl; + int err = 0; + int old_dma; + + /* Very old versions shared the same PCI ident for all three + functions on the chip. Verify the class too... */ + if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH) + return -ENODEV; + + err = pci_enable_device(pdev); + if (err) + return err; + + pci_set_master(pdev); + + cafe = kzalloc(sizeof(*cafe), GFP_KERNEL); + if (!cafe) + return -ENOMEM; + + mtd = nand_to_mtd(&cafe->nand); + mtd->dev.parent = &pdev->dev; + nand_set_controller_data(&cafe->nand, cafe); + + cafe->pdev = pdev; + cafe->mmio = pci_iomap(pdev, 0, 0); + if (!cafe->mmio) { + dev_warn(&pdev->dev, "failed to iomap\n"); + err = -ENOMEM; + goto out_free_mtd; + } + + cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8); + if (!cafe->rs) { + err = -ENOMEM; + goto out_ior; + } + + cafe->nand.cmdfunc = cafe_nand_cmdfunc; + cafe->nand.dev_ready = cafe_device_ready; + cafe->nand.read_byte = cafe_read_byte; + cafe->nand.read_buf = cafe_read_buf; + cafe->nand.write_buf = cafe_write_buf; + cafe->nand.select_chip = cafe_select_chip; + cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp; + cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp; + + cafe->nand.chip_delay = 0; + + /* Enable the following for a flash based bad block table */ + cafe->nand.bbt_options = NAND_BBT_USE_FLASH; + + if (skipbbt) { + cafe->nand.options |= NAND_SKIP_BBTSCAN; + cafe->nand.block_bad = cafe_nand_block_bad; + } + + if (numtimings && numtimings != 3) { + dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); + } + + if (numtimings == 3) { + cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", + timing[0], timing[1], timing[2]); + } else { + timing[0] = cafe_readl(cafe, NAND_TIMING1); + timing[1] = cafe_readl(cafe, NAND_TIMING2); + timing[2] = cafe_readl(cafe, NAND_TIMING3); + + if (timing[0] | timing[1] | timing[2]) { + cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", + timing[0], timing[1], timing[2]); + } else { + dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); + timing[0] = timing[1] = timing[2] = 0xffffffff; + } + } + + /* Start off by resetting the NAND controller completely */ + cafe_writel(cafe, 1, NAND_RESET); + cafe_writel(cafe, 0, NAND_RESET); + + cafe_writel(cafe, timing[0], NAND_TIMING1); + cafe_writel(cafe, timing[1], NAND_TIMING2); + cafe_writel(cafe, timing[2], NAND_TIMING3); + + cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); + err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, + "CAFE NAND", mtd); + if (err) { + dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); + goto out_ior; + } + + /* Disable master reset, enable NAND clock */ + ctrl = cafe_readl(cafe, GLOBAL_CTRL); + ctrl &= 0xffffeff0; + ctrl |= 0x00007000; + cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); + cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); + cafe_writel(cafe, 0, NAND_DMA_CTRL); + + cafe_writel(cafe, 0x7006, GLOBAL_CTRL); + cafe_writel(cafe, 0x700a, GLOBAL_CTRL); + + /* Enable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); + cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", + cafe_readl(cafe, GLOBAL_CTRL), + cafe_readl(cafe, GLOBAL_IRQ_MASK)); + + /* Do not use the DMA for the nand_scan_ident() */ + old_dma = usedma; + usedma = 0; + + /* Scan to find existence of the device */ + err = nand_scan_ident(mtd, 2, NULL); + if (err) + goto out_irq; + + cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112, + &cafe->dmaaddr, GFP_KERNEL); + if (!cafe->dmabuf) { + err = -ENOMEM; + goto out_irq; + } + + /* Set up DMA address */ + cafe_writel(cafe, lower_32_bits(cafe->dmaaddr), NAND_DMA_ADDR0); + cafe_writel(cafe, upper_32_bits(cafe->dmaaddr), NAND_DMA_ADDR1); + + cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", + cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); + + /* Restore the DMA flag */ + usedma = old_dma; + + cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ + if (mtd->writesize == 2048) + cafe->ctl2 |= 1<<29; /* 2KiB page size */ + + /* Set up ECC according to the type of chip we found */ + mtd_set_ooblayout(mtd, &cafe_ooblayout_ops); + if (mtd->writesize == 2048) { + cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; + } else if (mtd->writesize == 512) { + cafe->nand.bbt_td = &cafe_bbt_main_descr_512; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; + } else { + printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", + mtd->writesize); + goto out_free_dma; + } + cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + cafe->nand.ecc.size = mtd->writesize; + cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.strength = 4; + cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; + cafe->nand.ecc.calculate = (void *)cafe_nand_bug; + cafe->nand.ecc.correct = (void *)cafe_nand_bug; + cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; + cafe->nand.ecc.write_oob = cafe_nand_write_oob; + cafe->nand.ecc.read_page = cafe_nand_read_page; + cafe->nand.ecc.read_oob = cafe_nand_read_oob; + + err = nand_scan_tail(mtd); + if (err) + goto out_free_dma; + + pci_set_drvdata(pdev, mtd); + + mtd->name = "cafe_nand"; + mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); + + goto out; + + out_free_dma: + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + out_irq: + /* Disable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); + free_irq(pdev->irq, mtd); + out_ior: + pci_iounmap(pdev, cafe->mmio); + out_free_mtd: + kfree(cafe); + out: + return err; +} + +static void cafe_nand_remove(struct pci_dev *pdev) +{ + struct mtd_info *mtd = pci_get_drvdata(pdev); + struct nand_chip *chip = mtd_to_nand(mtd); + struct cafe_priv *cafe = nand_get_controller_data(chip); + + /* Disable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); + free_irq(pdev->irq, mtd); + nand_release(mtd); + free_rs(cafe->rs); + pci_iounmap(pdev, cafe->mmio); + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + kfree(cafe); +} + +static const struct pci_device_id cafe_nand_tbl[] = { + { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, + PCI_ANY_ID, PCI_ANY_ID }, + { } +}; + +MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); + +static int cafe_nand_resume(struct pci_dev *pdev) +{ + uint32_t ctrl; + struct mtd_info *mtd = pci_get_drvdata(pdev); + struct nand_chip *chip = mtd_to_nand(mtd); + struct cafe_priv *cafe = nand_get_controller_data(chip); + + /* Start off by resetting the NAND controller completely */ + cafe_writel(cafe, 1, NAND_RESET); + cafe_writel(cafe, 0, NAND_RESET); + cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); + + /* Restore timing configuration */ + cafe_writel(cafe, timing[0], NAND_TIMING1); + cafe_writel(cafe, timing[1], NAND_TIMING2); + cafe_writel(cafe, timing[2], NAND_TIMING3); + + /* Disable master reset, enable NAND clock */ + ctrl = cafe_readl(cafe, GLOBAL_CTRL); + ctrl &= 0xffffeff0; + ctrl |= 0x00007000; + cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); + cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); + cafe_writel(cafe, 0, NAND_DMA_CTRL); + cafe_writel(cafe, 0x7006, GLOBAL_CTRL); + cafe_writel(cafe, 0x700a, GLOBAL_CTRL); + + /* Set up DMA address */ + cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); + if (sizeof(cafe->dmaaddr) > 4) + /* Shift in two parts to shut the compiler up */ + cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); + else + cafe_writel(cafe, 0, NAND_DMA_ADDR1); + + /* Enable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); + return 0; +} + +static struct pci_driver cafe_nand_pci_driver = { + .name = "CAFÉ NAND", + .id_table = cafe_nand_tbl, + .probe = cafe_nand_probe, + .remove = cafe_nand_remove, + .resume = cafe_nand_resume, +}; + +module_pci_driver(cafe_nand_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); diff --git a/drivers/mtd/nand/raw/cmx270_nand.c b/drivers/mtd/nand/raw/cmx270_nand.c new file mode 100644 index 000000000000..02d6751e9efe --- /dev/null +++ b/drivers/mtd/nand/raw/cmx270_nand.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2006 Compulab, Ltd. + * Mike Rapoport + * + * Derived from drivers/mtd/nand/h1910.c (removed in v3.10) + * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * CM-X270 board. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define GPIO_NAND_CS (11) +#define GPIO_NAND_RB (89) + +/* MTD structure for CM-X270 board */ +static struct mtd_info *cmx270_nand_mtd; + +/* remaped IO address of the device */ +static void __iomem *cmx270_nand_io; + +/* + * Define static partitions for flash device + */ +static const struct mtd_partition partition_info[] = { + [0] = { + .name = "cmx270-0", + .offset = 0, + .size = MTDPART_SIZ_FULL + } +}; +#define NUM_PARTITIONS (ARRAY_SIZE(partition_info)) + +static u_char cmx270_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + return (readl(this->IO_ADDR_R) >> 16); +} + +static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd_to_nand(mtd); + + for (i=0; iIO_ADDR_W); +} + +static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd_to_nand(mtd); + + for (i=0; iIO_ADDR_R) >> 16; +} + +static inline void nand_cs_on(void) +{ + gpio_set_value(GPIO_NAND_CS, 0); +} + +static void nand_cs_off(void) +{ + dsb(); + + gpio_set_value(GPIO_NAND_CS, 1); +} + +/* + * hardware specific access to control-lines + */ +static void cmx270_hwcontrol(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip *this = mtd_to_nand(mtd); + unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; + + dsb(); + + if (ctrl & NAND_CTRL_CHANGE) { + if ( ctrl & NAND_ALE ) + nandaddr |= (1 << 3); + else + nandaddr &= ~(1 << 3); + if ( ctrl & NAND_CLE ) + nandaddr |= (1 << 2); + else + nandaddr &= ~(1 << 2); + if ( ctrl & NAND_NCE ) + nand_cs_on(); + else + nand_cs_off(); + } + + dsb(); + this->IO_ADDR_W = (void __iomem*)nandaddr; + if (dat != NAND_CMD_NONE) + writel((dat << 16), this->IO_ADDR_W); + + dsb(); +} + +/* + * read device ready pin + */ +static int cmx270_device_ready(struct mtd_info *mtd) +{ + dsb(); + + return (gpio_get_value(GPIO_NAND_RB)); +} + +/* + * Main initialization routine + */ +static int __init cmx270_init(void) +{ + struct nand_chip *this; + int ret; + + if (!(machine_is_armcore() && cpu_is_pxa27x())) + return -ENODEV; + + ret = gpio_request(GPIO_NAND_CS, "NAND CS"); + if (ret) { + pr_warn("CM-X270: failed to request NAND CS gpio\n"); + return ret; + } + + gpio_direction_output(GPIO_NAND_CS, 1); + + ret = gpio_request(GPIO_NAND_RB, "NAND R/B"); + if (ret) { + pr_warn("CM-X270: failed to request NAND R/B gpio\n"); + goto err_gpio_request; + } + + gpio_direction_input(GPIO_NAND_RB); + + /* Allocate memory for MTD device structure and private data */ + this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); + if (!this) { + ret = -ENOMEM; + goto err_kzalloc; + } + + cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12); + if (!cmx270_nand_io) { + pr_debug("Unable to ioremap NAND device\n"); + ret = -EINVAL; + goto err_ioremap; + } + + cmx270_nand_mtd = nand_to_mtd(this); + + /* Link the private data with the MTD structure */ + cmx270_nand_mtd->owner = THIS_MODULE; + + /* insert callbacks */ + this->IO_ADDR_R = cmx270_nand_io; + this->IO_ADDR_W = cmx270_nand_io; + this->cmd_ctrl = cmx270_hwcontrol; + this->dev_ready = cmx270_device_ready; + + /* 15 us command delay time */ + this->chip_delay = 20; + this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; + + /* read/write functions */ + this->read_byte = cmx270_read_byte; + this->read_buf = cmx270_read_buf; + this->write_buf = cmx270_write_buf; + + /* Scan to find existence of the device */ + ret = nand_scan(cmx270_nand_mtd, 1); + if (ret) { + pr_notice("No NAND device\n"); + goto err_scan; + } + + /* Register the partitions */ + ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL, + partition_info, NUM_PARTITIONS); + if (ret) + goto err_scan; + + /* Return happy */ + return 0; + +err_scan: + iounmap(cmx270_nand_io); +err_ioremap: + kfree(this); +err_kzalloc: + gpio_free(GPIO_NAND_RB); +err_gpio_request: + gpio_free(GPIO_NAND_CS); + + return ret; + +} +module_init(cmx270_init); + +/* + * Clean up routine + */ +static void __exit cmx270_cleanup(void) +{ + /* Release resources, unregister device */ + nand_release(cmx270_nand_mtd); + + gpio_free(GPIO_NAND_RB); + gpio_free(GPIO_NAND_CS); + + iounmap(cmx270_nand_io); + + kfree(mtd_to_nand(cmx270_nand_mtd)); +} +module_exit(cmx270_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Rapoport "); +MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module"); diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c new file mode 100644 index 000000000000..be1f28fc7363 --- /dev/null +++ b/drivers/mtd/nand/raw/cs553x_nand.c @@ -0,0 +1,355 @@ +/* + * (C) 2005, 2006 Red Hat Inc. + * + * Author: David Woodhouse + * Tom Sylla + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This is a device driver for the NAND flash controller found on + * the AMD CS5535/CS5536 companion chipsets for the Geode processor. + * mtd-id for command line partitioning is cs553x_nand_cs[0-3] + * where 0-3 reflects the chip select for NAND. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NR_CS553X_CONTROLLERS 4 + +#define MSR_DIVIL_GLD_CAP 0x51400000 /* DIVIL capabilitiies */ +#define CAP_CS5535 0x2df000ULL +#define CAP_CS5536 0x5df500ULL + +/* NAND Timing MSRs */ +#define MSR_NANDF_DATA 0x5140001b /* NAND Flash Data Timing MSR */ +#define MSR_NANDF_CTL 0x5140001c /* NAND Flash Control Timing */ +#define MSR_NANDF_RSVD 0x5140001d /* Reserved */ + +/* NAND BAR MSRs */ +#define MSR_DIVIL_LBAR_FLSH0 0x51400010 /* Flash Chip Select 0 */ +#define MSR_DIVIL_LBAR_FLSH1 0x51400011 /* Flash Chip Select 1 */ +#define MSR_DIVIL_LBAR_FLSH2 0x51400012 /* Flash Chip Select 2 */ +#define MSR_DIVIL_LBAR_FLSH3 0x51400013 /* Flash Chip Select 3 */ + /* Each made up of... */ +#define FLSH_LBAR_EN (1ULL<<32) +#define FLSH_NOR_NAND (1ULL<<33) /* 1 for NAND */ +#define FLSH_MEM_IO (1ULL<<34) /* 1 for MMIO */ + /* I/O BARs have BASE_ADDR in bits 15:4, IO_MASK in 47:36 */ + /* MMIO BARs have BASE_ADDR in bits 31:12, MEM_MASK in 63:44 */ + +/* Pin function selection MSR (IDE vs. flash on the IDE pins) */ +#define MSR_DIVIL_BALL_OPTS 0x51400015 +#define PIN_OPT_IDE (1<<0) /* 0 for flash, 1 for IDE */ + +/* Registers within the NAND flash controller BAR -- memory mapped */ +#define MM_NAND_DATA 0x00 /* 0 to 0x7ff, in fact */ +#define MM_NAND_CTL 0x800 /* Any even address 0x800-0x80e */ +#define MM_NAND_IO 0x801 /* Any odd address 0x801-0x80f */ +#define MM_NAND_STS 0x810 +#define MM_NAND_ECC_LSB 0x811 +#define MM_NAND_ECC_MSB 0x812 +#define MM_NAND_ECC_COL 0x813 +#define MM_NAND_LAC 0x814 +#define MM_NAND_ECC_CTL 0x815 + +/* Registers within the NAND flash controller BAR -- I/O mapped */ +#define IO_NAND_DATA 0x00 /* 0 to 3, in fact */ +#define IO_NAND_CTL 0x04 +#define IO_NAND_IO 0x05 +#define IO_NAND_STS 0x06 +#define IO_NAND_ECC_CTL 0x08 +#define IO_NAND_ECC_LSB 0x09 +#define IO_NAND_ECC_MSB 0x0a +#define IO_NAND_ECC_COL 0x0b +#define IO_NAND_LAC 0x0c + +#define CS_NAND_CTL_DIST_EN (1<<4) /* Enable NAND Distract interrupt */ +#define CS_NAND_CTL_RDY_INT_MASK (1<<3) /* Enable RDY/BUSY# interrupt */ +#define CS_NAND_CTL_ALE (1<<2) +#define CS_NAND_CTL_CLE (1<<1) +#define CS_NAND_CTL_CE (1<<0) /* Keep low; 1 to reset */ + +#define CS_NAND_STS_FLASH_RDY (1<<3) +#define CS_NAND_CTLR_BUSY (1<<2) +#define CS_NAND_CMD_COMP (1<<1) +#define CS_NAND_DIST_ST (1<<0) + +#define CS_NAND_ECC_PARITY (1<<2) +#define CS_NAND_ECC_CLRECC (1<<1) +#define CS_NAND_ECC_ENECC (1<<0) + +static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + while (unlikely(len > 0x800)) { + memcpy_fromio(buf, this->IO_ADDR_R, 0x800); + buf += 0x800; + len -= 0x800; + } + memcpy_fromio(buf, this->IO_ADDR_R, len); +} + +static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + while (unlikely(len > 0x800)) { + memcpy_toio(this->IO_ADDR_R, buf, 0x800); + buf += 0x800; + len -= 0x800; + } + memcpy_toio(this->IO_ADDR_R, buf, len); +} + +static unsigned char cs553x_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + return readb(this->IO_ADDR_R); +} + +static void cs553x_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int i = 100000; + + while (i && readb(this->IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) { + udelay(1); + i--; + } + writeb(byte, this->IO_ADDR_W + 0x801); +} + +static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *this = mtd_to_nand(mtd); + void __iomem *mmio_base = this->IO_ADDR_R; + if (ctrl & NAND_CTRL_CHANGE) { + unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01; + writeb(ctl, mmio_base + MM_NAND_CTL); + } + if (cmd != NAND_CMD_NONE) + cs553x_write_byte(mtd, cmd); +} + +static int cs553x_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + void __iomem *mmio_base = this->IO_ADDR_R; + unsigned char foo = readb(mmio_base + MM_NAND_STS); + + return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY); +} + +static void cs_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = mtd_to_nand(mtd); + void __iomem *mmio_base = this->IO_ADDR_R; + + writeb(0x07, mmio_base + MM_NAND_ECC_CTL); +} + +static int cs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + uint32_t ecc; + struct nand_chip *this = mtd_to_nand(mtd); + void __iomem *mmio_base = this->IO_ADDR_R; + + ecc = readl(mmio_base + MM_NAND_STS); + + ecc_code[1] = ecc >> 8; + ecc_code[0] = ecc >> 16; + ecc_code[2] = ecc >> 24; + return 0; +} + +static struct mtd_info *cs553x_mtd[4]; + +static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) +{ + int err = 0; + struct nand_chip *this; + struct mtd_info *new_mtd; + + printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr); + + if (!mmio) { + printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n"); + return -ENXIO; + } + + /* Allocate memory for MTD device structure and private data */ + this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); + if (!this) { + err = -ENOMEM; + goto out; + } + + new_mtd = nand_to_mtd(this); + + /* Link the private data with the MTD structure */ + new_mtd->owner = THIS_MODULE; + + /* map physical address */ + this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096); + if (!this->IO_ADDR_R) { + printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr); + err = -EIO; + goto out_mtd; + } + + this->cmd_ctrl = cs553x_hwcontrol; + this->dev_ready = cs553x_device_ready; + this->read_byte = cs553x_read_byte; + this->read_buf = cs553x_read_buf; + this->write_buf = cs553x_write_buf; + + this->chip_delay = 0; + + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 256; + this->ecc.bytes = 3; + this->ecc.hwctl = cs_enable_hwecc; + this->ecc.calculate = cs_calculate_ecc; + this->ecc.correct = nand_correct_data; + this->ecc.strength = 1; + + /* Enable the following for a flash based bad block table */ + this->bbt_options = NAND_BBT_USE_FLASH; + + new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs); + if (!new_mtd->name) { + err = -ENOMEM; + goto out_ior; + } + + /* Scan to find existence of the device */ + err = nand_scan(new_mtd, 1); + if (err) + goto out_free; + + cs553x_mtd[cs] = new_mtd; + goto out; + +out_free: + kfree(new_mtd->name); +out_ior: + iounmap(this->IO_ADDR_R); +out_mtd: + kfree(this); +out: + return err; +} + +static int is_geode(void) +{ + /* These are the CPUs which will have a CS553[56] companion chip */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + boot_cpu_data.x86 == 5 && + boot_cpu_data.x86_model == 10) + return 1; /* Geode LX */ + + if ((boot_cpu_data.x86_vendor == X86_VENDOR_NSC || + boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX) && + boot_cpu_data.x86 == 5 && + boot_cpu_data.x86_model == 5) + return 1; /* Geode GX (née GX2) */ + + return 0; +} + +static int __init cs553x_init(void) +{ + int err = -ENXIO; + int i; + uint64_t val; + + /* If the CPU isn't a Geode GX or LX, abort */ + if (!is_geode()) + return -ENXIO; + + /* If it doesn't have the CS553[56], abort */ + rdmsrl(MSR_DIVIL_GLD_CAP, val); + val &= ~0xFFULL; + if (val != CAP_CS5535 && val != CAP_CS5536) + return -ENXIO; + + /* If it doesn't have the NAND controller enabled, abort */ + rdmsrl(MSR_DIVIL_BALL_OPTS, val); + if (val & PIN_OPT_IDE) { + printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n"); + return -ENXIO; + } + + for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { + rdmsrl(MSR_DIVIL_LBAR_FLSH0 + i, val); + + if ((val & (FLSH_LBAR_EN|FLSH_NOR_NAND)) == (FLSH_LBAR_EN|FLSH_NOR_NAND)) + err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF); + } + + /* Register all devices together here. This means we can easily hack it to + do mtdconcat etc. if we want to. */ + for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { + if (cs553x_mtd[i]) { + /* If any devices registered, return success. Else the last error. */ + mtd_device_parse_register(cs553x_mtd[i], NULL, NULL, + NULL, 0); + err = 0; + } + } + + return err; +} + +module_init(cs553x_init); + +static void __exit cs553x_cleanup(void) +{ + int i; + + for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { + struct mtd_info *mtd = cs553x_mtd[i]; + struct nand_chip *this; + void __iomem *mmio_base; + + if (!mtd) + continue; + + this = mtd_to_nand(mtd); + mmio_base = this->IO_ADDR_R; + + /* Release resources, unregister device */ + nand_release(mtd); + kfree(mtd->name); + cs553x_mtd[i] = NULL; + + /* unmap physical address */ + iounmap(mmio_base); + + /* Free the MTD device structure */ + kfree(this); + } +} + +module_exit(cs553x_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("NAND controller driver for AMD CS5535/CS5536 companion chip"); diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c new file mode 100644 index 000000000000..ccc8c43abcff --- /dev/null +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -0,0 +1,879 @@ +/* + * davinci_nand.c - NAND Flash Driver for DaVinci family chips + * + * Copyright © 2006 Texas Instruments. + * + * Port to 2.6.23 Copyright © 2008 by: + * Sander Huijsen + * Troy Kisky + * Dirk Behme + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * This is a device driver for the NAND flash controller found on the + * various DaVinci family chips. It handles up to four SoC chipselects, + * and some flavors of secondary chipselect (e.g. based on A12) as used + * with multichip packages. + * + * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC + * available on chips like the DM355 and OMAP-L137 and needed with the + * more error-prone MLC NAND chips. + * + * This driver assumes EM_WAIT connects all the NAND devices' RDY/nBUSY + * outputs in a "wire-AND" configuration, with no per-chip signals. + */ +struct davinci_nand_info { + struct nand_chip chip; + + struct device *dev; + struct clk *clk; + + bool is_readmode; + + void __iomem *base; + void __iomem *vaddr; + + uint32_t ioaddr; + uint32_t current_cs; + + uint32_t mask_chipsel; + uint32_t mask_ale; + uint32_t mask_cle; + + uint32_t core_chipsel; + + struct davinci_aemif_timing *timing; +}; + +static DEFINE_SPINLOCK(davinci_nand_lock); +static bool ecc4_busy; + +static inline struct davinci_nand_info *to_davinci_nand(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct davinci_nand_info, chip); +} + +static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info, + int offset) +{ + return __raw_readl(info->base + offset); +} + +static inline void davinci_nand_writel(struct davinci_nand_info *info, + int offset, unsigned long value) +{ + __raw_writel(value, info->base + offset); +} + +/*----------------------------------------------------------------------*/ + +/* + * Access to hardware control lines: ALE, CLE, secondary chipselect. + */ + +static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + uint32_t addr = info->current_cs; + struct nand_chip *nand = mtd_to_nand(mtd); + + /* Did the control lines change? */ + if (ctrl & NAND_CTRL_CHANGE) { + if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE) + addr |= info->mask_cle; + else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE) + addr |= info->mask_ale; + + nand->IO_ADDR_W = (void __iomem __force *)addr; + } + + if (cmd != NAND_CMD_NONE) + iowrite8(cmd, nand->IO_ADDR_W); +} + +static void nand_davinci_select_chip(struct mtd_info *mtd, int chip) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + uint32_t addr = info->ioaddr; + + /* maybe kick in a second chipselect */ + if (chip > 0) + addr |= info->mask_chipsel; + info->current_cs = addr; + + info->chip.IO_ADDR_W = (void __iomem __force *)addr; + info->chip.IO_ADDR_R = info->chip.IO_ADDR_W; +} + +/*----------------------------------------------------------------------*/ + +/* + * 1-bit hardware ECC ... context maintained for each core chipselect + */ + +static inline uint32_t nand_davinci_readecc_1bit(struct mtd_info *mtd) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + + return davinci_nand_readl(info, NANDF1ECC_OFFSET + + 4 * info->core_chipsel); +} + +static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode) +{ + struct davinci_nand_info *info; + uint32_t nandcfr; + unsigned long flags; + + info = to_davinci_nand(mtd); + + /* Reset ECC hardware */ + nand_davinci_readecc_1bit(mtd); + + spin_lock_irqsave(&davinci_nand_lock, flags); + + /* Restart ECC hardware */ + nandcfr = davinci_nand_readl(info, NANDFCR_OFFSET); + nandcfr |= BIT(8 + info->core_chipsel); + davinci_nand_writel(info, NANDFCR_OFFSET, nandcfr); + + spin_unlock_irqrestore(&davinci_nand_lock, flags); +} + +/* + * Read hardware ECC value and pack into three bytes + */ +static int nand_davinci_calculate_1bit(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + unsigned int ecc_val = nand_davinci_readecc_1bit(mtd); + unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4); + + /* invert so that erased block ecc is correct */ + ecc24 = ~ecc24; + ecc_code[0] = (u_char)(ecc24); + ecc_code[1] = (u_char)(ecc24 >> 8); + ecc_code[2] = (u_char)(ecc24 >> 16); + + return 0; +} + +static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) | + (read_ecc[2] << 16); + uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) | + (calc_ecc[2] << 16); + uint32_t diff = eccCalc ^ eccNand; + + if (diff) { + if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { + /* Correctable error */ + if ((diff >> (12 + 3)) < chip->ecc.size) { + dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7); + return 1; + } else { + return -EBADMSG; + } + } else if (!(diff & (diff - 1))) { + /* Single bit ECC error in the ECC itself, + * nothing to fix */ + return 1; + } else { + /* Uncorrectable error */ + return -EBADMSG; + } + + } + return 0; +} + +/*----------------------------------------------------------------------*/ + +/* + * 4-bit hardware ECC ... context maintained over entire AEMIF + * + * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME + * since that forces use of a problematic "infix OOB" layout. + * Among other things, it trashes manufacturer bad block markers. + * Also, and specific to this hardware, it ECC-protects the "prepad" + * in the OOB ... while having ECC protection for parts of OOB would + * seem useful, the current MTD stack sometimes wants to update the + * OOB without recomputing ECC. + */ + +static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + unsigned long flags; + u32 val; + + /* Reset ECC hardware */ + davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); + + spin_lock_irqsave(&davinci_nand_lock, flags); + + /* Start 4-bit ECC calculation for read/write */ + val = davinci_nand_readl(info, NANDFCR_OFFSET); + val &= ~(0x03 << 4); + val |= (info->core_chipsel << 4) | BIT(12); + davinci_nand_writel(info, NANDFCR_OFFSET, val); + + info->is_readmode = (mode == NAND_ECC_READ); + + spin_unlock_irqrestore(&davinci_nand_lock, flags); +} + +/* Read raw ECC code after writing to NAND. */ +static void +nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4]) +{ + const u32 mask = 0x03ff03ff; + + code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask; + code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask; + code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask; + code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask; +} + +/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */ +static int nand_davinci_calculate_4bit(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + u32 raw_ecc[4], *p; + unsigned i; + + /* After a read, terminate ECC calculation by a dummy read + * of some 4-bit ECC register. ECC covers everything that + * was read; correct() just uses the hardware state, so + * ecc_code is not needed. + */ + if (info->is_readmode) { + davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); + return 0; + } + + /* Pack eight raw 10-bit ecc values into ten bytes, making + * two passes which each convert four values (in upper and + * lower halves of two 32-bit words) into five bytes. The + * ROM boot loader uses this same packing scheme. + */ + nand_davinci_readecc_4bit(info, raw_ecc); + for (i = 0, p = raw_ecc; i < 2; i++, p += 2) { + *ecc_code++ = p[0] & 0xff; + *ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc); + *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0); + *ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0); + *ecc_code++ = (p[1] >> 18) & 0xff; + } + + return 0; +} + +/* Correct up to 4 bits in data we just read, using state left in the + * hardware plus the ecc_code computed when it was first written. + */ +static int nand_davinci_correct_4bit(struct mtd_info *mtd, + u_char *data, u_char *ecc_code, u_char *null) +{ + int i; + struct davinci_nand_info *info = to_davinci_nand(mtd); + unsigned short ecc10[8]; + unsigned short *ecc16; + u32 syndrome[4]; + u32 ecc_state; + unsigned num_errors, corrected; + unsigned long timeo; + + /* Unpack ten bytes into eight 10 bit values. We know we're + * little-endian, and use type punning for less shifting/masking. + */ + if (WARN_ON(0x01 & (unsigned) ecc_code)) + return -EINVAL; + ecc16 = (unsigned short *)ecc_code; + + ecc10[0] = (ecc16[0] >> 0) & 0x3ff; + ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0); + ecc10[2] = (ecc16[1] >> 4) & 0x3ff; + ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc); + ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300); + ecc10[5] = (ecc16[3] >> 2) & 0x3ff; + ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0); + ecc10[7] = (ecc16[4] >> 6) & 0x3ff; + + /* Tell ECC controller about the expected ECC codes. */ + for (i = 7; i >= 0; i--) + davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]); + + /* Allow time for syndrome calculation ... then read it. + * A syndrome of all zeroes 0 means no detected errors. + */ + davinci_nand_readl(info, NANDFSR_OFFSET); + nand_davinci_readecc_4bit(info, syndrome); + if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) + return 0; + + /* + * Clear any previous address calculation by doing a dummy read of an + * error address register. + */ + davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET); + + /* Start address calculation, and wait for it to complete. + * We _could_ start reading more data while this is working, + * to speed up the overall page read. + */ + davinci_nand_writel(info, NANDFCR_OFFSET, + davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13)); + + /* + * ECC_STATE field reads 0x3 (Error correction complete) immediately + * after setting the 4BITECC_ADD_CALC_START bit. So if you immediately + * begin trying to poll for the state, you may fall right out of your + * loop without any of the correction calculations having taken place. + * The recommendation from the hardware team is to initially delay as + * long as ECC_STATE reads less than 4. After that, ECC HW has entered + * correction state. + */ + timeo = jiffies + usecs_to_jiffies(100); + do { + ecc_state = (davinci_nand_readl(info, + NANDFSR_OFFSET) >> 8) & 0x0f; + cpu_relax(); + } while ((ecc_state < 4) && time_before(jiffies, timeo)); + + for (;;) { + u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET); + + switch ((fsr >> 8) & 0x0f) { + case 0: /* no error, should not happen */ + davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); + return 0; + case 1: /* five or more errors detected */ + davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); + return -EBADMSG; + case 2: /* error addresses computed */ + case 3: + num_errors = 1 + ((fsr >> 16) & 0x03); + goto correct; + default: /* still working on it */ + cpu_relax(); + continue; + } + } + +correct: + /* correct each error */ + for (i = 0, corrected = 0; i < num_errors; i++) { + int error_address, error_value; + + if (i > 1) { + error_address = davinci_nand_readl(info, + NAND_ERR_ADD2_OFFSET); + error_value = davinci_nand_readl(info, + NAND_ERR_ERRVAL2_OFFSET); + } else { + error_address = davinci_nand_readl(info, + NAND_ERR_ADD1_OFFSET); + error_value = davinci_nand_readl(info, + NAND_ERR_ERRVAL1_OFFSET); + } + + if (i & 1) { + error_address >>= 16; + error_value >>= 16; + } + error_address &= 0x3ff; + error_address = (512 + 7) - error_address; + + if (error_address < 512) { + data[error_address] ^= error_value; + corrected++; + } + } + + return corrected; +} + +/*----------------------------------------------------------------------*/ + +/* + * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's + * how these chips are normally wired. This translates to both 8 and 16 + * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4). + * + * For now we assume that configuration, or any other one which ignores + * the two LSBs for NAND access ... so we can issue 32-bit reads/writes + * and have that transparently morphed into multiple NAND operations. + */ +static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) + ioread32_rep(chip->IO_ADDR_R, buf, len >> 2); + else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) + ioread16_rep(chip->IO_ADDR_R, buf, len >> 1); + else + ioread8_rep(chip->IO_ADDR_R, buf, len); +} + +static void nand_davinci_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) + iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2); + else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) + iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1); + else + iowrite8_rep(chip->IO_ADDR_R, buf, len); +} + +/* + * Check hardware register for wait status. Returns 1 if device is ready, + * 0 if it is still busy. + */ +static int nand_davinci_dev_ready(struct mtd_info *mtd) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + + return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0); +} + +/*----------------------------------------------------------------------*/ + +/* An ECC layout for using 4-bit ECC with small-page flash, storing + * ten ECC bytes plus the manufacturer's bad block marker byte, and + * and not overlapping the default BBT markers. + */ +static int hwecc4_ooblayout_small_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 2) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + oobregion->length = 5; + } else if (section == 1) { + oobregion->offset = 6; + oobregion->length = 2; + } else { + oobregion->offset = 13; + oobregion->length = 3; + } + + return 0; +} + +static int hwecc4_ooblayout_small_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + if (!section) { + oobregion->offset = 8; + oobregion->length = 5; + } else { + oobregion->offset = 16; + oobregion->length = mtd->oobsize - 16; + } + + return 0; +} + +static const struct mtd_ooblayout_ops hwecc4_small_ooblayout_ops = { + .ecc = hwecc4_ooblayout_small_ecc, + .free = hwecc4_ooblayout_small_free, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id davinci_nand_of_match[] = { + {.compatible = "ti,davinci-nand", }, + {.compatible = "ti,keystone-nand", }, + {}, +}; +MODULE_DEVICE_TABLE(of, davinci_nand_of_match); + +static struct davinci_nand_pdata + *nand_davinci_get_pdata(struct platform_device *pdev) +{ + if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) { + struct davinci_nand_pdata *pdata; + const char *mode; + u32 prop; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct davinci_nand_pdata), + GFP_KERNEL); + pdev->dev.platform_data = pdata; + if (!pdata) + return ERR_PTR(-ENOMEM); + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-chipselect", &prop)) + pdev->id = prop; + else + return ERR_PTR(-EINVAL); + + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-mask-ale", &prop)) + pdata->mask_ale = prop; + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-mask-cle", &prop)) + pdata->mask_cle = prop; + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-mask-chipsel", &prop)) + pdata->mask_chipsel = prop; + if (!of_property_read_string(pdev->dev.of_node, + "ti,davinci-ecc-mode", &mode)) { + if (!strncmp("none", mode, 4)) + pdata->ecc_mode = NAND_ECC_NONE; + if (!strncmp("soft", mode, 4)) + pdata->ecc_mode = NAND_ECC_SOFT; + if (!strncmp("hw", mode, 2)) + pdata->ecc_mode = NAND_ECC_HW; + } + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-ecc-bits", &prop)) + pdata->ecc_bits = prop; + + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-nand-buswidth", &prop) && prop == 16) + pdata->options |= NAND_BUSWIDTH_16; + + if (of_property_read_bool(pdev->dev.of_node, + "ti,davinci-nand-use-bbt")) + pdata->bbt_options = NAND_BBT_USE_FLASH; + + /* + * Since kernel v4.8, this driver has been fixed to enable + * use of 4-bit hardware ECC with subpages and verified on + * TI's keystone EVMs (K2L, K2HK and K2E). + * However, in the interest of not breaking systems using + * existing UBI partitions, sub-page writes are not being + * (re)enabled. If you want to use subpage writes on Keystone + * platforms (i.e. do not have any existing UBI partitions), + * then use "ti,davinci-nand" as the compatible in your + * device-tree file. + */ + if (of_device_is_compatible(pdev->dev.of_node, + "ti,keystone-nand")) { + pdata->options |= NAND_NO_SUBPAGE_WRITE; + } + } + + return dev_get_platdata(&pdev->dev); +} +#else +static struct davinci_nand_pdata + *nand_davinci_get_pdata(struct platform_device *pdev) +{ + return dev_get_platdata(&pdev->dev); +} +#endif + +static int nand_davinci_probe(struct platform_device *pdev) +{ + struct davinci_nand_pdata *pdata; + struct davinci_nand_info *info; + struct resource *res1; + struct resource *res2; + void __iomem *vaddr; + void __iomem *base; + int ret; + uint32_t val; + struct mtd_info *mtd; + + pdata = nand_davinci_get_pdata(pdev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + /* insist on board-specific configuration */ + if (!pdata) + return -ENODEV; + + /* which external chipselect will we be managing? */ + if (pdev->id < 0 || pdev->id > 3) + return -ENODEV; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + platform_set_drvdata(pdev, info); + + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res1 || !res2) { + dev_err(&pdev->dev, "resource missing\n"); + return -EINVAL; + } + + vaddr = devm_ioremap_resource(&pdev->dev, res1); + if (IS_ERR(vaddr)) + return PTR_ERR(vaddr); + + /* + * This registers range is used to setup NAND settings. In case with + * TI AEMIF driver, the same memory address range is requested already + * by AEMIF, so we cannot request it twice, just ioremap. + * The AEMIF and NAND drivers not use the same registers in this range. + */ + base = devm_ioremap(&pdev->dev, res2->start, resource_size(res2)); + if (!base) { + dev_err(&pdev->dev, "ioremap failed for resource %pR\n", res2); + return -EADDRNOTAVAIL; + } + + info->dev = &pdev->dev; + info->base = base; + info->vaddr = vaddr; + + mtd = nand_to_mtd(&info->chip); + mtd->dev.parent = &pdev->dev; + nand_set_flash_node(&info->chip, pdev->dev.of_node); + + info->chip.IO_ADDR_R = vaddr; + info->chip.IO_ADDR_W = vaddr; + info->chip.chip_delay = 0; + info->chip.select_chip = nand_davinci_select_chip; + + /* options such as NAND_BBT_USE_FLASH */ + info->chip.bbt_options = pdata->bbt_options; + /* options such as 16-bit widths */ + info->chip.options = pdata->options; + info->chip.bbt_td = pdata->bbt_td; + info->chip.bbt_md = pdata->bbt_md; + info->timing = pdata->timing; + + info->ioaddr = (uint32_t __force) vaddr; + + info->current_cs = info->ioaddr; + info->core_chipsel = pdev->id; + info->mask_chipsel = pdata->mask_chipsel; + + /* use nandboot-capable ALE/CLE masks by default */ + info->mask_ale = pdata->mask_ale ? : MASK_ALE; + info->mask_cle = pdata->mask_cle ? : MASK_CLE; + + /* Set address of hardware control function */ + info->chip.cmd_ctrl = nand_davinci_hwcontrol; + info->chip.dev_ready = nand_davinci_dev_ready; + + /* Speed up buffer I/O */ + info->chip.read_buf = nand_davinci_read_buf; + info->chip.write_buf = nand_davinci_write_buf; + + /* Use board-specific ECC config */ + info->chip.ecc.mode = pdata->ecc_mode; + + ret = -EINVAL; + + info->clk = devm_clk_get(&pdev->dev, "aemif"); + if (IS_ERR(info->clk)) { + ret = PTR_ERR(info->clk); + dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(info->clk); + if (ret < 0) { + dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n", + ret); + goto err_clk_enable; + } + + spin_lock_irq(&davinci_nand_lock); + + /* put CSxNAND into NAND mode */ + val = davinci_nand_readl(info, NANDFCR_OFFSET); + val |= BIT(info->core_chipsel); + davinci_nand_writel(info, NANDFCR_OFFSET, val); + + spin_unlock_irq(&davinci_nand_lock); + + /* Scan to find existence of the device(s) */ + ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL); + if (ret < 0) { + dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); + goto err; + } + + switch (info->chip.ecc.mode) { + case NAND_ECC_NONE: + pdata->ecc_bits = 0; + break; + case NAND_ECC_SOFT: + pdata->ecc_bits = 0; + /* + * This driver expects Hamming based ECC when ecc_mode is set + * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to + * avoid adding an extra ->ecc_algo field to + * davinci_nand_pdata. + */ + info->chip.ecc.algo = NAND_ECC_HAMMING; + break; + case NAND_ECC_HW: + if (pdata->ecc_bits == 4) { + /* No sanity checks: CPUs must support this, + * and the chips may not use NAND_BUSWIDTH_16. + */ + + /* No sharing 4-bit hardware between chipselects yet */ + spin_lock_irq(&davinci_nand_lock); + if (ecc4_busy) + ret = -EBUSY; + else + ecc4_busy = true; + spin_unlock_irq(&davinci_nand_lock); + + if (ret == -EBUSY) + return ret; + + info->chip.ecc.calculate = nand_davinci_calculate_4bit; + info->chip.ecc.correct = nand_davinci_correct_4bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; + info->chip.ecc.bytes = 10; + info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; + info->chip.ecc.algo = NAND_ECC_BCH; + } else { + /* 1bit ecc hamming */ + info->chip.ecc.calculate = nand_davinci_calculate_1bit; + info->chip.ecc.correct = nand_davinci_correct_1bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; + info->chip.ecc.bytes = 3; + info->chip.ecc.algo = NAND_ECC_HAMMING; + } + info->chip.ecc.size = 512; + info->chip.ecc.strength = pdata->ecc_bits; + break; + default: + return -EINVAL; + } + + /* Update ECC layout if needed ... for 1-bit HW ECC, the default + * is OK, but it allocates 6 bytes when only 3 are needed (for + * each 512 bytes). For the 4-bit HW ECC, that default is not + * usable: 10 bytes are needed, not 6. + */ + if (pdata->ecc_bits == 4) { + int chunks = mtd->writesize / 512; + + if (!chunks || mtd->oobsize < 16) { + dev_dbg(&pdev->dev, "too small\n"); + ret = -EINVAL; + goto err; + } + + /* For small page chips, preserve the manufacturer's + * badblock marking data ... and make sure a flash BBT + * table marker fits in the free bytes. + */ + if (chunks == 1) { + mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops); + } else if (chunks == 4 || chunks == 8) { + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; + } else { + ret = -EIO; + goto err; + } + } + + ret = nand_scan_tail(mtd); + if (ret < 0) + goto err; + + if (pdata->parts) + ret = mtd_device_parse_register(mtd, NULL, NULL, + pdata->parts, pdata->nr_parts); + else + ret = mtd_device_register(mtd, NULL, 0); + if (ret < 0) + goto err; + + val = davinci_nand_readl(info, NRCSR_OFFSET); + dev_info(&pdev->dev, "controller rev. %d.%d\n", + (val >> 8) & 0xff, val & 0xff); + + return 0; + +err: + clk_disable_unprepare(info->clk); + +err_clk_enable: + spin_lock_irq(&davinci_nand_lock); + if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) + ecc4_busy = false; + spin_unlock_irq(&davinci_nand_lock); + return ret; +} + +static int nand_davinci_remove(struct platform_device *pdev) +{ + struct davinci_nand_info *info = platform_get_drvdata(pdev); + + spin_lock_irq(&davinci_nand_lock); + if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) + ecc4_busy = false; + spin_unlock_irq(&davinci_nand_lock); + + nand_release(nand_to_mtd(&info->chip)); + + clk_disable_unprepare(info->clk); + + return 0; +} + +static struct platform_driver nand_davinci_driver = { + .probe = nand_davinci_probe, + .remove = nand_davinci_remove, + .driver = { + .name = "davinci_nand", + .of_match_table = of_match_ptr(davinci_nand_of_match), + }, +}; +MODULE_ALIAS("platform:davinci_nand"); + +module_platform_driver(nand_davinci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("Davinci NAND flash driver"); + diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c new file mode 100644 index 000000000000..313c7f50621b --- /dev/null +++ b/drivers/mtd/nand/raw/denali.c @@ -0,0 +1,1408 @@ +/* + * NAND Flash Controller Device Driver + * Copyright © 2009-2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "denali.h" + +MODULE_LICENSE("GPL"); + +#define DENALI_NAND_NAME "denali-nand" + +/* for Indexed Addressing */ +#define DENALI_INDEXED_CTRL 0x00 +#define DENALI_INDEXED_DATA 0x10 + +#define DENALI_MAP00 (0 << 26) /* direct access to buffer */ +#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */ +#define DENALI_MAP10 (2 << 26) /* high-level control plane */ +#define DENALI_MAP11 (3 << 26) /* direct controller access */ + +/* MAP11 access cycle type */ +#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */ +#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */ +#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */ + +/* MAP10 commands */ +#define DENALI_ERASE 0x01 + +#define DENALI_BANK(denali) ((denali)->active_bank << 24) + +#define DENALI_INVALID_BANK -1 +#define DENALI_NR_BANKS 4 + +/* + * The bus interface clock, clk_x, is phase aligned with the core clock. The + * clk_x is an integral multiple N of the core clk. The value N is configured + * at IP delivery time, and its available value is 4, 5, or 6. We need to align + * to the largest value to make it work with any possible configuration. + */ +#define DENALI_CLK_X_MULT 6 + +static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand); +} + +/* + * Direct Addressing - the slave address forms the control information (command + * type, bank, block, and page address). The slave data is the actual data to + * be transferred. This mode requires 28 bits of address region allocated. + */ +static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr) +{ + return ioread32(denali->host + addr); +} + +static void denali_direct_write(struct denali_nand_info *denali, u32 addr, + u32 data) +{ + iowrite32(data, denali->host + addr); +} + +/* + * Indexed Addressing - address translation module intervenes in passing the + * control information. This mode reduces the required address range. The + * control information and transferred data are latched by the registers in + * the translation module. + */ +static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr) +{ + iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); + return ioread32(denali->host + DENALI_INDEXED_DATA); +} + +static void denali_indexed_write(struct denali_nand_info *denali, u32 addr, + u32 data) +{ + iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); + iowrite32(data, denali->host + DENALI_INDEXED_DATA); +} + +/* + * Use the configuration feature register to determine the maximum number of + * banks that the hardware supports. + */ +static void denali_detect_max_banks(struct denali_nand_info *denali) +{ + uint32_t features = ioread32(denali->reg + FEATURES); + + denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features); + + /* the encoding changed from rev 5.0 to 5.1 */ + if (denali->revision < 0x0501) + denali->max_banks <<= 1; +} + +static void denali_enable_irq(struct denali_nand_info *denali) +{ + int i; + + for (i = 0; i < DENALI_NR_BANKS; i++) + iowrite32(U32_MAX, denali->reg + INTR_EN(i)); + iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE); +} + +static void denali_disable_irq(struct denali_nand_info *denali) +{ + int i; + + for (i = 0; i < DENALI_NR_BANKS; i++) + iowrite32(0, denali->reg + INTR_EN(i)); + iowrite32(0, denali->reg + GLOBAL_INT_ENABLE); +} + +static void denali_clear_irq(struct denali_nand_info *denali, + int bank, uint32_t irq_status) +{ + /* write one to clear bits */ + iowrite32(irq_status, denali->reg + INTR_STATUS(bank)); +} + +static void denali_clear_irq_all(struct denali_nand_info *denali) +{ + int i; + + for (i = 0; i < DENALI_NR_BANKS; i++) + denali_clear_irq(denali, i, U32_MAX); +} + +static irqreturn_t denali_isr(int irq, void *dev_id) +{ + struct denali_nand_info *denali = dev_id; + irqreturn_t ret = IRQ_NONE; + uint32_t irq_status; + int i; + + spin_lock(&denali->irq_lock); + + for (i = 0; i < DENALI_NR_BANKS; i++) { + irq_status = ioread32(denali->reg + INTR_STATUS(i)); + if (irq_status) + ret = IRQ_HANDLED; + + denali_clear_irq(denali, i, irq_status); + + if (i != denali->active_bank) + continue; + + denali->irq_status |= irq_status; + + if (denali->irq_status & denali->irq_mask) + complete(&denali->complete); + } + + spin_unlock(&denali->irq_lock); + + return ret; +} + +static void denali_reset_irq(struct denali_nand_info *denali) +{ + unsigned long flags; + + spin_lock_irqsave(&denali->irq_lock, flags); + denali->irq_status = 0; + denali->irq_mask = 0; + spin_unlock_irqrestore(&denali->irq_lock, flags); +} + +static uint32_t denali_wait_for_irq(struct denali_nand_info *denali, + uint32_t irq_mask) +{ + unsigned long time_left, flags; + uint32_t irq_status; + + spin_lock_irqsave(&denali->irq_lock, flags); + + irq_status = denali->irq_status; + + if (irq_mask & irq_status) { + /* return immediately if the IRQ has already happened. */ + spin_unlock_irqrestore(&denali->irq_lock, flags); + return irq_status; + } + + denali->irq_mask = irq_mask; + reinit_completion(&denali->complete); + spin_unlock_irqrestore(&denali->irq_lock, flags); + + time_left = wait_for_completion_timeout(&denali->complete, + msecs_to_jiffies(1000)); + if (!time_left) { + dev_err(denali->dev, "timeout while waiting for irq 0x%x\n", + irq_mask); + return 0; + } + + return denali->irq_status; +} + +static uint32_t denali_check_irq(struct denali_nand_info *denali) +{ + unsigned long flags; + uint32_t irq_status; + + spin_lock_irqsave(&denali->irq_lock, flags); + irq_status = denali->irq_status; + spin_unlock_irqrestore(&denali->irq_lock, flags); + + return irq_status; +} + +static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); + int i; + + for (i = 0; i < len; i++) + buf[i] = denali->host_read(denali, addr); +} + +static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); + int i; + + for (i = 0; i < len; i++) + denali->host_write(denali, addr, buf[i]); +} + +static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); + uint16_t *buf16 = (uint16_t *)buf; + int i; + + for (i = 0; i < len / 2; i++) + buf16[i] = denali->host_read(denali, addr); +} + +static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); + const uint16_t *buf16 = (const uint16_t *)buf; + int i; + + for (i = 0; i < len / 2; i++) + denali->host_write(denali, addr, buf16[i]); +} + +static uint8_t denali_read_byte(struct mtd_info *mtd) +{ + uint8_t byte; + + denali_read_buf(mtd, &byte, 1); + + return byte; +} + +static void denali_write_byte(struct mtd_info *mtd, uint8_t byte) +{ + denali_write_buf(mtd, &byte, 1); +} + +static uint16_t denali_read_word(struct mtd_info *mtd) +{ + uint16_t word; + + denali_read_buf16(mtd, (uint8_t *)&word, 2); + + return word; +} + +static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t type; + + if (ctrl & NAND_CLE) + type = DENALI_MAP11_CMD; + else if (ctrl & NAND_ALE) + type = DENALI_MAP11_ADDR; + else + return; + + /* + * Some commands are followed by chip->dev_ready or chip->waitfunc. + * irq_status must be cleared here to catch the R/B# interrupt later. + */ + if (ctrl & NAND_CTRL_CHANGE) + denali_reset_irq(denali); + + denali->host_write(denali, DENALI_BANK(denali) | type, dat); +} + +static int denali_dev_ready(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + return !!(denali_check_irq(denali) & INTR__INT_ACT); +} + +static int denali_check_erased_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + unsigned long uncor_ecc_flags, + unsigned int max_bitflips) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint8_t *ecc_code = chip->oob_poi + denali->oob_skip_bytes; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int i, stat; + + for (i = 0; i < ecc_steps; i++) { + if (!(uncor_ecc_flags & BIT(i))) + continue; + + stat = nand_check_erased_ecc_chunk(buf, ecc_size, + ecc_code, ecc_bytes, + NULL, 0, + chip->ecc.strength); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + + buf += ecc_size; + ecc_code += ecc_bytes; + } + + return max_bitflips; +} + +static int denali_hw_ecc_fixup(struct mtd_info *mtd, + struct denali_nand_info *denali, + unsigned long *uncor_ecc_flags) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int bank = denali->active_bank; + uint32_t ecc_cor; + unsigned int max_bitflips; + + ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank)); + ecc_cor >>= ECC_COR_INFO__SHIFT(bank); + + if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) { + /* + * This flag is set when uncorrectable error occurs at least in + * one ECC sector. We can not know "how many sectors", or + * "which sector(s)". We need erase-page check for all sectors. + */ + *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0); + return 0; + } + + max_bitflips = FIELD_GET(ECC_COR_INFO__MAX_ERRORS, ecc_cor); + + /* + * The register holds the maximum of per-sector corrected bitflips. + * This is suitable for the return value of the ->read_page() callback. + * Unfortunately, we can not know the total number of corrected bits in + * the page. Increase the stats by max_bitflips. (compromised solution) + */ + mtd->ecc_stats.corrected += max_bitflips; + + return max_bitflips; +} + +static int denali_sw_ecc_fixup(struct mtd_info *mtd, + struct denali_nand_info *denali, + unsigned long *uncor_ecc_flags, uint8_t *buf) +{ + unsigned int ecc_size = denali->nand.ecc.size; + unsigned int bitflips = 0; + unsigned int max_bitflips = 0; + uint32_t err_addr, err_cor_info; + unsigned int err_byte, err_sector, err_device; + uint8_t err_cor_value; + unsigned int prev_sector = 0; + uint32_t irq_status; + + denali_reset_irq(denali); + + do { + err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS); + err_sector = FIELD_GET(ECC_ERROR_ADDRESS__SECTOR, err_addr); + err_byte = FIELD_GET(ECC_ERROR_ADDRESS__OFFSET, err_addr); + + err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO); + err_cor_value = FIELD_GET(ERR_CORRECTION_INFO__BYTE, + err_cor_info); + err_device = FIELD_GET(ERR_CORRECTION_INFO__DEVICE, + err_cor_info); + + /* reset the bitflip counter when crossing ECC sector */ + if (err_sector != prev_sector) + bitflips = 0; + + if (err_cor_info & ERR_CORRECTION_INFO__UNCOR) { + /* + * Check later if this is a real ECC error, or + * an erased sector. + */ + *uncor_ecc_flags |= BIT(err_sector); + } else if (err_byte < ecc_size) { + /* + * If err_byte is larger than ecc_size, means error + * happened in OOB, so we ignore it. It's no need for + * us to correct it err_device is represented the NAND + * error bits are happened in if there are more than + * one NAND connected. + */ + int offset; + unsigned int flips_in_byte; + + offset = (err_sector * ecc_size + err_byte) * + denali->devs_per_cs + err_device; + + /* correct the ECC error */ + flips_in_byte = hweight8(buf[offset] ^ err_cor_value); + buf[offset] ^= err_cor_value; + mtd->ecc_stats.corrected += flips_in_byte; + bitflips += flips_in_byte; + + max_bitflips = max(max_bitflips, bitflips); + } + + prev_sector = err_sector; + } while (!(err_cor_info & ERR_CORRECTION_INFO__LAST_ERR)); + + /* + * Once handle all ECC errors, controller will trigger an + * ECC_TRANSACTION_DONE interrupt. + */ + irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE); + if (!(irq_status & INTR__ECC_TRANSACTION_DONE)) + return -EIO; + + return max_bitflips; +} + +static void denali_setup_dma64(struct denali_nand_info *denali, + dma_addr_t dma_addr, int page, int write) +{ + uint32_t mode; + const int page_count = 1; + + mode = DENALI_MAP10 | DENALI_BANK(denali) | page; + + /* DMA is a three step process */ + + /* + * 1. setup transfer type, interrupt when complete, + * burst len = 64 bytes, the number of pages + */ + denali->host_write(denali, mode, + 0x01002000 | (64 << 16) | (write << 8) | page_count); + + /* 2. set memory low address */ + denali->host_write(denali, mode, lower_32_bits(dma_addr)); + + /* 3. set memory high address */ + denali->host_write(denali, mode, upper_32_bits(dma_addr)); +} + +static void denali_setup_dma32(struct denali_nand_info *denali, + dma_addr_t dma_addr, int page, int write) +{ + uint32_t mode; + const int page_count = 1; + + mode = DENALI_MAP10 | DENALI_BANK(denali); + + /* DMA is a four step process */ + + /* 1. setup transfer type and # of pages */ + denali->host_write(denali, mode | page, + 0x2000 | (write << 8) | page_count); + + /* 2. set memory high address bits 23:8 */ + denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200); + + /* 3. set memory low address bits 23:8 */ + denali->host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300); + + /* 4. interrupt when complete, burst len = 64 bytes */ + denali->host_write(denali, mode | 0x14000, 0x2400); +} + +static int denali_pio_read(struct denali_nand_info *denali, void *buf, + size_t size, int page, int raw) +{ + u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; + uint32_t *buf32 = (uint32_t *)buf; + uint32_t irq_status, ecc_err_mask; + int i; + + if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) + ecc_err_mask = INTR__ECC_UNCOR_ERR; + else + ecc_err_mask = INTR__ECC_ERR; + + denali_reset_irq(denali); + + for (i = 0; i < size / 4; i++) + *buf32++ = denali->host_read(denali, addr); + + irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC); + if (!(irq_status & INTR__PAGE_XFER_INC)) + return -EIO; + + if (irq_status & INTR__ERASED_PAGE) + memset(buf, 0xff, size); + + return irq_status & ecc_err_mask ? -EBADMSG : 0; +} + +static int denali_pio_write(struct denali_nand_info *denali, + const void *buf, size_t size, int page, int raw) +{ + u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; + const uint32_t *buf32 = (uint32_t *)buf; + uint32_t irq_status; + int i; + + denali_reset_irq(denali); + + for (i = 0; i < size / 4; i++) + denali->host_write(denali, addr, *buf32++); + + irq_status = denali_wait_for_irq(denali, + INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL); + if (!(irq_status & INTR__PROGRAM_COMP)) + return -EIO; + + return 0; +} + +static int denali_pio_xfer(struct denali_nand_info *denali, void *buf, + size_t size, int page, int raw, int write) +{ + if (write) + return denali_pio_write(denali, buf, size, page, raw); + else + return denali_pio_read(denali, buf, size, page, raw); +} + +static int denali_dma_xfer(struct denali_nand_info *denali, void *buf, + size_t size, int page, int raw, int write) +{ + dma_addr_t dma_addr; + uint32_t irq_mask, irq_status, ecc_err_mask; + enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + int ret = 0; + + dma_addr = dma_map_single(denali->dev, buf, size, dir); + if (dma_mapping_error(denali->dev, dma_addr)) { + dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n"); + return denali_pio_xfer(denali, buf, size, page, raw, write); + } + + if (write) { + /* + * INTR__PROGRAM_COMP is never asserted for the DMA transfer. + * We can use INTR__DMA_CMD_COMP instead. This flag is asserted + * when the page program is completed. + */ + irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL; + ecc_err_mask = 0; + } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) { + irq_mask = INTR__DMA_CMD_COMP; + ecc_err_mask = INTR__ECC_UNCOR_ERR; + } else { + irq_mask = INTR__DMA_CMD_COMP; + ecc_err_mask = INTR__ECC_ERR; + } + + iowrite32(DMA_ENABLE__FLAG, denali->reg + DMA_ENABLE); + + denali_reset_irq(denali); + denali->setup_dma(denali, dma_addr, page, write); + + irq_status = denali_wait_for_irq(denali, irq_mask); + if (!(irq_status & INTR__DMA_CMD_COMP)) + ret = -EIO; + else if (irq_status & ecc_err_mask) + ret = -EBADMSG; + + iowrite32(0, denali->reg + DMA_ENABLE); + + dma_unmap_single(denali->dev, dma_addr, size, dir); + + if (irq_status & INTR__ERASED_PAGE) + memset(buf, 0xff, size); + + return ret; +} + +static int denali_data_xfer(struct denali_nand_info *denali, void *buf, + size_t size, int page, int raw, int write) +{ + iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE); + iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0, + denali->reg + TRANSFER_SPARE_REG); + + if (denali->dma_avail) + return denali_dma_xfer(denali, buf, size, page, raw, write); + else + return denali_pio_xfer(denali, buf, size, page, raw, write); +} + +static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, + int page, int write) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int writesize = mtd->writesize; + int oobsize = mtd->oobsize; + uint8_t *bufpoi = chip->oob_poi; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int oob_skip = denali->oob_skip_bytes; + size_t size = writesize + oobsize; + int i, pos, len; + + /* BBM at the beginning of the OOB area */ + if (write) + nand_prog_page_begin_op(chip, page, writesize, bufpoi, + oob_skip); + else + nand_read_page_op(chip, page, writesize, bufpoi, oob_skip); + bufpoi += oob_skip; + + /* OOB ECC */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + len = ecc_bytes; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + if (write) + nand_change_write_column_op(chip, pos, bufpoi, len, + false); + else + nand_change_read_column_op(chip, pos, bufpoi, len, + false); + bufpoi += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + if (write) + nand_change_write_column_op(chip, writesize + + oob_skip, bufpoi, + len, false); + else + nand_change_read_column_op(chip, writesize + + oob_skip, bufpoi, + len, false); + bufpoi += len; + } + } + + /* OOB free */ + len = oobsize - (bufpoi - chip->oob_poi); + if (write) + nand_change_write_column_op(chip, size - len, bufpoi, len, + false); + else + nand_change_read_column_op(chip, size - len, bufpoi, len, + false); +} + +static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int writesize = mtd->writesize; + int oobsize = mtd->oobsize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = denali->buf; + int oob_skip = denali->oob_skip_bytes; + size_t size = writesize + oobsize; + int ret, i, pos, len; + + ret = denali_data_xfer(denali, tmp_buf, size, page, 1, 0); + if (ret) + return ret; + + /* Arrange the buffer for syndrome payload/ecc layout */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(buf, tmp_buf + pos, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(buf, tmp_buf + writesize + oob_skip, + len); + buf += len; + } + } + } + + if (oob_required) { + uint8_t *oob = chip->oob_poi; + + /* BBM at the beginning of the OOB area */ + memcpy(oob, tmp_buf + writesize, oob_skip); + oob += oob_skip; + + /* OOB ECC */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + len = ecc_bytes; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(oob, tmp_buf + pos, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(oob, tmp_buf + writesize + oob_skip, + len); + oob += len; + } + } + + /* OOB free */ + len = oobsize - (oob - chip->oob_poi); + memcpy(oob, tmp_buf + size - len, len); + } + + return 0; +} + +static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + denali_oob_xfer(mtd, chip, page, 0); + + return 0; +} + +static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + denali_reset_irq(denali); + + denali_oob_xfer(mtd, chip, page, 1); + + return nand_prog_page_end_op(chip); +} + +static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + unsigned long uncor_ecc_flags = 0; + int stat = 0; + int ret; + + ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0); + if (ret && ret != -EBADMSG) + return ret; + + if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) + stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags); + else if (ret == -EBADMSG) + stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf); + + if (stat < 0) + return stat; + + if (uncor_ecc_flags) { + ret = denali_read_oob(mtd, chip, page); + if (ret) + return ret; + + stat = denali_check_erased_page(mtd, chip, buf, + uncor_ecc_flags, stat); + } + + return stat; +} + +static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int writesize = mtd->writesize; + int oobsize = mtd->oobsize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = denali->buf; + int oob_skip = denali->oob_skip_bytes; + size_t size = writesize + oobsize; + int i, pos, len; + + /* + * Fill the buffer with 0xff first except the full page transfer. + * This simplifies the logic. + */ + if (!buf || !oob_required) + memset(tmp_buf, 0xff, size); + + /* Arrange the buffer for syndrome payload/ecc layout */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, buf, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(tmp_buf + writesize + oob_skip, buf, + len); + buf += len; + } + } + } + + if (oob_required) { + const uint8_t *oob = chip->oob_poi; + + /* BBM at the beginning of the OOB area */ + memcpy(tmp_buf + writesize, oob, oob_skip); + oob += oob_skip; + + /* OOB ECC */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + len = ecc_bytes; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, oob, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(tmp_buf + writesize + oob_skip, oob, + len); + oob += len; + } + } + + /* OOB free */ + len = oobsize - (oob - chip->oob_poi); + memcpy(tmp_buf + size - len, oob, len); + } + + return denali_data_xfer(denali, tmp_buf, size, page, 1, 1); +} + +static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + return denali_data_xfer(denali, (void *)buf, mtd->writesize, + page, 0, 1); +} + +static void denali_select_chip(struct mtd_info *mtd, int chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + denali->active_bank = chip; +} + +static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_status; + + /* R/B# pin transitioned from low to high? */ + irq_status = denali_wait_for_irq(denali, INTR__INT_ACT); + + return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL; +} + +static int denali_erase(struct mtd_info *mtd, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_status; + + denali_reset_irq(denali); + + denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page, + DENALI_ERASE); + + /* wait for erase to complete or failure to occur */ + irq_status = denali_wait_for_irq(denali, + INTR__ERASE_COMP | INTR__ERASE_FAIL); + + return irq_status & INTR__ERASE_COMP ? 0 : -EIO; +} + +static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + const struct nand_sdr_timings *timings; + unsigned long t_clk; + int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data; + int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup; + int addr_2_data_mask; + uint32_t tmp; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + /* clk_x period in picoseconds */ + t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate); + if (!t_clk) + return -EINVAL; + + if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + /* tREA -> ACC_CLKS */ + acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk); + acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE); + + tmp = ioread32(denali->reg + ACC_CLKS); + tmp &= ~ACC_CLKS__VALUE; + tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks); + iowrite32(tmp, denali->reg + ACC_CLKS); + + /* tRWH -> RE_2_WE */ + re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk); + re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE); + + tmp = ioread32(denali->reg + RE_2_WE); + tmp &= ~RE_2_WE__VALUE; + tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we); + iowrite32(tmp, denali->reg + RE_2_WE); + + /* tRHZ -> RE_2_RE */ + re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk); + re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE); + + tmp = ioread32(denali->reg + RE_2_RE); + tmp &= ~RE_2_RE__VALUE; + tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re); + iowrite32(tmp, denali->reg + RE_2_RE); + + /* + * tCCS, tWHR -> WE_2_RE + * + * With WE_2_RE properly set, the Denali controller automatically takes + * care of the delay; the driver need not set NAND_WAIT_TCCS. + */ + we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), + t_clk); + we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE); + + tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE); + tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE; + tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re); + iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE); + + /* tADL -> ADDR_2_DATA */ + + /* for older versions, ADDR_2_DATA is only 6 bit wide */ + addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; + if (denali->revision < 0x0501) + addr_2_data_mask >>= 1; + + addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk); + addr_2_data = min_t(int, addr_2_data, addr_2_data_mask); + + tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA); + tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; + tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data); + iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA); + + /* tREH, tWH -> RDWR_EN_HI_CNT */ + rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min), + t_clk); + rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE); + + tmp = ioread32(denali->reg + RDWR_EN_HI_CNT); + tmp &= ~RDWR_EN_HI_CNT__VALUE; + tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi); + iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT); + + /* tRP, tWP -> RDWR_EN_LO_CNT */ + rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), + t_clk); + rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min), + t_clk); + rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT); + rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi); + rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE); + + tmp = ioread32(denali->reg + RDWR_EN_LO_CNT); + tmp &= ~RDWR_EN_LO_CNT__VALUE; + tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo); + iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT); + + /* tCS, tCEA -> CS_SETUP_CNT */ + cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo, + (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks, + 0); + cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE); + + tmp = ioread32(denali->reg + CS_SETUP_CNT); + tmp &= ~CS_SETUP_CNT__VALUE; + tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup); + iowrite32(tmp, denali->reg + CS_SETUP_CNT); + + return 0; +} + +static void denali_reset_banks(struct denali_nand_info *denali) +{ + u32 irq_status; + int i; + + for (i = 0; i < denali->max_banks; i++) { + denali->active_bank = i; + + denali_reset_irq(denali); + + iowrite32(DEVICE_RESET__BANK(i), + denali->reg + DEVICE_RESET); + + irq_status = denali_wait_for_irq(denali, + INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT); + if (!(irq_status & INTR__INT_ACT)) + break; + } + + dev_dbg(denali->dev, "%d chips connected\n", i); + denali->max_banks = i; +} + +static void denali_hw_init(struct denali_nand_info *denali) +{ + /* + * The REVISION register may not be reliable. Platforms are allowed to + * override it. + */ + if (!denali->revision) + denali->revision = swab16(ioread32(denali->reg + REVISION)); + + /* + * tell driver how many bit controller will skip before + * writing ECC code in OOB, this register may be already + * set by firmware. So we read this value out. + * if this value is 0, just let it be. + */ + denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES); + denali_detect_max_banks(denali); + iowrite32(0x0F, denali->reg + RB_PIN_ENABLED); + iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE); + + iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER); +} + +int denali_calc_ecc_bytes(int step_size, int strength) +{ + /* BCH code. Denali requires ecc.bytes to be multiple of 2 */ + return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2; +} +EXPORT_SYMBOL(denali_calc_ecc_bytes); + +static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, + struct denali_nand_info *denali) +{ + int oobavail = mtd->oobsize - denali->oob_skip_bytes; + int ret; + + /* + * If .size and .strength are already set (usually by DT), + * check if they are supported by this controller. + */ + if (chip->ecc.size && chip->ecc.strength) + return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail); + + /* + * We want .size and .strength closest to the chip's requirement + * unless NAND_ECC_MAXIMIZE is requested. + */ + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { + ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail); + if (!ret) + return 0; + } + + /* Max ECC strength is the last thing we can do */ + return nand_maximize_ecc(chip, denali->ecc_caps, oobavail); +} + +static int denali_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = denali->oob_skip_bytes; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int denali_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = chip->ecc.total + denali->oob_skip_bytes; + oobregion->length = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops denali_ooblayout_ops = { + .ecc = denali_ooblayout_ecc, + .free = denali_ooblayout_free, +}; + +static int denali_multidev_fixup(struct denali_nand_info *denali) +{ + struct nand_chip *chip = &denali->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + + /* + * Support for multi device: + * When the IP configuration is x16 capable and two x8 chips are + * connected in parallel, DEVICES_CONNECTED should be set to 2. + * In this case, the core framework knows nothing about this fact, + * so we should tell it the _logical_ pagesize and anything necessary. + */ + denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED); + + /* + * On some SoCs, DEVICES_CONNECTED is not auto-detected. + * For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case. + */ + if (denali->devs_per_cs == 0) { + denali->devs_per_cs = 1; + iowrite32(1, denali->reg + DEVICES_CONNECTED); + } + + if (denali->devs_per_cs == 1) + return 0; + + if (denali->devs_per_cs != 2) { + dev_err(denali->dev, "unsupported number of devices %d\n", + denali->devs_per_cs); + return -EINVAL; + } + + /* 2 chips in parallel */ + mtd->size <<= 1; + mtd->erasesize <<= 1; + mtd->writesize <<= 1; + mtd->oobsize <<= 1; + chip->chipsize <<= 1; + chip->page_shift += 1; + chip->phys_erase_shift += 1; + chip->bbt_erase_shift += 1; + chip->chip_shift += 1; + chip->pagemask <<= 1; + chip->ecc.size <<= 1; + chip->ecc.bytes <<= 1; + chip->ecc.strength <<= 1; + denali->oob_skip_bytes <<= 1; + + return 0; +} + +int denali_init(struct denali_nand_info *denali) +{ + struct nand_chip *chip = &denali->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + u32 features = ioread32(denali->reg + FEATURES); + int ret; + + mtd->dev.parent = denali->dev; + denali_hw_init(denali); + + init_completion(&denali->complete); + spin_lock_init(&denali->irq_lock); + + denali_clear_irq_all(denali); + + ret = devm_request_irq(denali->dev, denali->irq, denali_isr, + IRQF_SHARED, DENALI_NAND_NAME, denali); + if (ret) { + dev_err(denali->dev, "Unable to request IRQ\n"); + return ret; + } + + denali_enable_irq(denali); + denali_reset_banks(denali); + + denali->active_bank = DENALI_INVALID_BANK; + + nand_set_flash_node(chip, denali->dev->of_node); + /* Fallback to the default name if DT did not give "label" property */ + if (!mtd->name) + mtd->name = "denali-nand"; + + chip->select_chip = denali_select_chip; + chip->read_byte = denali_read_byte; + chip->write_byte = denali_write_byte; + chip->read_word = denali_read_word; + chip->cmd_ctrl = denali_cmd_ctrl; + chip->dev_ready = denali_dev_ready; + chip->waitfunc = denali_waitfunc; + + if (features & FEATURES__INDEX_ADDR) { + denali->host_read = denali_indexed_read; + denali->host_write = denali_indexed_write; + } else { + denali->host_read = denali_direct_read; + denali->host_write = denali_direct_write; + } + + /* clk rate info is needed for setup_data_interface */ + if (denali->clk_x_rate) + chip->setup_data_interface = denali_setup_data_interface; + + ret = nand_scan_ident(mtd, denali->max_banks, NULL); + if (ret) + goto disable_irq; + + if (ioread32(denali->reg + FEATURES) & FEATURES__DMA) + denali->dma_avail = 1; + + if (denali->dma_avail) { + int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32; + + ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit)); + if (ret) { + dev_info(denali->dev, + "Failed to set DMA mask. Disabling DMA.\n"); + denali->dma_avail = 0; + } + } + + if (denali->dma_avail) { + chip->options |= NAND_USE_BOUNCE_BUFFER; + chip->buf_align = 16; + if (denali->caps & DENALI_CAP_DMA_64BIT) + denali->setup_dma = denali_setup_dma64; + else + denali->setup_dma = denali_setup_dma32; + } + + chip->bbt_options |= NAND_BBT_USE_FLASH; + chip->bbt_options |= NAND_BBT_NO_OOB; + chip->ecc.mode = NAND_ECC_HW_SYNDROME; + chip->options |= NAND_NO_SUBPAGE_WRITE; + + ret = denali_ecc_setup(mtd, chip, denali); + if (ret) { + dev_err(denali->dev, "Failed to setup ECC settings.\n"); + goto disable_irq; + } + + dev_dbg(denali->dev, + "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", + chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); + + iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) | + FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength), + denali->reg + ECC_CORRECTION); + iowrite32(mtd->erasesize / mtd->writesize, + denali->reg + PAGES_PER_BLOCK); + iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0, + denali->reg + DEVICE_WIDTH); + iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG, + denali->reg + TWO_ROW_ADDR_CYCLES); + iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE); + iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE); + + iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE); + iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE); + /* chip->ecc.steps is set by nand_scan_tail(); not available here */ + iowrite32(mtd->writesize / chip->ecc.size, + denali->reg + CFG_NUM_DATA_BLOCKS); + + mtd_set_ooblayout(mtd, &denali_ooblayout_ops); + + if (chip->options & NAND_BUSWIDTH_16) { + chip->read_buf = denali_read_buf16; + chip->write_buf = denali_write_buf16; + } else { + chip->read_buf = denali_read_buf; + chip->write_buf = denali_write_buf; + } + chip->ecc.read_page = denali_read_page; + chip->ecc.read_page_raw = denali_read_page_raw; + chip->ecc.write_page = denali_write_page; + chip->ecc.write_page_raw = denali_write_page_raw; + chip->ecc.read_oob = denali_read_oob; + chip->ecc.write_oob = denali_write_oob; + chip->erase = denali_erase; + + ret = denali_multidev_fixup(denali); + if (ret) + goto disable_irq; + + /* + * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not + * use devm_kmalloc() because the memory allocated by devm_ does not + * guarantee DMA-safe alignment. + */ + denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!denali->buf) { + ret = -ENOMEM; + goto disable_irq; + } + + ret = nand_scan_tail(mtd); + if (ret) + goto free_buf; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(denali->dev, "Failed to register MTD: %d\n", ret); + goto free_buf; + } + return 0; + +free_buf: + kfree(denali->buf); +disable_irq: + denali_disable_irq(denali); + + return ret; +} +EXPORT_SYMBOL(denali_init); + +void denali_remove(struct denali_nand_info *denali) +{ + struct mtd_info *mtd = nand_to_mtd(&denali->nand); + + nand_release(mtd); + kfree(denali->buf); + denali_disable_irq(denali); +} +EXPORT_SYMBOL(denali_remove); diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h new file mode 100644 index 000000000000..9ad33d237378 --- /dev/null +++ b/drivers/mtd/nand/raw/denali.h @@ -0,0 +1,335 @@ +/* + * NAND Flash Controller Device Driver + * Copyright (c) 2009 - 2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __DENALI_H__ +#define __DENALI_H__ + +#include +#include +#include +#include +#include + +#define DEVICE_RESET 0x0 +#define DEVICE_RESET__BANK(bank) BIT(bank) + +#define TRANSFER_SPARE_REG 0x10 +#define TRANSFER_SPARE_REG__FLAG BIT(0) + +#define LOAD_WAIT_CNT 0x20 +#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0) + +#define PROGRAM_WAIT_CNT 0x30 +#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0) + +#define ERASE_WAIT_CNT 0x40 +#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0) + +#define INT_MON_CYCCNT 0x50 +#define INT_MON_CYCCNT__VALUE GENMASK(15, 0) + +#define RB_PIN_ENABLED 0x60 +#define RB_PIN_ENABLED__BANK(bank) BIT(bank) + +#define MULTIPLANE_OPERATION 0x70 +#define MULTIPLANE_OPERATION__FLAG BIT(0) + +#define MULTIPLANE_READ_ENABLE 0x80 +#define MULTIPLANE_READ_ENABLE__FLAG BIT(0) + +#define COPYBACK_DISABLE 0x90 +#define COPYBACK_DISABLE__FLAG BIT(0) + +#define CACHE_WRITE_ENABLE 0xa0 +#define CACHE_WRITE_ENABLE__FLAG BIT(0) + +#define CACHE_READ_ENABLE 0xb0 +#define CACHE_READ_ENABLE__FLAG BIT(0) + +#define PREFETCH_MODE 0xc0 +#define PREFETCH_MODE__PREFETCH_EN BIT(0) +#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4) + +#define CHIP_ENABLE_DONT_CARE 0xd0 +#define CHIP_EN_DONT_CARE__FLAG BIT(0) + +#define ECC_ENABLE 0xe0 +#define ECC_ENABLE__FLAG BIT(0) + +#define GLOBAL_INT_ENABLE 0xf0 +#define GLOBAL_INT_EN_FLAG BIT(0) + +#define TWHR2_AND_WE_2_RE 0x100 +#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0) +#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8) + +#define TCWAW_AND_ADDR_2_DATA 0x110 +/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */ +#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0) +#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8) + +#define RE_2_WE 0x120 +#define RE_2_WE__VALUE GENMASK(5, 0) + +#define ACC_CLKS 0x130 +#define ACC_CLKS__VALUE GENMASK(3, 0) + +#define NUMBER_OF_PLANES 0x140 +#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0) + +#define PAGES_PER_BLOCK 0x150 +#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0) + +#define DEVICE_WIDTH 0x160 +#define DEVICE_WIDTH__VALUE GENMASK(1, 0) + +#define DEVICE_MAIN_AREA_SIZE 0x170 +#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0) + +#define DEVICE_SPARE_AREA_SIZE 0x180 +#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0) + +#define TWO_ROW_ADDR_CYCLES 0x190 +#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0) + +#define MULTIPLANE_ADDR_RESTRICT 0x1a0 +#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0) + +#define ECC_CORRECTION 0x1b0 +#define ECC_CORRECTION__VALUE GENMASK(4, 0) +#define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16) + +#define READ_MODE 0x1c0 +#define READ_MODE__VALUE GENMASK(3, 0) + +#define WRITE_MODE 0x1d0 +#define WRITE_MODE__VALUE GENMASK(3, 0) + +#define COPYBACK_MODE 0x1e0 +#define COPYBACK_MODE__VALUE GENMASK(3, 0) + +#define RDWR_EN_LO_CNT 0x1f0 +#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0) + +#define RDWR_EN_HI_CNT 0x200 +#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0) + +#define MAX_RD_DELAY 0x210 +#define MAX_RD_DELAY__VALUE GENMASK(3, 0) + +#define CS_SETUP_CNT 0x220 +#define CS_SETUP_CNT__VALUE GENMASK(4, 0) +#define CS_SETUP_CNT__TWB GENMASK(17, 12) + +#define SPARE_AREA_SKIP_BYTES 0x230 +#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0) + +#define SPARE_AREA_MARKER 0x240 +#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0) + +#define DEVICES_CONNECTED 0x250 +#define DEVICES_CONNECTED__VALUE GENMASK(2, 0) + +#define DIE_MASK 0x260 +#define DIE_MASK__VALUE GENMASK(7, 0) + +#define FIRST_BLOCK_OF_NEXT_PLANE 0x270 +#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0) + +#define WRITE_PROTECT 0x280 +#define WRITE_PROTECT__FLAG BIT(0) + +#define RE_2_RE 0x290 +#define RE_2_RE__VALUE GENMASK(5, 0) + +#define MANUFACTURER_ID 0x300 +#define MANUFACTURER_ID__VALUE GENMASK(7, 0) + +#define DEVICE_ID 0x310 +#define DEVICE_ID__VALUE GENMASK(7, 0) + +#define DEVICE_PARAM_0 0x320 +#define DEVICE_PARAM_0__VALUE GENMASK(7, 0) + +#define DEVICE_PARAM_1 0x330 +#define DEVICE_PARAM_1__VALUE GENMASK(7, 0) + +#define DEVICE_PARAM_2 0x340 +#define DEVICE_PARAM_2__VALUE GENMASK(7, 0) + +#define LOGICAL_PAGE_DATA_SIZE 0x350 +#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0) + +#define LOGICAL_PAGE_SPARE_SIZE 0x360 +#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0) + +#define REVISION 0x370 +#define REVISION__VALUE GENMASK(15, 0) + +#define ONFI_DEVICE_FEATURES 0x380 +#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0) + +#define ONFI_OPTIONAL_COMMANDS 0x390 +#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0) + +#define ONFI_TIMING_MODE 0x3a0 +#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0) + +#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0 +#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0) + +#define ONFI_DEVICE_NO_OF_LUNS 0x3c0 +#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0) +#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8) + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0) + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0) + +#define FEATURES 0x3f0 +#define FEATURES__N_BANKS GENMASK(1, 0) +#define FEATURES__ECC_MAX_ERR GENMASK(5, 2) +#define FEATURES__DMA BIT(6) +#define FEATURES__CMD_DMA BIT(7) +#define FEATURES__PARTITION BIT(8) +#define FEATURES__XDMA_SIDEBAND BIT(9) +#define FEATURES__GPREG BIT(10) +#define FEATURES__INDEX_ADDR BIT(11) + +#define TRANSFER_MODE 0x400 +#define TRANSFER_MODE__VALUE GENMASK(1, 0) + +#define INTR_STATUS(bank) (0x410 + (bank) * 0x50) +#define INTR_EN(bank) (0x420 + (bank) * 0x50) +/* bit[1:0] is used differently depending on IP version */ +#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */ +#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */ +#define INTR__ECC_ERR BIT(1) /* old IP */ +#define INTR__DMA_CMD_COMP BIT(2) +#define INTR__TIME_OUT BIT(3) +#define INTR__PROGRAM_FAIL BIT(4) +#define INTR__ERASE_FAIL BIT(5) +#define INTR__LOAD_COMP BIT(6) +#define INTR__PROGRAM_COMP BIT(7) +#define INTR__ERASE_COMP BIT(8) +#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9) +#define INTR__LOCKED_BLK BIT(10) +#define INTR__UNSUP_CMD BIT(11) +#define INTR__INT_ACT BIT(12) +#define INTR__RST_COMP BIT(13) +#define INTR__PIPE_CMD_ERR BIT(14) +#define INTR__PAGE_XFER_INC BIT(15) +#define INTR__ERASED_PAGE BIT(16) + +#define PAGE_CNT(bank) (0x430 + (bank) * 0x50) +#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50) +#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50) + +#define ECC_THRESHOLD 0x600 +#define ECC_THRESHOLD__VALUE GENMASK(9, 0) + +#define ECC_ERROR_BLOCK_ADDRESS 0x610 +#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0) + +#define ECC_ERROR_PAGE_ADDRESS 0x620 +#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0) +#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12) + +#define ECC_ERROR_ADDRESS 0x630 +#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0) +#define ECC_ERROR_ADDRESS__SECTOR GENMASK(15, 12) + +#define ERR_CORRECTION_INFO 0x640 +#define ERR_CORRECTION_INFO__BYTE GENMASK(7, 0) +#define ERR_CORRECTION_INFO__DEVICE GENMASK(11, 8) +#define ERR_CORRECTION_INFO__UNCOR BIT(14) +#define ERR_CORRECTION_INFO__LAST_ERR BIT(15) + +#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10) +#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8) +#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0) +#define ECC_COR_INFO__UNCOR_ERR BIT(7) + +#define CFG_DATA_BLOCK_SIZE 0x6b0 + +#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0 + +#define CFG_NUM_DATA_BLOCKS 0x6d0 + +#define CFG_META_DATA_SIZE 0x6e0 + +#define DMA_ENABLE 0x700 +#define DMA_ENABLE__FLAG BIT(0) + +#define IGNORE_ECC_DONE 0x710 +#define IGNORE_ECC_DONE__FLAG BIT(0) + +#define DMA_INTR 0x720 +#define DMA_INTR_EN 0x730 +#define DMA_INTR__TARGET_ERROR BIT(0) +#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1) +#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2) +#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3) +#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4) +#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5) + +#define TARGET_ERR_ADDR_LO 0x740 +#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0) + +#define TARGET_ERR_ADDR_HI 0x750 +#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0) + +#define CHNL_ACTIVE 0x760 +#define CHNL_ACTIVE__CHANNEL0 BIT(0) +#define CHNL_ACTIVE__CHANNEL1 BIT(1) +#define CHNL_ACTIVE__CHANNEL2 BIT(2) +#define CHNL_ACTIVE__CHANNEL3 BIT(3) + +struct denali_nand_info { + struct nand_chip nand; + unsigned long clk_x_rate; /* bus interface clock rate */ + int active_bank; /* currently selected bank */ + struct device *dev; + void __iomem *reg; /* Register Interface */ + void __iomem *host; /* Host Data/Command Interface */ + struct completion complete; + spinlock_t irq_lock; /* protect irq_mask and irq_status */ + u32 irq_mask; /* interrupts we are waiting for */ + u32 irq_status; /* interrupts that have happened */ + int irq; + void *buf; /* for syndrome layout conversion */ + dma_addr_t dma_addr; + int dma_avail; /* can support DMA? */ + int devs_per_cs; /* devices connected in parallel */ + int oob_skip_bytes; /* number of bytes reserved for BBM */ + int max_banks; + unsigned int revision; /* IP revision */ + unsigned int caps; /* IP capability (or quirk) */ + const struct nand_ecc_caps *ecc_caps; + u32 (*host_read)(struct denali_nand_info *denali, u32 addr); + void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data); + void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr, + int page, int write); +}; + +#define DENALI_CAP_HW_ECC_FIXUP BIT(0) +#define DENALI_CAP_DMA_64BIT BIT(1) + +int denali_calc_ecc_bytes(int step_size, int strength); +int denali_init(struct denali_nand_info *denali); +void denali_remove(struct denali_nand_info *denali); + +#endif /* __DENALI_H__ */ diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c new file mode 100644 index 000000000000..cfd33e6ca77f --- /dev/null +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -0,0 +1,163 @@ +/* + * NAND Flash Controller Device Driver for DT + * + * Copyright © 2011, Picochip. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "denali.h" + +struct denali_dt { + struct denali_nand_info denali; + struct clk *clk; +}; + +struct denali_dt_data { + unsigned int revision; + unsigned int caps; + const struct nand_ecc_caps *ecc_caps; +}; + +NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes, + 512, 8, 15); +static const struct denali_dt_data denali_socfpga_data = { + .caps = DENALI_CAP_HW_ECC_FIXUP, + .ecc_caps = &denali_socfpga_ecc_caps, +}; + +NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes, + 1024, 8, 16, 24); +static const struct denali_dt_data denali_uniphier_v5a_data = { + .caps = DENALI_CAP_HW_ECC_FIXUP | + DENALI_CAP_DMA_64BIT, + .ecc_caps = &denali_uniphier_v5a_ecc_caps, +}; + +NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes, + 1024, 8, 16); +static const struct denali_dt_data denali_uniphier_v5b_data = { + .revision = 0x0501, + .caps = DENALI_CAP_HW_ECC_FIXUP | + DENALI_CAP_DMA_64BIT, + .ecc_caps = &denali_uniphier_v5b_ecc_caps, +}; + +static const struct of_device_id denali_nand_dt_ids[] = { + { + .compatible = "altr,socfpga-denali-nand", + .data = &denali_socfpga_data, + }, + { + .compatible = "socionext,uniphier-denali-nand-v5a", + .data = &denali_uniphier_v5a_data, + }, + { + .compatible = "socionext,uniphier-denali-nand-v5b", + .data = &denali_uniphier_v5b_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); + +static int denali_dt_probe(struct platform_device *pdev) +{ + struct resource *res; + struct denali_dt *dt; + const struct denali_dt_data *data; + struct denali_nand_info *denali; + int ret; + + dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL); + if (!dt) + return -ENOMEM; + denali = &dt->denali; + + data = of_device_get_match_data(&pdev->dev); + if (data) { + denali->revision = data->revision; + denali->caps = data->caps; + denali->ecc_caps = data->ecc_caps; + } + + denali->dev = &pdev->dev; + denali->irq = platform_get_irq(pdev, 0); + if (denali->irq < 0) { + dev_err(&pdev->dev, "no irq defined\n"); + return denali->irq; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg"); + denali->reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(denali->reg)) + return PTR_ERR(denali->reg); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); + denali->host = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(denali->host)) + return PTR_ERR(denali->host); + + dt->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dt->clk)) { + dev_err(&pdev->dev, "no clk available\n"); + return PTR_ERR(dt->clk); + } + ret = clk_prepare_enable(dt->clk); + if (ret) + return ret; + + denali->clk_x_rate = clk_get_rate(dt->clk); + + ret = denali_init(denali); + if (ret) + goto out_disable_clk; + + platform_set_drvdata(pdev, dt); + return 0; + +out_disable_clk: + clk_disable_unprepare(dt->clk); + + return ret; +} + +static int denali_dt_remove(struct platform_device *pdev) +{ + struct denali_dt *dt = platform_get_drvdata(pdev); + + denali_remove(&dt->denali); + clk_disable_unprepare(dt->clk); + + return 0; +} + +static struct platform_driver denali_dt_driver = { + .probe = denali_dt_probe, + .remove = denali_dt_remove, + .driver = { + .name = "denali-nand-dt", + .of_match_table = denali_nand_dt_ids, + }, +}; +module_platform_driver(denali_dt_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jamie Iles"); +MODULE_DESCRIPTION("DT driver for Denali NAND controller"); diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c new file mode 100644 index 000000000000..49cb3e1f8bd0 --- /dev/null +++ b/drivers/mtd/nand/raw/denali_pci.c @@ -0,0 +1,131 @@ +/* + * NAND Flash Controller Device Driver + * Copyright © 2009-2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include + +#include "denali.h" + +#define DENALI_NAND_NAME "denali-nand-pci" + +#define INTEL_CE4100 1 +#define INTEL_MRST 2 + +/* List of platforms this NAND controller has be integrated into */ +static const struct pci_device_id denali_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, + { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, denali_pci_ids); + +NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15); + +static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int ret; + resource_size_t csr_base, mem_base; + unsigned long csr_len, mem_len; + struct denali_nand_info *denali; + + denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL); + if (!denali) + return -ENOMEM; + + ret = pcim_enable_device(dev); + if (ret) { + dev_err(&dev->dev, "Spectra: pci_enable_device failed.\n"); + return ret; + } + + if (id->driver_data == INTEL_CE4100) { + mem_base = pci_resource_start(dev, 0); + mem_len = pci_resource_len(dev, 1); + csr_base = pci_resource_start(dev, 1); + csr_len = pci_resource_len(dev, 1); + } else { + csr_base = pci_resource_start(dev, 0); + csr_len = pci_resource_len(dev, 0); + mem_base = pci_resource_start(dev, 1); + mem_len = pci_resource_len(dev, 1); + if (!mem_len) { + mem_base = csr_base + csr_len; + mem_len = csr_len; + } + } + + pci_set_master(dev); + denali->dev = &dev->dev; + denali->irq = dev->irq; + denali->ecc_caps = &denali_pci_ecc_caps; + denali->nand.ecc.options |= NAND_ECC_MAXIMIZE; + denali->clk_x_rate = 200000000; /* 200 MHz */ + + ret = pci_request_regions(dev, DENALI_NAND_NAME); + if (ret) { + dev_err(&dev->dev, "Spectra: Unable to request memory regions\n"); + return ret; + } + + denali->reg = ioremap_nocache(csr_base, csr_len); + if (!denali->reg) { + dev_err(&dev->dev, "Spectra: Unable to remap memory region\n"); + return -ENOMEM; + } + + denali->host = ioremap_nocache(mem_base, mem_len); + if (!denali->host) { + dev_err(&dev->dev, "Spectra: ioremap_nocache failed!"); + ret = -ENOMEM; + goto failed_remap_reg; + } + + ret = denali_init(denali); + if (ret) + goto failed_remap_mem; + + pci_set_drvdata(dev, denali); + + return 0; + +failed_remap_mem: + iounmap(denali->host); +failed_remap_reg: + iounmap(denali->reg); + return ret; +} + +static void denali_pci_remove(struct pci_dev *dev) +{ + struct denali_nand_info *denali = pci_get_drvdata(dev); + + denali_remove(denali); + iounmap(denali->reg); + iounmap(denali->host); +} + +static struct pci_driver denali_pci_driver = { + .name = DENALI_NAND_NAME, + .id_table = denali_pci_ids, + .probe = denali_pci_probe, + .remove = denali_pci_remove, +}; +module_pci_driver(denali_pci_driver); + +MODULE_DESCRIPTION("PCI driver for Denali NAND controller"); +MODULE_AUTHOR("Intel Corporation and its suppliers"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c new file mode 100644 index 000000000000..1af77f798fe5 --- /dev/null +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -0,0 +1,1709 @@ +/* + * (C) 2003 Red Hat, Inc. + * (C) 2004 Dan Brown + * (C) 2004 Kalev Lember + * + * Author: David Woodhouse + * Additional Diskonchip 2000 and Millennium support by Dan Brown + * Diskonchip Millennium Plus support by Kalev Lember + * + * Error correction code lifted from the old docecc code + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * converted to the generic Reed-Solomon library by Thomas Gleixner + * + * Interface to generic NAND code for M-Systems DiskOnChip devices + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Where to look for the devices? */ +#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS +#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 +#endif + +static unsigned long doc_locations[] __initdata = { +#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) +#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH + 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, + 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, + 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, + 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, + 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, +#else + 0xc8000, 0xca000, 0xcc000, 0xce000, + 0xd0000, 0xd2000, 0xd4000, 0xd6000, + 0xd8000, 0xda000, 0xdc000, 0xde000, + 0xe0000, 0xe2000, 0xe4000, 0xe6000, + 0xe8000, 0xea000, 0xec000, 0xee000, +#endif +#endif + 0xffffffff }; + +static struct mtd_info *doclist = NULL; + +struct doc_priv { + void __iomem *virtadr; + unsigned long physadr; + u_char ChipID; + u_char CDSNControl; + int chips_per_floor; /* The number of chips detected on each floor */ + int curfloor; + int curchip; + int mh0_page; + int mh1_page; + struct mtd_info *nextdoc; + + /* Handle the last stage of initialization (BBT scan, partitioning) */ + int (*late_init)(struct mtd_info *mtd); +}; + +/* This is the ecc value computed by the HW ecc generator upon writing an empty + page, one with all 0xff for data. */ +static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; + +#define INFTL_BBT_RESERVED_BLOCKS 4 + +#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32) +#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) +#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int bitmask); +static void doc200x_select_chip(struct mtd_info *mtd, int chip); + +static int debug = 0; +module_param(debug, int, 0); + +static int try_dword = 1; +module_param(try_dword, int, 0); + +static int no_ecc_failures = 0; +module_param(no_ecc_failures, int, 0); + +static int no_autopart = 0; +module_param(no_autopart, int, 0); + +static int show_firmware_partition = 0; +module_param(show_firmware_partition, int, 0); + +#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE +static int inftl_bbt_write = 1; +#else +static int inftl_bbt_write = 0; +#endif +module_param(inftl_bbt_write, int, 0); + +static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; +module_param(doc_config_location, ulong, 0); +MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); + +/* Sector size for HW ECC */ +#define SECTOR_SIZE 512 +/* The sector bytes are packed into NB_DATA 10 bit words */ +#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) +/* Number of roots */ +#define NROOTS 4 +/* First consective root */ +#define FCR 510 +/* Number of symbols */ +#define NN 1023 + +/* the Reed Solomon control structure */ +static struct rs_control *rs_decoder; + +/* + * The HW decoder in the DoC ASIC's provides us a error syndrome, + * which we must convert to a standard syndrome usable by the generic + * Reed-Solomon library code. + * + * Fabrice Bellard figured this out in the old docecc code. I added + * some comments, improved a minor bit and converted it to make use + * of the generic Reed-Solomon library. tglx + */ +static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) +{ + int i, j, nerr, errpos[8]; + uint8_t parity; + uint16_t ds[4], s[5], tmp, errval[8], syn[4]; + + memset(syn, 0, sizeof(syn)); + /* Convert the ecc bytes into words */ + ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); + ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); + ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); + ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); + parity = ecc[1]; + + /* Initialize the syndrome buffer */ + for (i = 0; i < NROOTS; i++) + s[i] = ds[0]; + /* + * Evaluate + * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] + * where x = alpha^(FCR + i) + */ + for (j = 1; j < NROOTS; j++) { + if (ds[j] == 0) + continue; + tmp = rs->index_of[ds[j]]; + for (i = 0; i < NROOTS; i++) + s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; + } + + /* Calc syn[i] = s[i] / alpha^(v + i) */ + for (i = 0; i < NROOTS; i++) { + if (s[i]) + syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); + } + /* Call the decoder library */ + nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); + + /* Incorrectable errors ? */ + if (nerr < 0) + return nerr; + + /* + * Correct the errors. The bitpositions are a bit of magic, + * but they are given by the design of the de/encoder circuit + * in the DoC ASIC's. + */ + for (i = 0; i < nerr; i++) { + int index, bitpos, pos = 1015 - errpos[i]; + uint8_t val; + if (pos >= NB_DATA && pos < 1019) + continue; + if (pos < NB_DATA) { + /* extract bit position (MSB first) */ + pos = 10 * (NB_DATA - 1 - pos) - 6; + /* now correct the following 10 bits. At most two bytes + can be modified since pos is even */ + index = (pos >> 3) ^ 1; + bitpos = pos & 7; + if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { + val = (uint8_t) (errval[i] >> (2 + bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[index] ^= val; + } + index = ((pos >> 3) + 1) ^ 1; + bitpos = (bitpos + 10) & 7; + if (bitpos == 0) + bitpos = 8; + if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { + val = (uint8_t) (errval[i] << (8 - bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[index] ^= val; + } + } + } + /* If the parity is wrong, no rescue possible */ + return parity ? -EBADMSG : nerr; +} + +static void DoC_Delay(struct doc_priv *doc, unsigned short cycles) +{ + volatile char dummy; + int i; + + for (i = 0; i < cycles; i++) { + if (DoC_is_Millennium(doc)) + dummy = ReadDOC(doc->virtadr, NOP); + else if (DoC_is_MillenniumPlus(doc)) + dummy = ReadDOC(doc->virtadr, Mplus_NOP); + else + dummy = ReadDOC(doc->virtadr, DOCStatus); + } + +} + +#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) + +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +static int _DoC_WaitReady(struct doc_priv *doc) +{ + void __iomem *docptr = doc->virtadr; + unsigned long timeo = jiffies + (HZ * 10); + + if (debug) + printk("_DoC_WaitReady...\n"); + /* Out-of-line routine to wait for chip response */ + if (DoC_is_MillenniumPlus(doc)) { + while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { + if (time_after(jiffies, timeo)) { + printk("_DoC_WaitReady timed out.\n"); + return -EIO; + } + udelay(1); + cond_resched(); + } + } else { + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + if (time_after(jiffies, timeo)) { + printk("_DoC_WaitReady timed out.\n"); + return -EIO; + } + udelay(1); + cond_resched(); + } + } + + return 0; +} + +static inline int DoC_WaitReady(struct doc_priv *doc) +{ + void __iomem *docptr = doc->virtadr; + int ret = 0; + + if (DoC_is_MillenniumPlus(doc)) { + DoC_Delay(doc, 4); + + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + } else { + DoC_Delay(doc, 4); + + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + DoC_Delay(doc, 2); + } + + if (debug) + printk("DoC_WaitReady OK\n"); + return ret; +} + +static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + + if (debug) + printk("write_byte %02x\n", datum); + WriteDOC(datum, docptr, CDSNSlowIO); + WriteDOC(datum, docptr, 2k_CDSN_IO); +} + +static u_char doc2000_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + u_char ret; + + ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(doc, 2); + ret = ReadDOC(docptr, 2k_CDSN_IO); + if (debug) + printk("read_byte returns %02x\n", ret); + return ret; +} + +static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int i; + if (debug) + printk("writebuf of %d bytes: ", len); + for (i = 0; i < len; i++) { + WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + if (debug) + printk("\n"); +} + +static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) + printk("readbuf of %d bytes: ", len); + + for (i = 0; i < len; i++) { + buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); + } +} + +static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) + printk("readbuf_dword of %d bytes: ", len); + + if (unlikely((((unsigned long)buf) | len) & 3)) { + for (i = 0; i < len; i++) { + *(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); + } + } else { + for (i = 0; i < len; i += 4) { + *(uint32_t *) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); + } + } +} + +static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + uint16_t ret; + + doc200x_select_chip(mtd, nr); + doc200x_hwcontrol(mtd, NAND_CMD_READID, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* We can't use dev_ready here, but at least we wait for the + * command to complete + */ + udelay(50); + + ret = this->read_byte(mtd) << 8; + ret |= this->read_byte(mtd); + + if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { + /* First chip probe. See if we get same results by 32-bit access */ + union { + uint32_t dword; + uint8_t byte[4]; + } ident; + void __iomem *docptr = doc->virtadr; + + doc200x_hwcontrol(mtd, NAND_CMD_READID, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + udelay(50); + + ident.dword = readl(docptr + DoC_2k_CDSN_IO); + if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { + printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); + this->read_buf = &doc2000_readbuf_dword; + } + } + + return ret; +} + +static void __init doc2000_count_chips(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + uint16_t mfrid; + int i; + + /* Max 4 chips per floor on DiskOnChip 2000 */ + doc->chips_per_floor = 4; + + /* Find out what the first chip is */ + mfrid = doc200x_ident_chip(mtd, 0); + + /* Find how many chips in each floor. */ + for (i = 1; i < 4; i++) { + if (doc200x_ident_chip(mtd, i) != mfrid) + break; + } + doc->chips_per_floor = i; + printk(KERN_DEBUG "Detected %d chips per floor.\n", i); +} + +static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this) +{ + struct doc_priv *doc = nand_get_controller_data(this); + + int status; + + DoC_WaitReady(doc); + nand_status_op(this, NULL); + DoC_WaitReady(doc); + status = (int)this->read_byte(mtd); + + return status; +} + +static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + + WriteDOC(datum, docptr, CDSNSlowIO); + WriteDOC(datum, docptr, Mil_CDSN_IO); + WriteDOC(datum, docptr, WritePipeTerm); +} + +static u_char doc2001_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + + //ReadDOC(docptr, CDSNSlowIO); + /* 11.4.5 -- delay twice to allow extended length cycle */ + DoC_Delay(doc, 2); + ReadDOC(docptr, ReadPipeInit); + //return ReadDOC(docptr, Mil_CDSN_IO); + return ReadDOC(docptr, LastDataRead); +} + +static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int i; + + for (i = 0; i < len; i++) + WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); + /* Terminate write pipeline */ + WriteDOC(0x00, docptr, WritePipeTerm); +} + +static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int i; + + /* Start read pipeline */ + ReadDOC(docptr, ReadPipeInit); + + for (i = 0; i < len - 1; i++) + buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); + + /* Terminate read pipeline */ + buf[i] = ReadDOC(docptr, LastDataRead); +} + +static u_char doc2001plus_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + u_char ret; + + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + ret = ReadDOC(docptr, Mplus_LastDataRead); + if (debug) + printk("read_byte returns %02x\n", ret); + return ret; +} + +static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) + printk("writebuf of %d bytes: ", len); + for (i = 0; i < len; i++) { + WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + if (debug) + printk("\n"); +} + +static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) + printk("readbuf of %d bytes: ", len); + + /* Start read pipeline */ + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + + for (i = 0; i < len - 2; i++) { + buf[i] = ReadDOC(docptr, Mil_CDSN_IO); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + + /* Terminate read pipeline */ + buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead); + if (debug && i < 16) + printk("%02x ", buf[len - 2]); + buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead); + if (debug && i < 16) + printk("%02x ", buf[len - 1]); + if (debug) + printk("\n"); +} + +static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int floor = 0; + + if (debug) + printk("select chip (%d)\n", chip); + + if (chip == -1) { + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + return; + } + + floor = chip / doc->chips_per_floor; + chip -= (floor * doc->chips_per_floor); + + /* Assert ChipEnable and deassert WriteProtect */ + WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); + nand_reset_op(this); + + doc->curchip = chip; + doc->curfloor = floor; +} + +static void doc200x_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int floor = 0; + + if (debug) + printk("select chip (%d)\n", chip); + + if (chip == -1) + return; + + floor = chip / doc->chips_per_floor; + chip -= (floor * doc->chips_per_floor); + + /* 11.4.4 -- deassert CE before changing chip */ + doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + + WriteDOC(floor, docptr, FloorSelect); + WriteDOC(chip, docptr, CDSNDeviceSelect); + + doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + doc->curchip = chip; + doc->curfloor = floor; +} + +#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE) + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + + if (ctrl & NAND_CTRL_CHANGE) { + doc->CDSNControl &= ~CDSN_CTRL_MSK; + doc->CDSNControl |= ctrl & CDSN_CTRL_MSK; + if (debug) + printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + /* 11.4.3 -- 4 NOPs after CSDNControl write */ + DoC_Delay(doc, 4); + } + if (cmd != NAND_CMD_NONE) { + if (DoC_is_2000(doc)) + doc2000_write_byte(mtd, cmd); + else + doc2001_write_byte(mtd, cmd); + } +} + +static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + + /* + * Must terminate write pipeline before sending any commands + * to the device. + */ + if (command == NAND_CMD_PAGEPROG) { + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + } + + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + WriteDOC(readcmd, docptr, Mplus_FlashCmd); + } + WriteDOC(command, docptr, Mplus_FlashCmd); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + + if (column != -1 || page_addr != -1) { + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + WriteDOC(column, docptr, Mplus_FlashAddress); + } + if (page_addr != -1) { + WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress); + WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); + if (this->options & NAND_ROW_ADDR_3) { + WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); + printk("high density\n"); + } + } + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + /* deassert ALE */ + if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB || command == NAND_CMD_READID) + WriteDOC(0, docptr, Mplus_FlashControl); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + while (!(this->read_byte(mtd) & 0x40)) ; + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay(this->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + /* wait until command is processed */ + while (!this->dev_ready(mtd)) ; +} + +static int doc200x_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + + if (DoC_is_MillenniumPlus(doc)) { + /* 11.4.2 -- must NOP four times before checking FR/B# */ + DoC_Delay(doc, 4); + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { + if (debug) + printk("not ready\n"); + return 0; + } + if (debug) + printk("was ready\n"); + return 1; + } else { + /* 11.4.2 -- must NOP four times before checking FR/B# */ + DoC_Delay(doc, 4); + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + if (debug) + printk("not ready\n"); + return 0; + } + /* 11.4.2 -- Must NOP twice if it's ready */ + DoC_Delay(doc, 2); + if (debug) + printk("was ready\n"); + return 1; + } +} + +static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs) +{ + /* This is our last resort if we couldn't find or create a BBT. Just + pretend all blocks are good. */ + return 0; +} + +static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + + /* Prime the ECC engine */ + switch (mode) { + case NAND_ECC_READ: + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN, docptr, ECCConf); + break; + case NAND_ECC_WRITE: + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + break; + } +} + +static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + + /* Prime the ECC engine */ + switch (mode) { + case NAND_ECC_READ: + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); + break; + case NAND_ECC_WRITE: + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); + break; + } +} + +/* This code is only called on write */ +static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + int i; + int emptymatch = 1; + + /* flush the pipeline */ + if (DoC_is_2000(doc)) { + WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + } else if (DoC_is_MillenniumPlus(doc)) { + WriteDOC(0, docptr, Mplus_NOP); + WriteDOC(0, docptr, Mplus_NOP); + WriteDOC(0, docptr, Mplus_NOP); + } else { + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + } + + for (i = 0; i < 6; i++) { + if (DoC_is_MillenniumPlus(doc)) + ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); + else + ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); + if (ecc_code[i] != empty_write_ecc[i]) + emptymatch = 0; + } + if (DoC_is_MillenniumPlus(doc)) + WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); + else + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); +#if 0 + /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ + if (emptymatch) { + /* Note: this somewhat expensive test should not be triggered + often. It could be optimized away by examining the data in + the writebuf routine, and remembering the result. */ + for (i = 0; i < 512; i++) { + if (dat[i] == 0xff) + continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, we do have an all-0xff data buffer. + Return all-0xff ecc value instead of the computed one, so + it'll look just like a freshly-erased page. */ + if (emptymatch) + memset(ecc_code, 0xff, 6); +#endif + return 0; +} + +static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) +{ + int i, ret = 0; + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + void __iomem *docptr = doc->virtadr; + uint8_t calc_ecc[6]; + volatile u_char dummy; + + /* flush the pipeline */ + if (DoC_is_2000(doc)) { + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + } else if (DoC_is_MillenniumPlus(doc)) { + dummy = ReadDOC(docptr, Mplus_ECCConf); + dummy = ReadDOC(docptr, Mplus_ECCConf); + dummy = ReadDOC(docptr, Mplus_ECCConf); + } else { + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + } + + /* Error occurred ? */ + if (dummy & 0x80) { + for (i = 0; i < 6; i++) { + if (DoC_is_MillenniumPlus(doc)) + calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); + else + calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); + } + + ret = doc_ecc_decode(rs_decoder, dat, calc_ecc); + if (ret > 0) + printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); + } + if (DoC_is_MillenniumPlus(doc)) + WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); + else + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + if (no_ecc_failures && mtd_is_eccerr(ret)) { + printk(KERN_ERR "suppressing ECC failure\n"); + ret = 0; + } + return ret; +} + +//u_char mydatabuf[528]; + +static int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = 6; + + return 0; +} + +static int doc200x_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + /* + * The strange out-of-order free bytes definition is a (possibly + * unneeded) attempt to retain compatibility. It used to read: + * .oobfree = { {8, 8} } + * Since that leaves two bytes unusable, it was changed. But the + * following scheme might affect existing jffs2 installs by moving the + * cleanmarker: + * .oobfree = { {6, 10} } + * jffs2 seems to handle the above gracefully, but the current scheme + * seems safer. The only problem with it is that any code retrieving + * free bytes position must be able to handle out-of-order segments. + */ + if (!section) { + oobregion->offset = 8; + oobregion->length = 8; + } else { + oobregion->offset = 6; + oobregion->length = 2; + } + + return 0; +} + +static const struct mtd_ooblayout_ops doc200x_ooblayout_ops = { + .ecc = doc200x_ooblayout_ecc, + .free = doc200x_ooblayout_free, +}; + +/* Find the (I)NFTL Media Header, and optionally also the mirror media header. + On successful return, buf will contain a copy of the media header for + further processing. id is the string to scan for, and will presumably be + either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media + header. The page #s of the found media headers are placed in mh0_page and + mh1_page in the DOC private structure. */ +static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + unsigned offs; + int ret; + size_t retlen; + + for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { + ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); + if (retlen != mtd->writesize) + continue; + if (ret) { + printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs); + } + if (memcmp(buf, id, 6)) + continue; + printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); + if (doc->mh0_page == -1) { + doc->mh0_page = offs >> this->page_shift; + if (!findmirror) + return 1; + continue; + } + doc->mh1_page = offs >> this->page_shift; + return 2; + } + if (doc->mh0_page == -1) { + printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id); + return 0; + } + /* Only one mediaheader was found. We want buf to contain a + mediaheader on return, so we'll have to re-read the one we found. */ + offs = doc->mh0_page << this->page_shift; + ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); + if (retlen != mtd->writesize) { + /* Insanity. Give up. */ + printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); + return 0; + } + return 1; +} + +static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + int ret = 0; + u_char *buf; + struct NFTLMediaHeader *mh; + const unsigned psize = 1 << this->page_shift; + int numparts = 0; + unsigned blocks, maxblocks; + int offs, numheaders; + + buf = kmalloc(mtd->writesize, GFP_KERNEL); + if (!buf) { + return 0; + } + if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1))) + goto out; + mh = (struct NFTLMediaHeader *)buf; + + le16_to_cpus(&mh->NumEraseUnits); + le16_to_cpus(&mh->FirstPhysicalEUN); + le32_to_cpus(&mh->FormattedSize); + + printk(KERN_INFO " DataOrgID = %s\n" + " NumEraseUnits = %d\n" + " FirstPhysicalEUN = %d\n" + " FormattedSize = %d\n" + " UnitSizeFactor = %d\n", + mh->DataOrgID, mh->NumEraseUnits, + mh->FirstPhysicalEUN, mh->FormattedSize, + mh->UnitSizeFactor); + + blocks = mtd->size >> this->phys_erase_shift; + maxblocks = min(32768U, mtd->erasesize - psize); + + if (mh->UnitSizeFactor == 0x00) { + /* Auto-determine UnitSizeFactor. The constraints are: + - There can be at most 32768 virtual blocks. + - There can be at most (virtual block size - page size) + virtual blocks (because MediaHeader+BBT must fit in 1). + */ + mh->UnitSizeFactor = 0xff; + while (blocks > maxblocks) { + blocks >>= 1; + maxblocks = min(32768U, (maxblocks << 1) + psize); + mh->UnitSizeFactor--; + } + printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); + } + + /* NOTE: The lines below modify internal variables of the NAND and MTD + layers; variables with have already been configured by nand_scan. + Unfortunately, we didn't know before this point what these values + should be. Thus, this code is somewhat dependent on the exact + implementation of the NAND layer. */ + if (mh->UnitSizeFactor != 0xff) { + this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); + mtd->erasesize <<= (0xff - mh->UnitSizeFactor); + printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize); + blocks = mtd->size >> this->bbt_erase_shift; + maxblocks = min(32768U, mtd->erasesize - psize); + } + + if (blocks > maxblocks) { + printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); + goto out; + } + + /* Skip past the media headers. */ + offs = max(doc->mh0_page, doc->mh1_page); + offs <<= this->page_shift; + offs += mtd->erasesize; + + if (show_firmware_partition == 1) { + parts[0].name = " DiskOnChip Firmware / Media Header partition"; + parts[0].offset = 0; + parts[0].size = offs; + numparts = 1; + } + + parts[numparts].name = " DiskOnChip BDTL partition"; + parts[numparts].offset = offs; + parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; + + offs += parts[numparts].size; + numparts++; + + if (offs < mtd->size) { + parts[numparts].name = " DiskOnChip Remainder partition"; + parts[numparts].offset = offs; + parts[numparts].size = mtd->size - offs; + numparts++; + } + + ret = numparts; + out: + kfree(buf); + return ret; +} + +/* This is a stripped-down copy of the code in inftlmount.c */ +static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + int ret = 0; + u_char *buf; + struct INFTLMediaHeader *mh; + struct INFTLPartition *ip; + int numparts = 0; + int blocks; + int vshift, lastvunit = 0; + int i; + int end = mtd->size; + + if (inftl_bbt_write) + end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); + + buf = kmalloc(mtd->writesize, GFP_KERNEL); + if (!buf) { + return 0; + } + + if (!find_media_headers(mtd, buf, "BNAND", 0)) + goto out; + doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); + mh = (struct INFTLMediaHeader *)buf; + + le32_to_cpus(&mh->NoOfBootImageBlocks); + le32_to_cpus(&mh->NoOfBinaryPartitions); + le32_to_cpus(&mh->NoOfBDTLPartitions); + le32_to_cpus(&mh->BlockMultiplierBits); + le32_to_cpus(&mh->FormatFlags); + le32_to_cpus(&mh->PercentUsed); + + printk(KERN_INFO " bootRecordID = %s\n" + " NoOfBootImageBlocks = %d\n" + " NoOfBinaryPartitions = %d\n" + " NoOfBDTLPartitions = %d\n" + " BlockMultiplerBits = %d\n" + " FormatFlgs = %d\n" + " OsakVersion = %d.%d.%d.%d\n" + " PercentUsed = %d\n", + mh->bootRecordID, mh->NoOfBootImageBlocks, + mh->NoOfBinaryPartitions, + mh->NoOfBDTLPartitions, + mh->BlockMultiplierBits, mh->FormatFlags, + ((unsigned char *) &mh->OsakVersion)[0] & 0xf, + ((unsigned char *) &mh->OsakVersion)[1] & 0xf, + ((unsigned char *) &mh->OsakVersion)[2] & 0xf, + ((unsigned char *) &mh->OsakVersion)[3] & 0xf, + mh->PercentUsed); + + vshift = this->phys_erase_shift + mh->BlockMultiplierBits; + + blocks = mtd->size >> vshift; + if (blocks > 32768) { + printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); + goto out; + } + + blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); + if (inftl_bbt_write && (blocks > mtd->erasesize)) { + printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); + goto out; + } + + /* Scan the partitions */ + for (i = 0; (i < 4); i++) { + ip = &(mh->Partitions[i]); + le32_to_cpus(&ip->virtualUnits); + le32_to_cpus(&ip->firstUnit); + le32_to_cpus(&ip->lastUnit); + le32_to_cpus(&ip->flags); + le32_to_cpus(&ip->spareUnits); + le32_to_cpus(&ip->Reserved0); + + printk(KERN_INFO " PARTITION[%d] ->\n" + " virtualUnits = %d\n" + " firstUnit = %d\n" + " lastUnit = %d\n" + " flags = 0x%x\n" + " spareUnits = %d\n", + i, ip->virtualUnits, ip->firstUnit, + ip->lastUnit, ip->flags, + ip->spareUnits); + + if ((show_firmware_partition == 1) && + (i == 0) && (ip->firstUnit > 0)) { + parts[0].name = " DiskOnChip IPL / Media Header partition"; + parts[0].offset = 0; + parts[0].size = mtd->erasesize * ip->firstUnit; + numparts = 1; + } + + if (ip->flags & INFTL_BINARY) + parts[numparts].name = " DiskOnChip BDK partition"; + else + parts[numparts].name = " DiskOnChip BDTL partition"; + parts[numparts].offset = ip->firstUnit << vshift; + parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; + numparts++; + if (ip->lastUnit > lastvunit) + lastvunit = ip->lastUnit; + if (ip->flags & INFTL_LAST) + break; + } + lastvunit++; + if ((lastvunit << vshift) < end) { + parts[numparts].name = " DiskOnChip Remainder partition"; + parts[numparts].offset = lastvunit << vshift; + parts[numparts].size = end - parts[numparts].offset; + numparts++; + } + ret = numparts; + out: + kfree(buf); + return ret; +} + +static int __init nftl_scan_bbt(struct mtd_info *mtd) +{ + int ret, numparts; + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + struct mtd_partition parts[2]; + + memset((char *)parts, 0, sizeof(parts)); + /* On NFTL, we have to find the media headers before we can read the + BBTs, since they're stored in the media header eraseblocks. */ + numparts = nftl_partscan(mtd, parts); + if (!numparts) + return -EIO; + this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | + NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | + NAND_BBT_VERSION; + this->bbt_td->veroffs = 7; + this->bbt_td->pages[0] = doc->mh0_page + 1; + if (doc->mh1_page != -1) { + this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | + NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | + NAND_BBT_VERSION; + this->bbt_md->veroffs = 7; + this->bbt_md->pages[0] = doc->mh1_page + 1; + } else { + this->bbt_md = NULL; + } + + ret = this->scan_bbt(mtd); + if (ret) + return ret; + + return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts); +} + +static int __init inftl_scan_bbt(struct mtd_info *mtd) +{ + int ret, numparts; + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + struct mtd_partition parts[5]; + + if (this->numchips > doc->chips_per_floor) { + printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n"); + return -EIO; + } + + if (DoC_is_MillenniumPlus(doc)) { + this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE; + if (inftl_bbt_write) + this->bbt_td->options |= NAND_BBT_WRITE; + this->bbt_td->pages[0] = 2; + this->bbt_md = NULL; + } else { + this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; + if (inftl_bbt_write) + this->bbt_td->options |= NAND_BBT_WRITE; + this->bbt_td->offs = 8; + this->bbt_td->len = 8; + this->bbt_td->veroffs = 7; + this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS; + this->bbt_td->reserved_block_code = 0x01; + this->bbt_td->pattern = "MSYS_BBT"; + + this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; + if (inftl_bbt_write) + this->bbt_md->options |= NAND_BBT_WRITE; + this->bbt_md->offs = 8; + this->bbt_md->len = 8; + this->bbt_md->veroffs = 7; + this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS; + this->bbt_md->reserved_block_code = 0x01; + this->bbt_md->pattern = "TBB_SYSM"; + } + + ret = this->scan_bbt(mtd); + if (ret) + return ret; + + memset((char *)parts, 0, sizeof(parts)); + numparts = inftl_partscan(mtd, parts); + /* At least for now, require the INFTL Media Header. We could probably + do without it for non-INFTL use, since all it gives us is + autopartitioning, but I want to give it more thought. */ + if (!numparts) + return -EIO; + return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts); +} + +static inline int __init doc2000_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + + this->read_byte = doc2000_read_byte; + this->write_buf = doc2000_writebuf; + this->read_buf = doc2000_readbuf; + doc->late_init = nftl_scan_bbt; + + doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; + doc2000_count_chips(mtd); + mtd->name = "DiskOnChip 2000 (NFTL Model)"; + return (4 * doc->chips_per_floor); +} + +static inline int __init doc2001_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + + this->read_byte = doc2001_read_byte; + this->write_buf = doc2001_writebuf; + this->read_buf = doc2001_readbuf; + + ReadDOC(doc->virtadr, ChipID); + ReadDOC(doc->virtadr, ChipID); + ReadDOC(doc->virtadr, ChipID); + if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { + /* It's not a Millennium; it's one of the newer + DiskOnChip 2000 units with a similar ASIC. + Treat it like a Millennium, except that it + can have multiple chips. */ + doc2000_count_chips(mtd); + mtd->name = "DiskOnChip 2000 (INFTL Model)"; + doc->late_init = inftl_scan_bbt; + return (4 * doc->chips_per_floor); + } else { + /* Bog-standard Millennium */ + doc->chips_per_floor = 1; + mtd->name = "DiskOnChip Millennium"; + doc->late_init = nftl_scan_bbt; + return 1; + } +} + +static inline int __init doc2001plus_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct doc_priv *doc = nand_get_controller_data(this); + + this->read_byte = doc2001plus_read_byte; + this->write_buf = doc2001plus_writebuf; + this->read_buf = doc2001plus_readbuf; + doc->late_init = inftl_scan_bbt; + this->cmd_ctrl = NULL; + this->select_chip = doc2001plus_select_chip; + this->cmdfunc = doc2001plus_command; + this->ecc.hwctl = doc2001plus_enable_hwecc; + + doc->chips_per_floor = 1; + mtd->name = "DiskOnChip Millennium Plus"; + + return 1; +} + +static int __init doc_probe(unsigned long physadr) +{ + unsigned char ChipID; + struct mtd_info *mtd; + struct nand_chip *nand; + struct doc_priv *doc; + void __iomem *virtadr; + unsigned char save_control; + unsigned char tmp, tmpb, tmpc; + int reg, len, numchips; + int ret = 0; + + if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip")) + return -EBUSY; + virtadr = ioremap(physadr, DOC_IOREMAP_LEN); + if (!virtadr) { + printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); + ret = -EIO; + goto error_ioremap; + } + + /* It's not possible to cleanly detect the DiskOnChip - the + * bootup procedure will put the device into reset mode, and + * it's not possible to talk to it without actually writing + * to the DOCControl register. So we store the current contents + * of the DOCControl register's location, in case we later decide + * that it's not a DiskOnChip, and want to put it back how we + * found it. + */ + save_control = ReadDOC(virtadr, DOCControl); + + /* Reset the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); + + /* Enable the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); + + ChipID = ReadDOC(virtadr, ChipID); + + switch (ChipID) { + case DOC_ChipID_Doc2k: + reg = DoC_2k_ECCStatus; + break; + case DOC_ChipID_DocMil: + reg = DoC_ECCConf; + break; + case DOC_ChipID_DocMilPlus16: + case DOC_ChipID_DocMilPlus32: + case 0: + /* Possible Millennium Plus, need to do more checks */ + /* Possibly release from power down mode */ + for (tmp = 0; (tmp < 4); tmp++) + ReadDOC(virtadr, Mplus_Power); + + /* Reset the Millennium Plus ASIC */ + tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; + WriteDOC(tmp, virtadr, Mplus_DOCControl); + WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); + + mdelay(1); + /* Enable the Millennium Plus ASIC */ + tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; + WriteDOC(tmp, virtadr, Mplus_DOCControl); + WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); + mdelay(1); + + ChipID = ReadDOC(virtadr, ChipID); + + switch (ChipID) { + case DOC_ChipID_DocMilPlus16: + reg = DoC_Mplus_Toggle; + break; + case DOC_ChipID_DocMilPlus32: + printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); + default: + ret = -ENODEV; + goto notfound; + } + break; + + default: + ret = -ENODEV; + goto notfound; + } + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + if ((tmp == tmpb) || (tmp != tmpc)) { + printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); + ret = -ENODEV; + goto notfound; + } + + for (mtd = doclist; mtd; mtd = doc->nextdoc) { + unsigned char oldval; + unsigned char newval; + nand = mtd_to_nand(mtd); + doc = nand_get_controller_data(nand); + /* Use the alias resolution register to determine if this is + in fact the same DOC aliased to a new address. If writes + to one chip's alias resolution register change the value on + the other chip, they're the same chip. */ + if (ChipID == DOC_ChipID_DocMilPlus16) { + oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); + newval = ReadDOC(virtadr, Mplus_AliasResolution); + } else { + oldval = ReadDOC(doc->virtadr, AliasResolution); + newval = ReadDOC(virtadr, AliasResolution); + } + if (oldval != newval) + continue; + if (ChipID == DOC_ChipID_DocMilPlus16) { + WriteDOC(~newval, virtadr, Mplus_AliasResolution); + oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); + WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it + } else { + WriteDOC(~newval, virtadr, AliasResolution); + oldval = ReadDOC(doc->virtadr, AliasResolution); + WriteDOC(newval, virtadr, AliasResolution); // restore it + } + newval = ~newval; + if (oldval == newval) { + printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr); + goto notfound; + } + } + + printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); + + len = sizeof(struct nand_chip) + sizeof(struct doc_priv) + + (2 * sizeof(struct nand_bbt_descr)); + nand = kzalloc(len, GFP_KERNEL); + if (!nand) { + ret = -ENOMEM; + goto fail; + } + + mtd = nand_to_mtd(nand); + doc = (struct doc_priv *) (nand + 1); + nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); + nand->bbt_md = nand->bbt_td + 1; + + mtd->owner = THIS_MODULE; + mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops); + + nand_set_controller_data(nand, doc); + nand->select_chip = doc200x_select_chip; + nand->cmd_ctrl = doc200x_hwcontrol; + nand->dev_ready = doc200x_dev_ready; + nand->waitfunc = doc200x_wait; + nand->block_bad = doc200x_block_bad; + nand->ecc.hwctl = doc200x_enable_hwecc; + nand->ecc.calculate = doc200x_calculate_ecc; + nand->ecc.correct = doc200x_correct_data; + + nand->ecc.mode = NAND_ECC_HW_SYNDROME; + nand->ecc.size = 512; + nand->ecc.bytes = 6; + nand->ecc.strength = 2; + nand->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; + nand->bbt_options = NAND_BBT_USE_FLASH; + /* Skip the automatic BBT scan so we can run it manually */ + nand->options |= NAND_SKIP_BBTSCAN; + + doc->physadr = physadr; + doc->virtadr = virtadr; + doc->ChipID = ChipID; + doc->curfloor = -1; + doc->curchip = -1; + doc->mh0_page = -1; + doc->mh1_page = -1; + doc->nextdoc = doclist; + + if (ChipID == DOC_ChipID_Doc2k) + numchips = doc2000_init(mtd); + else if (ChipID == DOC_ChipID_DocMilPlus16) + numchips = doc2001plus_init(mtd); + else + numchips = doc2001_init(mtd); + + if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) { + /* DBB note: i believe nand_release is necessary here, as + buffers may have been allocated in nand_base. Check with + Thomas. FIX ME! */ + /* nand_release will call mtd_device_unregister, but we + haven't yet added it. This is handled without incident by + mtd_device_unregister, as far as I can tell. */ + nand_release(mtd); + kfree(nand); + goto fail; + } + + /* Success! */ + doclist = mtd; + return 0; + + notfound: + /* Put back the contents of the DOCControl register, in case it's not + actually a DiskOnChip. */ + WriteDOC(save_control, virtadr, DOCControl); + fail: + iounmap(virtadr); + +error_ioremap: + release_mem_region(physadr, DOC_IOREMAP_LEN); + + return ret; +} + +static void release_nanddoc(void) +{ + struct mtd_info *mtd, *nextmtd; + struct nand_chip *nand; + struct doc_priv *doc; + + for (mtd = doclist; mtd; mtd = nextmtd) { + nand = mtd_to_nand(mtd); + doc = nand_get_controller_data(nand); + + nextmtd = doc->nextdoc; + nand_release(mtd); + iounmap(doc->virtadr); + release_mem_region(doc->physadr, DOC_IOREMAP_LEN); + kfree(nand); + } +} + +static int __init init_nanddoc(void) +{ + int i, ret = 0; + + /* We could create the decoder on demand, if memory is a concern. + * This way we have it handy, if an error happens + * + * Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 510 + * primitve element to generate roots = 1 + * generator polinomial degree = 4 + */ + rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); + if (!rs_decoder) { + printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); + return -ENOMEM; + } + + if (doc_config_location) { + printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); + ret = doc_probe(doc_config_location); + if (ret < 0) + goto outerr; + } else { + for (i = 0; (doc_locations[i] != 0xffffffff); i++) { + doc_probe(doc_locations[i]); + } + } + /* No banner message any more. Print a message if no DiskOnChip + found, so the user knows we at least tried. */ + if (!doclist) { + printk(KERN_INFO "No valid DiskOnChip devices found\n"); + ret = -ENODEV; + goto outerr; + } + return 0; + outerr: + free_rs(rs_decoder); + return ret; +} + +static void __exit cleanup_nanddoc(void) +{ + /* Cleanup the nand/DoC resources */ + release_nanddoc(); + + /* Free the reed solomon resources */ + if (rs_decoder) { + free_rs(rs_decoder); + } +} + +module_init(init_nanddoc); +module_exit(cleanup_nanddoc); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver"); diff --git a/drivers/mtd/nand/raw/docg4.c b/drivers/mtd/nand/raw/docg4.c new file mode 100644 index 000000000000..72f1327c4430 --- /dev/null +++ b/drivers/mtd/nand/raw/docg4.c @@ -0,0 +1,1421 @@ +/* + * Copyright © 2012 Mike Dunn + * + * mtd nand driver for M-Systems DiskOnChip G4 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus + * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others. + * Should work on these as well. Let me know! + * + * TODO: + * + * Mechanism for management of password-protected areas + * + * Hamming ecc when reading oob only + * + * According to the M-Sys documentation, this device is also available in a + * "dual-die" configuration having a 256MB capacity, but no mechanism for + * detecting this variant is documented. Currently this driver assumes 128MB + * capacity. + * + * Support for multiple cascaded devices ("floors"). Not sure which gadgets + * contain multiple G4s in a cascaded configuration, if any. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * In "reliable mode" consecutive 2k pages are used in parallel (in some + * fashion) to store the same data. The data can be read back from the + * even-numbered pages in the normal manner; odd-numbered pages will appear to + * contain junk. Systems that boot from the docg4 typically write the secondary + * program loader (SPL) code in this mode. The SPL is loaded by the initial + * program loader (IPL, stored in the docg4's 2k NOR-like region that is mapped + * to the reset vector address). This module parameter enables you to use this + * driver to write the SPL. When in this mode, no more than 2k of data can be + * written at a time, because the addresses do not increment in the normal + * manner, and the starting offset must be within an even-numbered 2k region; + * i.e., invalid starting offsets are 0x800, 0xa00, 0xc00, 0xe00, 0x1800, + * 0x1a00, ... Reliable mode is a special case and should not be used unless + * you know what you're doing. + */ +static bool reliable_mode; +module_param(reliable_mode, bool, 0); +MODULE_PARM_DESC(reliable_mode, "pages are programmed in reliable mode"); + +/* + * You'll want to ignore badblocks if you're reading a partition that contains + * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since + * it does not use mtd nand's method for marking bad blocks (using oob area). + * This will also skip the check of the "page written" flag. + */ +static bool ignore_badblocks; +module_param(ignore_badblocks, bool, 0); +MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed"); + +struct docg4_priv { + struct mtd_info *mtd; + struct device *dev; + void __iomem *virtadr; + int status; + struct { + unsigned int command; + int column; + int page; + } last_command; + uint8_t oob_buf[16]; + uint8_t ecc_buf[7]; + int oob_page; + struct bch_control *bch; +}; + +/* + * Defines prefixed with DOCG4 are unique to the diskonchip G4. All others are + * shared with other diskonchip devices (P3, G3 at least). + * + * Functions with names prefixed with docg4_ are mtd / nand interface functions + * (though they may also be called internally). All others are internal. + */ + +#define DOC_IOSPACE_DATA 0x0800 + +/* register offsets */ +#define DOC_CHIPID 0x1000 +#define DOC_DEVICESELECT 0x100a +#define DOC_ASICMODE 0x100c +#define DOC_DATAEND 0x101e +#define DOC_NOP 0x103e + +#define DOC_FLASHSEQUENCE 0x1032 +#define DOC_FLASHCOMMAND 0x1034 +#define DOC_FLASHADDRESS 0x1036 +#define DOC_FLASHCONTROL 0x1038 +#define DOC_ECCCONF0 0x1040 +#define DOC_ECCCONF1 0x1042 +#define DOC_HAMMINGPARITY 0x1046 +#define DOC_BCH_SYNDROM(idx) (0x1048 + idx) + +#define DOC_ASICMODECONFIRM 0x1072 +#define DOC_CHIPID_INV 0x1074 +#define DOC_POWERMODE 0x107c + +#define DOCG4_MYSTERY_REG 0x1050 + +/* apparently used only to write oob bytes 6 and 7 */ +#define DOCG4_OOB_6_7 0x1052 + +/* DOC_FLASHSEQUENCE register commands */ +#define DOC_SEQ_RESET 0x00 +#define DOCG4_SEQ_PAGE_READ 0x03 +#define DOCG4_SEQ_FLUSH 0x29 +#define DOCG4_SEQ_PAGEWRITE 0x16 +#define DOCG4_SEQ_PAGEPROG 0x1e +#define DOCG4_SEQ_BLOCKERASE 0x24 +#define DOCG4_SEQ_SETMODE 0x45 + +/* DOC_FLASHCOMMAND register commands */ +#define DOCG4_CMD_PAGE_READ 0x00 +#define DOC_CMD_ERASECYCLE2 0xd0 +#define DOCG4_CMD_FLUSH 0x70 +#define DOCG4_CMD_READ2 0x30 +#define DOC_CMD_PROG_BLOCK_ADDR 0x60 +#define DOCG4_CMD_PAGEWRITE 0x80 +#define DOC_CMD_PROG_CYCLE2 0x10 +#define DOCG4_CMD_FAST_MODE 0xa3 /* functionality guessed */ +#define DOC_CMD_RELIABLE_MODE 0x22 +#define DOC_CMD_RESET 0xff + +/* DOC_POWERMODE register bits */ +#define DOC_POWERDOWN_READY 0x80 + +/* DOC_FLASHCONTROL register bits */ +#define DOC_CTRL_CE 0x10 +#define DOC_CTRL_UNKNOWN 0x40 +#define DOC_CTRL_FLASHREADY 0x01 + +/* DOC_ECCCONF0 register bits */ +#define DOC_ECCCONF0_READ_MODE 0x8000 +#define DOC_ECCCONF0_UNKNOWN 0x2000 +#define DOC_ECCCONF0_ECC_ENABLE 0x1000 +#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff + +/* DOC_ECCCONF1 register bits */ +#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80 +#define DOC_ECCCONF1_ECC_ENABLE 0x07 +#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20 + +/* DOC_ASICMODE register bits */ +#define DOC_ASICMODE_RESET 0x00 +#define DOC_ASICMODE_NORMAL 0x01 +#define DOC_ASICMODE_POWERDOWN 0x02 +#define DOC_ASICMODE_MDWREN 0x04 +#define DOC_ASICMODE_BDETCT_RESET 0x08 +#define DOC_ASICMODE_RSTIN_RESET 0x10 +#define DOC_ASICMODE_RAM_WE 0x20 + +/* good status values read after read/write/erase operations */ +#define DOCG4_PROGSTATUS_GOOD 0x51 +#define DOCG4_PROGSTATUS_GOOD_2 0xe0 + +/* + * On read operations (page and oob-only), the first byte read from I/O reg is a + * status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read + * after reset only) or 0x51, so bit 1 is presumed to be an error indicator. + */ +#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */ + +/* anatomy of the device */ +#define DOCG4_CHIP_SIZE 0x8000000 +#define DOCG4_PAGE_SIZE 0x200 +#define DOCG4_PAGES_PER_BLOCK 0x200 +#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE) +#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE) +#define DOCG4_OOB_SIZE 0x10 +#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */ +#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */ +#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */ + +/* all but the last byte is included in ecc calculation */ +#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1) + +#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */ + +/* expected values from the ID registers */ +#define DOCG4_IDREG1_VALUE 0x0400 +#define DOCG4_IDREG2_VALUE 0xfbff + +/* primitive polynomial used to build the Galois field used by hw ecc gen */ +#define DOCG4_PRIMITIVE_POLY 0x4443 + +#define DOCG4_M 14 /* Galois field is of order 2^14 */ +#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */ + +#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */ +#define DOCG4_REDUNDANT_BBT_PAGE 24 /* page where redundant factory bbt lives */ + +/* + * Bytes 0, 1 are used as badblock marker. + * Bytes 2 - 6 are available to the user. + * Byte 7 is hamming ecc for first 7 oob bytes only. + * Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14. + * Byte 15 (the last) is used by the driver as a "page written" flag. + */ +static int docg4_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 7; + oobregion->length = 9; + + return 0; +} + +static int docg4_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 2; + oobregion->length = 5; + + return 0; +} + +static const struct mtd_ooblayout_ops docg4_ooblayout_ops = { + .ecc = docg4_ooblayout_ecc, + .free = docg4_ooblayout_free, +}; + +/* + * The device has a nop register which M-Sys claims is for the purpose of + * inserting precise delays. But beware; at least some operations fail if the + * nop writes are replaced with a generic delay! + */ +static inline void write_nop(void __iomem *docptr) +{ + writew(0, docptr + DOC_NOP); +} + +static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd_to_nand(mtd); + uint16_t *p = (uint16_t *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + p[i] = readw(nand->IO_ADDR_R); +} + +static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd_to_nand(mtd); + uint16_t *p = (uint16_t *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + writew(p[i], nand->IO_ADDR_W); +} + +static int poll_status(struct docg4_priv *doc) +{ + /* + * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL + * register. Operations known to take a long time (e.g., block erase) + * should sleep for a while before calling this. + */ + + uint16_t flash_status; + unsigned long timeo; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s...\n", __func__); + + /* hardware quirk requires reading twice initially */ + flash_status = readw(docptr + DOC_FLASHCONTROL); + + timeo = jiffies + msecs_to_jiffies(200); /* generous timeout */ + do { + cpu_relax(); + flash_status = readb(docptr + DOC_FLASHCONTROL); + } while (!(flash_status & DOC_CTRL_FLASHREADY) && + time_before(jiffies, timeo)); + + if (unlikely(!(flash_status & DOC_CTRL_FLASHREADY))) { + dev_err(doc->dev, "%s: timed out!\n", __func__); + return NAND_STATUS_FAIL; + } + + return 0; +} + + +static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand) +{ + + struct docg4_priv *doc = nand_get_controller_data(nand); + int status = NAND_STATUS_WP; /* inverse logic?? */ + dev_dbg(doc->dev, "%s...\n", __func__); + + /* report any previously unreported error */ + if (doc->status) { + status |= doc->status; + doc->status = 0; + return status; + } + + status |= poll_status(doc); + return status; +} + +static void docg4_select_chip(struct mtd_info *mtd, int chip) +{ + /* + * Select among multiple cascaded chips ("floors"). Multiple floors are + * not yet supported, so the only valid non-negative value is 0. + */ + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip); + + if (chip < 0) + return; /* deselected */ + + if (chip > 0) + dev_warn(doc->dev, "multiple floors currently unsupported\n"); + + writew(0, docptr + DOC_DEVICESELECT); +} + +static void reset(struct mtd_info *mtd) +{ + /* full device reset */ + + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + + writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + write_nop(docptr); + + writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + + writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1); + + poll_status(doc); +} + +static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf) +{ + /* read the 7 hw-generated ecc bytes */ + + int i; + for (i = 0; i < 7; i++) { /* hw quirk; read twice */ + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + } +} + +static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + /* + * Called after a page read when hardware reports bitflips. + * Up to four bitflips can be corrected. + */ + + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + int i, numerrs, errpos[4]; + const uint8_t blank_read_hwecc[8] = { + 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 }; + + read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */ + + /* check if read error is due to a blank page */ + if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7)) + return 0; /* yes */ + + /* skip additional check of "written flag" if ignore_badblocks */ + if (ignore_badblocks == false) { + + /* + * If the hw ecc bytes are not those of a blank page, there's + * still a chance that the page is blank, but was read with + * errors. Check the "written flag" in last oob byte, which + * is set to zero when a page is written. If more than half + * the bits are set, assume a blank page. Unfortunately, the + * bit flips(s) are not reported in stats. + */ + + if (nand->oob_poi[15]) { + int bit, numsetbits = 0; + unsigned long written_flag = nand->oob_poi[15]; + for_each_set_bit(bit, &written_flag, 8) + numsetbits++; + if (numsetbits > 4) { /* assume blank */ + dev_warn(doc->dev, + "error(s) in blank page " + "at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return 0; + } + } + } + + /* + * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch + * algorithm is used to decode this. However the hw operates on page + * data in a bit order that is the reverse of that of the bch alg, + * requiring that the bits be reversed on the result. Thanks to Ivan + * Djelic for his analysis! + */ + for (i = 0; i < 7; i++) + doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]); + + numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL, + doc->ecc_buf, NULL, errpos); + + if (numerrs == -EBADMSG) { + dev_warn(doc->dev, "uncorrectable errors at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return -EBADMSG; + } + + BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */ + + /* undo last step in BCH alg (modulo mirroring not needed) */ + for (i = 0; i < numerrs; i++) + errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7)); + + /* fix the errors */ + for (i = 0; i < numerrs; i++) { + + /* ignore if error within oob ecc bytes */ + if (errpos[i] > DOCG4_USERDATA_LEN * 8) + continue; + + /* if error within oob area preceeding ecc bytes... */ + if (errpos[i] > DOCG4_PAGE_SIZE * 8) + change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8, + (unsigned long *)nand->oob_poi); + + else /* error in page data */ + change_bit(errpos[i], (unsigned long *)buf); + } + + dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n", + numerrs, page * DOCG4_PAGE_SIZE); + + return numerrs; +} + +static uint8_t docg4_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + + dev_dbg(doc->dev, "%s\n", __func__); + + if (doc->last_command.command == NAND_CMD_STATUS) { + int status; + + /* + * Previous nand command was status request, so nand + * infrastructure code expects to read the status here. If an + * error occurred in a previous operation, report it. + */ + doc->last_command.command = 0; + + if (doc->status) { + status = doc->status; + doc->status = 0; + } + + /* why is NAND_STATUS_WP inverse logic?? */ + else + status = NAND_STATUS_WP | NAND_STATUS_READY; + + return status; + } + + dev_warn(doc->dev, "unexpected call to read_byte()\n"); + + return 0; +} + +static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr) +{ + /* write the four address bytes packed in docg4_addr to the device */ + + void __iomem *docptr = doc->virtadr; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +} + +static int read_progstatus(struct docg4_priv *doc) +{ + /* + * This apparently checks the status of programming. Done after an + * erasure, and after page data is written. On error, the status is + * saved, to be later retrieved by the nand infrastructure code. + */ + void __iomem *docptr = doc->virtadr; + + /* status is read from the I/O reg */ + uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG); + + dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n", + __func__, status1, status2, status3); + + if (status1 != DOCG4_PROGSTATUS_GOOD + || status2 != DOCG4_PROGSTATUS_GOOD_2 + || status3 != DOCG4_PROGSTATUS_GOOD_2) { + doc->status = NAND_STATUS_FAIL; + dev_warn(doc->dev, "read_progstatus failed: " + "%02x, %02x, %02x\n", status1, status2, status3); + return -EIO; + } + return 0; +} + +static int pageprog(struct mtd_info *mtd) +{ + /* + * Final step in writing a page. Writes the contents of its + * internal buffer out to the flash array, or some such. + */ + + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + int retval = 0; + + dev_dbg(doc->dev, "docg4: %s\n", __func__); + + writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + /* Just busy-wait; usleep_range() slows things down noticeably. */ + poll_status(doc); + + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + retval = read_progstatus(doc); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); + + return retval; +} + +static void sequence_reset(struct mtd_info *mtd) +{ + /* common starting sequence for all operations */ + + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + + writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL); + writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); +} + +static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) +{ + /* first step in reading a page */ + + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, + "docg4: %s: g4 page %08x\n", __func__, docg4_addr); + + sequence_reset(mtd); + + writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + write_addr(doc, docg4_addr); + + write_nop(docptr); + writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + poll_status(doc); +} + +static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) +{ + /* first step in writing a page */ + + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, + "docg4: %s: g4 addr: %x\n", __func__, docg4_addr); + sequence_reset(mtd); + + if (unlikely(reliable_mode)) { + writew(DOCG4_SEQ_SETMODE, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FAST_MODE, docptr + DOC_FLASHCOMMAND); + writew(DOC_CMD_RELIABLE_MODE, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + } + + writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_addr(doc, docg4_addr); + write_nop(docptr); + write_nop(docptr); + poll_status(doc); +} + +static uint32_t mtd_to_docg4_address(int page, int column) +{ + /* + * Convert mtd address to format used by the device, 32 bit packed. + * + * Some notes on G4 addressing... The M-Sys documentation on this device + * claims that pages are 2K in length, and indeed, the format of the + * address used by the device reflects that. But within each page are + * four 512 byte "sub-pages", each with its own oob data that is + * read/written immediately after the 512 bytes of page data. This oob + * data contains the ecc bytes for the preceeding 512 bytes. + * + * Rather than tell the mtd nand infrastructure that page size is 2k, + * with four sub-pages each, we engage in a little subterfuge and tell + * the infrastructure code that pages are 512 bytes in size. This is + * done because during the course of reverse-engineering the device, I + * never observed an instance where an entire 2K "page" was read or + * written as a unit. Each "sub-page" is always addressed individually, + * its data read/written, and ecc handled before the next "sub-page" is + * addressed. + * + * This requires us to convert addresses passed by the mtd nand + * infrastructure code to those used by the device. + * + * The address that is written to the device consists of four bytes: the + * first two are the 2k page number, and the second is the index into + * the page. The index is in terms of 16-bit half-words and includes + * the preceeding oob data, so e.g., the index into the second + * "sub-page" is 0x108, and the full device address of the start of mtd + * page 0x201 is 0x00800108. + */ + int g4_page = page / 4; /* device's 2K page */ + int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */ + return (g4_page << 16) | g4_index; /* pack */ +} + +static void docg4_command(struct mtd_info *mtd, unsigned command, int column, + int page_addr) +{ + /* handle standard nand commands */ + + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + uint32_t g4_addr = mtd_to_docg4_address(page_addr, column); + + dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n", + __func__, command, page_addr, column); + + /* + * Save the command and its arguments. This enables emulation of + * standard flash devices, and also some optimizations. + */ + doc->last_command.command = command; + doc->last_command.column = column; + doc->last_command.page = page_addr; + + switch (command) { + + case NAND_CMD_RESET: + reset(mtd); + break; + + case NAND_CMD_READ0: + read_page_prologue(mtd, g4_addr); + break; + + case NAND_CMD_STATUS: + /* next call to read_byte() will expect a status */ + break; + + case NAND_CMD_SEQIN: + if (unlikely(reliable_mode)) { + uint16_t g4_page = g4_addr >> 16; + + /* writes to odd-numbered 2k pages are invalid */ + if (g4_page & 0x01) + dev_warn(doc->dev, + "invalid reliable mode address\n"); + } + + write_page_prologue(mtd, g4_addr); + + /* hack for deferred write of oob bytes */ + if (doc->oob_page == page_addr) + memcpy(nand->oob_poi, doc->oob_buf, 16); + break; + + case NAND_CMD_PAGEPROG: + pageprog(mtd); + break; + + /* we don't expect these, based on review of nand_base.c */ + case NAND_CMD_READOOB: + case NAND_CMD_READID: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + dev_warn(doc->dev, "docg4_command: " + "unexpected nand command 0x%x\n", command); + break; + + } +} + +static int read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page, bool use_ecc) +{ + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + uint16_t status, edc_err, *buf16; + int bits_corrected = 0; + + dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); + + nand_read_page_op(nand, page, 0, NULL, 0); + + writew(DOC_ECCCONF0_READ_MODE | + DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is page data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + dev_err(doc->dev, + "docg4_read_page: bad status: 0x%02x\n", status); + writew(0, docptr + DOC_DATAEND); + return -EIO; + } + + dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); + + docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */ + + /* this device always reads oob after page data */ + /* first 14 oob bytes read from I/O reg */ + docg4_read_buf(mtd, nand->oob_poi, 14); + + /* last 2 read from another reg */ + buf16 = (uint16_t *)(nand->oob_poi + 14); + *buf16 = readw(docptr + DOCG4_MYSTERY_REG); + + write_nop(docptr); + + if (likely(use_ecc == true)) { + + /* read the register that tells us if bitflip(s) detected */ + edc_err = readw(docptr + DOC_ECCCONF1); + edc_err = readw(docptr + DOC_ECCCONF1); + dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err); + + /* If bitflips are reported, attempt to correct with ecc */ + if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { + bits_corrected = correct_data(mtd, buf, page); + if (bits_corrected == -EBADMSG) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += bits_corrected; + } + } + + writew(0, docptr + DOC_DATAEND); + if (bits_corrected == -EBADMSG) /* uncorrectable errors */ + return 0; + return bits_corrected; +} + + +static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, int page) +{ + return read_page(mtd, nand, buf, page, false); +} + +static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, int page) +{ + return read_page(mtd, nand, buf, page, true); +} + +static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + uint16_t status; + + dev_dbg(doc->dev, "%s: page %x\n", __func__, page); + + nand_read_page_op(nand, page, nand->ecc.size, NULL, 0); + + writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is oob data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + dev_warn(doc->dev, + "docg4_read_oob failed: status = 0x%02x\n", status); + return -EIO; + } + + dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); + + docg4_read_buf(mtd, nand->oob_poi, 16); + + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + + return 0; +} + +static int docg4_erase_block(struct mtd_info *mtd, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + uint16_t g4_page; + int status; + + dev_dbg(doc->dev, "%s: page %04x\n", __func__, page); + + sequence_reset(mtd); + + writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + /* only 2 bytes of address are written to specify erase block */ + g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */ + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + g4_page >>= 8; + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + write_nop(docptr); + + /* start the erasure */ + writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + usleep_range(500, 1000); /* erasure is long; take a snooze */ + poll_status(doc); + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + read_progstatus(doc); + + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); + + status = nand->waitfunc(mtd, nand); + if (status < 0) + return status; + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +static int write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, int page, bool use_ecc) +{ + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + uint8_t ecc_buf[8]; + + dev_dbg(doc->dev, "%s...\n", __func__); + + nand_prog_page_begin_op(nand, page, 0, NULL, 0); + + writew(DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + + /* write the page data */ + docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE); + + /* oob bytes 0 through 5 are written to I/O reg */ + docg4_write_buf16(mtd, nand->oob_poi, 6); + + /* oob byte 6 written to a separate reg */ + writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7); + + write_nop(docptr); + write_nop(docptr); + + /* write hw-generated ecc bytes to oob */ + if (likely(use_ecc == true)) { + /* oob byte 7 is hamming code */ + uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY); + hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */ + writew(hamming, docptr + DOCG4_OOB_6_7); + write_nop(docptr); + + /* read the 7 bch bytes from ecc regs */ + read_hw_ecc(docptr, ecc_buf); + ecc_buf[7] = 0; /* clear the "page written" flag */ + } + + /* write user-supplied bytes to oob */ + else { + writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7); + write_nop(docptr); + memcpy(ecc_buf, &nand->oob_poi[8], 8); + } + + docg4_write_buf16(mtd, ecc_buf, 8); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + + return nand_prog_page_end_op(nand); +} + +static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, int oob_required, int page) +{ + return write_page(mtd, nand, buf, page, false); +} + +static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, int oob_required, int page) +{ + return write_page(mtd, nand, buf, page, true); +} + +static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + /* + * Writing oob-only is not really supported, because MLC nand must write + * oob bytes at the same time as page data. Nonetheless, we save the + * oob buffer contents here, and then write it along with the page data + * if the same page is subsequently written. This allows user space + * utilities that write the oob data prior to the page data to work + * (e.g., nandwrite). The disdvantage is that, if the intention was to + * write oob only, the operation is quietly ignored. Also, oob can get + * corrupted if two concurrent processes are running nandwrite. + */ + + /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */ + struct docg4_priv *doc = nand_get_controller_data(nand); + doc->oob_page = page; + memcpy(doc->oob_buf, nand->oob_poi, 16); + return 0; +} + +static int __init read_factory_bbt(struct mtd_info *mtd) +{ + /* + * The device contains a read-only factory bad block table. Read it and + * update the memory-based bbt accordingly. + */ + + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); + uint8_t *buf; + int i, block; + __u32 eccfailed_stats = mtd->ecc_stats.failed; + + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + read_page_prologue(mtd, g4_addr); + docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); + + /* + * If no memory-based bbt was created, exit. This will happen if module + * parameter ignore_badblocks is set. Then why even call this function? + * For an unknown reason, block erase always fails if it's the first + * operation after device power-up. The above read ensures it never is. + * Ugly, I know. + */ + if (nand->bbt == NULL) /* no memory-based bbt */ + goto exit; + + if (mtd->ecc_stats.failed > eccfailed_stats) { + /* + * Whoops, an ecc failure ocurred reading the factory bbt. + * It is stored redundantly, so we get another chance. + */ + eccfailed_stats = mtd->ecc_stats.failed; + docg4_read_page(mtd, nand, buf, 0, DOCG4_REDUNDANT_BBT_PAGE); + if (mtd->ecc_stats.failed > eccfailed_stats) { + dev_warn(doc->dev, + "The factory bbt could not be read!\n"); + goto exit; + } + } + + /* + * Parse factory bbt and update memory-based bbt. Factory bbt format is + * simple: one bit per block, block numbers increase left to right (msb + * to lsb). Bit clear means bad block. + */ + for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) { + int bitnum; + unsigned long bits = ~buf[i]; + for_each_set_bit(bitnum, &bits, 8) { + int badblock = block + 7 - bitnum; + nand->bbt[badblock / 4] |= + 0x03 << ((badblock % 4) * 2); + mtd->ecc_stats.badblocks++; + dev_notice(doc->dev, "factory-marked bad block: %d\n", + badblock); + } + } + exit: + kfree(buf); + return 0; +} + +static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + /* + * Mark a block as bad. Bad blocks are marked in the oob area of the + * first page of the block. The default scan_bbt() in the nand + * infrastructure code works fine for building the memory-based bbt + * during initialization, as does the nand infrastructure function that + * checks if a block is bad by reading the bbt. This function replaces + * the nand default because writes to oob-only are not supported. + */ + + int ret, i; + uint8_t *buf; + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + struct nand_bbt_descr *bbtd = nand->badblock_pattern; + int page = (int)(ofs >> nand->page_shift); + uint32_t g4_addr = mtd_to_docg4_address(page, 0); + + dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs); + + if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1))) + dev_warn(doc->dev, "%s: ofs %llx not start of block!\n", + __func__, ofs); + + /* allocate blank buffer for page data */ + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + /* write bit-wise negation of pattern to oob buffer */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + for (i = 0; i < bbtd->len; i++) + nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i]; + + /* write first page of block */ + write_page_prologue(mtd, g4_addr); + docg4_write_page(mtd, nand, buf, 1, page); + ret = pageprog(mtd); + + kfree(buf); + + return ret; +} + +static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs) +{ + /* only called when module_param ignore_badblocks is set */ + return 0; +} + +static int docg4_suspend(struct platform_device *pdev, pm_message_t state) +{ + /* + * Put the device into "deep power-down" mode. Note that CE# must be + * deasserted for this to take effect. The xscale, e.g., can be + * configured to float this signal when the processor enters power-down, + * and a suitable pull-up ensures its deassertion. + */ + + int i; + uint8_t pwr_down; + struct docg4_priv *doc = platform_get_drvdata(pdev); + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s...\n", __func__); + + /* poll the register that tells us we're ready to go to sleep */ + for (i = 0; i < 10; i++) { + pwr_down = readb(docptr + DOC_POWERMODE); + if (pwr_down & DOC_POWERDOWN_READY) + break; + usleep_range(1000, 4000); + } + + if (pwr_down & DOC_POWERDOWN_READY) { + dev_err(doc->dev, "suspend failed; " + "timeout polling DOC_POWERDOWN_READY\n"); + return -EIO; + } + + writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + + write_nop(docptr); + + return 0; +} + +static int docg4_resume(struct platform_device *pdev) +{ + + /* + * Exit power-down. Twelve consecutive reads of the address below + * accomplishes this, assuming CE# has been asserted. + */ + + struct docg4_priv *doc = platform_get_drvdata(pdev); + void __iomem *docptr = doc->virtadr; + int i; + + dev_dbg(doc->dev, "%s...\n", __func__); + + for (i = 0; i < 12; i++) + readb(docptr + 0x1fff); + + return 0; +} + +static void __init init_mtd_structs(struct mtd_info *mtd) +{ + /* initialize mtd and nand data structures */ + + /* + * Note that some of the following initializations are not usually + * required within a nand driver because they are performed by the nand + * infrastructure code as part of nand_scan(). In this case they need + * to be initialized here because we skip call to nand_scan_ident() (the + * first half of nand_scan()). The call to nand_scan_ident() is skipped + * because for this device the chip id is not read in the manner of a + * standard nand device. Unfortunately, nand_scan_ident() does other + * things as well, such as call nand_set_defaults(). + */ + + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + + mtd->size = DOCG4_CHIP_SIZE; + mtd->name = "Msys_Diskonchip_G4"; + mtd->writesize = DOCG4_PAGE_SIZE; + mtd->erasesize = DOCG4_BLOCK_SIZE; + mtd->oobsize = DOCG4_OOB_SIZE; + mtd_set_ooblayout(mtd, &docg4_ooblayout_ops); + nand->chipsize = DOCG4_CHIP_SIZE; + nand->chip_shift = DOCG4_CHIP_SHIFT; + nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT; + nand->chip_delay = 20; + nand->page_shift = DOCG4_PAGE_SHIFT; + nand->pagemask = 0x3ffff; + nand->badblockpos = NAND_LARGE_BADBLOCK_POS; + nand->badblockbits = 8; + nand->ecc.mode = NAND_ECC_HW_SYNDROME; + nand->ecc.size = DOCG4_PAGE_SIZE; + nand->ecc.prepad = 8; + nand->ecc.bytes = 8; + nand->ecc.strength = DOCG4_T; + nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; + nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; + nand->controller = &nand->hwcontrol; + nand_hw_control_init(nand->controller); + + /* methods */ + nand->cmdfunc = docg4_command; + nand->waitfunc = docg4_wait; + nand->select_chip = docg4_select_chip; + nand->read_byte = docg4_read_byte; + nand->block_markbad = docg4_block_markbad; + nand->read_buf = docg4_read_buf; + nand->write_buf = docg4_write_buf16; + nand->erase = docg4_erase_block; + nand->onfi_set_features = nand_onfi_get_set_features_notsupp; + nand->onfi_get_features = nand_onfi_get_set_features_notsupp; + nand->ecc.read_page = docg4_read_page; + nand->ecc.write_page = docg4_write_page; + nand->ecc.read_page_raw = docg4_read_page_raw; + nand->ecc.write_page_raw = docg4_write_page_raw; + nand->ecc.read_oob = docg4_read_oob; + nand->ecc.write_oob = docg4_write_oob; + + /* + * The way the nand infrastructure code is written, a memory-based bbt + * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt, + * nand->block_bad() is used. So when ignoring bad blocks, we skip the + * scan and define a dummy block_bad() which always returns 0. + */ + if (ignore_badblocks) { + nand->options |= NAND_SKIP_BBTSCAN; + nand->block_bad = docg4_block_neverbad; + } + +} + +static int __init read_id_reg(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct docg4_priv *doc = nand_get_controller_data(nand); + void __iomem *docptr = doc->virtadr; + uint16_t id1, id2; + + /* check for presence of g4 chip by reading id registers */ + id1 = readw(docptr + DOC_CHIPID); + id1 = readw(docptr + DOCG4_MYSTERY_REG); + id2 = readw(docptr + DOC_CHIPID_INV); + id2 = readw(docptr + DOCG4_MYSTERY_REG); + + if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) { + dev_info(doc->dev, + "NAND device: 128MiB Diskonchip G4 detected\n"); + return 0; + } + + return -ENODEV; +} + +static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL }; + +static int __init probe_docg4(struct platform_device *pdev) +{ + struct mtd_info *mtd; + struct nand_chip *nand; + void __iomem *virtadr; + struct docg4_priv *doc; + int len, retval; + struct resource *r; + struct device *dev = &pdev->dev; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(dev, "no io memory resource defined!\n"); + return -ENODEV; + } + + virtadr = ioremap(r->start, resource_size(r)); + if (!virtadr) { + dev_err(dev, "Diskonchip ioremap failed: %pR\n", r); + return -EIO; + } + + len = sizeof(struct nand_chip) + sizeof(struct docg4_priv); + nand = kzalloc(len, GFP_KERNEL); + if (nand == NULL) { + retval = -ENOMEM; + goto fail_unmap; + } + + mtd = nand_to_mtd(nand); + doc = (struct docg4_priv *) (nand + 1); + nand_set_controller_data(nand, doc); + mtd->dev.parent = &pdev->dev; + doc->virtadr = virtadr; + doc->dev = dev; + + init_mtd_structs(mtd); + + /* initialize kernel bch algorithm */ + doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); + if (doc->bch == NULL) { + retval = -EINVAL; + goto fail; + } + + platform_set_drvdata(pdev, doc); + + reset(mtd); + retval = read_id_reg(mtd); + if (retval == -ENODEV) { + dev_warn(dev, "No diskonchip G4 device found.\n"); + goto fail; + } + + retval = nand_scan_tail(mtd); + if (retval) + goto fail; + + retval = read_factory_bbt(mtd); + if (retval) + goto fail; + + retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); + if (retval) + goto fail; + + doc->mtd = mtd; + return 0; + +fail: + nand_release(mtd); /* deletes partitions and mtd devices */ + free_bch(doc->bch); + kfree(nand); + +fail_unmap: + iounmap(virtadr); + + return retval; +} + +static int __exit cleanup_docg4(struct platform_device *pdev) +{ + struct docg4_priv *doc = platform_get_drvdata(pdev); + nand_release(doc->mtd); + free_bch(doc->bch); + kfree(mtd_to_nand(doc->mtd)); + iounmap(doc->virtadr); + return 0; +} + +static struct platform_driver docg4_driver = { + .driver = { + .name = "docg4", + }, + .suspend = docg4_suspend, + .resume = docg4_resume, + .remove = __exit_p(cleanup_docg4), +}; + +module_platform_driver_probe(docg4_driver, probe_docg4); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Dunn"); +MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver"); diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c new file mode 100644 index 000000000000..8b6dcd739ecb --- /dev/null +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -0,0 +1,979 @@ +/* Freescale Enhanced Local Bus Controller NAND driver + * + * Copyright © 2006-2007, 2010 Freescale Semiconductor + * + * Authors: Nick Spence , + * Scott Wood + * Jack Lan + * Roy Zang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define MAX_BANKS 8 +#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ +#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */ + +/* mtd information per set */ + +struct fsl_elbc_mtd { + struct nand_chip chip; + struct fsl_lbc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + u8 __iomem *vbase; /* Chip select base virtual address */ + int page_size; /* NAND page size (0=512, 1=2048) */ + unsigned int fmr; /* FCM Flash Mode Register value */ +}; + +/* Freescale eLBC FCM controller information */ + +struct fsl_elbc_fcm_ctrl { + struct nand_hw_control controller; + struct fsl_elbc_mtd *chips[MAX_BANKS]; + + u8 __iomem *addr; /* Address of assigned FCM buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes; /* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int status; /* status read from LTESR after last op */ + unsigned int mdr; /* UPM/FCM Data Register value */ + unsigned int use_mdr; /* Non zero if the MDR is to be set */ + unsigned int oob; /* Non zero if operating on OOB data */ + unsigned int counter; /* counter for the initializations */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ +}; + +/* These map to the positions used by the FCM hardware ECC generator */ + +static int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (16 * section) + 6; + if (priv->fmr & FMR_ECCM) + oobregion->offset += 2; + + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + + if (section > chip->ecc.steps) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + if (mtd->writesize > 512) + oobregion->offset++; + oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5; + } else { + oobregion->offset = (16 * section) - + ((priv->fmr & FMR_ECCM) ? 5 : 7); + if (section < chip->ecc.steps) + oobregion->length = 13; + else + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = { + .ecc = fsl_elbc_ooblayout_ecc, + .free = fsl_elbc_ooblayout_free, +}; + +/* + * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, + * interfere with ECC positions, that's why we implement our own descriptors. + * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/*=================================*/ + +/* + * Set up the FCM hardware block and page address fields, and the fcm + * structure addr field to point to the correct FCM buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; + int buf_num; + + elbc_fcm_ctrl->page = page_addr; + + if (priv->page_size) { + /* + * large page size chip : FPAR[PI] save the lowest 6 bits, + * FBAR[BLK] save the other bits. + */ + out_be32(&lbc->fbar, page_addr >> 6); + out_be32(&lbc->fpar, + ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | + (oob ? FPAR_LP_MS : 0) | column); + buf_num = (page_addr & 1) << 2; + } else { + /* + * small page size chip : FPAR[PI] save the lowest 5 bits, + * FBAR[BLK] save the other bits. + */ + out_be32(&lbc->fbar, page_addr >> 5); + out_be32(&lbc->fpar, + ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | + (oob ? FPAR_SP_MS : 0) | column); + buf_num = page_addr & 7; + } + + elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024; + elbc_fcm_ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512; + + dev_vdbg(priv->dev, "set_addr: bank=%d, " + "elbc_fcm_ctrl->addr=0x%p (0x%p), " + "index %x, pes %d ps %d\n", + buf_num, elbc_fcm_ctrl->addr, priv->vbase, + elbc_fcm_ctrl->index, + chip->phys_erase_shift, chip->page_shift); +} + +/* + * execute FCM command and wait for it to complete + */ +static int fsl_elbc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + + /* Setup the FMR[OP] to execute without write protection */ + out_be32(&lbc->fmr, priv->fmr | 3); + if (elbc_fcm_ctrl->use_mdr) + out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr); + + dev_vdbg(priv->dev, + "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", + in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); + dev_vdbg(priv->dev, + "fsl_elbc_run_command: fbar=%08x fpar=%08x " + "fbcr=%08x bank=%d\n", + in_be32(&lbc->fbar), in_be32(&lbc->fpar), + in_be32(&lbc->fbcr), priv->bank); + + ctrl->irq_status = 0; + /* execute special operation */ + out_be32(&lbc->lsor, priv->bank); + + /* wait for FCM complete flag or timeout */ + wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, + FCM_TIMEOUT_MSECS * HZ/1000); + elbc_fcm_ctrl->status = ctrl->irq_status; + /* store mdr value in case it was needed */ + if (elbc_fcm_ctrl->use_mdr) + elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr); + + elbc_fcm_ctrl->use_mdr = 0; + + if (elbc_fcm_ctrl->status != LTESR_CC) { + dev_info(priv->dev, + "command failed: fir %x fcr %x status %x mdr %x\n", + in_be32(&lbc->fir), in_be32(&lbc->fcr), + elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr); + return -EIO; + } + + if (chip->ecc.mode != NAND_ECC_HW) + return 0; + + elbc_fcm_ctrl->max_bitflips = 0; + + if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) { + uint32_t lteccr = in_be32(&lbc->lteccr); + /* + * if command was a full page read and the ELBC + * has the LTECCR register, then bits 12-15 (ppc order) of + * LTECCR indicates which 512 byte sub-pages had fixed errors. + * bits 28-31 are uncorrectable errors, marked elsewhere. + * for small page nand only 1 bit is used. + * if the ELBC doesn't have the lteccr register it reads 0 + * FIXME: 4 bits can be corrected on NANDs with 2k pages, so + * count the number of sub-pages with bitflips and update + * ecc_stats.corrected accordingly. + */ + if (lteccr & 0x000F000F) + out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */ + if (lteccr & 0x000F0000) { + mtd->ecc_stats.corrected++; + elbc_fcm_ctrl->max_bitflips = 1; + } + } + + return 0; +} + +static void fsl_elbc_do_read(struct nand_chip *chip, int oob) +{ + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + + if (priv->page_size) { + out_be32(&lbc->fir, + (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_CM1 << FIR_OP3_SHIFT) | + (FIR_OP_RBW << FIR_OP4_SHIFT)); + + out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); + } else { + out_be32(&lbc->fir, + (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_RBW << FIR_OP3_SHIFT)); + + if (oob) + out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT); + else + out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); + } +} + +/* cmdfunc send commands to the FCM */ +static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + + elbc_fcm_ctrl->use_mdr = 0; + + /* clear the read buffer */ + elbc_fcm_ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + elbc_fcm_ctrl->index = 0; + + switch (command) { + /* READ0 and READ1 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ1: + column += 256; + + /* fall-through */ + case NAND_CMD_READ0: + dev_dbg(priv->dev, + "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + + out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ + set_addr(mtd, 0, page_addr, 0); + + elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + elbc_fcm_ctrl->index += column; + + fsl_elbc_do_read(chip, 0); + fsl_elbc_run_command(mtd); + return; + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + dev_vdbg(priv->dev, + "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + out_be32(&lbc->fbcr, mtd->oobsize - column); + set_addr(mtd, column, page_addr, 1); + + elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_elbc_do_read(chip, 1); + fsl_elbc_run_command(mtd); + return; + + case NAND_CMD_READID: + case NAND_CMD_PARAM: + dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD %x\n", command); + + out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_UA << FIR_OP1_SHIFT) | + (FIR_OP_RBW << FIR_OP2_SHIFT)); + out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + out_be32(&lbc->fbcr, 256); + elbc_fcm_ctrl->read_bytes = 256; + elbc_fcm_ctrl->use_mdr = 1; + elbc_fcm_ctrl->mdr = column; + set_addr(mtd, 0, 0, 0); + fsl_elbc_run_command(mtd); + return; + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + dev_vdbg(priv->dev, + "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " + "page_addr: 0x%x.\n", page_addr); + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); + + out_be32(&lbc->fir, + (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_PA << FIR_OP1_SHIFT) | + (FIR_OP_CM2 << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RS << FIR_OP4_SHIFT)); + + out_be32(&lbc->fcr, + (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | + (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | + (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT)); + + out_be32(&lbc->fbcr, 0); + elbc_fcm_ctrl->read_bytes = 0; + elbc_fcm_ctrl->use_mdr = 1; + + fsl_elbc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + __be32 fcr; + dev_vdbg(priv->dev, + "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " + "page_addr: 0x%x, column: 0x%x.\n", + page_addr, column); + + elbc_fcm_ctrl->column = column; + elbc_fcm_ctrl->use_mdr = 1; + + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + elbc_fcm_ctrl->oob = 1; + } else { + WARN_ON(column != 0); + elbc_fcm_ctrl->oob = 0; + } + + fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | + (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) | + (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT); + + if (priv->page_size) { + out_be32(&lbc->fir, + (FIR_OP_CM2 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_WB << FIR_OP3_SHIFT) | + (FIR_OP_CM3 << FIR_OP4_SHIFT) | + (FIR_OP_CW1 << FIR_OP5_SHIFT) | + (FIR_OP_RS << FIR_OP6_SHIFT)); + } else { + out_be32(&lbc->fir, + (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_CM2 << FIR_OP1_SHIFT) | + (FIR_OP_CA << FIR_OP2_SHIFT) | + (FIR_OP_PA << FIR_OP3_SHIFT) | + (FIR_OP_WB << FIR_OP4_SHIFT) | + (FIR_OP_CM3 << FIR_OP5_SHIFT) | + (FIR_OP_CW1 << FIR_OP6_SHIFT) | + (FIR_OP_RS << FIR_OP7_SHIFT)); + + if (elbc_fcm_ctrl->oob) + /* OOB area --> READOOB */ + fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; + else + /* First 256 bytes --> READ0 */ + fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; + } + + out_be32(&lbc->fcr, fcr); + set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: { + dev_vdbg(priv->dev, + "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " + "writing %d bytes.\n", elbc_fcm_ctrl->index); + + /* if the write did not start at 0 or is not a full page + * then set the exact length, otherwise use a full page + * write so the HW generates the ECC. + */ + if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 || + elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) + out_be32(&lbc->fbcr, + elbc_fcm_ctrl->index - elbc_fcm_ctrl->column); + else + out_be32(&lbc->fbcr, 0); + + fsl_elbc_run_command(mtd); + return; + } + + /* CMD_STATUS must read the status byte while CEB is active */ + /* Note - it does not wait for the ready line */ + case NAND_CMD_STATUS: + out_be32(&lbc->fir, + (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); + out_be32(&lbc->fbcr, 1); + set_addr(mtd, 0, 0, 0); + elbc_fcm_ctrl->read_bytes = 1; + + fsl_elbc_run_command(mtd); + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP); + return; + + /* RESET without waiting for the ready line */ + case NAND_CMD_RESET: + dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); + out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); + out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); + fsl_elbc_run_command(mtd); + return; + + default: + dev_err(priv->dev, + "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", + command); + } +} + +static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip) +{ + /* The hardware does not seem to support multiple + * chips per bank. + */ +} + +/* + * Write buf to the FCM Controller Data Buffer + */ +static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len <= 0) { + dev_err(priv->dev, "write_buf of %d bytes", len); + elbc_fcm_ctrl->status = 0; + return; + } + + if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) { + dev_err(priv->dev, + "write_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, bufsize - elbc_fcm_ctrl->index); + len = bufsize - elbc_fcm_ctrl->index; + } + + memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len); + /* + * This is workaround for the weird elbc hangs during nand write, + * Scott Wood says: "...perhaps difference in how long it takes a + * write to make it through the localbus compared to a write to IMMR + * is causing problems, and sync isn't helping for some reason." + * Reading back the last byte helps though. + */ + in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1); + + elbc_fcm_ctrl->index += len; +} + +/* + * read a byte from either the FCM hardware buffer if it has any data left + * otherwise issue a command to read a single byte. + */ +static u8 fsl_elbc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; + + /* If there are still bytes in the FCM, then use the next byte. */ + if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes) + return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]); + + dev_err(priv->dev, "read_byte beyond end of buffer\n"); + return ERR_BYTE; +} + +/* + * Read from the FCM Controller Data Buffer + */ +static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; + int avail; + + if (len < 0) + return; + + avail = min((unsigned int)len, + elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index); + memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail); + elbc_fcm_ctrl->index += avail; + + if (len > avail) + dev_err(priv->dev, + "read_buf beyond end of buffer " + "(%d requested, %d available)\n", + len, avail); +} + +/* This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; + + if (elbc_fcm_ctrl->status != LTESR_CC) + return NAND_STATUS_FAIL; + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP; +} + +static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + unsigned int al; + + /* calculate FMR Address Length field */ + al = 0; + if (chip->pagemask & 0xffff0000) + al++; + if (chip->pagemask & 0xff000000) + al++; + + priv->fmr |= al << FMR_AL_SHIFT; + + dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n", + chip->numchips); + dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n", + chip->chipsize); + dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n", + chip->pagemask); + dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n", + chip->chip_delay); + dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n", + chip->badblockpos); + dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n", + chip->chip_shift); + dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n", + chip->page_shift); + dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", + chip->phys_erase_shift); + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", + chip->ecc.mode); + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", + chip->ecc.steps); + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", + chip->ecc.bytes); + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n", + chip->ecc.total); + dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n", + mtd->ooblayout); + dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); + dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size); + dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n", + mtd->erasesize); + dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n", + mtd->writesize); + dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n", + mtd->oobsize); + + /* adjust Option Register and ECC to match Flash page size */ + if (mtd->writesize == 512) { + priv->page_size = 0; + clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); + } else if (mtd->writesize == 2048) { + priv->page_size = 1; + setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); + } else { + dev_err(priv->dev, + "fsl_elbc_init: page size %d is not supported\n", + mtd->writesize); + return -1; + } + + return 0; +} + +static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; + + nand_read_page_op(chip, page, 0, buf, mtd->writesize); + if (oob_required) + fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) + mtd->ecc_stats.failed++; + + return elbc_fcm_ctrl->max_bitflips; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, uint32_t data_len, + const uint8_t *buf, int oob_required, int page) +{ + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + fsl_elbc_write_buf(mtd, buf, mtd->writesize); + fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + return nand_prog_page_end_op(chip); +} + +static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) +{ + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; + struct nand_chip *chip = &priv->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + + dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank); + + /* Fill in fsl_elbc_mtd structure */ + mtd->dev.parent = priv->dev; + nand_set_flash_node(chip, priv->dev->of_node); + + /* set timeout to maximum */ + priv->fmr = 15 << FMR_CWTO_SHIFT; + if (in_be32(&lbc->bank[priv->bank].or) & OR_FCM_PGS) + priv->fmr |= FMR_ECCM; + + /* fill in nand_chip structure */ + /* set up function call table */ + chip->read_byte = fsl_elbc_read_byte; + chip->write_buf = fsl_elbc_write_buf; + chip->read_buf = fsl_elbc_read_buf; + chip->select_chip = fsl_elbc_select_chip; + chip->cmdfunc = fsl_elbc_cmdfunc; + chip->waitfunc = fsl_elbc_wait; + chip->onfi_set_features = nand_onfi_get_set_features_notsupp; + chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + + /* set up nand options */ + chip->bbt_options = NAND_BBT_USE_FLASH; + + chip->controller = &elbc_fcm_ctrl->controller; + nand_set_controller_data(chip, priv); + + chip->ecc.read_page = fsl_elbc_read_page; + chip->ecc.write_page = fsl_elbc_write_page; + chip->ecc.write_subpage = fsl_elbc_write_subpage; + + /* If CS Base Register selects full hardware ECC then use it */ + if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == + BR_DECC_CHK_GEN) { + chip->ecc.mode = NAND_ECC_HW; + mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops); + chip->ecc.size = 512; + chip->ecc.bytes = 3; + chip->ecc.strength = 1; + } else { + /* otherwise fall back to default software ECC */ + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + } + + return 0; +} + +static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) +{ + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; + struct mtd_info *mtd = nand_to_mtd(&priv->chip); + + nand_release(mtd); + + kfree(mtd->name); + + if (priv->vbase) + iounmap(priv->vbase); + + elbc_fcm_ctrl->chips[priv->bank] = NULL; + kfree(priv); + return 0; +} + +static DEFINE_MUTEX(fsl_elbc_nand_mutex); + +static int fsl_elbc_nand_probe(struct platform_device *pdev) +{ + struct fsl_lbc_regs __iomem *lbc; + struct fsl_elbc_mtd *priv; + struct resource res; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl; + static const char *part_probe_types[] + = { "cmdlinepart", "RedBoot", "ofpart", NULL }; + int ret; + int bank; + struct device *dev; + struct device_node *node = pdev->dev.of_node; + struct mtd_info *mtd; + + if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) + return -ENODEV; + lbc = fsl_lbc_ctrl_dev->regs; + dev = fsl_lbc_ctrl_dev->dev; + + /* get, allocate and map the memory resource */ + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(dev, "failed to get resource\n"); + return ret; + } + + /* find which chip select it is connected to */ + for (bank = 0; bank < MAX_BANKS; bank++) + if ((in_be32(&lbc->bank[bank].br) & BR_V) && + (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM && + (in_be32(&lbc->bank[bank].br) & + in_be32(&lbc->bank[bank].or) & BR_BA) + == fsl_lbc_addr(res.start)) + break; + + if (bank >= MAX_BANKS) { + dev_err(dev, "address did not match any chip selects\n"); + return -ENODEV; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_lock(&fsl_elbc_nand_mutex); + if (!fsl_lbc_ctrl_dev->nand) { + elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL); + if (!elbc_fcm_ctrl) { + mutex_unlock(&fsl_elbc_nand_mutex); + ret = -ENOMEM; + goto err; + } + elbc_fcm_ctrl->counter++; + + nand_hw_control_init(&elbc_fcm_ctrl->controller); + fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl; + } else { + elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; + } + mutex_unlock(&fsl_elbc_nand_mutex); + + elbc_fcm_ctrl->chips[bank] = priv; + priv->bank = bank; + priv->ctrl = fsl_lbc_ctrl_dev; + priv->dev = &pdev->dev; + dev_set_drvdata(priv->dev, priv); + + priv->vbase = ioremap(res.start, resource_size(&res)); + if (!priv->vbase) { + dev_err(dev, "failed to map chip region\n"); + ret = -ENOMEM; + goto err; + } + + mtd = nand_to_mtd(&priv->chip); + mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); + if (!nand_to_mtd(&priv->chip)->name) { + ret = -ENOMEM; + goto err; + } + + ret = fsl_elbc_chip_init(priv); + if (ret) + goto err; + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + goto err; + + ret = fsl_elbc_chip_init_tail(mtd); + if (ret) + goto err; + + ret = nand_scan_tail(mtd); + if (ret) + goto err; + + /* First look for RedBoot table or partitions on the command + * line, these take precedence over device tree information */ + mtd_device_parse_register(mtd, part_probe_types, NULL, + NULL, 0); + + printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n", + (unsigned long long)res.start, priv->bank); + return 0; + +err: + fsl_elbc_chip_remove(priv); + return ret; +} + +static int fsl_elbc_nand_remove(struct platform_device *pdev) +{ + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; + struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev); + + fsl_elbc_chip_remove(priv); + + mutex_lock(&fsl_elbc_nand_mutex); + elbc_fcm_ctrl->counter--; + if (!elbc_fcm_ctrl->counter) { + fsl_lbc_ctrl_dev->nand = NULL; + kfree(elbc_fcm_ctrl); + } + mutex_unlock(&fsl_elbc_nand_mutex); + + return 0; + +} + +static const struct of_device_id fsl_elbc_nand_match[] = { + { .compatible = "fsl,elbc-fcm-nand", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_elbc_nand_match); + +static struct platform_driver fsl_elbc_nand_driver = { + .driver = { + .name = "fsl,elbc-fcm-nand", + .of_match_table = fsl_elbc_nand_match, + }, + .probe = fsl_elbc_nand_probe, + .remove = fsl_elbc_nand_remove, +}; + +module_platform_driver(fsl_elbc_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale"); +MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver"); diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c new file mode 100644 index 000000000000..4872a7ba6503 --- /dev/null +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -0,0 +1,1117 @@ +/* + * Freescale Integrated Flash Controller NAND driver + * + * Copyright 2011-2012 Freescale Semiconductor, Inc + * + * Author: Dipen Dudhat + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ERR_BYTE 0xFF /* Value returned for read + bytes when read failed */ +#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait + for IFC NAND Machine */ + +struct fsl_ifc_ctrl; + +/* mtd information per set */ +struct fsl_ifc_mtd { + struct nand_chip chip; + struct fsl_ifc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ + u8 __iomem *vbase; /* Chip select base virtual address */ +}; + +/* overview of the fsl ifc controller */ +struct fsl_ifc_nand_ctrl { + struct nand_hw_control controller; + struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT]; + + void __iomem *addr; /* Address of assigned IFC buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes;/* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int oob; /* Non zero if operating on OOB data */ + unsigned int eccread; /* Non zero for a full-page ECC read */ + unsigned int counter; /* counter for the initializations */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ +}; + +static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; + +/* + * Generic flash bbt descriptors + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +static int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = 8; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section > 1) + return -ERANGE; + + if (mtd->writesize == 512 && + !(chip->options & NAND_BUSWIDTH_16)) { + if (!section) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 6; + oobregion->length = 2; + } + + return 0; + } + + if (!section) { + oobregion->offset = 2; + oobregion->length = 6; + } else { + oobregion->offset = chip->ecc.total + 8; + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = { + .ecc = fsl_ifc_ooblayout_ecc, + .free = fsl_ifc_ooblayout_free, +}; + +/* + * Set up the IFC hardware block and page address fields, and the ifc nand + * structure addr field to point to the correct IFC buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; + int buf_num; + + ifc_nand_ctrl->page = page_addr; + /* Program ROW0/COL0 */ + ifc_out32(page_addr, &ifc->ifc_nand.row0); + ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); + + buf_num = page_addr & priv->bufnum_mask; + + ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); + ifc_nand_ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ifc_nand_ctrl->index += mtd->writesize; +} + +/* returns nonzero if entire page is blank */ +static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, + u32 *eccstat, unsigned int bufnum) +{ + u32 reg = eccstat[bufnum / 4]; + int errors; + + errors = (reg >> ((3 - bufnum % 4) * 8)) & 15; + + return errors; +} + +/* + * execute IFC NAND command and wait for it to complete + */ +static void fsl_ifc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; + u32 eccstat[4]; + int i; + + /* set the chip select for NAND Transaction */ + ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT, + &ifc->ifc_nand.nand_csel); + + dev_vdbg(priv->dev, + "%s: fir0=%08x fcr0=%08x\n", + __func__, + ifc_in32(&ifc->ifc_nand.nand_fir0), + ifc_in32(&ifc->ifc_nand.nand_fcr0)); + + ctrl->nand_stat = 0; + + /* start read/write seq */ + ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); + + /* wait for command complete flag or timeout */ + wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, + msecs_to_jiffies(IFC_TIMEOUT_MSECS)); + + /* ctrl->nand_stat will be updated from IRQ context */ + if (!ctrl->nand_stat) + dev_err(priv->dev, "Controller is not responding\n"); + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER) + dev_err(priv->dev, "NAND Flash Timeout Error\n"); + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) + dev_err(priv->dev, "NAND Flash Write Protect Error\n"); + + nctrl->max_bitflips = 0; + + if (nctrl->eccread) { + int errors; + int bufnum = nctrl->page & priv->bufnum_mask; + int sector = bufnum * chip->ecc.steps; + int sector_end = sector + chip->ecc.steps - 1; + __be32 *eccstat_regs; + + if (ctrl->version >= FSL_IFC_VERSION_2_0_0) + eccstat_regs = ifc->ifc_nand.v2_nand_eccstat; + else + eccstat_regs = ifc->ifc_nand.v1_nand_eccstat; + + for (i = sector / 4; i <= sector_end / 4; i++) + eccstat[i] = ifc_in32(&eccstat_regs[i]); + + for (i = sector; i <= sector_end; i++) { + errors = check_read_ecc(mtd, ctrl, eccstat, i); + + if (errors == 15) { + /* + * Uncorrectable error. + * We'll check for blank pages later. + * + * We disable ECCER reporting due to... + * erratum IFC-A002770 -- so report it now if we + * see an uncorrectable error in ECCSTAT. + */ + ctrl->nand_stat |= IFC_NAND_EVTER_STAT_ECCER; + continue; + } + + mtd->ecc_stats.corrected += errors; + nctrl->max_bitflips = max_t(unsigned int, + nctrl->max_bitflips, + errors); + } + + nctrl->eccread = 0; + } +} + +static void fsl_ifc_do_read(struct nand_chip *chip, + int oob, + struct mtd_info *mtd) +{ + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; + + /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ + if (mtd->writesize > 512) { + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); + + ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), + &ifc->ifc_nand.nand_fcr0); + } else { + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); + + if (oob) + ifc_out32(NAND_CMD_READOOB << + IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + else + ifc_out32(NAND_CMD_READ0 << + IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + } +} + +/* cmdfunc send commands to the IFC NAND Machine */ +static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; + + /* clear the read buffer */ + ifc_nand_ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ifc_nand_ctrl->index = 0; + + switch (command) { + /* READ0 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ0: + ifc_out32(0, &ifc->ifc_nand.nand_fbcr); + set_addr(mtd, 0, page_addr, 0); + + ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ifc_nand_ctrl->index += column; + + if (chip->ecc.mode == NAND_ECC_HW) + ifc_nand_ctrl->eccread = 1; + + fsl_ifc_do_read(chip, 0, mtd); + fsl_ifc_run_command(mtd); + return; + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); + set_addr(mtd, column, page_addr, 1); + + ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_ifc_do_read(chip, 1, mtd); + fsl_ifc_run_command(mtd); + + return; + + case NAND_CMD_READID: + case NAND_CMD_PARAM: { + int timing = IFC_FIR_OP_RB; + if (command == NAND_CMD_PARAM) + timing = IFC_FIR_OP_RBCD; + + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (timing << IFC_NAND_FIR0_OP2_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + ifc_out32(column, &ifc->ifc_nand.row3); + + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + ifc_out32(256, &ifc->ifc_nand.nand_fbcr); + ifc_nand_ctrl->read_bytes = 256; + + set_addr(mtd, 0, 0, 0); + fsl_ifc_run_command(mtd); + return; + } + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), + &ifc->ifc_nand.nand_fir0); + + ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), + &ifc->ifc_nand.nand_fcr0); + + ifc_out32(0, &ifc->ifc_nand.nand_fbcr); + ifc_nand_ctrl->read_bytes = 0; + fsl_ifc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + u32 nand_fcr0; + ifc_nand_ctrl->column = column; + ifc_nand_ctrl->oob = 0; + + if (mtd->writesize > 512) { + nand_fcr0 = + (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); + + ifc_out32( + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32( + (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), + &ifc->ifc_nand.nand_fir1); + } else { + nand_fcr0 = ((NAND_CMD_PAGEPROG << + IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_SEQIN << + IFC_NAND_FCR0_CMD2_SHIFT) | + (NAND_CMD_STATUS << + IFC_NAND_FCR0_CMD3_SHIFT)); + + ifc_out32( + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32( + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), + &ifc->ifc_nand.nand_fir1); + + if (column >= mtd->writesize) + nand_fcr0 |= + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; + else + nand_fcr0 |= + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; + } + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + ifc_nand_ctrl->oob = 1; + } + ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0); + set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: { + if (ifc_nand_ctrl->oob) { + ifc_out32(ifc_nand_ctrl->index - + ifc_nand_ctrl->column, + &ifc->ifc_nand.nand_fbcr); + } else { + ifc_out32(0, &ifc->ifc_nand.nand_fbcr); + } + + fsl_ifc_run_command(mtd); + return; + } + + case NAND_CMD_STATUS: { + void __iomem *addr; + + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + ifc_out32(1, &ifc->ifc_nand.nand_fbcr); + set_addr(mtd, 0, 0, 0); + ifc_nand_ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + /* + * The chip always seems to report that it is + * write-protected, even when it is not. + */ + addr = ifc_nand_ctrl->addr; + if (chip->options & NAND_BUSWIDTH_16) + ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr); + else + ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr); + return; + } + + case NAND_CMD_RESET: + ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, + &ifc->ifc_nand.nand_fir0); + ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + fsl_ifc_run_command(mtd); + return; + + default: + dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n", + __func__, command); + } +} + +static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) +{ + /* The hardware does not seem to support multiple + * chips per bank. + */ +} + +/* + * Write buf to the IFC NAND Controller Data Buffer + */ +static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len <= 0) { + dev_err(priv->dev, "%s: len %d bytes", __func__, len); + return; + } + + if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) { + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %u available)\n", + __func__, len, bufsize - ifc_nand_ctrl->index); + len = bufsize - ifc_nand_ctrl->index; + } + + memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len); + ifc_nand_ctrl->index += len; +} + +/* + * Read a byte from either the IFC hardware buffer + * read function for 8-bit buswidth + */ +static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + unsigned int offset; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { + offset = ifc_nand_ctrl->index++; + return ifc_in8(ifc_nand_ctrl->addr + offset); + } + + dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read two bytes from the IFC hardware buffer + * read function for 16-bit buswith + */ +static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + uint16_t data; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { + data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); + ifc_nand_ctrl->index += 2; + return (uint8_t) data; + } + + dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read from the IFC Controller Data Buffer + */ +static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + int avail; + + if (len < 0) { + dev_err(priv->dev, "%s: len %d bytes", __func__, len); + return; + } + + avail = min((unsigned int)len, + ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index); + memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail); + ifc_nand_ctrl->index += avail; + + if (len > avail) + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %d available)\n", + __func__, len, avail); +} + +/* + * This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; + u32 nand_fsr; + + /* Use READ_STATUS command, but wait for the device to be ready */ + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + ifc_out32(1, &ifc->ifc_nand.nand_fbcr); + set_addr(mtd, 0, 0, 0); + ifc_nand_ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); + + /* + * The chip always seems to report that it is + * write-protected, even when it is not. + */ + return nand_fsr | NAND_STATUS_WP; +} + +/* + * The controller does not check for bitflips in erased pages, + * therefore software must check instead. + */ +static int check_erased_page(struct nand_chip *chip, u8 *buf) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u8 *ecc = chip->oob_poi; + const int ecc_size = chip->ecc.bytes; + const int pkt_size = chip->ecc.size; + int i, res, bitflips = 0; + struct mtd_oob_region oobregion = { }; + + mtd_ooblayout_ecc(mtd, 0, &oobregion); + ecc += oobregion.offset; + + for (i = 0; i < chip->ecc.steps; ++i) { + res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, + NULL, 0, + chip->ecc.strength); + if (res < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += res; + + bitflips = max(res, bitflips); + buf += pkt_size; + ecc += ecc_size; + } + + return bitflips; +} + +static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; + + nand_read_page_op(chip, page, 0, buf, mtd->writesize); + if (oob_required) + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) { + if (!oob_required) + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return check_erased_page(chip, buf); + } + + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + mtd->ecc_stats.failed++; + + return nctrl->max_bitflips; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + + dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, + chip->numchips); + dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__, + chip->chipsize); + dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__, + chip->pagemask); + dev_dbg(priv->dev, "%s: nand->chip_delay = %d\n", __func__, + chip->chip_delay); + dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__, + chip->badblockpos); + dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__, + chip->chip_shift); + dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__, + chip->page_shift); + dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, + chip->phys_erase_shift); + dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__, + chip->ecc.mode); + dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, + chip->ecc.steps); + dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__, + chip->ecc.bytes); + dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__, + chip->ecc.total); + dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__, + mtd->ooblayout); + dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags); + dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size); + dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__, + mtd->erasesize); + dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__, + mtd->writesize); + dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__, + mtd->oobsize); + + return 0; +} + +static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) +{ + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; + struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; + uint32_t csor = 0, csor_8k = 0, csor_ext = 0; + uint32_t cs = priv->bank; + + /* Save CSOR and CSOR_ext */ + csor = ifc_in32(&ifc_global->csor_cs[cs].csor); + csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext); + + /* chage PageSize 8K and SpareSize 1K*/ + csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; + ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor); + ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext); + + /* READID */ + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), + &ifc_runtime->ifc_nand.nand_fir0); + ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc_runtime->ifc_nand.nand_fcr0); + ifc_out32(0x0, &ifc_runtime->ifc_nand.row3); + + ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr); + + /* Program ROW0/COL0 */ + ifc_out32(0x0, &ifc_runtime->ifc_nand.row0); + ifc_out32(0x0, &ifc_runtime->ifc_nand.col0); + + /* set the chip select for NAND Transaction */ + ifc_out32(cs << IFC_NAND_CSEL_SHIFT, + &ifc_runtime->ifc_nand.nand_csel); + + /* start read seq */ + ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, + &ifc_runtime->ifc_nand.nandseq_strt); + + /* wait for command complete flag or timeout */ + wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, + msecs_to_jiffies(IFC_TIMEOUT_MSECS)); + + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); + + /* Restore CSOR and CSOR_ext */ + ifc_out32(csor, &ifc_global->csor_cs[cs].csor); + ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext); +} + +static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) +{ + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; + struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; + struct nand_chip *chip = &priv->chip; + struct mtd_info *mtd = nand_to_mtd(&priv->chip); + u32 csor; + + /* Fill in fsl_ifc_mtd structure */ + mtd->dev.parent = priv->dev; + nand_set_flash_node(chip, priv->dev->of_node); + + /* fill in nand_chip structure */ + /* set up function call table */ + if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)) + & CSPR_PORT_SIZE_16) + chip->read_byte = fsl_ifc_read_byte16; + else + chip->read_byte = fsl_ifc_read_byte; + + chip->write_buf = fsl_ifc_write_buf; + chip->read_buf = fsl_ifc_read_buf; + chip->select_chip = fsl_ifc_select_chip; + chip->cmdfunc = fsl_ifc_cmdfunc; + chip->waitfunc = fsl_ifc_wait; + chip->onfi_set_features = nand_onfi_get_set_features_notsupp; + chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + + ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr); + + /* set up nand options */ + chip->bbt_options = NAND_BBT_USE_FLASH; + chip->options = NAND_NO_SUBPAGE_WRITE; + + if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr) + & CSPR_PORT_SIZE_16) { + chip->read_byte = fsl_ifc_read_byte16; + chip->options |= NAND_BUSWIDTH_16; + } else { + chip->read_byte = fsl_ifc_read_byte; + } + + chip->controller = &ifc_nand_ctrl->controller; + nand_set_controller_data(chip, priv); + + chip->ecc.read_page = fsl_ifc_read_page; + chip->ecc.write_page = fsl_ifc_write_page; + + csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); + + switch (csor & CSOR_NAND_PGS_MASK) { + case CSOR_NAND_PGS_512: + if (!(chip->options & NAND_BUSWIDTH_16)) { + /* Avoid conflict with bad block marker */ + bbt_main_descr.offs = 0; + bbt_mirror_descr.offs = 0; + } + + priv->bufnum_mask = 15; + break; + + case CSOR_NAND_PGS_2K: + priv->bufnum_mask = 3; + break; + + case CSOR_NAND_PGS_4K: + priv->bufnum_mask = 1; + break; + + case CSOR_NAND_PGS_8K: + priv->bufnum_mask = 0; + break; + + default: + dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); + return -ENODEV; + } + + /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ + if (csor & CSOR_NAND_ECC_DEC_EN) { + chip->ecc.mode = NAND_ECC_HW; + mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); + + /* Hardware generates ECC per 512 Bytes */ + chip->ecc.size = 512; + if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) { + chip->ecc.bytes = 8; + chip->ecc.strength = 4; + } else { + chip->ecc.bytes = 16; + chip->ecc.strength = 8; + } + } else { + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + } + + if (ctrl->version >= FSL_IFC_VERSION_1_1_0) + fsl_ifc_sram_init(priv); + + /* + * As IFC version 2.0.0 has 16KB of internal SRAM as compared to older + * versions which had 8KB. Hence bufnum mask needs to be updated. + */ + if (ctrl->version >= FSL_IFC_VERSION_2_0_0) + priv->bufnum_mask = (priv->bufnum_mask * 2) + 1; + + return 0; +} + +static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) +{ + struct mtd_info *mtd = nand_to_mtd(&priv->chip); + + nand_release(mtd); + + kfree(mtd->name); + + if (priv->vbase) + iounmap(priv->vbase); + + ifc_nand_ctrl->chips[priv->bank] = NULL; + + return 0; +} + +static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank, + phys_addr_t addr) +{ + u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr); + + if (!(cspr & CSPR_V)) + return 0; + if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND) + return 0; + + return (cspr & CSPR_BA) == convert_ifc_address(addr); +} + +static DEFINE_MUTEX(fsl_ifc_nand_mutex); + +static int fsl_ifc_nand_probe(struct platform_device *dev) +{ + struct fsl_ifc_runtime __iomem *ifc; + struct fsl_ifc_mtd *priv; + struct resource res; + static const char *part_probe_types[] + = { "cmdlinepart", "RedBoot", "ofpart", NULL }; + int ret; + int bank; + struct device_node *node = dev->dev.of_node; + struct mtd_info *mtd; + + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) + return -ENODEV; + ifc = fsl_ifc_ctrl_dev->rregs; + + /* get, allocate and map the memory resource */ + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(&dev->dev, "%s: failed to get resource\n", __func__); + return ret; + } + + /* find which chip select it is connected to */ + for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { + if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) + break; + } + + if (bank >= fsl_ifc_ctrl_dev->banks) { + dev_err(&dev->dev, "%s: address did not match any chip selects\n", + __func__); + return -ENODEV; + } + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_lock(&fsl_ifc_nand_mutex); + if (!fsl_ifc_ctrl_dev->nand) { + ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL); + if (!ifc_nand_ctrl) { + mutex_unlock(&fsl_ifc_nand_mutex); + return -ENOMEM; + } + + ifc_nand_ctrl->read_bytes = 0; + ifc_nand_ctrl->index = 0; + ifc_nand_ctrl->addr = NULL; + fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; + + nand_hw_control_init(&ifc_nand_ctrl->controller); + } else { + ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; + } + mutex_unlock(&fsl_ifc_nand_mutex); + + ifc_nand_ctrl->chips[bank] = priv; + priv->bank = bank; + priv->ctrl = fsl_ifc_ctrl_dev; + priv->dev = &dev->dev; + + priv->vbase = ioremap(res.start, resource_size(&res)); + if (!priv->vbase) { + dev_err(priv->dev, "%s: failed to map chip region\n", __func__); + ret = -ENOMEM; + goto err; + } + + dev_set_drvdata(priv->dev, priv); + + ifc_out32(IFC_NAND_EVTER_EN_OPC_EN | + IFC_NAND_EVTER_EN_FTOER_EN | + IFC_NAND_EVTER_EN_WPER_EN, + &ifc->ifc_nand.nand_evter_en); + + /* enable NAND Machine Interrupts */ + ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN | + IFC_NAND_EVTER_INTR_FTOERIR_EN | + IFC_NAND_EVTER_INTR_WPERIR_EN, + &ifc->ifc_nand.nand_evter_intr_en); + + mtd = nand_to_mtd(&priv->chip); + mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); + if (!mtd->name) { + ret = -ENOMEM; + goto err; + } + + ret = fsl_ifc_chip_init(priv); + if (ret) + goto err; + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + goto err; + + ret = fsl_ifc_chip_init_tail(mtd); + if (ret) + goto err; + + ret = nand_scan_tail(mtd); + if (ret) + goto err; + + /* First look for RedBoot table or partitions on the command + * line, these take precedence over device tree information */ + mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); + + dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", + (unsigned long long)res.start, priv->bank); + return 0; + +err: + fsl_ifc_chip_remove(priv); + return ret; +} + +static int fsl_ifc_nand_remove(struct platform_device *dev) +{ + struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); + + fsl_ifc_chip_remove(priv); + + mutex_lock(&fsl_ifc_nand_mutex); + ifc_nand_ctrl->counter--; + if (!ifc_nand_ctrl->counter) { + fsl_ifc_ctrl_dev->nand = NULL; + kfree(ifc_nand_ctrl); + } + mutex_unlock(&fsl_ifc_nand_mutex); + + return 0; +} + +static const struct of_device_id fsl_ifc_nand_match[] = { + { + .compatible = "fsl,ifc-nand", + }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_ifc_nand_match); + +static struct platform_driver fsl_ifc_nand_driver = { + .driver = { + .name = "fsl,ifc-nand", + .of_match_table = fsl_ifc_nand_match, + }, + .probe = fsl_ifc_nand_probe, + .remove = fsl_ifc_nand_remove, +}; + +module_platform_driver(fsl_ifc_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale"); +MODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver"); diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c new file mode 100644 index 000000000000..a88e2cf66e0f --- /dev/null +++ b/drivers/mtd/nand/raw/fsl_upm.c @@ -0,0 +1,363 @@ +/* + * Freescale UPM NAND driver. + * + * Copyright © 2007-2008 MontaVista Software, Inc. + * + * Author: Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FSL_UPM_WAIT_RUN_PATTERN 0x1 +#define FSL_UPM_WAIT_WRITE_BYTE 0x2 +#define FSL_UPM_WAIT_WRITE_BUFFER 0x4 + +struct fsl_upm_nand { + struct device *dev; + struct nand_chip chip; + int last_ctrl; + struct mtd_partition *parts; + struct fsl_upm upm; + uint8_t upm_addr_offset; + uint8_t upm_cmd_offset; + void __iomem *io_base; + int rnb_gpio[NAND_MAX_CHIPS]; + uint32_t mchip_offsets[NAND_MAX_CHIPS]; + uint32_t mchip_count; + uint32_t mchip_number; + int chip_delay; + uint32_t wait_flags; +}; + +static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo) +{ + return container_of(mtd_to_nand(mtdinfo), struct fsl_upm_nand, + chip); +} + +static int fun_chip_ready(struct mtd_info *mtd) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + + if (gpio_get_value(fun->rnb_gpio[fun->mchip_number])) + return 1; + + dev_vdbg(fun->dev, "busy\n"); + return 0; +} + +static void fun_wait_rnb(struct fsl_upm_nand *fun) +{ + if (fun->rnb_gpio[fun->mchip_number] >= 0) { + struct mtd_info *mtd = nand_to_mtd(&fun->chip); + int cnt = 1000000; + + while (--cnt && !fun_chip_ready(mtd)) + cpu_relax(); + if (!cnt) + dev_err(fun->dev, "tired waiting for RNB\n"); + } else { + ndelay(100); + } +} + +static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + u32 mar; + + if (!(ctrl & fun->last_ctrl)) { + fsl_upm_end_pattern(&fun->upm); + + if (cmd == NAND_CMD_NONE) + return; + + fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); + } + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_ALE) + fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); + else if (ctrl & NAND_CLE) + fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); + } + + mar = (cmd << (32 - fun->upm.width)) | + fun->mchip_offsets[fun->mchip_number]; + fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar); + + if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) + fun_wait_rnb(fun); +} + +static void fun_select_chip(struct mtd_info *mtd, int mchip_nr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + + if (mchip_nr == -1) { + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + } else if (mchip_nr >= 0 && mchip_nr < NAND_MAX_CHIPS) { + fun->mchip_number = mchip_nr; + chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr]; + chip->IO_ADDR_W = chip->IO_ADDR_R; + } else { + BUG(); + } +} + +static uint8_t fun_read_byte(struct mtd_info *mtd) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + + return in_8(fun->chip.IO_ADDR_R); +} + +static void fun_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + int i; + + for (i = 0; i < len; i++) + buf[i] = in_8(fun->chip.IO_ADDR_R); +} + +static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + int i; + + for (i = 0; i < len; i++) { + out_8(fun->chip.IO_ADDR_W, buf[i]); + if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) + fun_wait_rnb(fun); + } + if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) + fun_wait_rnb(fun); +} + +static int fun_chip_init(struct fsl_upm_nand *fun, + const struct device_node *upm_np, + const struct resource *io_res) +{ + struct mtd_info *mtd = nand_to_mtd(&fun->chip); + int ret; + struct device_node *flash_np; + + fun->chip.IO_ADDR_R = fun->io_base; + fun->chip.IO_ADDR_W = fun->io_base; + fun->chip.cmd_ctrl = fun_cmd_ctrl; + fun->chip.chip_delay = fun->chip_delay; + fun->chip.read_byte = fun_read_byte; + fun->chip.read_buf = fun_read_buf; + fun->chip.write_buf = fun_write_buf; + fun->chip.ecc.mode = NAND_ECC_SOFT; + fun->chip.ecc.algo = NAND_ECC_HAMMING; + if (fun->mchip_count > 1) + fun->chip.select_chip = fun_select_chip; + + if (fun->rnb_gpio[0] >= 0) + fun->chip.dev_ready = fun_chip_ready; + + mtd->dev.parent = fun->dev; + + flash_np = of_get_next_child(upm_np, NULL); + if (!flash_np) + return -ENODEV; + + nand_set_flash_node(&fun->chip, flash_np); + mtd->name = kasprintf(GFP_KERNEL, "0x%llx.%s", (u64)io_res->start, + flash_np->name); + if (!mtd->name) { + ret = -ENOMEM; + goto err; + } + + ret = nand_scan(mtd, fun->mchip_count); + if (ret) + goto err; + + ret = mtd_device_register(mtd, NULL, 0); +err: + of_node_put(flash_np); + if (ret) + kfree(mtd->name); + return ret; +} + +static int fun_probe(struct platform_device *ofdev) +{ + struct fsl_upm_nand *fun; + struct resource io_res; + const __be32 *prop; + int rnb_gpio; + int ret; + int size; + int i; + + fun = kzalloc(sizeof(*fun), GFP_KERNEL); + if (!fun) + return -ENOMEM; + + ret = of_address_to_resource(ofdev->dev.of_node, 0, &io_res); + if (ret) { + dev_err(&ofdev->dev, "can't get IO base\n"); + goto err1; + } + + ret = fsl_upm_find(io_res.start, &fun->upm); + if (ret) { + dev_err(&ofdev->dev, "can't find UPM\n"); + goto err1; + } + + prop = of_get_property(ofdev->dev.of_node, "fsl,upm-addr-offset", + &size); + if (!prop || size != sizeof(uint32_t)) { + dev_err(&ofdev->dev, "can't get UPM address offset\n"); + ret = -EINVAL; + goto err1; + } + fun->upm_addr_offset = *prop; + + prop = of_get_property(ofdev->dev.of_node, "fsl,upm-cmd-offset", &size); + if (!prop || size != sizeof(uint32_t)) { + dev_err(&ofdev->dev, "can't get UPM command offset\n"); + ret = -EINVAL; + goto err1; + } + fun->upm_cmd_offset = *prop; + + prop = of_get_property(ofdev->dev.of_node, + "fsl,upm-addr-line-cs-offsets", &size); + if (prop && (size / sizeof(uint32_t)) > 0) { + fun->mchip_count = size / sizeof(uint32_t); + if (fun->mchip_count >= NAND_MAX_CHIPS) { + dev_err(&ofdev->dev, "too much multiple chips\n"); + goto err1; + } + for (i = 0; i < fun->mchip_count; i++) + fun->mchip_offsets[i] = be32_to_cpu(prop[i]); + } else { + fun->mchip_count = 1; + } + + for (i = 0; i < fun->mchip_count; i++) { + fun->rnb_gpio[i] = -1; + rnb_gpio = of_get_gpio(ofdev->dev.of_node, i); + if (rnb_gpio >= 0) { + ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev)); + if (ret) { + dev_err(&ofdev->dev, + "can't request RNB gpio #%d\n", i); + goto err2; + } + gpio_direction_input(rnb_gpio); + fun->rnb_gpio[i] = rnb_gpio; + } else if (rnb_gpio == -EINVAL) { + dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i); + goto err2; + } + } + + prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL); + if (prop) + fun->chip_delay = be32_to_cpup(prop); + else + fun->chip_delay = 50; + + prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size); + if (prop && size == sizeof(uint32_t)) + fun->wait_flags = be32_to_cpup(prop); + else + fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN | + FSL_UPM_WAIT_WRITE_BYTE; + + fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, + resource_size(&io_res)); + if (!fun->io_base) { + ret = -ENOMEM; + goto err2; + } + + fun->dev = &ofdev->dev; + fun->last_ctrl = NAND_CLE; + + ret = fun_chip_init(fun, ofdev->dev.of_node, &io_res); + if (ret) + goto err2; + + dev_set_drvdata(&ofdev->dev, fun); + + return 0; +err2: + for (i = 0; i < fun->mchip_count; i++) { + if (fun->rnb_gpio[i] < 0) + break; + gpio_free(fun->rnb_gpio[i]); + } +err1: + kfree(fun); + + return ret; +} + +static int fun_remove(struct platform_device *ofdev) +{ + struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); + struct mtd_info *mtd = nand_to_mtd(&fun->chip); + int i; + + nand_release(mtd); + kfree(mtd->name); + + for (i = 0; i < fun->mchip_count; i++) { + if (fun->rnb_gpio[i] < 0) + break; + gpio_free(fun->rnb_gpio[i]); + } + + kfree(fun); + + return 0; +} + +static const struct of_device_id of_fun_match[] = { + { .compatible = "fsl,upm-nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_fun_match); + +static struct platform_driver of_fun_driver = { + .driver = { + .name = "fsl,upm-nand", + .of_match_table = of_fun_match, + }, + .probe = fun_probe, + .remove = fun_remove, +}; + +module_platform_driver(of_fun_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anton Vorontsov "); +MODULE_DESCRIPTION("Driver for NAND chips working through Freescale " + "LocalBus User-Programmable Machine"); diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c new file mode 100644 index 000000000000..7e66268f8154 --- /dev/null +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -0,0 +1,1175 @@ +/* + * ST Microelectronics + * Flexible Static Memory Controller (FSMC) + * Driver for NAND portions + * + * Copyright © 2010 ST Microelectronics + * Vipin Kumar + * Ashish Priyadarshi + * + * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8) + * Copyright © 2007 STMicroelectronics Pvt. Ltd. + * Copyright © 2009 Alessandro Rubini + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* fsmc controller registers for NOR flash */ +#define CTRL 0x0 + /* ctrl register definitions */ + #define BANK_ENABLE (1 << 0) + #define MUXED (1 << 1) + #define NOR_DEV (2 << 2) + #define WIDTH_8 (0 << 4) + #define WIDTH_16 (1 << 4) + #define RSTPWRDWN (1 << 6) + #define WPROT (1 << 7) + #define WRT_ENABLE (1 << 12) + #define WAIT_ENB (1 << 13) + +#define CTRL_TIM 0x4 + /* ctrl_tim register definitions */ + +#define FSMC_NOR_BANK_SZ 0x8 +#define FSMC_NOR_REG_SIZE 0x40 + +#define FSMC_NOR_REG(base, bank, reg) (base + \ + FSMC_NOR_BANK_SZ * (bank) + \ + reg) + +/* fsmc controller registers for NAND flash */ +#define PC 0x00 + /* pc register definitions */ + #define FSMC_RESET (1 << 0) + #define FSMC_WAITON (1 << 1) + #define FSMC_ENABLE (1 << 2) + #define FSMC_DEVTYPE_NAND (1 << 3) + #define FSMC_DEVWID_8 (0 << 4) + #define FSMC_DEVWID_16 (1 << 4) + #define FSMC_ECCEN (1 << 6) + #define FSMC_ECCPLEN_512 (0 << 7) + #define FSMC_ECCPLEN_256 (1 << 7) + #define FSMC_TCLR_1 (1) + #define FSMC_TCLR_SHIFT (9) + #define FSMC_TCLR_MASK (0xF) + #define FSMC_TAR_1 (1) + #define FSMC_TAR_SHIFT (13) + #define FSMC_TAR_MASK (0xF) +#define STS 0x04 + /* sts register definitions */ + #define FSMC_CODE_RDY (1 << 15) +#define COMM 0x08 + /* comm register definitions */ + #define FSMC_TSET_0 0 + #define FSMC_TSET_SHIFT 0 + #define FSMC_TSET_MASK 0xFF + #define FSMC_TWAIT_6 6 + #define FSMC_TWAIT_SHIFT 8 + #define FSMC_TWAIT_MASK 0xFF + #define FSMC_THOLD_4 4 + #define FSMC_THOLD_SHIFT 16 + #define FSMC_THOLD_MASK 0xFF + #define FSMC_THIZ_1 1 + #define FSMC_THIZ_SHIFT 24 + #define FSMC_THIZ_MASK 0xFF +#define ATTRIB 0x0C +#define IOATA 0x10 +#define ECC1 0x14 +#define ECC2 0x18 +#define ECC3 0x1C +#define FSMC_NAND_BANK_SZ 0x20 + +#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \ + (FSMC_NAND_BANK_SZ * (bank)) + \ + reg) + +#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ) + +struct fsmc_nand_timings { + uint8_t tclr; + uint8_t tar; + uint8_t thiz; + uint8_t thold; + uint8_t twait; + uint8_t tset; +}; + +enum access_mode { + USE_DMA_ACCESS = 1, + USE_WORD_ACCESS, +}; + +/** + * struct fsmc_nand_data - structure for FSMC NAND device state + * + * @pid: Part ID on the AMBA PrimeCell format + * @mtd: MTD info for a NAND flash. + * @nand: Chip related info for a NAND flash. + * @partitions: Partition info for a NAND Flash. + * @nr_partitions: Total number of partition of a NAND flash. + * + * @bank: Bank number for probed device. + * @clk: Clock structure for FSMC. + * + * @read_dma_chan: DMA channel for read access + * @write_dma_chan: DMA channel for write access to NAND + * @dma_access_complete: Completion structure + * + * @data_pa: NAND Physical port for Data. + * @data_va: NAND port for Data. + * @cmd_va: NAND port for Command. + * @addr_va: NAND port for Address. + * @regs_va: FSMC regs base address. + */ +struct fsmc_nand_data { + u32 pid; + struct nand_chip nand; + + unsigned int bank; + struct device *dev; + enum access_mode mode; + struct clk *clk; + + /* DMA related objects */ + struct dma_chan *read_dma_chan; + struct dma_chan *write_dma_chan; + struct completion dma_access_complete; + + struct fsmc_nand_timings *dev_timings; + + dma_addr_t data_pa; + void __iomem *data_va; + void __iomem *cmd_va; + void __iomem *addr_va; + void __iomem *regs_va; +}; + +static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 2; + oobregion->length = 3; + + return 0; +} + +static int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 8; + + if (section < chip->ecc.steps - 1) + oobregion->length = 8; + else + oobregion->length = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = { + .ecc = fsmc_ecc1_ooblayout_ecc, + .free = fsmc_ecc1_ooblayout_free, +}; + +/* + * ECC placement definitions in oobfree type format. + * There are 13 bytes of ecc for every 512 byte block and it has to be read + * consecutively and immediately after the 512 byte data block for hardware to + * generate the error bit offsets in 512 byte data. + */ +static int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->length = chip->ecc.bytes; + + if (!section && mtd->writesize <= 512) + oobregion->offset = 0; + else + oobregion->offset = (section * 16) + 2; + + return 0; +} + +static int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 15; + + if (section < chip->ecc.steps - 1) + oobregion->length = 3; + else + oobregion->length = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = { + .ecc = fsmc_ecc4_ooblayout_ecc, + .free = fsmc_ecc4_ooblayout_free, +}; + +static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand); +} + +/* + * fsmc_cmd_ctrl - For facilitaing Hardware access + * This routine allows hardware specific access to control-lines(ALE,CLE) + */ +static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + void __iomem *regs = host->regs_va; + unsigned int bank = host->bank; + + if (ctrl & NAND_CTRL_CHANGE) { + u32 pc; + + if (ctrl & NAND_CLE) { + this->IO_ADDR_R = host->cmd_va; + this->IO_ADDR_W = host->cmd_va; + } else if (ctrl & NAND_ALE) { + this->IO_ADDR_R = host->addr_va; + this->IO_ADDR_W = host->addr_va; + } else { + this->IO_ADDR_R = host->data_va; + this->IO_ADDR_W = host->data_va; + } + + pc = readl(FSMC_NAND_REG(regs, bank, PC)); + if (ctrl & NAND_NCE) + pc |= FSMC_ENABLE; + else + pc &= ~FSMC_ENABLE; + writel_relaxed(pc, FSMC_NAND_REG(regs, bank, PC)); + } + + mb(); + + if (cmd != NAND_CMD_NONE) + writeb_relaxed(cmd, this->IO_ADDR_W); +} + +/* + * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine + * + * This routine initializes timing parameters related to NAND memory access in + * FSMC registers + */ +static void fsmc_nand_setup(struct fsmc_nand_data *host, + struct fsmc_nand_timings *tims) +{ + uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; + uint32_t tclr, tar, thiz, thold, twait, tset; + unsigned int bank = host->bank; + void __iomem *regs = host->regs_va; + + tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; + tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; + thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT; + thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT; + twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT; + tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; + + if (host->nand.options & NAND_BUSWIDTH_16) + writel_relaxed(value | FSMC_DEVWID_16, + FSMC_NAND_REG(regs, bank, PC)); + else + writel_relaxed(value | FSMC_DEVWID_8, + FSMC_NAND_REG(regs, bank, PC)); + + writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar, + FSMC_NAND_REG(regs, bank, PC)); + writel_relaxed(thiz | thold | twait | tset, + FSMC_NAND_REG(regs, bank, COMM)); + writel_relaxed(thiz | thold | twait | tset, + FSMC_NAND_REG(regs, bank, ATTRIB)); +} + +static int fsmc_calc_timings(struct fsmc_nand_data *host, + const struct nand_sdr_timings *sdrt, + struct fsmc_nand_timings *tims) +{ + unsigned long hclk = clk_get_rate(host->clk); + unsigned long hclkn = NSEC_PER_SEC / hclk; + uint32_t thiz, thold, twait, tset; + + if (sdrt->tRC_min < 30000) + return -EOPNOTSUPP; + + tims->tar = DIV_ROUND_UP(sdrt->tAR_min / 1000, hclkn) - 1; + if (tims->tar > FSMC_TAR_MASK) + tims->tar = FSMC_TAR_MASK; + tims->tclr = DIV_ROUND_UP(sdrt->tCLR_min / 1000, hclkn) - 1; + if (tims->tclr > FSMC_TCLR_MASK) + tims->tclr = FSMC_TCLR_MASK; + + thiz = sdrt->tCS_min - sdrt->tWP_min; + tims->thiz = DIV_ROUND_UP(thiz / 1000, hclkn); + + thold = sdrt->tDH_min; + if (thold < sdrt->tCH_min) + thold = sdrt->tCH_min; + if (thold < sdrt->tCLH_min) + thold = sdrt->tCLH_min; + if (thold < sdrt->tWH_min) + thold = sdrt->tWH_min; + if (thold < sdrt->tALH_min) + thold = sdrt->tALH_min; + if (thold < sdrt->tREH_min) + thold = sdrt->tREH_min; + tims->thold = DIV_ROUND_UP(thold / 1000, hclkn); + if (tims->thold == 0) + tims->thold = 1; + else if (tims->thold > FSMC_THOLD_MASK) + tims->thold = FSMC_THOLD_MASK; + + twait = max(sdrt->tRP_min, sdrt->tWP_min); + tims->twait = DIV_ROUND_UP(twait / 1000, hclkn) - 1; + if (tims->twait == 0) + tims->twait = 1; + else if (tims->twait > FSMC_TWAIT_MASK) + tims->twait = FSMC_TWAIT_MASK; + + tset = max(sdrt->tCS_min - sdrt->tWP_min, + sdrt->tCEA_max - sdrt->tREA_max); + tims->tset = DIV_ROUND_UP(tset / 1000, hclkn) - 1; + if (tims->tset == 0) + tims->tset = 1; + else if (tims->tset > FSMC_TSET_MASK) + tims->tset = FSMC_TSET_MASK; + + return 0; +} + +static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct fsmc_nand_data *host = nand_get_controller_data(nand); + struct fsmc_nand_timings tims; + const struct nand_sdr_timings *sdrt; + int ret; + + sdrt = nand_get_sdr_timings(conf); + if (IS_ERR(sdrt)) + return PTR_ERR(sdrt); + + ret = fsmc_calc_timings(host, sdrt, &tims); + if (ret) + return ret; + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + fsmc_nand_setup(host, &tims); + + return 0; +} + +/* + * fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers + */ +static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + void __iomem *regs = host->regs_va; + uint32_t bank = host->bank; + + writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256, + FSMC_NAND_REG(regs, bank, PC)); + writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN, + FSMC_NAND_REG(regs, bank, PC)); + writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN, + FSMC_NAND_REG(regs, bank, PC)); +} + +/* + * fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by + * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to + * max of 8-bits) + */ +static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, + uint8_t *ecc) +{ + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + void __iomem *regs = host->regs_va; + uint32_t bank = host->bank; + uint32_t ecc_tmp; + unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; + + do { + if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) + break; + else + cond_resched(); + } while (!time_after_eq(jiffies, deadline)); + + if (time_after_eq(jiffies, deadline)) { + dev_err(host->dev, "calculate ecc timed out\n"); + return -ETIMEDOUT; + } + + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); + ecc[0] = (uint8_t) (ecc_tmp >> 0); + ecc[1] = (uint8_t) (ecc_tmp >> 8); + ecc[2] = (uint8_t) (ecc_tmp >> 16); + ecc[3] = (uint8_t) (ecc_tmp >> 24); + + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); + ecc[4] = (uint8_t) (ecc_tmp >> 0); + ecc[5] = (uint8_t) (ecc_tmp >> 8); + ecc[6] = (uint8_t) (ecc_tmp >> 16); + ecc[7] = (uint8_t) (ecc_tmp >> 24); + + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); + ecc[8] = (uint8_t) (ecc_tmp >> 0); + ecc[9] = (uint8_t) (ecc_tmp >> 8); + ecc[10] = (uint8_t) (ecc_tmp >> 16); + ecc[11] = (uint8_t) (ecc_tmp >> 24); + + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); + ecc[12] = (uint8_t) (ecc_tmp >> 16); + + return 0; +} + +/* + * fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by + * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to + * max of 1-bit) + */ +static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, + uint8_t *ecc) +{ + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + void __iomem *regs = host->regs_va; + uint32_t bank = host->bank; + uint32_t ecc_tmp; + + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); + ecc[0] = (uint8_t) (ecc_tmp >> 0); + ecc[1] = (uint8_t) (ecc_tmp >> 8); + ecc[2] = (uint8_t) (ecc_tmp >> 16); + + return 0; +} + +/* Count the number of 0's in buff upto a max of max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static void dma_complete(void *param) +{ + struct fsmc_nand_data *host = param; + + complete(&host->dma_access_complete); +} + +static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, + enum dma_data_direction direction) +{ + struct dma_chan *chan; + struct dma_device *dma_dev; + struct dma_async_tx_descriptor *tx; + dma_addr_t dma_dst, dma_src, dma_addr; + dma_cookie_t cookie; + unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + int ret; + unsigned long time_left; + + if (direction == DMA_TO_DEVICE) + chan = host->write_dma_chan; + else if (direction == DMA_FROM_DEVICE) + chan = host->read_dma_chan; + else + return -EINVAL; + + dma_dev = chan->device; + dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); + + if (direction == DMA_TO_DEVICE) { + dma_src = dma_addr; + dma_dst = host->data_pa; + } else { + dma_src = host->data_pa; + dma_dst = dma_addr; + } + + tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, + len, flags); + if (!tx) { + dev_err(host->dev, "device_prep_dma_memcpy error\n"); + ret = -EIO; + goto unmap_dma; + } + + tx->callback = dma_complete; + tx->callback_param = host; + cookie = tx->tx_submit(tx); + + ret = dma_submit_error(cookie); + if (ret) { + dev_err(host->dev, "dma_submit_error %d\n", cookie); + goto unmap_dma; + } + + dma_async_issue_pending(chan); + + time_left = + wait_for_completion_timeout(&host->dma_access_complete, + msecs_to_jiffies(3000)); + if (time_left == 0) { + dmaengine_terminate_all(chan); + dev_err(host->dev, "wait_for_completion_timeout\n"); + ret = -ETIMEDOUT; + goto unmap_dma; + } + + ret = 0; + +unmap_dma: + dma_unmap_single(dma_dev->dev, dma_addr, len, direction); + + return ret; +} + +/* + * fsmc_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd_to_nand(mtd); + + if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && + IS_ALIGNED(len, sizeof(uint32_t))) { + uint32_t *p = (uint32_t *)buf; + len = len >> 2; + for (i = 0; i < len; i++) + writel_relaxed(p[i], chip->IO_ADDR_W); + } else { + for (i = 0; i < len; i++) + writeb_relaxed(buf[i], chip->IO_ADDR_W); + } +} + +/* + * fsmc_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd_to_nand(mtd); + + if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && + IS_ALIGNED(len, sizeof(uint32_t))) { + uint32_t *p = (uint32_t *)buf; + len = len >> 2; + for (i = 0; i < len; i++) + p[i] = readl_relaxed(chip->IO_ADDR_R); + } else { + for (i = 0; i < len; i++) + buf[i] = readb_relaxed(chip->IO_ADDR_R); + } +} + +/* + * fsmc_read_buf_dma - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + + dma_xfer(host, buf, len, DMA_FROM_DEVICE); +} + +/* + * fsmc_write_buf_dma - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + + dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); +} + +/* + * fsmc_read_page_hwecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi + * @page: page number to read + * + * This routine is needed for fsmc version 8 as reading from NAND chip has to be + * performed in a strict sequence as follows: + * data(512 byte) -> ecc(13 byte) + * After this read, fsmc hardware generates and reports error data bits(up to a + * max of 8 bits) + */ +static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, j, s, stat, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->ecc.calc_buf; + uint8_t *ecc_code = chip->ecc.code_buf; + int off, len, group = 0; + /* + * ecc_oob is intentionally taken as uint16_t. In 16bit devices, we + * end up reading 14 bytes (7 words) from oob. The local array is + * to maintain word alignment + */ + uint16_t ecc_oob[7]; + uint8_t *oob = (uint8_t *)&ecc_oob[0]; + unsigned int max_bitflips = 0; + + for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { + nand_read_page_op(chip, page, s * eccsize, NULL, 0); + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + for (j = 0; j < eccbytes;) { + struct mtd_oob_region oobregion; + int ret; + + ret = mtd_ooblayout_ecc(mtd, group++, &oobregion); + if (ret) + return ret; + + off = oobregion.offset; + len = oobregion.length; + + /* + * length is intentionally kept a higher multiple of 2 + * to read at least 13 bytes even in case of 16 bit NAND + * devices + */ + if (chip->options & NAND_BUSWIDTH_16) + len = roundup(len, 2); + + nand_read_oob_op(chip, page, off, oob + j, len); + j += len; + } + + memcpy(&ecc_code[i], oob, chip->ecc.bytes); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + return max_bitflips; +} + +/* + * fsmc_bch8_correct_data + * @mtd: mtd info structure + * @dat: buffer of read data + * @read_ecc: ecc read from device spare area + * @calc_ecc: ecc calculated from read data + * + * calc_ecc is a 104 bit information containing maximum of 8 error + * offset informations of 13 bits each in 512 bytes of read data. + */ +static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + void __iomem *regs = host->regs_va; + unsigned int bank = host->bank; + uint32_t err_idx[8]; + uint32_t num_err, i; + uint32_t ecc1, ecc2, ecc3, ecc4; + + num_err = (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF; + + /* no bit flipping */ + if (likely(num_err == 0)) + return 0; + + /* too many errors */ + if (unlikely(num_err > 8)) { + /* + * This is a temporary erase check. A newly erased page read + * would result in an ecc error because the oob data is also + * erased to FF and the calculated ecc for an FF data is not + * FF..FF. + * This is a workaround to skip performing correction in case + * data is FF..FF + * + * Logic: + * For every page, each bit written as 0 is counted until these + * number of bits are greater than 8 (the maximum correction + * capability of FSMC for each 512 + 13 bytes) + */ + + int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8); + int bits_data = count_written_bits(dat, chip->ecc.size, 8); + + if ((bits_ecc + bits_data) <= 8) { + if (bits_data) + memset(dat, 0xff, chip->ecc.size); + return bits_data; + } + + return -EBADMSG; + } + + /* + * ------------------- calc_ecc[] bit wise -----------|--13 bits--| + * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--| + * + * calc_ecc is a 104 bit information containing maximum of 8 error + * offset informations of 13 bits each. calc_ecc is copied into a + * uint64_t array and error offset indexes are populated in err_idx + * array + */ + ecc1 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); + ecc2 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); + ecc3 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); + ecc4 = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); + + err_idx[0] = (ecc1 >> 0) & 0x1FFF; + err_idx[1] = (ecc1 >> 13) & 0x1FFF; + err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); + err_idx[3] = (ecc2 >> 7) & 0x1FFF; + err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); + err_idx[5] = (ecc3 >> 1) & 0x1FFF; + err_idx[6] = (ecc3 >> 14) & 0x1FFF; + err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); + + i = 0; + while (num_err--) { + change_bit(0, (unsigned long *)&err_idx[i]); + change_bit(1, (unsigned long *)&err_idx[i]); + + if (err_idx[i] < chip->ecc.size * 8) { + change_bit(err_idx[i], (unsigned long *)dat); + i++; + } + } + return i; +} + +static bool filter(struct dma_chan *chan, void *slave) +{ + chan->private = slave; + return true; +} + +static int fsmc_nand_probe_config_dt(struct platform_device *pdev, + struct fsmc_nand_data *host, + struct nand_chip *nand) +{ + struct device_node *np = pdev->dev.of_node; + u32 val; + int ret; + + nand->options = 0; + + if (!of_property_read_u32(np, "bank-width", &val)) { + if (val == 2) { + nand->options |= NAND_BUSWIDTH_16; + } else if (val != 1) { + dev_err(&pdev->dev, "invalid bank-width %u\n", val); + return -EINVAL; + } + } + + if (of_get_property(np, "nand-skip-bbtscan", NULL)) + nand->options |= NAND_SKIP_BBTSCAN; + + host->dev_timings = devm_kzalloc(&pdev->dev, + sizeof(*host->dev_timings), GFP_KERNEL); + if (!host->dev_timings) + return -ENOMEM; + ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings, + sizeof(*host->dev_timings)); + if (ret) + host->dev_timings = NULL; + + /* Set default NAND bank to 0 */ + host->bank = 0; + if (!of_property_read_u32(np, "bank", &val)) { + if (val > 3) { + dev_err(&pdev->dev, "invalid bank %u\n", val); + return -EINVAL; + } + host->bank = val; + } + return 0; +} + +/* + * fsmc_nand_probe - Probe function + * @pdev: platform device structure + */ +static int __init fsmc_nand_probe(struct platform_device *pdev) +{ + struct fsmc_nand_data *host; + struct mtd_info *mtd; + struct nand_chip *nand; + struct resource *res; + dma_cap_mask_t mask; + int ret = 0; + u32 pid; + int i; + + /* Allocate memory for the device structure (and zero it) */ + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + nand = &host->nand; + + ret = fsmc_nand_probe_config_dt(pdev, host, nand); + if (ret) + return ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); + host->data_va = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->data_va)) + return PTR_ERR(host->data_va); + + host->data_pa = (dma_addr_t)res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); + host->addr_va = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->addr_va)) + return PTR_ERR(host->addr_va); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); + host->cmd_va = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->cmd_va)) + return PTR_ERR(host->cmd_va); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); + host->regs_va = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->regs_va)) + return PTR_ERR(host->regs_va); + + host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) { + dev_err(&pdev->dev, "failed to fetch block clock\n"); + return PTR_ERR(host->clk); + } + + ret = clk_prepare_enable(host->clk); + if (ret) + return ret; + + /* + * This device ID is actually a common AMBA ID as used on the + * AMBA PrimeCell bus. However it is not a PrimeCell. + */ + for (pid = 0, i = 0; i < 4; i++) + pid |= (readl(host->regs_va + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8); + host->pid = pid; + dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, " + "revision %02x, config %02x\n", + AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid), + AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid)); + + host->dev = &pdev->dev; + + if (host->mode == USE_DMA_ACCESS) + init_completion(&host->dma_access_complete); + + /* Link all private pointers */ + mtd = nand_to_mtd(&host->nand); + nand_set_controller_data(nand, host); + nand_set_flash_node(nand, pdev->dev.of_node); + + mtd->dev.parent = &pdev->dev; + nand->IO_ADDR_R = host->data_va; + nand->IO_ADDR_W = host->data_va; + nand->cmd_ctrl = fsmc_cmd_ctrl; + nand->chip_delay = 30; + + /* + * Setup default ECC mode. nand_dt_init() called from nand_scan_ident() + * can overwrite this value if the DT provides a different value. + */ + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.hwctl = fsmc_enable_hwecc; + nand->ecc.size = 512; + nand->badblockbits = 7; + + switch (host->mode) { + case USE_DMA_ACCESS: + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + host->read_dma_chan = dma_request_channel(mask, filter, NULL); + if (!host->read_dma_chan) { + dev_err(&pdev->dev, "Unable to get read dma channel\n"); + goto err_req_read_chnl; + } + host->write_dma_chan = dma_request_channel(mask, filter, NULL); + if (!host->write_dma_chan) { + dev_err(&pdev->dev, "Unable to get write dma channel\n"); + goto err_req_write_chnl; + } + nand->read_buf = fsmc_read_buf_dma; + nand->write_buf = fsmc_write_buf_dma; + break; + + default: + case USE_WORD_ACCESS: + nand->read_buf = fsmc_read_buf; + nand->write_buf = fsmc_write_buf; + break; + } + + if (host->dev_timings) + fsmc_nand_setup(host, host->dev_timings); + else + nand->setup_data_interface = fsmc_setup_data_interface; + + if (AMBA_REV_BITS(host->pid) >= 8) { + nand->ecc.read_page = fsmc_read_page_hwecc; + nand->ecc.calculate = fsmc_read_hwecc_ecc4; + nand->ecc.correct = fsmc_bch8_correct_data; + nand->ecc.bytes = 13; + nand->ecc.strength = 8; + } + + /* + * Scan to find existence of the device + */ + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) { + dev_err(&pdev->dev, "No NAND Device found!\n"); + goto err_scan_ident; + } + + if (AMBA_REV_BITS(host->pid) >= 8) { + switch (mtd->oobsize) { + case 16: + case 64: + case 128: + case 224: + case 256: + break; + default: + dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + ret = -EINVAL; + goto err_probe; + } + + mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops); + } else { + switch (nand->ecc.mode) { + case NAND_ECC_HW: + dev_info(&pdev->dev, "Using 1-bit HW ECC scheme\n"); + nand->ecc.calculate = fsmc_read_hwecc_ecc1; + nand->ecc.correct = nand_correct_data; + nand->ecc.bytes = 3; + nand->ecc.strength = 1; + break; + + case NAND_ECC_SOFT: + if (nand->ecc.algo == NAND_ECC_BCH) { + dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n"); + break; + } + + case NAND_ECC_ON_DIE: + break; + + default: + dev_err(&pdev->dev, "Unsupported ECC mode!\n"); + goto err_probe; + } + + /* + * Don't set layout for BCH4 SW ECC. This will be + * generated later in nand_bch_init() later. + */ + if (nand->ecc.mode == NAND_ECC_HW) { + switch (mtd->oobsize) { + case 16: + case 64: + case 128: + mtd_set_ooblayout(mtd, + &fsmc_ecc1_ooblayout_ops); + break; + default: + dev_warn(&pdev->dev, + "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + ret = -EINVAL; + goto err_probe; + } + } + } + + /* Second stage of scan to fill MTD data-structures */ + ret = nand_scan_tail(mtd); + if (ret) + goto err_probe; + + mtd->name = "nand"; + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + goto err_probe; + + platform_set_drvdata(pdev, host); + dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); + return 0; + +err_probe: +err_scan_ident: + if (host->mode == USE_DMA_ACCESS) + dma_release_channel(host->write_dma_chan); +err_req_write_chnl: + if (host->mode == USE_DMA_ACCESS) + dma_release_channel(host->read_dma_chan); +err_req_read_chnl: + clk_disable_unprepare(host->clk); + return ret; +} + +/* + * Clean up routine + */ +static int fsmc_nand_remove(struct platform_device *pdev) +{ + struct fsmc_nand_data *host = platform_get_drvdata(pdev); + + if (host) { + nand_release(nand_to_mtd(&host->nand)); + + if (host->mode == USE_DMA_ACCESS) { + dma_release_channel(host->write_dma_chan); + dma_release_channel(host->read_dma_chan); + } + clk_disable_unprepare(host->clk); + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fsmc_nand_suspend(struct device *dev) +{ + struct fsmc_nand_data *host = dev_get_drvdata(dev); + if (host) + clk_disable_unprepare(host->clk); + return 0; +} + +static int fsmc_nand_resume(struct device *dev) +{ + struct fsmc_nand_data *host = dev_get_drvdata(dev); + if (host) { + clk_prepare_enable(host->clk); + if (host->dev_timings) + fsmc_nand_setup(host, host->dev_timings); + } + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume); + +static const struct of_device_id fsmc_nand_id_table[] = { + { .compatible = "st,spear600-fsmc-nand" }, + { .compatible = "stericsson,fsmc-nand" }, + {} +}; +MODULE_DEVICE_TABLE(of, fsmc_nand_id_table); + +static struct platform_driver fsmc_nand_driver = { + .remove = fsmc_nand_remove, + .driver = { + .name = "fsmc-nand", + .of_match_table = fsmc_nand_id_table, + .pm = &fsmc_nand_pm_ops, + }, +}; + +module_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vipin Kumar , Ashish Priyadarshi"); +MODULE_DESCRIPTION("NAND driver for SPEAr Platforms"); diff --git a/drivers/mtd/nand/raw/gpio.c b/drivers/mtd/nand/raw/gpio.c new file mode 100644 index 000000000000..2780af26d9ab --- /dev/null +++ b/drivers/mtd/nand/raw/gpio.c @@ -0,0 +1,327 @@ +/* + * Updated, and converted to generic GPIO based driver by Russell King. + * + * Written by Ben Dooks + * Based on 2.4 version by Mark Whittaker + * + * © 2004 Simtec Electronics + * + * Device driver for NAND flash that uses a memory mapped interface to + * read/write the NAND commands and data, and GPIO pins for control signals + * (the DT binding refers to this as "GPIO assisted NAND flash") + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpiomtd { + void __iomem *io_sync; + struct nand_chip nand_chip; + struct gpio_nand_platdata plat; + struct gpio_desc *nce; /* Optional chip enable */ + struct gpio_desc *cle; + struct gpio_desc *ale; + struct gpio_desc *rdy; + struct gpio_desc *nwp; /* Optional write protection */ +}; + +static inline struct gpiomtd *gpio_nand_getpriv(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct gpiomtd, nand_chip); +} + + +#ifdef CONFIG_ARM +/* gpio_nand_dosync() + * + * Make sure the GPIO state changes occur in-order with writes to NAND + * memory region. + * Needed on PXA due to bus-reordering within the SoC itself (see section on + * I/O ordering in PXA manual (section 2.3, p35) + */ +static void gpio_nand_dosync(struct gpiomtd *gpiomtd) +{ + unsigned long tmp; + + if (gpiomtd->io_sync) { + /* + * Linux memory barriers don't cater for what's required here. + * What's required is what's here - a read from a separate + * region with a dependency on that read. + */ + tmp = readl(gpiomtd->io_sync); + asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp)); + } +} +#else +static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {} +#endif + +static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); + + gpio_nand_dosync(gpiomtd); + + if (ctrl & NAND_CTRL_CHANGE) { + if (gpiomtd->nce) + gpiod_set_value(gpiomtd->nce, !(ctrl & NAND_NCE)); + gpiod_set_value(gpiomtd->cle, !!(ctrl & NAND_CLE)); + gpiod_set_value(gpiomtd->ale, !!(ctrl & NAND_ALE)); + gpio_nand_dosync(gpiomtd); + } + if (cmd == NAND_CMD_NONE) + return; + + writeb(cmd, gpiomtd->nand_chip.IO_ADDR_W); + gpio_nand_dosync(gpiomtd); +} + +static int gpio_nand_devready(struct mtd_info *mtd) +{ + struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); + + return gpiod_get_value(gpiomtd->rdy); +} + +#ifdef CONFIG_OF +static const struct of_device_id gpio_nand_id_table[] = { + { .compatible = "gpio-control-nand" }, + {} +}; +MODULE_DEVICE_TABLE(of, gpio_nand_id_table); + +static int gpio_nand_get_config_of(const struct device *dev, + struct gpio_nand_platdata *plat) +{ + u32 val; + + if (!dev->of_node) + return -ENODEV; + + if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { + if (val == 2) { + plat->options |= NAND_BUSWIDTH_16; + } else if (val != 1) { + dev_err(dev, "invalid bank-width %u\n", val); + return -EINVAL; + } + } + + if (!of_property_read_u32(dev->of_node, "chip-delay", &val)) + plat->chip_delay = val; + + return 0; +} + +static struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev) +{ + struct resource *r; + u64 addr; + + if (of_property_read_u64(pdev->dev.of_node, + "gpio-control-nand,io-sync-reg", &addr)) + return NULL; + + r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL); + if (!r) + return NULL; + + r->start = addr; + r->end = r->start + 0x3; + r->flags = IORESOURCE_MEM; + + return r; +} +#else /* CONFIG_OF */ +static inline int gpio_nand_get_config_of(const struct device *dev, + struct gpio_nand_platdata *plat) +{ + return -ENOSYS; +} + +static inline struct resource * +gpio_nand_get_io_sync_of(struct platform_device *pdev) +{ + return NULL; +} +#endif /* CONFIG_OF */ + +static inline int gpio_nand_get_config(const struct device *dev, + struct gpio_nand_platdata *plat) +{ + int ret = gpio_nand_get_config_of(dev, plat); + + if (!ret) + return ret; + + if (dev_get_platdata(dev)) { + memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); + return 0; + } + + return -EINVAL; +} + +static inline struct resource * +gpio_nand_get_io_sync(struct platform_device *pdev) +{ + struct resource *r = gpio_nand_get_io_sync_of(pdev); + + if (r) + return r; + + return platform_get_resource(pdev, IORESOURCE_MEM, 1); +} + +static int gpio_nand_remove(struct platform_device *pdev) +{ + struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); + + nand_release(nand_to_mtd(&gpiomtd->nand_chip)); + + /* Enable write protection and disable the chip */ + if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) + gpiod_set_value(gpiomtd->nwp, 0); + if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) + gpiod_set_value(gpiomtd->nce, 0); + + return 0; +} + +static int gpio_nand_probe(struct platform_device *pdev) +{ + struct gpiomtd *gpiomtd; + struct nand_chip *chip; + struct mtd_info *mtd; + struct resource *res; + struct device *dev = &pdev->dev; + int ret = 0; + + if (!dev->of_node && !dev_get_platdata(dev)) + return -EINVAL; + + gpiomtd = devm_kzalloc(dev, sizeof(*gpiomtd), GFP_KERNEL); + if (!gpiomtd) + return -ENOMEM; + + chip = &gpiomtd->nand_chip; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->IO_ADDR_R = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->IO_ADDR_R)) + return PTR_ERR(chip->IO_ADDR_R); + + res = gpio_nand_get_io_sync(pdev); + if (res) { + gpiomtd->io_sync = devm_ioremap_resource(dev, res); + if (IS_ERR(gpiomtd->io_sync)) + return PTR_ERR(gpiomtd->io_sync); + } + + ret = gpio_nand_get_config(dev, &gpiomtd->plat); + if (ret) + return ret; + + /* Just enable the chip */ + gpiomtd->nce = devm_gpiod_get_optional(dev, "nce", GPIOD_OUT_HIGH); + if (IS_ERR(gpiomtd->nce)) + return PTR_ERR(gpiomtd->nce); + + /* We disable write protection once we know probe() will succeed */ + gpiomtd->nwp = devm_gpiod_get_optional(dev, "nwp", GPIOD_OUT_LOW); + if (IS_ERR(gpiomtd->nwp)) { + ret = PTR_ERR(gpiomtd->nwp); + goto out_ce; + } + + gpiomtd->ale = devm_gpiod_get(dev, "ale", GPIOD_OUT_LOW); + if (IS_ERR(gpiomtd->ale)) { + ret = PTR_ERR(gpiomtd->ale); + goto out_ce; + } + + gpiomtd->cle = devm_gpiod_get(dev, "cle", GPIOD_OUT_LOW); + if (IS_ERR(gpiomtd->cle)) { + ret = PTR_ERR(gpiomtd->cle); + goto out_ce; + } + + gpiomtd->rdy = devm_gpiod_get_optional(dev, "rdy", GPIOD_IN); + if (IS_ERR(gpiomtd->rdy)) { + ret = PTR_ERR(gpiomtd->rdy); + goto out_ce; + } + /* Using RDY pin */ + if (gpiomtd->rdy) + chip->dev_ready = gpio_nand_devready; + + nand_set_flash_node(chip, pdev->dev.of_node); + chip->IO_ADDR_W = chip->IO_ADDR_R; + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + chip->options = gpiomtd->plat.options; + chip->chip_delay = gpiomtd->plat.chip_delay; + chip->cmd_ctrl = gpio_nand_cmd_ctrl; + + mtd = nand_to_mtd(chip); + mtd->dev.parent = dev; + + platform_set_drvdata(pdev, gpiomtd); + + /* Disable write protection, if wired up */ + if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) + gpiod_direction_output(gpiomtd->nwp, 1); + + ret = nand_scan(mtd, 1); + if (ret) + goto err_wp; + + if (gpiomtd->plat.adjust_parts) + gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size); + + ret = mtd_device_register(mtd, gpiomtd->plat.parts, + gpiomtd->plat.num_parts); + if (!ret) + return 0; + +err_wp: + if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) + gpiod_set_value(gpiomtd->nwp, 0); +out_ce: + if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) + gpiod_set_value(gpiomtd->nce, 0); + + return ret; +} + +static struct platform_driver gpio_nand_driver = { + .probe = gpio_nand_probe, + .remove = gpio_nand_remove, + .driver = { + .name = "gpio-nand", + .of_match_table = of_match_ptr(gpio_nand_id_table), + }, +}; + +module_platform_driver(gpio_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("GPIO NAND Driver"); diff --git a/drivers/mtd/nand/raw/gpmi-nand/Makefile b/drivers/mtd/nand/raw/gpmi-nand/Makefile new file mode 100644 index 000000000000..3a462487c35e --- /dev/null +++ b/drivers/mtd/nand/raw/gpmi-nand/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o +gpmi_nand-objs += gpmi-nand.o +gpmi_nand-objs += gpmi-lib.o diff --git a/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h new file mode 100644 index 000000000000..05bb91f2f4c4 --- /dev/null +++ b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h @@ -0,0 +1,128 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __GPMI_NAND_BCH_REGS_H +#define __GPMI_NAND_BCH_REGS_H + +#define HW_BCH_CTRL 0x00000000 +#define HW_BCH_CTRL_SET 0x00000004 +#define HW_BCH_CTRL_CLR 0x00000008 +#define HW_BCH_CTRL_TOG 0x0000000c + +#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1 << 8) +#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0) + +#define HW_BCH_STATUS0 0x00000010 +#define HW_BCH_MODE 0x00000020 +#define HW_BCH_ENCODEPTR 0x00000030 +#define HW_BCH_DATAPTR 0x00000040 +#define HW_BCH_METAPTR 0x00000050 +#define HW_BCH_LAYOUTSELECT 0x00000070 + +#define HW_BCH_FLASH0LAYOUT0 0x00000080 + +#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS) +#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) + +#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE) +#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\ + & BM_BCH_FLASH0LAYOUT0_META_SIZE) + +#define BP_BCH_FLASH0LAYOUT0_ECC0 12 +#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) +#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) +#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \ + (GPMI_IS_MX6(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \ + & MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \ + : (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \ + & BM_BCH_FLASH0LAYOUT0_ECC0) \ + ) + +#define MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14 10 +#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \ + (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) +#define BF_BCH_FLASH0LAYOUT0_GF(v, x) \ + ((GPMI_IS_MX6(x) && ((v) == 14)) \ + ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \ + & MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \ + : 0 \ + ) + +#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \ + (GPMI_IS_MX6(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + ) + +#define HW_BCH_FLASH0LAYOUT1 0x00000090 + +#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \ + (0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) +#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \ + & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) + +#define BP_BCH_FLASH0LAYOUT1_ECCN 12 +#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) +#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) +#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \ + (GPMI_IS_MX6(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \ + & MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \ + : (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \ + & BM_BCH_FLASH0LAYOUT1_ECCN) \ + ) + +#define MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14 10 +#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \ + (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) +#define BF_BCH_FLASH0LAYOUT1_GF(v, x) \ + ((GPMI_IS_MX6(x) && ((v) == 14)) \ + ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \ + & MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \ + : 0 \ + ) + +#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \ + (GPMI_IS_MX6(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + ) + +#define HW_BCH_VERSION 0x00000160 +#endif diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c new file mode 100644 index 000000000000..97787246af41 --- /dev/null +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -0,0 +1,1510 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include +#include +#include + +#include "gpmi-nand.h" +#include "gpmi-regs.h" +#include "bch-regs.h" + +static struct timing_threshold timing_default_threshold = { + .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> + BP_GPMI_TIMING0_DATA_SETUP), + .internal_data_setup_in_ns = 0, + .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >> + BP_GPMI_CTRL1_RDN_DELAY), + .max_dll_clock_period_in_ns = 32, + .max_dll_delay_in_ns = 16, +}; + +#define MXS_SET_ADDR 0x4 +#define MXS_CLR_ADDR 0x8 +/* + * Clear the bit and poll it cleared. This is usually called with + * a reset address and mask being either SFTRST(bit 31) or CLKGATE + * (bit 30). + */ +static int clear_poll_bit(void __iomem *addr, u32 mask) +{ + int timeout = 0x400; + + /* clear the bit */ + writel(mask, addr + MXS_CLR_ADDR); + + /* + * SFTRST needs 3 GPMI clocks to settle, the reference manual + * recommends to wait 1us. + */ + udelay(1); + + /* poll the bit becoming clear */ + while ((readl(addr) & mask) && --timeout) + /* nothing */; + + return !timeout; +} + +#define MODULE_CLKGATE (1 << 30) +#define MODULE_SFTRST (1 << 31) +/* + * The current mxs_reset_block() will do two things: + * [1] enable the module. + * [2] reset the module. + * + * In most of the cases, it's ok. + * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). + * If you try to soft reset the BCH block, it becomes unusable until + * the next hard reset. This case occurs in the NAND boot mode. When the board + * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. + * So If the driver tries to reset the BCH again, the BCH will not work anymore. + * You will see a DMA timeout in this case. The bug has been fixed + * in the following chips, such as MX28. + * + * To avoid this bug, just add a new parameter `just_enable` for + * the mxs_reset_block(), and rewrite it here. + */ +static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) +{ + int ret; + int timeout = 0x400; + + /* clear and poll SFTRST */ + ret = clear_poll_bit(reset_addr, MODULE_SFTRST); + if (unlikely(ret)) + goto error; + + /* clear CLKGATE */ + writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); + + if (!just_enable) { + /* set SFTRST to reset the block */ + writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); + udelay(1); + + /* poll CLKGATE becoming set */ + while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout) + /* nothing */; + if (unlikely(!timeout)) + goto error; + } + + /* clear and poll SFTRST */ + ret = clear_poll_bit(reset_addr, MODULE_SFTRST); + if (unlikely(ret)) + goto error; + + /* clear and poll CLKGATE */ + ret = clear_poll_bit(reset_addr, MODULE_CLKGATE); + if (unlikely(ret)) + goto error; + + return 0; + +error: + pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); + return -ETIMEDOUT; +} + +static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) +{ + struct clk *clk; + int ret; + int i; + + for (i = 0; i < GPMI_CLK_MAX; i++) { + clk = this->resources.clock[i]; + if (!clk) + break; + + if (v) { + ret = clk_prepare_enable(clk); + if (ret) + goto err_clk; + } else { + clk_disable_unprepare(clk); + } + } + return 0; + +err_clk: + for (; i > 0; i--) + clk_disable_unprepare(this->resources.clock[i - 1]); + return ret; +} + +#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true) +#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false) + +int gpmi_init(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + int ret; + + ret = gpmi_enable_clk(this); + if (ret) + return ret; + ret = gpmi_reset_block(r->gpmi_regs, false); + if (ret) + goto err_out; + + /* + * Reset BCH here, too. We got failures otherwise :( + * See later BCH reset for explanation of MX23 handling + */ + ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); + if (ret) + goto err_out; + + + /* Choose NAND mode. */ + writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Set the IRQ polarity. */ + writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, + r->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Disable Write-Protection. */ + writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Select BCH ECC. */ + writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* + * Decouple the chip select from dma channel. We use dma0 for all + * the chips. + */ + writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + gpmi_disable_clk(this); + return 0; +err_out: + gpmi_disable_clk(this); + return ret; +} + +/* This function is very useful. It is called only when the bug occur. */ +void gpmi_dump_info(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + struct bch_geometry *geo = &this->bch_geometry; + u32 reg; + int i; + + dev_err(this->dev, "Show GPMI registers :\n"); + for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) { + reg = readl(r->gpmi_regs + i * 0x10); + dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); + } + + /* start to print out the BCH info */ + dev_err(this->dev, "Show BCH registers :\n"); + for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) { + reg = readl(r->bch_regs + i * 0x10); + dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); + } + dev_err(this->dev, "BCH Geometry :\n" + "GF length : %u\n" + "ECC Strength : %u\n" + "Page Size in Bytes : %u\n" + "Metadata Size in Bytes : %u\n" + "ECC Chunk Size in Bytes: %u\n" + "ECC Chunk Count : %u\n" + "Payload Size in Bytes : %u\n" + "Auxiliary Size in Bytes: %u\n" + "Auxiliary Status Offset: %u\n" + "Block Mark Byte Offset : %u\n" + "Block Mark Bit Offset : %u\n", + geo->gf_len, + geo->ecc_strength, + geo->page_size, + geo->metadata_size, + geo->ecc_chunk_size, + geo->ecc_chunk_count, + geo->payload_size, + geo->auxiliary_size, + geo->auxiliary_status_offset, + geo->block_mark_byte_offset, + geo->block_mark_bit_offset); +} + +/* Configures the geometry for BCH. */ +int bch_set_geometry(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + struct bch_geometry *bch_geo = &this->bch_geometry; + unsigned int block_count; + unsigned int block_size; + unsigned int metadata_size; + unsigned int ecc_strength; + unsigned int page_size; + unsigned int gf_len; + int ret; + + if (common_nfc_set_geometry(this)) + return !0; + + block_count = bch_geo->ecc_chunk_count - 1; + block_size = bch_geo->ecc_chunk_size; + metadata_size = bch_geo->metadata_size; + ecc_strength = bch_geo->ecc_strength >> 1; + page_size = bch_geo->page_size; + gf_len = bch_geo->gf_len; + + ret = gpmi_enable_clk(this); + if (ret) + return ret; + + /* + * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this + * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. + * On the other hand, the MX28 needs the reset, because one case has been + * seen where the BCH produced ECC errors constantly after 10000 + * consecutive reboots. The latter case has not been seen on the MX23 + * yet, still we don't know if it could happen there as well. + */ + ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); + if (ret) + goto err_out; + + /* Configure layout 0. */ + writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) + | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) + | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) + | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), + r->bch_regs + HW_BCH_FLASH0LAYOUT0); + + writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) + | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) + | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), + r->bch_regs + HW_BCH_FLASH0LAYOUT1); + + /* Set *all* chip selects to use layout 0. */ + writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); + + /* Enable interrupts. */ + writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, + r->bch_regs + HW_BCH_CTRL_SET); + + gpmi_disable_clk(this); + return 0; +err_out: + gpmi_disable_clk(this); + return ret; +} + +/* Converts time in nanoseconds to cycles. */ +static unsigned int ns_to_cycles(unsigned int time, + unsigned int period, unsigned int min) +{ + unsigned int k; + + k = (time + period - 1) / period; + return max(k, min); +} + +#define DEF_MIN_PROP_DELAY 5 +#define DEF_MAX_PROP_DELAY 9 +/* Apply timing to current hardware conditions. */ +static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, + struct gpmi_nfc_hardware_timing *hw) +{ + struct timing_threshold *nfc = &timing_default_threshold; + struct resources *r = &this->resources; + struct nand_chip *nand = &this->nand; + struct nand_timing target = this->timing; + bool improved_timing_is_available; + unsigned long clock_frequency_in_hz; + unsigned int clock_period_in_ns; + bool dll_use_half_periods; + unsigned int dll_delay_shift; + unsigned int max_sample_delay_in_ns; + unsigned int address_setup_in_cycles; + unsigned int data_setup_in_ns; + unsigned int data_setup_in_cycles; + unsigned int data_hold_in_cycles; + int ideal_sample_delay_in_ns; + unsigned int sample_delay_factor; + int tEYE; + unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY; + unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY; + + /* + * If there are multiple chips, we need to relax the timings to allow + * for signal distortion due to higher capacitance. + */ + if (nand->numchips > 2) { + target.data_setup_in_ns += 10; + target.data_hold_in_ns += 10; + target.address_setup_in_ns += 10; + } else if (nand->numchips > 1) { + target.data_setup_in_ns += 5; + target.data_hold_in_ns += 5; + target.address_setup_in_ns += 5; + } + + /* Check if improved timing information is available. */ + improved_timing_is_available = + (target.tREA_in_ns >= 0) && + (target.tRLOH_in_ns >= 0) && + (target.tRHOH_in_ns >= 0); + + /* Inspect the clock. */ + nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]); + clock_frequency_in_hz = nfc->clock_frequency_in_hz; + clock_period_in_ns = NSEC_PER_SEC / clock_frequency_in_hz; + + /* + * The NFC quantizes setup and hold parameters in terms of clock cycles. + * Here, we quantize the setup and hold timing parameters to the + * next-highest clock period to make sure we apply at least the + * specified times. + * + * For data setup and data hold, the hardware interprets a value of zero + * as the largest possible delay. This is not what's intended by a zero + * in the input parameter, so we impose a minimum of one cycle. + */ + data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns, + clock_period_in_ns, 1); + data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns, + clock_period_in_ns, 1); + address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns, + clock_period_in_ns, 0); + + /* + * The clock's period affects the sample delay in a number of ways: + * + * (1) The NFC HAL tells us the maximum clock period the sample delay + * DLL can tolerate. If the clock period is greater than half that + * maximum, we must configure the DLL to be driven by half periods. + * + * (2) We need to convert from an ideal sample delay, in ns, to a + * "sample delay factor," which the NFC uses. This factor depends on + * whether we're driving the DLL with full or half periods. + * Paraphrasing the reference manual: + * + * AD = SDF x 0.125 x RP + * + * where: + * + * AD is the applied delay, in ns. + * SDF is the sample delay factor, which is dimensionless. + * RP is the reference period, in ns, which is a full clock period + * if the DLL is being driven by full periods, or half that if + * the DLL is being driven by half periods. + * + * Let's re-arrange this in a way that's more useful to us: + * + * 8 + * SDF = AD x ---- + * RP + * + * The reference period is either the clock period or half that, so this + * is: + * + * 8 AD x DDF + * SDF = AD x ----- = -------- + * f x P P + * + * where: + * + * f is 1 or 1/2, depending on how we're driving the DLL. + * P is the clock period. + * DDF is the DLL Delay Factor, a dimensionless value that + * incorporates all the constants in the conversion. + * + * DDF will be either 8 or 16, both of which are powers of two. We can + * reduce the cost of this conversion by using bit shifts instead of + * multiplication or division. Thus: + * + * AD << DDS + * SDF = --------- + * P + * + * or + * + * AD = (SDF >> DDS) x P + * + * where: + * + * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF. + */ + if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) { + dll_use_half_periods = true; + dll_delay_shift = 3 + 1; + } else { + dll_use_half_periods = false; + dll_delay_shift = 3; + } + + /* + * Compute the maximum sample delay the NFC allows, under current + * conditions. If the clock is running too slowly, no sample delay is + * possible. + */ + if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns) + max_sample_delay_in_ns = 0; + else { + /* + * Compute the delay implied by the largest sample delay factor + * the NFC allows. + */ + max_sample_delay_in_ns = + (nfc->max_sample_delay_factor * clock_period_in_ns) >> + dll_delay_shift; + + /* + * Check if the implied sample delay larger than the NFC + * actually allows. + */ + if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns) + max_sample_delay_in_ns = nfc->max_dll_delay_in_ns; + } + + /* + * Check if improved timing information is available. If not, we have to + * use a less-sophisticated algorithm. + */ + if (!improved_timing_is_available) { + /* + * Fold the read setup time required by the NFC into the ideal + * sample delay. + */ + ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns + + nfc->internal_data_setup_in_ns; + + /* + * The ideal sample delay may be greater than the maximum + * allowed by the NFC. If so, we can trade off sample delay time + * for more data setup time. + * + * In each iteration of the following loop, we add a cycle to + * the data setup time and subtract a corresponding amount from + * the sample delay until we've satisified the constraints or + * can't do any better. + */ + while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + + data_setup_in_cycles++; + ideal_sample_delay_in_ns -= clock_period_in_ns; + + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + + } + + /* + * Compute the sample delay factor that corresponds most closely + * to the ideal sample delay. If the result is too large for the + * NFC, use the maximum value. + * + * Notice that we use the ns_to_cycles function to compute the + * sample delay factor. We do this because the form of the + * computation is the same as that for calculating cycles. + */ + sample_delay_factor = + ns_to_cycles( + ideal_sample_delay_in_ns << dll_delay_shift, + clock_period_in_ns, 0); + + if (sample_delay_factor > nfc->max_sample_delay_factor) + sample_delay_factor = nfc->max_sample_delay_factor; + + /* Skip to the part where we return our results. */ + goto return_results; + } + + /* + * If control arrives here, we have more detailed timing information, + * so we can use a better algorithm. + */ + + /* + * Fold the read setup time required by the NFC into the maximum + * propagation delay. + */ + max_prop_delay_in_ns += nfc->internal_data_setup_in_ns; + + /* + * Earlier, we computed the number of clock cycles required to satisfy + * the data setup time. Now, we need to know the actual nanoseconds. + */ + data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles; + + /* + * Compute tEYE, the width of the data eye when reading from the NAND + * Flash. The eye width is fundamentally determined by the data setup + * time, perturbed by propagation delays and some characteristics of the + * NAND Flash device. + * + * start of the eye = max_prop_delay + tREA + * end of the eye = min_prop_delay + tRHOH + data_setup + */ + tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns + + (int)data_setup_in_ns; + + tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns; + + /* + * The eye must be open. If it's not, we can try to open it by + * increasing its main forcer, the data setup time. + * + * In each iteration of the following loop, we increase the data setup + * time by a single clock cycle. We do this until either the eye is + * open or we run into NFC limits. + */ + while ((tEYE <= 0) && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + /* Give a cycle to data setup. */ + data_setup_in_cycles++; + /* Synchronize the data setup time with the cycles. */ + data_setup_in_ns += clock_period_in_ns; + /* Adjust tEYE accordingly. */ + tEYE += clock_period_in_ns; + } + + /* + * When control arrives here, the eye is open. The ideal time to sample + * the data is in the center of the eye: + * + * end of the eye + start of the eye + * --------------------------------- - data_setup + * 2 + * + * After some algebra, this simplifies to the code immediately below. + */ + ideal_sample_delay_in_ns = + ((int)max_prop_delay_in_ns + + (int)target.tREA_in_ns + + (int)min_prop_delay_in_ns + + (int)target.tRHOH_in_ns - + (int)data_setup_in_ns) >> 1; + + /* + * The following figure illustrates some aspects of a NAND Flash read: + * + * + * __ _____________________________________ + * RDN \_________________/ + * + * <---- tEYE -----> + * /-----------------\ + * Read Data ----------------------------< >--------- + * \-----------------/ + * ^ ^ ^ ^ + * | | | | + * |<--Data Setup -->|<--Delay Time -->| | + * | | | | + * | | | + * | |<-- Quantized Delay Time -->| + * | | | + * + * + * We have some issues we must now address: + * + * (1) The *ideal* sample delay time must not be negative. If it is, we + * jam it to zero. + * + * (2) The *ideal* sample delay time must not be greater than that + * allowed by the NFC. If it is, we can increase the data setup + * time, which will reduce the delay between the end of the data + * setup and the center of the eye. It will also make the eye + * larger, which might help with the next issue... + * + * (3) The *quantized* sample delay time must not fall either before the + * eye opens or after it closes (the latter is the problem + * illustrated in the above figure). + */ + + /* Jam a negative ideal sample delay to zero. */ + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + + /* + * Extend the data setup as needed to reduce the ideal sample delay + * below the maximum permitted by the NFC. + */ + while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + + /* Give a cycle to data setup. */ + data_setup_in_cycles++; + /* Synchronize the data setup time with the cycles. */ + data_setup_in_ns += clock_period_in_ns; + /* Adjust tEYE accordingly. */ + tEYE += clock_period_in_ns; + + /* + * Decrease the ideal sample delay by one half cycle, to keep it + * in the middle of the eye. + */ + ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); + + /* Jam a negative ideal sample delay to zero. */ + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + } + + /* + * Compute the sample delay factor that corresponds to the ideal sample + * delay. If the result is too large, then use the maximum allowed + * value. + * + * Notice that we use the ns_to_cycles function to compute the sample + * delay factor. We do this because the form of the computation is the + * same as that for calculating cycles. + */ + sample_delay_factor = + ns_to_cycles(ideal_sample_delay_in_ns << dll_delay_shift, + clock_period_in_ns, 0); + + if (sample_delay_factor > nfc->max_sample_delay_factor) + sample_delay_factor = nfc->max_sample_delay_factor; + + /* + * These macros conveniently encapsulate a computation we'll use to + * continuously evaluate whether or not the data sample delay is inside + * the eye. + */ + #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns) + + #define QUANTIZED_DELAY \ + ((int) ((sample_delay_factor * clock_period_in_ns) >> \ + dll_delay_shift)) + + #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY)) + + #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1)) + + /* + * While the quantized sample time falls outside the eye, reduce the + * sample delay or extend the data setup to move the sampling point back + * toward the eye. Do not allow the number of data setup cycles to + * exceed the maximum allowed by the NFC. + */ + while (SAMPLE_IS_NOT_WITHIN_THE_EYE && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + /* + * If control arrives here, the quantized sample delay falls + * outside the eye. Check if it's before the eye opens, or after + * the eye closes. + */ + if (QUANTIZED_DELAY > IDEAL_DELAY) { + /* + * If control arrives here, the quantized sample delay + * falls after the eye closes. Decrease the quantized + * delay time and then go back to re-evaluate. + */ + if (sample_delay_factor != 0) + sample_delay_factor--; + continue; + } + + /* + * If control arrives here, the quantized sample delay falls + * before the eye opens. Shift the sample point by increasing + * data setup time. This will also make the eye larger. + */ + + /* Give a cycle to data setup. */ + data_setup_in_cycles++; + /* Synchronize the data setup time with the cycles. */ + data_setup_in_ns += clock_period_in_ns; + /* Adjust tEYE accordingly. */ + tEYE += clock_period_in_ns; + + /* + * Decrease the ideal sample delay by one half cycle, to keep it + * in the middle of the eye. + */ + ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); + + /* ...and one less period for the delay time. */ + ideal_sample_delay_in_ns -= clock_period_in_ns; + + /* Jam a negative ideal sample delay to zero. */ + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + + /* + * We have a new ideal sample delay, so re-compute the quantized + * delay. + */ + sample_delay_factor = + ns_to_cycles( + ideal_sample_delay_in_ns << dll_delay_shift, + clock_period_in_ns, 0); + + if (sample_delay_factor > nfc->max_sample_delay_factor) + sample_delay_factor = nfc->max_sample_delay_factor; + } + + /* Control arrives here when we're ready to return our results. */ +return_results: + hw->data_setup_in_cycles = data_setup_in_cycles; + hw->data_hold_in_cycles = data_hold_in_cycles; + hw->address_setup_in_cycles = address_setup_in_cycles; + hw->use_half_periods = dll_use_half_periods; + hw->sample_delay_factor = sample_delay_factor; + hw->device_busy_timeout = GPMI_DEFAULT_BUSY_TIMEOUT; + hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; + + /* Return success. */ + return 0; +} + +/* + * <1> Firstly, we should know what's the GPMI-clock means. + * The GPMI-clock is the internal clock in the gpmi nand controller. + * If you set 100MHz to gpmi nand controller, the GPMI-clock's period + * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period. + * + * <2> Secondly, we should know what's the frequency on the nand chip pins. + * The frequency on the nand chip pins is derived from the GPMI-clock. + * We can get it from the following equation: + * + * F = G / (DS + DH) + * + * F : the frequency on the nand chip pins. + * G : the GPMI clock, such as 100MHz. + * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP + * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD + * + * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz, + * the nand EDO(extended Data Out) timing could be applied. + * The GPMI implements a feedback read strobe to sample the read data. + * The feedback read strobe can be delayed to support the nand EDO timing + * where the read strobe may deasserts before the read data is valid, and + * read data is valid for some time after read strobe. + * + * The following figure illustrates some aspects of a NAND Flash read: + * + * |<---tREA---->| + * | | + * | | | + * |<--tRP-->| | + * | | | + * __ ___|__________________________________ + * RDN \________/ | + * | + * /---------\ + * Read Data --------------< >--------- + * \---------/ + * | | + * |<-D->| + * FeedbackRDN ________ ____________ + * \___________/ + * + * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY. + * + * + * <4> Now, we begin to describe how to compute the right RDN_DELAY. + * + * 4.1) From the aspect of the nand chip pins: + * Delay = (tREA + C - tRP) {1} + * + * tREA : the maximum read access time. From the ONFI nand standards, + * we know that tREA is 16ns in mode 5, tREA is 20ns is mode 4. + * Please check it in : www.onfi.org + * C : a constant for adjust the delay. default is 4. + * tRP : the read pulse width. + * Specified by the HW_GPMI_TIMING0:DATA_SETUP: + * tRP = (GPMI-clock-period) * DATA_SETUP + * + * 4.2) From the aspect of the GPMI nand controller: + * Delay = RDN_DELAY * 0.125 * RP {2} + * + * RP : the DLL reference period. + * if (GPMI-clock-period > DLL_THRETHOLD) + * RP = GPMI-clock-period / 2; + * else + * RP = GPMI-clock-period; + * + * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period + * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD + * is 16ns, but in mx6q, we use 12ns. + * + * 4.3) since {1} equals {2}, we get: + * + * (tREA + 4 - tRP) * 8 + * RDN_DELAY = --------------------- {3} + * RP + * + * 4.4) We only support the fastest asynchronous mode of ONFI nand. + * For some ONFI nand, the mode 4 is the fastest mode; + * while for some ONFI nand, the mode 5 is the fastest mode. + * So we only support the mode 4 and mode 5. It is no need to + * support other modes. + */ +static void gpmi_compute_edo_timing(struct gpmi_nand_data *this, + struct gpmi_nfc_hardware_timing *hw) +{ + struct resources *r = &this->resources; + unsigned long rate = clk_get_rate(r->clock[0]); + int mode = this->timing_mode; + int dll_threshold = this->devdata->max_chain_delay; + unsigned long delay; + unsigned long clk_period; + int t_rea; + int c = 4; + int t_rp; + int rp; + + /* + * [1] for GPMI_HW_GPMI_TIMING0: + * The async mode requires 40MHz for mode 4, 50MHz for mode 5. + * The GPMI can support 100MHz at most. So if we want to + * get the 40MHz or 50MHz, we have to set DS=1, DH=1. + * Set the ADDRESS_SETUP to 0 in mode 4. + */ + hw->data_setup_in_cycles = 1; + hw->data_hold_in_cycles = 1; + hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0); + + /* [2] for GPMI_HW_GPMI_TIMING1 */ + hw->device_busy_timeout = 0x9000; + + /* [3] for GPMI_HW_GPMI_CTRL1 */ + hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; + + /* + * Enlarge 10 times for the numerator and denominator in {3}. + * This make us to get more accurate result. + */ + clk_period = NSEC_PER_SEC / (rate / 10); + dll_threshold *= 10; + t_rea = ((mode == 5) ? 16 : 20) * 10; + c *= 10; + + t_rp = clk_period * 1; /* DATA_SETUP is 1 */ + + if (clk_period > dll_threshold) { + hw->use_half_periods = 1; + rp = clk_period / 2; + } else { + hw->use_half_periods = 0; + rp = clk_period; + } + + /* + * Multiply the numerator with 10, we could do a round off: + * 7.8 round up to 8; 7.4 round down to 7. + */ + delay = (((t_rea + c - t_rp) * 8) * 10) / rp; + delay = (delay + 5) / 10; + + hw->sample_delay_factor = delay; +} + +static int enable_edo_mode(struct gpmi_nand_data *this, int mode) +{ + struct resources *r = &this->resources; + struct nand_chip *nand = &this->nand; + struct mtd_info *mtd = nand_to_mtd(nand); + uint8_t *feature; + unsigned long rate; + int ret; + + feature = kzalloc(ONFI_SUBFEATURE_PARAM_LEN, GFP_KERNEL); + if (!feature) + return -ENOMEM; + + nand->select_chip(mtd, 0); + + /* [1] send SET FEATURE command to NAND */ + feature[0] = mode; + ret = nand->onfi_set_features(mtd, nand, + ONFI_FEATURE_ADDR_TIMING_MODE, feature); + if (ret) + goto err_out; + + /* [2] send GET FEATURE command to double-check the timing mode */ + memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN); + ret = nand->onfi_get_features(mtd, nand, + ONFI_FEATURE_ADDR_TIMING_MODE, feature); + if (ret || feature[0] != mode) + goto err_out; + + nand->select_chip(mtd, -1); + + /* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */ + rate = (mode == 5) ? 100000000 : 80000000; + clk_set_rate(r->clock[0], rate); + + /* Let the gpmi_begin() re-compute the timing again. */ + this->flags &= ~GPMI_TIMING_INIT_OK; + + this->flags |= GPMI_ASYNC_EDO_ENABLED; + this->timing_mode = mode; + kfree(feature); + dev_info(this->dev, "enable the asynchronous EDO mode %d\n", mode); + return 0; + +err_out: + nand->select_chip(mtd, -1); + kfree(feature); + dev_err(this->dev, "mode:%d ,failed in set feature.\n", mode); + return -EINVAL; +} + +int gpmi_extra_init(struct gpmi_nand_data *this) +{ + struct nand_chip *chip = &this->nand; + + /* Enable the asynchronous EDO feature. */ + if (GPMI_IS_MX6(this) && chip->onfi_version) { + int mode = onfi_get_async_timing_mode(chip); + + /* We only support the timing mode 4 and mode 5. */ + if (mode & ONFI_TIMING_MODE_5) + mode = 5; + else if (mode & ONFI_TIMING_MODE_4) + mode = 4; + else + return 0; + + return enable_edo_mode(this, mode); + } + return 0; +} + +/* Begin the I/O */ +void gpmi_begin(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + void __iomem *gpmi_regs = r->gpmi_regs; + unsigned int clock_period_in_ns; + uint32_t reg; + unsigned int dll_wait_time_in_us; + struct gpmi_nfc_hardware_timing hw; + int ret; + + /* Enable the clock. */ + ret = gpmi_enable_clk(this); + if (ret) { + dev_err(this->dev, "We failed in enable the clk\n"); + goto err_out; + } + + /* Only initialize the timing once */ + if (this->flags & GPMI_TIMING_INIT_OK) + return; + this->flags |= GPMI_TIMING_INIT_OK; + + if (this->flags & GPMI_ASYNC_EDO_ENABLED) + gpmi_compute_edo_timing(this, &hw); + else + gpmi_nfc_compute_hardware_timing(this, &hw); + + /* [1] Set HW_GPMI_TIMING0 */ + reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) | + BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) | + BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles); + + writel(reg, gpmi_regs + HW_GPMI_TIMING0); + + /* [2] Set HW_GPMI_TIMING1 */ + writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw.device_busy_timeout), + gpmi_regs + HW_GPMI_TIMING1); + + /* [3] The following code is to set the HW_GPMI_CTRL1. */ + + /* Set the WRN_DLY_SEL */ + writel(BM_GPMI_CTRL1_WRN_DLY_SEL, gpmi_regs + HW_GPMI_CTRL1_CLR); + writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw.wrn_dly_sel), + gpmi_regs + HW_GPMI_CTRL1_SET); + + /* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. */ + writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Clear out the DLL control fields. */ + reg = BM_GPMI_CTRL1_RDN_DELAY | BM_GPMI_CTRL1_HALF_PERIOD; + writel(reg, gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* If no sample delay is called for, return immediately. */ + if (!hw.sample_delay_factor) + return; + + /* Set RDN_DELAY or HALF_PERIOD. */ + reg = ((hw.use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0) + | BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor); + + writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET); + + /* At last, we enable the DLL. */ + writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); + + /* + * After we enable the GPMI DLL, we have to wait 64 clock cycles before + * we can use the GPMI. Calculate the amount of time we need to wait, + * in microseconds. + */ + clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]); + dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; + + if (!dll_wait_time_in_us) + dll_wait_time_in_us = 1; + + /* Wait for the DLL to settle. */ + udelay(dll_wait_time_in_us); + +err_out: + return; +} + +void gpmi_end(struct gpmi_nand_data *this) +{ + gpmi_disable_clk(this); +} + +/* Clears a BCH interrupt. */ +void gpmi_clear_bch(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); +} + +/* Returns the Ready/Busy status of the given chip. */ +int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) +{ + struct resources *r = &this->resources; + uint32_t mask = 0; + uint32_t reg = 0; + + if (GPMI_IS_MX23(this)) { + mask = MX23_BM_GPMI_DEBUG_READY0 << chip; + reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); + } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) { + /* + * In the imx6, all the ready/busy pins are bound + * together. So we only need to check chip 0. + */ + if (GPMI_IS_MX6(this)) + chip = 0; + + /* MX28 shares the same R/B register as MX6Q. */ + mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); + reg = readl(r->gpmi_regs + HW_GPMI_STAT); + } else + dev_err(this->dev, "unknown arch.\n"); + return reg & mask; +} + +static inline void set_dma_type(struct gpmi_nand_data *this, + enum dma_ops_type type) +{ + this->last_dma_type = this->dma_type; + this->dma_type = type; +} + +int gpmi_send_command(struct gpmi_nand_data *this) +{ + struct dma_chan *channel = get_dma_chan(this); + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl; + int chip = this->current_chip; + u32 pio[3]; + + /* [1] send out the PIO words */ + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) + | BM_GPMI_CTRL0_ADDRESS_INCREMENT + | BF_GPMI_CTRL0_XFER_COUNT(this->command_length); + pio[1] = pio[2] = 0; + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); + if (!desc) + return -EINVAL; + + /* [2] send out the COMMAND + ADDRESS string stored in @buffer */ + sgl = &this->cmd_sgl; + + sg_init_one(sgl, this->cmd_buffer, this->command_length); + dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); + desc = dmaengine_prep_slave_sg(channel, + sgl, 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [3] submit the DMA */ + set_dma_type(this, DMA_FOR_COMMAND); + return start_dma_without_bch_irq(this, desc); +} + +int gpmi_send_data(struct gpmi_nand_data *this) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + int chip = this->current_chip; + uint32_t command_mode; + uint32_t address; + u32 pio[2]; + + /* [1] PIO */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); + pio[1] = 0; + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); + if (!desc) + return -EINVAL; + + /* [2] send DMA request */ + prepare_data_dma(this, DMA_TO_DEVICE); + desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, + 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [3] submit the DMA */ + set_dma_type(this, DMA_FOR_WRITE_DATA); + return start_dma_without_bch_irq(this, desc); +} + +int gpmi_read_data(struct gpmi_nand_data *this) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + int chip = this->current_chip; + u32 pio[2]; + + /* [1] : send PIO */ + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) + | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); + pio[1] = 0; + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); + if (!desc) + return -EINVAL; + + /* [2] : send DMA request */ + prepare_data_dma(this, DMA_FROM_DEVICE); + desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, + 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [3] : submit the DMA */ + set_dma_type(this, DMA_FOR_READ_DATA); + return start_dma_without_bch_irq(this, desc); +} + +int gpmi_send_page(struct gpmi_nand_data *this, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct bch_geometry *geo = &this->bch_geometry; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + int chip = this->current_chip; + u32 pio[6]; + + /* A DMA descriptor that does an ECC page read. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(0); + pio[1] = 0; + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); + pio[3] = geo->page_size; + pio[4] = payload; + pio[5] = auxiliary; + + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, + DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE); + return start_dma_with_bch_irq(this, desc); +} + +int gpmi_read_page(struct gpmi_nand_data *this, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct bch_geometry *geo = &this->bch_geometry; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + int chip = this->current_chip; + u32 pio[6]; + + /* [1] Wait for the chip to report ready. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(0); + pio[1] = 0; + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, 2, + DMA_TRANS_NONE, 0); + if (!desc) + return -EINVAL; + + /* [2] Enable the BCH block and read. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE + | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); + + pio[1] = 0; + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); + pio[3] = geo->page_size; + pio[4] = payload; + pio[5] = auxiliary; + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_TRANS_NONE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [3] Disable the BCH block */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); + pio[1] = 0; + pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */ + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, 3, + DMA_TRANS_NONE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EINVAL; + + /* [4] submit the DMA */ + set_dma_type(this, DMA_FOR_READ_ECC_PAGE); + return start_dma_with_bch_irq(this, desc); +} + +/** + * gpmi_copy_bits - copy bits from one memory region to another + * @dst: destination buffer + * @dst_bit_off: bit offset we're starting to write at + * @src: source buffer + * @src_bit_off: bit offset we're starting to read from + * @nbits: number of bits to copy + * + * This functions copies bits from one memory region to another, and is used by + * the GPMI driver to copy ECC sections which are not guaranteed to be byte + * aligned. + * + * src and dst should not overlap. + * + */ +void gpmi_copy_bits(u8 *dst, size_t dst_bit_off, + const u8 *src, size_t src_bit_off, + size_t nbits) +{ + size_t i; + size_t nbytes; + u32 src_buffer = 0; + size_t bits_in_src_buffer = 0; + + if (!nbits) + return; + + /* + * Move src and dst pointers to the closest byte pointer and store bit + * offsets within a byte. + */ + src += src_bit_off / 8; + src_bit_off %= 8; + + dst += dst_bit_off / 8; + dst_bit_off %= 8; + + /* + * Initialize the src_buffer value with bits available in the first + * byte of data so that we end up with a byte aligned src pointer. + */ + if (src_bit_off) { + src_buffer = src[0] >> src_bit_off; + if (nbits >= (8 - src_bit_off)) { + bits_in_src_buffer += 8 - src_bit_off; + } else { + src_buffer &= GENMASK(nbits - 1, 0); + bits_in_src_buffer += nbits; + } + nbits -= bits_in_src_buffer; + src++; + } + + /* Calculate the number of bytes that can be copied from src to dst. */ + nbytes = nbits / 8; + + /* Try to align dst to a byte boundary. */ + if (dst_bit_off) { + if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) { + src_buffer |= src[0] << bits_in_src_buffer; + bits_in_src_buffer += 8; + src++; + nbytes--; + } + + if (bits_in_src_buffer >= (8 - dst_bit_off)) { + dst[0] &= GENMASK(dst_bit_off - 1, 0); + dst[0] |= src_buffer << dst_bit_off; + src_buffer >>= (8 - dst_bit_off); + bits_in_src_buffer -= (8 - dst_bit_off); + dst_bit_off = 0; + dst++; + if (bits_in_src_buffer > 7) { + bits_in_src_buffer -= 8; + dst[0] = src_buffer; + dst++; + src_buffer >>= 8; + } + } + } + + if (!bits_in_src_buffer && !dst_bit_off) { + /* + * Both src and dst pointers are byte aligned, thus we can + * just use the optimized memcpy function. + */ + if (nbytes) + memcpy(dst, src, nbytes); + } else { + /* + * src buffer is not byte aligned, hence we have to copy each + * src byte to the src_buffer variable before extracting a byte + * to store in dst. + */ + for (i = 0; i < nbytes; i++) { + src_buffer |= src[i] << bits_in_src_buffer; + dst[i] = src_buffer; + src_buffer >>= 8; + } + } + /* Update dst and src pointers */ + dst += nbytes; + src += nbytes; + + /* + * nbits is the number of remaining bits. It should not exceed 8 as + * we've already copied as much bytes as possible. + */ + nbits %= 8; + + /* + * If there's no more bits to copy to the destination and src buffer + * was already byte aligned, then we're done. + */ + if (!nbits && !bits_in_src_buffer) + return; + + /* Copy the remaining bits to src_buffer */ + if (nbits) + src_buffer |= (*src & GENMASK(nbits - 1, 0)) << + bits_in_src_buffer; + bits_in_src_buffer += nbits; + + /* + * In case there were not enough bits to get a byte aligned dst buffer + * prepare the src_buffer variable to match the dst organization (shift + * src_buffer by dst_bit_off and retrieve the least significant bits + * from dst). + */ + if (dst_bit_off) + src_buffer = (src_buffer << dst_bit_off) | + (*dst & GENMASK(dst_bit_off - 1, 0)); + bits_in_src_buffer += dst_bit_off; + + /* + * Keep most significant bits from dst if we end up with an unaligned + * number of bits. + */ + nbytes = bits_in_src_buffer / 8; + if (bits_in_src_buffer % 8) { + src_buffer |= (dst[nbytes] & + GENMASK(7, bits_in_src_buffer % 8)) << + (nbytes * 8); + nbytes++; + } + + /* Copy the remaining bytes to dst */ + for (i = 0; i < nbytes; i++) { + dst[i] = src_buffer; + src_buffer >>= 8; + } +} diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c new file mode 100644 index 000000000000..61fdd733492f --- /dev/null +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -0,0 +1,2182 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "gpmi-nand.h" +#include "bch-regs.h" + +/* Resource names for the GPMI NAND driver. */ +#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" +#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" +#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" + +/* add our owner bbt descriptor */ +static uint8_t scan_ff_pattern[] = { 0xff }; +static struct nand_bbt_descr gpmi_bbt_descr = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern +}; + +/* + * We may change the layout if we can get the ECC info from the datasheet, + * else we will use all the (page + OOB). + */ +static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *geo = &this->bch_geometry; + + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = geo->page_size - mtd->writesize; + + return 0; +} + +static int gpmi_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *geo = &this->bch_geometry; + + if (section) + return -ERANGE; + + /* The available oob size we have. */ + if (geo->page_size < mtd->writesize + mtd->oobsize) { + oobregion->offset = geo->page_size - mtd->writesize; + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const char * const gpmi_clks_for_mx2x[] = { + "gpmi_io", +}; + +static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { + .ecc = gpmi_ooblayout_ecc, + .free = gpmi_ooblayout_free, +}; + +static const struct gpmi_devdata gpmi_devdata_imx23 = { + .type = IS_MX23, + .bch_max_ecc_strength = 20, + .max_chain_delay = 16, + .clks = gpmi_clks_for_mx2x, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), +}; + +static const struct gpmi_devdata gpmi_devdata_imx28 = { + .type = IS_MX28, + .bch_max_ecc_strength = 20, + .max_chain_delay = 16, + .clks = gpmi_clks_for_mx2x, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), +}; + +static const char * const gpmi_clks_for_mx6[] = { + "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", +}; + +static const struct gpmi_devdata gpmi_devdata_imx6q = { + .type = IS_MX6Q, + .bch_max_ecc_strength = 40, + .max_chain_delay = 12, + .clks = gpmi_clks_for_mx6, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), +}; + +static const struct gpmi_devdata gpmi_devdata_imx6sx = { + .type = IS_MX6SX, + .bch_max_ecc_strength = 62, + .max_chain_delay = 12, + .clks = gpmi_clks_for_mx6, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), +}; + +static const char * const gpmi_clks_for_mx7d[] = { + "gpmi_io", "gpmi_bch_apb", +}; + +static const struct gpmi_devdata gpmi_devdata_imx7d = { + .type = IS_MX7D, + .bch_max_ecc_strength = 62, + .max_chain_delay = 12, + .clks = gpmi_clks_for_mx7d, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), +}; + +static irqreturn_t bch_irq(int irq, void *cookie) +{ + struct gpmi_nand_data *this = cookie; + + gpmi_clear_bch(this); + complete(&this->bch_done); + return IRQ_HANDLED; +} + +/* + * Calculate the ECC strength by hand: + * E : The ECC strength. + * G : the length of Galois Field. + * N : The chunk count of per page. + * O : the oobsize of the NAND chip. + * M : the metasize of per page. + * + * The formula is : + * E * G * N + * ------------ <= (O - M) + * 8 + * + * So, we get E by: + * (O - M) * 8 + * E <= ------------- + * G * N + */ +static inline int get_ecc_strength(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct mtd_info *mtd = nand_to_mtd(&this->nand); + int ecc_strength; + + ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8) + / (geo->gf_len * geo->ecc_chunk_count); + + /* We need the minor even number. */ + return round_down(ecc_strength, 2); +} + +static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + + /* Do the sanity check. */ + if (GPMI_IS_MX23(this) || GPMI_IS_MX28(this)) { + /* The mx23/mx28 only support the GF13. */ + if (geo->gf_len == 14) + return false; + } + return geo->ecc_strength <= this->devdata->bch_max_ecc_strength; +} + +/* + * If we can get the ECC information from the nand chip, we do not + * need to calculate them ourselves. + * + * We may have available oob space in this case. + */ +static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int block_mark_bit_offset; + + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return -EINVAL; + + switch (chip->ecc_step_ds) { + case SZ_512: + geo->gf_len = 13; + break; + case SZ_1K: + geo->gf_len = 14; + break; + default: + dev_err(this->dev, + "unsupported nand chip. ecc bits : %d, ecc size : %d\n", + chip->ecc_strength_ds, chip->ecc_step_ds); + return -EINVAL; + } + geo->ecc_chunk_size = chip->ecc_step_ds; + geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); + if (!gpmi_check_ecc(this)) + return -EINVAL; + + /* Keep the C >= O */ + if (geo->ecc_chunk_size < mtd->oobsize) { + dev_err(this->dev, + "unsupported nand chip. ecc size: %d, oob size : %d\n", + chip->ecc_step_ds, mtd->oobsize); + return -EINVAL; + } + + /* The default value, see comment in the legacy_set_geometry(). */ + geo->metadata_size = 10; + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + /* + * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: + * + * | P | + * |<----------------------------------------------------->| + * | | + * | (Block Mark) | + * | P' | | | | + * |<-------------------------------------------->| D | | O' | + * | |<---->| |<--->| + * V V V V V + * +---+----------+-+----------+-+----------+-+----------+-+-----+ + * | M | data |E| data |E| data |E| data |E| | + * +---+----------+-+----------+-+----------+-+----------+-+-----+ + * ^ ^ + * | O | + * |<------------>| + * | | + * + * P : the page size for BCH module. + * E : The ECC strength. + * G : the length of Galois Field. + * N : The chunk count of per page. + * M : the metasize of per page. + * C : the ecc chunk size, aka the "data" above. + * P': the nand chip's page size. + * O : the nand chip's oob size. + * O': the free oob. + * + * The formula for P is : + * + * E * G * N + * P = ------------ + P' + M + * 8 + * + * The position of block mark moves forward in the ECC-based view + * of page, and the delta is: + * + * E * G * (N - 1) + * D = (---------------- + M) + * 8 + * + * Please see the comment in legacy_set_geometry(). + * With the condition C >= O , we still can get same result. + * So the bit position of the physical block mark within the ECC-based + * view of the page is : + * (P' - D) * 8 + */ + geo->page_size = mtd->writesize + geo->metadata_size + + (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; + + geo->payload_size = mtd->writesize; + + geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); + geo->auxiliary_size = ALIGN(geo->metadata_size, 4) + + ALIGN(geo->ecc_chunk_count, 4); + + if (!this->swap_block_mark) + return 0; + + /* For bit swap. */ + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) + + geo->metadata_size * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + return 0; +} + +static int legacy_set_geometry(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct mtd_info *mtd = nand_to_mtd(&this->nand); + unsigned int metadata_size; + unsigned int status_size; + unsigned int block_mark_bit_offset; + + /* + * The size of the metadata can be changed, though we set it to 10 + * bytes now. But it can't be too large, because we have to save + * enough space for BCH. + */ + geo->metadata_size = 10; + + /* The default for the length of Galois Field. */ + geo->gf_len = 13; + + /* The default for chunk size. */ + geo->ecc_chunk_size = 512; + while (geo->ecc_chunk_size < mtd->oobsize) { + geo->ecc_chunk_size *= 2; /* keep C >= O */ + geo->gf_len = 14; + } + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + /* We use the same ECC strength for all chunks. */ + geo->ecc_strength = get_ecc_strength(this); + if (!gpmi_check_ecc(this)) { + dev_err(this->dev, + "ecc strength: %d cannot be supported by the controller (%d)\n" + "try to use minimum ecc strength that NAND chip required\n", + geo->ecc_strength, + this->devdata->bch_max_ecc_strength); + return -EINVAL; + } + + geo->page_size = mtd->writesize + geo->metadata_size + + (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; + geo->payload_size = mtd->writesize; + + /* + * The auxiliary buffer contains the metadata and the ECC status. The + * metadata is padded to the nearest 32-bit boundary. The ECC status + * contains one byte for every ECC chunk, and is also padded to the + * nearest 32-bit boundary. + */ + metadata_size = ALIGN(geo->metadata_size, 4); + status_size = ALIGN(geo->ecc_chunk_count, 4); + + geo->auxiliary_size = metadata_size + status_size; + geo->auxiliary_status_offset = metadata_size; + + if (!this->swap_block_mark) + return 0; + + /* + * We need to compute the byte and bit offsets of + * the physical block mark within the ECC-based view of the page. + * + * NAND chip with 2K page shows below: + * (Block Mark) + * | | + * | D | + * |<---->| + * V V + * +---+----------+-+----------+-+----------+-+----------+-+ + * | M | data |E| data |E| data |E| data |E| + * +---+----------+-+----------+-+----------+-+----------+-+ + * + * The position of block mark moves forward in the ECC-based view + * of page, and the delta is: + * + * E * G * (N - 1) + * D = (---------------- + M) + * 8 + * + * With the formula to compute the ECC strength, and the condition + * : C >= O (C is the ecc chunk size) + * + * It's easy to deduce to the following result: + * + * E * G (O - M) C - M C - M + * ----------- <= ------- <= -------- < --------- + * 8 N N (N - 1) + * + * So, we get: + * + * E * G * (N - 1) + * D = (---------------- + M) < C + * 8 + * + * The above inequality means the position of block mark + * within the ECC-based view of the page is still in the data chunk, + * and it's NOT in the ECC bits of the chunk. + * + * Use the following to compute the bit position of the + * physical block mark within the ECC-based view of the page: + * (page_size - D) * 8 + * + * --Huang Shijie + */ + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) + + geo->metadata_size * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + return 0; +} + +int common_nfc_set_geometry(struct gpmi_nand_data *this) +{ + if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) + || legacy_set_geometry(this)) + return set_geometry_by_ecc_info(this); + + return 0; +} + +struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) +{ + /* We use the DMA channel 0 to access all the nand chips. */ + return this->dma_chans[0]; +} + +/* Can we use the upper's buffer directly for DMA? */ +void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) +{ + struct scatterlist *sgl = &this->data_sgl; + int ret; + + /* first try to map the upper buffer directly */ + if (virt_addr_valid(this->upper_buf) && + !object_is_on_stack(this->upper_buf)) { + sg_init_one(sgl, this->upper_buf, this->upper_len); + ret = dma_map_sg(this->dev, sgl, 1, dr); + if (ret == 0) + goto map_fail; + + this->direct_dma_map_ok = true; + return; + } + +map_fail: + /* We have to use our own DMA buffer. */ + sg_init_one(sgl, this->data_buffer_dma, this->upper_len); + + if (dr == DMA_TO_DEVICE) + memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len); + + dma_map_sg(this->dev, sgl, 1, dr); + + this->direct_dma_map_ok = false; +} + +/* This will be called after the DMA operation is finished. */ +static void dma_irq_callback(void *param) +{ + struct gpmi_nand_data *this = param; + struct completion *dma_c = &this->dma_done; + + switch (this->dma_type) { + case DMA_FOR_COMMAND: + dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE); + break; + + case DMA_FOR_READ_DATA: + dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); + if (this->direct_dma_map_ok == false) + memcpy(this->upper_buf, this->data_buffer_dma, + this->upper_len); + break; + + case DMA_FOR_WRITE_DATA: + dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); + break; + + case DMA_FOR_READ_ECC_PAGE: + case DMA_FOR_WRITE_ECC_PAGE: + /* We have to wait the BCH interrupt to finish. */ + break; + + default: + dev_err(this->dev, "in wrong DMA operation.\n"); + } + + complete(dma_c); +} + +int start_dma_without_bch_irq(struct gpmi_nand_data *this, + struct dma_async_tx_descriptor *desc) +{ + struct completion *dma_c = &this->dma_done; + unsigned long timeout; + + init_completion(dma_c); + + desc->callback = dma_irq_callback; + desc->callback_param = this; + dmaengine_submit(desc); + dma_async_issue_pending(get_dma_chan(this)); + + /* Wait for the interrupt from the DMA block. */ + timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000)); + if (!timeout) { + dev_err(this->dev, "DMA timeout, last DMA :%d\n", + this->last_dma_type); + gpmi_dump_info(this); + return -ETIMEDOUT; + } + return 0; +} + +/* + * This function is used in BCH reading or BCH writing pages. + * It will wait for the BCH interrupt as long as ONE second. + * Actually, we must wait for two interrupts : + * [1] firstly the DMA interrupt and + * [2] secondly the BCH interrupt. + */ +int start_dma_with_bch_irq(struct gpmi_nand_data *this, + struct dma_async_tx_descriptor *desc) +{ + struct completion *bch_c = &this->bch_done; + unsigned long timeout; + + /* Prepare to receive an interrupt from the BCH block. */ + init_completion(bch_c); + + /* start the DMA */ + start_dma_without_bch_irq(this, desc); + + /* Wait for the interrupt from the BCH block. */ + timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000)); + if (!timeout) { + dev_err(this->dev, "BCH timeout, last DMA :%d\n", + this->last_dma_type); + gpmi_dump_info(this); + return -ETIMEDOUT; + } + return 0; +} + +static int acquire_register_block(struct gpmi_nand_data *this, + const char *res_name) +{ + struct platform_device *pdev = this->pdev; + struct resources *res = &this->resources; + struct resource *r; + void __iomem *p; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); + p = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(p)) + return PTR_ERR(p); + + if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME)) + res->gpmi_regs = p; + else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME)) + res->bch_regs = p; + else + dev_err(this->dev, "unknown resource name : %s\n", res_name); + + return 0; +} + +static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h) +{ + struct platform_device *pdev = this->pdev; + const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME; + struct resource *r; + int err; + + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name); + if (!r) { + dev_err(this->dev, "Can't get resource for %s\n", res_name); + return -ENODEV; + } + + err = devm_request_irq(this->dev, r->start, irq_h, 0, res_name, this); + if (err) + dev_err(this->dev, "error requesting BCH IRQ\n"); + + return err; +} + +static void release_dma_channels(struct gpmi_nand_data *this) +{ + unsigned int i; + for (i = 0; i < DMA_CHANS; i++) + if (this->dma_chans[i]) { + dma_release_channel(this->dma_chans[i]); + this->dma_chans[i] = NULL; + } +} + +static int acquire_dma_channels(struct gpmi_nand_data *this) +{ + struct platform_device *pdev = this->pdev; + struct dma_chan *dma_chan; + + /* request dma channel */ + dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx"); + if (!dma_chan) { + dev_err(this->dev, "Failed to request DMA channel.\n"); + goto acquire_err; + } + + this->dma_chans[0] = dma_chan; + return 0; + +acquire_err: + release_dma_channels(this); + return -EINVAL; +} + +static int gpmi_get_clks(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + struct clk *clk; + int err, i; + + for (i = 0; i < this->devdata->clks_count; i++) { + clk = devm_clk_get(this->dev, this->devdata->clks[i]); + if (IS_ERR(clk)) { + err = PTR_ERR(clk); + goto err_clock; + } + + r->clock[i] = clk; + } + + if (GPMI_IS_MX6(this)) + /* + * Set the default value for the gpmi clock. + * + * If you want to use the ONFI nand which is in the + * Synchronous Mode, you should change the clock as you need. + */ + clk_set_rate(r->clock[0], 22000000); + + return 0; + +err_clock: + dev_dbg(this->dev, "failed in finding the clocks.\n"); + return err; +} + +static int acquire_resources(struct gpmi_nand_data *this) +{ + int ret; + + ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME); + if (ret) + goto exit_regs; + + ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME); + if (ret) + goto exit_regs; + + ret = acquire_bch_irq(this, bch_irq); + if (ret) + goto exit_regs; + + ret = acquire_dma_channels(this); + if (ret) + goto exit_regs; + + ret = gpmi_get_clks(this); + if (ret) + goto exit_clock; + return 0; + +exit_clock: + release_dma_channels(this); +exit_regs: + return ret; +} + +static void release_resources(struct gpmi_nand_data *this) +{ + release_dma_channels(this); +} + +static int init_hardware(struct gpmi_nand_data *this) +{ + int ret; + + /* + * This structure contains the "safe" GPMI timing that should succeed + * with any NAND Flash device + * (although, with less-than-optimal performance). + */ + struct nand_timing safe_timing = { + .data_setup_in_ns = 80, + .data_hold_in_ns = 60, + .address_setup_in_ns = 25, + .gpmi_sample_delay_in_ns = 6, + .tREA_in_ns = -1, + .tRLOH_in_ns = -1, + .tRHOH_in_ns = -1, + }; + + /* Initialize the hardwares. */ + ret = gpmi_init(this); + if (ret) + return ret; + + this->timing = safe_timing; + return 0; +} + +static int read_page_prepare(struct gpmi_nand_data *this, + void *destination, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + void **use_virt, dma_addr_t *use_phys) +{ + struct device *dev = this->dev; + + if (virt_addr_valid(destination)) { + dma_addr_t dest_phys; + + dest_phys = dma_map_single(dev, destination, + length, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, dest_phys)) { + if (alt_size < length) { + dev_err(dev, "Alternate buffer is too small\n"); + return -ENOMEM; + } + goto map_failed; + } + *use_virt = destination; + *use_phys = dest_phys; + this->direct_dma_map_ok = true; + return 0; + } + +map_failed: + *use_virt = alt_virt; + *use_phys = alt_phys; + this->direct_dma_map_ok = false; + return 0; +} + +static inline void read_page_end(struct gpmi_nand_data *this, + void *destination, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + void *used_virt, dma_addr_t used_phys) +{ + if (this->direct_dma_map_ok) + dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE); +} + +static inline void read_page_swap_end(struct gpmi_nand_data *this, + void *destination, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + void *used_virt, dma_addr_t used_phys) +{ + if (!this->direct_dma_map_ok) + memcpy(destination, alt_virt, length); +} + +static int send_page_prepare(struct gpmi_nand_data *this, + const void *source, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + const void **use_virt, dma_addr_t *use_phys) +{ + struct device *dev = this->dev; + + if (virt_addr_valid(source)) { + dma_addr_t source_phys; + + source_phys = dma_map_single(dev, (void *)source, length, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, source_phys)) { + if (alt_size < length) { + dev_err(dev, "Alternate buffer is too small\n"); + return -ENOMEM; + } + goto map_failed; + } + *use_virt = source; + *use_phys = source_phys; + return 0; + } +map_failed: + /* + * Copy the content of the source buffer into the alternate + * buffer and set up the return values accordingly. + */ + memcpy(alt_virt, source, length); + + *use_virt = alt_virt; + *use_phys = alt_phys; + return 0; +} + +static void send_page_end(struct gpmi_nand_data *this, + const void *source, unsigned length, + void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, + const void *used_virt, dma_addr_t used_phys) +{ + struct device *dev = this->dev; + if (used_virt == source) + dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE); +} + +static void gpmi_free_dma_buffer(struct gpmi_nand_data *this) +{ + struct device *dev = this->dev; + + if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt)) + dma_free_coherent(dev, this->page_buffer_size, + this->page_buffer_virt, + this->page_buffer_phys); + kfree(this->cmd_buffer); + kfree(this->data_buffer_dma); + kfree(this->raw_buffer); + + this->cmd_buffer = NULL; + this->data_buffer_dma = NULL; + this->raw_buffer = NULL; + this->page_buffer_virt = NULL; + this->page_buffer_size = 0; +} + +/* Allocate the DMA buffers */ +static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct device *dev = this->dev; + struct mtd_info *mtd = nand_to_mtd(&this->nand); + + /* [1] Allocate a command buffer. PAGE_SIZE is enough. */ + this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL); + if (this->cmd_buffer == NULL) + goto error_alloc; + + /* + * [2] Allocate a read/write data buffer. + * The gpmi_alloc_dma_buffer can be called twice. + * We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer + * is called before the nand_scan_ident; and we allocate a buffer + * of the real NAND page size when the gpmi_alloc_dma_buffer is + * called after the nand_scan_ident. + */ + this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE, + GFP_DMA | GFP_KERNEL); + if (this->data_buffer_dma == NULL) + goto error_alloc; + + /* + * [3] Allocate the page buffer. + * + * Both the payload buffer and the auxiliary buffer must appear on + * 32-bit boundaries. We presume the size of the payload buffer is a + * power of two and is much larger than four, which guarantees the + * auxiliary buffer will appear on a 32-bit boundary. + */ + this->page_buffer_size = geo->payload_size + geo->auxiliary_size; + this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size, + &this->page_buffer_phys, GFP_DMA); + if (!this->page_buffer_virt) + goto error_alloc; + + this->raw_buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!this->raw_buffer) + goto error_alloc; + + /* Slice up the page buffer. */ + this->payload_virt = this->page_buffer_virt; + this->payload_phys = this->page_buffer_phys; + this->auxiliary_virt = this->payload_virt + geo->payload_size; + this->auxiliary_phys = this->payload_phys + geo->payload_size; + return 0; + +error_alloc: + gpmi_free_dma_buffer(this); + return -ENOMEM; +} + +static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + int ret; + + /* + * Every operation begins with a command byte and a series of zero or + * more address bytes. These are distinguished by either the Address + * Latch Enable (ALE) or Command Latch Enable (CLE) signals being + * asserted. When MTD is ready to execute the command, it will deassert + * both latch enables. + * + * Rather than run a separate DMA operation for every single byte, we + * queue them up and run a single DMA operation for the entire series + * of command and data bytes. NAND_CMD_NONE means the END of the queue. + */ + if ((ctrl & (NAND_ALE | NAND_CLE))) { + if (data != NAND_CMD_NONE) + this->cmd_buffer[this->command_length++] = data; + return; + } + + if (!this->command_length) + return; + + ret = gpmi_send_command(this); + if (ret) + dev_err(this->dev, "Chip: %u, Error %d\n", + this->current_chip, ret); + + this->command_length = 0; +} + +static int gpmi_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + + return gpmi_is_ready(this, this->current_chip); +} + +static void gpmi_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + + if ((this->current_chip < 0) && (chipnr >= 0)) + gpmi_begin(this); + else if ((this->current_chip >= 0) && (chipnr < 0)) + gpmi_end(this); + + this->current_chip = chipnr; +} + +static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + + dev_dbg(this->dev, "len is %d\n", len); + this->upper_buf = buf; + this->upper_len = len; + + gpmi_read_data(this); +} + +static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + + dev_dbg(this->dev, "len is %d\n", len); + this->upper_buf = (uint8_t *)buf; + this->upper_len = len; + + gpmi_send_data(this); +} + +static uint8_t gpmi_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + uint8_t *buf = this->data_buffer_dma; + + gpmi_read_buf(mtd, buf, 1); + return buf[0]; +} + +/* + * Handles block mark swapping. + * It can be called in swapping the block mark, or swapping it back, + * because the the operations are the same. + */ +static void block_mark_swapping(struct gpmi_nand_data *this, + void *payload, void *auxiliary) +{ + struct bch_geometry *nfc_geo = &this->bch_geometry; + unsigned char *p; + unsigned char *a; + unsigned int bit; + unsigned char mask; + unsigned char from_data; + unsigned char from_oob; + + if (!this->swap_block_mark) + return; + + /* + * If control arrives here, we're swapping. Make some convenience + * variables. + */ + bit = nfc_geo->block_mark_bit_offset; + p = payload + nfc_geo->block_mark_byte_offset; + a = auxiliary; + + /* + * Get the byte from the data area that overlays the block mark. Since + * the ECC engine applies its own view to the bits in the page, the + * physical block mark won't (in general) appear on a byte boundary in + * the data. + */ + from_data = (p[0] >> bit) | (p[1] << (8 - bit)); + + /* Get the byte from the OOB. */ + from_oob = a[0]; + + /* Swap them. */ + a[0] = from_data; + + mask = (0x1 << bit) - 1; + p[0] = (p[0] & mask) | (from_oob << bit); + + mask = ~0 << bit; + p[1] = (p[1] & mask) | (from_oob >> (8 - bit)); +} + +static int gpmi_ecc_read_page_data(struct nand_chip *chip, + uint8_t *buf, int oob_required, + int page) +{ + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *nfc_geo = &this->bch_geometry; + struct mtd_info *mtd = nand_to_mtd(chip); + void *payload_virt; + dma_addr_t payload_phys; + void *auxiliary_virt; + dma_addr_t auxiliary_phys; + unsigned int i; + unsigned char *status; + unsigned int max_bitflips = 0; + int ret; + + dev_dbg(this->dev, "page number is : %d\n", page); + ret = read_page_prepare(this, buf, nfc_geo->payload_size, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + &payload_virt, &payload_phys); + if (ret) { + dev_err(this->dev, "Inadequate DMA buffer\n"); + ret = -ENOMEM; + return ret; + } + auxiliary_virt = this->auxiliary_virt; + auxiliary_phys = this->auxiliary_phys; + + /* go! */ + ret = gpmi_read_page(this, payload_phys, auxiliary_phys); + read_page_end(this, buf, nfc_geo->payload_size, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + if (ret) { + dev_err(this->dev, "Error in ECC-based read: %d\n", ret); + return ret; + } + + /* Loop over status bytes, accumulating ECC status. */ + status = auxiliary_virt + nfc_geo->auxiliary_status_offset; + + read_page_swap_end(this, buf, nfc_geo->payload_size, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + + for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { + if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) + continue; + + if (*status == STATUS_UNCORRECTABLE) { + int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; + u8 *eccbuf = this->raw_buffer; + int offset, bitoffset; + int eccbytes; + int flips; + + /* Read ECC bytes into our internal raw_buffer */ + offset = nfc_geo->metadata_size * 8; + offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1); + offset -= eccbits; + bitoffset = offset % 8; + eccbytes = DIV_ROUND_UP(offset + eccbits, 8); + offset /= 8; + eccbytes -= offset; + nand_change_read_column_op(chip, offset, eccbuf, + eccbytes, false); + + /* + * ECC data are not byte aligned and we may have + * in-band data in the first and last byte of + * eccbuf. Set non-eccbits to one so that + * nand_check_erased_ecc_chunk() does not count them + * as bitflips. + */ + if (bitoffset) + eccbuf[0] |= GENMASK(bitoffset - 1, 0); + + bitoffset = (bitoffset + eccbits) % 8; + if (bitoffset) + eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset); + + /* + * The ECC hardware has an uncorrectable ECC status + * code in case we have bitflips in an erased page. As + * nothing was written into this subpage the ECC is + * obviously wrong and we can not trust it. We assume + * at this point that we are reading an erased page and + * try to correct the bitflips in buffer up to + * ecc_strength bitflips. If this is a page with random + * data, we exceed this number of bitflips and have a + * ECC failure. Otherwise we use the corrected buffer. + */ + if (i == 0) { + /* The first block includes metadata */ + flips = nand_check_erased_ecc_chunk( + buf + i * nfc_geo->ecc_chunk_size, + nfc_geo->ecc_chunk_size, + eccbuf, eccbytes, + auxiliary_virt, + nfc_geo->metadata_size, + nfc_geo->ecc_strength); + } else { + flips = nand_check_erased_ecc_chunk( + buf + i * nfc_geo->ecc_chunk_size, + nfc_geo->ecc_chunk_size, + eccbuf, eccbytes, + NULL, 0, + nfc_geo->ecc_strength); + } + + if (flips > 0) { + max_bitflips = max_t(unsigned int, max_bitflips, + flips); + mtd->ecc_stats.corrected += flips; + continue; + } + + mtd->ecc_stats.failed++; + continue; + } + + mtd->ecc_stats.corrected += *status; + max_bitflips = max_t(unsigned int, max_bitflips, *status); + } + + /* handle the block mark swapping */ + block_mark_swapping(this, buf, auxiliary_virt); + + if (oob_required) { + /* + * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() + * for details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the + * block mark to th caller's buffer. Note that, if block mark + * swapping was necessary, it has already been done, so we can + * rely on the first byte of the auxiliary buffer to contain + * the block mark. + */ + memset(chip->oob_poi, ~0, mtd->oobsize); + chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + } + + return max_bitflips; +} + +static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + nand_read_page_op(chip, page, 0, NULL, 0); + + return gpmi_ecc_read_page_data(chip, buf, oob_required, page); +} + +/* Fake a virtual small page for the subpage read */ +static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offs, uint32_t len, uint8_t *buf, int page) +{ + struct gpmi_nand_data *this = nand_get_controller_data(chip); + void __iomem *bch_regs = this->resources.bch_regs; + struct bch_geometry old_geo = this->bch_geometry; + struct bch_geometry *geo = &this->bch_geometry; + int size = chip->ecc.size; /* ECC chunk size */ + int meta, n, page_size; + u32 r1_old, r2_old, r1_new, r2_new; + unsigned int max_bitflips; + int first, last, marker_pos; + int ecc_parity_size; + int col = 0; + int old_swap_block_mark = this->swap_block_mark; + + /* The size of ECC parity */ + ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; + + /* Align it with the chunk size */ + first = offs / size; + last = (offs + len - 1) / size; + + if (this->swap_block_mark) { + /* + * Find the chunk which contains the Block Marker. + * If this chunk is in the range of [first, last], + * we have to read out the whole page. + * Why? since we had swapped the data at the position of Block + * Marker to the metadata which is bound with the chunk 0. + */ + marker_pos = geo->block_mark_byte_offset / size; + if (last >= marker_pos && first <= marker_pos) { + dev_dbg(this->dev, + "page:%d, first:%d, last:%d, marker at:%d\n", + page, first, last, marker_pos); + return gpmi_ecc_read_page(mtd, chip, buf, 0, page); + } + } + + meta = geo->metadata_size; + if (first) { + col = meta + (size + ecc_parity_size) * first; + meta = 0; + buf = buf + first * size; + } + + nand_read_page_op(chip, page, col, NULL, 0); + + /* Save the old environment */ + r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0); + r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1); + + /* change the BCH registers and bch_geometry{} */ + n = last - first + 1; + page_size = meta + (size + ecc_parity_size) * n; + + r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS | + BM_BCH_FLASH0LAYOUT0_META_SIZE); + r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) + | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta); + writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0); + + r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE; + r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size); + writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1); + + geo->ecc_chunk_count = n; + geo->payload_size = n * size; + geo->page_size = page_size; + geo->auxiliary_status_offset = ALIGN(meta, 4); + + dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n", + page, offs, len, col, first, n, page_size); + + /* Read the subpage now */ + this->swap_block_mark = false; + max_bitflips = gpmi_ecc_read_page_data(chip, buf, 0, page); + + /* Restore */ + writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0); + writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1); + this->bch_geometry = old_geo; + this->swap_block_mark = old_swap_block_mark; + + return max_bitflips; +} + +static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *nfc_geo = &this->bch_geometry; + const void *payload_virt; + dma_addr_t payload_phys; + const void *auxiliary_virt; + dma_addr_t auxiliary_phys; + int ret; + + dev_dbg(this->dev, "ecc write page.\n"); + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + if (this->swap_block_mark) { + /* + * If control arrives here, we're doing block mark swapping. + * Since we can't modify the caller's buffers, we must copy them + * into our own. + */ + memcpy(this->payload_virt, buf, mtd->writesize); + payload_virt = this->payload_virt; + payload_phys = this->payload_phys; + + memcpy(this->auxiliary_virt, chip->oob_poi, + nfc_geo->auxiliary_size); + auxiliary_virt = this->auxiliary_virt; + auxiliary_phys = this->auxiliary_phys; + + /* Handle block mark swapping. */ + block_mark_swapping(this, + (void *)payload_virt, (void *)auxiliary_virt); + } else { + /* + * If control arrives here, we're not doing block mark swapping, + * so we can to try and use the caller's buffers. + */ + ret = send_page_prepare(this, + buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + &payload_virt, &payload_phys); + if (ret) { + dev_err(this->dev, "Inadequate payload DMA buffer\n"); + return 0; + } + + ret = send_page_prepare(this, + chip->oob_poi, mtd->oobsize, + this->auxiliary_virt, this->auxiliary_phys, + nfc_geo->auxiliary_size, + &auxiliary_virt, &auxiliary_phys); + if (ret) { + dev_err(this->dev, "Inadequate auxiliary DMA buffer\n"); + goto exit_auxiliary; + } + } + + /* Ask the NFC. */ + ret = gpmi_send_page(this, payload_phys, auxiliary_phys); + if (ret) + dev_err(this->dev, "Error in ECC-based write: %d\n", ret); + + if (!this->swap_block_mark) { + send_page_end(this, chip->oob_poi, mtd->oobsize, + this->auxiliary_virt, this->auxiliary_phys, + nfc_geo->auxiliary_size, + auxiliary_virt, auxiliary_phys); +exit_auxiliary: + send_page_end(this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + } + + if (ret) + return ret; + + return nand_prog_page_end_op(chip); +} + +/* + * There are several places in this driver where we have to handle the OOB and + * block marks. This is the function where things are the most complicated, so + * this is where we try to explain it all. All the other places refer back to + * here. + * + * These are the rules, in order of decreasing importance: + * + * 1) Nothing the caller does can be allowed to imperil the block mark. + * + * 2) In read operations, the first byte of the OOB we return must reflect the + * true state of the block mark, no matter where that block mark appears in + * the physical page. + * + * 3) ECC-based read operations return an OOB full of set bits (since we never + * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads + * return). + * + * 4) "Raw" read operations return a direct view of the physical bytes in the + * page, using the conventional definition of which bytes are data and which + * are OOB. This gives the caller a way to see the actual, physical bytes + * in the page, without the distortions applied by our ECC engine. + * + * + * What we do for this specific read operation depends on two questions: + * + * 1) Are we doing a "raw" read, or an ECC-based read? + * + * 2) Are we using block mark swapping or transcription? + * + * There are four cases, illustrated by the following Karnaugh map: + * + * | Raw | ECC-based | + * -------------+-------------------------+-------------------------+ + * | Read the conventional | | + * | OOB at the end of the | | + * Swapping | page and return it. It | | + * | contains exactly what | | + * | we want. | Read the block mark and | + * -------------+-------------------------+ return it in a buffer | + * | Read the conventional | full of set bits. | + * | OOB at the end of the | | + * | page and also the block | | + * Transcribing | mark in the metadata. | | + * | Copy the block mark | | + * | into the first byte of | | + * | the OOB. | | + * -------------+-------------------------+-------------------------+ + * + * Note that we break rule #4 in the Transcribing/Raw case because we're not + * giving an accurate view of the actual, physical bytes in the page (we're + * overwriting the block mark). That's OK because it's more important to follow + * rule #2. + * + * It turns out that knowing whether we want an "ECC-based" or "raw" read is not + * easy. When reading a page, for example, the NAND Flash MTD code calls our + * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an + * ECC-based or raw view of the page is implicit in which function it calls + * (there is a similar pair of ECC-based/raw functions for writing). + */ +static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct gpmi_nand_data *this = nand_get_controller_data(chip); + + dev_dbg(this->dev, "page number is %d\n", page); + /* clear the OOB buffer */ + memset(chip->oob_poi, ~0, mtd->oobsize); + + /* Read out the conventional OOB. */ + nand_read_page_op(chip, page, mtd->writesize, NULL, 0); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* + * Now, we want to make sure the block mark is correct. In the + * non-transcribing case (!GPMI_IS_MX23()), we already have it. + * Otherwise, we need to explicitly read it. + */ + if (GPMI_IS_MX23(this)) { + /* Read the block mark into the first byte of the OOB buffer. */ + nand_read_page_op(chip, page, 0, NULL, 0); + chip->oob_poi[0] = chip->read_byte(mtd); + } + + return 0; +} + +static int +gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) +{ + struct mtd_oob_region of = { }; + + /* Do we have available oob area? */ + mtd_ooblayout_free(mtd, 0, &of); + if (!of.length) + return -EPERM; + + if (!nand_is_slc(chip)) + return -EPERM; + + return nand_prog_page_op(chip, page, mtd->writesize + of.offset, + chip->oob_poi + of.offset, of.length); +} + +/* + * This function reads a NAND page without involving the ECC engine (no HW + * ECC correction). + * The tricky part in the GPMI/BCH controller is that it stores ECC bits + * inline (interleaved with payload DATA), and do not align data chunk on + * byte boundaries. + * We thus need to take care moving the payload data and ECC bits stored in the + * page into the provided buffers, which is why we're using gpmi_copy_bits. + * + * See set_geometry_by_ecc_info inline comments to have a full description + * of the layout used by the GPMI controller. + */ +static int gpmi_ecc_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *nfc_geo = &this->bch_geometry; + int eccsize = nfc_geo->ecc_chunk_size; + int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; + u8 *tmp_buf = this->raw_buffer; + size_t src_bit_off; + size_t oob_bit_off; + size_t oob_byte_off; + uint8_t *oob = chip->oob_poi; + int step; + + nand_read_page_op(chip, page, 0, tmp_buf, + mtd->writesize + mtd->oobsize); + + /* + * If required, swap the bad block marker and the data stored in the + * metadata section, so that we don't wrongly consider a block as bad. + * + * See the layout description for a detailed explanation on why this + * is needed. + */ + if (this->swap_block_mark) + swap(tmp_buf[0], tmp_buf[mtd->writesize]); + + /* + * Copy the metadata section into the oob buffer (this section is + * guaranteed to be aligned on a byte boundary). + */ + if (oob_required) + memcpy(oob, tmp_buf, nfc_geo->metadata_size); + + oob_bit_off = nfc_geo->metadata_size * 8; + src_bit_off = oob_bit_off; + + /* Extract interleaved payload data and ECC bits */ + for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { + if (buf) + gpmi_copy_bits(buf, step * eccsize * 8, + tmp_buf, src_bit_off, + eccsize * 8); + src_bit_off += eccsize * 8; + + /* Align last ECC block to align a byte boundary */ + if (step == nfc_geo->ecc_chunk_count - 1 && + (oob_bit_off + eccbits) % 8) + eccbits += 8 - ((oob_bit_off + eccbits) % 8); + + if (oob_required) + gpmi_copy_bits(oob, oob_bit_off, + tmp_buf, src_bit_off, + eccbits); + + src_bit_off += eccbits; + oob_bit_off += eccbits; + } + + if (oob_required) { + oob_byte_off = oob_bit_off / 8; + + if (oob_byte_off < mtd->oobsize) + memcpy(oob + oob_byte_off, + tmp_buf + mtd->writesize + oob_byte_off, + mtd->oobsize - oob_byte_off); + } + + return 0; +} + +/* + * This function writes a NAND page without involving the ECC engine (no HW + * ECC generation). + * The tricky part in the GPMI/BCH controller is that it stores ECC bits + * inline (interleaved with payload DATA), and do not align data chunk on + * byte boundaries. + * We thus need to take care moving the OOB area at the right place in the + * final page, which is why we're using gpmi_copy_bits. + * + * See set_geometry_by_ecc_info inline comments to have a full description + * of the layout used by the GPMI controller. + */ +static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required, int page) +{ + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *nfc_geo = &this->bch_geometry; + int eccsize = nfc_geo->ecc_chunk_size; + int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; + u8 *tmp_buf = this->raw_buffer; + uint8_t *oob = chip->oob_poi; + size_t dst_bit_off; + size_t oob_bit_off; + size_t oob_byte_off; + int step; + + /* + * Initialize all bits to 1 in case we don't have a buffer for the + * payload or oob data in order to leave unspecified bits of data + * to their initial state. + */ + if (!buf || !oob_required) + memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize); + + /* + * First copy the metadata section (stored in oob buffer) at the + * beginning of the page, as imposed by the GPMI layout. + */ + memcpy(tmp_buf, oob, nfc_geo->metadata_size); + oob_bit_off = nfc_geo->metadata_size * 8; + dst_bit_off = oob_bit_off; + + /* Interleave payload data and ECC bits */ + for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { + if (buf) + gpmi_copy_bits(tmp_buf, dst_bit_off, + buf, step * eccsize * 8, eccsize * 8); + dst_bit_off += eccsize * 8; + + /* Align last ECC block to align a byte boundary */ + if (step == nfc_geo->ecc_chunk_count - 1 && + (oob_bit_off + eccbits) % 8) + eccbits += 8 - ((oob_bit_off + eccbits) % 8); + + if (oob_required) + gpmi_copy_bits(tmp_buf, dst_bit_off, + oob, oob_bit_off, eccbits); + + dst_bit_off += eccbits; + oob_bit_off += eccbits; + } + + oob_byte_off = oob_bit_off / 8; + + if (oob_required && oob_byte_off < mtd->oobsize) + memcpy(tmp_buf + mtd->writesize + oob_byte_off, + oob + oob_byte_off, mtd->oobsize - oob_byte_off); + + /* + * If required, swap the bad block marker and the first byte of the + * metadata section, so that we don't modify the bad block marker. + * + * See the layout description for a detailed explanation on why this + * is needed. + */ + if (this->swap_block_mark) + swap(tmp_buf[0], tmp_buf[mtd->writesize]); + + return nand_prog_page_op(chip, page, 0, tmp_buf, + mtd->writesize + mtd->oobsize); +} + +static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return gpmi_ecc_read_page_raw(mtd, chip, NULL, 1, page); +} + +static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1, page); +} + +static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + int ret = 0; + uint8_t *block_mark; + int column, page, chipnr; + + chipnr = (int)(ofs >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + column = !GPMI_IS_MX23(this) ? mtd->writesize : 0; + + /* Write the block mark. */ + block_mark = this->data_buffer_dma; + block_mark[0] = 0; /* bad block marker */ + + /* Shift to get page */ + page = (int)(ofs >> chip->page_shift); + + ret = nand_prog_page_op(chip, page, column, block_mark, 1); + + chip->select_chip(mtd, -1); + + return ret; +} + +static int nand_boot_set_geometry(struct gpmi_nand_data *this) +{ + struct boot_rom_geometry *geometry = &this->rom_geometry; + + /* + * Set the boot block stride size. + * + * In principle, we should be reading this from the OTP bits, since + * that's where the ROM is going to get it. In fact, we don't have any + * way to read the OTP bits, so we go with the default and hope for the + * best. + */ + geometry->stride_size_in_pages = 64; + + /* + * Set the search area stride exponent. + * + * In principle, we should be reading this from the OTP bits, since + * that's where the ROM is going to get it. In fact, we don't have any + * way to read the OTP bits, so we go with the default and hope for the + * best. + */ + geometry->search_area_stride_exponent = 2; + return 0; +} + +static const char *fingerprint = "STMP"; +static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) +{ + struct boot_rom_geometry *rom_geo = &this->rom_geometry; + struct device *dev = this->dev; + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int search_area_size_in_strides; + unsigned int stride; + unsigned int page; + uint8_t *buffer = chip->data_buf; + int saved_chip_number; + int found_an_ncb_fingerprint = false; + + /* Compute the number of strides in a search area. */ + search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; + + saved_chip_number = this->current_chip; + chip->select_chip(mtd, 0); + + /* + * Loop through the first search area, looking for the NCB fingerprint. + */ + dev_dbg(dev, "Scanning for an NCB fingerprint...\n"); + + for (stride = 0; stride < search_area_size_in_strides; stride++) { + /* Compute the page addresses. */ + page = stride * rom_geo->stride_size_in_pages; + + dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page); + + /* + * Read the NCB fingerprint. The fingerprint is four bytes long + * and starts in the 12th byte of the page. + */ + nand_read_page_op(chip, page, 12, NULL, 0); + chip->read_buf(mtd, buffer, strlen(fingerprint)); + + /* Look for the fingerprint. */ + if (!memcmp(buffer, fingerprint, strlen(fingerprint))) { + found_an_ncb_fingerprint = true; + break; + } + + } + + chip->select_chip(mtd, saved_chip_number); + + if (found_an_ncb_fingerprint) + dev_dbg(dev, "\tFound a fingerprint\n"); + else + dev_dbg(dev, "\tNo fingerprint found\n"); + return found_an_ncb_fingerprint; +} + +/* Writes a transcription stamp. */ +static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) +{ + struct device *dev = this->dev; + struct boot_rom_geometry *rom_geo = &this->rom_geometry; + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int block_size_in_pages; + unsigned int search_area_size_in_strides; + unsigned int search_area_size_in_pages; + unsigned int search_area_size_in_blocks; + unsigned int block; + unsigned int stride; + unsigned int page; + uint8_t *buffer = chip->data_buf; + int saved_chip_number; + int status; + + /* Compute the search area geometry. */ + block_size_in_pages = mtd->erasesize / mtd->writesize; + search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; + search_area_size_in_pages = search_area_size_in_strides * + rom_geo->stride_size_in_pages; + search_area_size_in_blocks = + (search_area_size_in_pages + (block_size_in_pages - 1)) / + block_size_in_pages; + + dev_dbg(dev, "Search Area Geometry :\n"); + dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks); + dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides); + dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages); + + /* Select chip 0. */ + saved_chip_number = this->current_chip; + chip->select_chip(mtd, 0); + + /* Loop over blocks in the first search area, erasing them. */ + dev_dbg(dev, "Erasing the search area...\n"); + + for (block = 0; block < search_area_size_in_blocks; block++) { + /* Erase this block. */ + dev_dbg(dev, "\tErasing block 0x%x\n", block); + status = nand_erase_op(chip, block); + if (status) + dev_err(dev, "[%s] Erase failed.\n", __func__); + } + + /* Write the NCB fingerprint into the page buffer. */ + memset(buffer, ~0, mtd->writesize); + memcpy(buffer + 12, fingerprint, strlen(fingerprint)); + + /* Loop through the first search area, writing NCB fingerprints. */ + dev_dbg(dev, "Writing NCB fingerprints...\n"); + for (stride = 0; stride < search_area_size_in_strides; stride++) { + /* Compute the page addresses. */ + page = stride * rom_geo->stride_size_in_pages; + + /* Write the first page of the current stride. */ + dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); + + status = chip->ecc.write_page_raw(mtd, chip, buffer, 0, page); + if (status) + dev_err(dev, "[%s] Write failed.\n", __func__); + } + + /* Deselect chip 0. */ + chip->select_chip(mtd, saved_chip_number); + return 0; +} + +static int mx23_boot_init(struct gpmi_nand_data *this) +{ + struct device *dev = this->dev; + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int block_count; + unsigned int block; + int chipnr; + int page; + loff_t byte; + uint8_t block_mark; + int ret = 0; + + /* + * If control arrives here, we can't use block mark swapping, which + * means we're forced to use transcription. First, scan for the + * transcription stamp. If we find it, then we don't have to do + * anything -- the block marks are already transcribed. + */ + if (mx23_check_transcription_stamp(this)) + return 0; + + /* + * If control arrives here, we couldn't find a transcription stamp, so + * so we presume the block marks are in the conventional location. + */ + dev_dbg(dev, "Transcribing bad block marks...\n"); + + /* Compute the number of blocks in the entire medium. */ + block_count = chip->chipsize >> chip->phys_erase_shift; + + /* + * Loop over all the blocks in the medium, transcribing block marks as + * we go. + */ + for (block = 0; block < block_count; block++) { + /* + * Compute the chip, page and byte addresses for this block's + * conventional mark. + */ + chipnr = block >> (chip->chip_shift - chip->phys_erase_shift); + page = block << (chip->phys_erase_shift - chip->page_shift); + byte = block << chip->phys_erase_shift; + + /* Send the command to read the conventional block mark. */ + chip->select_chip(mtd, chipnr); + nand_read_page_op(chip, page, mtd->writesize, NULL, 0); + block_mark = chip->read_byte(mtd); + chip->select_chip(mtd, -1); + + /* + * Check if the block is marked bad. If so, we need to mark it + * again, but this time the result will be a mark in the + * location where we transcribe block marks. + */ + if (block_mark != 0xff) { + dev_dbg(dev, "Transcribing mark in block %u\n", block); + ret = chip->block_markbad(mtd, byte); + if (ret) + dev_err(dev, + "Failed to mark block bad with ret %d\n", + ret); + } + } + + /* Write the stamp that indicates we've transcribed the block marks. */ + mx23_write_transcription_stamp(this); + return 0; +} + +static int nand_boot_init(struct gpmi_nand_data *this) +{ + nand_boot_set_geometry(this); + + /* This is ROM arch-specific initilization before the BBT scanning. */ + if (GPMI_IS_MX23(this)) + return mx23_boot_init(this); + return 0; +} + +static int gpmi_set_geometry(struct gpmi_nand_data *this) +{ + int ret; + + /* Free the temporary DMA memory for reading ID. */ + gpmi_free_dma_buffer(this); + + /* Set up the NFC geometry which is used by BCH. */ + ret = bch_set_geometry(this); + if (ret) { + dev_err(this->dev, "Error setting BCH geometry : %d\n", ret); + return ret; + } + + /* Alloc the new DMA buffers according to the pagesize and oobsize */ + return gpmi_alloc_dma_buffer(this); +} + +static int gpmi_init_last(struct gpmi_nand_data *this) +{ + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct bch_geometry *bch_geo = &this->bch_geometry; + int ret; + + /* Set up the medium geometry */ + ret = gpmi_set_geometry(this); + if (ret) + return ret; + + /* Init the nand_ecc_ctrl{} */ + ecc->read_page = gpmi_ecc_read_page; + ecc->write_page = gpmi_ecc_write_page; + ecc->read_oob = gpmi_ecc_read_oob; + ecc->write_oob = gpmi_ecc_write_oob; + ecc->read_page_raw = gpmi_ecc_read_page_raw; + ecc->write_page_raw = gpmi_ecc_write_page_raw; + ecc->read_oob_raw = gpmi_ecc_read_oob_raw; + ecc->write_oob_raw = gpmi_ecc_write_oob_raw; + ecc->mode = NAND_ECC_HW; + ecc->size = bch_geo->ecc_chunk_size; + ecc->strength = bch_geo->ecc_strength; + mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); + + /* + * We only enable the subpage read when: + * (1) the chip is imx6, and + * (2) the size of the ECC parity is byte aligned. + */ + if (GPMI_IS_MX6(this) && + ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) { + ecc->read_subpage = gpmi_ecc_read_subpage; + chip->options |= NAND_SUBPAGE_READ; + } + + /* + * Can we enable the extra features? such as EDO or Sync mode. + * + * We do not check the return value now. That's means if we fail in + * enable the extra features, we still can run in the normal way. + */ + gpmi_extra_init(this); + + return 0; +} + +static int gpmi_nand_init(struct gpmi_nand_data *this) +{ + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + /* init current chip */ + this->current_chip = -1; + + /* init the MTD data structures */ + mtd->name = "gpmi-nand"; + mtd->dev.parent = this->dev; + + /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ + nand_set_controller_data(chip, this); + nand_set_flash_node(chip, this->pdev->dev.of_node); + chip->select_chip = gpmi_select_chip; + chip->cmd_ctrl = gpmi_cmd_ctrl; + chip->dev_ready = gpmi_dev_ready; + chip->read_byte = gpmi_read_byte; + chip->read_buf = gpmi_read_buf; + chip->write_buf = gpmi_write_buf; + chip->badblock_pattern = &gpmi_bbt_descr; + chip->block_markbad = gpmi_block_markbad; + chip->options |= NAND_NO_SUBPAGE_WRITE; + + /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ + this->swap_block_mark = !GPMI_IS_MX23(this); + + /* + * Allocate a temporary DMA buffer for reading ID in the + * nand_scan_ident(). + */ + this->bch_geometry.payload_size = 1024; + this->bch_geometry.auxiliary_size = 128; + ret = gpmi_alloc_dma_buffer(this); + if (ret) + goto err_out; + + ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL); + if (ret) + goto err_out; + + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + chip->bbt_options |= NAND_BBT_NO_OOB; + + if (of_property_read_bool(this->dev->of_node, + "fsl,no-blockmark-swap")) + this->swap_block_mark = false; + } + dev_dbg(this->dev, "Blockmark swapping %sabled\n", + this->swap_block_mark ? "en" : "dis"); + + ret = gpmi_init_last(this); + if (ret) + goto err_out; + + chip->options |= NAND_SKIP_BBTSCAN; + ret = nand_scan_tail(mtd); + if (ret) + goto err_out; + + ret = nand_boot_init(this); + if (ret) + goto err_nand_cleanup; + ret = chip->scan_bbt(mtd); + if (ret) + goto err_nand_cleanup; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + goto err_nand_cleanup; + return 0; + +err_nand_cleanup: + nand_cleanup(chip); +err_out: + gpmi_free_dma_buffer(this); + return ret; +} + +static const struct of_device_id gpmi_nand_id_table[] = { + { + .compatible = "fsl,imx23-gpmi-nand", + .data = &gpmi_devdata_imx23, + }, { + .compatible = "fsl,imx28-gpmi-nand", + .data = &gpmi_devdata_imx28, + }, { + .compatible = "fsl,imx6q-gpmi-nand", + .data = &gpmi_devdata_imx6q, + }, { + .compatible = "fsl,imx6sx-gpmi-nand", + .data = &gpmi_devdata_imx6sx, + }, { + .compatible = "fsl,imx7d-gpmi-nand", + .data = &gpmi_devdata_imx7d, + }, {} +}; +MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); + +static int gpmi_nand_probe(struct platform_device *pdev) +{ + struct gpmi_nand_data *this; + const struct of_device_id *of_id; + int ret; + + this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); + if (!this) + return -ENOMEM; + + of_id = of_match_device(gpmi_nand_id_table, &pdev->dev); + if (of_id) { + this->devdata = of_id->data; + } else { + dev_err(&pdev->dev, "Failed to find the right device id.\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, this); + this->pdev = pdev; + this->dev = &pdev->dev; + + ret = acquire_resources(this); + if (ret) + goto exit_acquire_resources; + + ret = init_hardware(this); + if (ret) + goto exit_nfc_init; + + ret = gpmi_nand_init(this); + if (ret) + goto exit_nfc_init; + + dev_info(this->dev, "driver registered.\n"); + + return 0; + +exit_nfc_init: + release_resources(this); +exit_acquire_resources: + + return ret; +} + +static int gpmi_nand_remove(struct platform_device *pdev) +{ + struct gpmi_nand_data *this = platform_get_drvdata(pdev); + + nand_release(nand_to_mtd(&this->nand)); + gpmi_free_dma_buffer(this); + release_resources(this); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int gpmi_pm_suspend(struct device *dev) +{ + struct gpmi_nand_data *this = dev_get_drvdata(dev); + + release_dma_channels(this); + return 0; +} + +static int gpmi_pm_resume(struct device *dev) +{ + struct gpmi_nand_data *this = dev_get_drvdata(dev); + int ret; + + ret = acquire_dma_channels(this); + if (ret < 0) + return ret; + + /* re-init the GPMI registers */ + this->flags &= ~GPMI_TIMING_INIT_OK; + ret = gpmi_init(this); + if (ret) { + dev_err(this->dev, "Error setting GPMI : %d\n", ret); + return ret; + } + + /* re-init the BCH registers */ + ret = bch_set_geometry(this); + if (ret) { + dev_err(this->dev, "Error setting BCH : %d\n", ret); + return ret; + } + + /* re-init others */ + gpmi_extra_init(this); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops gpmi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume) +}; + +static struct platform_driver gpmi_nand_driver = { + .driver = { + .name = "gpmi-nand", + .pm = &gpmi_pm_ops, + .of_match_table = gpmi_nand_id_table, + }, + .probe = gpmi_nand_probe, + .remove = gpmi_nand_remove, +}; +module_platform_driver(gpmi_nand_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h new file mode 100644 index 000000000000..06c1f993912c --- /dev/null +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -0,0 +1,315 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H +#define __DRIVERS_MTD_NAND_GPMI_NAND_H + +#include +#include +#include +#include + +#define GPMI_CLK_MAX 5 /* MX6Q needs five clocks */ +struct resources { + void __iomem *gpmi_regs; + void __iomem *bch_regs; + unsigned int dma_low_channel; + unsigned int dma_high_channel; + struct clk *clock[GPMI_CLK_MAX]; +}; + +/** + * struct bch_geometry - BCH geometry description. + * @gf_len: The length of Galois Field. (e.g., 13 or 14) + * @ecc_strength: A number that describes the strength of the ECC + * algorithm. + * @page_size: The size, in bytes, of a physical page, including + * both data and OOB. + * @metadata_size: The size, in bytes, of the metadata. + * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note + * the first chunk in the page includes both data and + * metadata, so it's a bit larger than this value. + * @ecc_chunk_count: The number of ECC chunks in the page, + * @payload_size: The size, in bytes, of the payload buffer. + * @auxiliary_size: The size, in bytes, of the auxiliary buffer. + * @auxiliary_status_offset: The offset into the auxiliary buffer at which + * the ECC status appears. + * @block_mark_byte_offset: The byte offset in the ECC-based page view at + * which the underlying physical block mark appears. + * @block_mark_bit_offset: The bit offset into the ECC-based page view at + * which the underlying physical block mark appears. + */ +struct bch_geometry { + unsigned int gf_len; + unsigned int ecc_strength; + unsigned int page_size; + unsigned int metadata_size; + unsigned int ecc_chunk_size; + unsigned int ecc_chunk_count; + unsigned int payload_size; + unsigned int auxiliary_size; + unsigned int auxiliary_status_offset; + unsigned int block_mark_byte_offset; + unsigned int block_mark_bit_offset; +}; + +/** + * struct boot_rom_geometry - Boot ROM geometry description. + * @stride_size_in_pages: The size of a boot block stride, in pages. + * @search_area_stride_exponent: The logarithm to base 2 of the size of a + * search area in boot block strides. + */ +struct boot_rom_geometry { + unsigned int stride_size_in_pages; + unsigned int search_area_stride_exponent; +}; + +/* DMA operations types */ +enum dma_ops_type { + DMA_FOR_COMMAND = 1, + DMA_FOR_READ_DATA, + DMA_FOR_WRITE_DATA, + DMA_FOR_READ_ECC_PAGE, + DMA_FOR_WRITE_ECC_PAGE +}; + +/** + * struct nand_timing - Fundamental timing attributes for NAND. + * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the + * maximum of tDS and tWP. A negative value + * indicates this characteristic isn't known. + * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the + * maximum of tDH, tWH and tREH. A negative value + * indicates this characteristic isn't known. + * @address_setup_in_ns: The address setup time, in nanoseconds. Usually + * the maximum of tCLS, tCS and tALS. A negative + * value indicates this characteristic isn't known. + * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value + * indicates this characteristic isn't known. + * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A + * negative value indicates this characteristic isn't + * known. + * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A + * negative value indicates this characteristic isn't + * known. + * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A + * negative value indicates this characteristic isn't + * known. + */ +struct nand_timing { + int8_t data_setup_in_ns; + int8_t data_hold_in_ns; + int8_t address_setup_in_ns; + int8_t gpmi_sample_delay_in_ns; + int8_t tREA_in_ns; + int8_t tRLOH_in_ns; + int8_t tRHOH_in_ns; +}; + +enum gpmi_type { + IS_MX23, + IS_MX28, + IS_MX6Q, + IS_MX6SX, + IS_MX7D, +}; + +struct gpmi_devdata { + enum gpmi_type type; + int bch_max_ecc_strength; + int max_chain_delay; /* See the async EDO mode */ + const char * const *clks; + const int clks_count; +}; + +struct gpmi_nand_data { + /* flags */ +#define GPMI_ASYNC_EDO_ENABLED (1 << 0) +#define GPMI_TIMING_INIT_OK (1 << 1) + int flags; + const struct gpmi_devdata *devdata; + + /* System Interface */ + struct device *dev; + struct platform_device *pdev; + + /* Resources */ + struct resources resources; + + /* Flash Hardware */ + struct nand_timing timing; + int timing_mode; + + /* BCH */ + struct bch_geometry bch_geometry; + struct completion bch_done; + + /* NAND Boot issue */ + bool swap_block_mark; + struct boot_rom_geometry rom_geometry; + + /* MTD / NAND */ + struct nand_chip nand; + + /* General-use Variables */ + int current_chip; + unsigned int command_length; + + /* passed from upper layer */ + uint8_t *upper_buf; + int upper_len; + + /* for DMA operations */ + bool direct_dma_map_ok; + + struct scatterlist cmd_sgl; + char *cmd_buffer; + + struct scatterlist data_sgl; + char *data_buffer_dma; + + void *page_buffer_virt; + dma_addr_t page_buffer_phys; + unsigned int page_buffer_size; + + void *payload_virt; + dma_addr_t payload_phys; + + void *auxiliary_virt; + dma_addr_t auxiliary_phys; + + void *raw_buffer; + + /* DMA channels */ +#define DMA_CHANS 8 + struct dma_chan *dma_chans[DMA_CHANS]; + enum dma_ops_type last_dma_type; + enum dma_ops_type dma_type; + struct completion dma_done; + + /* private */ + void *private; +}; + +/** + * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters. + * @data_setup_in_cycles: The data setup time, in cycles. + * @data_hold_in_cycles: The data hold time, in cycles. + * @address_setup_in_cycles: The address setup time, in cycles. + * @device_busy_timeout: The timeout waiting for NAND Ready/Busy, + * this value is the number of cycles multiplied + * by 4096. + * @use_half_periods: Indicates the clock is running slowly, so the + * NFC DLL should use half-periods. + * @sample_delay_factor: The sample delay factor. + * @wrn_dly_sel: The delay on the GPMI write strobe. + */ +struct gpmi_nfc_hardware_timing { + /* for HW_GPMI_TIMING0 */ + uint8_t data_setup_in_cycles; + uint8_t data_hold_in_cycles; + uint8_t address_setup_in_cycles; + + /* for HW_GPMI_TIMING1 */ + uint16_t device_busy_timeout; +#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/ + + /* for HW_GPMI_CTRL1 */ + bool use_half_periods; + uint8_t sample_delay_factor; + uint8_t wrn_dly_sel; +}; + +/** + * struct timing_threshold - Timing threshold + * @max_data_setup_cycles: The maximum number of data setup cycles that + * can be expressed in the hardware. + * @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires + * for data read internal setup. In the Reference + * Manual, see the chapter "High-Speed NAND + * Timing" for more details. + * @max_sample_delay_factor: The maximum sample delay factor that can be + * expressed in the hardware. + * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the + * sample delay DLL hardware can possibly work + * with (the DLL is unusable with longer periods). + * If the full-cycle period is greater than HALF + * this value, the DLL must be configured to use + * half-periods. + * @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the + * DLL can implement. + * @clock_frequency_in_hz: The clock frequency, in Hz, during the current + * I/O transaction. If no I/O transaction is in + * progress, this is the clock frequency during + * the most recent I/O transaction. + */ +struct timing_threshold { + const unsigned int max_chip_count; + const unsigned int max_data_setup_cycles; + const unsigned int internal_data_setup_in_ns; + const unsigned int max_sample_delay_factor; + const unsigned int max_dll_clock_period_in_ns; + const unsigned int max_dll_delay_in_ns; + unsigned long clock_frequency_in_hz; + +}; + +/* Common Services */ +int common_nfc_set_geometry(struct gpmi_nand_data *); +struct dma_chan *get_dma_chan(struct gpmi_nand_data *); +void prepare_data_dma(struct gpmi_nand_data *, + enum dma_data_direction dr); +int start_dma_without_bch_irq(struct gpmi_nand_data *, + struct dma_async_tx_descriptor *); +int start_dma_with_bch_irq(struct gpmi_nand_data *, + struct dma_async_tx_descriptor *); + +/* GPMI-NAND helper function library */ +int gpmi_init(struct gpmi_nand_data *); +int gpmi_extra_init(struct gpmi_nand_data *); +void gpmi_clear_bch(struct gpmi_nand_data *); +void gpmi_dump_info(struct gpmi_nand_data *); +int bch_set_geometry(struct gpmi_nand_data *); +int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip); +int gpmi_send_command(struct gpmi_nand_data *); +void gpmi_begin(struct gpmi_nand_data *); +void gpmi_end(struct gpmi_nand_data *); +int gpmi_read_data(struct gpmi_nand_data *); +int gpmi_send_data(struct gpmi_nand_data *); +int gpmi_send_page(struct gpmi_nand_data *, + dma_addr_t payload, dma_addr_t auxiliary); +int gpmi_read_page(struct gpmi_nand_data *, + dma_addr_t payload, dma_addr_t auxiliary); + +void gpmi_copy_bits(u8 *dst, size_t dst_bit_off, + const u8 *src, size_t src_bit_off, + size_t nbits); + +/* BCH : Status Block Completion Codes */ +#define STATUS_GOOD 0x00 +#define STATUS_ERASED 0xff +#define STATUS_UNCORRECTABLE 0xfe + +/* Use the devdata to distinguish different Archs. */ +#define GPMI_IS_MX23(x) ((x)->devdata->type == IS_MX23) +#define GPMI_IS_MX28(x) ((x)->devdata->type == IS_MX28) +#define GPMI_IS_MX6Q(x) ((x)->devdata->type == IS_MX6Q) +#define GPMI_IS_MX6SX(x) ((x)->devdata->type == IS_MX6SX) +#define GPMI_IS_MX7D(x) ((x)->devdata->type == IS_MX7D) + +#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x) || \ + GPMI_IS_MX7D(x)) +#endif diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h new file mode 100644 index 000000000000..82114cdc8330 --- /dev/null +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h @@ -0,0 +1,187 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __GPMI_NAND_GPMI_REGS_H +#define __GPMI_NAND_GPMI_REGS_H + +#define HW_GPMI_CTRL0 0x00000000 +#define HW_GPMI_CTRL0_SET 0x00000004 +#define HW_GPMI_CTRL0_CLR 0x00000008 +#define HW_GPMI_CTRL0_TOG 0x0000000c + +#define BP_GPMI_CTRL0_COMMAND_MODE 24 +#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE) +#define BF_GPMI_CTRL0_COMMAND_MODE(v) \ + (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE) +#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2 +#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3 + +#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23) +#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0 +#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1 + +/* + * Difference in LOCK_CS between imx23 and imx28 : + * This bit may impact the _POWER_ consumption. So some chips + * do not set it. + */ +#define MX23_BP_GPMI_CTRL0_LOCK_CS 22 +#define MX28_BP_GPMI_CTRL0_LOCK_CS 27 +#define LOCK_CS_ENABLE 0x1 +#define BF_GPMI_CTRL0_LOCK_CS(v, x) 0x0 + +/* Difference in CS between imx23 and imx28 */ +#define BP_GPMI_CTRL0_CS 20 +#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS) +#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS) +#define BF_GPMI_CTRL0_CS(v, x) (((v) << BP_GPMI_CTRL0_CS) & \ + (GPMI_IS_MX23((x)) \ + ? MX23_BM_GPMI_CTRL0_CS \ + : MX28_BM_GPMI_CTRL0_CS)) + +#define BP_GPMI_CTRL0_ADDRESS 17 +#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS) +#define BF_GPMI_CTRL0_ADDRESS(v) \ + (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS) +#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0 +#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1 +#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2 + +#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0 +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1 + +#define BP_GPMI_CTRL0_XFER_COUNT 0 +#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT) +#define BF_GPMI_CTRL0_XFER_COUNT(v) \ + (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT) + +#define HW_GPMI_COMPARE 0x00000010 + +#define HW_GPMI_ECCCTRL 0x00000020 +#define HW_GPMI_ECCCTRL_SET 0x00000024 +#define HW_GPMI_ECCCTRL_CLR 0x00000028 +#define HW_GPMI_ECCCTRL_TOG 0x0000002c + +#define BP_GPMI_ECCCTRL_ECC_CMD 13 +#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD) +#define BF_GPMI_ECCCTRL_ECC_CMD(v) \ + (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD) +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0 +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1 + +#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12) +#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1 +#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0 + +#define BP_GPMI_ECCCTRL_BUFFER_MASK 0 +#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK) +#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \ + (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK) +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF + +#define HW_GPMI_ECCCOUNT 0x00000030 +#define HW_GPMI_PAYLOAD 0x00000040 +#define HW_GPMI_AUXILIARY 0x00000050 +#define HW_GPMI_CTRL1 0x00000060 +#define HW_GPMI_CTRL1_SET 0x00000064 +#define HW_GPMI_CTRL1_CLR 0x00000068 +#define HW_GPMI_CTRL1_TOG 0x0000006c + +#define BP_GPMI_CTRL1_DECOUPLE_CS 24 +#define BM_GPMI_CTRL1_DECOUPLE_CS (1 << BP_GPMI_CTRL1_DECOUPLE_CS) + +#define BP_GPMI_CTRL1_WRN_DLY_SEL 22 +#define BM_GPMI_CTRL1_WRN_DLY_SEL (0x3 << BP_GPMI_CTRL1_WRN_DLY_SEL) +#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \ + (((v) << BP_GPMI_CTRL1_WRN_DLY_SEL) & BM_GPMI_CTRL1_WRN_DLY_SEL) +#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0 +#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1 +#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2 +#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3 + +#define BM_GPMI_CTRL1_BCH_MODE (1 << 18) + +#define BP_GPMI_CTRL1_DLL_ENABLE 17 +#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE) + +#define BP_GPMI_CTRL1_HALF_PERIOD 16 +#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD) + +#define BP_GPMI_CTRL1_RDN_DELAY 12 +#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY) +#define BF_GPMI_CTRL1_RDN_DELAY(v) \ + (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY) + +#define BM_GPMI_CTRL1_DEV_RESET (1 << 3) +#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 +#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 + +#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 + +#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1) +#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 +#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 + +#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) + +#define HW_GPMI_TIMING0 0x00000070 + +#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 +#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << BP_GPMI_TIMING0_ADDRESS_SETUP) +#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \ + (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP) + +#define BP_GPMI_TIMING0_DATA_HOLD 8 +#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD) +#define BF_GPMI_TIMING0_DATA_HOLD(v) \ + (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD) + +#define BP_GPMI_TIMING0_DATA_SETUP 0 +#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP) +#define BF_GPMI_TIMING0_DATA_SETUP(v) \ + (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP) + +#define HW_GPMI_TIMING1 0x00000080 +#define BP_GPMI_TIMING1_BUSY_TIMEOUT 16 +#define BM_GPMI_TIMING1_BUSY_TIMEOUT (0xffff << BP_GPMI_TIMING1_BUSY_TIMEOUT) +#define BF_GPMI_TIMING1_BUSY_TIMEOUT(v) \ + (((v) << BP_GPMI_TIMING1_BUSY_TIMEOUT) & BM_GPMI_TIMING1_BUSY_TIMEOUT) + +#define HW_GPMI_TIMING2 0x00000090 +#define HW_GPMI_DATA 0x000000a0 + +/* MX28 uses this to detect READY. */ +#define HW_GPMI_STAT 0x000000b0 +#define MX28_BP_GPMI_STAT_READY_BUSY 24 +#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << MX28_BP_GPMI_STAT_READY_BUSY) +#define MX28_BF_GPMI_STAT_READY_BUSY(v) \ + (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY) + +/* MX23 uses this to detect READY. */ +#define HW_GPMI_DEBUG 0x000000c0 +#define MX23_BP_GPMI_DEBUG_READY0 28 +#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0) +#endif diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c new file mode 100644 index 000000000000..cb862793ab6d --- /dev/null +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -0,0 +1,896 @@ +/* + * Hisilicon NAND Flash controller driver + * + * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd. + * http://www.hisilicon.com + * + * Author: Zhou Wang + * The initial developer of the original code is Zhiyong Cai + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HINFC504_MAX_CHIP (4) +#define HINFC504_W_LATCH (5) +#define HINFC504_R_LATCH (7) +#define HINFC504_RW_LATCH (3) + +#define HINFC504_NFC_TIMEOUT (2 * HZ) +#define HINFC504_NFC_PM_TIMEOUT (1 * HZ) +#define HINFC504_NFC_DMA_TIMEOUT (5 * HZ) +#define HINFC504_CHIP_DELAY (25) + +#define HINFC504_REG_BASE_ADDRESS_LEN (0x100) +#define HINFC504_BUFFER_BASE_ADDRESS_LEN (2048 + 128) + +#define HINFC504_ADDR_CYCLE_MASK 0x4 + +#define HINFC504_CON 0x00 +#define HINFC504_CON_OP_MODE_NORMAL BIT(0) +#define HINFC504_CON_PAGEISZE_SHIFT (1) +#define HINFC504_CON_PAGESIZE_MASK (0x07) +#define HINFC504_CON_BUS_WIDTH BIT(4) +#define HINFC504_CON_READY_BUSY_SEL BIT(8) +#define HINFC504_CON_ECCTYPE_SHIFT (9) +#define HINFC504_CON_ECCTYPE_MASK (0x07) + +#define HINFC504_PWIDTH 0x04 +#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \ + ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8)) + +#define HINFC504_CMD 0x0C +#define HINFC504_ADDRL 0x10 +#define HINFC504_ADDRH 0x14 +#define HINFC504_DATA_NUM 0x18 + +#define HINFC504_OP 0x1C +#define HINFC504_OP_READ_DATA_EN BIT(1) +#define HINFC504_OP_WAIT_READY_EN BIT(2) +#define HINFC504_OP_CMD2_EN BIT(3) +#define HINFC504_OP_WRITE_DATA_EN BIT(4) +#define HINFC504_OP_ADDR_EN BIT(5) +#define HINFC504_OP_CMD1_EN BIT(6) +#define HINFC504_OP_NF_CS_SHIFT (7) +#define HINFC504_OP_NF_CS_MASK (3) +#define HINFC504_OP_ADDR_CYCLE_SHIFT (9) +#define HINFC504_OP_ADDR_CYCLE_MASK (7) + +#define HINFC504_STATUS 0x20 +#define HINFC504_READY BIT(0) + +#define HINFC504_INTEN 0x24 +#define HINFC504_INTEN_DMA BIT(9) +#define HINFC504_INTEN_UE BIT(6) +#define HINFC504_INTEN_CE BIT(5) + +#define HINFC504_INTS 0x28 +#define HINFC504_INTS_DMA BIT(9) +#define HINFC504_INTS_UE BIT(6) +#define HINFC504_INTS_CE BIT(5) + +#define HINFC504_INTCLR 0x2C +#define HINFC504_INTCLR_DMA BIT(9) +#define HINFC504_INTCLR_UE BIT(6) +#define HINFC504_INTCLR_CE BIT(5) + +#define HINFC504_ECC_STATUS 0x5C +#define HINFC504_ECC_16_BIT_SHIFT 12 + +#define HINFC504_DMA_CTRL 0x60 +#define HINFC504_DMA_CTRL_DMA_START BIT(0) +#define HINFC504_DMA_CTRL_WE BIT(1) +#define HINFC504_DMA_CTRL_DATA_AREA_EN BIT(2) +#define HINFC504_DMA_CTRL_OOB_AREA_EN BIT(3) +#define HINFC504_DMA_CTRL_BURST4_EN BIT(4) +#define HINFC504_DMA_CTRL_BURST8_EN BIT(5) +#define HINFC504_DMA_CTRL_BURST16_EN BIT(6) +#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT (7) +#define HINFC504_DMA_CTRL_ADDR_NUM_MASK (1) +#define HINFC504_DMA_CTRL_CS_SHIFT (8) +#define HINFC504_DMA_CTRL_CS_MASK (0x03) + +#define HINFC504_DMA_ADDR_DATA 0x64 +#define HINFC504_DMA_ADDR_OOB 0x68 + +#define HINFC504_DMA_LEN 0x6C +#define HINFC504_DMA_LEN_OOB_SHIFT (16) +#define HINFC504_DMA_LEN_OOB_MASK (0xFFF) + +#define HINFC504_DMA_PARA 0x70 +#define HINFC504_DMA_PARA_DATA_RW_EN BIT(0) +#define HINFC504_DMA_PARA_OOB_RW_EN BIT(1) +#define HINFC504_DMA_PARA_DATA_EDC_EN BIT(2) +#define HINFC504_DMA_PARA_OOB_EDC_EN BIT(3) +#define HINFC504_DMA_PARA_DATA_ECC_EN BIT(4) +#define HINFC504_DMA_PARA_OOB_ECC_EN BIT(5) + +#define HINFC_VERSION 0x74 +#define HINFC504_LOG_READ_ADDR 0x7C +#define HINFC504_LOG_READ_LEN 0x80 + +#define HINFC504_NANDINFO_LEN 0x10 + +struct hinfc_host { + struct nand_chip chip; + struct device *dev; + void __iomem *iobase; + void __iomem *mmio; + struct completion cmd_complete; + unsigned int offset; + unsigned int command; + int chipselect; + unsigned int addr_cycle; + u32 addr_value[2]; + u32 cache_addr_value[2]; + char *buffer; + dma_addr_t dma_buffer; + dma_addr_t dma_oob; + int version; + unsigned int irq_status; /* interrupt status */ +}; + +static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg) +{ + return readl(host->iobase + reg); +} + +static inline void hinfc_write(struct hinfc_host *host, unsigned int value, + unsigned int reg) +{ + writel(value, host->iobase + reg); +} + +static void wait_controller_finished(struct hinfc_host *host) +{ + unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT; + int val; + + while (time_before(jiffies, timeout)) { + val = hinfc_read(host, HINFC504_STATUS); + if (host->command == NAND_CMD_ERASE2) { + /* nfc is ready */ + while (!(val & HINFC504_READY)) { + usleep_range(500, 1000); + val = hinfc_read(host, HINFC504_STATUS); + } + return; + } + + if (val & HINFC504_READY) + return; + } + + /* wait cmd timeout */ + dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n"); +} + +static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev) +{ + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned long val; + int ret; + + hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA); + hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB); + + if (chip->ecc.mode == NAND_ECC_NONE) { + hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK) + << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN); + + hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN + | HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA); + } else { + if (host->command == NAND_CMD_READOOB) + hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN + | HINFC504_DMA_PARA_OOB_EDC_EN + | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); + else + hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN + | HINFC504_DMA_PARA_OOB_RW_EN + | HINFC504_DMA_PARA_DATA_EDC_EN + | HINFC504_DMA_PARA_OOB_EDC_EN + | HINFC504_DMA_PARA_DATA_ECC_EN + | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); + + } + + val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN + | HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN + | HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN + | ((host->addr_cycle == 4 ? 1 : 0) + << HINFC504_DMA_CTRL_ADDR_NUM_SHIFT) + | ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK) + << HINFC504_DMA_CTRL_CS_SHIFT)); + + if (todev) + val |= HINFC504_DMA_CTRL_WE; + + init_completion(&host->cmd_complete); + + hinfc_write(host, val, HINFC504_DMA_CTRL); + ret = wait_for_completion_timeout(&host->cmd_complete, + HINFC504_NFC_DMA_TIMEOUT); + + if (!ret) { + dev_err(host->dev, "DMA operation(irq) timeout!\n"); + /* sanity check */ + val = hinfc_read(host, HINFC504_DMA_CTRL); + if (!(val & HINFC504_DMA_CTRL_DMA_START)) + dev_err(host->dev, "DMA is already done but without irq ACK!\n"); + else + dev_err(host->dev, "DMA is really timeout!\n"); + } +} + +static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host) +{ + host->addr_value[0] &= 0xffff0000; + + hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); + hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); + hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN, + HINFC504_CMD); + + hisi_nfc_dma_transfer(host, 1); + + return 0; +} + +static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host) +{ + struct mtd_info *mtd = nand_to_mtd(&host->chip); + + if ((host->addr_value[0] == host->cache_addr_value[0]) && + (host->addr_value[1] == host->cache_addr_value[1])) + return 0; + + host->addr_value[0] &= 0xffff0000; + + hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); + hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); + hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0, + HINFC504_CMD); + + hinfc_write(host, 0, HINFC504_LOG_READ_ADDR); + hinfc_write(host, mtd->writesize + mtd->oobsize, + HINFC504_LOG_READ_LEN); + + hisi_nfc_dma_transfer(host, 0); + + host->cache_addr_value[0] = host->addr_value[0]; + host->cache_addr_value[1] = host->addr_value[1]; + + return 0; +} + +static int hisi_nfc_send_cmd_erase(struct hinfc_host *host) +{ + hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); + hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1, + HINFC504_CMD); + + hinfc_write(host, HINFC504_OP_WAIT_READY_EN + | HINFC504_OP_CMD2_EN + | HINFC504_OP_CMD1_EN + | HINFC504_OP_ADDR_EN + | ((host->chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT) + | ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK) + << HINFC504_OP_ADDR_CYCLE_SHIFT), + HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static int hisi_nfc_send_cmd_readid(struct hinfc_host *host) +{ + hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); + hinfc_write(host, NAND_CMD_READID, HINFC504_CMD); + hinfc_write(host, 0, HINFC504_ADDRL); + + hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN + | HINFC504_OP_READ_DATA_EN + | ((host->chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT) + | 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static int hisi_nfc_send_cmd_status(struct hinfc_host *host) +{ + hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); + hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD); + hinfc_write(host, HINFC504_OP_CMD1_EN + | HINFC504_OP_READ_DATA_EN + | ((host->chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT), + HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect) +{ + hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD); + + hinfc_write(host, HINFC504_OP_CMD1_EN + | ((chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT) + | HINFC504_OP_WAIT_READY_EN, + HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct hinfc_host *host = nand_get_controller_data(chip); + + if (chipselect < 0) + return; + + host->chipselect = chipselect; +} + +static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct hinfc_host *host = nand_get_controller_data(chip); + + if (host->command == NAND_CMD_STATUS) + return *(uint8_t *)(host->mmio); + + host->offset++; + + if (host->command == NAND_CMD_READID) + return *(uint8_t *)(host->mmio + host->offset - 1); + + return *(uint8_t *)(host->buffer + host->offset - 1); +} + +static u16 hisi_nfc_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct hinfc_host *host = nand_get_controller_data(chip); + + host->offset += 2; + return *(u16 *)(host->buffer + host->offset - 2); +} + +static void +hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct hinfc_host *host = nand_get_controller_data(chip); + + memcpy(host->buffer + host->offset, buf, len); + host->offset += len; +} + +static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct hinfc_host *host = nand_get_controller_data(chip); + + memcpy(buf, host->buffer + host->offset, len); + host->offset += len; +} + +static void set_addr(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct hinfc_host *host = nand_get_controller_data(chip); + unsigned int command = host->command; + + host->addr_cycle = 0; + host->addr_value[0] = 0; + host->addr_value[1] = 0; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + + host->addr_value[0] = column & 0xffff; + host->addr_cycle = 2; + } + if (page_addr != -1) { + host->addr_value[0] |= (page_addr & 0xffff) + << (host->addr_cycle * 8); + host->addr_cycle += 2; + if (chip->options & NAND_ROW_ADDR_3) { + host->addr_cycle += 1; + if (host->command == NAND_CMD_ERASE1) + host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16; + else + host->addr_value[1] |= ((page_addr >> 16) & 0xff); + } + } +} + +static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column, + int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct hinfc_host *host = nand_get_controller_data(chip); + int is_cache_invalid = 1; + unsigned int flag = 0; + + host->command = command; + + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + if (command == NAND_CMD_READ0) + host->offset = column; + else + host->offset = column + mtd->writesize; + + is_cache_invalid = 0; + set_addr(mtd, column, page_addr); + hisi_nfc_send_cmd_readstart(host); + break; + + case NAND_CMD_SEQIN: + host->offset = column; + set_addr(mtd, column, page_addr); + break; + + case NAND_CMD_ERASE1: + set_addr(mtd, column, page_addr); + break; + + case NAND_CMD_PAGEPROG: + hisi_nfc_send_cmd_pageprog(host); + break; + + case NAND_CMD_ERASE2: + hisi_nfc_send_cmd_erase(host); + break; + + case NAND_CMD_READID: + host->offset = column; + memset(host->mmio, 0, 0x10); + hisi_nfc_send_cmd_readid(host); + break; + + case NAND_CMD_STATUS: + flag = hinfc_read(host, HINFC504_CON); + if (chip->ecc.mode == NAND_ECC_HW) + hinfc_write(host, + flag & ~(HINFC504_CON_ECCTYPE_MASK << + HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON); + + host->offset = 0; + memset(host->mmio, 0, 0x10); + hisi_nfc_send_cmd_status(host); + hinfc_write(host, flag, HINFC504_CON); + break; + + case NAND_CMD_RESET: + hisi_nfc_send_cmd_reset(host, host->chipselect); + break; + + default: + dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n", + command, column, page_addr); + } + + if (is_cache_invalid) { + host->cache_addr_value[0] = ~0; + host->cache_addr_value[1] = ~0; + } +} + +static irqreturn_t hinfc_irq_handle(int irq, void *devid) +{ + struct hinfc_host *host = devid; + unsigned int flag; + + flag = hinfc_read(host, HINFC504_INTS); + /* store interrupts state */ + host->irq_status |= flag; + + if (flag & HINFC504_INTS_DMA) { + hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR); + complete(&host->cmd_complete); + } else if (flag & HINFC504_INTS_CE) { + hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR); + } else if (flag & HINFC504_INTS_UE) { + hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR); + } + + return IRQ_HANDLED; +} + +static int hisi_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + struct hinfc_host *host = nand_get_controller_data(chip); + int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc; + int stat_1, stat_2; + + nand_read_page_op(chip, page, 0, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* errors which can not be corrected by ECC */ + if (host->irq_status & HINFC504_INTS_UE) { + mtd->ecc_stats.failed++; + } else if (host->irq_status & HINFC504_INTS_CE) { + /* TODO: need add other ECC modes! */ + switch (chip->ecc.strength) { + case 16: + status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >> + HINFC504_ECC_16_BIT_SHIFT & 0x0fff; + stat_2 = status_ecc & 0x3f; + stat_1 = status_ecc >> 6 & 0x3f; + stat = stat_1 + stat_2; + stat_max = max_t(int, stat_1, stat_2); + } + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(int, max_bitflips, stat_max); + } + host->irq_status = 0; + + return max_bitflips; +} + +static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct hinfc_host *host = nand_get_controller_data(chip); + + nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); + + if (host->irq_status & HINFC504_INTS_UE) { + host->irq_status = 0; + return -EBADMSG; + } + + host->irq_status = 0; + return 0; +} + +static int hisi_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) +{ + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +static void hisi_nfc_host_init(struct hinfc_host *host) +{ + struct nand_chip *chip = &host->chip; + unsigned int flag = 0; + + host->version = hinfc_read(host, HINFC_VERSION); + host->addr_cycle = 0; + host->addr_value[0] = 0; + host->addr_value[1] = 0; + host->cache_addr_value[0] = ~0; + host->cache_addr_value[1] = ~0; + host->chipselect = 0; + + /* default page size: 2K, ecc_none. need modify */ + flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL + | ((0x001 & HINFC504_CON_PAGESIZE_MASK) + << HINFC504_CON_PAGEISZE_SHIFT) + | ((0x0 & HINFC504_CON_ECCTYPE_MASK) + << HINFC504_CON_ECCTYPE_SHIFT) + | ((chip->options & NAND_BUSWIDTH_16) ? + HINFC504_CON_BUS_WIDTH : 0); + hinfc_write(host, flag, HINFC504_CON); + + memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN); + + hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, + HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); + + /* enable DMA irq */ + hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN); +} + +static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + /* FIXME: add ECC bytes position */ + return -ENOTSUPP; +} + +static int hisi_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 2; + oobregion->length = 6; + + return 0; +} + +static const struct mtd_ooblayout_ops hisi_ooblayout_ops = { + .ecc = hisi_ooblayout_ecc, + .free = hisi_ooblayout_free, +}; + +static int hisi_nfc_ecc_probe(struct hinfc_host *host) +{ + unsigned int flag; + int size, strength, ecc_bits; + struct device *dev = host->dev; + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + + size = chip->ecc.size; + strength = chip->ecc.strength; + if (size != 1024) { + dev_err(dev, "error ecc size: %d\n", size); + return -EINVAL; + } + + if ((size == 1024) && ((strength != 8) && (strength != 16) && + (strength != 24) && (strength != 40))) { + dev_err(dev, "ecc size and strength do not match\n"); + return -EINVAL; + } + + chip->ecc.size = size; + chip->ecc.strength = strength; + + chip->ecc.read_page = hisi_nand_read_page_hwecc; + chip->ecc.read_oob = hisi_nand_read_oob; + chip->ecc.write_page = hisi_nand_write_page_hwecc; + + switch (chip->ecc.strength) { + case 16: + ecc_bits = 6; + if (mtd->writesize == 2048) + mtd_set_ooblayout(mtd, &hisi_ooblayout_ops); + + /* TODO: add more page size support */ + break; + + /* TODO: add more ecc strength support */ + default: + dev_err(dev, "not support strength: %d\n", chip->ecc.strength); + return -EINVAL; + } + + flag = hinfc_read(host, HINFC504_CON); + /* add ecc type configure */ + flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK) + << HINFC504_CON_ECCTYPE_SHIFT); + hinfc_write(host, flag, HINFC504_CON); + + /* enable ecc irq */ + flag = hinfc_read(host, HINFC504_INTEN) & 0xfff; + hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE, + HINFC504_INTEN); + + return 0; +} + +static int hisi_nfc_probe(struct platform_device *pdev) +{ + int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP; + struct device *dev = &pdev->dev; + struct hinfc_host *host; + struct nand_chip *chip; + struct mtd_info *mtd; + struct resource *res; + struct device_node *np = dev->of_node; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + host->dev = dev; + + platform_set_drvdata(pdev, host); + chip = &host->chip; + mtd = nand_to_mtd(chip); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no IRQ resource defined\n"); + ret = -ENXIO; + goto err_res; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->iobase = devm_ioremap_resource(dev, res); + if (IS_ERR(host->iobase)) { + ret = PTR_ERR(host->iobase); + goto err_res; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + host->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(host->mmio)) { + ret = PTR_ERR(host->mmio); + dev_err(dev, "devm_ioremap_resource[1] fail\n"); + goto err_res; + } + + mtd->name = "hisi_nand"; + mtd->dev.parent = &pdev->dev; + + nand_set_controller_data(chip, host); + nand_set_flash_node(chip, np); + chip->cmdfunc = hisi_nfc_cmdfunc; + chip->select_chip = hisi_nfc_select_chip; + chip->read_byte = hisi_nfc_read_byte; + chip->read_word = hisi_nfc_read_word; + chip->write_buf = hisi_nfc_write_buf; + chip->read_buf = hisi_nfc_read_buf; + chip->chip_delay = HINFC504_CHIP_DELAY; + chip->onfi_set_features = nand_onfi_get_set_features_notsupp; + chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + + hisi_nfc_host_init(host); + + ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host); + if (ret) { + dev_err(dev, "failed to request IRQ\n"); + goto err_res; + } + + ret = nand_scan_ident(mtd, max_chips, NULL); + if (ret) + goto err_res; + + host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize, + &host->dma_buffer, GFP_KERNEL); + if (!host->buffer) { + ret = -ENOMEM; + goto err_res; + } + + host->dma_oob = host->dma_buffer + mtd->writesize; + memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize); + + flag = hinfc_read(host, HINFC504_CON); + flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT); + switch (mtd->writesize) { + case 2048: + flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break; + /* + * TODO: add more pagesize support, + * default pagesize has been set in hisi_nfc_host_init + */ + default: + dev_err(dev, "NON-2KB page size nand flash\n"); + ret = -EINVAL; + goto err_res; + } + hinfc_write(host, flag, HINFC504_CON); + + if (chip->ecc.mode == NAND_ECC_HW) + hisi_nfc_ecc_probe(host); + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "nand_scan_tail failed: %d\n", ret); + goto err_res; + } + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(dev, "Err MTD partition=%d\n", ret); + goto err_mtd; + } + + return 0; + +err_mtd: + nand_release(mtd); +err_res: + return ret; +} + +static int hisi_nfc_remove(struct platform_device *pdev) +{ + struct hinfc_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = nand_to_mtd(&host->chip); + + nand_release(mtd); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int hisi_nfc_suspend(struct device *dev) +{ + struct hinfc_host *host = dev_get_drvdata(dev); + unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT; + + while (time_before(jiffies, timeout)) { + if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) && + (hinfc_read(host, HINFC504_DMA_CTRL) & + HINFC504_DMA_CTRL_DMA_START)) { + cond_resched(); + return 0; + } + } + + dev_err(host->dev, "nand controller suspend timeout.\n"); + + return -EAGAIN; +} + +static int hisi_nfc_resume(struct device *dev) +{ + int cs; + struct hinfc_host *host = dev_get_drvdata(dev); + struct nand_chip *chip = &host->chip; + + for (cs = 0; cs < chip->numchips; cs++) + hisi_nfc_send_cmd_reset(host, cs); + hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, + HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume); + +static const struct of_device_id nfc_id_table[] = { + { .compatible = "hisilicon,504-nfc" }, + {} +}; +MODULE_DEVICE_TABLE(of, nfc_id_table); + +static struct platform_driver hisi_nfc_driver = { + .driver = { + .name = "hisi_nand", + .of_match_table = nfc_id_table, + .pm = &hisi_nfc_pm_ops, + }, + .probe = hisi_nfc_probe, + .remove = hisi_nfc_remove, +}; + +module_platform_driver(hisi_nfc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhou Wang"); +MODULE_AUTHOR("Zhiyong Cai"); +MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver"); diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c new file mode 100644 index 000000000000..613b00a9604b --- /dev/null +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen + * JZ4740 SoC NAND controller driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#define JZ_REG_NAND_CTRL 0x50 +#define JZ_REG_NAND_ECC_CTRL 0x100 +#define JZ_REG_NAND_DATA 0x104 +#define JZ_REG_NAND_PAR0 0x108 +#define JZ_REG_NAND_PAR1 0x10C +#define JZ_REG_NAND_PAR2 0x110 +#define JZ_REG_NAND_IRQ_STAT 0x114 +#define JZ_REG_NAND_IRQ_CTRL 0x118 +#define JZ_REG_NAND_ERR(x) (0x11C + ((x) << 2)) + +#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) +#define JZ_NAND_ECC_CTRL_ENCODING BIT(3) +#define JZ_NAND_ECC_CTRL_RS BIT(2) +#define JZ_NAND_ECC_CTRL_RESET BIT(1) +#define JZ_NAND_ECC_CTRL_ENABLE BIT(0) + +#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) +#define JZ_NAND_STATUS_PAD_FINISH BIT(4) +#define JZ_NAND_STATUS_DEC_FINISH BIT(3) +#define JZ_NAND_STATUS_ENC_FINISH BIT(2) +#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) +#define JZ_NAND_STATUS_ERROR BIT(0) + +#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) +#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) +#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa + +#define JZ_NAND_MEM_CMD_OFFSET 0x08000 +#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 + +struct jz_nand { + struct nand_chip chip; + void __iomem *base; + struct resource *mem; + + unsigned char banks[JZ_NAND_NUM_BANKS]; + void __iomem *bank_base[JZ_NAND_NUM_BANKS]; + struct resource *bank_mem[JZ_NAND_NUM_BANKS]; + + int selected_bank; + + struct gpio_desc *busy_gpio; + bool is_reading; +}; + +static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct jz_nand, chip); +} + +static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + uint32_t ctrl; + int banknr; + + ctrl = readl(nand->base + JZ_REG_NAND_CTRL); + ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; + + if (chipnr == -1) { + banknr = -1; + } else { + banknr = nand->banks[chipnr] - 1; + chip->IO_ADDR_R = nand->bank_base[banknr]; + chip->IO_ADDR_W = nand->bank_base[banknr]; + } + writel(ctrl, nand->base + JZ_REG_NAND_CTRL); + + nand->selected_bank = banknr; +} + +static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + uint32_t reg; + void __iomem *bank_base = nand->bank_base[nand->selected_bank]; + + BUG_ON(nand->selected_bank < 0); + + if (ctrl & NAND_CTRL_CHANGE) { + BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); + if (ctrl & NAND_ALE) + bank_base += JZ_NAND_MEM_ADDR_OFFSET; + else if (ctrl & NAND_CLE) + bank_base += JZ_NAND_MEM_CMD_OFFSET; + chip->IO_ADDR_W = bank_base; + + reg = readl(nand->base + JZ_REG_NAND_CTRL); + if (ctrl & NAND_NCE) + reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); + else + reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); + writel(reg, nand->base + JZ_REG_NAND_CTRL); + } + if (dat != NAND_CMD_NONE) + writeb(dat, chip->IO_ADDR_W); +} + +static int jz_nand_dev_ready(struct mtd_info *mtd) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + return gpiod_get_value_cansleep(nand->busy_gpio); +} + +static void jz_nand_hwctl(struct mtd_info *mtd, int mode) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + uint32_t reg; + + writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + + reg |= JZ_NAND_ECC_CTRL_RESET; + reg |= JZ_NAND_ECC_CTRL_ENABLE; + reg |= JZ_NAND_ECC_CTRL_RS; + + switch (mode) { + case NAND_ECC_READ: + reg &= ~JZ_NAND_ECC_CTRL_ENCODING; + nand->is_reading = true; + break; + case NAND_ECC_WRITE: + reg |= JZ_NAND_ECC_CTRL_ENCODING; + nand->is_reading = false; + break; + default: + break; + } + + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); +} + +static int jz_nand_calculate_ecc_rs(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + uint32_t reg, status; + int i; + unsigned int timeout = 1000; + static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4, + 0x8b, 0xff, 0xb7, 0x6f}; + + if (nand->is_reading) + return 0; + + do { + status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); + } while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout); + + if (timeout == 0) + return -1; + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg &= ~JZ_NAND_ECC_CTRL_ENABLE; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + for (i = 0; i < 9; ++i) + ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); + + /* If the written data is completly 0xff, we also want to write 0xff as + * ecc, otherwise we will get in trouble when doing subpage writes. */ + if (memcmp(ecc_code, empty_block_ecc, 9) == 0) + memset(ecc_code, 0xff, 9); + + return 0; +} + +static void jz_nand_correct_data(uint8_t *dat, int index, int mask) +{ + int offset = index & 0x7; + uint16_t data; + + index += (index >> 3); + + data = dat[index]; + data |= dat[index+1] << 8; + + mask ^= (data >> offset) & 0x1ff; + data &= ~(0x1ff << offset); + data |= (mask << offset); + + dat[index] = data & 0xff; + dat[index+1] = (data >> 8) & 0xff; +} + +static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + int i, error_count, index; + uint32_t reg, status, error; + unsigned int timeout = 1000; + + for (i = 0; i < 9; ++i) + writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg |= JZ_NAND_ECC_CTRL_PAR_READY; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + do { + status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); + } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout); + + if (timeout == 0) + return -ETIMEDOUT; + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg &= ~JZ_NAND_ECC_CTRL_ENABLE; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + if (status & JZ_NAND_STATUS_ERROR) { + if (status & JZ_NAND_STATUS_UNCOR_ERROR) + return -EBADMSG; + + error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; + + for (i = 0; i < error_count; ++i) { + error = readl(nand->base + JZ_REG_NAND_ERR(i)); + index = ((error >> 16) & 0x1ff) - 1; + if (index >= 0 && index < 512) + jz_nand_correct_data(dat, index, error & 0x1ff); + } + + return error_count; + } + + return 0; +} + +static int jz_nand_ioremap_resource(struct platform_device *pdev, + const char *name, struct resource **res, void *__iomem *base) +{ + int ret; + + *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!*res) { + dev_err(&pdev->dev, "Failed to get platform %s memory\n", name); + ret = -ENXIO; + goto err; + } + + *res = request_mem_region((*res)->start, resource_size(*res), + pdev->name); + if (!*res) { + dev_err(&pdev->dev, "Failed to request %s memory region\n", name); + ret = -EBUSY; + goto err; + } + + *base = ioremap((*res)->start, resource_size(*res)); + if (!*base) { + dev_err(&pdev->dev, "Failed to ioremap %s memory region\n", name); + ret = -EBUSY; + goto err_release_mem; + } + + return 0; + +err_release_mem: + release_mem_region((*res)->start, resource_size(*res)); +err: + *res = NULL; + *base = NULL; + return ret; +} + +static inline void jz_nand_iounmap_resource(struct resource *res, + void __iomem *base) +{ + iounmap(base); + release_mem_region(res->start, resource_size(res)); +} + +static int jz_nand_detect_bank(struct platform_device *pdev, + struct jz_nand *nand, unsigned char bank, + size_t chipnr, uint8_t *nand_maf_id, + uint8_t *nand_dev_id) +{ + int ret; + char res_name[6]; + uint32_t ctrl; + struct nand_chip *chip = &nand->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + u8 id[2]; + + /* Request I/O resource. */ + sprintf(res_name, "bank%d", bank); + ret = jz_nand_ioremap_resource(pdev, res_name, + &nand->bank_mem[bank - 1], + &nand->bank_base[bank - 1]); + if (ret) + return ret; + + /* Enable chip in bank. */ + ctrl = readl(nand->base + JZ_REG_NAND_CTRL); + ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); + writel(ctrl, nand->base + JZ_REG_NAND_CTRL); + + if (chipnr == 0) { + /* Detect first chip. */ + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + goto notfound_id; + + /* Retrieve the IDs from the first chip. */ + chip->select_chip(mtd, 0); + nand_reset_op(chip); + nand_readid_op(chip, 0, id, sizeof(id)); + *nand_maf_id = id[0]; + *nand_dev_id = id[1]; + } else { + /* Detect additional chip. */ + chip->select_chip(mtd, chipnr); + nand_reset_op(chip); + nand_readid_op(chip, 0, id, sizeof(id)); + if (*nand_maf_id != id[0] || *nand_dev_id != id[1]) { + ret = -ENODEV; + goto notfound_id; + } + + /* Update size of the MTD. */ + chip->numchips++; + mtd->size += chip->chipsize; + } + + dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); + return 0; + +notfound_id: + dev_info(&pdev->dev, "No chip found on bank %i\n", bank); + ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); + writel(ctrl, nand->base + JZ_REG_NAND_CTRL); + jz_nand_iounmap_resource(nand->bank_mem[bank - 1], + nand->bank_base[bank - 1]); + return ret; +} + +static int jz_nand_probe(struct platform_device *pdev) +{ + int ret; + struct jz_nand *nand; + struct nand_chip *chip; + struct mtd_info *mtd; + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + size_t chipnr, bank_idx; + uint8_t nand_maf_id = 0, nand_dev_id = 0; + + nand = kzalloc(sizeof(*nand), GFP_KERNEL); + if (!nand) + return -ENOMEM; + + ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); + if (ret) + goto err_free; + + nand->busy_gpio = devm_gpiod_get_optional(&pdev->dev, "busy", GPIOD_IN); + if (IS_ERR(nand->busy_gpio)) { + ret = PTR_ERR(nand->busy_gpio); + dev_err(&pdev->dev, "Failed to request busy gpio %d\n", + ret); + goto err_iounmap_mmio; + } + + chip = &nand->chip; + mtd = nand_to_mtd(chip); + mtd->dev.parent = &pdev->dev; + mtd->name = "jz4740-nand"; + + chip->ecc.hwctl = jz_nand_hwctl; + chip->ecc.calculate = jz_nand_calculate_ecc_rs; + chip->ecc.correct = jz_nand_correct_ecc_rs; + chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; + chip->ecc.size = 512; + chip->ecc.bytes = 9; + chip->ecc.strength = 4; + chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; + + chip->chip_delay = 50; + chip->cmd_ctrl = jz_nand_cmd_ctrl; + chip->select_chip = jz_nand_select_chip; + + if (nand->busy_gpio) + chip->dev_ready = jz_nand_dev_ready; + + platform_set_drvdata(pdev, nand); + + /* We are going to autodetect NAND chips in the banks specified in the + * platform data. Although nand_scan_ident() can detect multiple chips, + * it requires those chips to be numbered consecuitively, which is not + * always the case for external memory banks. And a fixed chip-to-bank + * mapping is not practical either, since for example Dingoo units + * produced at different times have NAND chips in different banks. + */ + chipnr = 0; + for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { + unsigned char bank; + + /* If there is no platform data, look for NAND in bank 1, + * which is the most likely bank since it is the only one + * that can be booted from. + */ + bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; + if (bank == 0) + break; + if (bank > JZ_NAND_NUM_BANKS) { + dev_warn(&pdev->dev, + "Skipping non-existing bank: %d\n", bank); + continue; + } + /* The detection routine will directly or indirectly call + * jz_nand_select_chip(), so nand->banks has to contain the + * bank we're checking. + */ + nand->banks[chipnr] = bank; + if (jz_nand_detect_bank(pdev, nand, bank, chipnr, + &nand_maf_id, &nand_dev_id) == 0) + chipnr++; + else + nand->banks[chipnr] = 0; + } + if (chipnr == 0) { + dev_err(&pdev->dev, "No NAND chips found\n"); + goto err_iounmap_mmio; + } + + if (pdata && pdata->ident_callback) { + pdata->ident_callback(pdev, mtd, &pdata->partitions, + &pdata->num_partitions); + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(&pdev->dev, "Failed to scan NAND\n"); + goto err_unclaim_banks; + } + + ret = mtd_device_parse_register(mtd, NULL, NULL, + pdata ? pdata->partitions : NULL, + pdata ? pdata->num_partitions : 0); + + if (ret) { + dev_err(&pdev->dev, "Failed to add mtd device\n"); + goto err_nand_release; + } + + dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); + + return 0; + +err_nand_release: + nand_release(mtd); +err_unclaim_banks: + while (chipnr--) { + unsigned char bank = nand->banks[chipnr]; + jz_nand_iounmap_resource(nand->bank_mem[bank - 1], + nand->bank_base[bank - 1]); + } + writel(0, nand->base + JZ_REG_NAND_CTRL); +err_iounmap_mmio: + jz_nand_iounmap_resource(nand->mem, nand->base); +err_free: + kfree(nand); + return ret; +} + +static int jz_nand_remove(struct platform_device *pdev) +{ + struct jz_nand *nand = platform_get_drvdata(pdev); + size_t i; + + nand_release(nand_to_mtd(&nand->chip)); + + /* Deassert and disable all chips */ + writel(0, nand->base + JZ_REG_NAND_CTRL); + + for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { + unsigned char bank = nand->banks[i]; + if (bank != 0) { + jz_nand_iounmap_resource(nand->bank_mem[bank - 1], + nand->bank_base[bank - 1]); + } + } + + jz_nand_iounmap_resource(nand->mem, nand->base); + + kfree(nand); + + return 0; +} + +static struct platform_driver jz_nand_driver = { + .probe = jz_nand_probe, + .remove = jz_nand_remove, + .driver = { + .name = "jz4740-nand", + }, +}; + +module_platform_driver(jz_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("NAND controller driver for JZ4740 SoC"); +MODULE_ALIAS("platform:jz4740-nand"); diff --git a/drivers/mtd/nand/raw/jz4780_bch.c b/drivers/mtd/nand/raw/jz4780_bch.c new file mode 100644 index 000000000000..731c6051d91e --- /dev/null +++ b/drivers/mtd/nand/raw/jz4780_bch.c @@ -0,0 +1,380 @@ +/* + * JZ4780 BCH controller + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jz4780_bch.h" + +#define BCH_BHCR 0x0 +#define BCH_BHCCR 0x8 +#define BCH_BHCNT 0xc +#define BCH_BHDR 0x10 +#define BCH_BHPAR0 0x14 +#define BCH_BHERR0 0x84 +#define BCH_BHINT 0x184 +#define BCH_BHINTES 0x188 +#define BCH_BHINTEC 0x18c +#define BCH_BHINTE 0x190 + +#define BCH_BHCR_BSEL_SHIFT 4 +#define BCH_BHCR_BSEL_MASK (0x7f << BCH_BHCR_BSEL_SHIFT) +#define BCH_BHCR_ENCE BIT(2) +#define BCH_BHCR_INIT BIT(1) +#define BCH_BHCR_BCHE BIT(0) + +#define BCH_BHCNT_PARITYSIZE_SHIFT 16 +#define BCH_BHCNT_PARITYSIZE_MASK (0x7f << BCH_BHCNT_PARITYSIZE_SHIFT) +#define BCH_BHCNT_BLOCKSIZE_SHIFT 0 +#define BCH_BHCNT_BLOCKSIZE_MASK (0x7ff << BCH_BHCNT_BLOCKSIZE_SHIFT) + +#define BCH_BHERR_MASK_SHIFT 16 +#define BCH_BHERR_MASK_MASK (0xffff << BCH_BHERR_MASK_SHIFT) +#define BCH_BHERR_INDEX_SHIFT 0 +#define BCH_BHERR_INDEX_MASK (0x7ff << BCH_BHERR_INDEX_SHIFT) + +#define BCH_BHINT_ERRC_SHIFT 24 +#define BCH_BHINT_ERRC_MASK (0x7f << BCH_BHINT_ERRC_SHIFT) +#define BCH_BHINT_TERRC_SHIFT 16 +#define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT) +#define BCH_BHINT_DECF BIT(3) +#define BCH_BHINT_ENCF BIT(2) +#define BCH_BHINT_UNCOR BIT(1) +#define BCH_BHINT_ERR BIT(0) + +#define BCH_CLK_RATE (200 * 1000 * 1000) + +/* Timeout for BCH calculation/correction. */ +#define BCH_TIMEOUT_US 100000 + +struct jz4780_bch { + struct device *dev; + void __iomem *base; + struct clk *clk; + struct mutex lock; +}; + +static void jz4780_bch_init(struct jz4780_bch *bch, + struct jz4780_bch_params *params, bool encode) +{ + u32 reg; + + /* Clear interrupt status. */ + writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); + + /* Set up BCH count register. */ + reg = params->size << BCH_BHCNT_BLOCKSIZE_SHIFT; + reg |= params->bytes << BCH_BHCNT_PARITYSIZE_SHIFT; + writel(reg, bch->base + BCH_BHCNT); + + /* Initialise and enable BCH. */ + reg = BCH_BHCR_BCHE | BCH_BHCR_INIT; + reg |= params->strength << BCH_BHCR_BSEL_SHIFT; + if (encode) + reg |= BCH_BHCR_ENCE; + writel(reg, bch->base + BCH_BHCR); +} + +static void jz4780_bch_disable(struct jz4780_bch *bch) +{ + writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); + writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR); +} + +static void jz4780_bch_write_data(struct jz4780_bch *bch, const void *buf, + size_t size) +{ + size_t size32 = size / sizeof(u32); + size_t size8 = size % sizeof(u32); + const u32 *src32; + const u8 *src8; + + src32 = (const u32 *)buf; + while (size32--) + writel(*src32++, bch->base + BCH_BHDR); + + src8 = (const u8 *)src32; + while (size8--) + writeb(*src8++, bch->base + BCH_BHDR); +} + +static void jz4780_bch_read_parity(struct jz4780_bch *bch, void *buf, + size_t size) +{ + size_t size32 = size / sizeof(u32); + size_t size8 = size % sizeof(u32); + u32 *dest32; + u8 *dest8; + u32 val, offset = 0; + + dest32 = (u32 *)buf; + while (size32--) { + *dest32++ = readl(bch->base + BCH_BHPAR0 + offset); + offset += sizeof(u32); + } + + dest8 = (u8 *)dest32; + val = readl(bch->base + BCH_BHPAR0 + offset); + switch (size8) { + case 3: + dest8[2] = (val >> 16) & 0xff; + case 2: + dest8[1] = (val >> 8) & 0xff; + case 1: + dest8[0] = val & 0xff; + break; + } +} + +static bool jz4780_bch_wait_complete(struct jz4780_bch *bch, unsigned int irq, + u32 *status) +{ + u32 reg; + int ret; + + /* + * While we could use interrupts here and sleep until the operation + * completes, the controller works fairly quickly (usually a few + * microseconds) and so the overhead of sleeping until we get an + * interrupt quite noticeably decreases performance. + */ + ret = readl_poll_timeout(bch->base + BCH_BHINT, reg, + (reg & irq) == irq, 0, BCH_TIMEOUT_US); + if (ret) + return false; + + if (status) + *status = reg; + + writel(reg, bch->base + BCH_BHINT); + return true; +} + +/** + * jz4780_bch_calculate() - calculate ECC for a data buffer + * @bch: BCH device. + * @params: BCH parameters. + * @buf: input buffer with raw data. + * @ecc_code: output buffer with ECC. + * + * Return: 0 on success, -ETIMEDOUT if timed out while waiting for BCH + * controller. + */ +int jz4780_bch_calculate(struct jz4780_bch *bch, struct jz4780_bch_params *params, + const u8 *buf, u8 *ecc_code) +{ + int ret = 0; + + mutex_lock(&bch->lock); + jz4780_bch_init(bch, params, true); + jz4780_bch_write_data(bch, buf, params->size); + + if (jz4780_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) { + jz4780_bch_read_parity(bch, ecc_code, params->bytes); + } else { + dev_err(bch->dev, "timed out while calculating ECC\n"); + ret = -ETIMEDOUT; + } + + jz4780_bch_disable(bch); + mutex_unlock(&bch->lock); + return ret; +} +EXPORT_SYMBOL(jz4780_bch_calculate); + +/** + * jz4780_bch_correct() - detect and correct bit errors + * @bch: BCH device. + * @params: BCH parameters. + * @buf: raw data read from the chip. + * @ecc_code: ECC read from the chip. + * + * Given the raw data and the ECC read from the NAND device, detects and + * corrects errors in the data. + * + * Return: the number of bit errors corrected, -EBADMSG if there are too many + * errors to correct or -ETIMEDOUT if we timed out waiting for the controller. + */ +int jz4780_bch_correct(struct jz4780_bch *bch, struct jz4780_bch_params *params, + u8 *buf, u8 *ecc_code) +{ + u32 reg, mask, index; + int i, ret, count; + + mutex_lock(&bch->lock); + + jz4780_bch_init(bch, params, false); + jz4780_bch_write_data(bch, buf, params->size); + jz4780_bch_write_data(bch, ecc_code, params->bytes); + + if (!jz4780_bch_wait_complete(bch, BCH_BHINT_DECF, ®)) { + dev_err(bch->dev, "timed out while correcting data\n"); + ret = -ETIMEDOUT; + goto out; + } + + if (reg & BCH_BHINT_UNCOR) { + dev_warn(bch->dev, "uncorrectable ECC error\n"); + ret = -EBADMSG; + goto out; + } + + /* Correct any detected errors. */ + if (reg & BCH_BHINT_ERR) { + count = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT; + ret = (reg & BCH_BHINT_TERRC_MASK) >> BCH_BHINT_TERRC_SHIFT; + + for (i = 0; i < count; i++) { + reg = readl(bch->base + BCH_BHERR0 + (i * 4)); + mask = (reg & BCH_BHERR_MASK_MASK) >> + BCH_BHERR_MASK_SHIFT; + index = (reg & BCH_BHERR_INDEX_MASK) >> + BCH_BHERR_INDEX_SHIFT; + buf[(index * 2) + 0] ^= mask; + buf[(index * 2) + 1] ^= mask >> 8; + } + } else { + ret = 0; + } + +out: + jz4780_bch_disable(bch); + mutex_unlock(&bch->lock); + return ret; +} +EXPORT_SYMBOL(jz4780_bch_correct); + +/** + * jz4780_bch_get() - get the BCH controller device + * @np: BCH device tree node. + * + * Gets the BCH controller device from the specified device tree node. The + * device must be released with jz4780_bch_release() when it is no longer being + * used. + * + * Return: a pointer to jz4780_bch, errors are encoded into the pointer. + * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet. + */ +static struct jz4780_bch *jz4780_bch_get(struct device_node *np) +{ + struct platform_device *pdev; + struct jz4780_bch *bch; + + pdev = of_find_device_by_node(np); + if (!pdev || !platform_get_drvdata(pdev)) + return ERR_PTR(-EPROBE_DEFER); + + get_device(&pdev->dev); + + bch = platform_get_drvdata(pdev); + clk_prepare_enable(bch->clk); + + return bch; +} + +/** + * of_jz4780_bch_get() - get the BCH controller from a DT node + * @of_node: the node that contains a bch-controller property. + * + * Get the bch-controller property from the given device tree + * node and pass it to jz4780_bch_get to do the work. + * + * Return: a pointer to jz4780_bch, errors are encoded into the pointer. + * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet. + */ +struct jz4780_bch *of_jz4780_bch_get(struct device_node *of_node) +{ + struct jz4780_bch *bch = NULL; + struct device_node *np; + + np = of_parse_phandle(of_node, "ingenic,bch-controller", 0); + + if (np) { + bch = jz4780_bch_get(np); + of_node_put(np); + } + return bch; +} +EXPORT_SYMBOL(of_jz4780_bch_get); + +/** + * jz4780_bch_release() - release the BCH controller device + * @bch: BCH device. + */ +void jz4780_bch_release(struct jz4780_bch *bch) +{ + clk_disable_unprepare(bch->clk); + put_device(bch->dev); +} +EXPORT_SYMBOL(jz4780_bch_release); + +static int jz4780_bch_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz4780_bch *bch; + struct resource *res; + + bch = devm_kzalloc(dev, sizeof(*bch), GFP_KERNEL); + if (!bch) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + bch->base = devm_ioremap_resource(dev, res); + if (IS_ERR(bch->base)) + return PTR_ERR(bch->base); + + jz4780_bch_disable(bch); + + bch->clk = devm_clk_get(dev, NULL); + if (IS_ERR(bch->clk)) { + dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(bch->clk)); + return PTR_ERR(bch->clk); + } + + clk_set_rate(bch->clk, BCH_CLK_RATE); + + mutex_init(&bch->lock); + + bch->dev = dev; + platform_set_drvdata(pdev, bch); + + return 0; +} + +static const struct of_device_id jz4780_bch_dt_match[] = { + { .compatible = "ingenic,jz4780-bch" }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4780_bch_dt_match); + +static struct platform_driver jz4780_bch_driver = { + .probe = jz4780_bch_probe, + .driver = { + .name = "jz4780-bch", + .of_match_table = of_match_ptr(jz4780_bch_dt_match), + }, +}; +module_platform_driver(jz4780_bch_driver); + +MODULE_AUTHOR("Alex Smith "); +MODULE_AUTHOR("Harvey Hunt "); +MODULE_DESCRIPTION("Ingenic JZ4780 BCH error correction driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/raw/jz4780_bch.h b/drivers/mtd/nand/raw/jz4780_bch.h new file mode 100644 index 000000000000..bf4718088a3a --- /dev/null +++ b/drivers/mtd/nand/raw/jz4780_bch.h @@ -0,0 +1,43 @@ +/* + * JZ4780 BCH controller + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef __DRIVERS_MTD_NAND_JZ4780_BCH_H__ +#define __DRIVERS_MTD_NAND_JZ4780_BCH_H__ + +#include + +struct device; +struct device_node; +struct jz4780_bch; + +/** + * struct jz4780_bch_params - BCH parameters + * @size: data bytes per ECC step. + * @bytes: ECC bytes per step. + * @strength: number of correctable bits per ECC step. + */ +struct jz4780_bch_params { + int size; + int bytes; + int strength; +}; + +int jz4780_bch_calculate(struct jz4780_bch *bch, + struct jz4780_bch_params *params, + const u8 *buf, u8 *ecc_code); +int jz4780_bch_correct(struct jz4780_bch *bch, + struct jz4780_bch_params *params, u8 *buf, + u8 *ecc_code); + +void jz4780_bch_release(struct jz4780_bch *bch); +struct jz4780_bch *of_jz4780_bch_get(struct device_node *np); + +#endif /* __DRIVERS_MTD_NAND_JZ4780_BCH_H__ */ diff --git a/drivers/mtd/nand/raw/jz4780_nand.c b/drivers/mtd/nand/raw/jz4780_nand.c new file mode 100644 index 000000000000..e69f6ae4c539 --- /dev/null +++ b/drivers/mtd/nand/raw/jz4780_nand.c @@ -0,0 +1,416 @@ +/* + * JZ4780 NAND driver + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "jz4780_bch.h" + +#define DRV_NAME "jz4780-nand" + +#define OFFSET_DATA 0x00000000 +#define OFFSET_CMD 0x00400000 +#define OFFSET_ADDR 0x00800000 + +/* Command delay when there is no R/B pin. */ +#define RB_DELAY_US 100 + +struct jz4780_nand_cs { + unsigned int bank; + void __iomem *base; +}; + +struct jz4780_nand_controller { + struct device *dev; + struct jz4780_bch *bch; + struct nand_hw_control controller; + unsigned int num_banks; + struct list_head chips; + int selected; + struct jz4780_nand_cs cs[]; +}; + +struct jz4780_nand_chip { + struct nand_chip chip; + struct list_head chip_list; + + struct gpio_desc *busy_gpio; + struct gpio_desc *wp_gpio; + unsigned int reading: 1; +}; + +static inline struct jz4780_nand_chip *to_jz4780_nand_chip(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct jz4780_nand_chip, chip); +} + +static inline struct jz4780_nand_controller *to_jz4780_nand_controller(struct nand_hw_control *ctrl) +{ + return container_of(ctrl, struct jz4780_nand_controller, controller); +} + +static void jz4780_nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); + struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); + struct jz4780_nand_cs *cs; + + /* Ensure the currently selected chip is deasserted. */ + if (chipnr == -1 && nfc->selected >= 0) { + cs = &nfc->cs[nfc->selected]; + jz4780_nemc_assert(nfc->dev, cs->bank, false); + } + + nfc->selected = chipnr; +} + +static void jz4780_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); + struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); + struct jz4780_nand_cs *cs; + + if (WARN_ON(nfc->selected < 0)) + return; + + cs = &nfc->cs[nfc->selected]; + + jz4780_nemc_assert(nfc->dev, cs->bank, ctrl & NAND_NCE); + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_ALE) + writeb(cmd, cs->base + OFFSET_ADDR); + else if (ctrl & NAND_CLE) + writeb(cmd, cs->base + OFFSET_CMD); +} + +static int jz4780_nand_dev_ready(struct mtd_info *mtd) +{ + struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); + + return !gpiod_get_value_cansleep(nand->busy_gpio); +} + +static void jz4780_nand_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); + + nand->reading = (mode == NAND_ECC_READ); +} + +static int jz4780_nand_ecc_calculate(struct mtd_info *mtd, const u8 *dat, + u8 *ecc_code) +{ + struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); + struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); + struct jz4780_bch_params params; + + /* + * Don't need to generate the ECC when reading, BCH does it for us as + * part of decoding/correction. + */ + if (nand->reading) + return 0; + + params.size = nand->chip.ecc.size; + params.bytes = nand->chip.ecc.bytes; + params.strength = nand->chip.ecc.strength; + + return jz4780_bch_calculate(nfc->bch, ¶ms, dat, ecc_code); +} + +static int jz4780_nand_ecc_correct(struct mtd_info *mtd, u8 *dat, + u8 *read_ecc, u8 *calc_ecc) +{ + struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); + struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); + struct jz4780_bch_params params; + + params.size = nand->chip.ecc.size; + params.bytes = nand->chip.ecc.bytes; + params.strength = nand->chip.ecc.strength; + + return jz4780_bch_correct(nfc->bch, ¶ms, dat, read_ecc); +} + +static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *dev) +{ + struct nand_chip *chip = &nand->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller); + int eccbytes; + + chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) * + (chip->ecc.strength / 8); + + switch (chip->ecc.mode) { + case NAND_ECC_HW: + if (!nfc->bch) { + dev_err(dev, "HW BCH selected, but BCH controller not found\n"); + return -ENODEV; + } + + chip->ecc.hwctl = jz4780_nand_ecc_hwctl; + chip->ecc.calculate = jz4780_nand_ecc_calculate; + chip->ecc.correct = jz4780_nand_ecc_correct; + /* fall through */ + case NAND_ECC_SOFT: + dev_info(dev, "using %s (strength %d, size %d, bytes %d)\n", + (nfc->bch) ? "hardware BCH" : "software ECC", + chip->ecc.strength, chip->ecc.size, chip->ecc.bytes); + break; + case NAND_ECC_NONE: + dev_info(dev, "not using ECC\n"); + break; + default: + dev_err(dev, "ECC mode %d not supported\n", chip->ecc.mode); + return -EINVAL; + } + + /* The NAND core will generate the ECC layout for SW ECC */ + if (chip->ecc.mode != NAND_ECC_HW) + return 0; + + /* Generate ECC layout. ECC codes are right aligned in the OOB area. */ + eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes; + + if (eccbytes > mtd->oobsize - 2) { + dev_err(dev, + "invalid ECC config: required %d ECC bytes, but only %d are available", + eccbytes, mtd->oobsize - 2); + return -EINVAL; + } + + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + + return 0; +} + +static int jz4780_nand_init_chip(struct platform_device *pdev, + struct jz4780_nand_controller *nfc, + struct device_node *np, + unsigned int chipnr) +{ + struct device *dev = &pdev->dev; + struct jz4780_nand_chip *nand; + struct jz4780_nand_cs *cs; + struct resource *res; + struct nand_chip *chip; + struct mtd_info *mtd; + const __be32 *reg; + int ret = 0; + + cs = &nfc->cs[chipnr]; + + reg = of_get_property(np, "reg", NULL); + if (!reg) + return -EINVAL; + + cs->bank = be32_to_cpu(*reg); + + jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND); + + res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr); + cs->base = devm_ioremap_resource(dev, res); + if (IS_ERR(cs->base)) + return PTR_ERR(cs->base); + + nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL); + if (!nand) + return -ENOMEM; + + nand->busy_gpio = devm_gpiod_get_optional(dev, "rb", GPIOD_IN); + + if (IS_ERR(nand->busy_gpio)) { + ret = PTR_ERR(nand->busy_gpio); + dev_err(dev, "failed to request busy GPIO: %d\n", ret); + return ret; + } else if (nand->busy_gpio) { + nand->chip.dev_ready = jz4780_nand_dev_ready; + } + + nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW); + + if (IS_ERR(nand->wp_gpio)) { + ret = PTR_ERR(nand->wp_gpio); + dev_err(dev, "failed to request WP GPIO: %d\n", ret); + return ret; + } + + chip = &nand->chip; + mtd = nand_to_mtd(chip); + mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev), + cs->bank); + if (!mtd->name) + return -ENOMEM; + mtd->dev.parent = dev; + + chip->IO_ADDR_R = cs->base + OFFSET_DATA; + chip->IO_ADDR_W = cs->base + OFFSET_DATA; + chip->chip_delay = RB_DELAY_US; + chip->options = NAND_NO_SUBPAGE_WRITE; + chip->select_chip = jz4780_nand_select_chip; + chip->cmd_ctrl = jz4780_nand_cmd_ctrl; + chip->ecc.mode = NAND_ECC_HW; + chip->controller = &nfc->controller; + nand_set_flash_node(chip, np); + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + ret = jz4780_nand_init_ecc(nand, dev); + if (ret) + return ret; + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + nand_release(mtd); + return ret; + } + + list_add_tail(&nand->chip_list, &nfc->chips); + + return 0; +} + +static void jz4780_nand_cleanup_chips(struct jz4780_nand_controller *nfc) +{ + struct jz4780_nand_chip *chip; + + while (!list_empty(&nfc->chips)) { + chip = list_first_entry(&nfc->chips, struct jz4780_nand_chip, chip_list); + nand_release(nand_to_mtd(&chip->chip)); + list_del(&chip->chip_list); + } +} + +static int jz4780_nand_init_chips(struct jz4780_nand_controller *nfc, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np; + int i = 0; + int ret; + int num_chips = of_get_child_count(dev->of_node); + + if (num_chips > nfc->num_banks) { + dev_err(dev, "found %d chips but only %d banks\n", num_chips, nfc->num_banks); + return -EINVAL; + } + + for_each_child_of_node(dev->of_node, np) { + ret = jz4780_nand_init_chip(pdev, nfc, np, i); + if (ret) { + jz4780_nand_cleanup_chips(nfc); + return ret; + } + + i++; + } + + return 0; +} + +static int jz4780_nand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + unsigned int num_banks; + struct jz4780_nand_controller *nfc; + int ret; + + num_banks = jz4780_nemc_num_banks(dev); + if (num_banks == 0) { + dev_err(dev, "no banks found\n"); + return -ENODEV; + } + + nfc = devm_kzalloc(dev, sizeof(*nfc) + (sizeof(nfc->cs[0]) * num_banks), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + /* + * Check for BCH HW before we call nand_scan_ident, to prevent us from + * having to call it again if the BCH driver returns -EPROBE_DEFER. + */ + nfc->bch = of_jz4780_bch_get(dev->of_node); + if (IS_ERR(nfc->bch)) + return PTR_ERR(nfc->bch); + + nfc->dev = dev; + nfc->num_banks = num_banks; + + nand_hw_control_init(&nfc->controller); + INIT_LIST_HEAD(&nfc->chips); + + ret = jz4780_nand_init_chips(nfc, pdev); + if (ret) { + if (nfc->bch) + jz4780_bch_release(nfc->bch); + return ret; + } + + platform_set_drvdata(pdev, nfc); + return 0; +} + +static int jz4780_nand_remove(struct platform_device *pdev) +{ + struct jz4780_nand_controller *nfc = platform_get_drvdata(pdev); + + if (nfc->bch) + jz4780_bch_release(nfc->bch); + + jz4780_nand_cleanup_chips(nfc); + + return 0; +} + +static const struct of_device_id jz4780_nand_dt_match[] = { + { .compatible = "ingenic,jz4780-nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4780_nand_dt_match); + +static struct platform_driver jz4780_nand_driver = { + .probe = jz4780_nand_probe, + .remove = jz4780_nand_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(jz4780_nand_dt_match), + }, +}; +module_platform_driver(jz4780_nand_driver); + +MODULE_AUTHOR("Alex Smith "); +MODULE_AUTHOR("Harvey Hunt "); +MODULE_DESCRIPTION("Ingenic JZ4780 NAND driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c new file mode 100644 index 000000000000..e357948a7505 --- /dev/null +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -0,0 +1,909 @@ +/* + * Driver for NAND MLC Controller in LPC32xx + * + * Author: Roland Stigge + * + * Copyright © 2011 WORK Microwave GmbH + * Copyright © 2011, 2012 Roland Stigge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * NAND Flash Controller Operation: + * - Read: Auto Decode + * - Write: Auto Encode + * - Tested Page Sizes: 2048, 4096 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "lpc32xx_mlc" + +/********************************************************************** +* MLC NAND controller register offsets +**********************************************************************/ + +#define MLC_BUFF(x) (x + 0x00000) +#define MLC_DATA(x) (x + 0x08000) +#define MLC_CMD(x) (x + 0x10000) +#define MLC_ADDR(x) (x + 0x10004) +#define MLC_ECC_ENC_REG(x) (x + 0x10008) +#define MLC_ECC_DEC_REG(x) (x + 0x1000C) +#define MLC_ECC_AUTO_ENC_REG(x) (x + 0x10010) +#define MLC_ECC_AUTO_DEC_REG(x) (x + 0x10014) +#define MLC_RPR(x) (x + 0x10018) +#define MLC_WPR(x) (x + 0x1001C) +#define MLC_RUBP(x) (x + 0x10020) +#define MLC_ROBP(x) (x + 0x10024) +#define MLC_SW_WP_ADD_LOW(x) (x + 0x10028) +#define MLC_SW_WP_ADD_HIG(x) (x + 0x1002C) +#define MLC_ICR(x) (x + 0x10030) +#define MLC_TIME_REG(x) (x + 0x10034) +#define MLC_IRQ_MR(x) (x + 0x10038) +#define MLC_IRQ_SR(x) (x + 0x1003C) +#define MLC_LOCK_PR(x) (x + 0x10044) +#define MLC_ISR(x) (x + 0x10048) +#define MLC_CEH(x) (x + 0x1004C) + +/********************************************************************** +* MLC_CMD bit definitions +**********************************************************************/ +#define MLCCMD_RESET 0xFF + +/********************************************************************** +* MLC_ICR bit definitions +**********************************************************************/ +#define MLCICR_WPROT (1 << 3) +#define MLCICR_LARGEBLOCK (1 << 2) +#define MLCICR_LONGADDR (1 << 1) +#define MLCICR_16BIT (1 << 0) /* unsupported by LPC32x0! */ + +/********************************************************************** +* MLC_TIME_REG bit definitions +**********************************************************************/ +#define MLCTIMEREG_TCEA_DELAY(n) (((n) & 0x03) << 24) +#define MLCTIMEREG_BUSY_DELAY(n) (((n) & 0x1F) << 19) +#define MLCTIMEREG_NAND_TA(n) (((n) & 0x07) << 16) +#define MLCTIMEREG_RD_HIGH(n) (((n) & 0x0F) << 12) +#define MLCTIMEREG_RD_LOW(n) (((n) & 0x0F) << 8) +#define MLCTIMEREG_WR_HIGH(n) (((n) & 0x0F) << 4) +#define MLCTIMEREG_WR_LOW(n) (((n) & 0x0F) << 0) + +/********************************************************************** +* MLC_IRQ_MR and MLC_IRQ_SR bit definitions +**********************************************************************/ +#define MLCIRQ_NAND_READY (1 << 5) +#define MLCIRQ_CONTROLLER_READY (1 << 4) +#define MLCIRQ_DECODE_FAILURE (1 << 3) +#define MLCIRQ_DECODE_ERROR (1 << 2) +#define MLCIRQ_ECC_READY (1 << 1) +#define MLCIRQ_WRPROT_FAULT (1 << 0) + +/********************************************************************** +* MLC_LOCK_PR bit definitions +**********************************************************************/ +#define MLCLOCKPR_MAGIC 0xA25E + +/********************************************************************** +* MLC_ISR bit definitions +**********************************************************************/ +#define MLCISR_DECODER_FAILURE (1 << 6) +#define MLCISR_ERRORS ((1 << 4) | (1 << 5)) +#define MLCISR_ERRORS_DETECTED (1 << 3) +#define MLCISR_ECC_READY (1 << 2) +#define MLCISR_CONTROLLER_READY (1 << 1) +#define MLCISR_NAND_READY (1 << 0) + +/********************************************************************** +* MLC_CEH bit definitions +**********************************************************************/ +#define MLCCEH_NORMAL (1 << 0) + +struct lpc32xx_nand_cfg_mlc { + uint32_t tcea_delay; + uint32_t busy_delay; + uint32_t nand_ta; + uint32_t rd_high; + uint32_t rd_low; + uint32_t wr_high; + uint32_t wr_low; + int wp_gpio; + struct mtd_partition *parts; + unsigned num_parts; +}; + +static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = ((section + 1) * 16) - nand_chip->ecc.bytes; + oobregion->length = nand_chip->ecc.bytes; + + return 0; +} + +static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = 16 * section; + oobregion->length = 16 - nand_chip->ecc.bytes; + + return 0; +} + +static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = { + .ecc = lpc32xx_ooblayout_ecc, + .free = lpc32xx_ooblayout_free, +}; + +static struct nand_bbt_descr lpc32xx_nand_bbt = { + .options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB | + NAND_BBT_WRITE, + .pages = { 524224, 0, 0, 0, 0, 0, 0, 0 }, +}; + +static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = { + .options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB | + NAND_BBT_WRITE, + .pages = { 524160, 0, 0, 0, 0, 0, 0, 0 }, +}; + +struct lpc32xx_nand_host { + struct nand_chip nand_chip; + struct lpc32xx_mlc_platform_data *pdata; + struct clk *clk; + void __iomem *io_base; + int irq; + struct lpc32xx_nand_cfg_mlc *ncfg; + struct completion comp_nand; + struct completion comp_controller; + uint32_t llptr; + /* + * Physical addresses of ECC buffer, DMA data buffers, OOB data buffer + */ + dma_addr_t oob_buf_phy; + /* + * Virtual addresses of ECC buffer, DMA data buffers, OOB data buffer + */ + uint8_t *oob_buf; + /* Physical address of DMA base address */ + dma_addr_t io_base_phy; + + struct completion comp_dma; + struct dma_chan *dma_chan; + struct dma_slave_config dma_slave_config; + struct scatterlist sgl; + uint8_t *dma_buf; + uint8_t *dummy_buf; + int mlcsubpages; /* number of 512bytes-subpages */ +}; + +/* + * Activate/Deactivate DMA Operation: + * + * Using the PL080 DMA Controller for transferring the 512 byte subpages + * instead of doing readl() / writel() in a loop slows it down significantly. + * Measurements via getnstimeofday() upon 512 byte subpage reads reveal: + * + * - readl() of 128 x 32 bits in a loop: ~20us + * - DMA read of 512 bytes (32 bit, 4...128 words bursts): ~60us + * - DMA read of 512 bytes (32 bit, no bursts): ~100us + * + * This applies to the transfer itself. In the DMA case: only the + * wait_for_completion() (DMA setup _not_ included). + * + * Note that the 512 bytes subpage transfer is done directly from/to a + * FIFO/buffer inside the NAND controller. Most of the time (~400-800us for a + * 2048 bytes page) is spent waiting for the NAND IRQ, anyway. (The NAND + * controller transferring data between its internal buffer to/from the NAND + * chip.) + * + * Therefore, using the PL080 DMA is disabled by default, for now. + * + */ +static int use_dma; + +static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) +{ + uint32_t clkrate, tmp; + + /* Reset MLC controller */ + writel(MLCCMD_RESET, MLC_CMD(host->io_base)); + udelay(1000); + + /* Get base clock for MLC block */ + clkrate = clk_get_rate(host->clk); + if (clkrate == 0) + clkrate = 104000000; + + /* Unlock MLC_ICR + * (among others, will be locked again automatically) */ + writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base)); + + /* Configure MLC Controller: Large Block, 5 Byte Address */ + tmp = MLCICR_LARGEBLOCK | MLCICR_LONGADDR; + writel(tmp, MLC_ICR(host->io_base)); + + /* Unlock MLC_TIME_REG + * (among others, will be locked again automatically) */ + writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base)); + + /* Compute clock setup values, see LPC and NAND manual */ + tmp = 0; + tmp |= MLCTIMEREG_TCEA_DELAY(clkrate / host->ncfg->tcea_delay + 1); + tmp |= MLCTIMEREG_BUSY_DELAY(clkrate / host->ncfg->busy_delay + 1); + tmp |= MLCTIMEREG_NAND_TA(clkrate / host->ncfg->nand_ta + 1); + tmp |= MLCTIMEREG_RD_HIGH(clkrate / host->ncfg->rd_high + 1); + tmp |= MLCTIMEREG_RD_LOW(clkrate / host->ncfg->rd_low); + tmp |= MLCTIMEREG_WR_HIGH(clkrate / host->ncfg->wr_high + 1); + tmp |= MLCTIMEREG_WR_LOW(clkrate / host->ncfg->wr_low); + writel(tmp, MLC_TIME_REG(host->io_base)); + + /* Enable IRQ for CONTROLLER_READY and NAND_READY */ + writeb(MLCIRQ_CONTROLLER_READY | MLCIRQ_NAND_READY, + MLC_IRQ_MR(host->io_base)); + + /* Normal nCE operation: nCE controlled by controller */ + writel(MLCCEH_NORMAL, MLC_CEH(host->io_base)); +} + +/* + * Hardware specific access to control lines + */ +static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip); + + if (cmd != NAND_CMD_NONE) { + if (ctrl & NAND_CLE) + writel(cmd, MLC_CMD(host->io_base)); + else + writel(cmd, MLC_ADDR(host->io_base)); + } +} + +/* + * Read Device Ready (NAND device _and_ controller ready) + */ +static int lpc32xx_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip); + + if ((readb(MLC_ISR(host->io_base)) & + (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) == + (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) + return 1; + + return 0; +} + +static irqreturn_t lpc3xxx_nand_irq(int irq, struct lpc32xx_nand_host *host) +{ + uint8_t sr; + + /* Clear interrupt flag by reading status */ + sr = readb(MLC_IRQ_SR(host->io_base)); + if (sr & MLCIRQ_NAND_READY) + complete(&host->comp_nand); + if (sr & MLCIRQ_CONTROLLER_READY) + complete(&host->comp_controller); + + return IRQ_HANDLED; +} + +static int lpc32xx_waitfunc_nand(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + + if (readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY) + goto exit; + + wait_for_completion(&host->comp_nand); + + while (!(readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)) { + /* Seems to be delayed sometimes by controller */ + dev_dbg(&mtd->dev, "Warning: NAND not ready.\n"); + cpu_relax(); + } + +exit: + return NAND_STATUS_READY; +} + +static int lpc32xx_waitfunc_controller(struct mtd_info *mtd, + struct nand_chip *chip) +{ + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + + if (readb(MLC_ISR(host->io_base)) & MLCISR_CONTROLLER_READY) + goto exit; + + wait_for_completion(&host->comp_controller); + + while (!(readb(MLC_ISR(host->io_base)) & + MLCISR_CONTROLLER_READY)) { + dev_dbg(&mtd->dev, "Warning: Controller not ready.\n"); + cpu_relax(); + } + +exit: + return NAND_STATUS_READY; +} + +static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + lpc32xx_waitfunc_nand(mtd, chip); + lpc32xx_waitfunc_controller(mtd, chip); + + return NAND_STATUS_READY; +} + +/* + * Enable NAND write protect + */ +static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) +{ + if (gpio_is_valid(host->ncfg->wp_gpio)) + gpio_set_value(host->ncfg->wp_gpio, 0); +} + +/* + * Disable NAND write protect + */ +static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) +{ + if (gpio_is_valid(host->ncfg->wp_gpio)) + gpio_set_value(host->ncfg->wp_gpio, 1); +} + +static void lpc32xx_dma_complete_func(void *completion) +{ + complete(completion); +} + +static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, + enum dma_transfer_direction dir) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + struct dma_async_tx_descriptor *desc; + int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + int res; + + sg_init_one(&host->sgl, mem, len); + + res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + if (res != 1) { + dev_err(mtd->dev.parent, "Failed to map sg list\n"); + return -ENXIO; + } + desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir, + flags); + if (!desc) { + dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); + goto out1; + } + + init_completion(&host->comp_dma); + desc->callback = lpc32xx_dma_complete_func; + desc->callback_param = &host->comp_dma; + + dmaengine_submit(desc); + dma_async_issue_pending(host->dma_chan); + + wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000)); + + dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + return 0; +out1: + dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + return -ENXIO; +} + +static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + int i, j; + uint8_t *oobbuf = chip->oob_poi; + uint32_t mlc_isr; + int res; + uint8_t *dma_buf; + bool dma_mapped; + + if ((void *)buf <= high_memory) { + dma_buf = buf; + dma_mapped = true; + } else { + dma_buf = host->dma_buf; + dma_mapped = false; + } + + /* Writing Command and Address */ + nand_read_page_op(chip, page, 0, NULL, 0); + + /* For all sub-pages */ + for (i = 0; i < host->mlcsubpages; i++) { + /* Start Auto Decode Command */ + writeb(0x00, MLC_ECC_AUTO_DEC_REG(host->io_base)); + + /* Wait for Controller Ready */ + lpc32xx_waitfunc_controller(mtd, chip); + + /* Check ECC Error status */ + mlc_isr = readl(MLC_ISR(host->io_base)); + if (mlc_isr & MLCISR_DECODER_FAILURE) { + mtd->ecc_stats.failed++; + dev_warn(&mtd->dev, "%s: DECODER_FAILURE\n", __func__); + } else if (mlc_isr & MLCISR_ERRORS_DETECTED) { + mtd->ecc_stats.corrected += ((mlc_isr >> 4) & 0x3) + 1; + } + + /* Read 512 + 16 Bytes */ + if (use_dma) { + res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512, + DMA_DEV_TO_MEM); + if (res) + return res; + } else { + for (j = 0; j < (512 >> 2); j++) { + *((uint32_t *)(buf)) = + readl(MLC_BUFF(host->io_base)); + buf += 4; + } + } + for (j = 0; j < (16 >> 2); j++) { + *((uint32_t *)(oobbuf)) = + readl(MLC_BUFF(host->io_base)); + oobbuf += 4; + } + } + + if (use_dma && !dma_mapped) + memcpy(buf, dma_buf, mtd->writesize); + + return 0; +} + +static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + const uint8_t *oobbuf = chip->oob_poi; + uint8_t *dma_buf = (uint8_t *)buf; + int res; + int i, j; + + if (use_dma && (void *)buf >= high_memory) { + dma_buf = host->dma_buf; + memcpy(dma_buf, buf, mtd->writesize); + } + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + for (i = 0; i < host->mlcsubpages; i++) { + /* Start Encode */ + writeb(0x00, MLC_ECC_ENC_REG(host->io_base)); + + /* Write 512 + 6 Bytes to Buffer */ + if (use_dma) { + res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512, + DMA_MEM_TO_DEV); + if (res) + return res; + } else { + for (j = 0; j < (512 >> 2); j++) { + writel(*((uint32_t *)(buf)), + MLC_BUFF(host->io_base)); + buf += 4; + } + } + writel(*((uint32_t *)(oobbuf)), MLC_BUFF(host->io_base)); + oobbuf += 4; + writew(*((uint16_t *)(oobbuf)), MLC_BUFF(host->io_base)); + oobbuf += 12; + + /* Auto Encode w/ Bit 8 = 0 (see LPC MLC Controller manual) */ + writeb(0x00, MLC_ECC_AUTO_ENC_REG(host->io_base)); + + /* Wait for Controller Ready */ + lpc32xx_waitfunc_controller(mtd, chip); + } + + return nand_prog_page_end_op(chip); +} + +static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + + /* Read whole page - necessary with MLC controller! */ + lpc32xx_read_page(mtd, chip, host->dummy_buf, 1, page); + + return 0; +} + +static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + /* None, write_oob conflicts with the automatic LPC MLC ECC decoder! */ + return 0; +} + +/* Prepares MLC for transfers with H/W ECC enabled: always enabled anyway */ +static void lpc32xx_ecc_enable(struct mtd_info *mtd, int mode) +{ + /* Always enabled! */ +} + +static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host) +{ + struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); + dma_cap_mask_t mask; + + if (!host->pdata || !host->pdata->dma_filter) { + dev_err(mtd->dev.parent, "no DMA platform data\n"); + return -ENOENT; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, + "nand-mlc"); + if (!host->dma_chan) { + dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); + return -EBUSY; + } + + /* + * Set direction to a sensible value even if the dmaengine driver + * should ignore it. With the default (DMA_MEM_TO_MEM), the amba-pl08x + * driver criticizes it as "alien transfer direction". + */ + host->dma_slave_config.direction = DMA_DEV_TO_MEM; + host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_slave_config.src_maxburst = 128; + host->dma_slave_config.dst_maxburst = 128; + /* DMA controller does flow control: */ + host->dma_slave_config.device_fc = false; + host->dma_slave_config.src_addr = MLC_BUFF(host->io_base_phy); + host->dma_slave_config.dst_addr = MLC_BUFF(host->io_base_phy); + if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) { + dev_err(mtd->dev.parent, "Failed to setup DMA slave\n"); + goto out1; + } + + return 0; +out1: + dma_release_channel(host->dma_chan); + return -ENXIO; +} + +static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev) +{ + struct lpc32xx_nand_cfg_mlc *ncfg; + struct device_node *np = dev->of_node; + + ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL); + if (!ncfg) + return NULL; + + of_property_read_u32(np, "nxp,tcea-delay", &ncfg->tcea_delay); + of_property_read_u32(np, "nxp,busy-delay", &ncfg->busy_delay); + of_property_read_u32(np, "nxp,nand-ta", &ncfg->nand_ta); + of_property_read_u32(np, "nxp,rd-high", &ncfg->rd_high); + of_property_read_u32(np, "nxp,rd-low", &ncfg->rd_low); + of_property_read_u32(np, "nxp,wr-high", &ncfg->wr_high); + of_property_read_u32(np, "nxp,wr-low", &ncfg->wr_low); + + if (!ncfg->tcea_delay || !ncfg->busy_delay || !ncfg->nand_ta || + !ncfg->rd_high || !ncfg->rd_low || !ncfg->wr_high || + !ncfg->wr_low) { + dev_err(dev, "chip parameters not specified correctly\n"); + return NULL; + } + + ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); + + return ncfg; +} + +/* + * Probe for NAND controller + */ +static int lpc32xx_nand_probe(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + struct resource *rc; + int res; + + /* Allocate memory for the device structure (and zero it) */ + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->io_base = devm_ioremap_resource(&pdev->dev, rc); + if (IS_ERR(host->io_base)) + return PTR_ERR(host->io_base); + + host->io_base_phy = rc->start; + + nand_chip = &host->nand_chip; + mtd = nand_to_mtd(nand_chip); + if (pdev->dev.of_node) + host->ncfg = lpc32xx_parse_dt(&pdev->dev); + if (!host->ncfg) { + dev_err(&pdev->dev, + "Missing or bad NAND config from device tree\n"); + return -ENOENT; + } + if (host->ncfg->wp_gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(host->ncfg->wp_gpio) && + gpio_request(host->ncfg->wp_gpio, "NAND WP")) { + dev_err(&pdev->dev, "GPIO not available\n"); + return -EBUSY; + } + lpc32xx_wp_disable(host); + + host->pdata = dev_get_platdata(&pdev->dev); + + /* link the private data structures */ + nand_set_controller_data(nand_chip, host); + nand_set_flash_node(nand_chip, pdev->dev.of_node); + mtd->dev.parent = &pdev->dev; + + /* Get NAND clock */ + host->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) { + dev_err(&pdev->dev, "Clock initialization failure\n"); + res = -ENOENT; + goto err_exit1; + } + res = clk_prepare_enable(host->clk); + if (res) + goto err_put_clk; + + nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; + nand_chip->dev_ready = lpc32xx_nand_device_ready; + nand_chip->chip_delay = 25; /* us */ + nand_chip->IO_ADDR_R = MLC_DATA(host->io_base); + nand_chip->IO_ADDR_W = MLC_DATA(host->io_base); + + /* Init NAND controller */ + lpc32xx_nand_setup(host); + + platform_set_drvdata(pdev, host); + + /* Initialize function pointers */ + nand_chip->ecc.hwctl = lpc32xx_ecc_enable; + nand_chip->ecc.read_page_raw = lpc32xx_read_page; + nand_chip->ecc.read_page = lpc32xx_read_page; + nand_chip->ecc.write_page_raw = lpc32xx_write_page_lowlevel; + nand_chip->ecc.write_page = lpc32xx_write_page_lowlevel; + nand_chip->ecc.write_oob = lpc32xx_write_oob; + nand_chip->ecc.read_oob = lpc32xx_read_oob; + nand_chip->ecc.strength = 4; + nand_chip->ecc.bytes = 10; + nand_chip->waitfunc = lpc32xx_waitfunc; + + nand_chip->options = NAND_NO_SUBPAGE_WRITE; + nand_chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + nand_chip->bbt_td = &lpc32xx_nand_bbt; + nand_chip->bbt_md = &lpc32xx_nand_bbt_mirror; + + if (use_dma) { + res = lpc32xx_dma_setup(host); + if (res) { + res = -EIO; + goto err_exit2; + } + } + + /* + * Scan to find existance of the device and + * Get the type of NAND device SMALL block or LARGE block + */ + res = nand_scan_ident(mtd, 1, NULL); + if (res) + goto err_exit3; + + host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); + if (!host->dma_buf) { + res = -ENOMEM; + goto err_exit3; + } + + host->dummy_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); + if (!host->dummy_buf) { + res = -ENOMEM; + goto err_exit3; + } + + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); + host->mlcsubpages = mtd->writesize / 512; + + /* initially clear interrupt status */ + readb(MLC_IRQ_SR(host->io_base)); + + init_completion(&host->comp_nand); + init_completion(&host->comp_controller); + + host->irq = platform_get_irq(pdev, 0); + if (host->irq < 0) { + dev_err(&pdev->dev, "failed to get platform irq\n"); + res = -EINVAL; + goto err_exit3; + } + + if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq, + IRQF_TRIGGER_HIGH, DRV_NAME, host)) { + dev_err(&pdev->dev, "Error requesting NAND IRQ\n"); + res = -ENXIO; + goto err_exit3; + } + + /* + * Fills out all the uninitialized function pointers with the defaults + * And scans for a bad block table if appropriate. + */ + res = nand_scan_tail(mtd); + if (res) + goto err_exit4; + + mtd->name = DRV_NAME; + + res = mtd_device_register(mtd, host->ncfg->parts, + host->ncfg->num_parts); + if (!res) + return res; + + nand_release(mtd); + +err_exit4: + free_irq(host->irq, host); +err_exit3: + if (use_dma) + dma_release_channel(host->dma_chan); +err_exit2: + clk_disable_unprepare(host->clk); +err_put_clk: + clk_put(host->clk); +err_exit1: + lpc32xx_wp_enable(host); + gpio_free(host->ncfg->wp_gpio); + + return res; +} + +/* + * Remove NAND device + */ +static int lpc32xx_nand_remove(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); + + nand_release(mtd); + free_irq(host->irq, host); + if (use_dma) + dma_release_channel(host->dma_chan); + + clk_disable_unprepare(host->clk); + clk_put(host->clk); + + lpc32xx_wp_enable(host); + gpio_free(host->ncfg->wp_gpio); + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_nand_resume(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + int ret; + + /* Re-enable NAND clock */ + ret = clk_prepare_enable(host->clk); + if (ret) + return ret; + + /* Fresh init of NAND controller */ + lpc32xx_nand_setup(host); + + /* Disable write protect */ + lpc32xx_wp_disable(host); + + return 0; +} + +static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) +{ + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + + /* Enable write protect for safety */ + lpc32xx_wp_enable(host); + + /* Disable clock */ + clk_disable_unprepare(host->clk); + return 0; +} + +#else +#define lpc32xx_nand_resume NULL +#define lpc32xx_nand_suspend NULL +#endif + +static const struct of_device_id lpc32xx_nand_match[] = { + { .compatible = "nxp,lpc3220-mlc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); + +static struct platform_driver lpc32xx_nand_driver = { + .probe = lpc32xx_nand_probe, + .remove = lpc32xx_nand_remove, + .resume = lpc32xx_nand_resume, + .suspend = lpc32xx_nand_suspend, + .driver = { + .name = DRV_NAME, + .of_match_table = lpc32xx_nand_match, + }, +}; + +module_platform_driver(lpc32xx_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Roland Stigge "); +MODULE_DESCRIPTION("NAND driver for the NXP LPC32XX MLC controller"); diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c new file mode 100644 index 000000000000..5f7cc6da0a7f --- /dev/null +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -0,0 +1,1032 @@ +/* + * NXP LPC32XX NAND SLC driver + * + * Authors: + * Kevin Wells + * Roland Stigge + * + * Copyright © 2011 NXP Semiconductors + * Copyright © 2012 Roland Stigge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LPC32XX_MODNAME "lpc32xx-nand" + +/********************************************************************** +* SLC NAND controller register offsets +**********************************************************************/ + +#define SLC_DATA(x) (x + 0x000) +#define SLC_ADDR(x) (x + 0x004) +#define SLC_CMD(x) (x + 0x008) +#define SLC_STOP(x) (x + 0x00C) +#define SLC_CTRL(x) (x + 0x010) +#define SLC_CFG(x) (x + 0x014) +#define SLC_STAT(x) (x + 0x018) +#define SLC_INT_STAT(x) (x + 0x01C) +#define SLC_IEN(x) (x + 0x020) +#define SLC_ISR(x) (x + 0x024) +#define SLC_ICR(x) (x + 0x028) +#define SLC_TAC(x) (x + 0x02C) +#define SLC_TC(x) (x + 0x030) +#define SLC_ECC(x) (x + 0x034) +#define SLC_DMA_DATA(x) (x + 0x038) + +/********************************************************************** +* slc_ctrl register definitions +**********************************************************************/ +#define SLCCTRL_SW_RESET (1 << 2) /* Reset the NAND controller bit */ +#define SLCCTRL_ECC_CLEAR (1 << 1) /* Reset ECC bit */ +#define SLCCTRL_DMA_START (1 << 0) /* Start DMA channel bit */ + +/********************************************************************** +* slc_cfg register definitions +**********************************************************************/ +#define SLCCFG_CE_LOW (1 << 5) /* Force CE low bit */ +#define SLCCFG_DMA_ECC (1 << 4) /* Enable DMA ECC bit */ +#define SLCCFG_ECC_EN (1 << 3) /* ECC enable bit */ +#define SLCCFG_DMA_BURST (1 << 2) /* DMA burst bit */ +#define SLCCFG_DMA_DIR (1 << 1) /* DMA write(0)/read(1) bit */ +#define SLCCFG_WIDTH (1 << 0) /* External device width, 0=8bit */ + +/********************************************************************** +* slc_stat register definitions +**********************************************************************/ +#define SLCSTAT_DMA_FIFO (1 << 2) /* DMA FIFO has data bit */ +#define SLCSTAT_SLC_FIFO (1 << 1) /* SLC FIFO has data bit */ +#define SLCSTAT_NAND_READY (1 << 0) /* NAND device is ready bit */ + +/********************************************************************** +* slc_int_stat, slc_ien, slc_isr, and slc_icr register definitions +**********************************************************************/ +#define SLCSTAT_INT_TC (1 << 1) /* Transfer count bit */ +#define SLCSTAT_INT_RDY_EN (1 << 0) /* Ready interrupt bit */ + +/********************************************************************** +* slc_tac register definitions +**********************************************************************/ +/* Computation of clock cycles on basis of controller and device clock rates */ +#define SLCTAC_CLOCKS(c, n, s) (min_t(u32, DIV_ROUND_UP(c, n) - 1, 0xF) << s) + +/* Clock setting for RDY write sample wait time in 2*n clocks */ +#define SLCTAC_WDR(n) (((n) & 0xF) << 28) +/* Write pulse width in clock cycles, 1 to 16 clocks */ +#define SLCTAC_WWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 24)) +/* Write hold time of control and data signals, 1 to 16 clocks */ +#define SLCTAC_WHOLD(c, n) (SLCTAC_CLOCKS(c, n, 20)) +/* Write setup time of control and data signals, 1 to 16 clocks */ +#define SLCTAC_WSETUP(c, n) (SLCTAC_CLOCKS(c, n, 16)) +/* Clock setting for RDY read sample wait time in 2*n clocks */ +#define SLCTAC_RDR(n) (((n) & 0xF) << 12) +/* Read pulse width in clock cycles, 1 to 16 clocks */ +#define SLCTAC_RWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 8)) +/* Read hold time of control and data signals, 1 to 16 clocks */ +#define SLCTAC_RHOLD(c, n) (SLCTAC_CLOCKS(c, n, 4)) +/* Read setup time of control and data signals, 1 to 16 clocks */ +#define SLCTAC_RSETUP(c, n) (SLCTAC_CLOCKS(c, n, 0)) + +/********************************************************************** +* slc_ecc register definitions +**********************************************************************/ +/* ECC line party fetch macro */ +#define SLCECC_TO_LINEPAR(n) (((n) >> 6) & 0x7FFF) +#define SLCECC_TO_COLPAR(n) ((n) & 0x3F) + +/* + * DMA requires storage space for the DMA local buffer and the hardware ECC + * storage area. The DMA local buffer is only used if DMA mapping fails + * during runtime. + */ +#define LPC32XX_DMA_DATA_SIZE 4096 +#define LPC32XX_ECC_SAVE_SIZE ((4096 / 256) * 4) + +/* Number of bytes used for ECC stored in NAND per 256 bytes */ +#define LPC32XX_SLC_DEV_ECC_BYTES 3 + +/* + * If the NAND base clock frequency can't be fetched, this frequency will be + * used instead as the base. This rate is used to setup the timing registers + * used for NAND accesses. + */ +#define LPC32XX_DEF_BUS_RATE 133250000 + +/* Milliseconds for DMA FIFO timeout (unlikely anyway) */ +#define LPC32XX_DMA_TIMEOUT 100 + +/* + * NAND ECC Layout for small page NAND devices + * Note: For large and huge page devices, the default layouts are used + */ +static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->length = 6; + oobregion->offset = 10; + + return 0; +} + +static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + oobregion->length = 4; + } else { + oobregion->offset = 6; + oobregion->length = 4; + } + + return 0; +} + +static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = { + .ecc = lpc32xx_ooblayout_ecc, + .free = lpc32xx_ooblayout_free, +}; + +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +/* + * Small page FLASH BBT descriptors, marker at offset 0, version at offset 6 + * Note: Large page devices used the default layout + */ +static struct nand_bbt_descr bbt_smallpage_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_smallpage_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/* + * NAND platform configuration structure + */ +struct lpc32xx_nand_cfg_slc { + uint32_t wdr_clks; + uint32_t wwidth; + uint32_t whold; + uint32_t wsetup; + uint32_t rdr_clks; + uint32_t rwidth; + uint32_t rhold; + uint32_t rsetup; + int wp_gpio; + struct mtd_partition *parts; + unsigned num_parts; +}; + +struct lpc32xx_nand_host { + struct nand_chip nand_chip; + struct lpc32xx_slc_platform_data *pdata; + struct clk *clk; + void __iomem *io_base; + struct lpc32xx_nand_cfg_slc *ncfg; + + struct completion comp; + struct dma_chan *dma_chan; + uint32_t dma_buf_len; + struct dma_slave_config dma_slave_config; + struct scatterlist sgl; + + /* + * DMA and CPU addresses of ECC work area and data buffer + */ + uint32_t *ecc_buf; + uint8_t *data_buf; + dma_addr_t io_base_dma; +}; + +static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) +{ + uint32_t clkrate, tmp; + + /* Reset SLC controller */ + writel(SLCCTRL_SW_RESET, SLC_CTRL(host->io_base)); + udelay(1000); + + /* Basic setup */ + writel(0, SLC_CFG(host->io_base)); + writel(0, SLC_IEN(host->io_base)); + writel((SLCSTAT_INT_TC | SLCSTAT_INT_RDY_EN), + SLC_ICR(host->io_base)); + + /* Get base clock for SLC block */ + clkrate = clk_get_rate(host->clk); + if (clkrate == 0) + clkrate = LPC32XX_DEF_BUS_RATE; + + /* Compute clock setup values */ + tmp = SLCTAC_WDR(host->ncfg->wdr_clks) | + SLCTAC_WWIDTH(clkrate, host->ncfg->wwidth) | + SLCTAC_WHOLD(clkrate, host->ncfg->whold) | + SLCTAC_WSETUP(clkrate, host->ncfg->wsetup) | + SLCTAC_RDR(host->ncfg->rdr_clks) | + SLCTAC_RWIDTH(clkrate, host->ncfg->rwidth) | + SLCTAC_RHOLD(clkrate, host->ncfg->rhold) | + SLCTAC_RSETUP(clkrate, host->ncfg->rsetup); + writel(tmp, SLC_TAC(host->io_base)); +} + +/* + * Hardware specific access to control lines + */ +static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + uint32_t tmp; + struct nand_chip *chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + + /* Does CE state need to be changed? */ + tmp = readl(SLC_CFG(host->io_base)); + if (ctrl & NAND_NCE) + tmp |= SLCCFG_CE_LOW; + else + tmp &= ~SLCCFG_CE_LOW; + writel(tmp, SLC_CFG(host->io_base)); + + if (cmd != NAND_CMD_NONE) { + if (ctrl & NAND_CLE) + writel(cmd, SLC_CMD(host->io_base)); + else + writel(cmd, SLC_ADDR(host->io_base)); + } +} + +/* + * Read the Device Ready pin + */ +static int lpc32xx_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + int rdy = 0; + + if ((readl(SLC_STAT(host->io_base)) & SLCSTAT_NAND_READY) != 0) + rdy = 1; + + return rdy; +} + +/* + * Enable NAND write protect + */ +static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) +{ + if (gpio_is_valid(host->ncfg->wp_gpio)) + gpio_set_value(host->ncfg->wp_gpio, 0); +} + +/* + * Disable NAND write protect + */ +static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) +{ + if (gpio_is_valid(host->ncfg->wp_gpio)) + gpio_set_value(host->ncfg->wp_gpio, 1); +} + +/* + * Prepares SLC for transfers with H/W ECC enabled + */ +static void lpc32xx_nand_ecc_enable(struct mtd_info *mtd, int mode) +{ + /* Hardware ECC is enabled automatically in hardware as needed */ +} + +/* + * Calculates the ECC for the data + */ +static int lpc32xx_nand_ecc_calculate(struct mtd_info *mtd, + const unsigned char *buf, + unsigned char *code) +{ + /* + * ECC is calculated automatically in hardware during syndrome read + * and write operations, so it doesn't need to be calculated here. + */ + return 0; +} + +/* + * Read a single byte from NAND device + */ +static uint8_t lpc32xx_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + + return (uint8_t)readl(SLC_DATA(host->io_base)); +} + +/* + * Simple device read without ECC + */ +static void lpc32xx_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + + /* Direct device read with no ECC */ + while (len-- > 0) + *buf++ = (uint8_t)readl(SLC_DATA(host->io_base)); +} + +/* + * Simple device write without ECC + */ +static void lpc32xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + + /* Direct device write with no ECC */ + while (len-- > 0) + writel((uint32_t)*buf++, SLC_DATA(host->io_base)); +} + +/* + * Read the OOB data from the device without ECC using FIFO method + */ +static int lpc32xx_nand_read_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); +} + +/* + * Write the OOB data to the device without ECC using FIFO method + */ +static int lpc32xx_nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); +} + +/* + * Fills in the ECC fields in the OOB buffer with the hardware generated ECC + */ +static void lpc32xx_slc_ecc_copy(uint8_t *spare, const uint32_t *ecc, int count) +{ + int i; + + for (i = 0; i < (count * 3); i += 3) { + uint32_t ce = ecc[i / 3]; + ce = ~(ce << 2) & 0xFFFFFF; + spare[i + 2] = (uint8_t)(ce & 0xFF); + ce >>= 8; + spare[i + 1] = (uint8_t)(ce & 0xFF); + ce >>= 8; + spare[i] = (uint8_t)(ce & 0xFF); + } +} + +static void lpc32xx_dma_complete_func(void *completion) +{ + complete(completion); +} + +static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, + void *mem, int len, enum dma_transfer_direction dir) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + struct dma_async_tx_descriptor *desc; + int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + int res; + + host->dma_slave_config.direction = dir; + host->dma_slave_config.src_addr = dma; + host->dma_slave_config.dst_addr = dma; + host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_slave_config.src_maxburst = 4; + host->dma_slave_config.dst_maxburst = 4; + /* DMA controller does flow control: */ + host->dma_slave_config.device_fc = false; + if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) { + dev_err(mtd->dev.parent, "Failed to setup DMA slave\n"); + return -ENXIO; + } + + sg_init_one(&host->sgl, mem, len); + + res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + if (res != 1) { + dev_err(mtd->dev.parent, "Failed to map sg list\n"); + return -ENXIO; + } + desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir, + flags); + if (!desc) { + dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); + goto out1; + } + + init_completion(&host->comp); + desc->callback = lpc32xx_dma_complete_func; + desc->callback_param = &host->comp; + + dmaengine_submit(desc); + dma_async_issue_pending(host->dma_chan); + + wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000)); + + dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + + return 0; +out1: + dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, + DMA_BIDIRECTIONAL); + return -ENXIO; +} + +/* + * DMA read/write transfers with ECC support + */ +static int lpc32xx_xfer(struct mtd_info *mtd, uint8_t *buf, int eccsubpages, + int read) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + int i, status = 0; + unsigned long timeout; + int res; + enum dma_transfer_direction dir = + read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + uint8_t *dma_buf; + bool dma_mapped; + + if ((void *)buf <= high_memory) { + dma_buf = buf; + dma_mapped = true; + } else { + dma_buf = host->data_buf; + dma_mapped = false; + if (!read) + memcpy(host->data_buf, buf, mtd->writesize); + } + + if (read) { + writel(readl(SLC_CFG(host->io_base)) | + SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | + SLCCFG_DMA_BURST, SLC_CFG(host->io_base)); + } else { + writel((readl(SLC_CFG(host->io_base)) | + SLCCFG_ECC_EN | SLCCFG_DMA_ECC | SLCCFG_DMA_BURST) & + ~SLCCFG_DMA_DIR, + SLC_CFG(host->io_base)); + } + + /* Clear initial ECC */ + writel(SLCCTRL_ECC_CLEAR, SLC_CTRL(host->io_base)); + + /* Transfer size is data area only */ + writel(mtd->writesize, SLC_TC(host->io_base)); + + /* Start transfer in the NAND controller */ + writel(readl(SLC_CTRL(host->io_base)) | SLCCTRL_DMA_START, + SLC_CTRL(host->io_base)); + + for (i = 0; i < chip->ecc.steps; i++) { + /* Data */ + res = lpc32xx_xmit_dma(mtd, SLC_DMA_DATA(host->io_base_dma), + dma_buf + i * chip->ecc.size, + mtd->writesize / chip->ecc.steps, dir); + if (res) + return res; + + /* Always _read_ ECC */ + if (i == chip->ecc.steps - 1) + break; + if (!read) /* ECC availability delayed on write */ + udelay(10); + res = lpc32xx_xmit_dma(mtd, SLC_ECC(host->io_base_dma), + &host->ecc_buf[i], 4, DMA_DEV_TO_MEM); + if (res) + return res; + } + + /* + * According to NXP, the DMA can be finished here, but the NAND + * controller may still have buffered data. After porting to using the + * dmaengine DMA driver (amba-pl080), the condition (DMA_FIFO empty) + * appears to be always true, according to tests. Keeping the check for + * safety reasons for now. + */ + if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) { + dev_warn(mtd->dev.parent, "FIFO not empty!\n"); + timeout = jiffies + msecs_to_jiffies(LPC32XX_DMA_TIMEOUT); + while ((readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) && + time_before(jiffies, timeout)) + cpu_relax(); + if (!time_before(jiffies, timeout)) { + dev_err(mtd->dev.parent, "FIFO held data too long\n"); + status = -EIO; + } + } + + /* Read last calculated ECC value */ + if (!read) + udelay(10); + host->ecc_buf[chip->ecc.steps - 1] = + readl(SLC_ECC(host->io_base)); + + /* Flush DMA */ + dmaengine_terminate_all(host->dma_chan); + + if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO || + readl(SLC_TC(host->io_base))) { + /* Something is left in the FIFO, something is wrong */ + dev_err(mtd->dev.parent, "DMA FIFO failure\n"); + status = -EIO; + } + + /* Stop DMA & HW ECC */ + writel(readl(SLC_CTRL(host->io_base)) & ~SLCCTRL_DMA_START, + SLC_CTRL(host->io_base)); + writel(readl(SLC_CFG(host->io_base)) & + ~(SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | + SLCCFG_DMA_BURST), SLC_CFG(host->io_base)); + + if (!dma_mapped && read) + memcpy(buf, host->data_buf, mtd->writesize); + + return status; +} + +/* + * Read the data and OOB data from the device, use ECC correction with the + * data, disable ECC for the OOB data + */ +static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + struct mtd_oob_region oobregion = { }; + int stat, i, status, error; + uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE]; + + /* Issue read command */ + nand_read_page_op(chip, page, 0, NULL, 0); + + /* Read data and oob, calculate ECC */ + status = lpc32xx_xfer(mtd, buf, chip->ecc.steps, 1); + + /* Get OOB data */ + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Convert to stored ECC format */ + lpc32xx_slc_ecc_copy(tmpecc, (uint32_t *) host->ecc_buf, chip->ecc.steps); + + /* Pointer to ECC data retrieved from NAND spare area */ + error = mtd_ooblayout_ecc(mtd, 0, &oobregion); + if (error) + return error; + + oobecc = chip->oob_poi + oobregion.offset; + + for (i = 0; i < chip->ecc.steps; i++) { + stat = chip->ecc.correct(mtd, buf, oobecc, + &tmpecc[i * chip->ecc.bytes]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + buf += chip->ecc.size; + oobecc += chip->ecc.bytes; + } + + return status; +} + +/* + * Read the data and OOB data from the device, no ECC correction with the + * data or OOB data + */ +static int lpc32xx_nand_read_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int oob_required, + int page) +{ + /* Issue read command */ + nand_read_page_op(chip, page, 0, NULL, 0); + + /* Raw reads can just use the FIFO interface */ + chip->read_buf(mtd, buf, chip->ecc.size * chip->ecc.steps); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +/* + * Write the data and OOB data to the device, use ECC with the data, + * disable ECC for the OOB data + */ +static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required, int page) +{ + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + struct mtd_oob_region oobregion = { }; + uint8_t *pb; + int error; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + /* Write data, calculate ECC on outbound data */ + error = lpc32xx_xfer(mtd, (uint8_t *)buf, chip->ecc.steps, 0); + if (error) + return error; + + /* + * The calculated ECC needs some manual work done to it before + * committing it to NAND. Process the calculated ECC and place + * the resultant values directly into the OOB buffer. */ + error = mtd_ooblayout_ecc(mtd, 0, &oobregion); + if (error) + return error; + + pb = chip->oob_poi + oobregion.offset; + lpc32xx_slc_ecc_copy(pb, (uint32_t *)host->ecc_buf, chip->ecc.steps); + + /* Write ECC data to device */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +/* + * Write the data and OOB data to the device, no ECC correction with the + * data or OOB data + */ +static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required, int page) +{ + /* Raw writes can just use the FIFO interface */ + nand_prog_page_begin_op(chip, page, 0, buf, + chip->ecc.size * chip->ecc.steps); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) +{ + struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); + dma_cap_mask_t mask; + + if (!host->pdata || !host->pdata->dma_filter) { + dev_err(mtd->dev.parent, "no DMA platform data\n"); + return -ENOENT; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, + "nand-slc"); + if (!host->dma_chan) { + dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); + return -EBUSY; + } + + return 0; +} + +static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev) +{ + struct lpc32xx_nand_cfg_slc *ncfg; + struct device_node *np = dev->of_node; + + ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL); + if (!ncfg) + return NULL; + + of_property_read_u32(np, "nxp,wdr-clks", &ncfg->wdr_clks); + of_property_read_u32(np, "nxp,wwidth", &ncfg->wwidth); + of_property_read_u32(np, "nxp,whold", &ncfg->whold); + of_property_read_u32(np, "nxp,wsetup", &ncfg->wsetup); + of_property_read_u32(np, "nxp,rdr-clks", &ncfg->rdr_clks); + of_property_read_u32(np, "nxp,rwidth", &ncfg->rwidth); + of_property_read_u32(np, "nxp,rhold", &ncfg->rhold); + of_property_read_u32(np, "nxp,rsetup", &ncfg->rsetup); + + if (!ncfg->wdr_clks || !ncfg->wwidth || !ncfg->whold || + !ncfg->wsetup || !ncfg->rdr_clks || !ncfg->rwidth || + !ncfg->rhold || !ncfg->rsetup) { + dev_err(dev, "chip parameters not specified correctly\n"); + return NULL; + } + + ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); + + return ncfg; +} + +/* + * Probe for NAND controller + */ +static int lpc32xx_nand_probe(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *chip; + struct resource *rc; + int res; + + /* Allocate memory for the device structure (and zero it) */ + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->io_base = devm_ioremap_resource(&pdev->dev, rc); + if (IS_ERR(host->io_base)) + return PTR_ERR(host->io_base); + + host->io_base_dma = rc->start; + if (pdev->dev.of_node) + host->ncfg = lpc32xx_parse_dt(&pdev->dev); + if (!host->ncfg) { + dev_err(&pdev->dev, + "Missing or bad NAND config from device tree\n"); + return -ENOENT; + } + if (host->ncfg->wp_gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(host->ncfg->wp_gpio) && devm_gpio_request(&pdev->dev, + host->ncfg->wp_gpio, "NAND WP")) { + dev_err(&pdev->dev, "GPIO not available\n"); + return -EBUSY; + } + lpc32xx_wp_disable(host); + + host->pdata = dev_get_platdata(&pdev->dev); + + chip = &host->nand_chip; + mtd = nand_to_mtd(chip); + nand_set_controller_data(chip, host); + nand_set_flash_node(chip, pdev->dev.of_node); + mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; + + /* Get NAND clock */ + host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) { + dev_err(&pdev->dev, "Clock failure\n"); + res = -ENOENT; + goto err_exit1; + } + res = clk_prepare_enable(host->clk); + if (res) + goto err_exit1; + + /* Set NAND IO addresses and command/ready functions */ + chip->IO_ADDR_R = SLC_DATA(host->io_base); + chip->IO_ADDR_W = SLC_DATA(host->io_base); + chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; + chip->dev_ready = lpc32xx_nand_device_ready; + chip->chip_delay = 20; /* 20us command delay time */ + + /* Init NAND controller */ + lpc32xx_nand_setup(host); + + platform_set_drvdata(pdev, host); + + /* NAND callbacks for LPC32xx SLC hardware */ + chip->ecc.mode = NAND_ECC_HW_SYNDROME; + chip->read_byte = lpc32xx_nand_read_byte; + chip->read_buf = lpc32xx_nand_read_buf; + chip->write_buf = lpc32xx_nand_write_buf; + chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; + chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; + chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; + chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; + chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; + chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; + chip->ecc.calculate = lpc32xx_nand_ecc_calculate; + chip->ecc.correct = nand_correct_data; + chip->ecc.strength = 1; + chip->ecc.hwctl = lpc32xx_nand_ecc_enable; + + /* + * Allocate a large enough buffer for a single huge page plus + * extra space for the spare area and ECC storage area + */ + host->dma_buf_len = LPC32XX_DMA_DATA_SIZE + LPC32XX_ECC_SAVE_SIZE; + host->data_buf = devm_kzalloc(&pdev->dev, host->dma_buf_len, + GFP_KERNEL); + if (host->data_buf == NULL) { + res = -ENOMEM; + goto err_exit2; + } + + res = lpc32xx_nand_dma_setup(host); + if (res) { + res = -EIO; + goto err_exit2; + } + + /* Find NAND device */ + res = nand_scan_ident(mtd, 1, NULL); + if (res) + goto err_exit3; + + /* OOB and ECC CPU and DMA work areas */ + host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); + + /* + * Small page FLASH has a unique OOB layout, but large and huge + * page FLASH use the standard layout. Small page FLASH uses a + * custom BBT marker layout. + */ + if (mtd->writesize <= 512) + mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); + + /* These sizes remain the same regardless of page size */ + chip->ecc.size = 256; + chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; + chip->ecc.prepad = chip->ecc.postpad = 0; + + /* + * Use a custom BBT marker setup for small page FLASH that + * won't interfere with the ECC layout. Large and huge page + * FLASH use the standard layout. + */ + if ((chip->bbt_options & NAND_BBT_USE_FLASH) && + mtd->writesize <= 512) { + chip->bbt_td = &bbt_smallpage_main_descr; + chip->bbt_md = &bbt_smallpage_mirror_descr; + } + + /* + * Fills out all the uninitialized function pointers with the defaults + */ + res = nand_scan_tail(mtd); + if (res) + goto err_exit3; + + mtd->name = "nxp_lpc3220_slc"; + res = mtd_device_register(mtd, host->ncfg->parts, + host->ncfg->num_parts); + if (!res) + return res; + + nand_release(mtd); + +err_exit3: + dma_release_channel(host->dma_chan); +err_exit2: + clk_disable_unprepare(host->clk); +err_exit1: + lpc32xx_wp_enable(host); + + return res; +} + +/* + * Remove NAND device. + */ +static int lpc32xx_nand_remove(struct platform_device *pdev) +{ + uint32_t tmp; + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); + + nand_release(mtd); + dma_release_channel(host->dma_chan); + + /* Force CE high */ + tmp = readl(SLC_CTRL(host->io_base)); + tmp &= ~SLCCFG_CE_LOW; + writel(tmp, SLC_CTRL(host->io_base)); + + clk_disable_unprepare(host->clk); + lpc32xx_wp_enable(host); + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_nand_resume(struct platform_device *pdev) +{ + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + int ret; + + /* Re-enable NAND clock */ + ret = clk_prepare_enable(host->clk); + if (ret) + return ret; + + /* Fresh init of NAND controller */ + lpc32xx_nand_setup(host); + + /* Disable write protect */ + lpc32xx_wp_disable(host); + + return 0; +} + +static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) +{ + uint32_t tmp; + struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + + /* Force CE high */ + tmp = readl(SLC_CTRL(host->io_base)); + tmp &= ~SLCCFG_CE_LOW; + writel(tmp, SLC_CTRL(host->io_base)); + + /* Enable write protect for safety */ + lpc32xx_wp_enable(host); + + /* Disable clock */ + clk_disable_unprepare(host->clk); + + return 0; +} + +#else +#define lpc32xx_nand_resume NULL +#define lpc32xx_nand_suspend NULL +#endif + +static const struct of_device_id lpc32xx_nand_match[] = { + { .compatible = "nxp,lpc3220-slc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); + +static struct platform_driver lpc32xx_nand_driver = { + .probe = lpc32xx_nand_probe, + .remove = lpc32xx_nand_remove, + .resume = lpc32xx_nand_resume, + .suspend = lpc32xx_nand_suspend, + .driver = { + .name = LPC32XX_MODNAME, + .of_match_table = lpc32xx_nand_match, + }, +}; + +module_platform_driver(lpc32xx_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kevin Wells "); +MODULE_AUTHOR("Roland Stigge "); +MODULE_DESCRIPTION("NAND driver for the NXP LPC32XX SLC controller"); diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c new file mode 100644 index 000000000000..cc21f9633edd --- /dev/null +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -0,0 +1,2915 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Marvell NAND flash controller driver + * + * Copyright (C) 2017 Marvell + * Author: Miquel RAYNAL + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Data FIFO granularity, FIFO reads/writes must be a multiple of this length */ +#define FIFO_DEPTH 8 +#define FIFO_REP(x) (x / sizeof(u32)) +#define BCH_SEQ_READS (32 / FIFO_DEPTH) +/* NFC does not support transfers of larger chunks at a time */ +#define MAX_CHUNK_SIZE 2112 +/* NFCv1 cannot read more that 7 bytes of ID */ +#define NFCV1_READID_LEN 7 +/* Polling is done at a pace of POLL_PERIOD us until POLL_TIMEOUT is reached */ +#define POLL_PERIOD 0 +#define POLL_TIMEOUT 100000 +/* Interrupt maximum wait period in ms */ +#define IRQ_TIMEOUT 1000 +/* Latency in clock cycles between SoC pins and NFC logic */ +#define MIN_RD_DEL_CNT 3 +/* Maximum number of contiguous address cycles */ +#define MAX_ADDRESS_CYC_NFCV1 5 +#define MAX_ADDRESS_CYC_NFCV2 7 +/* System control registers/bits to enable the NAND controller on some SoCs */ +#define GENCONF_SOC_DEVICE_MUX 0x208 +#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0) +#define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20) +#define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21) +#define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25) +#define GENCONF_CLK_GATING_CTRL 0x220 +#define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2) +#define GENCONF_ND_CLK_CTRL 0x700 +#define GENCONF_ND_CLK_CTRL_EN BIT(0) + +/* NAND controller data flash control register */ +#define NDCR 0x00 +#define NDCR_ALL_INT GENMASK(11, 0) +#define NDCR_CS1_CMDDM BIT(7) +#define NDCR_CS0_CMDDM BIT(8) +#define NDCR_RDYM BIT(11) +#define NDCR_ND_ARB_EN BIT(12) +#define NDCR_RA_START BIT(15) +#define NDCR_RD_ID_CNT(x) (min_t(unsigned int, x, 0x7) << 16) +#define NDCR_PAGE_SZ(x) (x >= 2048 ? BIT(24) : 0) +#define NDCR_DWIDTH_M BIT(26) +#define NDCR_DWIDTH_C BIT(27) +#define NDCR_ND_RUN BIT(28) +#define NDCR_DMA_EN BIT(29) +#define NDCR_ECC_EN BIT(30) +#define NDCR_SPARE_EN BIT(31) +#define NDCR_GENERIC_FIELDS_MASK (~(NDCR_RA_START | NDCR_PAGE_SZ(2048) | \ + NDCR_DWIDTH_M | NDCR_DWIDTH_C)) + +/* NAND interface timing parameter 0 register */ +#define NDTR0 0x04 +#define NDTR0_TRP(x) ((min_t(unsigned int, x, 0xF) & 0x7) << 0) +#define NDTR0_TRH(x) (min_t(unsigned int, x, 0x7) << 3) +#define NDTR0_ETRP(x) ((min_t(unsigned int, x, 0xF) & 0x8) << 3) +#define NDTR0_SEL_NRE_EDGE BIT(7) +#define NDTR0_TWP(x) (min_t(unsigned int, x, 0x7) << 8) +#define NDTR0_TWH(x) (min_t(unsigned int, x, 0x7) << 11) +#define NDTR0_TCS(x) (min_t(unsigned int, x, 0x7) << 16) +#define NDTR0_TCH(x) (min_t(unsigned int, x, 0x7) << 19) +#define NDTR0_RD_CNT_DEL(x) (min_t(unsigned int, x, 0xF) << 22) +#define NDTR0_SELCNTR BIT(26) +#define NDTR0_TADL(x) (min_t(unsigned int, x, 0x1F) << 27) + +/* NAND interface timing parameter 1 register */ +#define NDTR1 0x0C +#define NDTR1_TAR(x) (min_t(unsigned int, x, 0xF) << 0) +#define NDTR1_TWHR(x) (min_t(unsigned int, x, 0xF) << 4) +#define NDTR1_TRHW(x) (min_t(unsigned int, x / 16, 0x3) << 8) +#define NDTR1_PRESCALE BIT(14) +#define NDTR1_WAIT_MODE BIT(15) +#define NDTR1_TR(x) (min_t(unsigned int, x, 0xFFFF) << 16) + +/* NAND controller status register */ +#define NDSR 0x14 +#define NDSR_WRCMDREQ BIT(0) +#define NDSR_RDDREQ BIT(1) +#define NDSR_WRDREQ BIT(2) +#define NDSR_CORERR BIT(3) +#define NDSR_UNCERR BIT(4) +#define NDSR_CMDD(cs) BIT(8 - cs) +#define NDSR_RDY(rb) BIT(11 + rb) +#define NDSR_ERRCNT(x) ((x >> 16) & 0x1F) + +/* NAND ECC control register */ +#define NDECCCTRL 0x28 +#define NDECCCTRL_BCH_EN BIT(0) + +/* NAND controller data buffer register */ +#define NDDB 0x40 + +/* NAND controller command buffer 0 register */ +#define NDCB0 0x48 +#define NDCB0_CMD1(x) ((x & 0xFF) << 0) +#define NDCB0_CMD2(x) ((x & 0xFF) << 8) +#define NDCB0_ADDR_CYC(x) ((x & 0x7) << 16) +#define NDCB0_ADDR_GET_NUM_CYC(x) (((x) >> 16) & 0x7) +#define NDCB0_DBC BIT(19) +#define NDCB0_CMD_TYPE(x) ((x & 0x7) << 21) +#define NDCB0_CSEL BIT(24) +#define NDCB0_RDY_BYP BIT(27) +#define NDCB0_LEN_OVRD BIT(28) +#define NDCB0_CMD_XTYPE(x) ((x & 0x7) << 29) + +/* NAND controller command buffer 1 register */ +#define NDCB1 0x4C +#define NDCB1_COLS(x) ((x & 0xFFFF) << 0) +#define NDCB1_ADDRS_PAGE(x) (x << 16) + +/* NAND controller command buffer 2 register */ +#define NDCB2 0x50 +#define NDCB2_ADDR5_PAGE(x) (((x >> 16) & 0xFF) << 0) +#define NDCB2_ADDR5_CYC(x) ((x & 0xFF) << 0) + +/* NAND controller command buffer 3 register */ +#define NDCB3 0x54 +#define NDCB3_ADDR6_CYC(x) ((x & 0xFF) << 16) +#define NDCB3_ADDR7_CYC(x) ((x & 0xFF) << 24) + +/* NAND controller command buffer 0 register 'type' and 'xtype' fields */ +#define TYPE_READ 0 +#define TYPE_WRITE 1 +#define TYPE_ERASE 2 +#define TYPE_READ_ID 3 +#define TYPE_STATUS 4 +#define TYPE_RESET 5 +#define TYPE_NAKED_CMD 6 +#define TYPE_NAKED_ADDR 7 +#define TYPE_MASK 7 +#define XTYPE_MONOLITHIC_RW 0 +#define XTYPE_LAST_NAKED_RW 1 +#define XTYPE_FINAL_COMMAND 3 +#define XTYPE_READ 4 +#define XTYPE_WRITE_DISPATCH 4 +#define XTYPE_NAKED_RW 5 +#define XTYPE_COMMAND_DISPATCH 6 +#define XTYPE_MASK 7 + +/** + * Marvell ECC engine works differently than the others, in order to limit the + * size of the IP, hardware engineers chose to set a fixed strength at 16 bits + * per subpage, and depending on a the desired strength needed by the NAND chip, + * a particular layout mixing data/spare/ecc is defined, with a possible last + * chunk smaller that the others. + * + * @writesize: Full page size on which the layout applies + * @chunk: Desired ECC chunk size on which the layout applies + * @strength: Desired ECC strength (per chunk size bytes) on which the + * layout applies + * @nchunks: Total number of chunks + * @full_chunk_cnt: Number of full-sized chunks, which is the number of + * repetitions of the pattern: + * (data_bytes + spare_bytes + ecc_bytes). + * @data_bytes: Number of data bytes per chunk + * @spare_bytes: Number of spare bytes per chunk + * @ecc_bytes: Number of ecc bytes per chunk + * @last_data_bytes: Number of data bytes in the last chunk + * @last_spare_bytes: Number of spare bytes in the last chunk + * @last_ecc_bytes: Number of ecc bytes in the last chunk + */ +struct marvell_hw_ecc_layout { + /* Constraints */ + int writesize; + int chunk; + int strength; + /* Corresponding layout */ + int nchunks; + int full_chunk_cnt; + int data_bytes; + int spare_bytes; + int ecc_bytes; + int last_data_bytes; + int last_spare_bytes; + int last_ecc_bytes; +}; + +#define MARVELL_LAYOUT(ws, dc, ds, nc, fcc, db, sb, eb, ldb, lsb, leb) \ + { \ + .writesize = ws, \ + .chunk = dc, \ + .strength = ds, \ + .nchunks = nc, \ + .full_chunk_cnt = fcc, \ + .data_bytes = db, \ + .spare_bytes = sb, \ + .ecc_bytes = eb, \ + .last_data_bytes = ldb, \ + .last_spare_bytes = lsb, \ + .last_ecc_bytes = leb, \ + } + +/* Layouts explained in AN-379_Marvell_SoC_NFC_ECC */ +static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = { + MARVELL_LAYOUT( 512, 512, 1, 1, 1, 512, 8, 8, 0, 0, 0), + MARVELL_LAYOUT( 2048, 512, 1, 1, 1, 2048, 40, 24, 0, 0, 0), + MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0), + MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0), + MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30), +}; + +/** + * The Nand Flash Controller has up to 4 CE and 2 RB pins. The CE selection + * is made by a field in NDCB0 register, and in another field in NDCB2 register. + * The datasheet describes the logic with an error: ADDR5 field is once + * declared at the beginning of NDCB2, and another time at its end. Because the + * ADDR5 field of NDCB2 may be used by other bytes, it would be more logical + * to use the last bit of this field instead of the first ones. + * + * @cs: Wanted CE lane. + * @ndcb0_csel: Value of the NDCB0 register with or without the flag + * selecting the wanted CE lane. This is set once when + * the Device Tree is probed. + * @rb: Ready/Busy pin for the flash chip + */ +struct marvell_nand_chip_sel { + unsigned int cs; + u32 ndcb0_csel; + unsigned int rb; +}; + +/** + * NAND chip structure: stores NAND chip device related information + * + * @chip: Base NAND chip structure + * @node: Used to store NAND chips into a list + * @layout NAND layout when using hardware ECC + * @ndcr: Controller register value for this NAND chip + * @ndtr0: Timing registers 0 value for this NAND chip + * @ndtr1: Timing registers 1 value for this NAND chip + * @selected_die: Current active CS + * @nsels: Number of CS lines required by the NAND chip + * @sels: Array of CS lines descriptions + */ +struct marvell_nand_chip { + struct nand_chip chip; + struct list_head node; + const struct marvell_hw_ecc_layout *layout; + u32 ndcr; + u32 ndtr0; + u32 ndtr1; + int addr_cyc; + int selected_die; + unsigned int nsels; + struct marvell_nand_chip_sel sels[0]; +}; + +static inline struct marvell_nand_chip *to_marvell_nand(struct nand_chip *chip) +{ + return container_of(chip, struct marvell_nand_chip, chip); +} + +static inline struct marvell_nand_chip_sel *to_nand_sel(struct marvell_nand_chip + *nand) +{ + return &nand->sels[nand->selected_die]; +} + +/** + * NAND controller capabilities for distinction between compatible strings + * + * @max_cs_nb: Number of Chip Select lines available + * @max_rb_nb: Number of Ready/Busy lines available + * @need_system_controller: Indicates if the SoC needs to have access to the + * system controller (ie. to enable the NAND controller) + * @legacy_of_bindings: Indicates if DT parsing must be done using the old + * fashion way + * @is_nfcv2: NFCv2 has numerous enhancements compared to NFCv1, ie. + * BCH error detection and correction algorithm, + * NDCB3 register has been added + * @use_dma: Use dma for data transfers + */ +struct marvell_nfc_caps { + unsigned int max_cs_nb; + unsigned int max_rb_nb; + bool need_system_controller; + bool legacy_of_bindings; + bool is_nfcv2; + bool use_dma; +}; + +/** + * NAND controller structure: stores Marvell NAND controller information + * + * @controller: Base controller structure + * @dev: Parent device (used to print error messages) + * @regs: NAND controller registers + * @ecc_clk: ECC block clock, two times the NAND controller clock + * @complete: Completion object to wait for NAND controller events + * @assigned_cs: Bitmask describing already assigned CS lines + * @chips: List containing all the NAND chips attached to + * this NAND controller + * @caps: NAND controller capabilities for each compatible string + * @dma_chan: DMA channel (NFCv1 only) + * @dma_buf: 32-bit aligned buffer for DMA transfers (NFCv1 only) + */ +struct marvell_nfc { + struct nand_hw_control controller; + struct device *dev; + void __iomem *regs; + struct clk *ecc_clk; + struct completion complete; + unsigned long assigned_cs; + struct list_head chips; + struct nand_chip *selected_chip; + const struct marvell_nfc_caps *caps; + + /* DMA (NFCv1 only) */ + bool use_dma; + struct dma_chan *dma_chan; + u8 *dma_buf; +}; + +static inline struct marvell_nfc *to_marvell_nfc(struct nand_hw_control *ctrl) +{ + return container_of(ctrl, struct marvell_nfc, controller); +} + +/** + * NAND controller timings expressed in NAND Controller clock cycles + * + * @tRP: ND_nRE pulse width + * @tRH: ND_nRE high duration + * @tWP: ND_nWE pulse time + * @tWH: ND_nWE high duration + * @tCS: Enable signal setup time + * @tCH: Enable signal hold time + * @tADL: Address to write data delay + * @tAR: ND_ALE low to ND_nRE low delay + * @tWHR: ND_nWE high to ND_nRE low for status read + * @tRHW: ND_nRE high duration, read to write delay + * @tR: ND_nWE high to ND_nRE low for read + */ +struct marvell_nfc_timings { + /* NDTR0 fields */ + unsigned int tRP; + unsigned int tRH; + unsigned int tWP; + unsigned int tWH; + unsigned int tCS; + unsigned int tCH; + unsigned int tADL; + /* NDTR1 fields */ + unsigned int tAR; + unsigned int tWHR; + unsigned int tRHW; + unsigned int tR; +}; + +/** + * Derives a duration in numbers of clock cycles. + * + * @ps: Duration in pico-seconds + * @period_ns: Clock period in nano-seconds + * + * Convert the duration in nano-seconds, then divide by the period and + * return the number of clock periods. + */ +#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns)) +#define TO_CYCLES64(ps, period_ns) (DIV_ROUND_UP_ULL(div_u64(ps, 1000), \ + period_ns)) + +/** + * NAND driver structure filled during the parsing of the ->exec_op() subop + * subset of instructions. + * + * @ndcb: Array of values written to NDCBx registers + * @cle_ale_delay_ns: Optional delay after the last CMD or ADDR cycle + * @rdy_timeout_ms: Timeout for waits on Ready/Busy pin + * @rdy_delay_ns: Optional delay after waiting for the RB pin + * @data_delay_ns: Optional delay after the data xfer + * @data_instr_idx: Index of the data instruction in the subop + * @data_instr: Pointer to the data instruction in the subop + */ +struct marvell_nfc_op { + u32 ndcb[4]; + unsigned int cle_ale_delay_ns; + unsigned int rdy_timeout_ms; + unsigned int rdy_delay_ns; + unsigned int data_delay_ns; + unsigned int data_instr_idx; + const struct nand_op_instr *data_instr; +}; + +/* + * Internal helper to conditionnally apply a delay (from the above structure, + * most of the time). + */ +static void cond_delay(unsigned int ns) +{ + if (!ns) + return; + + if (ns < 10000) + ndelay(ns); + else + udelay(DIV_ROUND_UP(ns, 1000)); +} + +/* + * The controller has many flags that could generate interrupts, most of them + * are disabled and polling is used. For the very slow signals, using interrupts + * may relax the CPU charge. + */ +static void marvell_nfc_disable_int(struct marvell_nfc *nfc, u32 int_mask) +{ + u32 reg; + + /* Writing 1 disables the interrupt */ + reg = readl_relaxed(nfc->regs + NDCR); + writel_relaxed(reg | int_mask, nfc->regs + NDCR); +} + +static void marvell_nfc_enable_int(struct marvell_nfc *nfc, u32 int_mask) +{ + u32 reg; + + /* Writing 0 enables the interrupt */ + reg = readl_relaxed(nfc->regs + NDCR); + writel_relaxed(reg & ~int_mask, nfc->regs + NDCR); +} + +static void marvell_nfc_clear_int(struct marvell_nfc *nfc, u32 int_mask) +{ + writel_relaxed(int_mask, nfc->regs + NDSR); +} + +static void marvell_nfc_force_byte_access(struct nand_chip *chip, + bool force_8bit) +{ + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + u32 ndcr; + + /* + * Callers of this function do not verify if the NAND is using a 16-bit + * an 8-bit bus for normal operations, so we need to take care of that + * here by leaving the configuration unchanged if the NAND does not have + * the NAND_BUSWIDTH_16 flag set. + */ + if (!(chip->options & NAND_BUSWIDTH_16)) + return; + + ndcr = readl_relaxed(nfc->regs + NDCR); + + if (force_8bit) + ndcr &= ~(NDCR_DWIDTH_M | NDCR_DWIDTH_C); + else + ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C; + + writel_relaxed(ndcr, nfc->regs + NDCR); +} + +static int marvell_nfc_wait_ndrun(struct nand_chip *chip) +{ + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + u32 val; + int ret; + + /* + * The command is being processed, wait for the ND_RUN bit to be + * cleared by the NFC. If not, we must clear it by hand. + */ + ret = readl_relaxed_poll_timeout(nfc->regs + NDCR, val, + (val & NDCR_ND_RUN) == 0, + POLL_PERIOD, POLL_TIMEOUT); + if (ret) { + dev_err(nfc->dev, "Timeout on NAND controller run mode\n"); + writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN, + nfc->regs + NDCR); + return ret; + } + + return 0; +} + +/* + * Any time a command has to be sent to the controller, the following sequence + * has to be followed: + * - call marvell_nfc_prepare_cmd() + * -> activate the ND_RUN bit that will kind of 'start a job' + * -> wait the signal indicating the NFC is waiting for a command + * - send the command (cmd and address cycles) + * - enventually send or receive the data + * - call marvell_nfc_end_cmd() with the corresponding flag + * -> wait the flag to be triggered or cancel the job with a timeout + * + * The following helpers are here to factorize the code a bit so that + * specialized functions responsible for executing the actual NAND + * operations do not have to replicate the same code blocks. + */ +static int marvell_nfc_prepare_cmd(struct nand_chip *chip) +{ + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + u32 ndcr, val; + int ret; + + /* Poll ND_RUN and clear NDSR before issuing any command */ + ret = marvell_nfc_wait_ndrun(chip); + if (ret) { + dev_err(nfc->dev, "Last operation did not succeed\n"); + return ret; + } + + ndcr = readl_relaxed(nfc->regs + NDCR); + writel_relaxed(readl(nfc->regs + NDSR), nfc->regs + NDSR); + + /* Assert ND_RUN bit and wait the NFC to be ready */ + writel_relaxed(ndcr | NDCR_ND_RUN, nfc->regs + NDCR); + ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val, + val & NDSR_WRCMDREQ, + POLL_PERIOD, POLL_TIMEOUT); + if (ret) { + dev_err(nfc->dev, "Timeout on WRCMDRE\n"); + return -ETIMEDOUT; + } + + /* Command may be written, clear WRCMDREQ status bit */ + writel_relaxed(NDSR_WRCMDREQ, nfc->regs + NDSR); + + return 0; +} + +static void marvell_nfc_send_cmd(struct nand_chip *chip, + struct marvell_nfc_op *nfc_op) +{ + struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + + dev_dbg(nfc->dev, "\nNDCR: 0x%08x\n" + "NDCB0: 0x%08x\nNDCB1: 0x%08x\nNDCB2: 0x%08x\nNDCB3: 0x%08x\n", + (u32)readl_relaxed(nfc->regs + NDCR), nfc_op->ndcb[0], + nfc_op->ndcb[1], nfc_op->ndcb[2], nfc_op->ndcb[3]); + + writel_relaxed(to_nand_sel(marvell_nand)->ndcb0_csel | nfc_op->ndcb[0], + nfc->regs + NDCB0); + writel_relaxed(nfc_op->ndcb[1], nfc->regs + NDCB0); + writel(nfc_op->ndcb[2], nfc->regs + NDCB0); + + /* + * Write NDCB0 four times only if LEN_OVRD is set or if ADDR6 or ADDR7 + * fields are used (only available on NFCv2). + */ + if (nfc_op->ndcb[0] & NDCB0_LEN_OVRD || + NDCB0_ADDR_GET_NUM_CYC(nfc_op->ndcb[0]) >= 6) { + if (!WARN_ON_ONCE(!nfc->caps->is_nfcv2)) + writel(nfc_op->ndcb[3], nfc->regs + NDCB0); + } +} + +static int marvell_nfc_end_cmd(struct nand_chip *chip, int flag, + const char *label) +{ + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + u32 val; + int ret; + + ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val, + val & flag, + POLL_PERIOD, POLL_TIMEOUT); + + if (ret) { + dev_err(nfc->dev, "Timeout on %s (NDSR: 0x%08x)\n", + label, val); + if (nfc->dma_chan) + dmaengine_terminate_all(nfc->dma_chan); + return ret; + } + + /* + * DMA function uses this helper to poll on CMDD bits without wanting + * them to be cleared. + */ + if (nfc->use_dma && (readl_relaxed(nfc->regs + NDCR) & NDCR_DMA_EN)) + return 0; + + writel_relaxed(flag, nfc->regs + NDSR); + + return 0; +} + +static int marvell_nfc_wait_cmdd(struct nand_chip *chip) +{ + struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); + int cs_flag = NDSR_CMDD(to_nand_sel(marvell_nand)->ndcb0_csel); + + return marvell_nfc_end_cmd(chip, cs_flag, "CMDD"); +} + +static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms) +{ + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + int ret; + + /* Timeout is expressed in ms */ + if (!timeout_ms) + timeout_ms = IRQ_TIMEOUT; + + init_completion(&nfc->complete); + + marvell_nfc_enable_int(nfc, NDCR_RDYM); + ret = wait_for_completion_timeout(&nfc->complete, + msecs_to_jiffies(timeout_ms)); + marvell_nfc_disable_int(nfc, NDCR_RDYM); + marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1)); + if (!ret) { + dev_err(nfc->dev, "Timeout waiting for RB signal\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void marvell_nfc_select_chip(struct mtd_info *mtd, int die_nr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + u32 ndcr_generic; + + if (chip == nfc->selected_chip && die_nr == marvell_nand->selected_die) + return; + + if (die_nr < 0 || die_nr >= marvell_nand->nsels) { + nfc->selected_chip = NULL; + marvell_nand->selected_die = -1; + return; + } + + /* + * Do not change the timing registers when using the DT property + * marvell,nand-keep-config; in that case ->ndtr0 and ->ndtr1 from the + * marvell_nand structure are supposedly empty. + */ + writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0); + writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1); + + /* + * Reset the NDCR register to a clean state for this particular chip, + * also clear ND_RUN bit. + */ + ndcr_generic = readl_relaxed(nfc->regs + NDCR) & + NDCR_GENERIC_FIELDS_MASK & ~NDCR_ND_RUN; + writel_relaxed(ndcr_generic | marvell_nand->ndcr, nfc->regs + NDCR); + + /* Also reset the interrupt status register */ + marvell_nfc_clear_int(nfc, NDCR_ALL_INT); + + nfc->selected_chip = chip; + marvell_nand->selected_die = die_nr; +} + +static irqreturn_t marvell_nfc_isr(int irq, void *dev_id) +{ + struct marvell_nfc *nfc = dev_id; + u32 st = readl_relaxed(nfc->regs + NDSR); + u32 ien = (~readl_relaxed(nfc->regs + NDCR)) & NDCR_ALL_INT; + + /* + * RDY interrupt mask is one bit in NDCR while there are two status + * bit in NDSR (RDY[cs0/cs2] and RDY[cs1/cs3]). + */ + if (st & NDSR_RDY(1)) + st |= NDSR_RDY(0); + + if (!(st & ien)) + return IRQ_NONE; + + marvell_nfc_disable_int(nfc, st & NDCR_ALL_INT); + + if (!(st & (NDSR_RDDREQ | NDSR_WRDREQ | NDSR_WRCMDREQ))) + complete(&nfc->complete); + + return IRQ_HANDLED; +} + +/* HW ECC related functions */ +static void marvell_nfc_enable_hw_ecc(struct nand_chip *chip) +{ + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + u32 ndcr = readl_relaxed(nfc->regs + NDCR); + + if (!(ndcr & NDCR_ECC_EN)) { + writel_relaxed(ndcr | NDCR_ECC_EN, nfc->regs + NDCR); + + /* + * When enabling BCH, set threshold to 0 to always know the + * number of corrected bitflips. + */ + if (chip->ecc.algo == NAND_ECC_BCH) + writel_relaxed(NDECCCTRL_BCH_EN, nfc->regs + NDECCCTRL); + } +} + +static void marvell_nfc_disable_hw_ecc(struct nand_chip *chip) +{ + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + u32 ndcr = readl_relaxed(nfc->regs + NDCR); + + if (ndcr & NDCR_ECC_EN) { + writel_relaxed(ndcr & ~NDCR_ECC_EN, nfc->regs + NDCR); + if (chip->ecc.algo == NAND_ECC_BCH) + writel_relaxed(0, nfc->regs + NDECCCTRL); + } +} + +/* DMA related helpers */ +static void marvell_nfc_enable_dma(struct marvell_nfc *nfc) +{ + u32 reg; + + reg = readl_relaxed(nfc->regs + NDCR); + writel_relaxed(reg | NDCR_DMA_EN, nfc->regs + NDCR); +} + +static void marvell_nfc_disable_dma(struct marvell_nfc *nfc) +{ + u32 reg; + + reg = readl_relaxed(nfc->regs + NDCR); + writel_relaxed(reg & ~NDCR_DMA_EN, nfc->regs + NDCR); +} + +/* Read/write PIO/DMA accessors */ +static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc, + enum dma_data_direction direction, + unsigned int len) +{ + unsigned int dma_len = min_t(int, ALIGN(len, 32), MAX_CHUNK_SIZE); + struct dma_async_tx_descriptor *tx; + struct scatterlist sg; + dma_cookie_t cookie; + int ret; + + marvell_nfc_enable_dma(nfc); + /* Prepare the DMA transfer */ + sg_init_one(&sg, nfc->dma_buf, dma_len); + dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction); + tx = dmaengine_prep_slave_sg(nfc->dma_chan, &sg, 1, + direction == DMA_FROM_DEVICE ? + DMA_DEV_TO_MEM : DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(nfc->dev, "Could not prepare DMA S/G list\n"); + return -ENXIO; + } + + /* Do the task and wait for it to finish */ + cookie = dmaengine_submit(tx); + ret = dma_submit_error(cookie); + if (ret) + return -EIO; + + dma_async_issue_pending(nfc->dma_chan); + ret = marvell_nfc_wait_cmdd(nfc->selected_chip); + dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction); + marvell_nfc_disable_dma(nfc); + if (ret) { + dev_err(nfc->dev, "Timeout waiting for DMA (status: %d)\n", + dmaengine_tx_status(nfc->dma_chan, cookie, NULL)); + dmaengine_terminate_all(nfc->dma_chan); + return -ETIMEDOUT; + } + + return 0; +} + +static int marvell_nfc_xfer_data_in_pio(struct marvell_nfc *nfc, u8 *in, + unsigned int len) +{ + unsigned int last_len = len % FIFO_DEPTH; + unsigned int last_full_offset = round_down(len, FIFO_DEPTH); + int i; + + for (i = 0; i < last_full_offset; i += FIFO_DEPTH) + ioread32_rep(nfc->regs + NDDB, in + i, FIFO_REP(FIFO_DEPTH)); + + if (last_len) { + u8 tmp_buf[FIFO_DEPTH]; + + ioread32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH)); + memcpy(in + last_full_offset, tmp_buf, last_len); + } + + return 0; +} + +static int marvell_nfc_xfer_data_out_pio(struct marvell_nfc *nfc, const u8 *out, + unsigned int len) +{ + unsigned int last_len = len % FIFO_DEPTH; + unsigned int last_full_offset = round_down(len, FIFO_DEPTH); + int i; + + for (i = 0; i < last_full_offset; i += FIFO_DEPTH) + iowrite32_rep(nfc->regs + NDDB, out + i, FIFO_REP(FIFO_DEPTH)); + + if (last_len) { + u8 tmp_buf[FIFO_DEPTH]; + + memcpy(tmp_buf, out + last_full_offset, last_len); + iowrite32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH)); + } + + return 0; +} + +static void marvell_nfc_check_empty_chunk(struct nand_chip *chip, + u8 *data, int data_len, + u8 *spare, int spare_len, + u8 *ecc, int ecc_len, + unsigned int *max_bitflips) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int bf; + + /* + * Blank pages (all 0xFF) that have not been written may be recognized + * as bad if bitflips occur, so whenever an uncorrectable error occurs, + * check if the entire page (with ECC bytes) is actually blank or not. + */ + if (!data) + data_len = 0; + if (!spare) + spare_len = 0; + if (!ecc) + ecc_len = 0; + + bf = nand_check_erased_ecc_chunk(data, data_len, ecc, ecc_len, + spare, spare_len, chip->ecc.strength); + if (bf < 0) { + mtd->ecc_stats.failed++; + return; + } + + /* Update the stats and max_bitflips */ + mtd->ecc_stats.corrected += bf; + *max_bitflips = max_t(unsigned int, *max_bitflips, bf); +} + +/* + * Check a chunk is correct or not according to hardware ECC engine. + * mtd->ecc_stats.corrected is updated, as well as max_bitflips, however + * mtd->ecc_stats.failure is not, the function will instead return a non-zero + * value indicating that a check on the emptyness of the subpage must be + * performed before declaring the subpage corrupted. + */ +static int marvell_nfc_hw_ecc_correct(struct nand_chip *chip, + unsigned int *max_bitflips) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + int bf = 0; + u32 ndsr; + + ndsr = readl_relaxed(nfc->regs + NDSR); + + /* Check uncorrectable error flag */ + if (ndsr & NDSR_UNCERR) { + writel_relaxed(ndsr, nfc->regs + NDSR); + + /* + * Do not increment ->ecc_stats.failed now, instead, return a + * non-zero value to indicate that this chunk was apparently + * bad, and it should be check to see if it empty or not. If + * the chunk (with ECC bytes) is not declared empty, the calling + * function must increment the failure count. + */ + return -EBADMSG; + } + + /* Check correctable error flag */ + if (ndsr & NDSR_CORERR) { + writel_relaxed(ndsr, nfc->regs + NDSR); + + if (chip->ecc.algo == NAND_ECC_BCH) + bf = NDSR_ERRCNT(ndsr); + else + bf = 1; + } + + /* Update the stats and max_bitflips */ + mtd->ecc_stats.corrected += bf; + *max_bitflips = max_t(unsigned int, *max_bitflips, bf); + + return 0; +} + +/* Hamming read helpers */ +static int marvell_nfc_hw_ecc_hmg_do_read_page(struct nand_chip *chip, + u8 *data_buf, u8 *oob_buf, + bool raw, int page) +{ + struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + struct marvell_nfc_op nfc_op = { + .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) | + NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | + NDCB0_DBC | + NDCB0_CMD1(NAND_CMD_READ0) | + NDCB0_CMD2(NAND_CMD_READSTART), + .ndcb[1] = NDCB1_ADDRS_PAGE(page), + .ndcb[2] = NDCB2_ADDR5_PAGE(page), + }; + unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0); + int ret; + + /* NFCv2 needs more information about the operation being executed */ + if (nfc->caps->is_nfcv2) + nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW); + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return ret; + + marvell_nfc_send_cmd(chip, &nfc_op); + ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ, + "RDDREQ while draining FIFO (data/oob)"); + if (ret) + return ret; + + /* + * Read the page then the OOB area. Unlike what is shown in current + * documentation, spare bytes are protected by the ECC engine, and must + * be at the beginning of the OOB area or running this driver on legacy + * systems will prevent the discovery of the BBM/BBT. + */ + if (nfc->use_dma) { + marvell_nfc_xfer_data_dma(nfc, DMA_FROM_DEVICE, + lt->data_bytes + oob_bytes); + memcpy(data_buf, nfc->dma_buf, lt->data_bytes); + memcpy(oob_buf, nfc->dma_buf + lt->data_bytes, oob_bytes); + } else { + marvell_nfc_xfer_data_in_pio(nfc, data_buf, lt->data_bytes); + marvell_nfc_xfer_data_in_pio(nfc, oob_buf, oob_bytes); + } + + ret = marvell_nfc_wait_cmdd(chip); + + return ret; +} + +static int marvell_nfc_hw_ecc_hmg_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + return marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, + true, page); +} + +static int marvell_nfc_hw_ecc_hmg_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + u8 *buf, int oob_required, + int page) +{ + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + unsigned int full_sz = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes; + int max_bitflips = 0, ret; + u8 *raw_buf; + + marvell_nfc_enable_hw_ecc(chip); + marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, false, + page); + ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips); + marvell_nfc_disable_hw_ecc(chip); + + if (!ret) + return max_bitflips; + + /* + * When ECC failures are detected, check if the full page has been + * written or not. Ignore the failure if it is actually empty. + */ + raw_buf = kmalloc(full_sz, GFP_KERNEL); + if (!raw_buf) + return -ENOMEM; + + marvell_nfc_hw_ecc_hmg_do_read_page(chip, raw_buf, raw_buf + + lt->data_bytes, true, page); + marvell_nfc_check_empty_chunk(chip, raw_buf, full_sz, NULL, 0, NULL, 0, + &max_bitflips); + kfree(raw_buf); + + return max_bitflips; +} + +/* + * Spare area in Hamming layouts is not protected by the ECC engine (even if + * it appears before the ECC bytes when reading), the ->read_oob_raw() function + * also stands for ->read_oob(). + */ +static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + /* Invalidate page cache */ + chip->pagebuf = -1; + + return marvell_nfc_hw_ecc_hmg_do_read_page(chip, chip->data_buf, + chip->oob_poi, true, page); +} + +/* Hamming write helpers */ +static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip, + const u8 *data_buf, + const u8 *oob_buf, bool raw, + int page) +{ + struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + struct marvell_nfc_op nfc_op = { + .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) | + NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | + NDCB0_CMD1(NAND_CMD_SEQIN) | + NDCB0_CMD2(NAND_CMD_PAGEPROG) | + NDCB0_DBC, + .ndcb[1] = NDCB1_ADDRS_PAGE(page), + .ndcb[2] = NDCB2_ADDR5_PAGE(page), + }; + unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0); + int ret; + + /* NFCv2 needs more information about the operation being executed */ + if (nfc->caps->is_nfcv2) + nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW); + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return ret; + + marvell_nfc_send_cmd(chip, &nfc_op); + ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ, + "WRDREQ while loading FIFO (data)"); + if (ret) + return ret; + + /* Write the page then the OOB area */ + if (nfc->use_dma) { + memcpy(nfc->dma_buf, data_buf, lt->data_bytes); + memcpy(nfc->dma_buf + lt->data_bytes, oob_buf, oob_bytes); + marvell_nfc_xfer_data_dma(nfc, DMA_TO_DEVICE, lt->data_bytes + + lt->ecc_bytes + lt->spare_bytes); + } else { + marvell_nfc_xfer_data_out_pio(nfc, data_buf, lt->data_bytes); + marvell_nfc_xfer_data_out_pio(nfc, oob_buf, oob_bytes); + } + + ret = marvell_nfc_wait_cmdd(chip); + if (ret) + return ret; + + ret = marvell_nfc_wait_op(chip, + chip->data_interface.timings.sdr.tPROG_max); + return ret; +} + +static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, + int oob_required, int page) +{ + return marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi, + true, page); +} + +static int marvell_nfc_hw_ecc_hmg_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, + int oob_required, int page) +{ + int ret; + + marvell_nfc_enable_hw_ecc(chip); + ret = marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi, + false, page); + marvell_nfc_disable_hw_ecc(chip); + + return ret; +} + +/* + * Spare area in Hamming layouts is not protected by the ECC engine (even if + * it appears before the ECC bytes when reading), the ->write_oob_raw() function + * also stands for ->write_oob(). + */ +static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct mtd_info *mtd, + struct nand_chip *chip, + int page) +{ + /* Invalidate page cache */ + chip->pagebuf = -1; + + memset(chip->data_buf, 0xFF, mtd->writesize); + + return marvell_nfc_hw_ecc_hmg_do_write_page(chip, chip->data_buf, + chip->oob_poi, true, page); +} + +/* BCH read helpers */ +static int marvell_nfc_hw_ecc_bch_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + u8 *oob = chip->oob_poi; + int chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes; + int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) + + lt->last_spare_bytes; + int data_len = lt->data_bytes; + int spare_len = lt->spare_bytes; + int ecc_len = lt->ecc_bytes; + int chunk; + + if (oob_required) + memset(chip->oob_poi, 0xFF, mtd->oobsize); + + nand_read_page_op(chip, page, 0, NULL, 0); + + for (chunk = 0; chunk < lt->nchunks; chunk++) { + /* Update last chunk length */ + if (chunk >= lt->full_chunk_cnt) { + data_len = lt->last_data_bytes; + spare_len = lt->last_spare_bytes; + ecc_len = lt->last_ecc_bytes; + } + + /* Read data bytes*/ + nand_change_read_column_op(chip, chunk * chunk_size, + buf + (lt->data_bytes * chunk), + data_len, false); + + /* Read spare bytes */ + nand_read_data_op(chip, oob + (lt->spare_bytes * chunk), + spare_len, false); + + /* Read ECC bytes */ + nand_read_data_op(chip, oob + ecc_offset + + (ALIGN(lt->ecc_bytes, 32) * chunk), + ecc_len, false); + } + + return 0; +} + +static void marvell_nfc_hw_ecc_bch_read_chunk(struct nand_chip *chip, int chunk, + u8 *data, unsigned int data_len, + u8 *spare, unsigned int spare_len, + int page) +{ + struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + int i, ret; + struct marvell_nfc_op nfc_op = { + .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) | + NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | + NDCB0_LEN_OVRD, + .ndcb[1] = NDCB1_ADDRS_PAGE(page), + .ndcb[2] = NDCB2_ADDR5_PAGE(page), + .ndcb[3] = data_len + spare_len, + }; + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return; + + if (chunk == 0) + nfc_op.ndcb[0] |= NDCB0_DBC | + NDCB0_CMD1(NAND_CMD_READ0) | + NDCB0_CMD2(NAND_CMD_READSTART); + + /* + * Trigger the naked read operation only on the last chunk. + * Otherwise, use monolithic read. + */ + if (lt->nchunks == 1 || (chunk < lt->nchunks - 1)) + nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW); + else + nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); + + marvell_nfc_send_cmd(chip, &nfc_op); + + /* + * According to the datasheet, when reading from NDDB + * with BCH enabled, after each 32 bytes reads, we + * have to make sure that the NDSR.RDDREQ bit is set. + * + * Drain the FIFO, 8 32-bit reads at a time, and skip + * the polling on the last read. + * + * Length is a multiple of 32 bytes, hence it is a multiple of 8 too. + */ + for (i = 0; i < data_len; i += FIFO_DEPTH * BCH_SEQ_READS) { + marvell_nfc_end_cmd(chip, NDSR_RDDREQ, + "RDDREQ while draining FIFO (data)"); + marvell_nfc_xfer_data_in_pio(nfc, data, + FIFO_DEPTH * BCH_SEQ_READS); + data += FIFO_DEPTH * BCH_SEQ_READS; + } + + for (i = 0; i < spare_len; i += FIFO_DEPTH * BCH_SEQ_READS) { + marvell_nfc_end_cmd(chip, NDSR_RDDREQ, + "RDDREQ while draining FIFO (OOB)"); + marvell_nfc_xfer_data_in_pio(nfc, spare, + FIFO_DEPTH * BCH_SEQ_READS); + spare += FIFO_DEPTH * BCH_SEQ_READS; + } +} + +static int marvell_nfc_hw_ecc_bch_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + u8 *buf, int oob_required, + int page) +{ + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + int data_len = lt->data_bytes, spare_len = lt->spare_bytes, ecc_len; + u8 *data = buf, *spare = chip->oob_poi, *ecc; + int max_bitflips = 0; + u32 failure_mask = 0; + int chunk, ecc_offset_in_page, ret; + + /* + * With BCH, OOB is not fully used (and thus not read entirely), not + * expected bytes could show up at the end of the OOB buffer if not + * explicitly erased. + */ + if (oob_required) + memset(chip->oob_poi, 0xFF, mtd->oobsize); + + marvell_nfc_enable_hw_ecc(chip); + + for (chunk = 0; chunk < lt->nchunks; chunk++) { + /* Update length for the last chunk */ + if (chunk >= lt->full_chunk_cnt) { + data_len = lt->last_data_bytes; + spare_len = lt->last_spare_bytes; + } + + /* Read the chunk and detect number of bitflips */ + marvell_nfc_hw_ecc_bch_read_chunk(chip, chunk, data, data_len, + spare, spare_len, page); + ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips); + if (ret) + failure_mask |= BIT(chunk); + + data += data_len; + spare += spare_len; + } + + marvell_nfc_disable_hw_ecc(chip); + + if (!failure_mask) + return max_bitflips; + + /* + * Please note that dumping the ECC bytes during a normal read with OOB + * area would add a significant overhead as ECC bytes are "consumed" by + * the controller in normal mode and must be re-read in raw mode. To + * avoid dropping the performances, we prefer not to include them. The + * user should re-read the page in raw mode if ECC bytes are required. + * + * However, for any subpage read error reported by ->correct(), the ECC + * bytes must be read in raw mode and the full subpage must be checked + * to see if it is entirely empty of if there was an actual error. + */ + for (chunk = 0; chunk < lt->nchunks; chunk++) { + /* No failure reported for this chunk, move to the next one */ + if (!(failure_mask & BIT(chunk))) + continue; + + /* Derive ECC bytes positions (in page/buffer) and length */ + ecc = chip->oob_poi + + (lt->full_chunk_cnt * lt->spare_bytes) + + lt->last_spare_bytes + + (chunk * ALIGN(lt->ecc_bytes, 32)); + ecc_offset_in_page = + (chunk * (lt->data_bytes + lt->spare_bytes + + lt->ecc_bytes)) + + (chunk < lt->full_chunk_cnt ? + lt->data_bytes + lt->spare_bytes : + lt->last_data_bytes + lt->last_spare_bytes); + ecc_len = chunk < lt->full_chunk_cnt ? + lt->ecc_bytes : lt->last_ecc_bytes; + + /* Do the actual raw read of the ECC bytes */ + nand_change_read_column_op(chip, ecc_offset_in_page, + ecc, ecc_len, false); + + /* Derive data/spare bytes positions (in buffer) and length */ + data = buf + (chunk * lt->data_bytes); + data_len = chunk < lt->full_chunk_cnt ? + lt->data_bytes : lt->last_data_bytes; + spare = chip->oob_poi + (chunk * (lt->spare_bytes + + lt->ecc_bytes)); + spare_len = chunk < lt->full_chunk_cnt ? + lt->spare_bytes : lt->last_spare_bytes; + + /* Check the entire chunk (data + spare + ecc) for emptyness */ + marvell_nfc_check_empty_chunk(chip, data, data_len, spare, + spare_len, ecc, ecc_len, + &max_bitflips); + } + + return max_bitflips; +} + +static int marvell_nfc_hw_ecc_bch_read_oob_raw(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + /* Invalidate page cache */ + chip->pagebuf = -1; + + return chip->ecc.read_page_raw(mtd, chip, chip->data_buf, true, page); +} + +static int marvell_nfc_hw_ecc_bch_read_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + /* Invalidate page cache */ + chip->pagebuf = -1; + + return chip->ecc.read_page(mtd, chip, chip->data_buf, true, page); +} + +/* BCH write helpers */ +static int marvell_nfc_hw_ecc_bch_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, + int oob_required, int page) +{ + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + int full_chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes; + int data_len = lt->data_bytes; + int spare_len = lt->spare_bytes; + int ecc_len = lt->ecc_bytes; + int spare_offset = 0; + int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) + + lt->last_spare_bytes; + int chunk; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + for (chunk = 0; chunk < lt->nchunks; chunk++) { + if (chunk >= lt->full_chunk_cnt) { + data_len = lt->last_data_bytes; + spare_len = lt->last_spare_bytes; + ecc_len = lt->last_ecc_bytes; + } + + /* Point to the column of the next chunk */ + nand_change_write_column_op(chip, chunk * full_chunk_size, + NULL, 0, false); + + /* Write the data */ + nand_write_data_op(chip, buf + (chunk * lt->data_bytes), + data_len, false); + + if (!oob_required) + continue; + + /* Write the spare bytes */ + if (spare_len) + nand_write_data_op(chip, chip->oob_poi + spare_offset, + spare_len, false); + + /* Write the ECC bytes */ + if (ecc_len) + nand_write_data_op(chip, chip->oob_poi + ecc_offset, + ecc_len, false); + + spare_offset += spare_len; + ecc_offset += ALIGN(ecc_len, 32); + } + + return nand_prog_page_end_op(chip); +} + +static int +marvell_nfc_hw_ecc_bch_write_chunk(struct nand_chip *chip, int chunk, + const u8 *data, unsigned int data_len, + const u8 *spare, unsigned int spare_len, + int page) +{ + struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + int ret; + struct marvell_nfc_op nfc_op = { + .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) | NDCB0_LEN_OVRD, + .ndcb[3] = data_len + spare_len, + }; + + /* + * First operation dispatches the CMD_SEQIN command, issue the address + * cycles and asks for the first chunk of data. + * All operations in the middle (if any) will issue a naked write and + * also ask for data. + * Last operation (if any) asks for the last chunk of data through a + * last naked write. + */ + if (chunk == 0) { + nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_WRITE_DISPATCH) | + NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | + NDCB0_CMD1(NAND_CMD_SEQIN); + nfc_op.ndcb[1] |= NDCB1_ADDRS_PAGE(page); + nfc_op.ndcb[2] |= NDCB2_ADDR5_PAGE(page); + } else if (chunk < lt->nchunks - 1) { + nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_NAKED_RW); + } else { + nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); + } + + /* Always dispatch the PAGEPROG command on the last chunk */ + if (chunk == lt->nchunks - 1) + nfc_op.ndcb[0] |= NDCB0_CMD2(NAND_CMD_PAGEPROG) | NDCB0_DBC; + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return ret; + + marvell_nfc_send_cmd(chip, &nfc_op); + ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ, + "WRDREQ while loading FIFO (data)"); + if (ret) + return ret; + + /* Transfer the contents */ + iowrite32_rep(nfc->regs + NDDB, data, FIFO_REP(data_len)); + iowrite32_rep(nfc->regs + NDDB, spare, FIFO_REP(spare_len)); + + return 0; +} + +static int marvell_nfc_hw_ecc_bch_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, + int oob_required, int page) +{ + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + const u8 *data = buf; + const u8 *spare = chip->oob_poi; + int data_len = lt->data_bytes; + int spare_len = lt->spare_bytes; + int chunk, ret; + + /* Spare data will be written anyway, so clear it to avoid garbage */ + if (!oob_required) + memset(chip->oob_poi, 0xFF, mtd->oobsize); + + marvell_nfc_enable_hw_ecc(chip); + + for (chunk = 0; chunk < lt->nchunks; chunk++) { + if (chunk >= lt->full_chunk_cnt) { + data_len = lt->last_data_bytes; + spare_len = lt->last_spare_bytes; + } + + marvell_nfc_hw_ecc_bch_write_chunk(chip, chunk, data, data_len, + spare, spare_len, page); + data += data_len; + spare += spare_len; + + /* + * Waiting only for CMDD or PAGED is not enough, ECC are + * partially written. No flag is set once the operation is + * really finished but the ND_RUN bit is cleared, so wait for it + * before stepping into the next command. + */ + marvell_nfc_wait_ndrun(chip); + } + + ret = marvell_nfc_wait_op(chip, + chip->data_interface.timings.sdr.tPROG_max); + + marvell_nfc_disable_hw_ecc(chip); + + if (ret) + return ret; + + return 0; +} + +static int marvell_nfc_hw_ecc_bch_write_oob_raw(struct mtd_info *mtd, + struct nand_chip *chip, + int page) +{ + /* Invalidate page cache */ + chip->pagebuf = -1; + + memset(chip->data_buf, 0xFF, mtd->writesize); + + return chip->ecc.write_page_raw(mtd, chip, chip->data_buf, true, page); +} + +static int marvell_nfc_hw_ecc_bch_write_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + /* Invalidate page cache */ + chip->pagebuf = -1; + + memset(chip->data_buf, 0xFF, mtd->writesize); + + return chip->ecc.write_page(mtd, chip, chip->data_buf, true, page); +} + +/* NAND framework ->exec_op() hooks and related helpers */ +static void marvell_nfc_parse_instructions(struct nand_chip *chip, + const struct nand_subop *subop, + struct marvell_nfc_op *nfc_op) +{ + const struct nand_op_instr *instr = NULL; + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + bool first_cmd = true; + unsigned int op_id; + int i; + + /* Reset the input structure as most of its fields will be OR'ed */ + memset(nfc_op, 0, sizeof(struct marvell_nfc_op)); + + for (op_id = 0; op_id < subop->ninstrs; op_id++) { + unsigned int offset, naddrs; + const u8 *addrs; + int len = nand_subop_get_data_len(subop, op_id); + + instr = &subop->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + if (first_cmd) + nfc_op->ndcb[0] |= + NDCB0_CMD1(instr->ctx.cmd.opcode); + else + nfc_op->ndcb[0] |= + NDCB0_CMD2(instr->ctx.cmd.opcode) | + NDCB0_DBC; + + nfc_op->cle_ale_delay_ns = instr->delay_ns; + first_cmd = false; + break; + + case NAND_OP_ADDR_INSTR: + offset = nand_subop_get_addr_start_off(subop, op_id); + naddrs = nand_subop_get_num_addr_cyc(subop, op_id); + addrs = &instr->ctx.addr.addrs[offset]; + + nfc_op->ndcb[0] |= NDCB0_ADDR_CYC(naddrs); + + for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) + nfc_op->ndcb[1] |= addrs[i] << (8 * i); + + if (naddrs >= 5) + nfc_op->ndcb[2] |= NDCB2_ADDR5_CYC(addrs[4]); + if (naddrs >= 6) + nfc_op->ndcb[3] |= NDCB3_ADDR6_CYC(addrs[5]); + if (naddrs == 7) + nfc_op->ndcb[3] |= NDCB3_ADDR7_CYC(addrs[6]); + + nfc_op->cle_ale_delay_ns = instr->delay_ns; + break; + + case NAND_OP_DATA_IN_INSTR: + nfc_op->data_instr = instr; + nfc_op->data_instr_idx = op_id; + nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ); + if (nfc->caps->is_nfcv2) { + nfc_op->ndcb[0] |= + NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) | + NDCB0_LEN_OVRD; + nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH); + } + nfc_op->data_delay_ns = instr->delay_ns; + break; + + case NAND_OP_DATA_OUT_INSTR: + nfc_op->data_instr = instr; + nfc_op->data_instr_idx = op_id; + nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE); + if (nfc->caps->is_nfcv2) { + nfc_op->ndcb[0] |= + NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) | + NDCB0_LEN_OVRD; + nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH); + } + nfc_op->data_delay_ns = instr->delay_ns; + break; + + case NAND_OP_WAITRDY_INSTR: + nfc_op->rdy_timeout_ms = instr->ctx.waitrdy.timeout_ms; + nfc_op->rdy_delay_ns = instr->delay_ns; + break; + } + } +} + +static int marvell_nfc_xfer_data_pio(struct nand_chip *chip, + const struct nand_subop *subop, + struct marvell_nfc_op *nfc_op) +{ + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + const struct nand_op_instr *instr = nfc_op->data_instr; + unsigned int op_id = nfc_op->data_instr_idx; + unsigned int len = nand_subop_get_data_len(subop, op_id); + unsigned int offset = nand_subop_get_data_start_off(subop, op_id); + bool reading = (instr->type == NAND_OP_DATA_IN_INSTR); + int ret; + + if (instr->ctx.data.force_8bit) + marvell_nfc_force_byte_access(chip, true); + + if (reading) { + u8 *in = instr->ctx.data.buf.in + offset; + + ret = marvell_nfc_xfer_data_in_pio(nfc, in, len); + } else { + const u8 *out = instr->ctx.data.buf.out + offset; + + ret = marvell_nfc_xfer_data_out_pio(nfc, out, len); + } + + if (instr->ctx.data.force_8bit) + marvell_nfc_force_byte_access(chip, false); + + return ret; +} + +static int marvell_nfc_monolithic_access_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct marvell_nfc_op nfc_op; + bool reading; + int ret; + + marvell_nfc_parse_instructions(chip, subop, &nfc_op); + reading = (nfc_op.data_instr->type == NAND_OP_DATA_IN_INSTR); + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return ret; + + marvell_nfc_send_cmd(chip, &nfc_op); + ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ, + "RDDREQ/WRDREQ while draining raw data"); + if (ret) + return ret; + + cond_delay(nfc_op.cle_ale_delay_ns); + + if (reading) { + if (nfc_op.rdy_timeout_ms) { + ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + return ret; + } + + cond_delay(nfc_op.rdy_delay_ns); + } + + marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); + ret = marvell_nfc_wait_cmdd(chip); + if (ret) + return ret; + + cond_delay(nfc_op.data_delay_ns); + + if (!reading) { + if (nfc_op.rdy_timeout_ms) { + ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + return ret; + } + + cond_delay(nfc_op.rdy_delay_ns); + } + + /* + * NDCR ND_RUN bit should be cleared automatically at the end of each + * operation but experience shows that the behavior is buggy when it + * comes to writes (with LEN_OVRD). Clear it by hand in this case. + */ + if (!reading) { + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + + writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN, + nfc->regs + NDCR); + } + + return 0; +} + +static int marvell_nfc_naked_access_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct marvell_nfc_op nfc_op; + int ret; + + marvell_nfc_parse_instructions(chip, subop, &nfc_op); + + /* + * Naked access are different in that they need to be flagged as naked + * by the controller. Reset the controller registers fields that inform + * on the type and refill them according to the ongoing operation. + */ + nfc_op.ndcb[0] &= ~(NDCB0_CMD_TYPE(TYPE_MASK) | + NDCB0_CMD_XTYPE(XTYPE_MASK)); + switch (subop->instrs[0].type) { + case NAND_OP_CMD_INSTR: + nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_CMD); + break; + case NAND_OP_ADDR_INSTR: + nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_ADDR); + break; + case NAND_OP_DATA_IN_INSTR: + nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ) | + NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); + break; + case NAND_OP_DATA_OUT_INSTR: + nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE) | + NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); + break; + default: + /* This should never happen */ + break; + } + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return ret; + + marvell_nfc_send_cmd(chip, &nfc_op); + + if (!nfc_op.data_instr) { + ret = marvell_nfc_wait_cmdd(chip); + cond_delay(nfc_op.cle_ale_delay_ns); + return ret; + } + + ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ, + "RDDREQ/WRDREQ while draining raw data"); + if (ret) + return ret; + + marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); + ret = marvell_nfc_wait_cmdd(chip); + if (ret) + return ret; + + /* + * NDCR ND_RUN bit should be cleared automatically at the end of each + * operation but experience shows that the behavior is buggy when it + * comes to writes (with LEN_OVRD). Clear it by hand in this case. + */ + if (subop->instrs[0].type == NAND_OP_DATA_OUT_INSTR) { + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + + writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN, + nfc->regs + NDCR); + } + + return 0; +} + +static int marvell_nfc_naked_waitrdy_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct marvell_nfc_op nfc_op; + int ret; + + marvell_nfc_parse_instructions(chip, subop, &nfc_op); + + ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + cond_delay(nfc_op.rdy_delay_ns); + + return ret; +} + +static int marvell_nfc_read_id_type_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct marvell_nfc_op nfc_op; + int ret; + + marvell_nfc_parse_instructions(chip, subop, &nfc_op); + nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ); + nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ_ID); + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return ret; + + marvell_nfc_send_cmd(chip, &nfc_op); + ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ, + "RDDREQ while reading ID"); + if (ret) + return ret; + + cond_delay(nfc_op.cle_ale_delay_ns); + + if (nfc_op.rdy_timeout_ms) { + ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + return ret; + } + + cond_delay(nfc_op.rdy_delay_ns); + + marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); + ret = marvell_nfc_wait_cmdd(chip); + if (ret) + return ret; + + cond_delay(nfc_op.data_delay_ns); + + return 0; +} + +static int marvell_nfc_read_status_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct marvell_nfc_op nfc_op; + int ret; + + marvell_nfc_parse_instructions(chip, subop, &nfc_op); + nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ); + nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_STATUS); + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return ret; + + marvell_nfc_send_cmd(chip, &nfc_op); + ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ, + "RDDREQ while reading status"); + if (ret) + return ret; + + cond_delay(nfc_op.cle_ale_delay_ns); + + if (nfc_op.rdy_timeout_ms) { + ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + return ret; + } + + cond_delay(nfc_op.rdy_delay_ns); + + marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); + ret = marvell_nfc_wait_cmdd(chip); + if (ret) + return ret; + + cond_delay(nfc_op.data_delay_ns); + + return 0; +} + +static int marvell_nfc_reset_cmd_type_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct marvell_nfc_op nfc_op; + int ret; + + marvell_nfc_parse_instructions(chip, subop, &nfc_op); + nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_RESET); + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return ret; + + marvell_nfc_send_cmd(chip, &nfc_op); + ret = marvell_nfc_wait_cmdd(chip); + if (ret) + return ret; + + cond_delay(nfc_op.cle_ale_delay_ns); + + ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + return ret; + + cond_delay(nfc_op.rdy_delay_ns); + + return 0; +} + +static int marvell_nfc_erase_cmd_type_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct marvell_nfc_op nfc_op; + int ret; + + marvell_nfc_parse_instructions(chip, subop, &nfc_op); + nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_ERASE); + + ret = marvell_nfc_prepare_cmd(chip); + if (ret) + return ret; + + marvell_nfc_send_cmd(chip, &nfc_op); + ret = marvell_nfc_wait_cmdd(chip); + if (ret) + return ret; + + cond_delay(nfc_op.cle_ale_delay_ns); + + ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); + if (ret) + return ret; + + cond_delay(nfc_op.rdy_delay_ns); + + return 0; +} + +static const struct nand_op_parser marvell_nfcv2_op_parser = NAND_OP_PARSER( + /* Monolithic reads/writes */ + NAND_OP_PARSER_PATTERN( + marvell_nfc_monolithic_access_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(true, MAX_ADDRESS_CYC_NFCV2), + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)), + NAND_OP_PARSER_PATTERN( + marvell_nfc_monolithic_access_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2), + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE), + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), + /* Naked commands */ + NAND_OP_PARSER_PATTERN( + marvell_nfc_naked_access_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false)), + NAND_OP_PARSER_PATTERN( + marvell_nfc_naked_access_exec, + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2)), + NAND_OP_PARSER_PATTERN( + marvell_nfc_naked_access_exec, + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)), + NAND_OP_PARSER_PATTERN( + marvell_nfc_naked_access_exec, + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE)), + NAND_OP_PARSER_PATTERN( + marvell_nfc_naked_waitrdy_exec, + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + ); + +static const struct nand_op_parser marvell_nfcv1_op_parser = NAND_OP_PARSER( + /* Naked commands not supported, use a function for each pattern */ + NAND_OP_PARSER_PATTERN( + marvell_nfc_read_id_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)), + NAND_OP_PARSER_PATTERN( + marvell_nfc_erase_cmd_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1), + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + NAND_OP_PARSER_PATTERN( + marvell_nfc_read_status_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)), + NAND_OP_PARSER_PATTERN( + marvell_nfc_reset_cmd_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + NAND_OP_PARSER_PATTERN( + marvell_nfc_naked_waitrdy_exec, + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + ); + +static int marvell_nfc_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + + if (nfc->caps->is_nfcv2) + return nand_op_parser_exec_op(chip, &marvell_nfcv2_op_parser, + op, check_only); + else + return nand_op_parser_exec_op(chip, &marvell_nfcv1_op_parser, + op, check_only); +} + +/* + * Layouts were broken in old pxa3xx_nand driver, these are supposed to be + * usable. + */ +static int marvell_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + + if (section) + return -ERANGE; + + oobregion->length = (lt->full_chunk_cnt * lt->ecc_bytes) + + lt->last_ecc_bytes; + oobregion->offset = mtd->oobsize - oobregion->length; + + return 0; +} + +static int marvell_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; + + if (section) + return -ERANGE; + + /* + * Bootrom looks in bytes 0 & 5 for bad blocks for the + * 4KB page / 4bit BCH combination. + */ + if (mtd->writesize == SZ_4K && lt->data_bytes == SZ_2K) + oobregion->offset = 6; + else + oobregion->offset = 2; + + oobregion->length = (lt->full_chunk_cnt * lt->spare_bytes) + + lt->last_spare_bytes - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops marvell_nand_ooblayout_ops = { + .ecc = marvell_nand_ooblayout_ecc, + .free = marvell_nand_ooblayout_free, +}; + +static int marvell_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + const struct marvell_hw_ecc_layout *l; + int i; + + if (!nfc->caps->is_nfcv2 && + (mtd->writesize + mtd->oobsize > MAX_CHUNK_SIZE)) { + dev_err(nfc->dev, + "NFCv1: writesize (%d) cannot be bigger than a chunk (%d)\n", + mtd->writesize, MAX_CHUNK_SIZE - mtd->oobsize); + return -ENOTSUPP; + } + + to_marvell_nand(chip)->layout = NULL; + for (i = 0; i < ARRAY_SIZE(marvell_nfc_layouts); i++) { + l = &marvell_nfc_layouts[i]; + if (mtd->writesize == l->writesize && + ecc->size == l->chunk && ecc->strength == l->strength) { + to_marvell_nand(chip)->layout = l; + break; + } + } + + if (!to_marvell_nand(chip)->layout || + (!nfc->caps->is_nfcv2 && ecc->strength > 1)) { + dev_err(nfc->dev, + "ECC strength %d at page size %d is not supported\n", + ecc->strength, mtd->writesize); + return -ENOTSUPP; + } + + mtd_set_ooblayout(mtd, &marvell_nand_ooblayout_ops); + ecc->steps = l->nchunks; + ecc->size = l->data_bytes; + + if (ecc->strength == 1) { + chip->ecc.algo = NAND_ECC_HAMMING; + ecc->read_page_raw = marvell_nfc_hw_ecc_hmg_read_page_raw; + ecc->read_page = marvell_nfc_hw_ecc_hmg_read_page; + ecc->read_oob_raw = marvell_nfc_hw_ecc_hmg_read_oob_raw; + ecc->read_oob = ecc->read_oob_raw; + ecc->write_page_raw = marvell_nfc_hw_ecc_hmg_write_page_raw; + ecc->write_page = marvell_nfc_hw_ecc_hmg_write_page; + ecc->write_oob_raw = marvell_nfc_hw_ecc_hmg_write_oob_raw; + ecc->write_oob = ecc->write_oob_raw; + } else { + chip->ecc.algo = NAND_ECC_BCH; + ecc->strength = 16; + ecc->read_page_raw = marvell_nfc_hw_ecc_bch_read_page_raw; + ecc->read_page = marvell_nfc_hw_ecc_bch_read_page; + ecc->read_oob_raw = marvell_nfc_hw_ecc_bch_read_oob_raw; + ecc->read_oob = marvell_nfc_hw_ecc_bch_read_oob; + ecc->write_page_raw = marvell_nfc_hw_ecc_bch_write_page_raw; + ecc->write_page = marvell_nfc_hw_ecc_bch_write_page; + ecc->write_oob_raw = marvell_nfc_hw_ecc_bch_write_oob_raw; + ecc->write_oob = marvell_nfc_hw_ecc_bch_write_oob; + } + + return 0; +} + +static int marvell_nand_ecc_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + int ret; + + if (ecc->mode != NAND_ECC_NONE && (!ecc->size || !ecc->strength)) { + if (chip->ecc_step_ds && chip->ecc_strength_ds) { + ecc->size = chip->ecc_step_ds; + ecc->strength = chip->ecc_strength_ds; + } else { + dev_info(nfc->dev, + "No minimum ECC strength, using 1b/512B\n"); + ecc->size = 512; + ecc->strength = 1; + } + } + + switch (ecc->mode) { + case NAND_ECC_HW: + ret = marvell_nand_hw_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + break; + case NAND_ECC_NONE: + case NAND_ECC_SOFT: + if (!nfc->caps->is_nfcv2 && mtd->writesize != SZ_512 && + mtd->writesize != SZ_2K) { + dev_err(nfc->dev, "NFCv1 cannot write %d bytes pages\n", + mtd->writesize); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; +static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_mirror_pattern +}; + +static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface + *conf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + unsigned int period_ns = 1000000000 / clk_get_rate(nfc->ecc_clk) * 2; + const struct nand_sdr_timings *sdr; + struct marvell_nfc_timings nfc_tmg; + int read_delay; + + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + /* + * SDR timings are given in pico-seconds while NFC timings must be + * expressed in NAND controller clock cycles, which is half of the + * frequency of the accessible ECC clock retrieved by clk_get_rate(). + * This is not written anywhere in the datasheet but was observed + * with an oscilloscope. + * + * NFC datasheet gives equations from which thoses calculations + * are derived, they tend to be slightly more restrictives than the + * given core timings and may improve the overall speed. + */ + nfc_tmg.tRP = TO_CYCLES(DIV_ROUND_UP(sdr->tRC_min, 2), period_ns) - 1; + nfc_tmg.tRH = nfc_tmg.tRP; + nfc_tmg.tWP = TO_CYCLES(DIV_ROUND_UP(sdr->tWC_min, 2), period_ns) - 1; + nfc_tmg.tWH = nfc_tmg.tWP; + nfc_tmg.tCS = TO_CYCLES(sdr->tCS_min, period_ns); + nfc_tmg.tCH = TO_CYCLES(sdr->tCH_min, period_ns) - 1; + nfc_tmg.tADL = TO_CYCLES(sdr->tADL_min, period_ns); + /* + * Read delay is the time of propagation from SoC pins to NFC internal + * logic. With non-EDO timings, this is MIN_RD_DEL_CNT clock cycles. In + * EDO mode, an additional delay of tRH must be taken into account so + * the data is sampled on the falling edge instead of the rising edge. + */ + read_delay = sdr->tRC_min >= 30000 ? + MIN_RD_DEL_CNT : MIN_RD_DEL_CNT + nfc_tmg.tRH; + + nfc_tmg.tAR = TO_CYCLES(sdr->tAR_min, period_ns); + /* + * tWHR and tRHW are supposed to be read to write delays (and vice + * versa) but in some cases, ie. when doing a change column, they must + * be greater than that to be sure tCCS delay is respected. + */ + nfc_tmg.tWHR = TO_CYCLES(max_t(int, sdr->tWHR_min, sdr->tCCS_min), + period_ns) - 2, + nfc_tmg.tRHW = TO_CYCLES(max_t(int, sdr->tRHW_min, sdr->tCCS_min), + period_ns); + + /* + * NFCv2: Use WAIT_MODE (wait for RB line), do not rely only on delays. + * NFCv1: No WAIT_MODE, tR must be maximal. + */ + if (nfc->caps->is_nfcv2) { + nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns); + } else { + nfc_tmg.tR = TO_CYCLES64(sdr->tWB_max + sdr->tR_max, + period_ns); + if (nfc_tmg.tR + 3 > nfc_tmg.tCH) + nfc_tmg.tR = nfc_tmg.tCH - 3; + else + nfc_tmg.tR = 0; + } + + if (chipnr < 0) + return 0; + + marvell_nand->ndtr0 = + NDTR0_TRP(nfc_tmg.tRP) | + NDTR0_TRH(nfc_tmg.tRH) | + NDTR0_ETRP(nfc_tmg.tRP) | + NDTR0_TWP(nfc_tmg.tWP) | + NDTR0_TWH(nfc_tmg.tWH) | + NDTR0_TCS(nfc_tmg.tCS) | + NDTR0_TCH(nfc_tmg.tCH); + + marvell_nand->ndtr1 = + NDTR1_TAR(nfc_tmg.tAR) | + NDTR1_TWHR(nfc_tmg.tWHR) | + NDTR1_TR(nfc_tmg.tR); + + if (nfc->caps->is_nfcv2) { + marvell_nand->ndtr0 |= + NDTR0_RD_CNT_DEL(read_delay) | + NDTR0_SELCNTR | + NDTR0_TADL(nfc_tmg.tADL); + + marvell_nand->ndtr1 |= + NDTR1_TRHW(nfc_tmg.tRHW) | + NDTR1_WAIT_MODE; + } + + return 0; +} + +static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, + struct device_node *np) +{ + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(dev); + struct marvell_nand_chip *marvell_nand; + struct mtd_info *mtd; + struct nand_chip *chip; + int nsels, ret, i; + u32 cs, rb; + + /* + * The legacy "num-cs" property indicates the number of CS on the only + * chip connected to the controller (legacy bindings does not support + * more than one chip). CS are only incremented one by one while the RB + * pin is always the #0. + * + * When not using legacy bindings, a couple of "reg" and "nand-rb" + * properties must be filled. For each chip, expressed as a subnode, + * "reg" points to the CS lines and "nand-rb" to the RB line. + */ + if (pdata) { + nsels = 1; + } else if (nfc->caps->legacy_of_bindings && + !of_get_property(np, "num-cs", &nsels)) { + dev_err(dev, "missing num-cs property\n"); + return -EINVAL; + } else if (!of_get_property(np, "reg", &nsels)) { + dev_err(dev, "missing reg property\n"); + return -EINVAL; + } + + if (!pdata) + nsels /= sizeof(u32); + if (!nsels) { + dev_err(dev, "invalid reg property size\n"); + return -EINVAL; + } + + /* Alloc the nand chip structure */ + marvell_nand = devm_kzalloc(dev, sizeof(*marvell_nand) + + (nsels * + sizeof(struct marvell_nand_chip_sel)), + GFP_KERNEL); + if (!marvell_nand) { + dev_err(dev, "could not allocate chip structure\n"); + return -ENOMEM; + } + + marvell_nand->nsels = nsels; + marvell_nand->selected_die = -1; + + for (i = 0; i < nsels; i++) { + if (pdata || nfc->caps->legacy_of_bindings) { + /* + * Legacy bindings use the CS lines in natural + * order (0, 1, ...) + */ + cs = i; + } else { + /* Retrieve CS id */ + ret = of_property_read_u32_index(np, "reg", i, &cs); + if (ret) { + dev_err(dev, "could not retrieve reg property: %d\n", + ret); + return ret; + } + } + + if (cs >= nfc->caps->max_cs_nb) { + dev_err(dev, "invalid reg value: %u (max CS = %d)\n", + cs, nfc->caps->max_cs_nb); + return -EINVAL; + } + + if (test_and_set_bit(cs, &nfc->assigned_cs)) { + dev_err(dev, "CS %d already assigned\n", cs); + return -EINVAL; + } + + /* + * The cs variable represents the chip select id, which must be + * converted in bit fields for NDCB0 and NDCB2 to select the + * right chip. Unfortunately, due to a lack of information on + * the subject and incoherent documentation, the user should not + * use CS1 and CS3 at all as asserting them is not supported in + * a reliable way (due to multiplexing inside ADDR5 field). + */ + marvell_nand->sels[i].cs = cs; + switch (cs) { + case 0: + case 2: + marvell_nand->sels[i].ndcb0_csel = 0; + break; + case 1: + case 3: + marvell_nand->sels[i].ndcb0_csel = NDCB0_CSEL; + break; + default: + return -EINVAL; + } + + /* Retrieve RB id */ + if (pdata || nfc->caps->legacy_of_bindings) { + /* Legacy bindings always use RB #0 */ + rb = 0; + } else { + ret = of_property_read_u32_index(np, "nand-rb", i, + &rb); + if (ret) { + dev_err(dev, + "could not retrieve RB property: %d\n", + ret); + return ret; + } + } + + if (rb >= nfc->caps->max_rb_nb) { + dev_err(dev, "invalid reg value: %u (max RB = %d)\n", + rb, nfc->caps->max_rb_nb); + return -EINVAL; + } + + marvell_nand->sels[i].rb = rb; + } + + chip = &marvell_nand->chip; + chip->controller = &nfc->controller; + nand_set_flash_node(chip, np); + + chip->exec_op = marvell_nfc_exec_op; + chip->select_chip = marvell_nfc_select_chip; + if (!of_property_read_bool(np, "marvell,nand-keep-config")) + chip->setup_data_interface = marvell_nfc_setup_data_interface; + + mtd = nand_to_mtd(chip); + mtd->dev.parent = dev; + + /* + * Default to HW ECC engine mode. If the nand-ecc-mode property is given + * in the DT node, this entry will be overwritten in nand_scan_ident(). + */ + chip->ecc.mode = NAND_ECC_HW; + + /* + * Save a reference value for timing registers before + * ->setup_data_interface() is called. + */ + marvell_nand->ndtr0 = readl_relaxed(nfc->regs + NDTR0); + marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1); + + chip->options |= NAND_BUSWIDTH_AUTO; + ret = nand_scan_ident(mtd, marvell_nand->nsels, NULL); + if (ret) { + dev_err(dev, "could not identify the nand chip\n"); + return ret; + } + + if (pdata && pdata->flash_bbt) + chip->bbt_options |= NAND_BBT_USE_FLASH; + + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + /* + * We'll use a bad block table stored in-flash and don't + * allow writing the bad block marker to the flash. + */ + chip->bbt_options |= NAND_BBT_NO_OOB_BBM; + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + } + + /* Save the chip-specific fields of NDCR */ + marvell_nand->ndcr = NDCR_PAGE_SZ(mtd->writesize); + if (chip->options & NAND_BUSWIDTH_16) + marvell_nand->ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C; + + /* + * On small page NANDs, only one cycle is needed to pass the + * column address. + */ + if (mtd->writesize <= 512) { + marvell_nand->addr_cyc = 1; + } else { + marvell_nand->addr_cyc = 2; + marvell_nand->ndcr |= NDCR_RA_START; + } + + /* + * Now add the number of cycles needed to pass the row + * address. + * + * Addressing a chip using CS 2 or 3 should also need the third row + * cycle but due to inconsistance in the documentation and lack of + * hardware to test this situation, this case is not supported. + */ + if (chip->options & NAND_ROW_ADDR_3) + marvell_nand->addr_cyc += 3; + else + marvell_nand->addr_cyc += 2; + + if (pdata) { + chip->ecc.size = pdata->ecc_step_size; + chip->ecc.strength = pdata->ecc_strength; + } + + ret = marvell_nand_ecc_init(mtd, &chip->ecc); + if (ret) { + dev_err(dev, "ECC init failed: %d\n", ret); + return ret; + } + + if (chip->ecc.mode == NAND_ECC_HW) { + /* + * Subpage write not available with hardware ECC, prohibit also + * subpage read as in userspace subpage access would still be + * allowed and subpage write, if used, would lead to numerous + * uncorrectable ECC errors. + */ + chip->options |= NAND_NO_SUBPAGE_WRITE; + } + + if (pdata || nfc->caps->legacy_of_bindings) { + /* + * We keep the MTD name unchanged to avoid breaking platforms + * where the MTD cmdline parser is used and the bootloader + * has not been updated to use the new naming scheme. + */ + mtd->name = "pxa3xx_nand-0"; + } else if (!mtd->name) { + /* + * If the new bindings are used and the bootloader has not been + * updated to pass a new mtdparts parameter on the cmdline, you + * should define the following property in your NAND node, ie: + * + * label = "main-storage"; + * + * This way, mtd->name will be set by the core when + * nand_set_flash_node() is called. + */ + mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL, + "%s:nand.%d", dev_name(nfc->dev), + marvell_nand->sels[0].cs); + if (!mtd->name) { + dev_err(nfc->dev, "Failed to allocate mtd->name\n"); + return -ENOMEM; + } + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "nand_scan_tail failed: %d\n", ret); + return ret; + } + + if (pdata) + /* Legacy bindings support only one chip */ + ret = mtd_device_register(mtd, pdata->parts[0], + pdata->nr_parts[0]); + else + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(dev, "failed to register mtd device: %d\n", ret); + nand_release(mtd); + return ret; + } + + list_add_tail(&marvell_nand->node, &nfc->chips); + + return 0; +} + +static int marvell_nand_chips_init(struct device *dev, struct marvell_nfc *nfc) +{ + struct device_node *np = dev->of_node; + struct device_node *nand_np; + int max_cs = nfc->caps->max_cs_nb; + int nchips; + int ret; + + if (!np) + nchips = 1; + else + nchips = of_get_child_count(np); + + if (nchips > max_cs) { + dev_err(dev, "too many NAND chips: %d (max = %d CS)\n", nchips, + max_cs); + return -EINVAL; + } + + /* + * Legacy bindings do not use child nodes to exhibit NAND chip + * properties and layout. Instead, NAND properties are mixed with the + * controller ones, and partitions are defined as direct subnodes of the + * NAND controller node. + */ + if (nfc->caps->legacy_of_bindings) { + ret = marvell_nand_chip_init(dev, nfc, np); + return ret; + } + + for_each_child_of_node(np, nand_np) { + ret = marvell_nand_chip_init(dev, nfc, nand_np); + if (ret) { + of_node_put(nand_np); + return ret; + } + } + + return 0; +} + +static void marvell_nand_chips_cleanup(struct marvell_nfc *nfc) +{ + struct marvell_nand_chip *entry, *temp; + + list_for_each_entry_safe(entry, temp, &nfc->chips, node) { + nand_release(nand_to_mtd(&entry->chip)); + list_del(&entry->node); + } +} + +static int marvell_nfc_init_dma(struct marvell_nfc *nfc) +{ + struct platform_device *pdev = container_of(nfc->dev, + struct platform_device, + dev); + struct dma_slave_config config = {}; + struct resource *r; + dma_cap_mask_t mask; + struct pxad_param param; + int ret; + + if (!IS_ENABLED(CONFIG_PXA_DMA)) { + dev_warn(nfc->dev, + "DMA not enabled in configuration\n"); + return -ENOTSUPP; + } + + ret = dma_set_mask_and_coherent(nfc->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) { + dev_err(nfc->dev, "No resource defined for data DMA\n"); + return -ENXIO; + } + + param.drcmr = r->start; + param.prio = PXAD_PRIO_LOWEST; + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + nfc->dma_chan = + dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m, nfc->dev, + "data"); + if (!nfc->dma_chan) { + dev_err(nfc->dev, + "Unable to request data DMA channel\n"); + return -ENODEV; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + return -ENXIO; + + config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.src_addr = r->start + NDDB; + config.dst_addr = r->start + NDDB; + config.src_maxburst = 32; + config.dst_maxburst = 32; + ret = dmaengine_slave_config(nfc->dma_chan, &config); + if (ret < 0) { + dev_err(nfc->dev, "Failed to configure DMA channel\n"); + return ret; + } + + /* + * DMA must act on length multiple of 32 and this length may be + * bigger than the destination buffer. Use this buffer instead + * for DMA transfers and then copy the desired amount of data to + * the provided buffer. + */ + nfc->dma_buf = kmalloc(MAX_CHUNK_SIZE, GFP_KERNEL | GFP_DMA); + if (!nfc->dma_buf) + return -ENOMEM; + + nfc->use_dma = true; + + return 0; +} + +static int marvell_nfc_init(struct marvell_nfc *nfc) +{ + struct device_node *np = nfc->dev->of_node; + + /* + * Some SoCs like A7k/A8k need to enable manually the NAND + * controller, gated clocks and reset bits to avoid being bootloader + * dependent. This is done through the use of the System Functions + * registers. + */ + if (nfc->caps->need_system_controller) { + struct regmap *sysctrl_base = + syscon_regmap_lookup_by_phandle(np, + "marvell,system-controller"); + u32 reg; + + if (IS_ERR(sysctrl_base)) + return PTR_ERR(sysctrl_base); + + reg = GENCONF_SOC_DEVICE_MUX_NFC_EN | + GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST | + GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST | + GENCONF_SOC_DEVICE_MUX_NFC_INT_EN; + regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg); + + regmap_read(sysctrl_base, GENCONF_CLK_GATING_CTRL, ®); + reg |= GENCONF_CLK_GATING_CTRL_ND_GATE; + regmap_write(sysctrl_base, GENCONF_CLK_GATING_CTRL, reg); + + regmap_read(sysctrl_base, GENCONF_ND_CLK_CTRL, ®); + reg |= GENCONF_ND_CLK_CTRL_EN; + regmap_write(sysctrl_base, GENCONF_ND_CLK_CTRL, reg); + } + + /* Configure the DMA if appropriate */ + if (!nfc->caps->is_nfcv2) + marvell_nfc_init_dma(nfc); + + /* + * ECC operations and interruptions are only enabled when specifically + * needed. ECC shall not be activated in the early stages (fails probe). + * Arbiter flag, even if marked as "reserved", must be set (empirical). + * SPARE_EN bit must always be set or ECC bytes will not be at the same + * offset in the read page and this will fail the protection. + */ + writel_relaxed(NDCR_ALL_INT | NDCR_ND_ARB_EN | NDCR_SPARE_EN | + NDCR_RD_ID_CNT(NFCV1_READID_LEN), nfc->regs + NDCR); + writel_relaxed(0xFFFFFFFF, nfc->regs + NDSR); + writel_relaxed(0, nfc->regs + NDECCCTRL); + + return 0; +} + +static int marvell_nfc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *r; + struct marvell_nfc *nfc; + int ret; + int irq; + + nfc = devm_kzalloc(&pdev->dev, sizeof(struct marvell_nfc), + GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->dev = dev; + nand_hw_control_init(&nfc->controller); + INIT_LIST_HEAD(&nfc->chips); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->regs = devm_ioremap_resource(dev, r); + if (IS_ERR(nfc->regs)) + return PTR_ERR(nfc->regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "failed to retrieve irq\n"); + return irq; + } + + nfc->ecc_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(nfc->ecc_clk)) + return PTR_ERR(nfc->ecc_clk); + + ret = clk_prepare_enable(nfc->ecc_clk); + if (ret) + return ret; + + marvell_nfc_disable_int(nfc, NDCR_ALL_INT); + marvell_nfc_clear_int(nfc, NDCR_ALL_INT); + ret = devm_request_irq(dev, irq, marvell_nfc_isr, + 0, "marvell-nfc", nfc); + if (ret) + goto unprepare_clk; + + /* Get NAND controller capabilities */ + if (pdev->id_entry) + nfc->caps = (void *)pdev->id_entry->driver_data; + else + nfc->caps = of_device_get_match_data(&pdev->dev); + + if (!nfc->caps) { + dev_err(dev, "Could not retrieve NFC caps\n"); + ret = -EINVAL; + goto unprepare_clk; + } + + /* Init the controller and then probe the chips */ + ret = marvell_nfc_init(nfc); + if (ret) + goto unprepare_clk; + + platform_set_drvdata(pdev, nfc); + + ret = marvell_nand_chips_init(dev, nfc); + if (ret) + goto unprepare_clk; + + return 0; + +unprepare_clk: + clk_disable_unprepare(nfc->ecc_clk); + + return ret; +} + +static int marvell_nfc_remove(struct platform_device *pdev) +{ + struct marvell_nfc *nfc = platform_get_drvdata(pdev); + + marvell_nand_chips_cleanup(nfc); + + if (nfc->use_dma) { + dmaengine_terminate_all(nfc->dma_chan); + dma_release_channel(nfc->dma_chan); + } + + clk_disable_unprepare(nfc->ecc_clk); + + return 0; +} + +static const struct marvell_nfc_caps marvell_armada_8k_nfc_caps = { + .max_cs_nb = 4, + .max_rb_nb = 2, + .need_system_controller = true, + .is_nfcv2 = true, +}; + +static const struct marvell_nfc_caps marvell_armada370_nfc_caps = { + .max_cs_nb = 4, + .max_rb_nb = 2, + .is_nfcv2 = true, +}; + +static const struct marvell_nfc_caps marvell_pxa3xx_nfc_caps = { + .max_cs_nb = 2, + .max_rb_nb = 1, + .use_dma = true, +}; + +static const struct marvell_nfc_caps marvell_armada_8k_nfc_legacy_caps = { + .max_cs_nb = 4, + .max_rb_nb = 2, + .need_system_controller = true, + .legacy_of_bindings = true, + .is_nfcv2 = true, +}; + +static const struct marvell_nfc_caps marvell_armada370_nfc_legacy_caps = { + .max_cs_nb = 4, + .max_rb_nb = 2, + .legacy_of_bindings = true, + .is_nfcv2 = true, +}; + +static const struct marvell_nfc_caps marvell_pxa3xx_nfc_legacy_caps = { + .max_cs_nb = 2, + .max_rb_nb = 1, + .legacy_of_bindings = true, + .use_dma = true, +}; + +static const struct platform_device_id marvell_nfc_platform_ids[] = { + { + .name = "pxa3xx-nand", + .driver_data = (kernel_ulong_t)&marvell_pxa3xx_nfc_legacy_caps, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, marvell_nfc_platform_ids); + +static const struct of_device_id marvell_nfc_of_ids[] = { + { + .compatible = "marvell,armada-8k-nand-controller", + .data = &marvell_armada_8k_nfc_caps, + }, + { + .compatible = "marvell,armada370-nand-controller", + .data = &marvell_armada370_nfc_caps, + }, + { + .compatible = "marvell,pxa3xx-nand-controller", + .data = &marvell_pxa3xx_nfc_caps, + }, + /* Support for old/deprecated bindings: */ + { + .compatible = "marvell,armada-8k-nand", + .data = &marvell_armada_8k_nfc_legacy_caps, + }, + { + .compatible = "marvell,armada370-nand", + .data = &marvell_armada370_nfc_legacy_caps, + }, + { + .compatible = "marvell,pxa3xx-nand", + .data = &marvell_pxa3xx_nfc_legacy_caps, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, marvell_nfc_of_ids); + +static struct platform_driver marvell_nfc_driver = { + .driver = { + .name = "marvell-nfc", + .of_match_table = marvell_nfc_of_ids, + }, + .id_table = marvell_nfc_platform_ids, + .probe = marvell_nfc_probe, + .remove = marvell_nfc_remove, +}; +module_platform_driver(marvell_nfc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Marvell NAND controller driver"); diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c new file mode 100644 index 000000000000..913b9d1225c6 --- /dev/null +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -0,0 +1,856 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. + * Copyright 2009 Semihalf. + * + * Approved as OSADL project by a majority of OSADL members and funded + * by OSADL membership fees in 2009; for details see www.osadl.org. + * + * Based on original driver from Freescale Semiconductor + * written by John Rigby on basis of mxc_nand.c. + * Reworked and extended by Piotr Ziecik . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x200) + +/* Addresses for NFC SPARE BUFFER areas */ +#define NFC_SPARE_BUFFERS 8 +#define NFC_SPARE_LEN 0x40 +#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN)) + +/* MPC5121 NFC registers */ +#define NFC_BUF_ADDR 0x1E04 +#define NFC_FLASH_ADDR 0x1E06 +#define NFC_FLASH_CMD 0x1E08 +#define NFC_CONFIG 0x1E0A +#define NFC_ECC_STATUS1 0x1E0C +#define NFC_ECC_STATUS2 0x1E0E +#define NFC_SPAS 0x1E10 +#define NFC_WRPROT 0x1E12 +#define NFC_NF_WRPRST 0x1E18 +#define NFC_CONFIG1 0x1E1A +#define NFC_CONFIG2 0x1E1C +#define NFC_UNLOCKSTART_BLK0 0x1E20 +#define NFC_UNLOCKEND_BLK0 0x1E22 +#define NFC_UNLOCKSTART_BLK1 0x1E24 +#define NFC_UNLOCKEND_BLK1 0x1E26 +#define NFC_UNLOCKSTART_BLK2 0x1E28 +#define NFC_UNLOCKEND_BLK2 0x1E2A +#define NFC_UNLOCKSTART_BLK3 0x1E2C +#define NFC_UNLOCKEND_BLK3 0x1E2E + +/* Bit Definitions: NFC_BUF_ADDR */ +#define NFC_RBA_MASK (7 << 0) +#define NFC_ACTIVE_CS_SHIFT 5 +#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT) + +/* Bit Definitions: NFC_CONFIG */ +#define NFC_BLS_UNLOCKED (1 << 1) + +/* Bit Definitions: NFC_CONFIG1 */ +#define NFC_ECC_4BIT (1 << 0) +#define NFC_FULL_PAGE_DMA (1 << 1) +#define NFC_SPARE_ONLY (1 << 2) +#define NFC_ECC_ENABLE (1 << 3) +#define NFC_INT_MASK (1 << 4) +#define NFC_BIG_ENDIAN (1 << 5) +#define NFC_RESET (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_PPB_32 (0 << 9) +#define NFC_PPB_64 (1 << 9) +#define NFC_PPB_128 (2 << 9) +#define NFC_PPB_256 (3 << 9) +#define NFC_PPB_MASK (3 << 9) +#define NFC_FULL_PAGE_INT (1 << 11) + +/* Bit Definitions: NFC_CONFIG2 */ +#define NFC_COMMAND (1 << 0) +#define NFC_ADDRESS (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) +#define NFC_CMD_FAIL (1 << 15) +#define NFC_INT (1 << 15) + +/* Bit Definitions: NFC_WRPROT */ +#define NFC_WPC_LOCK_TIGHT (1 << 0) +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) + +#define DRV_NAME "mpc5121_nfc" + +/* Timeouts */ +#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ +#define NFC_TIMEOUT (HZ / 10) /* 1/10 s */ + +struct mpc5121_nfc_prv { + struct nand_chip chip; + int irq; + void __iomem *regs; + struct clk *clk; + wait_queue_head_t irq_waitq; + uint column; + int spareonly; + void __iomem *csreg; + struct device *dev; +}; + +static void mpc5121_nfc_done(struct mtd_info *mtd); + +/* Read NFC register */ +static inline u16 nfc_read(struct mtd_info *mtd, uint reg) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); + + return in_be16(prv->regs + reg); +} + +/* Write NFC register */ +static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); + + out_be16(prv->regs + reg, val); +} + +/* Set bits in NFC register */ +static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); +} + +/* Clear bits in NFC register */ +static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); +} + +/* Invoke address cycle */ +static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr) +{ + nfc_write(mtd, NFC_FLASH_ADDR, addr); + nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS); + mpc5121_nfc_done(mtd); +} + +/* Invoke command cycle */ +static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd) +{ + nfc_write(mtd, NFC_FLASH_CMD, cmd); + nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND); + mpc5121_nfc_done(mtd); +} + +/* Send data from NFC buffers to NAND flash */ +static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_INPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive data from NAND flash */ +static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive ID from NAND flash */ +static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_ID); + mpc5121_nfc_done(mtd); +} + +/* Receive status from NAND flash */ +static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_STATUS); + mpc5121_nfc_done(mtd); +} + +/* NFC interrupt handler */ +static irqreturn_t mpc5121_nfc_irq(int irq, void *data) +{ + struct mtd_info *mtd = data; + struct nand_chip *chip = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); + + nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK); + wake_up(&prv->irq_waitq); + + return IRQ_HANDLED; +} + +/* Wait for operation complete */ +static void mpc5121_nfc_done(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); + int rv; + + if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK); + rv = wait_event_timeout(prv->irq_waitq, + (nfc_read(mtd, NFC_CONFIG2) & NFC_INT), NFC_TIMEOUT); + + if (!rv) + dev_warn(prv->dev, + "Timeout while waiting for interrupt.\n"); + } + + nfc_clear(mtd, NFC_CONFIG2, NFC_INT); +} + +/* Do address cycle(s) */ +static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u32 pagemask = chip->pagemask; + + if (column != -1) { + mpc5121_nfc_send_addr(mtd, column); + if (mtd->writesize > 512) + mpc5121_nfc_send_addr(mtd, column >> 8); + } + + if (page != -1) { + do { + mpc5121_nfc_send_addr(mtd, page & 0xFF); + page >>= 8; + pagemask >>= 8; + } while (pagemask); + } +} + +/* Control chip select signals */ +static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + if (chip < 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_CE); + return; + } + + nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) & + NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_CONFIG1, NFC_CE); +} + +/* Init external chip select logic on ADS5121 board */ +static int ads5121_chipselect_init(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); + struct device_node *dn; + + dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld"); + if (dn) { + prv->csreg = of_iomap(dn, 0); + of_node_put(dn); + if (!prv->csreg) + return -ENOMEM; + + /* CPLD Register 9 controls NAND /CE Lines */ + prv->csreg += 9; + return 0; + } + + return -EINVAL; +} + +/* Control chips select signal on ADS5121 board */ +static void ads5121_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand); + u8 v; + + v = in_8(prv->csreg); + v |= 0x0F; + + if (chip >= 0) { + mpc5121_nfc_select_chip(mtd, 0); + v &= ~(1 << chip); + } else + mpc5121_nfc_select_chip(mtd, -1); + + out_8(prv->csreg, v); +} + +/* Read NAND Ready/Busy signal */ +static int mpc5121_nfc_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles ready/busy signal internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +/* Write command to NAND flash */ +static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); + + prv->column = (column >= 0) ? column : 0; + prv->spareonly = 0; + + switch (command) { + case NAND_CMD_PAGEPROG: + mpc5121_nfc_send_prog_page(mtd); + break; + /* + * NFC does not support sub-page reads and writes, + * so emulate them using full page transfers. + */ + case NAND_CMD_READ0: + column = 0; + break; + + case NAND_CMD_READ1: + prv->column += 256; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_READOOB: + prv->spareonly = 1; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_SEQIN: + mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page); + column = 0; + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_READID: + case NAND_CMD_STATUS: + break; + + default: + return; + } + + mpc5121_nfc_send_cmd(mtd, command); + mpc5121_nfc_addr_cycle(mtd, column, page); + + switch (command) { + case NAND_CMD_READ0: + if (mtd->writesize > 512) + mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART); + mpc5121_nfc_send_read_page(mtd); + break; + + case NAND_CMD_READID: + mpc5121_nfc_send_read_id(mtd); + break; + + case NAND_CMD_STATUS: + mpc5121_nfc_send_read_status(mtd); + if (chip->options & NAND_BUSWIDTH_16) + prv->column = 1; + else + prv->column = 0; + break; + } +} + +/* Copy data from/to NFC spare buffers. */ +static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset, + u8 *buffer, uint size, int wr) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand); + uint o, s, sbsize, blksize; + + /* + * NAND spare area is available through NFC spare buffers. + * The NFC divides spare area into (page_size / 512) chunks. + * Each chunk is placed into separate spare memory area, using + * first (spare_size / num_of_chunks) bytes of the buffer. + * + * For NAND device in which the spare area is not divided fully + * by the number of chunks, number of used bytes in each spare + * buffer is rounded down to the nearest even number of bytes, + * and all remaining bytes are added to the last used spare area. + * + * For more information read section 26.6.10 of MPC5121e + * Microcontroller Reference Manual, Rev. 3. + */ + + /* Calculate number of valid bytes in each spare buffer */ + sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1; + + while (size) { + /* Calculate spare buffer number */ + s = offset / sbsize; + if (s > NFC_SPARE_BUFFERS - 1) + s = NFC_SPARE_BUFFERS - 1; + + /* + * Calculate offset to requested data block in selected spare + * buffer and its size. + */ + o = offset - (s * sbsize); + blksize = min(sbsize - o, size); + + if (wr) + memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, + buffer, blksize); + else + memcpy_fromio(buffer, + prv->regs + NFC_SPARE_AREA(s) + o, blksize); + + buffer += blksize; + offset += blksize; + size -= blksize; + }; +} + +/* Copy data from/to NFC main and spare buffers */ +static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len, + int wr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); + uint c = prv->column; + uint l; + + /* Handle spare area access */ + if (prv->spareonly || c >= mtd->writesize) { + /* Calculate offset from beginning of spare area */ + if (c >= mtd->writesize) + c -= mtd->writesize; + + prv->column += len; + mpc5121_nfc_copy_spare(mtd, c, buf, len, wr); + return; + } + + /* + * Handle main area access - limit copy length to prevent + * crossing main/spare boundary. + */ + l = min((uint)len, mtd->writesize - c); + prv->column += l; + + if (wr) + memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); + else + memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); + + /* Handle crossing main/spare boundary */ + if (l != len) { + buf += l; + len -= l; + mpc5121_nfc_buf_copy(mtd, buf, len, wr); + } +} + +/* Read data from NFC buffers */ +static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, buf, len, 0); +} + +/* Write data to NFC buffers */ +static void mpc5121_nfc_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1); +} + +/* Read byte from NFC buffers */ +static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) +{ + u8 tmp; + + mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp)); + + return tmp; +} + +/* Read word from NFC buffers */ +static u16 mpc5121_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); + + return tmp; +} + +/* + * Read NFC configuration from Reset Config Word + * + * NFC is configured during reset in basis of information stored + * in Reset Config Word. There is no other way to set NAND block + * size, spare size and bus width. + */ +static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); + struct mpc512x_reset_module *rm; + struct device_node *rmnode; + uint rcw_pagesize = 0; + uint rcw_sparesize = 0; + uint rcw_width; + uint rcwh; + uint romloc, ps; + int ret = 0; + + rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset"); + if (!rmnode) { + dev_err(prv->dev, "Missing 'fsl,mpc5121-reset' " + "node in device tree!\n"); + return -ENODEV; + } + + rm = of_iomap(rmnode, 0); + if (!rm) { + dev_err(prv->dev, "Error mapping reset module node!\n"); + ret = -EBUSY; + goto out; + } + + rcwh = in_be32(&rm->rcwhr); + + /* Bit 6: NFC bus width */ + rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1; + + /* Bit 7: NFC Page/Spare size */ + ps = (rcwh >> 7) & 0x1; + + /* Bits [22:21]: ROM Location */ + romloc = (rcwh >> 21) & 0x3; + + /* Decode RCW bits */ + switch ((ps << 2) | romloc) { + case 0x00: + case 0x01: + rcw_pagesize = 512; + rcw_sparesize = 16; + break; + case 0x02: + case 0x03: + rcw_pagesize = 4096; + rcw_sparesize = 128; + break; + case 0x04: + case 0x05: + rcw_pagesize = 2048; + rcw_sparesize = 64; + break; + case 0x06: + case 0x07: + rcw_pagesize = 4096; + rcw_sparesize = 218; + break; + } + + mtd->writesize = rcw_pagesize; + mtd->oobsize = rcw_sparesize; + if (rcw_width == 2) + chip->options |= NAND_BUSWIDTH_16; + + dev_notice(prv->dev, "Configured for " + "%u-bit NAND, page size %u " + "with %u spare.\n", + rcw_width * 8, rcw_pagesize, + rcw_sparesize); + iounmap(rm); +out: + of_node_put(rmnode); + return ret; +} + +/* Free driver resources */ +static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); + + if (prv->clk) + clk_disable_unprepare(prv->clk); + + if (prv->csreg) + iounmap(prv->csreg); +} + +static int mpc5121_nfc_probe(struct platform_device *op) +{ + struct device_node *dn = op->dev.of_node; + struct clk *clk; + struct device *dev = &op->dev; + struct mpc5121_nfc_prv *prv; + struct resource res; + struct mtd_info *mtd; + struct nand_chip *chip; + unsigned long regs_paddr, regs_size; + const __be32 *chips_no; + int resettime = 0; + int retval = 0; + int rev, len; + + /* + * Check SoC revision. This driver supports only NFC + * in MPC5121 revision 2 and MPC5123 revision 3. + */ + rev = (mfspr(SPRN_SVR) >> 4) & 0xF; + if ((rev != 2) && (rev != 3)) { + dev_err(dev, "SoC revision %u is not supported!\n", rev); + return -ENXIO; + } + + prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL); + if (!prv) + return -ENOMEM; + + chip = &prv->chip; + mtd = nand_to_mtd(chip); + + mtd->dev.parent = dev; + nand_set_controller_data(chip, prv); + nand_set_flash_node(chip, dn); + prv->dev = dev; + + /* Read NFC configuration from Reset Config Word */ + retval = mpc5121_nfc_read_hw_config(mtd); + if (retval) { + dev_err(dev, "Unable to read NFC config!\n"); + return retval; + } + + prv->irq = irq_of_parse_and_map(dn, 0); + if (prv->irq == NO_IRQ) { + dev_err(dev, "Error mapping IRQ!\n"); + return -EINVAL; + } + + retval = of_address_to_resource(dn, 0, &res); + if (retval) { + dev_err(dev, "Error parsing memory region!\n"); + return retval; + } + + chips_no = of_get_property(dn, "chips", &len); + if (!chips_no || len != sizeof(*chips_no)) { + dev_err(dev, "Invalid/missing 'chips' property!\n"); + return -EINVAL; + } + + regs_paddr = res.start; + regs_size = resource_size(&res); + + if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) { + dev_err(dev, "Error requesting memory region!\n"); + return -EBUSY; + } + + prv->regs = devm_ioremap(dev, regs_paddr, regs_size); + if (!prv->regs) { + dev_err(dev, "Error mapping memory region!\n"); + return -ENOMEM; + } + + mtd->name = "MPC5121 NAND"; + chip->dev_ready = mpc5121_nfc_dev_ready; + chip->cmdfunc = mpc5121_nfc_command; + chip->read_byte = mpc5121_nfc_read_byte; + chip->read_word = mpc5121_nfc_read_word; + chip->read_buf = mpc5121_nfc_read_buf; + chip->write_buf = mpc5121_nfc_write_buf; + chip->select_chip = mpc5121_nfc_select_chip; + chip->onfi_set_features = nand_onfi_get_set_features_notsupp; + chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->bbt_options = NAND_BBT_USE_FLASH; + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + + /* Support external chip-select logic on ADS5121 board */ + if (of_machine_is_compatible("fsl,mpc5121ads")) { + retval = ads5121_chipselect_init(mtd); + if (retval) { + dev_err(dev, "Chipselect init error!\n"); + return retval; + } + + chip->select_chip = ads5121_select_chip; + } + + /* Enable NFC clock */ + clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(clk)) { + dev_err(dev, "Unable to acquire NFC clock!\n"); + retval = PTR_ERR(clk); + goto error; + } + retval = clk_prepare_enable(clk); + if (retval) { + dev_err(dev, "Unable to enable NFC clock!\n"); + goto error; + } + prv->clk = clk; + + /* Reset NAND Flash controller */ + nfc_set(mtd, NFC_CONFIG1, NFC_RESET); + while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) { + if (resettime++ >= NFC_RESET_TIMEOUT) { + dev_err(dev, "Timeout while resetting NFC!\n"); + retval = -EINVAL; + goto error; + } + + udelay(1); + } + + /* Enable write to NFC memory */ + nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED); + + /* Enable write to all NAND pages */ + nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000); + nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF); + nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK); + + /* + * Setup NFC: + * - Big Endian transfers, + * - Interrupt after full page read/write. + */ + nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK | + NFC_FULL_PAGE_INT); + + /* Set spare area size */ + nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1); + + init_waitqueue_head(&prv->irq_waitq); + retval = devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq, 0, DRV_NAME, + mtd); + if (retval) { + dev_err(dev, "Error requesting IRQ!\n"); + goto error; + } + + /* Detect NAND chips */ + retval = nand_scan(mtd, be32_to_cpup(chips_no)); + if (retval) { + dev_err(dev, "NAND Flash not found !\n"); + goto error; + } + + /* Set erase block size */ + switch (mtd->erasesize / mtd->writesize) { + case 32: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32); + break; + + case 64: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64); + break; + + case 128: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128); + break; + + case 256: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256); + break; + + default: + dev_err(dev, "Unsupported NAND flash!\n"); + retval = -ENXIO; + goto error; + } + + dev_set_drvdata(dev, mtd); + + /* Register device in MTD */ + retval = mtd_device_register(mtd, NULL, 0); + if (retval) { + dev_err(dev, "Error adding MTD device!\n"); + goto error; + } + + return 0; +error: + mpc5121_nfc_free(dev, mtd); + return retval; +} + +static int mpc5121_nfc_remove(struct platform_device *op) +{ + struct device *dev = &op->dev; + struct mtd_info *mtd = dev_get_drvdata(dev); + + nand_release(mtd); + mpc5121_nfc_free(dev, mtd); + + return 0; +} + +static const struct of_device_id mpc5121_nfc_match[] = { + { .compatible = "fsl,mpc5121-nfc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc5121_nfc_match); + +static struct platform_driver mpc5121_nfc_driver = { + .probe = mpc5121_nfc_probe, + .remove = mpc5121_nfc_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = mpc5121_nfc_match, + }, +}; + +module_platform_driver(mpc5121_nfc_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MPC5121 NAND MTD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c new file mode 100644 index 000000000000..40d86a861a70 --- /dev/null +++ b/drivers/mtd/nand/raw/mtk_ecc.c @@ -0,0 +1,608 @@ +/* + * MTK ECC controller driver. + * Copyright (C) 2016 MediaTek Inc. + * Authors: Xiaolei Li + * Jorge Ramirez-Ortiz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_ecc.h" + +#define ECC_IDLE_MASK BIT(0) +#define ECC_IRQ_EN BIT(0) +#define ECC_PG_IRQ_SEL BIT(1) +#define ECC_OP_ENABLE (1) +#define ECC_OP_DISABLE (0) + +#define ECC_ENCCON (0x00) +#define ECC_ENCCNFG (0x04) +#define ECC_MS_SHIFT (16) +#define ECC_ENCDIADDR (0x08) +#define ECC_ENCIDLE (0x0C) +#define ECC_DECCON (0x100) +#define ECC_DECCNFG (0x104) +#define DEC_EMPTY_EN BIT(31) +#define DEC_CNFG_CORRECT (0x3 << 12) +#define ECC_DECIDLE (0x10C) +#define ECC_DECENUM0 (0x114) + +#define ECC_TIMEOUT (500000) + +#define ECC_IDLE_REG(op) ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE) +#define ECC_CTL_REG(op) ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON) + +struct mtk_ecc_caps { + u32 err_mask; + const u8 *ecc_strength; + const u32 *ecc_regs; + u8 num_ecc_strength; + u8 ecc_mode_shift; + u32 parity_bits; + int pg_irq_sel; +}; + +struct mtk_ecc { + struct device *dev; + const struct mtk_ecc_caps *caps; + void __iomem *regs; + struct clk *clk; + + struct completion done; + struct mutex lock; + u32 sectors; + + u8 *eccdata; +}; + +/* ecc strength that each IP supports */ +static const u8 ecc_strength_mt2701[] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, + 40, 44, 48, 52, 56, 60 +}; + +static const u8 ecc_strength_mt2712[] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, + 40, 44, 48, 52, 56, 60, 68, 72, 80 +}; + +static const u8 ecc_strength_mt7622[] = { + 4, 6, 8, 10, 12, 14, 16 +}; + +enum mtk_ecc_regs { + ECC_ENCPAR00, + ECC_ENCIRQ_EN, + ECC_ENCIRQ_STA, + ECC_DECDONE, + ECC_DECIRQ_EN, + ECC_DECIRQ_STA, +}; + +static int mt2701_ecc_regs[] = { + [ECC_ENCPAR00] = 0x10, + [ECC_ENCIRQ_EN] = 0x80, + [ECC_ENCIRQ_STA] = 0x84, + [ECC_DECDONE] = 0x124, + [ECC_DECIRQ_EN] = 0x200, + [ECC_DECIRQ_STA] = 0x204, +}; + +static int mt2712_ecc_regs[] = { + [ECC_ENCPAR00] = 0x300, + [ECC_ENCIRQ_EN] = 0x80, + [ECC_ENCIRQ_STA] = 0x84, + [ECC_DECDONE] = 0x124, + [ECC_DECIRQ_EN] = 0x200, + [ECC_DECIRQ_STA] = 0x204, +}; + +static int mt7622_ecc_regs[] = { + [ECC_ENCPAR00] = 0x10, + [ECC_ENCIRQ_EN] = 0x30, + [ECC_ENCIRQ_STA] = 0x34, + [ECC_DECDONE] = 0x11c, + [ECC_DECIRQ_EN] = 0x140, + [ECC_DECIRQ_STA] = 0x144, +}; + +static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc, + enum mtk_ecc_operation op) +{ + struct device *dev = ecc->dev; + u32 val; + int ret; + + ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val, + val & ECC_IDLE_MASK, + 10, ECC_TIMEOUT); + if (ret) + dev_warn(dev, "%s NOT idle\n", + op == ECC_ENCODE ? "encoder" : "decoder"); +} + +static irqreturn_t mtk_ecc_irq(int irq, void *id) +{ + struct mtk_ecc *ecc = id; + u32 dec, enc; + + dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]) + & ECC_IRQ_EN; + if (dec) { + dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]); + if (dec & ecc->sectors) { + /* + * Clear decode IRQ status once again to ensure that + * there will be no extra IRQ. + */ + readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]); + ecc->sectors = 0; + complete(&ecc->done); + } else { + return IRQ_HANDLED; + } + } else { + enc = readl(ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_STA]) + & ECC_IRQ_EN; + if (enc) + complete(&ecc->done); + else + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config) +{ + u32 ecc_bit, dec_sz, enc_sz; + u32 reg, i; + + for (i = 0; i < ecc->caps->num_ecc_strength; i++) { + if (ecc->caps->ecc_strength[i] == config->strength) + break; + } + + if (i == ecc->caps->num_ecc_strength) { + dev_err(ecc->dev, "invalid ecc strength %d\n", + config->strength); + return -EINVAL; + } + + ecc_bit = i; + + if (config->op == ECC_ENCODE) { + /* configure ECC encoder (in bits) */ + enc_sz = config->len << 3; + + reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift); + reg |= (enc_sz << ECC_MS_SHIFT); + writel(reg, ecc->regs + ECC_ENCCNFG); + + if (config->mode != ECC_NFI_MODE) + writel(lower_32_bits(config->addr), + ecc->regs + ECC_ENCDIADDR); + + } else { + /* configure ECC decoder (in bits) */ + dec_sz = (config->len << 3) + + config->strength * ecc->caps->parity_bits; + + reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift); + reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT; + reg |= DEC_EMPTY_EN; + writel(reg, ecc->regs + ECC_DECCNFG); + + if (config->sectors) + ecc->sectors = 1 << (config->sectors - 1); + } + + return 0; +} + +void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats, + int sectors) +{ + u32 offset, i, err; + u32 bitflips = 0; + + stats->corrected = 0; + stats->failed = 0; + + for (i = 0; i < sectors; i++) { + offset = (i >> 2) << 2; + err = readl(ecc->regs + ECC_DECENUM0 + offset); + err = err >> ((i % 4) * 8); + err &= ecc->caps->err_mask; + if (err == ecc->caps->err_mask) { + /* uncorrectable errors */ + stats->failed++; + continue; + } + + stats->corrected += err; + bitflips = max_t(u32, bitflips, err); + } + + stats->bitflips = bitflips; +} +EXPORT_SYMBOL(mtk_ecc_get_stats); + +void mtk_ecc_release(struct mtk_ecc *ecc) +{ + clk_disable_unprepare(ecc->clk); + put_device(ecc->dev); +} +EXPORT_SYMBOL(mtk_ecc_release); + +static void mtk_ecc_hw_init(struct mtk_ecc *ecc) +{ + mtk_ecc_wait_idle(ecc, ECC_ENCODE); + writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON); + + mtk_ecc_wait_idle(ecc, ECC_DECODE); + writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON); +} + +static struct mtk_ecc *mtk_ecc_get(struct device_node *np) +{ + struct platform_device *pdev; + struct mtk_ecc *ecc; + + pdev = of_find_device_by_node(np); + if (!pdev || !platform_get_drvdata(pdev)) + return ERR_PTR(-EPROBE_DEFER); + + get_device(&pdev->dev); + ecc = platform_get_drvdata(pdev); + clk_prepare_enable(ecc->clk); + mtk_ecc_hw_init(ecc); + + return ecc; +} + +struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node) +{ + struct mtk_ecc *ecc = NULL; + struct device_node *np; + + np = of_parse_phandle(of_node, "ecc-engine", 0); + if (np) { + ecc = mtk_ecc_get(np); + of_node_put(np); + } + + return ecc; +} +EXPORT_SYMBOL(of_mtk_ecc_get); + +int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config) +{ + enum mtk_ecc_operation op = config->op; + u16 reg_val; + int ret; + + ret = mutex_lock_interruptible(&ecc->lock); + if (ret) { + dev_err(ecc->dev, "interrupted when attempting to lock\n"); + return ret; + } + + mtk_ecc_wait_idle(ecc, op); + + ret = mtk_ecc_config(ecc, config); + if (ret) { + mutex_unlock(&ecc->lock); + return ret; + } + + if (config->mode != ECC_NFI_MODE || op != ECC_ENCODE) { + init_completion(&ecc->done); + reg_val = ECC_IRQ_EN; + /* + * For ECC_NFI_MODE, if ecc->caps->pg_irq_sel is 1, then it + * means this chip can only generate one ecc irq during page + * read / write. If is 0, generate one ecc irq each ecc step. + */ + if (ecc->caps->pg_irq_sel && config->mode == ECC_NFI_MODE) + reg_val |= ECC_PG_IRQ_SEL; + if (op == ECC_ENCODE) + writew(reg_val, ecc->regs + + ecc->caps->ecc_regs[ECC_ENCIRQ_EN]); + else + writew(reg_val, ecc->regs + + ecc->caps->ecc_regs[ECC_DECIRQ_EN]); + } + + writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op)); + + return 0; +} +EXPORT_SYMBOL(mtk_ecc_enable); + +void mtk_ecc_disable(struct mtk_ecc *ecc) +{ + enum mtk_ecc_operation op = ECC_ENCODE; + + /* find out the running operation */ + if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE) + op = ECC_DECODE; + + /* disable it */ + mtk_ecc_wait_idle(ecc, op); + if (op == ECC_DECODE) { + /* + * Clear decode IRQ status in case there is a timeout to wait + * decode IRQ. + */ + readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]); + writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_EN]); + } else { + writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_EN]); + } + + writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op)); + + mutex_unlock(&ecc->lock); +} +EXPORT_SYMBOL(mtk_ecc_disable); + +int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op) +{ + int ret; + + ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500)); + if (!ret) { + dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n", + (op == ECC_ENCODE) ? "encoder" : "decoder"); + return -ETIMEDOUT; + } + + return 0; +} +EXPORT_SYMBOL(mtk_ecc_wait_done); + +int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config, + u8 *data, u32 bytes) +{ + dma_addr_t addr; + u32 len; + int ret; + + addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE); + ret = dma_mapping_error(ecc->dev, addr); + if (ret) { + dev_err(ecc->dev, "dma mapping error\n"); + return -EINVAL; + } + + config->op = ECC_ENCODE; + config->addr = addr; + ret = mtk_ecc_enable(ecc, config); + if (ret) { + dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); + return ret; + } + + ret = mtk_ecc_wait_done(ecc, ECC_ENCODE); + if (ret) + goto timeout; + + mtk_ecc_wait_idle(ecc, ECC_ENCODE); + + /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */ + len = (config->strength * ecc->caps->parity_bits + 7) >> 3; + + /* write the parity bytes generated by the ECC back to temp buffer */ + __ioread32_copy(ecc->eccdata, + ecc->regs + ecc->caps->ecc_regs[ECC_ENCPAR00], + round_up(len, 4)); + + /* copy into possibly unaligned OOB region with actual length */ + memcpy(data + bytes, ecc->eccdata, len); +timeout: + + dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); + mtk_ecc_disable(ecc); + + return ret; +} +EXPORT_SYMBOL(mtk_ecc_encode); + +void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p) +{ + const u8 *ecc_strength = ecc->caps->ecc_strength; + int i; + + for (i = 0; i < ecc->caps->num_ecc_strength; i++) { + if (*p <= ecc_strength[i]) { + if (!i) + *p = ecc_strength[i]; + else if (*p != ecc_strength[i]) + *p = ecc_strength[i - 1]; + return; + } + } + + *p = ecc_strength[ecc->caps->num_ecc_strength - 1]; +} +EXPORT_SYMBOL(mtk_ecc_adjust_strength); + +unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc) +{ + return ecc->caps->parity_bits; +} +EXPORT_SYMBOL(mtk_ecc_get_parity_bits); + +static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = { + .err_mask = 0x3f, + .ecc_strength = ecc_strength_mt2701, + .ecc_regs = mt2701_ecc_regs, + .num_ecc_strength = 20, + .ecc_mode_shift = 5, + .parity_bits = 14, + .pg_irq_sel = 0, +}; + +static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = { + .err_mask = 0x7f, + .ecc_strength = ecc_strength_mt2712, + .ecc_regs = mt2712_ecc_regs, + .num_ecc_strength = 23, + .ecc_mode_shift = 5, + .parity_bits = 14, + .pg_irq_sel = 1, +}; + +static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = { + .err_mask = 0x3f, + .ecc_strength = ecc_strength_mt7622, + .ecc_regs = mt7622_ecc_regs, + .num_ecc_strength = 7, + .ecc_mode_shift = 4, + .parity_bits = 13, + .pg_irq_sel = 0, +}; + +static const struct of_device_id mtk_ecc_dt_match[] = { + { + .compatible = "mediatek,mt2701-ecc", + .data = &mtk_ecc_caps_mt2701, + }, { + .compatible = "mediatek,mt2712-ecc", + .data = &mtk_ecc_caps_mt2712, + }, { + .compatible = "mediatek,mt7622-ecc", + .data = &mtk_ecc_caps_mt7622, + }, + {}, +}; + +static int mtk_ecc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_ecc *ecc; + struct resource *res; + const struct of_device_id *of_ecc_id = NULL; + u32 max_eccdata_size; + int irq, ret; + + ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); + if (!ecc) + return -ENOMEM; + + of_ecc_id = of_match_device(mtk_ecc_dt_match, &pdev->dev); + if (!of_ecc_id) + return -ENODEV; + + ecc->caps = of_ecc_id->data; + + max_eccdata_size = ecc->caps->num_ecc_strength - 1; + max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size]; + max_eccdata_size = (max_eccdata_size * ecc->caps->parity_bits + 7) >> 3; + max_eccdata_size = round_up(max_eccdata_size, 4); + ecc->eccdata = devm_kzalloc(dev, max_eccdata_size, GFP_KERNEL); + if (!ecc->eccdata) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ecc->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(ecc->regs)) { + dev_err(dev, "failed to map regs: %ld\n", PTR_ERR(ecc->regs)); + return PTR_ERR(ecc->regs); + } + + ecc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(ecc->clk)) { + dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk)); + return PTR_ERR(ecc->clk); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "failed to get irq: %d\n", irq); + return irq; + } + + ret = dma_set_mask(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "failed to set DMA mask\n"); + return ret; + } + + ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return -EINVAL; + } + + ecc->dev = dev; + mutex_init(&ecc->lock); + platform_set_drvdata(pdev, ecc); + dev_info(dev, "probed\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_ecc_suspend(struct device *dev) +{ + struct mtk_ecc *ecc = dev_get_drvdata(dev); + + clk_disable_unprepare(ecc->clk); + + return 0; +} + +static int mtk_ecc_resume(struct device *dev) +{ + struct mtk_ecc *ecc = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(ecc->clk); + if (ret) { + dev_err(dev, "failed to enable clk\n"); + return ret; + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume); +#endif + +MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match); + +static struct platform_driver mtk_ecc_driver = { + .probe = mtk_ecc_probe, + .driver = { + .name = "mtk-ecc", + .of_match_table = of_match_ptr(mtk_ecc_dt_match), +#ifdef CONFIG_PM_SLEEP + .pm = &mtk_ecc_pm_ops, +#endif + }, +}; + +module_platform_driver(mtk_ecc_driver); + +MODULE_AUTHOR("Xiaolei Li "); +MODULE_DESCRIPTION("MTK Nand ECC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/raw/mtk_ecc.h b/drivers/mtd/nand/raw/mtk_ecc.h new file mode 100644 index 000000000000..a455df080952 --- /dev/null +++ b/drivers/mtd/nand/raw/mtk_ecc.h @@ -0,0 +1,49 @@ +/* + * MTK SDG1 ECC controller + * + * Copyright (c) 2016 Mediatek + * Authors: Xiaolei Li + * Jorge Ramirez-Ortiz + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__ +#define __DRIVERS_MTD_NAND_MTK_ECC_H__ + +#include + +enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1}; +enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE}; + +struct device_node; +struct mtk_ecc; + +struct mtk_ecc_stats { + u32 corrected; + u32 bitflips; + u32 failed; +}; + +struct mtk_ecc_config { + enum mtk_ecc_operation op; + enum mtk_ecc_mode mode; + dma_addr_t addr; + u32 strength; + u32 sectors; + u32 len; +}; + +int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32); +void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int); +int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation); +int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *); +void mtk_ecc_disable(struct mtk_ecc *); +void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p); +unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc); + +struct mtk_ecc *of_mtk_ecc_get(struct device_node *); +void mtk_ecc_release(struct mtk_ecc *); + +#endif diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c new file mode 100644 index 000000000000..6977da3a26aa --- /dev/null +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -0,0 +1,1599 @@ +/* + * MTK NAND Flash controller driver. + * Copyright (C) 2016 MediaTek Inc. + * Authors: Xiaolei Li + * Jorge Ramirez-Ortiz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtk_ecc.h" + +/* NAND controller register definition */ +#define NFI_CNFG (0x00) +#define CNFG_AHB BIT(0) +#define CNFG_READ_EN BIT(1) +#define CNFG_DMA_BURST_EN BIT(2) +#define CNFG_BYTE_RW BIT(6) +#define CNFG_HW_ECC_EN BIT(8) +#define CNFG_AUTO_FMT_EN BIT(9) +#define CNFG_OP_CUST (6 << 12) +#define NFI_PAGEFMT (0x04) +#define PAGEFMT_FDM_ECC_SHIFT (12) +#define PAGEFMT_FDM_SHIFT (8) +#define PAGEFMT_SEC_SEL_512 BIT(2) +#define PAGEFMT_512_2K (0) +#define PAGEFMT_2K_4K (1) +#define PAGEFMT_4K_8K (2) +#define PAGEFMT_8K_16K (3) +/* NFI control */ +#define NFI_CON (0x08) +#define CON_FIFO_FLUSH BIT(0) +#define CON_NFI_RST BIT(1) +#define CON_BRD BIT(8) /* burst read */ +#define CON_BWR BIT(9) /* burst write */ +#define CON_SEC_SHIFT (12) +/* Timming control register */ +#define NFI_ACCCON (0x0C) +#define NFI_INTR_EN (0x10) +#define INTR_AHB_DONE_EN BIT(6) +#define NFI_INTR_STA (0x14) +#define NFI_CMD (0x20) +#define NFI_ADDRNOB (0x30) +#define NFI_COLADDR (0x34) +#define NFI_ROWADDR (0x38) +#define NFI_STRDATA (0x40) +#define STAR_EN (1) +#define STAR_DE (0) +#define NFI_CNRNB (0x44) +#define NFI_DATAW (0x50) +#define NFI_DATAR (0x54) +#define NFI_PIO_DIRDY (0x58) +#define PIO_DI_RDY (0x01) +#define NFI_STA (0x60) +#define STA_CMD BIT(0) +#define STA_ADDR BIT(1) +#define STA_BUSY BIT(8) +#define STA_EMP_PAGE BIT(12) +#define NFI_FSM_CUSTDATA (0xe << 16) +#define NFI_FSM_MASK (0xf << 16) +#define NFI_ADDRCNTR (0x70) +#define CNTR_MASK GENMASK(16, 12) +#define ADDRCNTR_SEC_SHIFT (12) +#define ADDRCNTR_SEC(val) \ + (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT) +#define NFI_STRADDR (0x80) +#define NFI_BYTELEN (0x84) +#define NFI_CSEL (0x90) +#define NFI_FDML(x) (0xA0 + (x) * sizeof(u32) * 2) +#define NFI_FDMM(x) (0xA4 + (x) * sizeof(u32) * 2) +#define NFI_FDM_MAX_SIZE (8) +#define NFI_FDM_MIN_SIZE (1) +#define NFI_MASTER_STA (0x224) +#define MASTER_STA_MASK (0x0FFF) +#define NFI_EMPTY_THRESH (0x23C) + +#define MTK_NAME "mtk-nand" +#define KB(x) ((x) * 1024UL) +#define MB(x) (KB(x) * 1024UL) + +#define MTK_TIMEOUT (500000) +#define MTK_RESET_TIMEOUT (1000000) +#define MTK_NAND_MAX_NSELS (2) +#define MTK_NFC_MIN_SPARE (16) +#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \ + ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \ + (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt)) + +struct mtk_nfc_caps { + const u8 *spare_size; + u8 num_spare_size; + u8 pageformat_spare_shift; + u8 nfi_clk_div; + u8 max_sector; + u32 max_sector_size; +}; + +struct mtk_nfc_bad_mark_ctl { + void (*bm_swap)(struct mtd_info *, u8 *buf, int raw); + u32 sec; + u32 pos; +}; + +/* + * FDM: region used to store free OOB data + */ +struct mtk_nfc_fdm { + u32 reg_size; + u32 ecc_size; +}; + +struct mtk_nfc_nand_chip { + struct list_head node; + struct nand_chip nand; + + struct mtk_nfc_bad_mark_ctl bad_mark; + struct mtk_nfc_fdm fdm; + u32 spare_per_sector; + + int nsels; + u8 sels[0]; + /* nothing after this field */ +}; + +struct mtk_nfc_clk { + struct clk *nfi_clk; + struct clk *pad_clk; +}; + +struct mtk_nfc { + struct nand_hw_control controller; + struct mtk_ecc_config ecc_cfg; + struct mtk_nfc_clk clk; + struct mtk_ecc *ecc; + + struct device *dev; + const struct mtk_nfc_caps *caps; + void __iomem *regs; + + struct completion done; + struct list_head chips; + + u8 *buffer; +}; + +/* + * supported spare size of each IP. + * order should be the same with the spare size bitfiled defination of + * register NFI_PAGEFMT. + */ +static const u8 spare_size_mt2701[] = { + 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 63, 64 +}; + +static const u8 spare_size_mt2712[] = { + 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, 67, + 74 +}; + +static const u8 spare_size_mt7622[] = { + 16, 26, 27, 28 +}; + +static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct nand_chip *nand) +{ + return container_of(nand, struct mtk_nfc_nand_chip, nand); +} + +static inline u8 *data_ptr(struct nand_chip *chip, const u8 *p, int i) +{ + return (u8 *)p + i * chip->ecc.size; +} + +static inline u8 *oob_ptr(struct nand_chip *chip, int i) +{ + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + u8 *poi; + + /* map the sector's FDM data to free oob: + * the beginning of the oob area stores the FDM data of bad mark sectors + */ + + if (i < mtk_nand->bad_mark.sec) + poi = chip->oob_poi + (i + 1) * mtk_nand->fdm.reg_size; + else if (i == mtk_nand->bad_mark.sec) + poi = chip->oob_poi; + else + poi = chip->oob_poi + i * mtk_nand->fdm.reg_size; + + return poi; +} + +static inline int mtk_data_len(struct nand_chip *chip) +{ + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + + return chip->ecc.size + mtk_nand->spare_per_sector; +} + +static inline u8 *mtk_data_ptr(struct nand_chip *chip, int i) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + + return nfc->buffer + i * mtk_data_len(chip); +} + +static inline u8 *mtk_oob_ptr(struct nand_chip *chip, int i) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + + return nfc->buffer + i * mtk_data_len(chip) + chip->ecc.size; +} + +static inline void nfi_writel(struct mtk_nfc *nfc, u32 val, u32 reg) +{ + writel(val, nfc->regs + reg); +} + +static inline void nfi_writew(struct mtk_nfc *nfc, u16 val, u32 reg) +{ + writew(val, nfc->regs + reg); +} + +static inline void nfi_writeb(struct mtk_nfc *nfc, u8 val, u32 reg) +{ + writeb(val, nfc->regs + reg); +} + +static inline u32 nfi_readl(struct mtk_nfc *nfc, u32 reg) +{ + return readl_relaxed(nfc->regs + reg); +} + +static inline u16 nfi_readw(struct mtk_nfc *nfc, u32 reg) +{ + return readw_relaxed(nfc->regs + reg); +} + +static inline u8 nfi_readb(struct mtk_nfc *nfc, u32 reg) +{ + return readb_relaxed(nfc->regs + reg); +} + +static void mtk_nfc_hw_reset(struct mtk_nfc *nfc) +{ + struct device *dev = nfc->dev; + u32 val; + int ret; + + /* reset all registers and force the NFI master to terminate */ + nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON); + + /* wait for the master to finish the last transaction */ + ret = readl_poll_timeout(nfc->regs + NFI_MASTER_STA, val, + !(val & MASTER_STA_MASK), 50, + MTK_RESET_TIMEOUT); + if (ret) + dev_warn(dev, "master active in reset [0x%x] = 0x%x\n", + NFI_MASTER_STA, val); + + /* ensure any status register affected by the NFI master is reset */ + nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON); + nfi_writew(nfc, STAR_DE, NFI_STRDATA); +} + +static int mtk_nfc_send_command(struct mtk_nfc *nfc, u8 command) +{ + struct device *dev = nfc->dev; + u32 val; + int ret; + + nfi_writel(nfc, command, NFI_CMD); + + ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val, + !(val & STA_CMD), 10, MTK_TIMEOUT); + if (ret) { + dev_warn(dev, "nfi core timed out entering command mode\n"); + return -EIO; + } + + return 0; +} + +static int mtk_nfc_send_address(struct mtk_nfc *nfc, int addr) +{ + struct device *dev = nfc->dev; + u32 val; + int ret; + + nfi_writel(nfc, addr, NFI_COLADDR); + nfi_writel(nfc, 0, NFI_ROWADDR); + nfi_writew(nfc, 1, NFI_ADDRNOB); + + ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val, + !(val & STA_ADDR), 10, MTK_TIMEOUT); + if (ret) { + dev_warn(dev, "nfi core timed out entering address mode\n"); + return -EIO; + } + + return 0; +} + +static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + struct mtk_nfc *nfc = nand_get_controller_data(chip); + u32 fmt, spare, i; + + if (!mtd->writesize) + return 0; + + spare = mtk_nand->spare_per_sector; + + switch (mtd->writesize) { + case 512: + fmt = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512; + break; + case KB(2): + if (chip->ecc.size == 512) + fmt = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512; + else + fmt = PAGEFMT_512_2K; + break; + case KB(4): + if (chip->ecc.size == 512) + fmt = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512; + else + fmt = PAGEFMT_2K_4K; + break; + case KB(8): + if (chip->ecc.size == 512) + fmt = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512; + else + fmt = PAGEFMT_4K_8K; + break; + case KB(16): + fmt = PAGEFMT_8K_16K; + break; + default: + dev_err(nfc->dev, "invalid page len: %d\n", mtd->writesize); + return -EINVAL; + } + + /* + * the hardware will double the value for this eccsize, so we need to + * halve it + */ + if (chip->ecc.size == 1024) + spare >>= 1; + + for (i = 0; i < nfc->caps->num_spare_size; i++) { + if (nfc->caps->spare_size[i] == spare) + break; + } + + if (i == nfc->caps->num_spare_size) { + dev_err(nfc->dev, "invalid spare size %d\n", spare); + return -EINVAL; + } + + fmt |= i << nfc->caps->pageformat_spare_shift; + + fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT; + fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT; + nfi_writel(nfc, fmt, NFI_PAGEFMT); + + nfc->ecc_cfg.strength = chip->ecc.strength; + nfc->ecc_cfg.len = chip->ecc.size + mtk_nand->fdm.ecc_size; + + return 0; +} + +static void mtk_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mtk_nfc *nfc = nand_get_controller_data(nand); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand); + + if (chip < 0) + return; + + mtk_nfc_hw_runtime_config(mtd); + + nfi_writel(nfc, mtk_nand->sels[chip], NFI_CSEL); +} + +static int mtk_nfc_dev_ready(struct mtd_info *mtd) +{ + struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); + + if (nfi_readl(nfc, NFI_STA) & STA_BUSY) + return 0; + + return 1; +} + +static void mtk_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); + + if (ctrl & NAND_ALE) { + mtk_nfc_send_address(nfc, dat); + } else if (ctrl & NAND_CLE) { + mtk_nfc_hw_reset(nfc); + + nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG); + mtk_nfc_send_command(nfc, dat); + } +} + +static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc) +{ + int rc; + u8 val; + + rc = readb_poll_timeout_atomic(nfc->regs + NFI_PIO_DIRDY, val, + val & PIO_DI_RDY, 10, MTK_TIMEOUT); + if (rc < 0) + dev_err(nfc->dev, "data not ready\n"); +} + +static inline u8 mtk_nfc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtk_nfc *nfc = nand_get_controller_data(chip); + u32 reg; + + /* after each byte read, the NFI_STA reg is reset by the hardware */ + reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK; + if (reg != NFI_FSM_CUSTDATA) { + reg = nfi_readw(nfc, NFI_CNFG); + reg |= CNFG_BYTE_RW | CNFG_READ_EN; + nfi_writew(nfc, reg, NFI_CNFG); + + /* + * set to max sector to allow the HW to continue reading over + * unaligned accesses + */ + reg = (nfc->caps->max_sector << CON_SEC_SHIFT) | CON_BRD; + nfi_writel(nfc, reg, NFI_CON); + + /* trigger to fetch data */ + nfi_writew(nfc, STAR_EN, NFI_STRDATA); + } + + mtk_nfc_wait_ioready(nfc); + + return nfi_readb(nfc, NFI_DATAR); +} + +static void mtk_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + buf[i] = mtk_nfc_read_byte(mtd); +} + +static void mtk_nfc_write_byte(struct mtd_info *mtd, u8 byte) +{ + struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); + u32 reg; + + reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK; + + if (reg != NFI_FSM_CUSTDATA) { + reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW; + nfi_writew(nfc, reg, NFI_CNFG); + + reg = nfc->caps->max_sector << CON_SEC_SHIFT | CON_BWR; + nfi_writel(nfc, reg, NFI_CON); + + nfi_writew(nfc, STAR_EN, NFI_STRDATA); + } + + mtk_nfc_wait_ioready(nfc); + nfi_writeb(nfc, byte, NFI_DATAW); +} + +static void mtk_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + mtk_nfc_write_byte(mtd, buf[i]); +} + +static int mtk_nfc_setup_data_interface(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf) +{ + struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); + const struct nand_sdr_timings *timings; + u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + rate = clk_get_rate(nfc->clk.nfi_clk); + /* There is a frequency divider in some IPs */ + rate /= nfc->caps->nfi_clk_div; + + /* turn clock rate into KHZ */ + rate /= 1000; + + tpoecs = max(timings->tALH_min, timings->tCLH_min) / 1000; + tpoecs = DIV_ROUND_UP(tpoecs * rate, 1000000); + tpoecs &= 0xf; + + tprecs = max(timings->tCLS_min, timings->tALS_min) / 1000; + tprecs = DIV_ROUND_UP(tprecs * rate, 1000000); + tprecs &= 0x3f; + + /* sdr interface has no tCR which means CE# low to RE# low */ + tc2r = 0; + + tw2r = timings->tWHR_min / 1000; + tw2r = DIV_ROUND_UP(tw2r * rate, 1000000); + tw2r = DIV_ROUND_UP(tw2r - 1, 2); + tw2r &= 0xf; + + twh = max(timings->tREH_min, timings->tWH_min) / 1000; + twh = DIV_ROUND_UP(twh * rate, 1000000) - 1; + twh &= 0xf; + + twst = timings->tWP_min / 1000; + twst = DIV_ROUND_UP(twst * rate, 1000000) - 1; + twst &= 0xf; + + trlt = max(timings->tREA_max, timings->tRP_min) / 1000; + trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1; + trlt &= 0xf; + + /* + * ACCON: access timing control register + * ------------------------------------- + * 31:28: tpoecs, minimum required time for CS post pulling down after + * accessing the device + * 27:22: tprecs, minimum required time for CS pre pulling down before + * accessing the device + * 21:16: tc2r, minimum required time from NCEB low to NREB low + * 15:12: tw2r, minimum required time from NWEB high to NREB low. + * 11:08: twh, write enable hold time + * 07:04: twst, write wait states + * 03:00: trlt, read wait states + */ + trlt = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt); + nfi_writel(nfc, trlt, NFI_ACCCON); + + return 0; +} + +static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + int size = chip->ecc.size + mtk_nand->fdm.reg_size; + + nfc->ecc_cfg.mode = ECC_DMA_MODE; + nfc->ecc_cfg.op = ECC_ENCODE; + + return mtk_ecc_encode(nfc->ecc, &nfc->ecc_cfg, data, size); +} + +static void mtk_nfc_no_bad_mark_swap(struct mtd_info *a, u8 *b, int c) +{ + /* nop */ +} + +static void mtk_nfc_bad_mark_swap(struct mtd_info *mtd, u8 *buf, int raw) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtk_nfc_nand_chip *nand = to_mtk_nand(chip); + u32 bad_pos = nand->bad_mark.pos; + + if (raw) + bad_pos += nand->bad_mark.sec * mtk_data_len(chip); + else + bad_pos += nand->bad_mark.sec * chip->ecc.size; + + swap(chip->oob_poi[0], buf[bad_pos]); +} + +static int mtk_nfc_format_subpage(struct mtd_info *mtd, u32 offset, + u32 len, const u8 *buf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; + u32 start, end; + int i, ret; + + start = offset / chip->ecc.size; + end = DIV_ROUND_UP(offset + len, chip->ecc.size); + + memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); + for (i = 0; i < chip->ecc.steps; i++) { + memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i), + chip->ecc.size); + + if (start > i || i >= end) + continue; + + if (i == mtk_nand->bad_mark.sec) + mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); + + memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size); + + /* program the CRC back to the OOB */ + ret = mtk_nfc_sector_encode(chip, mtk_data_ptr(chip, i)); + if (ret < 0) + return ret; + } + + return 0; +} + +static void mtk_nfc_format_page(struct mtd_info *mtd, const u8 *buf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; + u32 i; + + memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); + for (i = 0; i < chip->ecc.steps; i++) { + if (buf) + memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i), + chip->ecc.size); + + if (i == mtk_nand->bad_mark.sec) + mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); + + memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size); + } +} + +static inline void mtk_nfc_read_fdm(struct nand_chip *chip, u32 start, + u32 sectors) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; + u32 vall, valm; + u8 *oobptr; + int i, j; + + for (i = 0; i < sectors; i++) { + oobptr = oob_ptr(chip, start + i); + vall = nfi_readl(nfc, NFI_FDML(i)); + valm = nfi_readl(nfc, NFI_FDMM(i)); + + for (j = 0; j < fdm->reg_size; j++) + oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8); + } +} + +static inline void mtk_nfc_write_fdm(struct nand_chip *chip) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; + u32 vall, valm; + u8 *oobptr; + int i, j; + + for (i = 0; i < chip->ecc.steps; i++) { + oobptr = oob_ptr(chip, i); + vall = 0; + valm = 0; + for (j = 0; j < 8; j++) { + if (j < 4) + vall |= (j < fdm->reg_size ? oobptr[j] : 0xff) + << (j * 8); + else + valm |= (j < fdm->reg_size ? oobptr[j] : 0xff) + << ((j - 4) * 8); + } + nfi_writel(nfc, vall, NFI_FDML(i)); + nfi_writel(nfc, valm, NFI_FDMM(i)); + } +} + +static int mtk_nfc_do_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int page, int len) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct device *dev = nfc->dev; + dma_addr_t addr; + u32 reg; + int ret; + + addr = dma_map_single(dev, (void *)buf, len, DMA_TO_DEVICE); + ret = dma_mapping_error(nfc->dev, addr); + if (ret) { + dev_err(nfc->dev, "dma mapping error\n"); + return -EINVAL; + } + + reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AHB | CNFG_DMA_BURST_EN; + nfi_writew(nfc, reg, NFI_CNFG); + + nfi_writel(nfc, chip->ecc.steps << CON_SEC_SHIFT, NFI_CON); + nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR); + nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN); + + init_completion(&nfc->done); + + reg = nfi_readl(nfc, NFI_CON) | CON_BWR; + nfi_writel(nfc, reg, NFI_CON); + nfi_writew(nfc, STAR_EN, NFI_STRDATA); + + ret = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500)); + if (!ret) { + dev_err(dev, "program ahb done timeout\n"); + nfi_writew(nfc, 0, NFI_INTR_EN); + ret = -ETIMEDOUT; + goto timeout; + } + + ret = readl_poll_timeout_atomic(nfc->regs + NFI_ADDRCNTR, reg, + ADDRCNTR_SEC(reg) >= chip->ecc.steps, + 10, MTK_TIMEOUT); + if (ret) + dev_err(dev, "hwecc write timeout\n"); + +timeout: + + dma_unmap_single(nfc->dev, addr, len, DMA_TO_DEVICE); + nfi_writel(nfc, 0, NFI_CON); + + return ret; +} + +static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int page, int raw) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + size_t len; + const u8 *bufpoi; + u32 reg; + int ret; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + if (!raw) { + /* OOB => FDM: from register, ECC: from HW */ + reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AUTO_FMT_EN; + nfi_writew(nfc, reg | CNFG_HW_ECC_EN, NFI_CNFG); + + nfc->ecc_cfg.op = ECC_ENCODE; + nfc->ecc_cfg.mode = ECC_NFI_MODE; + ret = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg); + if (ret) { + /* clear NFI config */ + reg = nfi_readw(nfc, NFI_CNFG); + reg &= ~(CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); + nfi_writew(nfc, reg, NFI_CNFG); + + return ret; + } + + memcpy(nfc->buffer, buf, mtd->writesize); + mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, raw); + bufpoi = nfc->buffer; + + /* write OOB into the FDM registers (OOB area in MTK NAND) */ + mtk_nfc_write_fdm(chip); + } else { + bufpoi = buf; + } + + len = mtd->writesize + (raw ? mtd->oobsize : 0); + ret = mtk_nfc_do_write_page(mtd, chip, bufpoi, page, len); + + if (!raw) + mtk_ecc_disable(nfc->ecc); + + if (ret) + return ret; + + return nand_prog_page_end_op(chip); +} + +static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, + int oob_on, int page) +{ + return mtk_nfc_write_page(mtd, chip, buf, page, 0); +} + +static int mtk_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_on, int pg) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + + mtk_nfc_format_page(mtd, buf); + return mtk_nfc_write_page(mtd, chip, nfc->buffer, pg, 1); +} + +static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, u32 offset, + u32 data_len, const u8 *buf, + int oob_on, int page) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + int ret; + + ret = mtk_nfc_format_subpage(mtd, offset, data_len, buf); + if (ret < 0) + return ret; + + /* use the data in the private buffer (now with FDM and CRC) */ + return mtk_nfc_write_page(mtd, chip, nfc->buffer, page, 1); +} + +static int mtk_nfc_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return mtk_nfc_write_page_raw(mtd, chip, NULL, 1, page); +} + +static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + struct mtk_ecc_stats stats; + int rc, i; + + rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE; + if (rc) { + memset(buf, 0xff, sectors * chip->ecc.size); + for (i = 0; i < sectors; i++) + memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size); + return 0; + } + + mtk_ecc_get_stats(nfc->ecc, &stats, sectors); + mtd->ecc_stats.corrected += stats.corrected; + mtd->ecc_stats.failed += stats.failed; + + return stats.bitflips; +} + +static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + u32 data_offs, u32 readlen, + u8 *bufpoi, int page, int raw) +{ + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + u32 spare = mtk_nand->spare_per_sector; + u32 column, sectors, start, end, reg; + dma_addr_t addr; + int bitflips; + size_t len; + u8 *buf; + int rc; + + start = data_offs / chip->ecc.size; + end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); + + sectors = end - start; + column = start * (chip->ecc.size + spare); + + len = sectors * chip->ecc.size + (raw ? sectors * spare : 0); + buf = bufpoi + start * chip->ecc.size; + + nand_read_page_op(chip, page, column, NULL, 0); + + addr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE); + rc = dma_mapping_error(nfc->dev, addr); + if (rc) { + dev_err(nfc->dev, "dma mapping error\n"); + + return -EINVAL; + } + + reg = nfi_readw(nfc, NFI_CNFG); + reg |= CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_AHB; + if (!raw) { + reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN; + nfi_writew(nfc, reg, NFI_CNFG); + + nfc->ecc_cfg.mode = ECC_NFI_MODE; + nfc->ecc_cfg.sectors = sectors; + nfc->ecc_cfg.op = ECC_DECODE; + rc = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg); + if (rc) { + dev_err(nfc->dev, "ecc enable\n"); + /* clear NFI_CNFG */ + reg &= ~(CNFG_DMA_BURST_EN | CNFG_AHB | CNFG_READ_EN | + CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); + nfi_writew(nfc, reg, NFI_CNFG); + dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); + + return rc; + } + } else { + nfi_writew(nfc, reg, NFI_CNFG); + } + + nfi_writel(nfc, sectors << CON_SEC_SHIFT, NFI_CON); + nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN); + nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR); + + init_completion(&nfc->done); + reg = nfi_readl(nfc, NFI_CON) | CON_BRD; + nfi_writel(nfc, reg, NFI_CON); + nfi_writew(nfc, STAR_EN, NFI_STRDATA); + + rc = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500)); + if (!rc) + dev_warn(nfc->dev, "read ahb/dma done timeout\n"); + + rc = readl_poll_timeout_atomic(nfc->regs + NFI_BYTELEN, reg, + ADDRCNTR_SEC(reg) >= sectors, 10, + MTK_TIMEOUT); + if (rc < 0) { + dev_err(nfc->dev, "subpage done timeout\n"); + bitflips = -EIO; + } else { + bitflips = 0; + if (!raw) { + rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE); + bitflips = rc < 0 ? -ETIMEDOUT : + mtk_nfc_update_ecc_stats(mtd, buf, sectors); + mtk_nfc_read_fdm(chip, start, sectors); + } + } + + dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); + + if (raw) + goto done; + + mtk_ecc_disable(nfc->ecc); + + if (clamp(mtk_nand->bad_mark.sec, start, end) == mtk_nand->bad_mark.sec) + mtk_nand->bad_mark.bm_swap(mtd, bufpoi, raw); +done: + nfi_writel(nfc, 0, NFI_CON); + + return bitflips; +} + +static int mtk_nfc_read_subpage_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, u32 off, + u32 len, u8 *p, int pg) +{ + return mtk_nfc_read_subpage(mtd, chip, off, len, p, pg, 0); +} + +static int mtk_nfc_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, u8 *p, + int oob_on, int pg) +{ + return mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, p, pg, 0); +} + +static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_on, int page) +{ + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; + int i, ret; + + memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); + ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, nfc->buffer, + page, 1); + if (ret < 0) + return ret; + + for (i = 0; i < chip->ecc.steps; i++) { + memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size); + + if (i == mtk_nand->bad_mark.sec) + mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); + + if (buf) + memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i), + chip->ecc.size); + } + + return ret; +} + +static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return mtk_nfc_read_page_raw(mtd, chip, NULL, 1, page); +} + +static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc) +{ + /* + * CNRNB: nand ready/busy register + * ------------------------------- + * 7:4: timeout register for polling the NAND busy/ready signal + * 0 : poll the status of the busy/ready signal after [7:4]*16 cycles. + */ + nfi_writew(nfc, 0xf1, NFI_CNRNB); + nfi_writel(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT); + + mtk_nfc_hw_reset(nfc); + + nfi_readl(nfc, NFI_INTR_STA); + nfi_writel(nfc, 0, NFI_INTR_EN); +} + +static irqreturn_t mtk_nfc_irq(int irq, void *id) +{ + struct mtk_nfc *nfc = id; + u16 sta, ien; + + sta = nfi_readw(nfc, NFI_INTR_STA); + ien = nfi_readw(nfc, NFI_INTR_EN); + + if (!(sta & ien)) + return IRQ_NONE; + + nfi_writew(nfc, ~sta & ien, NFI_INTR_EN); + complete(&nfc->done); + + return IRQ_HANDLED; +} + +static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk) +{ + int ret; + + ret = clk_prepare_enable(clk->nfi_clk); + if (ret) { + dev_err(dev, "failed to enable nfi clk\n"); + return ret; + } + + ret = clk_prepare_enable(clk->pad_clk); + if (ret) { + dev_err(dev, "failed to enable pad clk\n"); + clk_disable_unprepare(clk->nfi_clk); + return ret; + } + + return 0; +} + +static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk) +{ + clk_disable_unprepare(clk->nfi_clk); + clk_disable_unprepare(clk->pad_clk); +} + +static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oob_region) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; + u32 eccsteps; + + eccsteps = mtd->writesize / chip->ecc.size; + + if (section >= eccsteps) + return -ERANGE; + + oob_region->length = fdm->reg_size - fdm->ecc_size; + oob_region->offset = section * fdm->reg_size + fdm->ecc_size; + + return 0; +} + +static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oob_region) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + u32 eccsteps; + + if (section) + return -ERANGE; + + eccsteps = mtd->writesize / chip->ecc.size; + oob_region->offset = mtk_nand->fdm.reg_size * eccsteps; + oob_region->length = mtd->oobsize - oob_region->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = { + .free = mtk_nfc_ooblayout_free, + .ecc = mtk_nfc_ooblayout_ecc, +}; + +static void mtk_nfc_set_fdm(struct mtk_nfc_fdm *fdm, struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand); + struct mtk_nfc *nfc = nand_get_controller_data(nand); + u32 ecc_bytes; + + ecc_bytes = DIV_ROUND_UP(nand->ecc.strength * + mtk_ecc_get_parity_bits(nfc->ecc), 8); + + fdm->reg_size = chip->spare_per_sector - ecc_bytes; + if (fdm->reg_size > NFI_FDM_MAX_SIZE) + fdm->reg_size = NFI_FDM_MAX_SIZE; + + /* bad block mark storage */ + fdm->ecc_size = 1; +} + +static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl, + struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + + if (mtd->writesize == 512) { + bm_ctl->bm_swap = mtk_nfc_no_bad_mark_swap; + } else { + bm_ctl->bm_swap = mtk_nfc_bad_mark_swap; + bm_ctl->sec = mtd->writesize / mtk_data_len(nand); + bm_ctl->pos = mtd->writesize % mtk_data_len(nand); + } +} + +static int mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mtk_nfc *nfc = nand_get_controller_data(nand); + const u8 *spare = nfc->caps->spare_size; + u32 eccsteps, i, closest_spare = 0; + + eccsteps = mtd->writesize / nand->ecc.size; + *sps = mtd->oobsize / eccsteps; + + if (nand->ecc.size == 1024) + *sps >>= 1; + + if (*sps < MTK_NFC_MIN_SPARE) + return -EINVAL; + + for (i = 0; i < nfc->caps->num_spare_size; i++) { + if (*sps >= spare[i] && spare[i] >= spare[closest_spare]) { + closest_spare = i; + if (*sps == spare[i]) + break; + } + } + + *sps = spare[closest_spare]; + + if (nand->ecc.size == 1024) + *sps <<= 1; + + return 0; +} + +static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mtk_nfc *nfc = nand_get_controller_data(nand); + u32 spare; + int free, ret; + + /* support only ecc hw mode */ + if (nand->ecc.mode != NAND_ECC_HW) { + dev_err(dev, "ecc.mode not supported\n"); + return -EINVAL; + } + + /* if optional dt settings not present */ + if (!nand->ecc.size || !nand->ecc.strength) { + /* use datasheet requirements */ + nand->ecc.strength = nand->ecc_strength_ds; + nand->ecc.size = nand->ecc_step_ds; + + /* + * align eccstrength and eccsize + * this controller only supports 512 and 1024 sizes + */ + if (nand->ecc.size < 1024) { + if (mtd->writesize > 512 && + nfc->caps->max_sector_size > 512) { + nand->ecc.size = 1024; + nand->ecc.strength <<= 1; + } else { + nand->ecc.size = 512; + } + } else { + nand->ecc.size = 1024; + } + + ret = mtk_nfc_set_spare_per_sector(&spare, mtd); + if (ret) + return ret; + + /* calculate oob bytes except ecc parity data */ + free = (nand->ecc.strength * mtk_ecc_get_parity_bits(nfc->ecc) + + 7) >> 3; + free = spare - free; + + /* + * enhance ecc strength if oob left is bigger than max FDM size + * or reduce ecc strength if oob size is not enough for ecc + * parity data. + */ + if (free > NFI_FDM_MAX_SIZE) { + spare -= NFI_FDM_MAX_SIZE; + nand->ecc.strength = (spare << 3) / + mtk_ecc_get_parity_bits(nfc->ecc); + } else if (free < 0) { + spare -= NFI_FDM_MIN_SIZE; + nand->ecc.strength = (spare << 3) / + mtk_ecc_get_parity_bits(nfc->ecc); + } + } + + mtk_ecc_adjust_strength(nfc->ecc, &nand->ecc.strength); + + dev_info(dev, "eccsize %d eccstrength %d\n", + nand->ecc.size, nand->ecc.strength); + + return 0; +} + +static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, + struct device_node *np) +{ + struct mtk_nfc_nand_chip *chip; + struct nand_chip *nand; + struct mtd_info *mtd; + int nsels, len; + u32 tmp; + int ret; + int i; + + if (!of_get_property(np, "reg", &nsels)) + return -ENODEV; + + nsels /= sizeof(u32); + if (!nsels || nsels > MTK_NAND_MAX_NSELS) { + dev_err(dev, "invalid reg property size %d\n", nsels); + return -EINVAL; + } + + chip = devm_kzalloc(dev, sizeof(*chip) + nsels * sizeof(u8), + GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->nsels = nsels; + for (i = 0; i < nsels; i++) { + ret = of_property_read_u32_index(np, "reg", i, &tmp); + if (ret) { + dev_err(dev, "reg property failure : %d\n", ret); + return ret; + } + chip->sels[i] = tmp; + } + + nand = &chip->nand; + nand->controller = &nfc->controller; + + nand_set_flash_node(nand, np); + nand_set_controller_data(nand, nfc); + + nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ; + nand->dev_ready = mtk_nfc_dev_ready; + nand->select_chip = mtk_nfc_select_chip; + nand->write_byte = mtk_nfc_write_byte; + nand->write_buf = mtk_nfc_write_buf; + nand->read_byte = mtk_nfc_read_byte; + nand->read_buf = mtk_nfc_read_buf; + nand->cmd_ctrl = mtk_nfc_cmd_ctrl; + nand->setup_data_interface = mtk_nfc_setup_data_interface; + + /* set default mode in case dt entry is missing */ + nand->ecc.mode = NAND_ECC_HW; + + nand->ecc.write_subpage = mtk_nfc_write_subpage_hwecc; + nand->ecc.write_page_raw = mtk_nfc_write_page_raw; + nand->ecc.write_page = mtk_nfc_write_page_hwecc; + nand->ecc.write_oob_raw = mtk_nfc_write_oob_std; + nand->ecc.write_oob = mtk_nfc_write_oob_std; + + nand->ecc.read_subpage = mtk_nfc_read_subpage_hwecc; + nand->ecc.read_page_raw = mtk_nfc_read_page_raw; + nand->ecc.read_page = mtk_nfc_read_page_hwecc; + nand->ecc.read_oob_raw = mtk_nfc_read_oob_std; + nand->ecc.read_oob = mtk_nfc_read_oob_std; + + mtd = nand_to_mtd(nand); + mtd->owner = THIS_MODULE; + mtd->dev.parent = dev; + mtd->name = MTK_NAME; + mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops); + + mtk_nfc_hw_init(nfc); + + ret = nand_scan_ident(mtd, nsels, NULL); + if (ret) + return ret; + + /* store bbt magic in page, cause OOB is not protected */ + if (nand->bbt_options & NAND_BBT_USE_FLASH) + nand->bbt_options |= NAND_BBT_NO_OOB; + + ret = mtk_nfc_ecc_init(dev, mtd); + if (ret) + return -EINVAL; + + if (nand->options & NAND_BUSWIDTH_16) { + dev_err(dev, "16bits buswidth not supported"); + return -EINVAL; + } + + ret = mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd); + if (ret) + return ret; + + mtk_nfc_set_fdm(&chip->fdm, mtd); + mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, mtd); + + len = mtd->writesize + mtd->oobsize; + nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL); + if (!nfc->buffer) + return -ENOMEM; + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); + if (ret) { + dev_err(dev, "mtd parse partition error\n"); + nand_release(mtd); + return ret; + } + + list_add_tail(&chip->node, &nfc->chips); + + return 0; +} + +static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc) +{ + struct device_node *np = dev->of_node; + struct device_node *nand_np; + int ret; + + for_each_child_of_node(np, nand_np) { + ret = mtk_nfc_nand_chip_init(dev, nfc, nand_np); + if (ret) { + of_node_put(nand_np); + return ret; + } + } + + return 0; +} + +static const struct mtk_nfc_caps mtk_nfc_caps_mt2701 = { + .spare_size = spare_size_mt2701, + .num_spare_size = 16, + .pageformat_spare_shift = 4, + .nfi_clk_div = 1, + .max_sector = 16, + .max_sector_size = 1024, +}; + +static const struct mtk_nfc_caps mtk_nfc_caps_mt2712 = { + .spare_size = spare_size_mt2712, + .num_spare_size = 19, + .pageformat_spare_shift = 16, + .nfi_clk_div = 2, + .max_sector = 16, + .max_sector_size = 1024, +}; + +static const struct mtk_nfc_caps mtk_nfc_caps_mt7622 = { + .spare_size = spare_size_mt7622, + .num_spare_size = 4, + .pageformat_spare_shift = 4, + .nfi_clk_div = 1, + .max_sector = 8, + .max_sector_size = 512, +}; + +static const struct of_device_id mtk_nfc_id_table[] = { + { + .compatible = "mediatek,mt2701-nfc", + .data = &mtk_nfc_caps_mt2701, + }, { + .compatible = "mediatek,mt2712-nfc", + .data = &mtk_nfc_caps_mt2712, + }, { + .compatible = "mediatek,mt7622-nfc", + .data = &mtk_nfc_caps_mt7622, + }, + {} +}; +MODULE_DEVICE_TABLE(of, mtk_nfc_id_table); + +static int mtk_nfc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct mtk_nfc *nfc; + struct resource *res; + const struct of_device_id *of_nfc_id = NULL; + int ret, irq; + + nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + spin_lock_init(&nfc->controller.lock); + init_waitqueue_head(&nfc->controller.wq); + INIT_LIST_HEAD(&nfc->chips); + + /* probe defer if not ready */ + nfc->ecc = of_mtk_ecc_get(np); + if (IS_ERR(nfc->ecc)) + return PTR_ERR(nfc->ecc); + else if (!nfc->ecc) + return -ENODEV; + + nfc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(nfc->regs)) { + ret = PTR_ERR(nfc->regs); + goto release_ecc; + } + + nfc->clk.nfi_clk = devm_clk_get(dev, "nfi_clk"); + if (IS_ERR(nfc->clk.nfi_clk)) { + dev_err(dev, "no clk\n"); + ret = PTR_ERR(nfc->clk.nfi_clk); + goto release_ecc; + } + + nfc->clk.pad_clk = devm_clk_get(dev, "pad_clk"); + if (IS_ERR(nfc->clk.pad_clk)) { + dev_err(dev, "no pad clk\n"); + ret = PTR_ERR(nfc->clk.pad_clk); + goto release_ecc; + } + + ret = mtk_nfc_enable_clk(dev, &nfc->clk); + if (ret) + goto release_ecc; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no nfi irq resource\n"); + ret = -EINVAL; + goto clk_disable; + } + + ret = devm_request_irq(dev, irq, mtk_nfc_irq, 0x0, "mtk-nand", nfc); + if (ret) { + dev_err(dev, "failed to request nfi irq\n"); + goto clk_disable; + } + + ret = dma_set_mask(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "failed to set dma mask\n"); + goto clk_disable; + } + + of_nfc_id = of_match_device(mtk_nfc_id_table, &pdev->dev); + if (!of_nfc_id) { + ret = -ENODEV; + goto clk_disable; + } + + nfc->caps = of_nfc_id->data; + + platform_set_drvdata(pdev, nfc); + + ret = mtk_nfc_nand_chips_init(dev, nfc); + if (ret) { + dev_err(dev, "failed to init nand chips\n"); + goto clk_disable; + } + + return 0; + +clk_disable: + mtk_nfc_disable_clk(&nfc->clk); + +release_ecc: + mtk_ecc_release(nfc->ecc); + + return ret; +} + +static int mtk_nfc_remove(struct platform_device *pdev) +{ + struct mtk_nfc *nfc = platform_get_drvdata(pdev); + struct mtk_nfc_nand_chip *chip; + + while (!list_empty(&nfc->chips)) { + chip = list_first_entry(&nfc->chips, struct mtk_nfc_nand_chip, + node); + nand_release(nand_to_mtd(&chip->nand)); + list_del(&chip->node); + } + + mtk_ecc_release(nfc->ecc); + mtk_nfc_disable_clk(&nfc->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_nfc_suspend(struct device *dev) +{ + struct mtk_nfc *nfc = dev_get_drvdata(dev); + + mtk_nfc_disable_clk(&nfc->clk); + + return 0; +} + +static int mtk_nfc_resume(struct device *dev) +{ + struct mtk_nfc *nfc = dev_get_drvdata(dev); + struct mtk_nfc_nand_chip *chip; + struct nand_chip *nand; + int ret; + u32 i; + + udelay(200); + + ret = mtk_nfc_enable_clk(dev, &nfc->clk); + if (ret) + return ret; + + /* reset NAND chip if VCC was powered off */ + list_for_each_entry(chip, &nfc->chips, node) { + nand = &chip->nand; + for (i = 0; i < chip->nsels; i++) + nand_reset(nand, i); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume); +#endif + +static struct platform_driver mtk_nfc_driver = { + .probe = mtk_nfc_probe, + .remove = mtk_nfc_remove, + .driver = { + .name = MTK_NAME, + .of_match_table = mtk_nfc_id_table, +#ifdef CONFIG_PM_SLEEP + .pm = &mtk_nfc_pm_ops, +#endif + }, +}; + +module_platform_driver(mtk_nfc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xiaolei Li "); +MODULE_DESCRIPTION("MTK Nand Flash Controller Driver"); diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c new file mode 100644 index 000000000000..87b5ee66e501 --- /dev/null +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -0,0 +1,1966 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Sascha Hauer, kernel@pengutronix.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVER_NAME "mxc_nand" + +/* Addresses for NFC registers */ +#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00) +#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04) +#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06) +#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08) +#define NFC_V1_V2_CONFIG (host->regs + 0x0a) +#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c) +#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e) +#define NFC_V1_V2_RSLTSPARE_AREA (host->regs + 0x10) +#define NFC_V1_V2_WRPROT (host->regs + 0x12) +#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) +#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) +#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20) +#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24) +#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28) +#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c) +#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22) +#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26) +#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a) +#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e) +#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18) +#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) +#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) + +#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) +#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2) +#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) +#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) +#define NFC_V1_V2_CONFIG1_BIG (1 << 5) +#define NFC_V1_V2_CONFIG1_RST (1 << 6) +#define NFC_V1_V2_CONFIG1_CE (1 << 7) +#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) +#define NFC_V2_CONFIG1_PPB(x) (((x) & 0x3) << 9) +#define NFC_V2_CONFIG1_FP_INT (1 << 11) + +#define NFC_V1_V2_CONFIG2_INT (1 << 15) + +/* + * Operation modes for the NFC. Valid for v1, v2 and v3 + * type controllers. + */ +#define NFC_CMD (1 << 0) +#define NFC_ADDR (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) + +#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00) +#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04) + +#define NFC_V3_CONFIG1 (host->regs_axi + 0x34) +#define NFC_V3_CONFIG1_SP_EN (1 << 0) +#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4) + +#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38) + +#define NFC_V3_LAUNCH (host->regs_axi + 0x40) + +#define NFC_V3_WRPROT (host->regs_ip + 0x0) +#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0) +#define NFC_V3_WRPROT_LOCK (1 << 1) +#define NFC_V3_WRPROT_UNLOCK (1 << 2) +#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) + +#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04) + +#define NFC_V3_CONFIG2 (host->regs_ip + 0x24) +#define NFC_V3_CONFIG2_PS_512 (0 << 0) +#define NFC_V3_CONFIG2_PS_2048 (1 << 0) +#define NFC_V3_CONFIG2_PS_4096 (2 << 0) +#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) +#define NFC_V3_CONFIG2_ECC_EN (1 << 3) +#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) +#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5) +#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) +#define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift) +#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12) +#define NFC_V3_CONFIG2_INT_MSK (1 << 15) +#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) +#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) + +#define NFC_V3_CONFIG3 (host->regs_ip + 0x28) +#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) +#define NFC_V3_CONFIG3_FW8 (1 << 3) +#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) +#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12) +#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) +#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) + +#define NFC_V3_IPC (host->regs_ip + 0x2C) +#define NFC_V3_IPC_CREQ (1 << 0) +#define NFC_V3_IPC_INT (1 << 31) + +#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) + +struct mxc_nand_host; + +struct mxc_nand_devtype_data { + void (*preset)(struct mtd_info *); + int (*read_page)(struct nand_chip *chip, void *buf, void *oob, bool ecc, + int page); + void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); + void (*send_addr)(struct mxc_nand_host *, uint16_t, int); + void (*send_page)(struct mtd_info *, unsigned int); + void (*send_read_id)(struct mxc_nand_host *); + uint16_t (*get_dev_status)(struct mxc_nand_host *); + int (*check_int)(struct mxc_nand_host *); + void (*irq_control)(struct mxc_nand_host *, int); + u32 (*get_ecc_status)(struct mxc_nand_host *); + const struct mtd_ooblayout_ops *ooblayout; + void (*select_chip)(struct mtd_info *mtd, int chip); + int (*setup_data_interface)(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf); + void (*enable_hwecc)(struct nand_chip *chip, bool enable); + + /* + * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked + * (CONFIG1:INT_MSK is set). To handle this the driver uses + * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK + */ + int irqpending_quirk; + int needs_ip; + + size_t regs_offset; + size_t spare0_offset; + size_t axi_offset; + + int spare_len; + int eccbytes; + int eccsize; + int ppb_shift; +}; + +struct mxc_nand_host { + struct nand_chip nand; + struct device *dev; + + void __iomem *spare0; + void __iomem *main_area0; + + void __iomem *base; + void __iomem *regs; + void __iomem *regs_axi; + void __iomem *regs_ip; + int status_request; + struct clk *clk; + int clk_act; + int irq; + int eccsize; + int used_oobsize; + int active_cs; + + struct completion op_completion; + + uint8_t *data_buf; + unsigned int buf_start; + + const struct mxc_nand_devtype_data *devtype_data; + struct mxc_nand_platform_data pdata; +}; + +static const char * const part_probes[] = { + "cmdlinepart", "RedBoot", "ofpart", NULL }; + +static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) +{ + int i; + u32 *t = trg; + const __iomem u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + *t++ = __raw_readl(s++); +} + +static void memcpy16_fromio(void *trg, const void __iomem *src, size_t size) +{ + int i; + u16 *t = trg; + const __iomem u16 *s = src; + + /* We assume that src (IO) is always 32bit aligned */ + if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) { + memcpy32_fromio(trg, src, size); + return; + } + + for (i = 0; i < (size >> 1); i++) + *t++ = __raw_readw(s++); +} + +static inline void memcpy32_toio(void __iomem *trg, const void *src, int size) +{ + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy(trg, src, size / 4); +} + +static void memcpy16_toio(void __iomem *trg, const void *src, int size) +{ + int i; + __iomem u16 *t = trg; + const u16 *s = src; + + /* We assume that trg (IO) is always 32bit aligned */ + if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) { + memcpy32_toio(trg, src, size); + return; + } + + for (i = 0; i < (size >> 1); i++) + __raw_writew(*s++, t++); +} + +/* + * The controller splits a page into data chunks of 512 bytes + partial oob. + * There are writesize / 512 such chunks, the size of the partial oob parts is + * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then + * contains additionally the byte lost by rounding (if any). + * This function handles the needed shuffling between host->data_buf (which + * holds a page in natural order, i.e. writesize bytes data + oobsize bytes + * spare) and the NFC buffer. + */ +static void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(this); + u16 i, oob_chunk_size; + u16 num_chunks = mtd->writesize / 512; + + u8 *d = buf; + u8 __iomem *s = host->spare0; + u16 sparebuf_size = host->devtype_data->spare_len; + + /* size of oob chunk for all but possibly the last one */ + oob_chunk_size = (host->used_oobsize / num_chunks) & ~1; + + if (bfrom) { + for (i = 0; i < num_chunks - 1; i++) + memcpy16_fromio(d + i * oob_chunk_size, + s + i * sparebuf_size, + oob_chunk_size); + + /* the last chunk */ + memcpy16_fromio(d + i * oob_chunk_size, + s + i * sparebuf_size, + host->used_oobsize - i * oob_chunk_size); + } else { + for (i = 0; i < num_chunks - 1; i++) + memcpy16_toio(&s[i * sparebuf_size], + &d[i * oob_chunk_size], + oob_chunk_size); + + /* the last chunk */ + memcpy16_toio(&s[i * sparebuf_size], + &d[i * oob_chunk_size], + host->used_oobsize - i * oob_chunk_size); + } +} + +/* + * MXC NANDFC can only perform full page+spare or spare-only read/write. When + * the upper layers perform a read/write buf operation, the saved column address + * is used to index into the full page. So usually this function is called with + * column == 0 (unless no column cycle is needed indicated by column == -1) + */ +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + /* Write out column address, if necessary */ + if (column != -1) { + host->devtype_data->send_addr(host, column & 0xff, + page_addr == -1); + if (mtd->writesize > 512) + /* another col addr cycle for 2k page */ + host->devtype_data->send_addr(host, + (column >> 8) & 0xff, + false); + } + + /* Write out page address, if necessary */ + if (page_addr != -1) { + /* paddr_0 - p_addr_7 */ + host->devtype_data->send_addr(host, (page_addr & 0xff), false); + + if (mtd->writesize > 512) { + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); + } else + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); + } else { + if (nand_chip->options & NAND_ROW_ADDR_3) { + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); + } else + /* paddr_8 - paddr_15 */ + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); + } + } +} + +static int check_int_v3(struct mxc_nand_host *host) +{ + uint32_t tmp; + + tmp = readl(NFC_V3_IPC); + if (!(tmp & NFC_V3_IPC_INT)) + return 0; + + tmp &= ~NFC_V3_IPC_INT; + writel(tmp, NFC_V3_IPC); + + return 1; +} + +static int check_int_v1_v2(struct mxc_nand_host *host) +{ + uint32_t tmp; + + tmp = readw(NFC_V1_V2_CONFIG2); + if (!(tmp & NFC_V1_V2_CONFIG2_INT)) + return 0; + + if (!host->devtype_data->irqpending_quirk) + writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); + + return 1; +} + +static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) +{ + uint16_t tmp; + + tmp = readw(NFC_V1_V2_CONFIG1); + + if (activate) + tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK; + else + tmp |= NFC_V1_V2_CONFIG1_INT_MSK; + + writew(tmp, NFC_V1_V2_CONFIG1); +} + +static void irq_control_v3(struct mxc_nand_host *host, int activate) +{ + uint32_t tmp; + + tmp = readl(NFC_V3_CONFIG2); + + if (activate) + tmp &= ~NFC_V3_CONFIG2_INT_MSK; + else + tmp |= NFC_V3_CONFIG2_INT_MSK; + + writel(tmp, NFC_V3_CONFIG2); +} + +static void irq_control(struct mxc_nand_host *host, int activate) +{ + if (host->devtype_data->irqpending_quirk) { + if (activate) + enable_irq(host->irq); + else + disable_irq_nosync(host->irq); + } else { + host->devtype_data->irq_control(host, activate); + } +} + +static u32 get_ecc_status_v1(struct mxc_nand_host *host) +{ + return readw(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v2(struct mxc_nand_host *host) +{ + return readl(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v3(struct mxc_nand_host *host) +{ + return readl(NFC_V3_ECC_STATUS_RESULT); +} + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + struct mxc_nand_host *host = dev_id; + + if (!host->devtype_data->check_int(host)) + return IRQ_NONE; + + irq_control(host, 0); + + complete(&host->op_completion); + + return IRQ_HANDLED; +} + +/* This function polls the NANDFC to wait for the basic operation to + * complete by checking the INT bit of config2 register. + */ +static int wait_op_done(struct mxc_nand_host *host, int useirq) +{ + int ret = 0; + + /* + * If operation is already complete, don't bother to setup an irq or a + * loop. + */ + if (host->devtype_data->check_int(host)) + return 0; + + if (useirq) { + unsigned long timeout; + + reinit_completion(&host->op_completion); + + irq_control(host, 1); + + timeout = wait_for_completion_timeout(&host->op_completion, HZ); + if (!timeout && !host->devtype_data->check_int(host)) { + dev_dbg(host->dev, "timeout waiting for irq\n"); + ret = -ETIMEDOUT; + } + } else { + int max_retries = 8000; + int done; + + do { + udelay(1); + + done = host->devtype_data->check_int(host); + if (done) + break; + + } while (--max_retries); + + if (!done) { + dev_dbg(host->dev, "timeout polling for completion\n"); + ret = -ETIMEDOUT; + } + } + + WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq); + + return ret; +} + +static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) +{ + /* fill command */ + writel(cmd, NFC_V3_FLASH_CMD); + + /* send out command */ + writel(NFC_CMD, NFC_V3_LAUNCH); + + /* Wait for operation to complete */ + wait_op_done(host, useirq); +} + +/* This function issues the specified command to the NAND device and + * waits for completion. */ +static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) +{ + dev_dbg(host->dev, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); + + writew(cmd, NFC_V1_V2_FLASH_CMD); + writew(NFC_CMD, NFC_V1_V2_CONFIG2); + + if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) { + int max_retries = 100; + /* Reset completion is indicated by NFC_CONFIG2 */ + /* being set to 0 */ + while (max_retries-- > 0) { + if (readw(NFC_V1_V2_CONFIG2) == 0) { + break; + } + udelay(1); + } + if (max_retries < 0) + dev_dbg(host->dev, "%s: RESET failed\n", __func__); + } else { + /* Wait for operation to complete */ + wait_op_done(host, useirq); + } +} + +static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast) +{ + /* fill address */ + writel(addr, NFC_V3_FLASH_ADDR0); + + /* send out address */ + writel(NFC_ADDR, NFC_V3_LAUNCH); + + wait_op_done(host, 0); +} + +/* This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. */ +static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast) +{ + dev_dbg(host->dev, "send_addr(host, 0x%x %d)\n", addr, islast); + + writew(addr, NFC_V1_V2_FLASH_ADDR); + writew(NFC_ADDR, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, islast); +} + +static void send_page_v3(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint32_t tmp; + + tmp = readl(NFC_V3_CONFIG1); + tmp &= ~(7 << 4); + writel(tmp, NFC_V3_CONFIG1); + + /* transfer data from NFC ram to nand */ + writel(ops, NFC_V3_LAUNCH); + + wait_op_done(host, false); +} + +static void send_page_v2(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + /* NANDFC buffer 0 is used for page read/write */ + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + writew(ops, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); +} + +static void send_page_v1(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int bufs, i; + + if (mtd->writesize > 512) + bufs = 4; + else + bufs = 1; + + for (i = 0; i < bufs; i++) { + + /* NANDFC buffer 0 is used for page read/write */ + writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); + + writew(ops, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); + } +} + +static void send_read_id_v3(struct mxc_nand_host *host) +{ + /* Read ID into main buffer */ + writel(NFC_ID, NFC_V3_LAUNCH); + + wait_op_done(host, true); + + memcpy32_fromio(host->data_buf, host->main_area0, 16); +} + +/* Request the NANDFC to perform a read of the NAND device ID. */ +static void send_read_id_v1_v2(struct mxc_nand_host *host) +{ + /* NANDFC buffer 0 is used for device ID output */ + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + writew(NFC_ID, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); + + memcpy32_fromio(host->data_buf, host->main_area0, 16); +} + +static uint16_t get_dev_status_v3(struct mxc_nand_host *host) +{ + writew(NFC_STATUS, NFC_V3_LAUNCH); + wait_op_done(host, true); + + return readl(NFC_V3_CONFIG1) >> 16; +} + +/* This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. */ +static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) +{ + void __iomem *main_buf = host->main_area0; + uint32_t store; + uint16_t ret; + + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + /* + * The device status is stored in main_area0. To + * prevent corruption of the buffer save the value + * and restore it afterwards. + */ + store = readl(main_buf); + + writew(NFC_STATUS, NFC_V1_V2_CONFIG2); + wait_op_done(host, true); + + ret = readw(main_buf); + + writel(store, main_buf); + + return ret; +} + +static void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + uint16_t config1; + + if (chip->ecc.mode != NAND_ECC_HW) + return; + + config1 = readw(NFC_V1_V2_CONFIG1); + + if (enable) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + else + config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN; + + writew(config1, NFC_V1_V2_CONFIG1); +} + +static void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + uint32_t config2; + + if (chip->ecc.mode != NAND_ECC_HW) + return; + + config2 = readl(NFC_V3_CONFIG2); + + if (enable) + config2 |= NFC_V3_CONFIG2_ECC_EN; + else + config2 &= ~NFC_V3_CONFIG2_ECC_EN; + + writel(config2, NFC_V3_CONFIG2); +} + +/* This functions is used by upper layer to checks if device is ready */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles R/B internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, + bool ecc, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + unsigned int bitflips_corrected = 0; + int no_subpages; + int i; + + host->devtype_data->enable_hwecc(chip, ecc); + + host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); + mxc_do_addr_cycle(mtd, 0, page); + + if (mtd->writesize > 512) + host->devtype_data->send_cmd(host, NAND_CMD_READSTART, true); + + no_subpages = mtd->writesize >> 9; + + for (i = 0; i < no_subpages; i++) { + uint16_t ecc_stats; + + /* NANDFC buffer 0 is used for page read/write */ + writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); + + writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); + + ecc_stats = get_ecc_status_v1(host); + + ecc_stats >>= 2; + + if (buf && ecc) { + switch (ecc_stats & 0x3) { + case 0: + default: + break; + case 1: + mtd->ecc_stats.corrected++; + bitflips_corrected = 1; + break; + case 2: + mtd->ecc_stats.failed++; + break; + } + } + } + + if (buf) + memcpy32_fromio(buf, host->main_area0, mtd->writesize); + if (oob) + copy_spare(mtd, true, oob); + + return bitflips_corrected; +} + +static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf, + void *oob, bool ecc, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + unsigned int max_bitflips = 0; + u32 ecc_stat, err; + int no_subpages; + u8 ecc_bit_mask, err_limit; + + host->devtype_data->enable_hwecc(chip, ecc); + + host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); + mxc_do_addr_cycle(mtd, 0, page); + + if (mtd->writesize > 512) + host->devtype_data->send_cmd(host, + NAND_CMD_READSTART, true); + + host->devtype_data->send_page(mtd, NFC_OUTPUT); + + if (buf) + memcpy32_fromio(buf, host->main_area0, mtd->writesize); + if (oob) + copy_spare(mtd, true, oob); + + ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; + err_limit = (host->eccsize == 4) ? 0x4 : 0x8; + + no_subpages = mtd->writesize >> 9; + + ecc_stat = host->devtype_data->get_ecc_status(host); + + do { + err = ecc_stat & ecc_bit_mask; + if (err > err_limit) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += err; + max_bitflips = max_t(unsigned int, max_bitflips, err); + } + + ecc_stat >>= 4; + } while (--no_subpages); + + return max_bitflips; +} + +static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + void *oob_buf; + + if (oob_required) + oob_buf = chip->oob_poi; + else + oob_buf = NULL; + + return host->devtype_data->read_page(chip, buf, oob_buf, 1, page); +} + +static int mxc_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + void *oob_buf; + + if (oob_required) + oob_buf = chip->oob_poi; + else + oob_buf = NULL; + + return host->devtype_data->read_page(chip, buf, oob_buf, 0, page); +} + +static int mxc_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + return host->devtype_data->read_page(chip, NULL, chip->oob_poi, 0, + page); +} + +static int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf, + bool ecc, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + + host->devtype_data->enable_hwecc(chip, ecc); + + host->devtype_data->send_cmd(host, NAND_CMD_SEQIN, false); + mxc_do_addr_cycle(mtd, 0, page); + + memcpy32_toio(host->main_area0, buf, mtd->writesize); + copy_spare(mtd, false, chip->oob_poi); + + host->devtype_data->send_page(mtd, NFC_INPUT); + host->devtype_data->send_cmd(host, NAND_CMD_PAGEPROG, true); + mxc_do_addr_cycle(mtd, 0, page); + + return 0; +} + +static int mxc_nand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + return mxc_nand_write_page(chip, buf, true, page); +} + +static int mxc_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + return mxc_nand_write_page(chip, buf, false, page); +} + +static int mxc_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + memset(host->data_buf, 0xff, mtd->writesize); + + return mxc_nand_write_page(chip, host->data_buf, false, page); +} + +static u_char mxc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint8_t ret; + + /* Check for status request */ + if (host->status_request) + return host->devtype_data->get_dev_status(host) & 0xFF; + + if (nand_chip->options & NAND_BUSWIDTH_16) { + /* only take the lower byte of each word */ + ret = *(uint16_t *)(host->data_buf + host->buf_start); + + host->buf_start += 2; + } else { + ret = *(uint8_t *)(host->data_buf + host->buf_start); + host->buf_start++; + } + + dev_dbg(host->dev, "%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start); + return ret; +} + +static uint16_t mxc_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint16_t ret; + + ret = *(uint16_t *)(host->data_buf + host->buf_start); + host->buf_start += 2; + + return ret; +} + +/* Write data of length len to buffer buf. The data to be + * written on NAND Flash is first copied to RAMbuffer. After the Data Input + * Operation by the NFC, the data is written to NAND Flash */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + u16 col = host->buf_start; + int n = mtd->oobsize + mtd->writesize - col; + + n = min(n, len); + + memcpy(host->data_buf + col, buf, n); + + host->buf_start += n; +} + +/* Read the data buffer from the NAND Flash. To read the data from NAND + * Flash first the data output cycle is initiated by the NFC, which copies + * the data to RAMbuffer. This data of length len is then copied to buffer buf. + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + u16 col = host->buf_start; + int n = mtd->oobsize + mtd->writesize - col; + + n = min(n, len); + + memcpy(buf, host->data_buf + col, n); + + host->buf_start += n; +} + +/* This function is used by upper layer for select and + * deselect of the NAND chip */ +static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + if (chip == -1) { + /* Disable the NFC clock */ + if (host->clk_act) { + clk_disable_unprepare(host->clk); + host->clk_act = 0; + } + return; + } + + if (!host->clk_act) { + /* Enable the NFC clock */ + clk_prepare_enable(host->clk); + host->clk_act = 1; + } +} + +static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + if (chip == -1) { + /* Disable the NFC clock */ + if (host->clk_act) { + clk_disable_unprepare(host->clk); + host->clk_act = 0; + } + return; + } + + if (!host->clk_act) { + /* Enable the NFC clock */ + clk_prepare_enable(host->clk); + host->clk_act = 1; + } + + host->active_cs = chip; + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); +} + +#define MXC_V1_ECCBYTES 5 + +static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 6; + oobregion->length = MXC_V1_ECCBYTES; + + return 0; +} + +static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section > nand_chip->ecc.steps) + return -ERANGE; + + if (!section) { + if (mtd->writesize <= 512) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 2; + oobregion->length = 4; + } + } else { + oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6; + if (section < nand_chip->ecc.steps) + oobregion->length = (section * 16) + 6 - + oobregion->offset; + else + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = { + .ecc = mxc_v1_ooblayout_ecc, + .free = mxc_v1_ooblayout_free, +}; + +static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * stepsize) + 7; + oobregion->length = nand_chip->ecc.bytes; + + return 0; +} + +static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + if (!section) { + if (mtd->writesize <= 512) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 2; + oobregion->length = 4; + } + } else { + oobregion->offset = section * stepsize; + oobregion->length = 7; + } + + return 0; +} + +static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = { + .ecc = mxc_v2_ooblayout_ecc, + .free = mxc_v2_ooblayout_free, +}; + +/* + * v2 and v3 type controllers can do 4bit or 8bit ecc depending + * on how much oob the nand chip has. For 8bit ecc we need at least + * 26 bytes of oob data per 512 byte block. + */ +static int get_eccsize(struct mtd_info *mtd) +{ + int oobbytes_per_512 = 0; + + oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize; + + if (oobbytes_per_512 < 26) + return 4; + else + return 8; +} + +static void preset_v1(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint16_t config1 = 0; + + if (nand_chip->ecc.mode == NAND_ECC_HW && mtd->writesize) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + + if (!host->devtype_data->irqpending_quirk) + config1 |= NFC_V1_V2_CONFIG1_INT_MSK; + + host->eccsize = 1; + + writew(config1, NFC_V1_V2_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, NFC_V1_V2_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); + writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + writew(0x4, NFC_V1_V2_WRPROT); +} + +static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int tRC_min_ns, tRC_ps, ret; + unsigned long rate, rate_round; + const struct nand_sdr_timings *timings; + u16 config1; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + + config1 = readw(NFC_V1_V2_CONFIG1); + + tRC_min_ns = timings->tRC_min / 1000; + rate = 1000000000 / tRC_min_ns; + + /* + * For tRC < 30ns we have to use EDO mode. In this case the controller + * does one access per clock cycle. Otherwise the controller does one + * access in two clock cycles, thus we have to double the rate to the + * controller. + */ + if (tRC_min_ns < 30) { + rate_round = clk_round_rate(host->clk, rate); + config1 |= NFC_V2_CONFIG1_ONE_CYCLE; + tRC_ps = 1000000000 / (rate_round / 1000); + } else { + rate *= 2; + rate_round = clk_round_rate(host->clk, rate); + config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE; + tRC_ps = 1000000000 / (rate_round / 1000 / 2); + } + + /* + * The timing values compared against are from the i.MX25 Automotive + * datasheet, Table 50. NFC Timing Parameters + */ + if (timings->tCLS_min > tRC_ps - 1000 || + timings->tCLH_min > tRC_ps - 2000 || + timings->tCS_min > tRC_ps - 1000 || + timings->tCH_min > tRC_ps - 2000 || + timings->tWP_min > tRC_ps - 1500 || + timings->tALS_min > tRC_ps || + timings->tALH_min > tRC_ps - 3000 || + timings->tDS_min > tRC_ps || + timings->tDH_min > tRC_ps - 5000 || + timings->tWC_min > 2 * tRC_ps || + timings->tWH_min > tRC_ps - 2500 || + timings->tRR_min > 6 * tRC_ps || + timings->tRP_min > 3 * tRC_ps / 2 || + timings->tRC_min > 2 * tRC_ps || + timings->tREH_min > (tRC_ps / 2) - 2500) { + dev_dbg(host->dev, "Timing out of bounds\n"); + return -EINVAL; + } + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + ret = clk_set_rate(host->clk, rate); + if (ret) + return ret; + + writew(config1, NFC_V1_V2_CONFIG1); + + dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round, + config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" : + "normal"); + + return 0; +} + +static void preset_v2(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint16_t config1 = 0; + + config1 |= NFC_V2_CONFIG1_FP_INT; + + if (!host->devtype_data->irqpending_quirk) + config1 |= NFC_V1_V2_CONFIG1_INT_MSK; + + if (mtd->writesize) { + uint16_t pages_per_block = mtd->erasesize / mtd->writesize; + + if (nand_chip->ecc.mode == NAND_ECC_HW) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + + host->eccsize = get_eccsize(mtd); + if (host->eccsize == 4) + config1 |= NFC_V2_CONFIG1_ECC_MODE_4; + + config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6); + } else { + host->eccsize = 1; + } + + writew(config1, NFC_V1_V2_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, NFC_V1_V2_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); + + /* Unlock Block Command for given address range */ + writew(0x4, NFC_V1_V2_WRPROT); +} + +static void preset_v3(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(chip); + uint32_t config2, config3; + int i, addr_phases; + + writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1); + writel(NFC_V3_IPC_CREQ, NFC_V3_IPC); + + /* Unlock the internal RAM Buffer */ + writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, + NFC_V3_WRPROT); + + /* Blocks to be unlocked */ + for (i = 0; i < NAND_MAX_CHIPS; i++) + writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2)); + + writel(0, NFC_V3_IPC); + + config2 = NFC_V3_CONFIG2_ONE_CYCLE | + NFC_V3_CONFIG2_2CMD_PHASES | + NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | + NFC_V3_CONFIG2_ST_CMD(0x70) | + NFC_V3_CONFIG2_INT_MSK | + NFC_V3_CONFIG2_NUM_ADDR_PHASE0; + + addr_phases = fls(chip->pagemask) >> 3; + + if (mtd->writesize == 2048) { + config2 |= NFC_V3_CONFIG2_PS_2048; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); + } else if (mtd->writesize == 4096) { + config2 |= NFC_V3_CONFIG2_PS_4096; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); + } else { + config2 |= NFC_V3_CONFIG2_PS_512; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1); + } + + if (mtd->writesize) { + if (chip->ecc.mode == NAND_ECC_HW) + config2 |= NFC_V3_CONFIG2_ECC_EN; + + config2 |= NFC_V3_CONFIG2_PPB( + ffs(mtd->erasesize / mtd->writesize) - 6, + host->devtype_data->ppb_shift); + host->eccsize = get_eccsize(mtd); + if (host->eccsize == 8) + config2 |= NFC_V3_CONFIG2_ECC_MODE_8; + } + + writel(config2, NFC_V3_CONFIG2); + + config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); + + if (!(chip->options & NAND_BUSWIDTH_16)) + config3 |= NFC_V3_CONFIG3_FW8; + + writel(config3, NFC_V3_CONFIG3); + + writel(0, NFC_V3_DELAY_LINE); +} + +/* Used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash */ +static void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + dev_dbg(host->dev, "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* Reset command state information */ + host->status_request = false; + + /* Command pre-processing step */ + switch (command) { + case NAND_CMD_RESET: + host->devtype_data->preset(mtd); + host->devtype_data->send_cmd(host, command, false); + break; + + case NAND_CMD_STATUS: + host->buf_start = 0; + host->status_request = true; + + host->devtype_data->send_cmd(host, command, true); + WARN_ONCE(column != -1 || page_addr != -1, + "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", + command, column, page_addr); + mxc_do_addr_cycle(mtd, column, page_addr); + break; + + case NAND_CMD_READID: + host->devtype_data->send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); + host->devtype_data->send_read_id(host); + host->buf_start = 0; + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + host->devtype_data->send_cmd(host, command, false); + WARN_ONCE(column != -1, + "Unexpected column value (cmd=%u, col=%d)\n", + command, column); + mxc_do_addr_cycle(mtd, column, page_addr); + + break; + case NAND_CMD_PARAM: + host->devtype_data->send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); + host->devtype_data->send_page(mtd, NFC_OUTPUT); + memcpy32_fromio(host->data_buf, host->main_area0, 512); + host->buf_start = 0; + break; + default: + WARN_ONCE(1, "Unimplemented command (cmd=%u)\n", + command); + break; + } +} + +static int mxc_nand_onfi_set_features(struct mtd_info *mtd, + struct nand_chip *chip, int addr, + u8 *subfeature_param) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int i; + + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -EINVAL; + + host->buf_start = 0; + + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, subfeature_param[i]); + + memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); + host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false); + mxc_do_addr_cycle(mtd, addr, -1); + host->devtype_data->send_page(mtd, NFC_INPUT); + + return 0; +} + +static int mxc_nand_onfi_get_features(struct mtd_info *mtd, + struct nand_chip *chip, int addr, + u8 *subfeature_param) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int i; + + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -EINVAL; + + host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false); + mxc_do_addr_cycle(mtd, addr, -1); + host->devtype_data->send_page(mtd, NFC_OUTPUT); + memcpy32_fromio(host->data_buf, host->main_area0, 512); + host->buf_start = 0; + + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + *subfeature_param++ = chip->read_byte(mtd); + + return 0; +} + +/* + * The generic flash bbt decriptors overlap with our ecc + * hardware, so define some i.MX specific ones. + */ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* v1 + irqpending_quirk: i.MX21 */ +static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { + .preset = preset_v1, + .read_page = mxc_nand_read_page_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ooblayout = &mxc_v1_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v1_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, + .irqpending_quirk = 1, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v1 + !irqpending_quirk: i.MX27, i.MX31 */ +static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { + .preset = preset_v1, + .read_page = mxc_nand_read_page_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ooblayout = &mxc_v1_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v1_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .axi_offset = 0, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v21: i.MX25, i.MX35 */ +static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { + .preset = preset_v2, + .read_page = mxc_nand_read_page_v2_v3, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v2, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v2, + .ooblayout = &mxc_v2_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v2, + .setup_data_interface = mxc_nand_v2_setup_data_interface, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0x1e00, + .spare0_offset = 0x1000, + .axi_offset = 0, + .spare_len = 64, + .eccbytes = 9, + .eccsize = 0, +}; + +/* v3.2a: i.MX51 */ +static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { + .preset = preset_v3, + .read_page = mxc_nand_read_page_v2_v3, + .send_cmd = send_cmd_v3, + .send_addr = send_addr_v3, + .send_page = send_page_v3, + .send_read_id = send_read_id_v3, + .get_dev_status = get_dev_status_v3, + .check_int = check_int_v3, + .irq_control = irq_control_v3, + .get_ecc_status = get_ecc_status_v3, + .ooblayout = &mxc_v2_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v1_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v3, + .irqpending_quirk = 0, + .needs_ip = 1, + .regs_offset = 0, + .spare0_offset = 0x1000, + .axi_offset = 0x1e00, + .spare_len = 64, + .eccbytes = 0, + .eccsize = 0, + .ppb_shift = 7, +}; + +/* v3.2b: i.MX53 */ +static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { + .preset = preset_v3, + .read_page = mxc_nand_read_page_v2_v3, + .send_cmd = send_cmd_v3, + .send_addr = send_addr_v3, + .send_page = send_page_v3, + .send_read_id = send_read_id_v3, + .get_dev_status = get_dev_status_v3, + .check_int = check_int_v3, + .irq_control = irq_control_v3, + .get_ecc_status = get_ecc_status_v3, + .ooblayout = &mxc_v2_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v1_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v3, + .irqpending_quirk = 0, + .needs_ip = 1, + .regs_offset = 0, + .spare0_offset = 0x1000, + .axi_offset = 0x1e00, + .spare_len = 64, + .eccbytes = 0, + .eccsize = 0, + .ppb_shift = 8, +}; + +static inline int is_imx21_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx21_nand_devtype_data; +} + +static inline int is_imx27_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx27_nand_devtype_data; +} + +static inline int is_imx25_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx25_nand_devtype_data; +} + +static inline int is_imx51_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx51_nand_devtype_data; +} + +static inline int is_imx53_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx53_nand_devtype_data; +} + +static const struct platform_device_id mxcnd_devtype[] = { + { + .name = "imx21-nand", + .driver_data = (kernel_ulong_t) &imx21_nand_devtype_data, + }, { + .name = "imx27-nand", + .driver_data = (kernel_ulong_t) &imx27_nand_devtype_data, + }, { + .name = "imx25-nand", + .driver_data = (kernel_ulong_t) &imx25_nand_devtype_data, + }, { + .name = "imx51-nand", + .driver_data = (kernel_ulong_t) &imx51_nand_devtype_data, + }, { + .name = "imx53-nand", + .driver_data = (kernel_ulong_t) &imx53_nand_devtype_data, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, mxcnd_devtype); + +#ifdef CONFIG_OF +static const struct of_device_id mxcnd_dt_ids[] = { + { + .compatible = "fsl,imx21-nand", + .data = &imx21_nand_devtype_data, + }, { + .compatible = "fsl,imx27-nand", + .data = &imx27_nand_devtype_data, + }, { + .compatible = "fsl,imx25-nand", + .data = &imx25_nand_devtype_data, + }, { + .compatible = "fsl,imx51-nand", + .data = &imx51_nand_devtype_data, + }, { + .compatible = "fsl,imx53-nand", + .data = &imx53_nand_devtype_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxcnd_dt_ids); + +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + struct device_node *np = host->dev->of_node; + const struct of_device_id *of_id = + of_match_device(mxcnd_dt_ids, host->dev); + + if (!np) + return 1; + + host->devtype_data = of_id->data; + + return 0; +} +#else +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + return 1; +} +#endif + +static int mxcnd_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct mxc_nand_host *host; + struct resource *res; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host), + GFP_KERNEL); + if (!host) + return -ENOMEM; + + /* allocate a temporary buffer for the nand_scan_ident() */ + host->data_buf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL); + if (!host->data_buf) + return -ENOMEM; + + host->dev = &pdev->dev; + /* structures must be linked */ + this = &host->nand; + mtd = nand_to_mtd(this); + mtd->dev.parent = &pdev->dev; + mtd->name = DRIVER_NAME; + + /* 50 us command delay time */ + this->chip_delay = 5; + + nand_set_controller_data(this, host); + nand_set_flash_node(this, pdev->dev.of_node), + this->dev_ready = mxc_nand_dev_ready; + this->cmdfunc = mxc_nand_command; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->onfi_set_features = mxc_nand_onfi_set_features; + this->onfi_get_features = mxc_nand_onfi_get_features; + + host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + err = mxcnd_probe_dt(host); + if (err > 0) { + struct mxc_nand_platform_data *pdata = + dev_get_platdata(&pdev->dev); + if (pdata) { + host->pdata = *pdata; + host->devtype_data = (struct mxc_nand_devtype_data *) + pdev->id_entry->driver_data; + } else { + err = -ENODEV; + } + } + if (err < 0) + return err; + + this->setup_data_interface = host->devtype_data->setup_data_interface; + + if (host->devtype_data->needs_ip) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->regs_ip = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->regs_ip)) + return PTR_ERR(host->regs_ip); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + } else { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + } + + host->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->base)) + return PTR_ERR(host->base); + + host->main_area0 = host->base; + + if (host->devtype_data->regs_offset) + host->regs = host->base + host->devtype_data->regs_offset; + host->spare0 = host->base + host->devtype_data->spare0_offset; + if (host->devtype_data->axi_offset) + host->regs_axi = host->base + host->devtype_data->axi_offset; + + this->ecc.bytes = host->devtype_data->eccbytes; + host->eccsize = host->devtype_data->eccsize; + + this->select_chip = host->devtype_data->select_chip; + this->ecc.size = 512; + mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); + + if (host->pdata.hw_ecc) { + this->ecc.mode = NAND_ECC_HW; + } else { + this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; + } + + /* NAND bus width determines access functions used by upper layer */ + if (host->pdata.width == 2) + this->options |= NAND_BUSWIDTH_16; + + /* update flash based bbt */ + if (host->pdata.flash_bbt) + this->bbt_options |= NAND_BBT_USE_FLASH; + + init_completion(&host->op_completion); + + host->irq = platform_get_irq(pdev, 0); + if (host->irq < 0) + return host->irq; + + /* + * Use host->devtype_data->irq_control() here instead of irq_control() + * because we must not disable_irq_nosync without having requested the + * irq. + */ + host->devtype_data->irq_control(host, 0); + + err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq, + 0, DRIVER_NAME, host); + if (err) + return err; + + err = clk_prepare_enable(host->clk); + if (err) + return err; + host->clk_act = 1; + + /* + * Now that we "own" the interrupt make sure the interrupt mask bit is + * cleared on i.MX21. Otherwise we can't read the interrupt status bit + * on this machine. + */ + if (host->devtype_data->irqpending_quirk) { + disable_irq_nosync(host->irq); + host->devtype_data->irq_control(host, 1); + } + + /* first scan to find the device and get the page size */ + err = nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL); + if (err) + goto escan; + + switch (this->ecc.mode) { + case NAND_ECC_HW: + this->ecc.read_page = mxc_nand_read_page; + this->ecc.read_page_raw = mxc_nand_read_page_raw; + this->ecc.read_oob = mxc_nand_read_oob; + this->ecc.write_page = mxc_nand_write_page_ecc; + this->ecc.write_page_raw = mxc_nand_write_page_raw; + this->ecc.write_oob = mxc_nand_write_oob; + break; + + case NAND_ECC_SOFT: + break; + + default: + err = -EINVAL; + goto escan; + } + + if (this->bbt_options & NAND_BBT_USE_FLASH) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + + /* allocate the right size buffer now */ + devm_kfree(&pdev->dev, (void *)host->data_buf); + host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize, + GFP_KERNEL); + if (!host->data_buf) { + err = -ENOMEM; + goto escan; + } + + /* Call preset again, with correct writesize this time */ + host->devtype_data->preset(mtd); + + if (!this->ecc.bytes) { + if (host->eccsize == 8) + this->ecc.bytes = 18; + else if (host->eccsize == 4) + this->ecc.bytes = 9; + } + + /* + * Experimentation shows that i.MX NFC can only handle up to 218 oob + * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare() + * into copying invalid data to/from the spare IO buffer, as this + * might cause ECC data corruption when doing sub-page write to a + * partially written page. + */ + host->used_oobsize = min(mtd->oobsize, 218U); + + if (this->ecc.mode == NAND_ECC_HW) { + if (is_imx21_nfc(host) || is_imx27_nfc(host)) + this->ecc.strength = 1; + else + this->ecc.strength = (host->eccsize == 4) ? 4 : 8; + } + + /* second phase scan */ + err = nand_scan_tail(mtd); + if (err) + goto escan; + + /* Register the partitions */ + mtd_device_parse_register(mtd, part_probes, + NULL, + host->pdata.parts, + host->pdata.nr_parts); + + platform_set_drvdata(pdev, host); + + return 0; + +escan: + if (host->clk_act) + clk_disable_unprepare(host->clk); + + return err; +} + +static int mxcnd_remove(struct platform_device *pdev) +{ + struct mxc_nand_host *host = platform_get_drvdata(pdev); + + nand_release(nand_to_mtd(&host->nand)); + if (host->clk_act) + clk_disable_unprepare(host->clk); + + return 0; +} + +static struct platform_driver mxcnd_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(mxcnd_dt_ids), + }, + .id_table = mxcnd_devtype, + .probe = mxcnd_probe, + .remove = mxcnd_remove, +}; +module_platform_driver(mxcnd_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC NAND MTD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/raw/nand_amd.c b/drivers/mtd/nand/raw/nand_amd.c new file mode 100644 index 000000000000..22f060f38123 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_amd.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 Free Electrons + * Copyright (C) 2017 NextThing Co + * + * Author: Boris Brezillon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +static void amd_nand_decode_id(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + nand_decode_ext_id(chip); + + /* + * Check for Spansion/AMD ID + repeating 5th, 6th byte since + * some Spansion chips have erasesize that conflicts with size + * listed in nand_ids table. + * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) + */ + if (chip->id.data[4] != 0x00 && chip->id.data[5] == 0x00 && + chip->id.data[6] == 0x00 && chip->id.data[7] == 0x00 && + mtd->writesize == 512) { + mtd->erasesize = 128 * 1024; + mtd->erasesize <<= ((chip->id.data[3] & 0x03) << 1); + } +} + +static int amd_nand_init(struct nand_chip *chip) +{ + if (nand_is_slc(chip)) + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; + + return 0; +} + +const struct nand_manufacturer_ops amd_nand_manuf_ops = { + .detect = amd_nand_decode_id, + .init = amd_nand_init, +}; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c new file mode 100644 index 000000000000..e70ca16a5118 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_base.c @@ -0,0 +1,6582 @@ +/* + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + * + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/doc/nand.html + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002-2006 Thomas Gleixner (tglx@linutronix.de) + * + * Credits: + * David Woodhouse for adding multichip support + * + * Aleph One Ltd. and Toby Churchill Ltd. for supporting the + * rework for 2K page size chips + * + * TODO: + * Enable cached programming for 2k page size chips + * Check, if mtd->ecctype should be set to MTD_ECC_HW + * if we have HW ECC support. + * BBT table is not serialized, has to be fixed + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int nand_get_device(struct mtd_info *mtd, int new_state); + +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + +/* Define default oob placement schemes for large and small page devices */ +static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section > 1) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + if (mtd->oobsize == 16) + oobregion->length = 4; + else + oobregion->length = 3; + } else { + if (mtd->oobsize == 8) + return -ERANGE; + + oobregion->offset = 6; + oobregion->length = ecc->total - 4; + } + + return 0; +} + +static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + if (mtd->oobsize == 16) { + if (section) + return -ERANGE; + + oobregion->length = 8; + oobregion->offset = 8; + } else { + oobregion->length = 2; + if (!section) + oobregion->offset = 3; + else + oobregion->offset = 6; + } + + return 0; +} + +const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = { + .ecc = nand_ooblayout_ecc_sp, + .free = nand_ooblayout_free_sp, +}; +EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops); + +static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section || !ecc->total) + return -ERANGE; + + oobregion->length = ecc->total; + oobregion->offset = mtd->oobsize - oobregion->length; + + return 0; +} + +static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + oobregion->length = mtd->oobsize - ecc->total - 2; + oobregion->offset = 2; + + return 0; +} + +const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = { + .ecc = nand_ooblayout_ecc_lp, + .free = nand_ooblayout_free_lp, +}; +EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops); + +/* + * Support the old "large page" layout used for 1-bit Hamming ECC where ECC + * are placed at a fixed offset. + */ +static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + switch (mtd->oobsize) { + case 64: + oobregion->offset = 40; + break; + case 128: + oobregion->offset = 80; + break; + default: + return -EINVAL; + } + + oobregion->length = ecc->total; + if (oobregion->offset + oobregion->length > mtd->oobsize) + return -ERANGE; + + return 0; +} + +static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ecc_offset = 0; + + if (section < 0 || section > 1) + return -ERANGE; + + switch (mtd->oobsize) { + case 64: + ecc_offset = 40; + break; + case 128: + ecc_offset = 80; + break; + default: + return -EINVAL; + } + + if (section == 0) { + oobregion->offset = 2; + oobregion->length = ecc_offset - 2; + } else { + oobregion->offset = ecc_offset + ecc->total; + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = { + .ecc = nand_ooblayout_ecc_lp_hamming, + .free = nand_ooblayout_free_lp_hamming, +}; + +static int check_offs_len(struct mtd_info *mtd, + loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + /* Start address must align on block boundary */ + if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { + pr_debug("%s: unaligned address\n", __func__); + ret = -EINVAL; + } + + /* Length must align on block boundary */ + if (len & ((1ULL << chip->phys_erase_shift) - 1)) { + pr_debug("%s: length not block aligned\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +/** + * nand_release_device - [GENERIC] release chip + * @mtd: MTD device structure + * + * Release chip lock and wake up anyone waiting on the device. + */ +static void nand_release_device(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + /* Release the controller and the chip */ + spin_lock(&chip->controller->lock); + chip->controller->active = NULL; + chip->state = FL_READY; + wake_up(&chip->controller->wq); + spin_unlock(&chip->controller->lock); +} + +/** + * nand_read_byte - [DEFAULT] read one byte from the chip + * @mtd: MTD device structure + * + * Default read function for 8bit buswidth + */ +static uint8_t nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + return readb(chip->IO_ADDR_R); +} + +/** + * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswidth with endianness conversion. + * + */ +static uint8_t nand_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); +} + +/** + * nand_read_word - [DEFAULT] read one word from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswidth without endianness conversion. + */ +static u16 nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + return readw(chip->IO_ADDR_R); +} + +/** + * nand_select_chip - [DEFAULT] control CE line + * @mtd: MTD device structure + * @chipnr: chipnumber to select, -1 for deselect + * + * Default select function for 1 chip devices. + */ +static void nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + switch (chipnr) { + case -1: + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + break; + case 0: + break; + + default: + BUG(); + } +} + +/** + * nand_write_byte - [DEFAULT] write single byte to chip + * @mtd: MTD device structure + * @byte: value to write + * + * Default function to write a byte to I/O[7:0] + */ +static void nand_write_byte(struct mtd_info *mtd, uint8_t byte) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + chip->write_buf(mtd, &byte, 1); +} + +/** + * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16 + * @mtd: MTD device structure + * @byte: value to write + * + * Default function to write a byte to I/O[7:0] on a 16-bit wide chip. + */ +static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + uint16_t word = byte; + + /* + * It's not entirely clear what should happen to I/O[15:8] when writing + * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads: + * + * When the host supports a 16-bit bus width, only data is + * transferred at the 16-bit width. All address and command line + * transfers shall use only the lower 8-bits of the data bus. During + * command transfers, the host may place any value on the upper + * 8-bits of the data bus. During address transfers, the host shall + * set the upper 8-bits of the data bus to 00h. + * + * One user of the write_byte callback is nand_onfi_set_features. The + * four parameters are specified to be written to I/O[7:0], but this is + * neither an address nor a command transfer. Let's assume a 0 on the + * upper I/O lines is OK. + */ + chip->write_buf(mtd, (uint8_t *)&word, 2); +} + +/** + * nand_write_buf - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 8bit buswidth. + */ +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + iowrite8_rep(chip->IO_ADDR_W, buf, len); +} + +/** + * nand_read_buf - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 8bit buswidth. + */ +static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + ioread8_rep(chip->IO_ADDR_R, buf, len); +} + +/** + * nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 16bit buswidth. + */ +static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u16 *p = (u16 *) buf; + + iowrite16_rep(chip->IO_ADDR_W, p, len >> 1); +} + +/** + * nand_read_buf16 - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 16bit buswidth. + */ +static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u16 *p = (u16 *) buf; + + ioread16_rep(chip->IO_ADDR_R, p, len >> 1); +} + +/** + * nand_block_bad - [DEFAULT] Read bad block marker from the chip + * @mtd: MTD device structure + * @ofs: offset from device start + * + * Check, if the block is bad. + */ +static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) +{ + int page, page_end, res; + struct nand_chip *chip = mtd_to_nand(mtd); + u8 bad; + + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; + + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + page_end = page + (chip->bbt_options & NAND_BBT_SCAN2NDPAGE ? 2 : 1); + + for (; page < page_end; page++) { + res = chip->ecc.read_oob(mtd, chip, page); + if (res) + return res; + + bad = chip->oob_poi[chip->badblockpos]; + + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + if (res) + return res; + } + + return 0; +} + +/** + * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This is the default implementation, which can be overridden by a hardware + * specific driver. It provides the details for writing a bad block marker to a + * block. + */ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_oob_ops ops; + uint8_t buf[2] = { 0, 0 }; + int ret = 0, res, i = 0; + + memset(&ops, 0, sizeof(ops)); + ops.oobbuf = buf; + ops.ooboffs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + ops.ooboffs &= ~0x01; + ops.len = ops.ooblen = 2; + } else { + ops.len = ops.ooblen = 1; + } + ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; + do { + res = nand_do_write_oob(mtd, ofs, &ops); + if (!ret) + ret = res; + + i++; + ofs += mtd->writesize; + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); + + return ret; +} + +/** + * nand_block_markbad_lowlevel - mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This function performs the generic NAND bad block marking steps (i.e., bad + * block table(s) and/or marker(s)). We only allow the hardware driver to + * specify how to write bad block markers to OOB (chip->block_markbad). + * + * We try operations in the following order: + * + * (1) erase the affected block, to allow OOB marker to be written cleanly + * (2) write bad block marker to OOB area of affected block (unless flag + * NAND_BBT_NO_OOB_BBM is present) + * (3) update the BBT + * + * Note that we retain the first error encountered in (2) or (3), finish the + * procedures, and dump the error in the end. +*/ +static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int res, ret = 0; + + if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { + struct erase_info einfo; + + /* Attempt erase before marking OOB */ + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = ofs; + einfo.len = 1ULL << chip->phys_erase_shift; + nand_erase_nand(mtd, &einfo, 0); + + /* Write bad block marker to OOB */ + nand_get_device(mtd, FL_WRITING); + ret = chip->block_markbad(mtd, ofs); + nand_release_device(mtd); + } + + /* Mark block bad in BBT */ + if (chip->bbt) { + res = nand_markbad_bbt(mtd, ofs); + if (!ret) + ret = res; + } + + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} + +/** + * nand_check_wp - [GENERIC] check if the chip is write protected + * @mtd: MTD device structure + * + * Check, if the device is write protected. The function expects, that the + * device is already selected. + */ +static int nand_check_wp(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u8 status; + int ret; + + /* Broken xD cards report WP despite being writable */ + if (chip->options & NAND_BROKEN_XD) + return 0; + + /* Check the WP bit */ + ret = nand_status_op(chip, &status); + if (ret) + return ret; + + return status & NAND_STATUS_WP ? 0 : 1; +} + +/** + * nand_block_isreserved - [GENERIC] Check if a block is marked reserved. + * @mtd: MTD device structure + * @ofs: offset from device start + * + * Check if the block is marked as reserved. + */ +static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!chip->bbt) + return 0; + /* Return info from the table */ + return nand_isreserved_bbt(mtd, ofs); +} + +/** + * nand_block_checkbad - [GENERIC] Check if a block is marked bad + * @mtd: MTD device structure + * @ofs: offset from device start + * @allowbbt: 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!chip->bbt) + return chip->block_bad(mtd, ofs); + + /* Return info from the table */ + return nand_isbad_bbt(mtd, ofs, allowbbt); +} + +/** + * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands. + * @mtd: MTD device structure + * @timeo: Timeout + * + * Helper function for nand_wait_ready used when needing to wait in interrupt + * context. + */ +static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int i; + + /* Wait for the device to get ready */ + for (i = 0; i < timeo; i++) { + if (chip->dev_ready(mtd)) + break; + touch_softlockup_watchdog(); + mdelay(1); + } +} + +/** + * nand_wait_ready - [GENERIC] Wait for the ready pin after commands. + * @mtd: MTD device structure + * + * Wait for the ready pin after a command, and warn if a timeout occurs. + */ +void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + unsigned long timeo = 400; + + if (in_interrupt() || oops_in_progress) + return panic_nand_wait_ready(mtd, timeo); + + /* Wait until command is processed or timeout occurs */ + timeo = jiffies + msecs_to_jiffies(timeo); + do { + if (chip->dev_ready(mtd)) + return; + cond_resched(); + } while (time_before(jiffies, timeo)); + + if (!chip->dev_ready(mtd)) + pr_warn_ratelimited("timeout while waiting for chip to become ready\n"); +} +EXPORT_SYMBOL_GPL(nand_wait_ready); + +/** + * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands. + * @mtd: MTD device structure + * @timeo: Timeout in ms + * + * Wait for status ready (i.e. command done) or timeout. + */ +static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + int ret; + + timeo = jiffies + msecs_to_jiffies(timeo); + do { + u8 status; + + ret = nand_read_data_op(chip, &status, sizeof(status), true); + if (ret) + return; + + if (status & NAND_STATUS_READY) + break; + touch_softlockup_watchdog(); + } while (time_before(jiffies, timeo)); +}; + +/** + * nand_soft_waitrdy - Poll STATUS reg until RDY bit is set to 1 + * @chip: NAND chip structure + * @timeout_ms: Timeout in ms + * + * Poll the STATUS register using ->exec_op() until the RDY bit becomes 1. + * If that does not happen whitin the specified timeout, -ETIMEDOUT is + * returned. + * + * This helper is intended to be used when the controller does not have access + * to the NAND R/B pin. + * + * Be aware that calling this helper from an ->exec_op() implementation means + * ->exec_op() must be re-entrant. + * + * Return 0 if the NAND chip is ready, a negative error otherwise. + */ +int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) +{ + u8 status = 0; + int ret; + + if (!chip->exec_op) + return -ENOTSUPP; + + ret = nand_status_op(chip, NULL); + if (ret) + return ret; + + timeout_ms = jiffies + msecs_to_jiffies(timeout_ms); + do { + ret = nand_read_data_op(chip, &status, sizeof(status), true); + if (ret) + break; + + if (status & NAND_STATUS_READY) + break; + + /* + * Typical lowest execution time for a tR on most NANDs is 10us, + * use this as polling delay before doing something smarter (ie. + * deriving a delay from the timeout value, timeout_ms/ratio). + */ + udelay(10); + } while (time_before(jiffies, timeout_ms)); + + /* + * We have to exit READ_STATUS mode in order to read real data on the + * bus in case the WAITRDY instruction is preceding a DATA_IN + * instruction. + */ + nand_exit_status_op(chip); + + if (ret) + return ret; + + return status & NAND_STATUS_READY ? 0 : -ETIMEDOUT; +}; +EXPORT_SYMBOL_GPL(nand_soft_waitrdy); + +/** + * nand_command - [DEFAULT] Send command to NAND device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This function is used for small page devices + * (512 Bytes per page). + */ +static void nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; + + /* Write out the command to the device */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + chip->cmd_ctrl(mtd, readcmd, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + if (command != NAND_CMD_NONE) + chip->cmd_ctrl(mtd, command, ctrl); + + /* Address cycle, when necessary */ + ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); + if (chip->options & NAND_ROW_ADDR_3) + chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Program and erase have their own busy handlers status and sequential + * in needs no delay + */ + switch (command) { + + case NAND_CMD_NONE: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + case NAND_CMD_READID: + case NAND_CMD_SET_FEATURES: + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, + NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ + nand_wait_status_ready(mtd, 250); + return; + + /* This applies to read commands */ + case NAND_CMD_READ0: + /* + * READ0 is sometimes used to exit GET STATUS mode. When this + * is the case no address cycles are requested, and we can use + * this information to detect that we should not wait for the + * device to be ready. + */ + if (column == -1 && page_addr == -1) + return; + + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + /* + * Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. + */ + ndelay(100); + + nand_wait_ready(mtd); +} + +static void nand_ccs_delay(struct nand_chip *chip) +{ + /* + * The controller already takes care of waiting for tCCS when the RNDIN + * or RNDOUT command is sent, return directly. + */ + if (!(chip->options & NAND_WAIT_TCCS)) + return; + + /* + * Wait tCCS_min if it is correctly defined, otherwise wait 500ns + * (which should be safe for all NANDs). + */ + if (chip->setup_data_interface) + ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000); + else + ndelay(500); +} + +/** + * nand_command_lp - [DEFAULT] Send command to NAND large page device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This is the version for the new large page + * devices. We don't have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. + */ +static void nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Command latch cycle */ + if (command != NAND_CMD_NONE) + chip->cmd_ctrl(mtd, command, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + + if (column != -1 || page_addr != -1) { + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + + /* Only output a single addr cycle for 8bits opcodes. */ + if (!nand_opcode_8bits(command)) + chip->cmd_ctrl(mtd, column >> 8, ctrl); + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + chip->cmd_ctrl(mtd, page_addr >> 8, + NAND_NCE | NAND_ALE); + if (chip->options & NAND_ROW_ADDR_3) + chip->cmd_ctrl(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); + } + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Program and erase have their own busy handlers status, sequential + * in and status need no delay. + */ + switch (command) { + + case NAND_CMD_NONE: + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + case NAND_CMD_READID: + case NAND_CMD_SET_FEATURES: + return; + + case NAND_CMD_RNDIN: + nand_ccs_delay(chip); + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ + nand_wait_status_ready(mtd, 250); + return; + + case NAND_CMD_RNDOUT: + /* No ready / busy check necessary */ + chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + nand_ccs_delay(chip); + return; + + case NAND_CMD_READ0: + /* + * READ0 is sometimes used to exit GET STATUS mode. When this + * is the case no address cycles are requested, and we can use + * this information to detect that READSTART should not be + * issued. + */ + if (column == -1 && page_addr == -1) + return; + + chip->cmd_ctrl(mtd, NAND_CMD_READSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay. + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + + /* + * Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. + */ + ndelay(100); + + nand_wait_ready(mtd); +} + +/** + * panic_nand_get_device - [GENERIC] Get chip for selected access + * @chip: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Used when in panic, no locks are taken. + */ +static void panic_nand_get_device(struct nand_chip *chip, + struct mtd_info *mtd, int new_state) +{ + /* Hardware controller shared among independent devices */ + chip->controller->active = chip; + chip->state = new_state; +} + +/** + * nand_get_device - [GENERIC] Get chip for selected access + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Get the device and lock it for exclusive access + */ +static int +nand_get_device(struct mtd_info *mtd, int new_state) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + spinlock_t *lock = &chip->controller->lock; + wait_queue_head_t *wq = &chip->controller->wq; + DECLARE_WAITQUEUE(wait, current); +retry: + spin_lock(lock); + + /* Hardware controller shared among independent devices */ + if (!chip->controller->active) + chip->controller->active = chip; + + if (chip->controller->active == chip && chip->state == FL_READY) { + chip->state = new_state; + spin_unlock(lock); + return 0; + } + if (new_state == FL_PM_SUSPENDED) { + if (chip->controller->active->state == FL_PM_SUSPENDED) { + chip->state = FL_PM_SUSPENDED; + spin_unlock(lock); + return 0; + } + } + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(wq, &wait); + spin_unlock(lock); + schedule(); + remove_wait_queue(wq, &wait); + goto retry; +} + +/** + * panic_nand_wait - [GENERIC] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * @timeo: timeout + * + * Wait for command done. This is a helper function for nand_wait used when + * we are in interrupt context. May happen when in panic and trying to write + * an oops through mtdoops. + */ +static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, + unsigned long timeo) +{ + int i; + for (i = 0; i < timeo; i++) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + int ret; + u8 status; + + ret = nand_read_data_op(chip, &status, sizeof(status), + true); + if (ret) + return; + + if (status & NAND_STATUS_READY) + break; + } + mdelay(1); + } +} + +/** + * nand_wait - [DEFAULT] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * + * Wait for command done. This applies to erase and program only. + */ +static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + + unsigned long timeo = 400; + u8 status; + int ret; + + /* + * Apply this short delay always to ensure that we do wait tWB in any + * case on any machine. + */ + ndelay(100); + + ret = nand_status_op(chip, NULL); + if (ret) + return ret; + + if (in_interrupt() || oops_in_progress) + panic_nand_wait(mtd, chip, timeo); + else { + timeo = jiffies + msecs_to_jiffies(timeo); + do { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + ret = nand_read_data_op(chip, &status, + sizeof(status), true); + if (ret) + return ret; + + if (status & NAND_STATUS_READY) + break; + } + cond_resched(); + } while (time_before(jiffies, timeo)); + } + + ret = nand_read_data_op(chip, &status, sizeof(status), true); + if (ret) + return ret; + + /* This can happen if in case of timeout or buggy dev_ready */ + WARN_ON(!(status & NAND_STATUS_READY)); + return status; +} + +/** + * nand_reset_data_interface - Reset data interface and timings + * @chip: The NAND chip + * @chipnr: Internal die id + * + * Reset the Data interface and timings to ONFI mode 0. + * + * Returns 0 for success or negative error code otherwise. + */ +static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + if (!chip->setup_data_interface) + return 0; + + /* + * The ONFI specification says: + * " + * To transition from NV-DDR or NV-DDR2 to the SDR data + * interface, the host shall use the Reset (FFh) command + * using SDR timing mode 0. A device in any timing mode is + * required to recognize Reset (FFh) command issued in SDR + * timing mode 0. + * " + * + * Configure the data interface in SDR mode and set the + * timings to timing mode 0. + */ + + onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); + ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); + if (ret) + pr_err("Failed to configure data interface to SDR timing mode 0\n"); + + return ret; +} + +/** + * nand_setup_data_interface - Setup the best data interface and timings + * @chip: The NAND chip + * @chipnr: Internal die id + * + * Find and configure the best data interface and NAND timings supported by + * the chip and the driver. + * First tries to retrieve supported timing modes from ONFI information, + * and if the NAND chip does not support ONFI, relies on the + * ->onfi_timing_mode_default specified in the nand_ids table. + * + * Returns 0 for success or negative error code otherwise. + */ +static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + if (!chip->setup_data_interface) + return 0; + + /* + * Ensure the timing mode has been changed on the chip side + * before changing timings on the controller side. + */ + if (chip->onfi_version && + (le16_to_cpu(chip->onfi_params.opt_cmd) & + ONFI_OPT_CMD_SET_GET_FEATURES)) { + u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { + chip->onfi_timing_mode_default, + }; + + ret = chip->onfi_set_features(mtd, chip, + ONFI_FEATURE_ADDR_TIMING_MODE, + tmode_param); + if (ret) + goto err; + } + + ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); +err: + return ret; +} + +/** + * nand_init_data_interface - find the best data interface and timings + * @chip: The NAND chip + * + * Find the best data interface and NAND timings supported by the chip + * and the driver. + * First tries to retrieve supported timing modes from ONFI information, + * and if the NAND chip does not support ONFI, relies on the + * ->onfi_timing_mode_default specified in the nand_ids table. After this + * function nand_chip->data_interface is initialized with the best timing mode + * available. + * + * Returns 0 for success or negative error code otherwise. + */ +static int nand_init_data_interface(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int modes, mode, ret; + + if (!chip->setup_data_interface) + return 0; + + /* + * First try to identify the best timings from ONFI parameters and + * if the NAND does not support ONFI, fallback to the default ONFI + * timing mode. + */ + modes = onfi_get_async_timing_mode(chip); + if (modes == ONFI_TIMING_MODE_UNKNOWN) { + if (!chip->onfi_timing_mode_default) + return 0; + + modes = GENMASK(chip->onfi_timing_mode_default, 0); + } + + + for (mode = fls(modes) - 1; mode >= 0; mode--) { + ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode); + if (ret) + continue; + + /* + * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the + * controller supports the requested timings. + */ + ret = chip->setup_data_interface(mtd, + NAND_DATA_IFACE_CHECK_ONLY, + &chip->data_interface); + if (!ret) { + chip->onfi_timing_mode_default = mode; + break; + } + } + + return 0; +} + +/** + * nand_fill_column_cycles - fill the column cycles of an address + * @chip: The NAND chip + * @addrs: Array of address cycles to fill + * @offset_in_page: The offset in the page + * + * Fills the first or the first two bytes of the @addrs field depending + * on the NAND bus width and the page size. + * + * Returns the number of cycles needed to encode the column, or a negative + * error code in case one of the arguments is invalid. + */ +static int nand_fill_column_cycles(struct nand_chip *chip, u8 *addrs, + unsigned int offset_in_page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + /* Make sure the offset is less than the actual page size. */ + if (offset_in_page > mtd->writesize + mtd->oobsize) + return -EINVAL; + + /* + * On small page NANDs, there's a dedicated command to access the OOB + * area, and the column address is relative to the start of the OOB + * area, not the start of the page. Asjust the address accordingly. + */ + if (mtd->writesize <= 512 && offset_in_page >= mtd->writesize) + offset_in_page -= mtd->writesize; + + /* + * The offset in page is expressed in bytes, if the NAND bus is 16-bit + * wide, then it must be divided by 2. + */ + if (chip->options & NAND_BUSWIDTH_16) { + if (WARN_ON(offset_in_page % 2)) + return -EINVAL; + + offset_in_page /= 2; + } + + addrs[0] = offset_in_page; + + /* + * Small page NANDs use 1 cycle for the columns, while large page NANDs + * need 2 + */ + if (mtd->writesize <= 512) + return 1; + + addrs[1] = offset_in_page >> 8; + + return 2; +} + +static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[4]; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_READ0, 0), + NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), + PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int ret; + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + if (offset_in_page >= mtd->writesize) + instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB; + else if (offset_in_page >= 256 && + !(chip->options & NAND_BUSWIDTH_16)) + instrs[0].ctx.cmd.opcode = NAND_CMD_READ1; + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + addrs[1] = page; + addrs[2] = page >> 8; + + if (chip->options & NAND_ROW_ADDR_3) { + addrs[3] = page >> 16; + instrs[1].ctx.addr.naddrs++; + } + + return nand_exec_op(chip, &op); +} + +static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, + unsigned int len) +{ + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[5]; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_READ0, 0), + NAND_OP_ADDR(4, addrs, 0), + NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), + PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int ret; + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + addrs[2] = page; + addrs[3] = page >> 8; + + if (chip->options & NAND_ROW_ADDR_3) { + addrs[4] = page >> 16; + instrs[1].ctx.addr.naddrs++; + } + + return nand_exec_op(chip, &op); +} + +/** + * nand_read_page_op - Do a READ PAGE operation + * @chip: The NAND chip + * @page: page to read + * @offset_in_page: offset within the page + * @buf: buffer used to store the data + * @len: length of the buffer + * + * This function issues a READ PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + if (chip->exec_op) { + if (mtd->writesize > 512) + return nand_lp_exec_read_page_op(chip, page, + offset_in_page, buf, + len); + + return nand_sp_exec_read_page_op(chip, page, offset_in_page, + buf, len); + } + + chip->cmdfunc(mtd, NAND_CMD_READ0, offset_in_page, page); + if (len) + chip->read_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_read_page_op); + +/** + * nand_read_param_page_op - Do a READ PARAMETER PAGE operation + * @chip: The NAND chip + * @page: parameter page to read + * @buf: buffer used to store the data + * @len: length of the buffer + * + * This function issues a READ PARAMETER PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int i; + u8 *p = buf; + + if (len && !buf) + return -EINVAL; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_PARAM, 0), + NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), + PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_8BIT_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + return nand_exec_op(chip, &op); + } + + chip->cmdfunc(mtd, NAND_CMD_PARAM, page, -1); + for (i = 0; i < len; i++) + p[i] = chip->read_byte(mtd); + + return 0; +} + +/** + * nand_change_read_column_op - Do a CHANGE READ COLUMN operation + * @chip: The NAND chip + * @offset_in_page: offset within the page + * @buf: buffer used to store the data + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function issues a CHANGE READ COLUMN operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_change_read_column_op(struct nand_chip *chip, + unsigned int offset_in_page, void *buf, + unsigned int len, bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + /* Small page NANDs do not support column change. */ + if (mtd->writesize <= 512) + return -ENOTSUPP; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[2] = {}; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_RNDOUT, 0), + NAND_OP_ADDR(2, addrs, 0), + NAND_OP_CMD(NAND_CMD_RNDOUTSTART, + PSEC_TO_NSEC(sdr->tCCS_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int ret; + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + instrs[3].ctx.data.force_8bit = force_8bit; + + return nand_exec_op(chip, &op); + } + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset_in_page, -1); + if (len) + chip->read_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_change_read_column_op); + +/** + * nand_read_oob_op - Do a READ OOB operation + * @chip: The NAND chip + * @page: page to read + * @offset_in_oob: offset within the OOB area + * @buf: buffer used to store the data + * @len: length of the buffer + * + * This function issues a READ OOB operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_read_oob_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_oob, void *buf, unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_oob + len > mtd->oobsize) + return -EINVAL; + + if (chip->exec_op) + return nand_read_page_op(chip, page, + mtd->writesize + offset_in_oob, + buf, len); + + chip->cmdfunc(mtd, NAND_CMD_READOOB, offset_in_oob, page); + if (len) + chip->read_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_read_oob_op); + +static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len, bool prog) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[5] = {}; + struct nand_op_instr instrs[] = { + /* + * The first instruction will be dropped if we're dealing + * with a large page NAND and adjusted if we're dealing + * with a small page NAND and the page offset is > 255. + */ + NAND_OP_CMD(NAND_CMD_READ0, 0), + NAND_OP_CMD(NAND_CMD_SEQIN, 0), + NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_DATA_OUT(len, buf, 0), + NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); + int ret; + u8 status; + + if (naddrs < 0) + return naddrs; + + addrs[naddrs++] = page; + addrs[naddrs++] = page >> 8; + if (chip->options & NAND_ROW_ADDR_3) + addrs[naddrs++] = page >> 16; + + instrs[2].ctx.addr.naddrs = naddrs; + + /* Drop the last two instructions if we're not programming the page. */ + if (!prog) { + op.ninstrs -= 2; + /* Also drop the DATA_OUT instruction if empty. */ + if (!len) + op.ninstrs--; + } + + if (mtd->writesize <= 512) { + /* + * Small pages need some more tweaking: we have to adjust the + * first instruction depending on the page offset we're trying + * to access. + */ + if (offset_in_page >= mtd->writesize) + instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB; + else if (offset_in_page >= 256 && + !(chip->options & NAND_BUSWIDTH_16)) + instrs[0].ctx.cmd.opcode = NAND_CMD_READ1; + } else { + /* + * Drop the first command if we're dealing with a large page + * NAND. + */ + op.instrs++; + op.ninstrs--; + } + + ret = nand_exec_op(chip, &op); + if (!prog || ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; + + return status; +} + +/** + * nand_prog_page_begin_op - starts a PROG PAGE operation + * @chip: The NAND chip + * @page: page to write + * @offset_in_page: offset within the page + * @buf: buffer containing the data to write to the page + * @len: length of the buffer + * + * This function issues the first half of a PROG PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + if (chip->exec_op) + return nand_exec_prog_page_op(chip, page, offset_in_page, buf, + len, false); + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); + + if (buf) + chip->write_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_prog_page_begin_op); + +/** + * nand_prog_page_end_op - ends a PROG PAGE operation + * @chip: The NAND chip + * + * This function issues the second half of a PROG PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_prog_page_end_op(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + u8 status; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_PAGEPROG, + PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + ret = nand_exec_op(chip, &op); + if (ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; + } else { + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + ret = chip->waitfunc(mtd, chip); + if (ret < 0) + return ret; + + status = ret; + } + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_prog_page_end_op); + +/** + * nand_prog_page_op - Do a full PROG PAGE operation + * @chip: The NAND chip + * @page: page to write + * @offset_in_page: offset within the page + * @buf: buffer containing the data to write to the page + * @len: length of the buffer + * + * This function issues a full PROG PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_prog_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int status; + + if (!len || !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + if (chip->exec_op) { + status = nand_exec_prog_page_op(chip, page, offset_in_page, buf, + len, true); + } else { + chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); + chip->write_buf(mtd, buf, len); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_prog_page_op); + +/** + * nand_change_write_column_op - Do a CHANGE WRITE COLUMN operation + * @chip: The NAND chip + * @offset_in_page: offset within the page + * @buf: buffer containing the data to send to the NAND + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function issues a CHANGE WRITE COLUMN operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_change_write_column_op(struct nand_chip *chip, + unsigned int offset_in_page, + const void *buf, unsigned int len, + bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + /* Small page NANDs do not support column change. */ + if (mtd->writesize <= 512) + return -ENOTSUPP; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[2]; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_RNDIN, 0), + NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)), + NAND_OP_DATA_OUT(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int ret; + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + instrs[2].ctx.data.force_8bit = force_8bit; + + /* Drop the DATA_OUT instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + return nand_exec_op(chip, &op); + } + + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset_in_page, -1); + if (len) + chip->write_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_change_write_column_op); + +/** + * nand_readid_op - Do a READID operation + * @chip: The NAND chip + * @addr: address cycle to pass after the READID command + * @buf: buffer used to store the ID + * @len: length of the buffer + * + * This function sends a READID command and reads back the ID returned by the + * NAND. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int i; + u8 *id = buf; + + if (len && !buf) + return -EINVAL; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_READID, 0), + NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_8BIT_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + return nand_exec_op(chip, &op); + } + + chip->cmdfunc(mtd, NAND_CMD_READID, addr, -1); + + for (i = 0; i < len; i++) + id[i] = chip->read_byte(mtd); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_readid_op); + +/** + * nand_status_op - Do a STATUS operation + * @chip: The NAND chip + * @status: out variable to store the NAND status + * + * This function sends a STATUS command and reads back the status returned by + * the NAND. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_status_op(struct nand_chip *chip, u8 *status) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_STATUS, + PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_8BIT_DATA_IN(1, status, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + if (!status) + op.ninstrs--; + + return nand_exec_op(chip, &op); + } + + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + if (status) + *status = chip->read_byte(mtd); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_status_op); + +/** + * nand_exit_status_op - Exit a STATUS operation + * @chip: The NAND chip + * + * This function sends a READ0 command to cancel the effect of the STATUS + * command to avoid reading only the status until a new read command is sent. + * + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_exit_status_op(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (chip->exec_op) { + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_READ0, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + return nand_exec_op(chip, &op); + } + + chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_exit_status_op); + +/** + * nand_erase_op - Do an erase operation + * @chip: The NAND chip + * @eraseblock: block to erase + * + * This function sends an ERASE command and waits for the NAND to be ready + * before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int page = eraseblock << + (chip->phys_erase_shift - chip->page_shift); + int ret; + u8 status; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[3] = { page, page >> 8, page >> 16 }; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_ERASE1, 0), + NAND_OP_ADDR(2, addrs, 0), + NAND_OP_CMD(NAND_CMD_ERASE2, + PSEC_TO_MSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + if (chip->options & NAND_ROW_ADDR_3) + instrs[1].ctx.addr.naddrs++; + + ret = nand_exec_op(chip, &op); + if (ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; + } else { + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + + ret = chip->waitfunc(mtd, chip); + if (ret < 0) + return ret; + + status = ret; + } + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_erase_op); + +/** + * nand_set_features_op - Do a SET FEATURES operation + * @chip: The NAND chip + * @feature: feature id + * @data: 4 bytes of data + * + * This function sends a SET FEATURES command and waits for the NAND to be + * ready before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int nand_set_features_op(struct nand_chip *chip, u8 feature, + const void *data) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const u8 *params = data; + int i, ret; + u8 status; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0), + NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data, + PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + ret = nand_exec_op(chip, &op); + if (ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; + } else { + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, params[i]); + + ret = chip->waitfunc(mtd, chip); + if (ret < 0) + return ret; + + status = ret; + } + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +/** + * nand_get_features_op - Do a GET FEATURES operation + * @chip: The NAND chip + * @feature: feature id + * @data: 4 bytes of data + * + * This function sends a GET FEATURES command and waits for the NAND to be + * ready before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int nand_get_features_op(struct nand_chip *chip, u8 feature, + void *data) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u8 *params = data; + int i; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0), + NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), + PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN, + data, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + return nand_exec_op(chip, &op); + } + + chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, feature, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + params[i] = chip->read_byte(mtd); + + return 0; +} + +/** + * nand_reset_op - Do a reset operation + * @chip: The NAND chip + * + * This function sends a RESET command and waits for the NAND to be ready + * before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_reset_op(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + return nand_exec_op(chip, &op); + } + + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_reset_op); + +/** + * nand_read_data_op - Read data from the NAND + * @chip: The NAND chip + * @buf: buffer used to store the data + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function does a raw data read on the bus. Usually used after launching + * another NAND operation like nand_read_page_op(). + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, + bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!len || !buf) + return -EINVAL; + + if (chip->exec_op) { + struct nand_op_instr instrs[] = { + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + instrs[0].ctx.data.force_8bit = force_8bit; + + return nand_exec_op(chip, &op); + } + + if (force_8bit) { + u8 *p = buf; + unsigned int i; + + for (i = 0; i < len; i++) + p[i] = chip->read_byte(mtd); + } else { + chip->read_buf(mtd, buf, len); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nand_read_data_op); + +/** + * nand_write_data_op - Write data from the NAND + * @chip: The NAND chip + * @buf: buffer containing the data to send on the bus + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function does a raw data write on the bus. Usually used after launching + * another NAND operation like nand_write_page_begin_op(). + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_write_data_op(struct nand_chip *chip, const void *buf, + unsigned int len, bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!len || !buf) + return -EINVAL; + + if (chip->exec_op) { + struct nand_op_instr instrs[] = { + NAND_OP_DATA_OUT(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + instrs[0].ctx.data.force_8bit = force_8bit; + + return nand_exec_op(chip, &op); + } + + if (force_8bit) { + const u8 *p = buf; + unsigned int i; + + for (i = 0; i < len; i++) + chip->write_byte(mtd, p[i]); + } else { + chip->write_buf(mtd, buf, len); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nand_write_data_op); + +/** + * struct nand_op_parser_ctx - Context used by the parser + * @instrs: array of all the instructions that must be addressed + * @ninstrs: length of the @instrs array + * @subop: Sub-operation to be passed to the NAND controller + * + * This structure is used by the core to split NAND operations into + * sub-operations that can be handled by the NAND controller. + */ +struct nand_op_parser_ctx { + const struct nand_op_instr *instrs; + unsigned int ninstrs; + struct nand_subop subop; +}; + +/** + * nand_op_parser_must_split_instr - Checks if an instruction must be split + * @pat: the parser pattern element that matches @instr + * @instr: pointer to the instruction to check + * @start_offset: this is an in/out parameter. If @instr has already been + * split, then @start_offset is the offset from which to start + * (either an address cycle or an offset in the data buffer). + * Conversely, if the function returns true (ie. instr must be + * split), this parameter is updated to point to the first + * data/address cycle that has not been taken care of. + * + * Some NAND controllers are limited and cannot send X address cycles with a + * unique operation, or cannot read/write more than Y bytes at the same time. + * In this case, split the instruction that does not fit in a single + * controller-operation into two or more chunks. + * + * Returns true if the instruction must be split, false otherwise. + * The @start_offset parameter is also updated to the offset at which the next + * bundle of instruction must start (if an address or a data instruction). + */ +static bool +nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat, + const struct nand_op_instr *instr, + unsigned int *start_offset) +{ + switch (pat->type) { + case NAND_OP_ADDR_INSTR: + if (!pat->ctx.addr.maxcycles) + break; + + if (instr->ctx.addr.naddrs - *start_offset > + pat->ctx.addr.maxcycles) { + *start_offset += pat->ctx.addr.maxcycles; + return true; + } + break; + + case NAND_OP_DATA_IN_INSTR: + case NAND_OP_DATA_OUT_INSTR: + if (!pat->ctx.data.maxlen) + break; + + if (instr->ctx.data.len - *start_offset > + pat->ctx.data.maxlen) { + *start_offset += pat->ctx.data.maxlen; + return true; + } + break; + + default: + break; + } + + return false; +} + +/** + * nand_op_parser_match_pat - Checks if a pattern matches the instructions + * remaining in the parser context + * @pat: the pattern to test + * @ctx: the parser context structure to match with the pattern @pat + * + * Check if @pat matches the set or a sub-set of instructions remaining in @ctx. + * Returns true if this is the case, false ortherwise. When true is returned, + * @ctx->subop is updated with the set of instructions to be passed to the + * controller driver. + */ +static bool +nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat, + struct nand_op_parser_ctx *ctx) +{ + unsigned int instr_offset = ctx->subop.first_instr_start_off; + const struct nand_op_instr *end = ctx->instrs + ctx->ninstrs; + const struct nand_op_instr *instr = ctx->subop.instrs; + unsigned int i, ninstrs; + + for (i = 0, ninstrs = 0; i < pat->nelems && instr < end; i++) { + /* + * The pattern instruction does not match the operation + * instruction. If the instruction is marked optional in the + * pattern definition, we skip the pattern element and continue + * to the next one. If the element is mandatory, there's no + * match and we can return false directly. + */ + if (instr->type != pat->elems[i].type) { + if (!pat->elems[i].optional) + return false; + + continue; + } + + /* + * Now check the pattern element constraints. If the pattern is + * not able to handle the whole instruction in a single step, + * we have to split it. + * The last_instr_end_off value comes back updated to point to + * the position where we have to split the instruction (the + * start of the next subop chunk). + */ + if (nand_op_parser_must_split_instr(&pat->elems[i], instr, + &instr_offset)) { + ninstrs++; + i++; + break; + } + + instr++; + ninstrs++; + instr_offset = 0; + } + + /* + * This can happen if all instructions of a pattern are optional. + * Still, if there's not at least one instruction handled by this + * pattern, this is not a match, and we should try the next one (if + * any). + */ + if (!ninstrs) + return false; + + /* + * We had a match on the pattern head, but the pattern may be longer + * than the instructions we're asked to execute. We need to make sure + * there's no mandatory elements in the pattern tail. + */ + for (; i < pat->nelems; i++) { + if (!pat->elems[i].optional) + return false; + } + + /* + * We have a match: update the subop structure accordingly and return + * true. + */ + ctx->subop.ninstrs = ninstrs; + ctx->subop.last_instr_end_off = instr_offset; + + return true; +} + +#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) +{ + const struct nand_op_instr *instr; + char *prefix = " "; + unsigned int i; + + pr_debug("executing subop:\n"); + + for (i = 0; i < ctx->ninstrs; i++) { + instr = &ctx->instrs[i]; + + if (instr == &ctx->subop.instrs[0]) + prefix = " ->"; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + pr_debug("%sCMD [0x%02x]\n", prefix, + instr->ctx.cmd.opcode); + break; + case NAND_OP_ADDR_INSTR: + pr_debug("%sADDR [%d cyc: %*ph]\n", prefix, + instr->ctx.addr.naddrs, + instr->ctx.addr.naddrs < 64 ? + instr->ctx.addr.naddrs : 64, + instr->ctx.addr.addrs); + break; + case NAND_OP_DATA_IN_INSTR: + pr_debug("%sDATA_IN [%d B%s]\n", prefix, + instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + break; + case NAND_OP_DATA_OUT_INSTR: + pr_debug("%sDATA_OUT [%d B%s]\n", prefix, + instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + break; + case NAND_OP_WAITRDY_INSTR: + pr_debug("%sWAITRDY [max %d ms]\n", prefix, + instr->ctx.waitrdy.timeout_ms); + break; + } + + if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1]) + prefix = " "; + } +} +#else +static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) +{ + /* NOP */ +} +#endif + +/** + * nand_op_parser_exec_op - exec_op parser + * @chip: the NAND chip + * @parser: patterns description provided by the controller driver + * @op: the NAND operation to address + * @check_only: when true, the function only checks if @op can be handled but + * does not execute the operation + * + * Helper function designed to ease integration of NAND controller drivers that + * only support a limited set of instruction sequences. The supported sequences + * are described in @parser, and the framework takes care of splitting @op into + * multiple sub-operations (if required) and pass them back to the ->exec() + * callback of the matching pattern if @check_only is set to false. + * + * NAND controller drivers should call this function from their own ->exec_op() + * implementation. + * + * Returns 0 on success, a negative error code otherwise. A failure can be + * caused by an unsupported operation (none of the supported patterns is able + * to handle the requested operation), or an error returned by one of the + * matching pattern->exec() hook. + */ +int nand_op_parser_exec_op(struct nand_chip *chip, + const struct nand_op_parser *parser, + const struct nand_operation *op, bool check_only) +{ + struct nand_op_parser_ctx ctx = { + .subop.instrs = op->instrs, + .instrs = op->instrs, + .ninstrs = op->ninstrs, + }; + unsigned int i; + + while (ctx.subop.instrs < op->instrs + op->ninstrs) { + int ret; + + for (i = 0; i < parser->npatterns; i++) { + const struct nand_op_parser_pattern *pattern; + + pattern = &parser->patterns[i]; + if (!nand_op_parser_match_pat(pattern, &ctx)) + continue; + + nand_op_parser_trace(&ctx); + + if (check_only) + break; + + ret = pattern->exec(chip, &ctx.subop); + if (ret) + return ret; + + break; + } + + if (i == parser->npatterns) { + pr_debug("->exec_op() parser: pattern not found!\n"); + return -ENOTSUPP; + } + + /* + * Update the context structure by pointing to the start of the + * next subop. + */ + ctx.subop.instrs = ctx.subop.instrs + ctx.subop.ninstrs; + if (ctx.subop.last_instr_end_off) + ctx.subop.instrs -= 1; + + ctx.subop.first_instr_start_off = ctx.subop.last_instr_end_off; + } + + return 0; +} +EXPORT_SYMBOL_GPL(nand_op_parser_exec_op); + +static bool nand_instr_is_data(const struct nand_op_instr *instr) +{ + return instr && (instr->type == NAND_OP_DATA_IN_INSTR || + instr->type == NAND_OP_DATA_OUT_INSTR); +} + +static bool nand_subop_instr_is_valid(const struct nand_subop *subop, + unsigned int instr_idx) +{ + return subop && instr_idx < subop->ninstrs; +} + +static int nand_subop_get_start_off(const struct nand_subop *subop, + unsigned int instr_idx) +{ + if (instr_idx) + return 0; + + return subop->first_instr_start_off; +} + +/** + * nand_subop_get_addr_start_off - Get the start offset in an address array + * @subop: The entire sub-operation + * @instr_idx: Index of the instruction inside the sub-operation + * + * During driver development, one could be tempted to directly use the + * ->addr.addrs field of address instructions. This is wrong as address + * instructions might be split. + * + * Given an address instruction, returns the offset of the first cycle to issue. + */ +int nand_subop_get_addr_start_off(const struct nand_subop *subop, + unsigned int instr_idx) +{ + if (!nand_subop_instr_is_valid(subop, instr_idx) || + subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR) + return -EINVAL; + + return nand_subop_get_start_off(subop, instr_idx); +} +EXPORT_SYMBOL_GPL(nand_subop_get_addr_start_off); + +/** + * nand_subop_get_num_addr_cyc - Get the remaining address cycles to assert + * @subop: The entire sub-operation + * @instr_idx: Index of the instruction inside the sub-operation + * + * During driver development, one could be tempted to directly use the + * ->addr->naddrs field of a data instruction. This is wrong as instructions + * might be split. + * + * Given an address instruction, returns the number of address cycle to issue. + */ +int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, + unsigned int instr_idx) +{ + int start_off, end_off; + + if (!nand_subop_instr_is_valid(subop, instr_idx) || + subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR) + return -EINVAL; + + start_off = nand_subop_get_addr_start_off(subop, instr_idx); + + if (instr_idx == subop->ninstrs - 1 && + subop->last_instr_end_off) + end_off = subop->last_instr_end_off; + else + end_off = subop->instrs[instr_idx].ctx.addr.naddrs; + + return end_off - start_off; +} +EXPORT_SYMBOL_GPL(nand_subop_get_num_addr_cyc); + +/** + * nand_subop_get_data_start_off - Get the start offset in a data array + * @subop: The entire sub-operation + * @instr_idx: Index of the instruction inside the sub-operation + * + * During driver development, one could be tempted to directly use the + * ->data->buf.{in,out} field of data instructions. This is wrong as data + * instructions might be split. + * + * Given a data instruction, returns the offset to start from. + */ +int nand_subop_get_data_start_off(const struct nand_subop *subop, + unsigned int instr_idx) +{ + if (!nand_subop_instr_is_valid(subop, instr_idx) || + !nand_instr_is_data(&subop->instrs[instr_idx])) + return -EINVAL; + + return nand_subop_get_start_off(subop, instr_idx); +} +EXPORT_SYMBOL_GPL(nand_subop_get_data_start_off); + +/** + * nand_subop_get_data_len - Get the number of bytes to retrieve + * @subop: The entire sub-operation + * @instr_idx: Index of the instruction inside the sub-operation + * + * During driver development, one could be tempted to directly use the + * ->data->len field of a data instruction. This is wrong as data instructions + * might be split. + * + * Returns the length of the chunk of data to send/receive. + */ +int nand_subop_get_data_len(const struct nand_subop *subop, + unsigned int instr_idx) +{ + int start_off = 0, end_off; + + if (!nand_subop_instr_is_valid(subop, instr_idx) || + !nand_instr_is_data(&subop->instrs[instr_idx])) + return -EINVAL; + + start_off = nand_subop_get_data_start_off(subop, instr_idx); + + if (instr_idx == subop->ninstrs - 1 && + subop->last_instr_end_off) + end_off = subop->last_instr_end_off; + else + end_off = subop->instrs[instr_idx].ctx.data.len; + + return end_off - start_off; +} +EXPORT_SYMBOL_GPL(nand_subop_get_data_len); + +/** + * nand_reset - Reset and initialize a NAND device + * @chip: The NAND chip + * @chipnr: Internal die id + * + * Save the timings data structure, then apply SDR timings mode 0 (see + * nand_reset_data_interface for details), do the reset operation, and + * apply back the previous timings. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_reset(struct nand_chip *chip, int chipnr) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_data_interface saved_data_intf = chip->data_interface; + int ret; + + ret = nand_reset_data_interface(chip, chipnr); + if (ret) + return ret; + + /* + * The CS line has to be released before we can apply the new NAND + * interface settings, hence this weird ->select_chip() dance. + */ + chip->select_chip(mtd, chipnr); + ret = nand_reset_op(chip); + chip->select_chip(mtd, -1); + if (ret) + return ret; + + chip->select_chip(mtd, chipnr); + chip->data_interface = saved_data_intf; + ret = nand_setup_data_interface(chip, chipnr); + chip->select_chip(mtd, -1); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_reset); + +/** + * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data + * @buf: buffer to test + * @len: buffer length + * @bitflips_threshold: maximum number of bitflips + * + * Check if a buffer contains only 0xff, which means the underlying region + * has been erased and is ready to be programmed. + * The bitflips_threshold specify the maximum number of bitflips before + * considering the region is not erased. + * Note: The logic of this function has been extracted from the memweight + * implementation, except that nand_check_erased_buf function exit before + * testing the whole buffer if the number of bitflips exceed the + * bitflips_threshold value. + * + * Returns a positive number of bitflips less than or equal to + * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the + * threshold. + */ +static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold) +{ + const unsigned char *bitmap = buf; + int bitflips = 0; + int weight; + + for (; len && ((uintptr_t)bitmap) % sizeof(long); + len--, bitmap++) { + weight = hweight8(*bitmap); + bitflips += BITS_PER_BYTE - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + for (; len >= sizeof(long); + len -= sizeof(long), bitmap += sizeof(long)) { + unsigned long d = *((unsigned long *)bitmap); + if (d == ~0UL) + continue; + weight = hweight_long(d); + bitflips += BITS_PER_LONG - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + for (; len > 0; len--, bitmap++) { + weight = hweight8(*bitmap); + bitflips += BITS_PER_BYTE - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + return bitflips; +} + +/** + * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only + * 0xff data + * @data: data buffer to test + * @datalen: data length + * @ecc: ECC buffer + * @ecclen: ECC length + * @extraoob: extra OOB buffer + * @extraooblen: extra OOB length + * @bitflips_threshold: maximum number of bitflips + * + * Check if a data buffer and its associated ECC and OOB data contains only + * 0xff pattern, which means the underlying region has been erased and is + * ready to be programmed. + * The bitflips_threshold specify the maximum number of bitflips before + * considering the region as not erased. + * + * Note: + * 1/ ECC algorithms are working on pre-defined block sizes which are usually + * different from the NAND page size. When fixing bitflips, ECC engines will + * report the number of errors per chunk, and the NAND core infrastructure + * expect you to return the maximum number of bitflips for the whole page. + * This is why you should always use this function on a single chunk and + * not on the whole page. After checking each chunk you should update your + * max_bitflips value accordingly. + * 2/ When checking for bitflips in erased pages you should not only check + * the payload data but also their associated ECC data, because a user might + * have programmed almost all bits to 1 but a few. In this case, we + * shouldn't consider the chunk as erased, and checking ECC bytes prevent + * this case. + * 3/ The extraoob argument is optional, and should be used if some of your OOB + * data are protected by the ECC engine. + * It could also be used if you support subpages and want to attach some + * extra OOB data to an ECC chunk. + * + * Returns a positive number of bitflips less than or equal to + * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the + * threshold. In case of success, the passed buffers are filled with 0xff. + */ +int nand_check_erased_ecc_chunk(void *data, int datalen, + void *ecc, int ecclen, + void *extraoob, int extraooblen, + int bitflips_threshold) +{ + int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0; + + data_bitflips = nand_check_erased_buf(data, datalen, + bitflips_threshold); + if (data_bitflips < 0) + return data_bitflips; + + bitflips_threshold -= data_bitflips; + + ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold); + if (ecc_bitflips < 0) + return ecc_bitflips; + + bitflips_threshold -= ecc_bitflips; + + extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen, + bitflips_threshold); + if (extraoob_bitflips < 0) + return extraoob_bitflips; + + if (data_bitflips) + memset(data, 0xff, datalen); + + if (ecc_bitflips) + memset(ecc, 0xff, ecclen); + + if (extraoob_bitflips) + memset(extraoob, 0xff, extraooblen); + + return data_bitflips + ecc_bitflips + extraoob_bitflips; +} +EXPORT_SYMBOL(nand_check_erased_ecc_chunk); + +/** + * nand_read_page_raw - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Not for syndrome calculating ECC controllers, which use a special oob layout. + */ +int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int ret; + + ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize); + if (ret) + return ret; + + if (oob_required) { + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(nand_read_page_raw); + +/** + * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * We need a special oob layout and handling even when OOB isn't used. + */ +static int nand_read_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size, ret; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + ret = nand_read_data_op(chip, buf, eccsize, false); + if (ret) + return ret; + + buf += eccsize; + + if (chip->ecc.prepad) { + ret = nand_read_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + + oob += chip->ecc.prepad; + } + + ret = nand_read_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + + oob += eccbytes; + + if (chip->ecc.postpad) { + ret = nand_read_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) { + ret = nand_read_data_op(chip, oob, size, false); + if (ret) + return ret; + } + + return 0; +} + +/** + * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + */ +static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size, ret; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->ecc.calc_buf; + uint8_t *ecc_code = chip->ecc.code_buf; + unsigned int max_bitflips = 0; + + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + return max_bitflips; +} + +/** + * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @data_offs: offset of requested data within the page + * @readlen: data length + * @bufpoi: buffer to store read data + * @page: page number to read + */ +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, + int page) +{ + int start_step, end_step, num_steps, ret; + uint8_t *p; + int data_col_addr, i, gaps = 0; + int datafrag_len, eccfrag_len, aligned_len, aligned_pos; + int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + int index, section = 0; + unsigned int max_bitflips = 0; + struct mtd_oob_region oobregion = { }; + + /* Column address within the page aligned to ECC size (256bytes) */ + start_step = data_offs / chip->ecc.size; + end_step = (data_offs + readlen - 1) / chip->ecc.size; + num_steps = end_step - start_step + 1; + index = start_step * chip->ecc.bytes; + + /* Data size aligned to ECC ecc.size */ + datafrag_len = num_steps * chip->ecc.size; + eccfrag_len = num_steps * chip->ecc.bytes; + + data_col_addr = start_step * chip->ecc.size; + /* If we read not a page aligned data */ + p = bufpoi + data_col_addr; + ret = nand_read_page_op(chip, page, data_col_addr, p, datafrag_len); + if (ret) + return ret; + + /* Calculate ECC */ + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) + chip->ecc.calculate(mtd, p, &chip->ecc.calc_buf[i]); + + /* + * The performance is faster if we position offsets according to + * ecc.pos. Let's make sure that there are no gaps in ECC positions. + */ + ret = mtd_ooblayout_find_eccregion(mtd, index, §ion, &oobregion); + if (ret) + return ret; + + if (oobregion.length < eccfrag_len) + gaps = 1; + + if (gaps) { + ret = nand_change_read_column_op(chip, mtd->writesize, + chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; + } else { + /* + * Send the command to read the particular ECC bytes take care + * about buswidth alignment in read_buf. + */ + aligned_pos = oobregion.offset & ~(busw - 1); + aligned_len = eccfrag_len; + if (oobregion.offset & (busw - 1)) + aligned_len++; + if ((oobregion.offset + (num_steps * chip->ecc.bytes)) & + (busw - 1)) + aligned_len++; + + ret = nand_change_read_column_op(chip, + mtd->writesize + aligned_pos, + &chip->oob_poi[aligned_pos], + aligned_len, false); + if (ret) + return ret; + } + + ret = mtd_ooblayout_get_eccbytes(mtd, chip->ecc.code_buf, + chip->oob_poi, index, eccfrag_len); + if (ret) + return ret; + + p = bufpoi + data_col_addr; + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + int stat; + + stat = chip->ecc.correct(mtd, p, &chip->ecc.code_buf[i], + &chip->ecc.calc_buf[i]); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, + &chip->ecc.code_buf[i], + chip->ecc.bytes, + NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + return max_bitflips; +} + +/** + * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Not for syndrome calculating ECC controllers which need a special oob layout. + */ +static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size, ret; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->ecc.calc_buf; + uint8_t *ecc_code = chip->ecc.code_buf; + unsigned int max_bitflips = 0; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; + + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (ret) + return ret; + + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, eccsize, + &ecc_code[i], eccbytes, + NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + return max_bitflips; +} + +/** + * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Hardware ECC for large page chips, require OOB to be read first. For this + * ECC mode, the write_page method is re-used from ECC_HW. These methods + * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with + * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from + * the data area, by overwriting the NAND manufacturer bad block markings. + */ +static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size, ret; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_code = chip->ecc.code_buf; + uint8_t *ecc_calc = chip->ecc.calc_buf; + unsigned int max_bitflips = 0; + + /* Read the OOB area first */ + ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); + if (ret) + return ret; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; + + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, eccsize, + &ecc_code[i], eccbytes, + NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + return max_bitflips; +} + +/** + * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling. + */ +static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int ret, i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + unsigned int max_bitflips = 0; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; + + if (chip->ecc.prepad) { + ret = nand_read_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + + oob += chip->ecc.prepad; + } + + chip->ecc.hwctl(mtd, NAND_ECC_READSYN); + + ret = nand_read_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + + stat = chip->ecc.correct(mtd, p, oob, NULL); + + oob += eccbytes; + + if (chip->ecc.postpad) { + ret = nand_read_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + + oob += chip->ecc.postpad; + } + + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, + oob - eccpadbytes, + eccpadbytes, + NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) { + ret = nand_read_data_op(chip, oob, i, false); + if (ret) + return ret; + } + + return max_bitflips; +} + +/** + * nand_transfer_oob - [INTERN] Transfer oob to client buffer + * @mtd: mtd info structure + * @oob: oob destination address + * @ops: oob ops structure + * @len: size of oob to transfer + */ +static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob, + struct mtd_oob_ops *ops, size_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret; + + switch (ops->mode) { + + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + memcpy(oob, chip->oob_poi + ops->ooboffs, len); + return oob + len; + + case MTD_OPS_AUTO_OOB: + ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi, + ops->ooboffs, len); + BUG_ON(ret); + return oob + len; + + default: + BUG(); + } + return NULL; +} + +/** + * nand_setup_read_retry - [INTERN] Set the READ RETRY mode + * @mtd: MTD device structure + * @retry_mode: the retry mode to use + * + * Some vendors supply a special command to shift the Vt threshold, to be used + * when there are too many bitflips in a page (i.e., ECC error). After setting + * a new threshold, the host should retry reading the page. + */ +static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + pr_debug("setting READ RETRY mode %d\n", retry_mode); + + if (retry_mode >= chip->read_retries) + return -EINVAL; + + if (!chip->setup_read_retry) + return -EOPNOTSUPP; + + return chip->setup_read_retry(mtd, retry_mode); +} + +/** + * nand_do_read_ops - [INTERN] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob ops structure + * + * Internal function. Called with chip held. + */ +static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int chipnr, page, realpage, col, bytes, aligned, oob_required; + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + uint32_t readlen = ops->len; + uint32_t oobreadlen = ops->ooblen; + uint32_t max_oobsize = mtd_oobavail(mtd, ops); + + uint8_t *bufpoi, *oob, *buf; + int use_bufpoi; + unsigned int max_bitflips = 0; + int retry_mode = 0; + bool ecc_fail = false; + + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; + + col = (int)(from & (mtd->writesize - 1)); + + buf = ops->datbuf; + oob = ops->oobbuf; + oob_required = oob ? 1 : 0; + + while (1) { + unsigned int ecc_failures = mtd->ecc_stats.failed; + + bytes = min(mtd->writesize - col, readlen); + aligned = (bytes == mtd->writesize); + + if (!aligned) + use_bufpoi = 1; + else if (chip->options & NAND_USE_BOUNCE_BUFFER) + use_bufpoi = !virt_addr_valid(buf) || + !IS_ALIGNED((unsigned long)buf, + chip->buf_align); + else + use_bufpoi = 0; + + /* Is the current page in the buffer? */ + if (realpage != chip->pagebuf || oob) { + bufpoi = use_bufpoi ? chip->data_buf : buf; + + if (use_bufpoi && aligned) + pr_debug("%s: using read bounce buffer for buf@%p\n", + __func__, buf); + +read_retry: + /* + * Now read the page into the buffer. Absent an error, + * the read methods return max bitflips per ecc step. + */ + if (unlikely(ops->mode == MTD_OPS_RAW)) + ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, + oob_required, + page); + else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && + !oob) + ret = chip->ecc.read_subpage(mtd, chip, + col, bytes, bufpoi, + page); + else + ret = chip->ecc.read_page(mtd, chip, bufpoi, + oob_required, page); + if (ret < 0) { + if (use_bufpoi) + /* Invalidate page cache */ + chip->pagebuf = -1; + break; + } + + /* Transfer not aligned data */ + if (use_bufpoi) { + if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && + !(mtd->ecc_stats.failed - ecc_failures) && + (ops->mode != MTD_OPS_RAW)) { + chip->pagebuf = realpage; + chip->pagebuf_bitflips = ret; + } else { + /* Invalidate page cache */ + chip->pagebuf = -1; + } + memcpy(buf, chip->data_buf + col, bytes); + } + + if (unlikely(oob)) { + int toread = min(oobreadlen, max_oobsize); + + if (toread) { + oob = nand_transfer_oob(mtd, + oob, ops, toread); + oobreadlen -= toread; + } + } + + if (chip->options & NAND_NEED_READRDY) { + /* Apply delay or wait for ready/busy pin */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } + + if (mtd->ecc_stats.failed - ecc_failures) { + if (retry_mode + 1 < chip->read_retries) { + retry_mode++; + ret = nand_setup_read_retry(mtd, + retry_mode); + if (ret < 0) + break; + + /* Reset failures; retry */ + mtd->ecc_stats.failed = ecc_failures; + goto read_retry; + } else { + /* No more retry modes; real failure */ + ecc_fail = true; + } + } + + buf += bytes; + max_bitflips = max_t(unsigned int, max_bitflips, ret); + } else { + memcpy(buf, chip->data_buf + col, bytes); + buf += bytes; + max_bitflips = max_t(unsigned int, max_bitflips, + chip->pagebuf_bitflips); + } + + readlen -= bytes; + + /* Reset to retry mode 0 */ + if (retry_mode) { + ret = nand_setup_read_retry(mtd, 0); + if (ret < 0) + break; + retry_mode = 0; + } + + if (!readlen) + break; + + /* For subsequent reads align to page boundary */ + col = 0; + /* Increment page address */ + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + chip->select_chip(mtd, -1); + + ops->retlen = ops->len - (size_t) readlen; + if (oob) + ops->oobretlen = ops->ooblen - oobreadlen; + + if (ret < 0) + return ret; + + if (ecc_fail) + return -EBADMSG; + + return max_bitflips; +} + +/** + * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + */ +int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) +{ + return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); +} +EXPORT_SYMBOL(nand_read_oob_std); + +/** + * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC + * with syndromes + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + */ +int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int length = mtd->oobsize; + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size; + uint8_t *bufpoi = chip->oob_poi; + int i, toread, sndrnd = 0, pos, ret; + + ret = nand_read_page_op(chip, page, chip->ecc.size, NULL, 0); + if (ret) + return ret; + + for (i = 0; i < chip->ecc.steps; i++) { + if (sndrnd) { + int ret; + + pos = eccsize + i * (eccsize + chunk); + if (mtd->writesize > 512) + ret = nand_change_read_column_op(chip, pos, + NULL, 0, + false); + else + ret = nand_read_page_op(chip, page, pos, NULL, + 0); + + if (ret) + return ret; + } else + sndrnd = 1; + toread = min_t(int, length, chunk); + + ret = nand_read_data_op(chip, bufpoi, toread, false); + if (ret) + return ret; + + bufpoi += toread; + length -= toread; + } + if (length > 0) { + ret = nand_read_data_op(chip, bufpoi, length, false); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(nand_read_oob_syndrome); + +/** + * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) +{ + return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); +} +EXPORT_SYMBOL(nand_write_oob_std); + +/** + * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC + * with syndrome - only for large page flash + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size, length = mtd->oobsize; + int ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; + + /* + * data-ecc-data-ecc ... ecc-oob + * or + * data-pad-ecc-pad-data-pad .... ecc-pad-oob + */ + if (!chip->ecc.prepad && !chip->ecc.postpad) { + pos = steps * (eccsize + chunk); + steps = 0; + } else + pos = eccsize; + + ret = nand_prog_page_begin_op(chip, page, pos, NULL, 0); + if (ret) + return ret; + + for (i = 0; i < steps; i++) { + if (sndcmd) { + if (mtd->writesize <= 512) { + uint32_t fill = 0xFFFFFFFF; + + len = eccsize; + while (len > 0) { + int num = min_t(int, len, 4); + + ret = nand_write_data_op(chip, &fill, + num, false); + if (ret) + return ret; + + len -= num; + } + } else { + pos = eccsize + i * (eccsize + chunk); + ret = nand_change_write_column_op(chip, pos, + NULL, 0, + false); + if (ret) + return ret; + } + } else + sndcmd = 1; + len = min_t(int, length, chunk); + + ret = nand_write_data_op(chip, bufpoi, len, false); + if (ret) + return ret; + + bufpoi += len; + length -= len; + } + if (length > 0) { + ret = nand_write_data_op(chip, bufpoi, length, false); + if (ret) + return ret; + } + + return nand_prog_page_end_op(chip); +} +EXPORT_SYMBOL(nand_write_oob_syndrome); + +/** + * nand_do_read_oob - [INTERN] NAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operations description structure + * + * NAND read out-of-band data from the spare area. + */ +static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + unsigned int max_bitflips = 0; + int page, realpage, chipnr; + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_ecc_stats stats; + int readlen = ops->ooblen; + int len; + uint8_t *buf = ops->oobbuf; + int ret = 0; + + pr_debug("%s: from = 0x%08Lx, len = %i\n", + __func__, (unsigned long long)from, readlen); + + stats = mtd->ecc_stats; + + len = mtd_oobavail(mtd, ops); + + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Shift to get page */ + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; + + while (1) { + if (ops->mode == MTD_OPS_RAW) + ret = chip->ecc.read_oob_raw(mtd, chip, page); + else + ret = chip->ecc.read_oob(mtd, chip, page); + + if (ret < 0) + break; + + len = min(len, readlen); + buf = nand_transfer_oob(mtd, buf, ops, len); + + if (chip->options & NAND_NEED_READRDY) { + /* Apply delay or wait for ready/busy pin */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } + + max_bitflips = max_t(unsigned int, max_bitflips, ret); + + readlen -= len; + if (!readlen) + break; + + /* Increment page address */ + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + chip->select_chip(mtd, -1); + + ops->oobretlen = ops->ooblen - readlen; + + if (ret < 0) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return max_bitflips; +} + +/** + * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + * + * NAND read data and/or out-of-band data. + */ +static int nand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int ret; + + ops->retlen = 0; + + if (ops->mode != MTD_OPS_PLACE_OOB && + ops->mode != MTD_OPS_AUTO_OOB && + ops->mode != MTD_OPS_RAW) + return -ENOTSUPP; + + nand_get_device(mtd, FL_READING); + + if (!ops->datbuf) + ret = nand_do_read_oob(mtd, from, ops); + else + ret = nand_do_read_ops(mtd, from, ops); + + nand_release_device(mtd); + return ret; +} + + +/** + * nand_write_page_raw - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * Not for syndrome calculating ECC controllers, which use a special oob layout. + */ +int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + int ret; + + ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + if (ret) + return ret; + + if (oob_required) { + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; + } + + return nand_prog_page_end_op(chip); +} +EXPORT_SYMBOL(nand_write_page_raw); + +/** + * nand_write_page_raw_syndrome - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * We need a special oob layout and handling even when ECC isn't checked. + */ +static int nand_write_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size, ret; + + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + ret = nand_write_data_op(chip, buf, eccsize, false); + if (ret) + return ret; + + buf += eccsize; + + if (chip->ecc.prepad) { + ret = nand_write_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + + oob += chip->ecc.prepad; + } + + ret = nand_write_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + + oob += eccbytes; + + if (chip->ecc.postpad) { + ret = nand_write_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) { + ret = nand_write_data_op(chip, oob, size, false); + if (ret) + return ret; + } + + return nand_prog_page_end_op(chip); +} +/** + * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + */ +static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int i, eccsize = chip->ecc.size, ret; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->ecc.calc_buf; + const uint8_t *p = buf; + + /* Software ECC calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); +} + +/** + * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + */ +static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int i, eccsize = chip->ecc.size, ret; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->ecc.calc_buf; + const uint8_t *p = buf; + + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + ret = nand_write_data_op(chip, p, eccsize, false); + if (ret) + return ret; + + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (ret) + return ret; + + return nand_prog_page_end_op(chip); +} + + +/** + * nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @offset: column address of subpage within the page + * @data_len: data length + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + */ +static int nand_write_subpage_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint32_t offset, + uint32_t data_len, const uint8_t *buf, + int oob_required, int page) +{ + uint8_t *oob_buf = chip->oob_poi; + uint8_t *ecc_calc = chip->ecc.calc_buf; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int ecc_steps = chip->ecc.steps; + uint32_t start_step = offset / ecc_size; + uint32_t end_step = (offset + data_len - 1) / ecc_size; + int oob_bytes = mtd->oobsize / ecc_steps; + int step, ret; + + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + for (step = 0; step < ecc_steps; step++) { + /* configure controller for WRITE access */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + /* write data (untouched subpages already masked by 0xFF) */ + ret = nand_write_data_op(chip, buf, ecc_size, false); + if (ret) + return ret; + + /* mask ECC of un-touched subpages by padding 0xFF */ + if ((step < start_step) || (step > end_step)) + memset(ecc_calc, 0xff, ecc_bytes); + else + chip->ecc.calculate(mtd, buf, ecc_calc); + + /* mask OOB of un-touched subpages by padding 0xFF */ + /* if oob_required, preserve OOB metadata of written subpage */ + if (!oob_required || (step < start_step) || (step > end_step)) + memset(oob_buf, 0xff, oob_bytes); + + buf += ecc_size; + ecc_calc += ecc_bytes; + oob_buf += oob_bytes; + } + + /* copy calculated ECC for whole page to chip->buffer->oob */ + /* this include masked-value(0xFF) for unwritten subpages */ + ecc_calc = chip->ecc.calc_buf; + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + /* write OOB buffer to NAND device */ + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (ret) + return ret; + + return nand_prog_page_end_op(chip); +} + + +/** + * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling. + */ +static int nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + int ret; + + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + ret = nand_write_data_op(chip, p, eccsize, false); + if (ret) + return ret; + + if (chip->ecc.prepad) { + ret = nand_write_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + + oob += chip->ecc.prepad; + } + + chip->ecc.calculate(mtd, p, oob); + + ret = nand_write_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + + oob += eccbytes; + + if (chip->ecc.postpad) { + ret = nand_write_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) { + ret = nand_write_data_op(chip, oob, i, false); + if (ret) + return ret; + } + + return nand_prog_page_end_op(chip); +} + +/** + * nand_write_page - write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @offset: address offset within the page + * @data_len: length of actual data to be written + * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * @raw: use _raw version of write_page + */ +static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int raw) +{ + int status, subpage; + + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && + chip->ecc.write_subpage) + subpage = offset || (data_len < mtd->writesize); + else + subpage = 0; + + if (unlikely(raw)) + status = chip->ecc.write_page_raw(mtd, chip, buf, + oob_required, page); + else if (subpage) + status = chip->ecc.write_subpage(mtd, chip, offset, data_len, + buf, oob_required, page); + else + status = chip->ecc.write_page(mtd, chip, buf, oob_required, + page); + + if (status < 0) + return status; + + return 0; +} + +/** + * nand_fill_oob - [INTERN] Transfer client buffer to oob + * @mtd: MTD device structure + * @oob: oob data buffer + * @len: oob data write length + * @ops: oob ops structure + */ +static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret; + + /* + * Initialise to all 0xFF, to avoid the possibility of left over OOB + * data from a previous OOB read. + */ + memset(chip->oob_poi, 0xff, mtd->oobsize); + + switch (ops->mode) { + + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OPS_AUTO_OOB: + ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi, + ops->ooboffs, len); + BUG_ON(ret); + return oob + len; + + default: + BUG(); + } + return NULL; +} + +#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0) + +/** + * nand_do_write_ops - [INTERN] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operations description structure + * + * NAND write with ECC. + */ +static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, realpage, page, column; + struct nand_chip *chip = mtd_to_nand(mtd); + uint32_t writelen = ops->len; + + uint32_t oobwritelen = ops->ooblen; + uint32_t oobmaxlen = mtd_oobavail(mtd, ops); + + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; + int ret; + int oob_required = oob ? 1 : 0; + + ops->retlen = 0; + if (!writelen) + return 0; + + /* Reject writes, which are not page aligned */ + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { + pr_notice("%s: attempt to write non page aligned data\n", + __func__); + return -EINVAL; + } + + column = to & (mtd->writesize - 1); + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + ret = -EIO; + goto err_out; + } + + realpage = (int)(to >> chip->page_shift); + page = realpage & chip->pagemask; + + /* Invalidate the page cache, when we write to the cached page */ + if (to <= ((loff_t)chip->pagebuf << chip->page_shift) && + ((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len)) + chip->pagebuf = -1; + + /* Don't allow multipage oob writes with offset */ + if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) { + ret = -EINVAL; + goto err_out; + } + + while (1) { + int bytes = mtd->writesize; + uint8_t *wbuf = buf; + int use_bufpoi; + int part_pagewr = (column || writelen < mtd->writesize); + + if (part_pagewr) + use_bufpoi = 1; + else if (chip->options & NAND_USE_BOUNCE_BUFFER) + use_bufpoi = !virt_addr_valid(buf) || + !IS_ALIGNED((unsigned long)buf, + chip->buf_align); + else + use_bufpoi = 0; + + /* Partial page write?, or need to use bounce buffer */ + if (use_bufpoi) { + pr_debug("%s: using write bounce buffer for buf@%p\n", + __func__, buf); + if (part_pagewr) + bytes = min_t(int, bytes - column, writelen); + chip->pagebuf = -1; + memset(chip->data_buf, 0xff, mtd->writesize); + memcpy(&chip->data_buf[column], buf, bytes); + wbuf = chip->data_buf; + } + + if (unlikely(oob)) { + size_t len = min(oobwritelen, oobmaxlen); + oob = nand_fill_oob(mtd, oob, len, ops); + oobwritelen -= len; + } else { + /* We still need to erase leftover OOB data */ + memset(chip->oob_poi, 0xff, mtd->oobsize); + } + + ret = nand_write_page(mtd, chip, column, bytes, wbuf, + oob_required, page, + (ops->mode == MTD_OPS_RAW)); + if (ret) + break; + + writelen -= bytes; + if (!writelen) + break; + + column = 0; + buf += bytes; + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + + ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; + +err_out: + chip->select_chip(mtd, -1); + return ret; +} + +/** + * panic_nand_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. Used when performing writes in interrupt context, this + * may for example be called by mtdoops when writing an oops while in panic. + */ +static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int chipnr = (int)(to >> chip->chip_shift); + struct mtd_oob_ops ops; + int ret; + + /* Grab the device */ + panic_nand_get_device(chip, mtd, FL_WRITING); + + chip->select_chip(mtd, chipnr); + + /* Wait for the device to get ready */ + panic_nand_wait(mtd, chip, 400); + + memset(&ops, 0, sizeof(ops)); + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.mode = MTD_OPS_PLACE_OOB; + + ret = nand_do_write_ops(mtd, to, &ops); + + *retlen = ops.retlen; + return ret; +} + +/** + * nand_do_write_oob - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + * + * NAND write out-of-band. + */ +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, page, status, len; + struct nand_chip *chip = mtd_to_nand(mtd); + + pr_debug("%s: to = 0x%08x, len = %i\n", + __func__, (unsigned int)to, (int)ops->ooblen); + + len = mtd_oobavail(mtd, ops); + + /* Do not allow write past end of page */ + if ((ops->ooboffs + ops->ooblen) > len) { + pr_debug("%s: attempt to write past end of page\n", + __func__); + return -EINVAL; + } + + chipnr = (int)(to >> chip->chip_shift); + + /* + * Reset the chip. Some chips (like the Toshiba TC5832DC found in one + * of my DiskOnChip 2000 test units) will clear the whole data page too + * if we don't do this. I have no clue why, but I seem to have 'fixed' + * it in the doc2000 driver in August 1999. dwmw2. + */ + nand_reset(chip, chipnr); + + chip->select_chip(mtd, chipnr); + + /* Shift to get page */ + page = (int)(to >> chip->page_shift); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + chip->select_chip(mtd, -1); + return -EROFS; + } + + /* Invalidate the page cache, if we write to the cached page */ + if (page == chip->pagebuf) + chip->pagebuf = -1; + + nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); + + if (ops->mode == MTD_OPS_RAW) + status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); + else + status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + + chip->select_chip(mtd, -1); + + if (status) + return status; + + ops->oobretlen = ops->ooblen; + + return 0; +} + +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + */ +static int nand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int ret = -ENOTSUPP; + + ops->retlen = 0; + + nand_get_device(mtd, FL_WRITING); + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(mtd, to, ops); + else + ret = nand_do_write_ops(mtd, to, ops); + +out: + nand_release_device(mtd); + return ret; +} + +/** + * single_erase - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * Standard erase command for NAND chips. Returns NAND status. + */ +static int single_erase(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + unsigned int eraseblock; + + /* Send commands to erase a block */ + eraseblock = page >> (chip->phys_erase_shift - chip->page_shift); + + return nand_erase_op(chip, eraseblock); +} + +/** + * nand_erase - [MTD Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks. + */ +static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + return nand_erase_nand(mtd, instr, 0); +} + +/** + * nand_erase_nand - [INTERN] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area + * + * Erase one ore more blocks. + */ +int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + int allowbbt) +{ + int page, status, pages_per_block, ret, chipnr; + struct nand_chip *chip = mtd_to_nand(mtd); + loff_t len; + + pr_debug("%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)instr->addr, + (unsigned long long)instr->len); + + if (check_offs_len(mtd, instr->addr, instr->len)) + return -EINVAL; + + /* Grab the lock and see if the device is available */ + nand_get_device(mtd, FL_ERASING); + + /* Shift to get first page */ + page = (int)(instr->addr >> chip->page_shift); + chipnr = (int)(instr->addr >> chip->chip_shift); + + /* Calculate pages in each block */ + pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); + + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + pr_debug("%s: device is write protected!\n", + __func__); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* Loop through the pages */ + len = instr->len; + + instr->state = MTD_ERASING; + + while (len) { + /* Check if we have a bad block, we do not erase bad blocks! */ + if (nand_block_checkbad(mtd, ((loff_t) page) << + chip->page_shift, allowbbt)) { + pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", + __func__, page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* + * Invalidate the page cache, if we erase the block which + * contains the current cached page. + */ + if (page <= chip->pagebuf && chip->pagebuf < + (page + pages_per_block)) + chip->pagebuf = -1; + + status = chip->erase(mtd, page & chip->pagemask); + + /* See if block erase succeeded */ + if (status) { + pr_debug("%s: failed erase, page 0x%08x\n", + __func__, page); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = + ((loff_t)page << chip->page_shift); + goto erase_exit; + } + + /* Increment page address and decrement length */ + len -= (1ULL << chip->phys_erase_shift); + page += pages_per_block; + + /* Check, if we cross a chip boundary */ + if (len && !(page & chip->pagemask)) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + instr->state = MTD_ERASE_DONE; + +erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + + /* Deselect and wake up anyone waiting on the device */ + chip->select_chip(mtd, -1); + nand_release_device(mtd); + + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); + + /* Return more or less happy */ + return ret; +} + +/** + * nand_sync - [MTD Interface] sync + * @mtd: MTD device structure + * + * Sync is actually a wait for chip ready function. + */ +static void nand_sync(struct mtd_info *mtd) +{ + pr_debug("%s: called\n", __func__); + + /* Grab the lock and see if the device is available */ + nand_get_device(mtd, FL_SYNCING); + /* Release it and go back */ + nand_release_device(mtd); +} + +/** + * nand_block_isbad - [MTD Interface] Check if block at offset is bad + * @mtd: MTD device structure + * @offs: offset relative to mtd start + */ +static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int chipnr = (int)(offs >> chip->chip_shift); + int ret; + + /* Select the NAND device */ + nand_get_device(mtd, FL_READING); + chip->select_chip(mtd, chipnr); + + ret = nand_block_checkbad(mtd, offs, 0); + + chip->select_chip(mtd, -1); + nand_release_device(mtd); + + return ret; +} + +/** + * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + int ret; + + ret = nand_block_isbad(mtd, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + return nand_block_markbad_lowlevel(mtd, ofs); +} + +/** + * nand_max_bad_blocks - [MTD Interface] Max number of bad blocks for an mtd + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + * @len: length of mtd + */ +static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u32 part_start_block; + u32 part_end_block; + u32 part_start_die; + u32 part_end_die; + + /* + * max_bb_per_die and blocks_per_die used to determine + * the maximum bad block count. + */ + if (!chip->max_bb_per_die || !chip->blocks_per_die) + return -ENOTSUPP; + + /* Get the start and end of the partition in erase blocks. */ + part_start_block = mtd_div_by_eb(ofs, mtd); + part_end_block = mtd_div_by_eb(len, mtd) + part_start_block - 1; + + /* Get the start and end LUNs of the partition. */ + part_start_die = part_start_block / chip->blocks_per_die; + part_end_die = part_end_block / chip->blocks_per_die; + + /* + * Look up the bad blocks per unit and multiply by the number of units + * that the partition spans. + */ + return chip->max_bb_per_die * (part_end_die - part_start_die + 1); +} + +/** + * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand + * @mtd: MTD device structure + * @chip: nand chip info structure + * @addr: feature address. + * @subfeature_param: the subfeature parameters, a four bytes array. + */ +static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, uint8_t *subfeature_param) +{ + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -EINVAL; + + return nand_set_features_op(chip, addr, subfeature_param); +} + +/** + * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand + * @mtd: MTD device structure + * @chip: nand chip info structure + * @addr: feature address. + * @subfeature_param: the subfeature parameters, a four bytes array. + */ +static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, uint8_t *subfeature_param) +{ + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -EINVAL; + + return nand_get_features_op(chip, addr, subfeature_param); +} + +/** + * nand_onfi_get_set_features_notsupp - set/get features stub returning + * -ENOTSUPP + * @mtd: MTD device structure + * @chip: nand chip info structure + * @addr: feature address. + * @subfeature_param: the subfeature parameters, a four bytes array. + * + * Should be used by NAND controller drivers that do not support the SET/GET + * FEATURES operations. + */ +int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd, + struct nand_chip *chip, int addr, + u8 *subfeature_param) +{ + return -ENOTSUPP; +} +EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp); + +/** + * nand_suspend - [MTD Interface] Suspend the NAND flash + * @mtd: MTD device structure + */ +static int nand_suspend(struct mtd_info *mtd) +{ + return nand_get_device(mtd, FL_PM_SUSPENDED); +} + +/** + * nand_resume - [MTD Interface] Resume the NAND flash + * @mtd: MTD device structure + */ +static void nand_resume(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (chip->state == FL_PM_SUSPENDED) + nand_release_device(mtd); + else + pr_err("%s called for a chip which is not in suspended state\n", + __func__); +} + +/** + * nand_shutdown - [MTD Interface] Finish the current NAND operation and + * prevent further operations + * @mtd: MTD device structure + */ +static void nand_shutdown(struct mtd_info *mtd) +{ + nand_get_device(mtd, FL_PM_SUSPENDED); +} + +/* Set default functions */ +static void nand_set_defaults(struct nand_chip *chip) +{ + unsigned int busw = chip->options & NAND_BUSWIDTH_16; + + /* check for proper chip_delay setup, set 20us if not */ + if (!chip->chip_delay) + chip->chip_delay = 20; + + /* check, if a user supplied command function given */ + if (!chip->cmdfunc && !chip->exec_op) + chip->cmdfunc = nand_command; + + /* check, if a user supplied wait function given */ + if (chip->waitfunc == NULL) + chip->waitfunc = nand_wait; + + if (!chip->select_chip) + chip->select_chip = nand_select_chip; + + /* set for ONFI nand */ + if (!chip->onfi_set_features) + chip->onfi_set_features = nand_onfi_set_features; + if (!chip->onfi_get_features) + chip->onfi_get_features = nand_onfi_get_features; + + /* If called twice, pointers that depend on busw may need to be reset */ + if (!chip->read_byte || chip->read_byte == nand_read_byte) + chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; + if (!chip->read_word) + chip->read_word = nand_read_word; + if (!chip->block_bad) + chip->block_bad = nand_block_bad; + if (!chip->block_markbad) + chip->block_markbad = nand_default_block_markbad; + if (!chip->write_buf || chip->write_buf == nand_write_buf) + chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; + if (!chip->write_byte || chip->write_byte == nand_write_byte) + chip->write_byte = busw ? nand_write_byte16 : nand_write_byte; + if (!chip->read_buf || chip->read_buf == nand_read_buf) + chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; + if (!chip->scan_bbt) + chip->scan_bbt = nand_default_bbt; + + if (!chip->controller) { + chip->controller = &chip->hwcontrol; + nand_hw_control_init(chip->controller); + } + + if (!chip->buf_align) + chip->buf_align = 1; +} + +/* Sanitize ONFI strings so we can safely print them */ +static void sanitize_string(uint8_t *s, size_t len) +{ + ssize_t i; + + /* Null terminate */ + s[len - 1] = 0; + + /* Remove non printable chars */ + for (i = 0; i < len - 1; i++) { + if (s[i] < ' ' || s[i] > 127) + s[i] = '?'; + } + + /* Remove trailing spaces */ + strim(s); +} + +static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++ << 8; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); + } + + return crc; +} + +/* Parse the Extended Parameter Page. */ +static int nand_flash_detect_ext_param_page(struct nand_chip *chip, + struct nand_onfi_params *p) +{ + struct onfi_ext_param_page *ep; + struct onfi_ext_section *s; + struct onfi_ext_ecc_info *ecc; + uint8_t *cursor; + int ret; + int len; + int i; + + len = le16_to_cpu(p->ext_param_page_length) * 16; + ep = kmalloc(len, GFP_KERNEL); + if (!ep) + return -ENOMEM; + + /* Send our own NAND_CMD_PARAM. */ + ret = nand_read_param_page_op(chip, 0, NULL, 0); + if (ret) + goto ext_out; + + /* Use the Change Read Column command to skip the ONFI param pages. */ + ret = nand_change_read_column_op(chip, + sizeof(*p) * p->num_of_param_pages, + ep, len, true); + if (ret) + goto ext_out; + + ret = -EINVAL; + if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) + != le16_to_cpu(ep->crc))) { + pr_debug("fail in the CRC.\n"); + goto ext_out; + } + + /* + * Check the signature. + * Do not strictly follow the ONFI spec, maybe changed in future. + */ + if (strncmp(ep->sig, "EPPS", 4)) { + pr_debug("The signature is invalid.\n"); + goto ext_out; + } + + /* find the ECC section. */ + cursor = (uint8_t *)(ep + 1); + for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { + s = ep->sections + i; + if (s->type == ONFI_SECTION_TYPE_2) + break; + cursor += s->length * 16; + } + if (i == ONFI_EXT_SECTION_MAX) { + pr_debug("We can not find the ECC section.\n"); + goto ext_out; + } + + /* get the info we want. */ + ecc = (struct onfi_ext_ecc_info *)cursor; + + if (!ecc->codeword_size) { + pr_debug("Invalid codeword size\n"); + goto ext_out; + } + + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + ret = 0; + +ext_out: + kfree(ep); + return ret; +} + +/* + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. + */ +static int nand_flash_detect_onfi(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_onfi_params *p = &chip->onfi_params; + char id[4]; + int i, ret, val; + + /* Try ONFI for unknown chip or LP */ + ret = nand_readid_op(chip, 0x20, id, sizeof(id)); + if (ret || strncmp(id, "ONFI", 4)) + return 0; + + ret = nand_read_param_page_op(chip, 0, NULL, 0); + if (ret) + return 0; + + for (i = 0; i < 3; i++) { + ret = nand_read_data_op(chip, p, sizeof(*p), true); + if (ret) + return 0; + + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == + le16_to_cpu(p->crc)) { + break; + } + } + + if (i == 3) { + pr_err("Could not find valid ONFI parameter page; aborting\n"); + return 0; + } + + /* Check version */ + val = le16_to_cpu(p->revision); + if (val & (1 << 5)) + chip->onfi_version = 23; + else if (val & (1 << 4)) + chip->onfi_version = 22; + else if (val & (1 << 3)) + chip->onfi_version = 21; + else if (val & (1 << 2)) + chip->onfi_version = 20; + else if (val & (1 << 1)) + chip->onfi_version = 10; + + if (!chip->onfi_version) { + pr_info("unsupported ONFI version: %d\n", val); + return 0; + } + + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); + if (!mtd->name) + mtd->name = p->model; + + mtd->writesize = le32_to_cpu(p->byte_per_page); + + /* + * pages_per_block and blocks_per_lun may not be a power-of-2 size + * (don't ask me who thought of this...). MTD assumes that these + * dimensions will be power-of-2, so just truncate the remaining area. + */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + + /* See erasesize comment */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; + chip->bits_per_cell = p->bits_per_cell; + + chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun); + chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun); + + if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) + chip->options |= NAND_BUSWIDTH_16; + + if (p->ecc_bits != 0xff) { + chip->ecc_strength_ds = p->ecc_bits; + chip->ecc_step_ds = 512; + } else if (chip->onfi_version >= 21 && + (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { + + /* + * The nand_flash_detect_ext_param_page() uses the + * Change Read Column command which maybe not supported + * by the chip->cmdfunc. So try to update the chip->cmdfunc + * now. We do not replace user supplied command function. + */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + /* The Extended Parameter Page is supported since ONFI 2.1. */ + if (nand_flash_detect_ext_param_page(chip, p)) + pr_warn("Failed to detect ONFI extended param page\n"); + } else { + pr_warn("Could not retrieve ONFI ECC requirements\n"); + } + + return 1; +} + +/* + * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise. + */ +static int nand_flash_detect_jedec(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_jedec_params *p = &chip->jedec_params; + struct jedec_ecc_info *ecc; + char id[5]; + int i, val, ret; + + /* Try JEDEC for unknown chip or LP */ + ret = nand_readid_op(chip, 0x40, id, sizeof(id)); + if (ret || strncmp(id, "JEDEC", sizeof(id))) + return 0; + + ret = nand_read_param_page_op(chip, 0x40, NULL, 0); + if (ret) + return 0; + + for (i = 0; i < 3; i++) { + ret = nand_read_data_op(chip, p, sizeof(*p), true); + if (ret) + return 0; + + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == + le16_to_cpu(p->crc)) + break; + } + + if (i == 3) { + pr_err("Could not find valid JEDEC parameter page; aborting\n"); + return 0; + } + + /* Check version */ + val = le16_to_cpu(p->revision); + if (val & (1 << 2)) + chip->jedec_version = 10; + else if (val & (1 << 1)) + chip->jedec_version = 1; /* vendor specific version */ + + if (!chip->jedec_version) { + pr_info("unsupported JEDEC version: %d\n", val); + return 0; + } + + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); + if (!mtd->name) + mtd->name = p->model; + + mtd->writesize = le32_to_cpu(p->byte_per_page); + + /* Please reference to the comment for nand_flash_detect_onfi. */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + + /* Please reference to the comment for nand_flash_detect_onfi. */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; + chip->bits_per_cell = p->bits_per_cell; + + if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS) + chip->options |= NAND_BUSWIDTH_16; + + /* ECC info */ + ecc = &p->ecc_info[0]; + + if (ecc->codeword_size >= 9) { + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + } else { + pr_warn("Invalid codeword size\n"); + } + + return 1; +} + +/* + * nand_id_has_period - Check if an ID string has a given wraparound period + * @id_data: the ID string + * @arrlen: the length of the @id_data array + * @period: the period of repitition + * + * Check if an ID string is repeated within a given sequence of bytes at + * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a + * period of 3). This is a helper function for nand_id_len(). Returns non-zero + * if the repetition has a period of @period; otherwise, returns zero. + */ +static int nand_id_has_period(u8 *id_data, int arrlen, int period) +{ + int i, j; + for (i = 0; i < period; i++) + for (j = i + period; j < arrlen; j += period) + if (id_data[i] != id_data[j]) + return 0; + return 1; +} + +/* + * nand_id_len - Get the length of an ID string returned by CMD_READID + * @id_data: the ID string + * @arrlen: the length of the @id_data array + + * Returns the length of the ID string, according to known wraparound/trailing + * zero patterns. If no pattern exists, returns the length of the array. + */ +static int nand_id_len(u8 *id_data, int arrlen) +{ + int last_nonzero, period; + + /* Find last non-zero byte */ + for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--) + if (id_data[last_nonzero]) + break; + + /* All zeros */ + if (last_nonzero < 0) + return 0; + + /* Calculate wraparound period */ + for (period = 1; period < arrlen; period++) + if (nand_id_has_period(id_data, arrlen, period)) + break; + + /* There's a repeated pattern */ + if (period < arrlen) + return period; + + /* There are trailing zeros */ + if (last_nonzero < arrlen - 1) + return last_nonzero + 1; + + /* No pattern detected */ + return arrlen; +} + +/* Extract the bits of per cell from the 3rd byte of the extended ID */ +static int nand_get_bits_per_cell(u8 cellinfo) +{ + int bits; + + bits = cellinfo & NAND_CI_CELLTYPE_MSK; + bits >>= NAND_CI_CELLTYPE_SHIFT; + return bits + 1; +} + +/* + * Many new NAND share similar device ID codes, which represent the size of the + * chip. The rest of the parameters must be decoded according to generic or + * manufacturer-specific "extended ID" decoding patterns. + */ +void nand_decode_ext_id(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int extid; + u8 *id_data = chip->id.data; + /* The 3rd id byte holds MLC / multichip data */ + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); + /* The 4th id byte is the important one */ + extid = id_data[3]; + + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + if (extid & 0x1) + chip->options |= NAND_BUSWIDTH_16; +} +EXPORT_SYMBOL_GPL(nand_decode_ext_id); + +/* + * Old devices have chip data hardcoded in the device ID table. nand_decode_id + * decodes a matching ID table entry and assigns the MTD size parameters for + * the chip. + */ +static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + mtd->erasesize = type->erasesize; + mtd->writesize = type->pagesize; + mtd->oobsize = mtd->writesize / 32; + + /* All legacy ID NAND are small-page, SLC */ + chip->bits_per_cell = 1; +} + +/* + * Set the bad block marker/indicator (BBM/BBI) patterns according to some + * heuristic patterns using various detected parameters (e.g., manufacturer, + * page size, cell-type information). + */ +static void nand_decode_bbm_options(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + /* Set the bad block position */ + if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16)) + chip->badblockpos = NAND_LARGE_BADBLOCK_POS; + else + chip->badblockpos = NAND_SMALL_BADBLOCK_POS; +} + +static inline bool is_full_id_nand(struct nand_flash_dev *type) +{ + return type->id_len; +} + +static bool find_full_id_nand(struct nand_chip *chip, + struct nand_flash_dev *type) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u8 *id_data = chip->id.data; + + if (!strncmp(type->id, id_data, type->id_len)) { + mtd->writesize = type->pagesize; + mtd->erasesize = type->erasesize; + mtd->oobsize = type->oobsize; + + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); + chip->chipsize = (uint64_t)type->chipsize << 20; + chip->options |= type->options; + chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); + chip->ecc_step_ds = NAND_ECC_STEP(type); + chip->onfi_timing_mode_default = + type->onfi_timing_mode_default; + + if (!mtd->name) + mtd->name = type->name; + + return true; + } + return false; +} + +/* + * Manufacturer detection. Only used when the NAND is not ONFI or JEDEC + * compliant and does not have a full-id or legacy-id entry in the nand_ids + * table. + */ +static void nand_manufacturer_detect(struct nand_chip *chip) +{ + /* + * Try manufacturer detection if available and use + * nand_decode_ext_id() otherwise. + */ + if (chip->manufacturer.desc && chip->manufacturer.desc->ops && + chip->manufacturer.desc->ops->detect) { + /* The 3rd id byte holds MLC / multichip data */ + chip->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]); + chip->manufacturer.desc->ops->detect(chip); + } else { + nand_decode_ext_id(chip); + } +} + +/* + * Manufacturer initialization. This function is called for all NANDs including + * ONFI and JEDEC compliant ones. + * Manufacturer drivers should put all their specific initialization code in + * their ->init() hook. + */ +static int nand_manufacturer_init(struct nand_chip *chip) +{ + if (!chip->manufacturer.desc || !chip->manufacturer.desc->ops || + !chip->manufacturer.desc->ops->init) + return 0; + + return chip->manufacturer.desc->ops->init(chip); +} + +/* + * Manufacturer cleanup. This function is called for all NANDs including + * ONFI and JEDEC compliant ones. + * Manufacturer drivers should put all their specific cleanup code in their + * ->cleanup() hook. + */ +static void nand_manufacturer_cleanup(struct nand_chip *chip) +{ + /* Release manufacturer private data */ + if (chip->manufacturer.desc && chip->manufacturer.desc->ops && + chip->manufacturer.desc->ops->cleanup) + chip->manufacturer.desc->ops->cleanup(chip); +} + +/* + * Get the flash and manufacturer id and lookup if the type is supported. + */ +static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) +{ + const struct nand_manufacturer *manufacturer; + struct mtd_info *mtd = nand_to_mtd(chip); + int busw, ret; + u8 *id_data = chip->id.data; + u8 maf_id, dev_id; + + /* + * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) + * after power-up. + */ + ret = nand_reset(chip, 0); + if (ret) + return ret; + + /* Select the device */ + chip->select_chip(mtd, 0); + + /* Send the command for reading device ID */ + ret = nand_readid_op(chip, 0, id_data, 2); + if (ret) + return ret; + + /* Read manufacturer and device IDs */ + maf_id = id_data[0]; + dev_id = id_data[1]; + + /* + * Try again to make sure, as some systems the bus-hold or other + * interface concerns can cause random data which looks like a + * possibly credible NAND flash to appear. If the two results do + * not match, ignore the device completely. + */ + + /* Read entire ID string */ + ret = nand_readid_op(chip, 0, id_data, sizeof(chip->id.data)); + if (ret) + return ret; + + if (id_data[0] != maf_id || id_data[1] != dev_id) { + pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", + maf_id, dev_id, id_data[0], id_data[1]); + return -ENODEV; + } + + chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data)); + + /* Try to identify manufacturer */ + manufacturer = nand_get_manufacturer(maf_id); + chip->manufacturer.desc = manufacturer; + + if (!type) + type = nand_flash_ids; + + /* + * Save the NAND_BUSWIDTH_16 flag before letting auto-detection logic + * override it. + * This is required to make sure initial NAND bus width set by the + * NAND controller driver is coherent with the real NAND bus width + * (extracted by auto-detection code). + */ + busw = chip->options & NAND_BUSWIDTH_16; + + /* + * The flag is only set (never cleared), reset it to its default value + * before starting auto-detection. + */ + chip->options &= ~NAND_BUSWIDTH_16; + + for (; type->name != NULL; type++) { + if (is_full_id_nand(type)) { + if (find_full_id_nand(chip, type)) + goto ident_done; + } else if (dev_id == type->dev_id) { + break; + } + } + + chip->onfi_version = 0; + if (!type->name || !type->pagesize) { + /* Check if the chip is ONFI compliant */ + if (nand_flash_detect_onfi(chip)) + goto ident_done; + + /* Check if the chip is JEDEC compliant */ + if (nand_flash_detect_jedec(chip)) + goto ident_done; + } + + if (!type->name) + return -ENODEV; + + if (!mtd->name) + mtd->name = type->name; + + chip->chipsize = (uint64_t)type->chipsize << 20; + + if (!type->pagesize) + nand_manufacturer_detect(chip); + else + nand_decode_id(chip, type); + + /* Get chip options */ + chip->options |= type->options; + +ident_done: + + if (chip->options & NAND_BUSWIDTH_AUTO) { + WARN_ON(busw & NAND_BUSWIDTH_16); + nand_set_defaults(chip); + } else if (busw != (chip->options & NAND_BUSWIDTH_16)) { + /* + * Check, if buswidth is correct. Hardware drivers should set + * chip correct! + */ + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", + maf_id, dev_id); + pr_info("%s %s\n", nand_manufacturer_name(manufacturer), + mtd->name); + pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8, + (chip->options & NAND_BUSWIDTH_16) ? 16 : 8); + return -EINVAL; + } + + nand_decode_bbm_options(chip); + + /* Calculate the address shift from the page size */ + chip->page_shift = ffs(mtd->writesize) - 1; + /* Convert chipsize to number of pages per chip -1 */ + chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; + + chip->bbt_erase_shift = chip->phys_erase_shift = + ffs(mtd->erasesize) - 1; + if (chip->chipsize & 0xffffffff) + chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; + else { + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); + chip->chip_shift += 32 - 1; + } + + if (chip->chip_shift - chip->page_shift > 16) + chip->options |= NAND_ROW_ADDR_3; + + chip->badblockbits = 8; + chip->erase = single_erase; + + /* Do not replace user supplied command function! */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", + maf_id, dev_id); + + if (chip->onfi_version) + pr_info("%s %s\n", nand_manufacturer_name(manufacturer), + chip->onfi_params.model); + else if (chip->jedec_version) + pr_info("%s %s\n", nand_manufacturer_name(manufacturer), + chip->jedec_params.model); + else + pr_info("%s %s\n", nand_manufacturer_name(manufacturer), + type->name); + + pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", + (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", + mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); + return 0; +} + +static const char * const nand_ecc_modes[] = { + [NAND_ECC_NONE] = "none", + [NAND_ECC_SOFT] = "soft", + [NAND_ECC_HW] = "hw", + [NAND_ECC_HW_SYNDROME] = "hw_syndrome", + [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first", + [NAND_ECC_ON_DIE] = "on-die", +}; + +static int of_get_nand_ecc_mode(struct device_node *np) +{ + const char *pm; + int err, i; + + err = of_property_read_string(np, "nand-ecc-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++) + if (!strcasecmp(pm, nand_ecc_modes[i])) + return i; + + /* + * For backward compatibility we support few obsoleted values that don't + * have their mappings into nand_ecc_modes_t anymore (they were merged + * with other enums). + */ + if (!strcasecmp(pm, "soft_bch")) + return NAND_ECC_SOFT; + + return -ENODEV; +} + +static const char * const nand_ecc_algos[] = { + [NAND_ECC_HAMMING] = "hamming", + [NAND_ECC_BCH] = "bch", +}; + +static int of_get_nand_ecc_algo(struct device_node *np) +{ + const char *pm; + int err, i; + + err = of_property_read_string(np, "nand-ecc-algo", &pm); + if (!err) { + for (i = NAND_ECC_HAMMING; i < ARRAY_SIZE(nand_ecc_algos); i++) + if (!strcasecmp(pm, nand_ecc_algos[i])) + return i; + return -ENODEV; + } + + /* + * For backward compatibility we also read "nand-ecc-mode" checking + * for some obsoleted values that were specifying ECC algorithm. + */ + err = of_property_read_string(np, "nand-ecc-mode", &pm); + if (err < 0) + return err; + + if (!strcasecmp(pm, "soft")) + return NAND_ECC_HAMMING; + else if (!strcasecmp(pm, "soft_bch")) + return NAND_ECC_BCH; + + return -ENODEV; +} + +static int of_get_nand_ecc_step_size(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-step-size", &val); + return ret ? ret : val; +} + +static int of_get_nand_ecc_strength(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-strength", &val); + return ret ? ret : val; +} + +static int of_get_nand_bus_width(struct device_node *np) +{ + u32 val; + + if (of_property_read_u32(np, "nand-bus-width", &val)) + return 8; + + switch (val) { + case 8: + case 16: + return val; + default: + return -EIO; + } +} + +static bool of_get_nand_on_flash_bbt(struct device_node *np) +{ + return of_property_read_bool(np, "nand-on-flash-bbt"); +} + +static int nand_dt_init(struct nand_chip *chip) +{ + struct device_node *dn = nand_get_flash_node(chip); + int ecc_mode, ecc_algo, ecc_strength, ecc_step; + + if (!dn) + return 0; + + if (of_get_nand_bus_width(dn) == 16) + chip->options |= NAND_BUSWIDTH_16; + + if (of_get_nand_on_flash_bbt(dn)) + chip->bbt_options |= NAND_BBT_USE_FLASH; + + ecc_mode = of_get_nand_ecc_mode(dn); + ecc_algo = of_get_nand_ecc_algo(dn); + ecc_strength = of_get_nand_ecc_strength(dn); + ecc_step = of_get_nand_ecc_step_size(dn); + + if (ecc_mode >= 0) + chip->ecc.mode = ecc_mode; + + if (ecc_algo >= 0) + chip->ecc.algo = ecc_algo; + + if (ecc_strength >= 0) + chip->ecc.strength = ecc_strength; + + if (ecc_step > 0) + chip->ecc.size = ecc_step; + + if (of_property_read_bool(dn, "nand-ecc-maximize")) + chip->ecc.options |= NAND_ECC_MAXIMIZE; + + return 0; +} + +/** + * nand_scan_ident - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: number of chips to scan for + * @table: alternative NAND ID table + * + * This is the first phase of the normal nand_scan() function. It reads the + * flash ID and sets up MTD fields accordingly. + * + */ +int nand_scan_ident(struct mtd_info *mtd, int maxchips, + struct nand_flash_dev *table) +{ + int i, nand_maf_id, nand_dev_id; + struct nand_chip *chip = mtd_to_nand(mtd); + int ret; + + /* Enforce the right timings for reset/detection */ + onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); + + ret = nand_dt_init(chip); + if (ret) + return ret; + + if (!mtd->name && mtd->dev.parent) + mtd->name = dev_name(mtd->dev.parent); + + /* + * ->cmdfunc() is legacy and will only be used if ->exec_op() is not + * populated. + */ + if (!chip->exec_op) { + /* + * Default functions assigned for ->cmdfunc() and + * ->select_chip() both expect ->cmd_ctrl() to be populated. + */ + if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) { + pr_err("->cmd_ctrl() should be provided\n"); + return -EINVAL; + } + } + + /* Set the default functions */ + nand_set_defaults(chip); + + /* Read the flash type */ + ret = nand_detect(chip, table); + if (ret) { + if (!(chip->options & NAND_SCAN_SILENT_NODEV)) + pr_warn("No NAND device found\n"); + chip->select_chip(mtd, -1); + return ret; + } + + nand_maf_id = chip->id.data[0]; + nand_dev_id = chip->id.data[1]; + + chip->select_chip(mtd, -1); + + /* Check for a chip array */ + for (i = 1; i < maxchips; i++) { + u8 id[2]; + + /* See comment in nand_get_flash_type for reset */ + nand_reset(chip, i); + + chip->select_chip(mtd, i); + /* Send the command for reading device ID */ + nand_readid_op(chip, 0, id, sizeof(id)); + /* Read manufacturer and device IDs */ + if (nand_maf_id != id[0] || nand_dev_id != id[1]) { + chip->select_chip(mtd, -1); + break; + } + chip->select_chip(mtd, -1); + } + if (i > 1) + pr_info("%d chips detected\n", i); + + /* Store the number of chips and calc total size for mtd */ + chip->numchips = i; + mtd->size = i * chip->chipsize; + + return 0; +} +EXPORT_SYMBOL(nand_scan_ident); + +static int nand_set_ecc_soft_ops(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (WARN_ON(ecc->mode != NAND_ECC_SOFT)) + return -EINVAL; + + switch (ecc->algo) { + case NAND_ECC_HAMMING: + ecc->calculate = nand_calculate_ecc; + ecc->correct = nand_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + if (!ecc->size) + ecc->size = 256; + ecc->bytes = 3; + ecc->strength = 1; + return 0; + case NAND_ECC_BCH: + if (!mtd_nand_has_bch()) { + WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + return -EINVAL; + } + ecc->calculate = nand_bch_calculate_ecc; + ecc->correct = nand_bch_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + + /* + * Board driver should supply ecc.size and ecc.strength + * values to select how many bits are correctable. + * Otherwise, default to 4 bits for large page devices. + */ + if (!ecc->size && (mtd->oobsize >= 64)) { + ecc->size = 512; + ecc->strength = 4; + } + + /* + * if no ecc placement scheme was provided pickup the default + * large page one. + */ + if (!mtd->ooblayout) { + /* handle large page devices only */ + if (mtd->oobsize < 64) { + WARN(1, "OOB layout is required when using software BCH on small pages\n"); + return -EINVAL; + } + + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + + } + + /* + * We can only maximize ECC config when the default layout is + * used, otherwise we don't know how many bytes can really be + * used. + */ + if (mtd->ooblayout == &nand_ooblayout_lp_ops && + ecc->options & NAND_ECC_MAXIMIZE) { + int steps, bytes; + + /* Always prefer 1k blocks over 512bytes ones */ + ecc->size = 1024; + steps = mtd->writesize / ecc->size; + + /* Reserve 2 bytes for the BBM */ + bytes = (mtd->oobsize - 2) / steps; + ecc->strength = bytes * 8 / fls(8 * ecc->size); + } + + /* See nand_bch_init() for details. */ + ecc->bytes = 0; + ecc->priv = nand_bch_init(mtd); + if (!ecc->priv) { + WARN(1, "BCH ECC initialization failed!\n"); + return -EINVAL; + } + return 0; + default: + WARN(1, "Unsupported ECC algorithm!\n"); + return -EINVAL; + } +} + +/** + * nand_check_ecc_caps - check the sanity of preset ECC settings + * @chip: nand chip info structure + * @caps: ECC caps info structure + * @oobavail: OOB size that the ECC engine can use + * + * When ECC step size and strength are already set, check if they are supported + * by the controller and the calculated ECC bytes fit within the chip's OOB. + * On success, the calculated ECC bytes is set. + */ +int nand_check_ecc_caps(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_ecc_step_info *stepinfo; + int preset_step = chip->ecc.size; + int preset_strength = chip->ecc.strength; + int nsteps, ecc_bytes; + int i, j; + + if (WARN_ON(oobavail < 0)) + return -EINVAL; + + if (!preset_step || !preset_strength) + return -ENODATA; + + nsteps = mtd->writesize / preset_step; + + for (i = 0; i < caps->nstepinfos; i++) { + stepinfo = &caps->stepinfos[i]; + + if (stepinfo->stepsize != preset_step) + continue; + + for (j = 0; j < stepinfo->nstrengths; j++) { + if (stepinfo->strengths[j] != preset_strength) + continue; + + ecc_bytes = caps->calc_ecc_bytes(preset_step, + preset_strength); + if (WARN_ON_ONCE(ecc_bytes < 0)) + return ecc_bytes; + + if (ecc_bytes * nsteps > oobavail) { + pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB", + preset_step, preset_strength); + return -ENOSPC; + } + + chip->ecc.bytes = ecc_bytes; + + return 0; + } + } + + pr_err("ECC (step, strength) = (%d, %d) not supported on this controller", + preset_step, preset_strength); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(nand_check_ecc_caps); + +/** + * nand_match_ecc_req - meet the chip's requirement with least ECC bytes + * @chip: nand chip info structure + * @caps: ECC engine caps info structure + * @oobavail: OOB size that the ECC engine can use + * + * If a chip's ECC requirement is provided, try to meet it with the least + * number of ECC bytes (i.e. with the largest number of OOB-free bytes). + * On success, the chosen ECC settings are set. + */ +int nand_match_ecc_req(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_ecc_step_info *stepinfo; + int req_step = chip->ecc_step_ds; + int req_strength = chip->ecc_strength_ds; + int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total; + int best_step, best_strength, best_ecc_bytes; + int best_ecc_bytes_total = INT_MAX; + int i, j; + + if (WARN_ON(oobavail < 0)) + return -EINVAL; + + /* No information provided by the NAND chip */ + if (!req_step || !req_strength) + return -ENOTSUPP; + + /* number of correctable bits the chip requires in a page */ + req_corr = mtd->writesize / req_step * req_strength; + + for (i = 0; i < caps->nstepinfos; i++) { + stepinfo = &caps->stepinfos[i]; + step_size = stepinfo->stepsize; + + for (j = 0; j < stepinfo->nstrengths; j++) { + strength = stepinfo->strengths[j]; + + /* + * If both step size and strength are smaller than the + * chip's requirement, it is not easy to compare the + * resulted reliability. + */ + if (step_size < req_step && strength < req_strength) + continue; + + if (mtd->writesize % step_size) + continue; + + nsteps = mtd->writesize / step_size; + + ecc_bytes = caps->calc_ecc_bytes(step_size, strength); + if (WARN_ON_ONCE(ecc_bytes < 0)) + continue; + ecc_bytes_total = ecc_bytes * nsteps; + + if (ecc_bytes_total > oobavail || + strength * nsteps < req_corr) + continue; + + /* + * We assume the best is to meet the chip's requrement + * with the least number of ECC bytes. + */ + if (ecc_bytes_total < best_ecc_bytes_total) { + best_ecc_bytes_total = ecc_bytes_total; + best_step = step_size; + best_strength = strength; + best_ecc_bytes = ecc_bytes; + } + } + } + + if (best_ecc_bytes_total == INT_MAX) + return -ENOTSUPP; + + chip->ecc.size = best_step; + chip->ecc.strength = best_strength; + chip->ecc.bytes = best_ecc_bytes; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_match_ecc_req); + +/** + * nand_maximize_ecc - choose the max ECC strength available + * @chip: nand chip info structure + * @caps: ECC engine caps info structure + * @oobavail: OOB size that the ECC engine can use + * + * Choose the max ECC strength that is supported on the controller, and can fit + * within the chip's OOB. On success, the chosen ECC settings are set. + */ +int nand_maximize_ecc(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_ecc_step_info *stepinfo; + int step_size, strength, nsteps, ecc_bytes, corr; + int best_corr = 0; + int best_step = 0; + int best_strength, best_ecc_bytes; + int i, j; + + if (WARN_ON(oobavail < 0)) + return -EINVAL; + + for (i = 0; i < caps->nstepinfos; i++) { + stepinfo = &caps->stepinfos[i]; + step_size = stepinfo->stepsize; + + /* If chip->ecc.size is already set, respect it */ + if (chip->ecc.size && step_size != chip->ecc.size) + continue; + + for (j = 0; j < stepinfo->nstrengths; j++) { + strength = stepinfo->strengths[j]; + + if (mtd->writesize % step_size) + continue; + + nsteps = mtd->writesize / step_size; + + ecc_bytes = caps->calc_ecc_bytes(step_size, strength); + if (WARN_ON_ONCE(ecc_bytes < 0)) + continue; + + if (ecc_bytes * nsteps > oobavail) + continue; + + corr = strength * nsteps; + + /* + * If the number of correctable bits is the same, + * bigger step_size has more reliability. + */ + if (corr > best_corr || + (corr == best_corr && step_size > best_step)) { + best_corr = corr; + best_step = step_size; + best_strength = strength; + best_ecc_bytes = ecc_bytes; + } + } + } + + if (!best_corr) + return -ENOTSUPP; + + chip->ecc.size = best_step; + chip->ecc.strength = best_strength; + chip->ecc.bytes = best_ecc_bytes; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_maximize_ecc); + +/* + * Check if the chip configuration meet the datasheet requirements. + + * If our configuration corrects A bits per B bytes and the minimum + * required correction level is X bits per Y bytes, then we must ensure + * both of the following are true: + * + * (1) A / B >= X / Y + * (2) A >= X + * + * Requirement (1) ensures we can correct for the required bitflip density. + * Requirement (2) ensures we can correct even when all bitflips are clumped + * in the same sector. + */ +static bool nand_ecc_strength_good(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int corr, ds_corr; + + if (ecc->size == 0 || chip->ecc_step_ds == 0) + /* Not enough information */ + return true; + + /* + * We get the number of corrected bits per page to compare + * the correction density. + */ + corr = (mtd->writesize * ecc->strength) / ecc->size; + ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds; + + return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; +} + +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * + * This is the second phase of the normal nand_scan() function. It fills out + * all the uninitialized function pointers with the defaults and scans for a + * bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i; + + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ + if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH))) { + return -EINVAL; + } + + chip->data_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!chip->data_buf) + return -ENOMEM; + + /* + * FIXME: some NAND manufacturer drivers expect the first die to be + * selected when manufacturer->init() is called. They should be fixed + * to explictly select the relevant die when interacting with the NAND + * chip. + */ + chip->select_chip(mtd, 0); + ret = nand_manufacturer_init(chip); + chip->select_chip(mtd, -1); + if (ret) + goto err_free_buf; + + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->data_buf + mtd->writesize; + + /* + * If no default placement scheme is given, select an appropriate one. + */ + if (!mtd->ooblayout && + !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) { + switch (mtd->oobsize) { + case 8: + case 16: + mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops); + break; + case 64: + case 128: + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops); + break; + default: + /* + * Expose the whole OOB area to users if ECC_NONE + * is passed. We could do that for all kind of + * ->oobsize, but we must keep the old large/small + * page with ECC layout when ->oobsize <= 128 for + * compatibility reasons. + */ + if (ecc->mode == NAND_ECC_NONE) { + mtd_set_ooblayout(mtd, + &nand_ooblayout_lp_ops); + break; + } + + WARN(1, "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + ret = -EINVAL; + goto err_nand_manuf_cleanup; + } + } + + /* + * Check ECC mode, default to software if 3byte/512byte hardware ECC is + * selected and we have 256 byte pagesize fallback to software ECC + */ + + switch (ecc->mode) { + case NAND_ECC_HW_OOB_FIRST: + /* Similar to NAND_ECC_HW, but a separate read_page handle */ + if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { + WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); + ret = -EINVAL; + goto err_nand_manuf_cleanup; + } + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc_oob_first; + + case NAND_ECC_HW: + /* Use standard hwecc read page function? */ + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc; + if (!ecc->write_page) + ecc->write_page = nand_write_page_hwecc; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_std; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_std; + if (!ecc->read_subpage) + ecc->read_subpage = nand_read_subpage; + if (!ecc->write_subpage && ecc->hwctl && ecc->calculate) + ecc->write_subpage = nand_write_subpage_hwecc; + + case NAND_ECC_HW_SYNDROME: + if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && + (!ecc->read_page || + ecc->read_page == nand_read_page_hwecc || + !ecc->write_page || + ecc->write_page == nand_write_page_hwecc)) { + WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); + ret = -EINVAL; + goto err_nand_manuf_cleanup; + } + /* Use standard syndrome read/write page function? */ + if (!ecc->read_page) + ecc->read_page = nand_read_page_syndrome; + if (!ecc->write_page) + ecc->write_page = nand_write_page_syndrome; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw_syndrome; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw_syndrome; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_syndrome; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_syndrome; + + if (mtd->writesize >= ecc->size) { + if (!ecc->strength) { + WARN(1, "Driver must set ecc.strength when using hardware ECC\n"); + ret = -EINVAL; + goto err_nand_manuf_cleanup; + } + break; + } + pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", + ecc->size, mtd->writesize); + ecc->mode = NAND_ECC_SOFT; + ecc->algo = NAND_ECC_HAMMING; + + case NAND_ECC_SOFT: + ret = nand_set_ecc_soft_ops(mtd); + if (ret) { + ret = -EINVAL; + goto err_nand_manuf_cleanup; + } + break; + + case NAND_ECC_ON_DIE: + if (!ecc->read_page || !ecc->write_page) { + WARN(1, "No ECC functions supplied; on-die ECC not possible\n"); + ret = -EINVAL; + goto err_nand_manuf_cleanup; + } + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_std; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_std; + break; + + case NAND_ECC_NONE: + pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n"); + ecc->read_page = nand_read_page_raw; + ecc->write_page = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->write_oob = nand_write_oob_std; + ecc->size = mtd->writesize; + ecc->bytes = 0; + ecc->strength = 0; + break; + + default: + WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode); + ret = -EINVAL; + goto err_nand_manuf_cleanup; + } + + if (ecc->correct || ecc->calculate) { + ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL); + ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL); + if (!ecc->calc_buf || !ecc->code_buf) { + ret = -ENOMEM; + goto err_nand_manuf_cleanup; + } + } + + /* For many systems, the standard OOB write also works for raw */ + if (!ecc->read_oob_raw) + ecc->read_oob_raw = ecc->read_oob; + if (!ecc->write_oob_raw) + ecc->write_oob_raw = ecc->write_oob; + + /* propagate ecc info to mtd_info */ + mtd->ecc_strength = ecc->strength; + mtd->ecc_step_size = ecc->size; + + /* + * Set the number of read / write steps for one page depending on ECC + * mode. + */ + ecc->steps = mtd->writesize / ecc->size; + if (ecc->steps * ecc->size != mtd->writesize) { + WARN(1, "Invalid ECC parameters\n"); + ret = -EINVAL; + goto err_nand_manuf_cleanup; + } + ecc->total = ecc->steps * ecc->bytes; + if (ecc->total > mtd->oobsize) { + WARN(1, "Total number of ECC bytes exceeded oobsize\n"); + ret = -EINVAL; + goto err_nand_manuf_cleanup; + } + + /* + * The number of bytes available for a client to place data into + * the out of band area. + */ + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + ret = 0; + + mtd->oobavail = ret; + + /* ECC sanity check: warn if it's too weak */ + if (!nand_ecc_strength_good(mtd)) + pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", + mtd->name); + + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { + switch (ecc->steps) { + case 2: + mtd->subpage_sft = 1; + break; + case 4: + case 8: + case 16: + mtd->subpage_sft = 2; + break; + } + } + chip->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* Initialize state */ + chip->state = FL_READY; + + /* Invalidate the pagebuffer reference */ + chip->pagebuf = -1; + + /* Large page NAND with SOFT_ECC should support subpage reads */ + switch (ecc->mode) { + case NAND_ECC_SOFT: + if (chip->page_shift > 9) + chip->options |= NAND_SUBPAGE_READ; + break; + + default: + break; + } + + /* Fill in remaining MTD driver data */ + mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; + mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : + MTD_CAP_NANDFLASH; + mtd->_erase = nand_erase; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_panic_write = panic_nand_write; + mtd->_read_oob = nand_read_oob; + mtd->_write_oob = nand_write_oob; + mtd->_sync = nand_sync; + mtd->_lock = NULL; + mtd->_unlock = NULL; + mtd->_suspend = nand_suspend; + mtd->_resume = nand_resume; + mtd->_reboot = nand_shutdown; + mtd->_block_isreserved = nand_block_isreserved; + mtd->_block_isbad = nand_block_isbad; + mtd->_block_markbad = nand_block_markbad; + mtd->_max_bad_blocks = nand_max_bad_blocks; + mtd->writebufsize = mtd->writesize; + + /* + * Initialize bitflip_threshold to its default prior scan_bbt() call. + * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be + * properly set. + */ + if (!mtd->bitflip_threshold) + mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); + + /* Initialize the ->data_interface field. */ + ret = nand_init_data_interface(chip); + if (ret) + goto err_nand_manuf_cleanup; + + /* Enter fastest possible mode on all dies. */ + for (i = 0; i < chip->numchips; i++) { + chip->select_chip(mtd, i); + ret = nand_setup_data_interface(chip, i); + chip->select_chip(mtd, -1); + + if (ret) + goto err_nand_manuf_cleanup; + } + + /* Check, if we should skip the bad block table scan */ + if (chip->options & NAND_SKIP_BBTSCAN) + return 0; + + /* Build bad block table */ + ret = chip->scan_bbt(mtd); + if (ret) + goto err_nand_manuf_cleanup; + + return 0; + + +err_nand_manuf_cleanup: + nand_manufacturer_cleanup(chip); + +err_free_buf: + kfree(chip->data_buf); + kfree(ecc->code_buf); + kfree(ecc->calc_buf); + + return ret; +} +EXPORT_SYMBOL(nand_scan_tail); + +/* + * is_module_text_address() isn't exported, and it's mostly a pointless + * test if this is a module _anyway_ -- they'd have to try _really_ hard + * to call us from in-kernel code if the core NAND support is modular. + */ +#ifdef MODULE +#define caller_is_module() (1) +#else +#define caller_is_module() \ + is_module_text_address((unsigned long)__builtin_return_address(0)) +#endif + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: number of chips to scan for + * + * This fills out all the uninitialized function pointers with the defaults. + * The flash ID is read and the mtd/chip structures are filled with the + * appropriate values. + */ +int nand_scan(struct mtd_info *mtd, int maxchips) +{ + int ret; + + ret = nand_scan_ident(mtd, maxchips, NULL); + if (!ret) + ret = nand_scan_tail(mtd); + return ret; +} +EXPORT_SYMBOL(nand_scan); + +/** + * nand_cleanup - [NAND Interface] Free resources held by the NAND device + * @chip: NAND chip object + */ +void nand_cleanup(struct nand_chip *chip) +{ + if (chip->ecc.mode == NAND_ECC_SOFT && + chip->ecc.algo == NAND_ECC_BCH) + nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + + /* Free bad block table memory */ + kfree(chip->bbt); + kfree(chip->data_buf); + kfree(chip->ecc.code_buf); + kfree(chip->ecc.calc_buf); + + /* Free bad block descriptor memory */ + if (chip->badblock_pattern && chip->badblock_pattern->options + & NAND_BBT_DYNAMICSTRUCT) + kfree(chip->badblock_pattern); + + /* Free manufacturer priv data. */ + nand_manufacturer_cleanup(chip); +} +EXPORT_SYMBOL_GPL(nand_cleanup); + +/** + * nand_release - [NAND Interface] Unregister the MTD device and free resources + * held by the NAND device + * @mtd: MTD device structure + */ +void nand_release(struct mtd_info *mtd) +{ + mtd_device_unregister(mtd); + nand_cleanup(mtd_to_nand(mtd)); +} +EXPORT_SYMBOL_GPL(nand_release); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Steven J. Hill "); +MODULE_AUTHOR("Thomas Gleixner "); +MODULE_DESCRIPTION("Generic NAND flash driver code"); diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c new file mode 100644 index 000000000000..36092850be2c --- /dev/null +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -0,0 +1,1452 @@ +/* + * Overview: + * Bad block table support for the NAND driver + * + * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Description: + * + * When nand_scan_bbt is called, then it tries to find the bad block table + * depending on the options in the BBT descriptor(s). If no flash based BBT + * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory + * marked good / bad blocks. This information is used to create a memory BBT. + * Once a new bad block is discovered then the "factory" information is updated + * on the device. + * If a flash based BBT is specified then the function first tries to find the + * BBT on flash. If a BBT is found then the contents are read and the memory + * based BBT is created. If a mirrored BBT is selected then the mirror is + * searched too and the versions are compared. If the mirror has a greater + * version number, then the mirror BBT is used to build the memory based BBT. + * If the tables are not versioned, then we "or" the bad block information. + * If one of the BBTs is out of date or does not exist it is (re)created. + * If no BBT exists at all then the device is scanned for factory marked + * good / bad blocks and the bad block tables are created. + * + * For manufacturer created BBTs like the one found on M-SYS DOC devices + * the BBT is searched and read but never created + * + * The auto generated bad block table is located in the last good blocks + * of the device. The table is mirrored, so it can be updated eventually. + * The table is marked in the OOB area with an ident pattern and a version + * number which indicates which of both tables is more up to date. If the NAND + * controller needs the complete OOB area for the ECC information then the + * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of + * course): it moves the ident pattern and the version byte into the data area + * and the OOB area will remain untouched. + * + * The table uses 2 bits per block + * 11b: block is good + * 00b: block is factory marked bad + * 01b, 10b: block is marked bad due to wear + * + * The memory bad block table uses the following scheme: + * 00b: block is good + * 01b: block is marked bad due to wear + * 10b: block is reserved (to protect the bbt area) + * 11b: block is factory marked bad + * + * Multichip devices like DOC store the bad block info per floor. + * + * Following assumptions are made: + * - bbts start at a page boundary, if autolocated on a block boundary + * - the space necessary for a bbt in FLASH does not exceed a block boundary + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BBT_BLOCK_GOOD 0x00 +#define BBT_BLOCK_WORN 0x01 +#define BBT_BLOCK_RESERVED 0x02 +#define BBT_BLOCK_FACTORY_BAD 0x03 + +#define BBT_ENTRY_MASK 0x03 +#define BBT_ENTRY_SHIFT 2 + +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); + +static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) +{ + uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; + entry >>= (block & BBT_ENTRY_MASK) * 2; + return entry & BBT_ENTRY_MASK; +} + +static inline void bbt_mark_entry(struct nand_chip *chip, int block, + uint8_t mark) +{ + uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); + chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; +} + +static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) +{ + if (memcmp(buf, td->pattern, td->len)) + return -1; + return 0; +} + +/** + * check_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block tables and + * good / bad block identifiers. + */ +static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + if (td->options & NAND_BBT_NO_OOB) + return check_pattern_no_oob(buf, td); + + /* Compare the pattern */ + if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) + return -1; + + return 0; +} + +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block tables and + * good / bad block identifiers. Same as check_pattern, but no optional empty + * check. + */ +static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) +{ + /* Compare the pattern */ + if (memcmp(buf + td->offs, td->pattern, td->len)) + return -1; + return 0; +} + +/** + * add_marker_len - compute the length of the marker in data area + * @td: BBT descriptor used for computation + * + * The length will be 0 if the marker is located in OOB area. + */ +static u32 add_marker_len(struct nand_bbt_descr *td) +{ + u32 len; + + if (!(td->options & NAND_BBT_NO_OOB)) + return 0; + + len = td->len; + if (td->options & NAND_BBT_VERSION) + len++; + return len; +} + +/** + * read_bbt - [GENERIC] Read the bad block table starting from page + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @td: the bbt describtion table + * @offs: block number offset in the table + * + * Read the bad block table starting from page. + */ +static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, + struct nand_bbt_descr *td, int offs) +{ + int res, ret = 0, i, j, act = 0; + struct nand_chip *this = mtd_to_nand(mtd); + size_t retlen, len, totlen; + loff_t from; + int bits = td->options & NAND_BBT_NRBITS_MSK; + uint8_t msk = (uint8_t)((1 << bits) - 1); + u32 marker_len; + int reserved_block_code = td->reserved_block_code; + + totlen = (num * bits) >> 3; + marker_len = add_marker_len(td); + from = ((loff_t)page) << this->page_shift; + + while (totlen) { + len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); + if (marker_len) { + /* + * In case the BBT marker is not in the OOB area it + * will be just in the first page. + */ + len -= marker_len; + from += marker_len; + marker_len = 0; + } + res = mtd_read(mtd, from, len, &retlen, buf); + if (res < 0) { + if (mtd_is_eccerr(res)) { + pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n", + from & ~mtd->writesize); + return res; + } else if (mtd_is_bitflip(res)) { + pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n", + from & ~mtd->writesize); + ret = res; + } else { + pr_info("nand_bbt: error reading BBT\n"); + return res; + } + } + + /* Analyse data */ + for (i = 0; i < len; i++) { + uint8_t dat = buf[i]; + for (j = 0; j < 8; j += bits, act++) { + uint8_t tmp = (dat >> j) & msk; + if (tmp == msk) + continue; + if (reserved_block_code && (tmp == reserved_block_code)) { + pr_info("nand_read_bbt: reserved block at 0x%012llx\n", + (loff_t)(offs + act) << + this->bbt_erase_shift); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_RESERVED); + mtd->ecc_stats.bbtblocks++; + continue; + } + /* + * Leave it for now, if it's matured we can + * move this message to pr_debug. + */ + pr_info("nand_read_bbt: bad block at 0x%012llx\n", + (loff_t)(offs + act) << + this->bbt_erase_shift); + /* Factory marked bad or worn out? */ + if (tmp == 0) + bbt_mark_entry(this, offs + act, + BBT_BLOCK_FACTORY_BAD); + else + bbt_mark_entry(this, offs + act, + BBT_BLOCK_WORN); + mtd->ecc_stats.badblocks++; + } + } + totlen -= len; + from += len; + } + return ret; +} + +/** + * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips; applies only if + * NAND_BBT_PERCHIP option is set + * + * Read the bad block table for all chips starting at a given page. We assume + * that the bbt bits are in consecutive order. + */ +static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int res = 0, i; + + if (td->options & NAND_BBT_PERCHIP) { + int offs = 0; + for (i = 0; i < this->numchips; i++) { + if (chip == -1 || chip == i) + res = read_bbt(mtd, buf, td->pages[i], + this->chipsize >> this->bbt_erase_shift, + td, offs); + if (res) + return res; + offs += this->chipsize >> this->bbt_erase_shift; + } + } else { + res = read_bbt(mtd, buf, td->pages[0], + mtd->size >> this->bbt_erase_shift, td, 0); + if (res) + return res; + } + return 0; +} + +/* BBT marker is in the first page, no OOB */ +static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + struct nand_bbt_descr *td) +{ + size_t retlen; + size_t len; + + len = td->len; + if (td->options & NAND_BBT_VERSION) + len++; + + return mtd_read(mtd, offs, len, &retlen, buf); +} + +/** + * scan_read_oob - [GENERIC] Scan data+OOB region to buffer + * @mtd: MTD device structure + * @buf: temporary buffer + * @offs: offset at which to scan + * @len: length of data region to read + * + * Scan read data from data+OOB. May traverse multiple pages, interleaving + * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" + * ECC condition (error or bitflip). May quit on the first (non-ECC) error. + */ +static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len) +{ + struct mtd_oob_ops ops; + int res, ret = 0; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + + while (len > 0) { + ops.datbuf = buf; + ops.len = min(len, (size_t)mtd->writesize); + ops.oobbuf = buf + ops.len; + + res = mtd_read_oob(mtd, offs, &ops); + if (res) { + if (!mtd_is_bitflip_or_eccerr(res)) + return res; + else if (mtd_is_eccerr(res) || !ret) + ret = res; + } + + buf += mtd->oobsize + mtd->writesize; + len -= mtd->writesize; + offs += mtd->writesize; + } + return ret; +} + +static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len, struct nand_bbt_descr *td) +{ + if (td->options & NAND_BBT_NO_OOB) + return scan_read_data(mtd, buf, offs, td); + else + return scan_read_oob(mtd, buf, offs, len); +} + +/* Scan write data with oob to flash */ +static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, + uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + + return mtd_write_oob(mtd, offs, &ops); +} + +static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + u32 ver_offs = td->veroffs; + + if (!(td->options & NAND_BBT_NO_OOB)) + ver_offs += mtd->writesize; + return ver_offs; +} + +/** + * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Read the bad block table(s) for all chips starting at a given page. We + * assume that the bbt bits are in consecutive order. + */ +static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + /* Read the primary version, if available */ + if (td->options & NAND_BBT_VERSION) { + scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift, + mtd->writesize, td); + td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; + pr_info("Bad block table at page %d, version 0x%02X\n", + td->pages[0], td->version[0]); + } + + /* Read the mirror version, if available */ + if (md && (md->options & NAND_BBT_VERSION)) { + scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift, + mtd->writesize, md); + md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; + pr_info("Bad block table at page %d, version 0x%02X\n", + md->pages[0], md->version[0]); + } +} + +/* Scan a given block partially */ +static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, int numpages) +{ + struct mtd_oob_ops ops; + int j, ret; + + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + for (j = 0; j < numpages; j++) { + /* + * Read the full oob until read_oob is fixed to handle single + * byte reads for 16 bit buswidth. + */ + ret = mtd_read_oob(mtd, offs, &ops); + /* Ignore ECC errors when checking for BBM */ + if (ret && !mtd_is_bitflip_or_eccerr(ret)) + return ret; + + if (check_short_pattern(buf, bd)) + return 1; + + offs += mtd->writesize; + } + return 0; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @chip: create the table for a specific chip, -1 read all chips; applies only + * if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device for the given good/bad block + * identify pattern. + */ +static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *bd, int chip) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int i, numblocks, numpages; + int startblock; + loff_t from; + + pr_info("Scanning device for bad blocks\n"); + + if (bd->options & NAND_BBT_SCAN2NDPAGE) + numpages = 2; + else + numpages = 1; + + if (chip == -1) { + numblocks = mtd->size >> this->bbt_erase_shift; + startblock = 0; + from = 0; + } else { + if (chip >= this->numchips) { + pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", + chip + 1, this->numchips); + return -EINVAL; + } + numblocks = this->chipsize >> this->bbt_erase_shift; + startblock = chip * numblocks; + numblocks += startblock; + from = (loff_t)startblock << this->bbt_erase_shift; + } + + if (this->bbt_options & NAND_BBT_SCANLASTPAGE) + from += mtd->erasesize - (mtd->writesize * numpages); + + for (i = startblock; i < numblocks; i++) { + int ret; + + BUG_ON(bd->options & NAND_BBT_NO_OOB); + + ret = scan_block_fast(mtd, bd, from, buf, numpages); + if (ret < 0) + return ret; + + if (ret) { + bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); + pr_warn("Bad eraseblock %d at 0x%012llx\n", + i, (unsigned long long)from); + mtd->ecc_stats.badblocks++; + } + + from += (1 << this->bbt_erase_shift); + } + return 0; +} + +/** + * search_bbt - [GENERIC] scan the device for a specific bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * + * Read the bad block table by searching for a given ident pattern. Search is + * preformed either from the beginning up or from the end of the device + * downwards. The search starts always at the start of a block. If the option + * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains + * the bad block information of this chip. This is necessary to provide support + * for certain DOC devices. + * + * The bbt ident pattern resides in the oob area of the first page in a block. + */ +static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int i, chips; + int startblock, block, dir; + int scanlen = mtd->writesize + mtd->oobsize; + int bbtblocks; + int blocktopage = this->bbt_erase_shift - this->page_shift; + + /* Search direction top -> down? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = (mtd->size >> this->bbt_erase_shift) - 1; + dir = -1; + } else { + startblock = 0; + dir = 1; + } + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + bbtblocks = this->chipsize >> this->bbt_erase_shift; + startblock &= bbtblocks - 1; + } else { + chips = 1; + bbtblocks = mtd->size >> this->bbt_erase_shift; + } + + for (i = 0; i < chips; i++) { + /* Reset version information */ + td->version[i] = 0; + td->pages[i] = -1; + /* Scan the maximum number of blocks */ + for (block = 0; block < td->maxblocks; block++) { + + int actblock = startblock + dir * block; + loff_t offs = (loff_t)actblock << this->bbt_erase_shift; + + /* Read first page */ + scan_read(mtd, buf, offs, mtd->writesize, td); + if (!check_pattern(buf, scanlen, mtd->writesize, td)) { + td->pages[i] = actblock << blocktopage; + if (td->options & NAND_BBT_VERSION) { + offs = bbt_get_ver_offs(mtd, td); + td->version[i] = buf[offs]; + } + break; + } + } + startblock += this->chipsize >> this->bbt_erase_shift; + } + /* Check, if we found a bbt for each requested chip */ + for (i = 0; i < chips; i++) { + if (td->pages[i] == -1) + pr_warn("Bad block table not found for chip %d\n", i); + else + pr_info("Bad block table found at page %d, version 0x%02X\n", + td->pages[i], td->version[i]); + } + return 0; +} + +/** + * search_read_bbts - [GENERIC] scan the device for bad block table(s) + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Search and read the bad block table(s). + */ +static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, + struct nand_bbt_descr *md) +{ + /* Search the primary table */ + search_bbt(mtd, buf, td); + + /* Search the mirror table */ + if (md) + search_bbt(mtd, buf, md); +} + +/** + * get_bbt_block - Get the first valid eraseblock suitable to store a BBT + * @this: the NAND device + * @td: the BBT description + * @md: the mirror BBT descriptor + * @chip: the CHIP selector + * + * This functions returns a positive block number pointing a valid eraseblock + * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if + * all blocks are already used of marked bad. If td->pages[chip] was already + * pointing to a valid block we re-use it, otherwise we search for the next + * valid one. + */ +static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, + struct nand_bbt_descr *md, int chip) +{ + int startblock, dir, page, numblocks, i; + + /* + * There was already a version of the table, reuse the page. This + * applies for absolute placement too, as we have the page number in + * td->pages. + */ + if (td->pages[chip] != -1) + return td->pages[chip] >> + (this->bbt_erase_shift - this->page_shift); + + numblocks = (int)(this->chipsize >> this->bbt_erase_shift); + if (!(td->options & NAND_BBT_PERCHIP)) + numblocks *= this->numchips; + + /* + * Automatic placement of the bad block table. Search direction + * top -> down? + */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + + for (i = 0; i < td->maxblocks; i++) { + int block = startblock + dir * i; + + /* Check, if the block is bad */ + switch (bbt_get_entry(this, block)) { + case BBT_BLOCK_WORN: + case BBT_BLOCK_FACTORY_BAD: + continue; + } + + page = block << (this->bbt_erase_shift - this->page_shift); + + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + return block; + } + + return -ENOSPC; +} + +/** + * mark_bbt_block_bad - Mark one of the block reserved for BBT bad + * @this: the NAND device + * @td: the BBT description + * @chip: the CHIP selector + * @block: the BBT block to mark + * + * Blocks reserved for BBT can become bad. This functions is an helper to mark + * such blocks as bad. It takes care of updating the in-memory BBT, marking the + * block as bad using a bad block marker and invalidating the associated + * td->pages[] entry. + */ +static void mark_bbt_block_bad(struct nand_chip *this, + struct nand_bbt_descr *td, + int chip, int block) +{ + struct mtd_info *mtd = nand_to_mtd(this); + loff_t to; + int res; + + bbt_mark_entry(this, block, BBT_BLOCK_WORN); + + to = (loff_t)block << this->bbt_erase_shift; + res = this->block_markbad(mtd, to); + if (res) + pr_warn("nand_bbt: error %d while marking block %d bad\n", + res, block); + + td->pages[chip] = -1; +} + +/** + * write_bbt - [GENERIC] (Re)write the bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all + * + * (Re)write the bad block table. + */ +static int write_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, + int chipsel) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct erase_info einfo; + int i, res, chip = 0; + int bits, page, offs, numblocks, sft, sftmsk; + int nrchips, pageoffs, ooboffs; + uint8_t msk[4]; + uint8_t rcode = td->reserved_block_code; + size_t retlen, len = 0; + loff_t to; + struct mtd_oob_ops ops; + + ops.ooblen = mtd->oobsize; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + if (!rcode) + rcode = 0xff; + /* Write bad block table per chip rather than per device? */ + if (td->options & NAND_BBT_PERCHIP) { + numblocks = (int)(this->chipsize >> this->bbt_erase_shift); + /* Full device write or specific chip? */ + if (chipsel == -1) { + nrchips = this->numchips; + } else { + nrchips = chipsel + 1; + chip = chipsel; + } + } else { + numblocks = (int)(mtd->size >> this->bbt_erase_shift); + nrchips = 1; + } + + /* Loop through the chips */ + while (chip < nrchips) { + int block; + + block = get_bbt_block(this, td, md, chip); + if (block < 0) { + pr_err("No space left to write bad block table\n"); + res = block; + goto outerr; + } + + /* + * get_bbt_block() returns a block number, shift the value to + * get a page number. + */ + page = block << (this->bbt_erase_shift - this->page_shift); + + /* Set up shift count and masks for the flash table */ + bits = td->options & NAND_BBT_NRBITS_MSK; + msk[2] = ~rcode; + switch (bits) { + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x01; + break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x03; + break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; + msk[3] = 0x0f; + break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; + msk[3] = 0xff; + break; + default: return -EINVAL; + } + + to = ((loff_t)page) << this->page_shift; + + /* Must we save the block contents? */ + if (td->options & NAND_BBT_SAVECONTENT) { + /* Make it block aligned */ + to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1); + len = 1 << this->bbt_erase_shift; + res = mtd_read(mtd, to, len, &retlen, buf); + if (res < 0) { + if (retlen != len) { + pr_info("nand_bbt: error reading block for writing the bad block table\n"); + return res; + } + pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n"); + } + /* Read oob data */ + ops.ooblen = (len >> this->page_shift) * mtd->oobsize; + ops.oobbuf = &buf[len]; + res = mtd_read_oob(mtd, to + mtd->writesize, &ops); + if (res < 0 || ops.oobretlen != ops.ooblen) + goto outerr; + + /* Calc the byte offset in the buffer */ + pageoffs = page - (int)(to >> this->page_shift); + offs = pageoffs << this->page_shift; + /* Preset the bbt area with 0xff */ + memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); + ooboffs = len + (pageoffs * mtd->oobsize); + + } else if (td->options & NAND_BBT_NO_OOB) { + ooboffs = 0; + offs = td->len; + /* The version byte */ + if (td->options & NAND_BBT_VERSION) + offs++; + /* Calc length */ + len = (size_t)(numblocks >> sft); + len += offs; + /* Make it page aligned! */ + len = ALIGN(len, mtd->writesize); + /* Preset the buffer with 0xff */ + memset(buf, 0xff, len); + /* Pattern is located at the begin of first page */ + memcpy(buf, td->pattern, td->len); + } else { + /* Calc length */ + len = (size_t)(numblocks >> sft); + /* Make it page aligned! */ + len = ALIGN(len, mtd->writesize); + /* Preset the buffer with 0xff */ + memset(buf, 0xff, len + + (len >> this->page_shift)* mtd->oobsize); + offs = 0; + ooboffs = len; + /* Pattern is located in oob area of first page */ + memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); + } + + if (td->options & NAND_BBT_VERSION) + buf[ooboffs + td->veroffs] = td->version[chip]; + + /* Walk through the memory table */ + for (i = 0; i < numblocks; i++) { + uint8_t dat; + int sftcnt = (i << (3 - sft)) & sftmsk; + dat = bbt_get_entry(this, chip * numblocks + i); + /* Do not store the reserved bbt blocks! */ + buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); + } + + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = to; + einfo.len = 1 << this->bbt_erase_shift; + res = nand_erase_nand(mtd, &einfo, 1); + if (res < 0) { + pr_warn("nand_bbt: error while erasing BBT block %d\n", + res); + mark_bbt_block_bad(this, td, chip, block); + continue; + } + + res = scan_write_bbt(mtd, to, len, buf, + td->options & NAND_BBT_NO_OOB ? NULL : + &buf[len]); + if (res < 0) { + pr_warn("nand_bbt: error while writing BBT block %d\n", + res); + mark_bbt_block_bad(this, td, chip, block); + continue; + } + + pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", + (unsigned long long)to, td->version[chip]); + + /* Mark it as used */ + td->pages[chip++] = page; + } + return 0; + + outerr: + pr_warn("nand_bbt: error while writing bad block table %d\n", res); + return res; +} + +/** + * nand_memory_bbt - [GENERIC] create a memory based bad block table + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device for + * manufacturer / software marked good / bad blocks. + */ +static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + return create_bbt(mtd, this->data_buf, bd, -1); +} + +/** + * check_create - [GENERIC] create and write bbt(s) if necessary + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * + * The function checks the results of the previous call to read_bbt and creates + * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found + * for the chip/device. Update is necessary if one of the tables is missing or + * the version nr. of one table is less than the other. + */ +static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +{ + int i, chips, writeops, create, chipsel, res, res2; + struct nand_chip *this = mtd_to_nand(mtd); + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + struct nand_bbt_descr *rd, *rd2; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) + chips = this->numchips; + else + chips = 1; + + for (i = 0; i < chips; i++) { + writeops = 0; + create = 0; + rd = NULL; + rd2 = NULL; + res = res2 = 0; + /* Per chip or per device? */ + chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; + /* Mirrored table available? */ + if (md) { + if (td->pages[i] == -1 && md->pages[i] == -1) { + create = 1; + writeops = 0x03; + } else if (td->pages[i] == -1) { + rd = md; + writeops = 0x01; + } else if (md->pages[i] == -1) { + rd = td; + writeops = 0x02; + } else if (td->version[i] == md->version[i]) { + rd = td; + if (!(td->options & NAND_BBT_VERSION)) + rd2 = md; + } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { + rd = td; + writeops = 0x02; + } else { + rd = md; + writeops = 0x01; + } + } else { + if (td->pages[i] == -1) { + create = 1; + writeops = 0x01; + } else { + rd = td; + } + } + + if (create) { + /* Create the bad block table by scanning the device? */ + if (!(td->options & NAND_BBT_CREATE)) + continue; + + /* Create the table in memory by scanning the chip(s) */ + if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) + create_bbt(mtd, buf, bd, chipsel); + + td->version[i] = 1; + if (md) + md->version[i] = 1; + } + + /* Read back first? */ + if (rd) { + res = read_abs_bbt(mtd, buf, rd, chipsel); + if (mtd_is_eccerr(res)) { + /* Mark table as invalid */ + rd->pages[i] = -1; + rd->version[i] = 0; + i--; + continue; + } + } + /* If they weren't versioned, read both */ + if (rd2) { + res2 = read_abs_bbt(mtd, buf, rd2, chipsel); + if (mtd_is_eccerr(res2)) { + /* Mark table as invalid */ + rd2->pages[i] = -1; + rd2->version[i] = 0; + i--; + continue; + } + } + + /* Scrub the flash table(s)? */ + if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) + writeops = 0x03; + + /* Update version numbers before writing */ + if (md) { + td->version[i] = max(td->version[i], md->version[i]); + md->version[i] = td->version[i]; + } + + /* Write the bad block table to the device? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + return res; + } + + /* Write the mirror bad block table to the device? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, md, td, chipsel); + if (res < 0) + return res; + } + } + return 0; +} + +/** + * mark_bbt_regions - [GENERIC] mark the bad block table regions + * @mtd: MTD device structure + * @td: bad block table descriptor + * + * The bad block table regions are marked as "bad" to prevent accidental + * erasures / writes. The regions are identified by the mark 0x02. + */ +static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int i, j, chips, block, nrblocks, update; + uint8_t oldval; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); + } else { + chips = 1; + nrblocks = (int)(mtd->size >> this->bbt_erase_shift); + } + + for (i = 0; i < chips; i++) { + if ((td->options & NAND_BBT_ABSPAGE) || + !(td->options & NAND_BBT_WRITE)) { + if (td->pages[i] == -1) + continue; + block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if ((oldval != BBT_BLOCK_RESERVED) && + td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)block << + this->bbt_erase_shift); + continue; + } + update = 0; + if (td->options & NAND_BBT_LASTBLOCK) + block = ((i + 1) * nrblocks) - td->maxblocks; + else + block = i * nrblocks; + for (j = 0; j < td->maxblocks; j++) { + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if (oldval != BBT_BLOCK_RESERVED) + update = 1; + block++; + } + /* + * If we want reserved blocks to be recorded to flash, and some + * new ones have been marked, then we need to update the stored + * bbts. This should only happen once. + */ + if (update && td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)(block - 1) << + this->bbt_erase_shift); + } +} + +/** + * verify_bbt_descr - verify the bad block description + * @mtd: MTD device structure + * @bd: the table to verify + * + * This functions performs a few sanity checks on the bad block description + * table. + */ +static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u32 pattern_len; + u32 bits; + u32 table_size; + + if (!bd) + return; + + pattern_len = bd->len; + bits = bd->options & NAND_BBT_NRBITS_MSK; + + BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && + !(this->bbt_options & NAND_BBT_USE_FLASH)); + BUG_ON(!bits); + + if (bd->options & NAND_BBT_VERSION) + pattern_len++; + + if (bd->options & NAND_BBT_NO_OOB) { + BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); + BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); + BUG_ON(bd->offs); + if (bd->options & NAND_BBT_VERSION) + BUG_ON(bd->veroffs != bd->len); + BUG_ON(bd->options & NAND_BBT_SAVECONTENT); + } + + if (bd->options & NAND_BBT_PERCHIP) + table_size = this->chipsize >> this->bbt_erase_shift; + else + table_size = mtd->size >> this->bbt_erase_shift; + table_size >>= 3; + table_size *= bits; + if (bd->options & NAND_BBT_NO_OOB) + table_size += pattern_len; + BUG_ON(table_size > (1 << this->bbt_erase_shift)); +} + +/** + * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already available. If + * not it scans the device for manufacturer marked good / bad blocks and writes + * the bad block table(s) to the selected place. + * + * The bad block table memory is allocated here. It must be freed by calling + * the nand_free_bbt function. + */ +static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int len, res; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1; + /* + * Allocate memory (2bit per block) and clear the memory bad block + * table. + */ + this->bbt = kzalloc(len, GFP_KERNEL); + if (!this->bbt) + return -ENOMEM; + + /* + * If no primary table decriptor is given, scan the device to build a + * memory based bad block table. + */ + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); + goto err; + } + return 0; + } + verify_bbt_descr(mtd, td); + verify_bbt_descr(mtd, md); + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = vmalloc(len); + if (!buf) { + res = -ENOMEM; + goto err; + } + + /* Is the bbt at a given page? */ + if (td->options & NAND_BBT_ABSPAGE) { + read_abs_bbts(mtd, buf, td, md); + } else { + /* Search the bad block table using a pattern in oob */ + search_read_bbts(mtd, buf, td, md); + } + + res = check_create(mtd, buf, bd); + if (res) + goto err; + + /* Prevent the bbt regions from erasing / writing */ + mark_bbt_region(mtd, td); + if (md) + mark_bbt_region(mtd, md); + + vfree(buf); + return 0; + +err: + kfree(this->bbt); + this->bbt = NULL; + return res; +} + +/** + * nand_update_bbt - update bad block table(s) + * @mtd: MTD device structure + * @offs: the offset of the newly marked block + * + * The function updates the bad block table(s). + */ +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int len, res = 0; + int chip, chipsel; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + if (!this->bbt || !td) + return -EINVAL; + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chip = (int)(offs >> this->chip_shift); + chipsel = chip; + } else { + chip = 0; + chipsel = -1; + } + + td->version[chip]++; + if (md) + md->version[chip]++; + + /* Write the bad block table to the device? */ + if (td->options & NAND_BBT_WRITE) { + res = write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + goto out; + } + /* Write the mirror bad block table to the device? */ + if (md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, md, td, chipsel); + } + + out: + kfree(buf); + return res; +} + +/* + * Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. + */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +/* Generic flash bbt descriptors */ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = mirror_pattern +}; + +static struct nand_bbt_descr bbt_main_no_oob_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP + | NAND_BBT_NO_OOB, + .len = 4, + .veroffs = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_no_oob_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP + | NAND_BBT_NO_OOB, + .len = 4, + .veroffs = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = mirror_pattern +}; + +#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) +/** + * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure + * @this: NAND chip to create descriptor for + * + * This function allocates and initializes a nand_bbt_descr for BBM detection + * based on the properties of @this. The new descriptor is stored in + * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when + * passed to this function. + */ +static int nand_create_badblock_pattern(struct nand_chip *this) +{ + struct nand_bbt_descr *bd; + if (this->badblock_pattern) { + pr_warn("Bad block pattern already allocated; not replacing\n"); + return -EINVAL; + } + bd = kzalloc(sizeof(*bd), GFP_KERNEL); + if (!bd) + return -ENOMEM; + bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; + bd->offs = this->badblockpos; + bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; + bd->pattern = scan_ff_pattern; + bd->options |= NAND_BBT_DYNAMICSTRUCT; + this->badblock_pattern = bd; + return 0; +} + +/** + * nand_default_bbt - [NAND Interface] Select a default bad block table for the device + * @mtd: MTD device structure + * + * This function selects the default bad block table support for the device and + * calls the nand_scan_bbt function. + */ +int nand_default_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int ret; + + /* Is a flash based bad block table requested? */ + if (this->bbt_options & NAND_BBT_USE_FLASH) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + if (this->bbt_options & NAND_BBT_NO_OOB) { + this->bbt_td = &bbt_main_no_oob_descr; + this->bbt_md = &bbt_mirror_no_oob_descr; + } else { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + } + } else { + this->bbt_td = NULL; + this->bbt_md = NULL; + } + + if (!this->badblock_pattern) { + ret = nand_create_badblock_pattern(this); + if (ret) + return ret; + } + + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +/** + * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved + * @mtd: MTD device structure + * @offs: offset in the device + */ +int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int block; + + block = (int)(offs >> this->bbt_erase_shift); + return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED; +} + +/** + * nand_isbad_bbt - [NAND Interface] Check if a block is bad + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + */ +int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int block, res; + + block = (int)(offs >> this->bbt_erase_shift); + res = bbt_get_entry(this, block); + + pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block, res); + + switch (res) { + case BBT_BLOCK_GOOD: + return 0; + case BBT_BLOCK_WORN: + return 1; + case BBT_BLOCK_RESERVED: + return allowbbt ? 0 : 1; + } + return 1; +} + +/** + * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT + * @mtd: MTD device structure + * @offs: offset of the bad block + */ +int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int block, ret = 0; + + block = (int)(offs >> this->bbt_erase_shift); + + /* Mark bad block in memory */ + bbt_mark_entry(this, block, BBT_BLOCK_WORN); + + /* Update flash-based bad block table */ + if (this->bbt_options & NAND_BBT_USE_FLASH) + ret = nand_update_bbt(mtd, offs); + + return ret; +} diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c new file mode 100644 index 000000000000..505441c9373b --- /dev/null +++ b/drivers/mtd/nand/raw/nand_bch.c @@ -0,0 +1,234 @@ +/* + * This file provides ECC correction for more than 1 bit per block of data, + * using binary BCH codes. It relies on the generic BCH library lib/bch.c. + * + * Copyright © 2011 Ivan Djelic + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. + * + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct nand_bch_control - private NAND BCH control structure + * @bch: BCH control structure + * @errloc: error location array + * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid + */ +struct nand_bch_control { + struct bch_control *bch; + unsigned int *errloc; + unsigned char *eccmask; +}; + +/** + * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + const struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int i; + + memset(code, 0, chip->ecc.bytes); + encode_bch(nbc->bch, buf, chip->ecc.size, code); + + /* apply mask so that an erased page is a valid codeword */ + for (i = 0; i < chip->ecc.bytes; i++) + code[i] ^= nbc->eccmask[i]; + + return 0; +} +EXPORT_SYMBOL(nand_bch_calculate_ecc); + +/** + * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct bit errors for a data byte block + */ +int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + const struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int *errloc = nbc->errloc; + int i, count; + + count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, + NULL, errloc); + if (count > 0) { + for (i = 0; i < count; i++) { + if (errloc[i] < (chip->ecc.size*8)) + /* error is located in data, correct it */ + buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); + /* else error in ecc, no action needed */ + + pr_debug("%s: corrected bitflip %u\n", __func__, + errloc[i]); + } + } else if (count < 0) { + printk(KERN_ERR "ecc unrecoverable error\n"); + count = -EBADMSG; + } + return count; +} +EXPORT_SYMBOL(nand_bch_correct_data); + +/** + * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction + * @mtd: MTD block structure + * + * Returns: + * a pointer to a new NAND BCH control structure, or NULL upon failure + * + * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes + * are used to compute BCH parameters m (Galois field order) and t (error + * correction capability). @eccbytes should be equal to the number of bytes + * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. + * + * Example: to configure 4 bit correction per 512 bytes, you should pass + * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) + * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) + */ +struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + unsigned int m, t, eccsteps, i; + struct nand_bch_control *nbc = NULL; + unsigned char *erased_page; + unsigned int eccsize = nand->ecc.size; + unsigned int eccbytes = nand->ecc.bytes; + unsigned int eccstrength = nand->ecc.strength; + + if (!eccbytes && eccstrength) { + eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); + nand->ecc.bytes = eccbytes; + } + + if (!eccsize || !eccbytes) { + printk(KERN_WARNING "ecc parameters not supplied\n"); + goto fail; + } + + m = fls(1+8*eccsize); + t = (eccbytes*8)/m; + + nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); + if (!nbc) + goto fail; + + nbc->bch = init_bch(m, t, 0); + if (!nbc->bch) + goto fail; + + /* verify that eccbytes has the expected value */ + if (nbc->bch->ecc_bytes != eccbytes) { + printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", + eccbytes, nbc->bch->ecc_bytes); + goto fail; + } + + eccsteps = mtd->writesize/eccsize; + + /* Check that we have an oob layout description. */ + if (!mtd->ooblayout) { + pr_warn("missing oob scheme"); + goto fail; + } + + /* sanity checks */ + if (8*(eccsize+eccbytes) >= (1 << m)) { + printk(KERN_WARNING "eccsize %u is too large\n", eccsize); + goto fail; + } + + /* + * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), + * which is called by mtd_ooblayout_count_eccbytes(). + * Make sure they are properly initialized before calling + * mtd_ooblayout_count_eccbytes(). + * FIXME: we should probably rework the sequencing in nand_scan_tail() + * to avoid setting those fields twice. + */ + nand->ecc.steps = eccsteps; + nand->ecc.total = eccsteps * eccbytes; + if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { + printk(KERN_WARNING "invalid ecc layout\n"); + goto fail; + } + + nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); + nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); + if (!nbc->eccmask || !nbc->errloc) + goto fail; + /* + * compute and store the inverted ecc of an erased ecc block + */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) + goto fail; + + memset(erased_page, 0xff, eccsize); + memset(nbc->eccmask, 0, eccbytes); + encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + nbc->eccmask[i] ^= 0xff; + + if (!eccstrength) + nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); + + return nbc; +fail: + nand_bch_free(nbc); + return NULL; +} +EXPORT_SYMBOL(nand_bch_init); + +/** + * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources + * @nbc: NAND BCH control structure + */ +void nand_bch_free(struct nand_bch_control *nbc) +{ + if (nbc) { + free_bch(nbc->bch); + kfree(nbc->errloc); + kfree(nbc->eccmask); + kfree(nbc); + } +} +EXPORT_SYMBOL(nand_bch_free); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ivan Djelic "); +MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/drivers/mtd/nand/raw/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c new file mode 100644 index 000000000000..3630f0fe8fa4 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_ecc.c @@ -0,0 +1,531 @@ +/* + * This file contains an ECC algorithm that detects and corrects 1 bit + * errors in a 256 byte block of data. + * + * Copyright © 2008 Koninklijke Philips Electronics NV. + * Author: Frans Meulenbroeks + * + * Completely replaces the previous ECC implementation which was written by: + * Steven J. Hill (sjhill@realitydiluted.com) + * Thomas Gleixner (tglx@linutronix.de) + * + * Information on how this algorithm works and how it was developed + * can be found in Documentation/mtd/nand_ecc.txt + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. + * + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + */ + +/* + * The STANDALONE macro is useful when running the code outside the kernel + * e.g. when running the code in a testbed or a benchmark program. + * When STANDALONE is used, the module related macros are commented out + * as well as the linux include files. + * Instead a private definition of mtd_info is given to satisfy the compiler + * (the code does not use mtd_info, so the code does not care) + */ +#ifndef STANDALONE +#include +#include +#include +#include +#include +#include +#include +#else +#include +struct mtd_info; +#define EXPORT_SYMBOL(x) /* x */ + +#define MODULE_LICENSE(x) /* x */ +#define MODULE_AUTHOR(x) /* x */ +#define MODULE_DESCRIPTION(x) /* x */ + +#define pr_err printf +#endif + +/* + * invparity is a 256 byte table that contains the odd parity + * for each byte. So if the number of bits in a byte is even, + * the array element is 1, and when the number of bits is odd + * the array eleemnt is 0. + */ +static const char invparity[256] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 +}; + +/* + * bitsperbyte contains the number of bits per byte + * this is only used for testing and repairing parity + * (a precalculated value slightly improves performance) + */ +static const char bitsperbyte[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; + +/* + * addressbits is a lookup table to filter out the bits from the xor-ed + * ECC data that identify the faulty location. + * this is only used for repairing parity + * see the comments in nand_correct_data for more details + */ +static const char addressbits[256] = { + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f +}; + +/** + * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte + * block + * @buf: input buffer with raw data + * @eccsize: data bytes per ECC step (256 or 512) + * @code: output buffer with ECC + */ +void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, + unsigned char *code) +{ + int i; + const uint32_t *bp = (uint32_t *)buf; + /* 256 or 512 bytes/ecc */ + const uint32_t eccsize_mult = eccsize >> 8; + uint32_t cur; /* current value in buffer */ + /* rp0..rp15..rp17 are the various accumulated parities (per byte) */ + uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; + uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16; + uint32_t uninitialized_var(rp17); /* to make compiler happy */ + uint32_t par; /* the cumulative parity for all data */ + uint32_t tmppar; /* the cumulative parity for this iteration; + for rp12, rp14 and rp16 at the end of the + loop */ + + par = 0; + rp4 = 0; + rp6 = 0; + rp8 = 0; + rp10 = 0; + rp12 = 0; + rp14 = 0; + rp16 = 0; + + /* + * The loop is unrolled a number of times; + * This avoids if statements to decide on which rp value to update + * Also we process the data by longwords. + * Note: passing unaligned data might give a performance penalty. + * It is assumed that the buffers are aligned. + * tmppar is the cumulative sum of this iteration. + * needed for calculating rp12, rp14, rp16 and par + * also used as a performance improvement for rp6, rp8 and rp10 + */ + for (i = 0; i < eccsize_mult << 2; i++) { + cur = *bp++; + tmppar = cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= tmppar; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp8 ^= tmppar; + + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp10 ^= tmppar; + + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp8 ^= cur; + + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + + par ^= tmppar; + if ((i & 0x1) == 0) + rp12 ^= tmppar; + if ((i & 0x2) == 0) + rp14 ^= tmppar; + if (eccsize_mult == 2 && (i & 0x4) == 0) + rp16 ^= tmppar; + } + + /* + * handle the fact that we use longword operations + * we'll bring rp4..rp14..rp16 back to single byte entities by + * shifting and xoring first fold the upper and lower 16 bits, + * then the upper and lower 8 bits. + */ + rp4 ^= (rp4 >> 16); + rp4 ^= (rp4 >> 8); + rp4 &= 0xff; + rp6 ^= (rp6 >> 16); + rp6 ^= (rp6 >> 8); + rp6 &= 0xff; + rp8 ^= (rp8 >> 16); + rp8 ^= (rp8 >> 8); + rp8 &= 0xff; + rp10 ^= (rp10 >> 16); + rp10 ^= (rp10 >> 8); + rp10 &= 0xff; + rp12 ^= (rp12 >> 16); + rp12 ^= (rp12 >> 8); + rp12 &= 0xff; + rp14 ^= (rp14 >> 16); + rp14 ^= (rp14 >> 8); + rp14 &= 0xff; + if (eccsize_mult == 2) { + rp16 ^= (rp16 >> 16); + rp16 ^= (rp16 >> 8); + rp16 &= 0xff; + } + + /* + * we also need to calculate the row parity for rp0..rp3 + * This is present in par, because par is now + * rp3 rp3 rp2 rp2 in little endian and + * rp2 rp2 rp3 rp3 in big endian + * as well as + * rp1 rp0 rp1 rp0 in little endian and + * rp0 rp1 rp0 rp1 in big endian + * First calculate rp2 and rp3 + */ +#ifdef __BIG_ENDIAN + rp2 = (par >> 16); + rp2 ^= (rp2 >> 8); + rp2 &= 0xff; + rp3 = par & 0xffff; + rp3 ^= (rp3 >> 8); + rp3 &= 0xff; +#else + rp3 = (par >> 16); + rp3 ^= (rp3 >> 8); + rp3 &= 0xff; + rp2 = par & 0xffff; + rp2 ^= (rp2 >> 8); + rp2 &= 0xff; +#endif + + /* reduce par to 16 bits then calculate rp1 and rp0 */ + par ^= (par >> 16); +#ifdef __BIG_ENDIAN + rp0 = (par >> 8) & 0xff; + rp1 = (par & 0xff); +#else + rp1 = (par >> 8) & 0xff; + rp0 = (par & 0xff); +#endif + + /* finally reduce par to 8 bits */ + par ^= (par >> 8); + par &= 0xff; + + /* + * and calculate rp5..rp15..rp17 + * note that par = rp4 ^ rp5 and due to the commutative property + * of the ^ operator we can say: + * rp5 = (par ^ rp4); + * The & 0xff seems superfluous, but benchmarking learned that + * leaving it out gives slightly worse results. No idea why, probably + * it has to do with the way the pipeline in pentium is organized. + */ + rp5 = (par ^ rp4) & 0xff; + rp7 = (par ^ rp6) & 0xff; + rp9 = (par ^ rp8) & 0xff; + rp11 = (par ^ rp10) & 0xff; + rp13 = (par ^ rp12) & 0xff; + rp15 = (par ^ rp14) & 0xff; + if (eccsize_mult == 2) + rp17 = (par ^ rp16) & 0xff; + + /* + * Finally calculate the ECC bits. + * Again here it might seem that there are performance optimisations + * possible, but benchmarks showed that on the system this is developed + * the code below is the fastest + */ +#ifdef CONFIG_MTD_NAND_ECC_SMC + code[0] = + (invparity[rp7] << 7) | + (invparity[rp6] << 6) | + (invparity[rp5] << 5) | + (invparity[rp4] << 4) | + (invparity[rp3] << 3) | + (invparity[rp2] << 2) | + (invparity[rp1] << 1) | + (invparity[rp0]); + code[1] = + (invparity[rp15] << 7) | + (invparity[rp14] << 6) | + (invparity[rp13] << 5) | + (invparity[rp12] << 4) | + (invparity[rp11] << 3) | + (invparity[rp10] << 2) | + (invparity[rp9] << 1) | + (invparity[rp8]); +#else + code[1] = + (invparity[rp7] << 7) | + (invparity[rp6] << 6) | + (invparity[rp5] << 5) | + (invparity[rp4] << 4) | + (invparity[rp3] << 3) | + (invparity[rp2] << 2) | + (invparity[rp1] << 1) | + (invparity[rp0]); + code[0] = + (invparity[rp15] << 7) | + (invparity[rp14] << 6) | + (invparity[rp13] << 5) | + (invparity[rp12] << 4) | + (invparity[rp11] << 3) | + (invparity[rp10] << 2) | + (invparity[rp9] << 1) | + (invparity[rp8]); +#endif + if (eccsize_mult == 1) + code[2] = + (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + 3; + else + code[2] = + (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + (invparity[rp17] << 1) | + (invparity[rp16] << 0); +} +EXPORT_SYMBOL(__nand_calculate_ecc); + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte + * block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + __nand_calculate_ecc(buf, + mtd_to_nand(mtd)->ecc.size, code); + + return 0; +} +EXPORT_SYMBOL(nand_calculate_ecc); + +/** + * __nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * @eccsize: data bytes per ECC step (256 or 512) + * + * Detect and correct a 1 bit error for eccsize byte block + */ +int __nand_correct_data(unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc, + unsigned int eccsize) +{ + unsigned char b0, b1, b2, bit_addr; + unsigned int byte_addr; + /* 256 or 512 bytes/ecc */ + const uint32_t eccsize_mult = eccsize >> 8; + + /* + * b0 to b2 indicate which bit is faulty (if any) + * we might need the xor result more than once, + * so keep them in a local var + */ +#ifdef CONFIG_MTD_NAND_ECC_SMC + b0 = read_ecc[0] ^ calc_ecc[0]; + b1 = read_ecc[1] ^ calc_ecc[1]; +#else + b0 = read_ecc[1] ^ calc_ecc[1]; + b1 = read_ecc[0] ^ calc_ecc[0]; +#endif + b2 = read_ecc[2] ^ calc_ecc[2]; + + /* check if there are any bitfaults */ + + /* repeated if statements are slightly more efficient than switch ... */ + /* ordered in order of likelihood */ + + if ((b0 | b1 | b2) == 0) + return 0; /* no error */ + + if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) && + (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) && + ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) || + (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) { + /* single bit error */ + /* + * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty + * byte, cp 5/3/1 indicate the faulty bit. + * A lookup table (called addressbits) is used to filter + * the bits from the byte they are in. + * A marginal optimisation is possible by having three + * different lookup tables. + * One as we have now (for b0), one for b2 + * (that would avoid the >> 1), and one for b1 (with all values + * << 4). However it was felt that introducing two more tables + * hardly justify the gain. + * + * The b2 shift is there to get rid of the lowest two bits. + * We could also do addressbits[b2] >> 1 but for the + * performance it does not make any difference + */ + if (eccsize_mult == 1) + byte_addr = (addressbits[b1] << 4) + addressbits[b0]; + else + byte_addr = (addressbits[b2 & 0x3] << 8) + + (addressbits[b1] << 4) + addressbits[b0]; + bit_addr = addressbits[b2 >> 2]; + /* flip the bit */ + buf[byte_addr] ^= (1 << bit_addr); + return 1; + + } + /* count nr of bits; use table lookup, faster than calculating it */ + if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) + return 1; /* error in ECC data; no action needed */ + + pr_err("%s: uncorrectable ECC error\n", __func__); + return -EBADMSG; +} +EXPORT_SYMBOL(__nand_correct_data); + +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256/512 byte block + */ +int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + return __nand_correct_data(buf, read_ecc, calc_ecc, + mtd_to_nand(mtd)->ecc.size); +} +EXPORT_SYMBOL(nand_correct_data); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Frans Meulenbroeks "); +MODULE_DESCRIPTION("Generic NAND ECC support"); diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c new file mode 100644 index 000000000000..d542908a0ebb --- /dev/null +++ b/drivers/mtd/nand/raw/nand_hynix.c @@ -0,0 +1,676 @@ +/* + * Copyright (C) 2017 Free Electrons + * Copyright (C) 2017 NextThing Co + * + * Author: Boris Brezillon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#define NAND_HYNIX_CMD_SET_PARAMS 0x36 +#define NAND_HYNIX_CMD_APPLY_PARAMS 0x16 + +#define NAND_HYNIX_1XNM_RR_REPEAT 8 + +/** + * struct hynix_read_retry - read-retry data + * @nregs: number of register to set when applying a new read-retry mode + * @regs: register offsets (NAND chip dependent) + * @values: array of values to set in registers. The array size is equal to + * (nregs * nmodes) + */ +struct hynix_read_retry { + int nregs; + const u8 *regs; + u8 values[0]; +}; + +/** + * struct hynix_nand - private Hynix NAND struct + * @nand_technology: manufacturing process expressed in picometer + * @read_retry: read-retry information + */ +struct hynix_nand { + const struct hynix_read_retry *read_retry; +}; + +/** + * struct hynix_read_retry_otp - structure describing how the read-retry OTP + * area + * @nregs: number of hynix private registers to set before reading the reading + * the OTP area + * @regs: registers that should be configured + * @values: values that should be set in regs + * @page: the address to pass to the READ_PAGE command. Depends on the NAND + * chip + * @size: size of the read-retry OTP section + */ +struct hynix_read_retry_otp { + int nregs; + const u8 *regs; + const u8 *values; + int page; + int size; +}; + +static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) +{ + u8 jedecid[5] = { }; + int ret; + + ret = nand_readid_op(chip, 0x40, jedecid, sizeof(jedecid)); + if (ret) + return false; + + return !strncmp("JEDEC", jedecid, sizeof(jedecid)); +} + +static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (chip->exec_op) { + struct nand_op_instr instrs[] = { + NAND_OP_CMD(cmd, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + return nand_exec_op(chip, &op); + } + + chip->cmdfunc(mtd, cmd, -1, -1); + + return 0; +} + +static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u16 column = ((u16)addr << 8) | addr; + + chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); + chip->write_byte(mtd, val); + + return 0; +} + +static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct hynix_nand *hynix = nand_get_manufacturer_data(chip); + const u8 *values; + int i, ret; + + values = hynix->read_retry->values + + (retry_mode * hynix->read_retry->nregs); + + /* Enter 'Set Hynix Parameters' mode */ + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); + if (ret) + return ret; + + /* + * Configure the NAND in the requested read-retry mode. + * This is done by setting pre-defined values in internal NAND + * registers. + * + * The set of registers is NAND specific, and the values are either + * predefined or extracted from an OTP area on the NAND (values are + * probably tweaked at production in this case). + */ + for (i = 0; i < hynix->read_retry->nregs; i++) { + ret = hynix_nand_reg_write_op(chip, hynix->read_retry->regs[i], + values[i]); + if (ret) + return ret; + } + + /* Apply the new settings. */ + return hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); +} + +/** + * hynix_get_majority - get the value that is occurring the most in a given + * set of values + * @in: the array of values to test + * @repeat: the size of the in array + * @out: pointer used to store the output value + * + * This function implements the 'majority check' logic that is supposed to + * overcome the unreliability of MLC NANDs when reading the OTP area storing + * the read-retry parameters. + * + * It's based on a pretty simple assumption: if we repeat the same value + * several times and then take the one that is occurring the most, we should + * find the correct value. + * Let's hope this dummy algorithm prevents us from losing the read-retry + * parameters. + */ +static int hynix_get_majority(const u8 *in, int repeat, u8 *out) +{ + int i, j, half = repeat / 2; + + /* + * We only test the first half of the in array because we must ensure + * that the value is at least occurring repeat / 2 times. + * + * This loop is suboptimal since we may count the occurrences of the + * same value several time, but we are doing that on small sets, which + * makes it acceptable. + */ + for (i = 0; i < half; i++) { + int cnt = 0; + u8 val = in[i]; + + /* Count all values that are matching the one at index i. */ + for (j = i + 1; j < repeat; j++) { + if (in[j] == val) + cnt++; + } + + /* We found a value occurring more than repeat / 2. */ + if (cnt > half) { + *out = val; + return 0; + } + } + + return -EIO; +} + +static int hynix_read_rr_otp(struct nand_chip *chip, + const struct hynix_read_retry_otp *info, + void *buf) +{ + int i, ret; + + ret = nand_reset_op(chip); + if (ret) + return ret; + + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); + if (ret) + return ret; + + for (i = 0; i < info->nregs; i++) { + ret = hynix_nand_reg_write_op(chip, info->regs[i], + info->values[i]); + if (ret) + return ret; + } + + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); + if (ret) + return ret; + + /* Sequence to enter OTP mode? */ + ret = hynix_nand_cmd_op(chip, 0x17); + if (ret) + return ret; + + ret = hynix_nand_cmd_op(chip, 0x4); + if (ret) + return ret; + + ret = hynix_nand_cmd_op(chip, 0x19); + if (ret) + return ret; + + /* Now read the page */ + ret = nand_read_page_op(chip, info->page, 0, buf, info->size); + if (ret) + return ret; + + /* Put everything back to normal */ + ret = nand_reset_op(chip); + if (ret) + return ret; + + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); + if (ret) + return ret; + + ret = hynix_nand_reg_write_op(chip, 0x38, 0); + if (ret) + return ret; + + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); + if (ret) + return ret; + + return nand_read_page_op(chip, 0, 0, NULL, 0); +} + +#define NAND_HYNIX_1XNM_RR_COUNT_OFFS 0 +#define NAND_HYNIX_1XNM_RR_REG_COUNT_OFFS 8 +#define NAND_HYNIX_1XNM_RR_SET_OFFS(x, setsize, inv) \ + (16 + ((((x) * 2) + ((inv) ? 1 : 0)) * (setsize))) + +static int hynix_mlc_1xnm_rr_value(const u8 *buf, int nmodes, int nregs, + int mode, int reg, bool inv, u8 *val) +{ + u8 tmp[NAND_HYNIX_1XNM_RR_REPEAT]; + int val_offs = (mode * nregs) + reg; + int set_size = nmodes * nregs; + int i, ret; + + for (i = 0; i < NAND_HYNIX_1XNM_RR_REPEAT; i++) { + int set_offs = NAND_HYNIX_1XNM_RR_SET_OFFS(i, set_size, inv); + + tmp[i] = buf[val_offs + set_offs]; + } + + ret = hynix_get_majority(tmp, NAND_HYNIX_1XNM_RR_REPEAT, val); + if (ret) + return ret; + + if (inv) + *val = ~*val; + + return 0; +} + +static u8 hynix_1xnm_mlc_read_retry_regs[] = { + 0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf +}; + +static int hynix_mlc_1xnm_rr_init(struct nand_chip *chip, + const struct hynix_read_retry_otp *info) +{ + struct hynix_nand *hynix = nand_get_manufacturer_data(chip); + struct hynix_read_retry *rr = NULL; + int ret, i, j; + u8 nregs, nmodes; + u8 *buf; + + buf = kmalloc(info->size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hynix_read_rr_otp(chip, info, buf); + if (ret) + goto out; + + ret = hynix_get_majority(buf, NAND_HYNIX_1XNM_RR_REPEAT, + &nmodes); + if (ret) + goto out; + + ret = hynix_get_majority(buf + NAND_HYNIX_1XNM_RR_REPEAT, + NAND_HYNIX_1XNM_RR_REPEAT, + &nregs); + if (ret) + goto out; + + rr = kzalloc(sizeof(*rr) + (nregs * nmodes), GFP_KERNEL); + if (!rr) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < nmodes; i++) { + for (j = 0; j < nregs; j++) { + u8 *val = rr->values + (i * nregs); + + ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j, + false, val); + if (!ret) + continue; + + ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j, + true, val); + if (ret) + goto out; + } + } + + rr->nregs = nregs; + rr->regs = hynix_1xnm_mlc_read_retry_regs; + hynix->read_retry = rr; + chip->setup_read_retry = hynix_nand_setup_read_retry; + chip->read_retries = nmodes; + +out: + kfree(buf); + + if (ret) + kfree(rr); + + return ret; +} + +static const u8 hynix_mlc_1xnm_rr_otp_regs[] = { 0x38 }; +static const u8 hynix_mlc_1xnm_rr_otp_values[] = { 0x52 }; + +static const struct hynix_read_retry_otp hynix_mlc_1xnm_rr_otps[] = { + { + .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs), + .regs = hynix_mlc_1xnm_rr_otp_regs, + .values = hynix_mlc_1xnm_rr_otp_values, + .page = 0x21f, + .size = 784 + }, + { + .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs), + .regs = hynix_mlc_1xnm_rr_otp_regs, + .values = hynix_mlc_1xnm_rr_otp_values, + .page = 0x200, + .size = 528, + }, +}; + +static int hynix_nand_rr_init(struct nand_chip *chip) +{ + int i, ret = 0; + bool valid_jedecid; + + valid_jedecid = hynix_nand_has_valid_jedecid(chip); + + /* + * We only support read-retry for 1xnm NANDs, and those NANDs all + * expose a valid JEDEC ID. + */ + if (valid_jedecid) { + u8 nand_tech = chip->id.data[5] >> 4; + + /* 1xnm technology */ + if (nand_tech == 4) { + for (i = 0; i < ARRAY_SIZE(hynix_mlc_1xnm_rr_otps); + i++) { + /* + * FIXME: Hynix recommend to copy the + * read-retry OTP area into a normal page. + */ + ret = hynix_mlc_1xnm_rr_init(chip, + hynix_mlc_1xnm_rr_otps); + if (!ret) + break; + } + } + } + + if (ret) + pr_warn("failed to initialize read-retry infrastructure"); + + return 0; +} + +static void hynix_nand_extract_oobsize(struct nand_chip *chip, + bool valid_jedecid) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u8 oobsize; + + oobsize = ((chip->id.data[3] >> 2) & 0x3) | + ((chip->id.data[3] >> 4) & 0x4); + + if (valid_jedecid) { + switch (oobsize) { + case 0: + mtd->oobsize = 2048; + break; + case 1: + mtd->oobsize = 1664; + break; + case 2: + mtd->oobsize = 1024; + break; + case 3: + mtd->oobsize = 640; + break; + default: + /* + * We should never reach this case, but if that + * happens, this probably means Hynix decided to use + * a different extended ID format, and we should find + * a way to support it. + */ + WARN(1, "Invalid OOB size"); + break; + } + } else { + switch (oobsize) { + case 0: + mtd->oobsize = 128; + break; + case 1: + mtd->oobsize = 224; + break; + case 2: + mtd->oobsize = 448; + break; + case 3: + mtd->oobsize = 64; + break; + case 4: + mtd->oobsize = 32; + break; + case 5: + mtd->oobsize = 16; + break; + case 6: + mtd->oobsize = 640; + break; + default: + /* + * We should never reach this case, but if that + * happens, this probably means Hynix decided to use + * a different extended ID format, and we should find + * a way to support it. + */ + WARN(1, "Invalid OOB size"); + break; + } + } +} + +static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip, + bool valid_jedecid) +{ + u8 ecc_level = (chip->id.data[4] >> 4) & 0x7; + + if (valid_jedecid) { + /* Reference: H27UCG8T2E datasheet */ + chip->ecc_step_ds = 1024; + + switch (ecc_level) { + case 0: + chip->ecc_step_ds = 0; + chip->ecc_strength_ds = 0; + break; + case 1: + chip->ecc_strength_ds = 4; + break; + case 2: + chip->ecc_strength_ds = 24; + break; + case 3: + chip->ecc_strength_ds = 32; + break; + case 4: + chip->ecc_strength_ds = 40; + break; + case 5: + chip->ecc_strength_ds = 50; + break; + case 6: + chip->ecc_strength_ds = 60; + break; + default: + /* + * We should never reach this case, but if that + * happens, this probably means Hynix decided to use + * a different extended ID format, and we should find + * a way to support it. + */ + WARN(1, "Invalid ECC requirements"); + } + } else { + /* + * The ECC requirements field meaning depends on the + * NAND technology. + */ + u8 nand_tech = chip->id.data[5] & 0x7; + + if (nand_tech < 3) { + /* > 26nm, reference: H27UBG8T2A datasheet */ + if (ecc_level < 5) { + chip->ecc_step_ds = 512; + chip->ecc_strength_ds = 1 << ecc_level; + } else if (ecc_level < 7) { + if (ecc_level == 5) + chip->ecc_step_ds = 2048; + else + chip->ecc_step_ds = 1024; + chip->ecc_strength_ds = 24; + } else { + /* + * We should never reach this case, but if that + * happens, this probably means Hynix decided + * to use a different extended ID format, and + * we should find a way to support it. + */ + WARN(1, "Invalid ECC requirements"); + } + } else { + /* <= 26nm, reference: H27UBG8T2B datasheet */ + if (!ecc_level) { + chip->ecc_step_ds = 0; + chip->ecc_strength_ds = 0; + } else if (ecc_level < 5) { + chip->ecc_step_ds = 512; + chip->ecc_strength_ds = 1 << (ecc_level - 1); + } else { + chip->ecc_step_ds = 1024; + chip->ecc_strength_ds = 24 + + (8 * (ecc_level - 5)); + } + } + } +} + +static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip, + bool valid_jedecid) +{ + u8 nand_tech; + + /* We need scrambling on all TLC NANDs*/ + if (chip->bits_per_cell > 2) + chip->options |= NAND_NEED_SCRAMBLING; + + /* And on MLC NANDs with sub-3xnm process */ + if (valid_jedecid) { + nand_tech = chip->id.data[5] >> 4; + + /* < 3xnm */ + if (nand_tech > 0) + chip->options |= NAND_NEED_SCRAMBLING; + } else { + nand_tech = chip->id.data[5] & 0x7; + + /* < 32nm */ + if (nand_tech > 2) + chip->options |= NAND_NEED_SCRAMBLING; + } +} + +static void hynix_nand_decode_id(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + bool valid_jedecid; + u8 tmp; + + /* + * Exclude all SLC NANDs from this advanced detection scheme. + * According to the ranges defined in several datasheets, it might + * appear that even SLC NANDs could fall in this extended ID scheme. + * If that the case rework the test to let SLC NANDs go through the + * detection process. + */ + if (chip->id.len < 6 || nand_is_slc(chip)) { + nand_decode_ext_id(chip); + return; + } + + /* Extract pagesize */ + mtd->writesize = 2048 << (chip->id.data[3] & 0x03); + + tmp = (chip->id.data[3] >> 4) & 0x3; + /* + * When bit7 is set that means we start counting at 1MiB, otherwise + * we start counting at 128KiB and shift this value the content of + * ID[3][4:5]. + * The only exception is when ID[3][4:5] == 3 and ID[3][7] == 0, in + * this case the erasesize is set to 768KiB. + */ + if (chip->id.data[3] & 0x80) + mtd->erasesize = SZ_1M << tmp; + else if (tmp == 3) + mtd->erasesize = SZ_512K + SZ_256K; + else + mtd->erasesize = SZ_128K << tmp; + + /* + * Modern Toggle DDR NANDs have a valid JEDECID even though they are + * not exposing a valid JEDEC parameter table. + * These NANDs use a different NAND ID scheme. + */ + valid_jedecid = hynix_nand_has_valid_jedecid(chip); + + hynix_nand_extract_oobsize(chip, valid_jedecid); + hynix_nand_extract_ecc_requirements(chip, valid_jedecid); + hynix_nand_extract_scrambling_requirements(chip, valid_jedecid); +} + +static void hynix_nand_cleanup(struct nand_chip *chip) +{ + struct hynix_nand *hynix = nand_get_manufacturer_data(chip); + + if (!hynix) + return; + + kfree(hynix->read_retry); + kfree(hynix); + nand_set_manufacturer_data(chip, NULL); +} + +static int hynix_nand_init(struct nand_chip *chip) +{ + struct hynix_nand *hynix; + int ret; + + if (!nand_is_slc(chip)) + chip->bbt_options |= NAND_BBT_SCANLASTPAGE; + else + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; + + hynix = kzalloc(sizeof(*hynix), GFP_KERNEL); + if (!hynix) + return -ENOMEM; + + nand_set_manufacturer_data(chip, hynix); + + ret = hynix_nand_rr_init(chip); + if (ret) + hynix_nand_cleanup(chip); + + return ret; +} + +const struct nand_manufacturer_ops hynix_nand_manuf_ops = { + .detect = hynix_nand_decode_id, + .init = hynix_nand_init, + .cleanup = hynix_nand_cleanup, +}; diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c new file mode 100644 index 000000000000..5423c3bb388e --- /dev/null +++ b/drivers/mtd/nand/raw/nand_ids.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include + +#define LP_OPTIONS 0 +#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) + +#define SP_OPTIONS NAND_NEED_READRDY +#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16) + +/* + * The chip ID list: + * name, device ID, page size, chip size in MiB, eraseblock size, options + * + * If page size and eraseblock size are 0, the sizes are taken from the + * extended chip ID. + */ +struct nand_flash_dev nand_flash_ids[] = { + /* + * Some incompatible NAND chips share device ID's and so must be + * listed by full ID. We list them first so that we can easily identify + * the most specific match. + */ + {"TC58NVG0S3E 1G 3.3V 8-bit", + { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} }, + SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), + 2 }, + {"TC58NVG2S0F 4G 3.3V 8-bit", + { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, + SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, + {"TC58NVG2S0H 4G 3.3V 8-bit", + { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} }, + SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) }, + {"TC58NVG3S0F 8G 3.3V 8-bit", + { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, + SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, + {"TC58NVG5D2 32G 3.3V 8-bit", + { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} }, + SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"TC58NVG6D2 64G 3.3V 8-bit", + { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, + SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"SDTNRGAMA 64G 3.3V 8-bit", + { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} }, + SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, + {"H27UCG8T2ATR-BC 64G 3.3V 8-bit", + { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, + SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640, + NAND_ECC_INFO(40, SZ_1K), 4 }, + + LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS), + + LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS), + + /* + * These are the new chips with large page size. Their page size and + * eraseblock size are determined from the extended ID bytes. + */ + + /* 512 Megabit */ + EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16), + + /* 1 Gigabit */ + EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16), + + /* 2 Gigabit */ + EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16), + + /* 4 Gigabit */ + EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16), + + /* 8 Gigabit */ + EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16), + + /* 16 Gigabit */ + EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16), + + /* 32 Gigabit */ + EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16), + + /* 64 Gigabit */ + EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16), + + /* 128 Gigabit */ + EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16), + + /* 256 Gigabit */ + EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16), + + /* 512 Gigabit */ + EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16), + + {NULL} +}; + +/* Manufacturer IDs */ +static const struct nand_manufacturer nand_manufacturers[] = { + {NAND_MFR_TOSHIBA, "Toshiba", &toshiba_nand_manuf_ops}, + {NAND_MFR_ESMT, "ESMT"}, + {NAND_MFR_SAMSUNG, "Samsung", &samsung_nand_manuf_ops}, + {NAND_MFR_FUJITSU, "Fujitsu"}, + {NAND_MFR_NATIONAL, "National"}, + {NAND_MFR_RENESAS, "Renesas"}, + {NAND_MFR_STMICRO, "ST Micro"}, + {NAND_MFR_HYNIX, "Hynix", &hynix_nand_manuf_ops}, + {NAND_MFR_MICRON, "Micron", µn_nand_manuf_ops}, + {NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops}, + {NAND_MFR_MACRONIX, "Macronix", ¯onix_nand_manuf_ops}, + {NAND_MFR_EON, "Eon"}, + {NAND_MFR_SANDISK, "SanDisk"}, + {NAND_MFR_INTEL, "Intel"}, + {NAND_MFR_ATO, "ATO"}, + {NAND_MFR_WINBOND, "Winbond"}, +}; + +/** + * nand_get_manufacturer - Get manufacturer information from the manufacturer + * ID + * @id: manufacturer ID + * + * Returns a pointer a nand_manufacturer object if the manufacturer is defined + * in the NAND manufacturers database, NULL otherwise. + */ +const struct nand_manufacturer *nand_get_manufacturer(u8 id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(nand_manufacturers); i++) + if (nand_manufacturers[i].id == id) + return &nand_manufacturers[i]; + + return NULL; +} diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c new file mode 100644 index 000000000000..d290ff2a6d2f --- /dev/null +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Free Electrons + * Copyright (C) 2017 NextThing Co + * + * Author: Boris Brezillon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +static int macronix_nand_init(struct nand_chip *chip) +{ + if (nand_is_slc(chip)) + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; + + return 0; +} + +const struct nand_manufacturer_ops macronix_nand_manuf_ops = { + .init = macronix_nand_init, +}; diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c new file mode 100644 index 000000000000..02e109ae73f1 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2017 Free Electrons + * Copyright (C) 2017 NextThing Co + * + * Author: Boris Brezillon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +/* + * Special Micron status bit that indicates when the block has been + * corrected by on-die ECC and should be rewritten + */ +#define NAND_STATUS_WRITE_RECOMMENDED BIT(3) + +struct nand_onfi_vendor_micron { + u8 two_plane_read; + u8 read_cache; + u8 read_unique_id; + u8 dq_imped; + u8 dq_imped_num_settings; + u8 dq_imped_feat_addr; + u8 rb_pulldown_strength; + u8 rb_pulldown_strength_feat_addr; + u8 rb_pulldown_strength_num_settings; + u8 otp_mode; + u8 otp_page_start; + u8 otp_data_prot_addr; + u8 otp_num_pages; + u8 otp_feat_addr; + u8 read_retry_options; + u8 reserved[72]; + u8 param_revision; +} __packed; + +static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; + + return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, + feature); +} + +/* + * Configure chip properties from Micron vendor-specific ONFI table + */ +static int micron_nand_onfi_init(struct nand_chip *chip) +{ + struct nand_onfi_params *p = &chip->onfi_params; + struct nand_onfi_vendor_micron *micron = (void *)p->vendor; + + if (!chip->onfi_version) + return 0; + + if (le16_to_cpu(p->vendor_revision) < 1) + return 0; + + chip->read_retries = micron->read_retry_options; + chip->setup_read_retry = micron_nand_setup_read_retry; + + return 0; +} + +static int micron_nand_on_die_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section >= 4) + return -ERANGE; + + oobregion->offset = (section * 16) + 8; + oobregion->length = 8; + + return 0; +} + +static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section >= 4) + return -ERANGE; + + oobregion->offset = (section * 16) + 2; + oobregion->length = 6; + + return 0; +} + +static const struct mtd_ooblayout_ops micron_nand_on_die_ooblayout_ops = { + .ecc = micron_nand_on_die_ooblayout_ecc, + .free = micron_nand_on_die_ooblayout_free, +}; + +static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; + + if (enable) + feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN; + + return chip->onfi_set_features(nand_to_mtd(chip), chip, + ONFI_FEATURE_ON_DIE_ECC, feature); +} + +static int +micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, + int page) +{ + u8 status; + int ret, max_bitflips = 0; + + ret = micron_nand_on_die_ecc_setup(chip, true); + if (ret) + return ret; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + goto out; + + ret = nand_status_op(chip, &status); + if (ret) + goto out; + + ret = nand_exit_status_op(chip); + if (ret) + goto out; + + if (status & NAND_STATUS_FAIL) + mtd->ecc_stats.failed++; + + /* + * The internal ECC doesn't tell us the number of bitflips + * that have been corrected, but tells us if it recommends to + * rewrite the block. If it's the case, then we pretend we had + * a number of bitflips equal to the ECC strength, which will + * hint the NAND core to rewrite the block. + */ + else if (status & NAND_STATUS_WRITE_RECOMMENDED) + max_bitflips = chip->ecc.strength; + + ret = nand_read_data_op(chip, buf, mtd->writesize, false); + if (!ret && oob_required) + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, + false); + +out: + micron_nand_on_die_ecc_setup(chip, false); + + return ret ? ret : max_bitflips; +} + +static int +micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int ret; + + ret = micron_nand_on_die_ecc_setup(chip, true); + if (ret) + return ret; + + ret = nand_write_page_raw(mtd, chip, buf, oob_required, page); + micron_nand_on_die_ecc_setup(chip, false); + + return ret; +} + +enum { + /* The NAND flash doesn't support on-die ECC */ + MICRON_ON_DIE_UNSUPPORTED, + + /* + * The NAND flash supports on-die ECC and it can be + * enabled/disabled by a set features command. + */ + MICRON_ON_DIE_SUPPORTED, + + /* + * The NAND flash supports on-die ECC, and it cannot be + * disabled. + */ + MICRON_ON_DIE_MANDATORY, +}; + +/* + * Try to detect if the NAND support on-die ECC. To do this, we enable + * the feature, and read back if it has been enabled as expected. We + * also check if it can be disabled, because some Micron NANDs do not + * allow disabling the on-die ECC and we don't support such NANDs for + * now. + * + * This function also has the side effect of disabling on-die ECC if + * it had been left enabled by the firmware/bootloader. + */ +static int micron_supports_on_die_ecc(struct nand_chip *chip) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; + int ret; + + if (chip->onfi_version == 0) + return MICRON_ON_DIE_UNSUPPORTED; + + if (chip->bits_per_cell != 1) + return MICRON_ON_DIE_UNSUPPORTED; + + ret = micron_nand_on_die_ecc_setup(chip, true); + if (ret) + return MICRON_ON_DIE_UNSUPPORTED; + + chip->onfi_get_features(nand_to_mtd(chip), chip, + ONFI_FEATURE_ON_DIE_ECC, feature); + if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0) + return MICRON_ON_DIE_UNSUPPORTED; + + ret = micron_nand_on_die_ecc_setup(chip, false); + if (ret) + return MICRON_ON_DIE_UNSUPPORTED; + + chip->onfi_get_features(nand_to_mtd(chip), chip, + ONFI_FEATURE_ON_DIE_ECC, feature); + if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) + return MICRON_ON_DIE_MANDATORY; + + /* + * Some Micron NANDs have an on-die ECC of 4/512, some other + * 8/512. We only support the former. + */ + if (chip->onfi_params.ecc_bits != 4) + return MICRON_ON_DIE_UNSUPPORTED; + + return MICRON_ON_DIE_SUPPORTED; +} + +static int micron_nand_init(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ondie; + int ret; + + ret = micron_nand_onfi_init(chip); + if (ret) + return ret; + + if (mtd->writesize == 2048) + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; + + ondie = micron_supports_on_die_ecc(chip); + + if (ondie == MICRON_ON_DIE_MANDATORY) { + pr_err("On-die ECC forcefully enabled, not supported\n"); + return -EINVAL; + } + + if (chip->ecc.mode == NAND_ECC_ON_DIE) { + if (ondie == MICRON_ON_DIE_UNSUPPORTED) { + pr_err("On-die ECC selected but not supported\n"); + return -EINVAL; + } + + chip->ecc.bytes = 8; + chip->ecc.size = 512; + chip->ecc.strength = 4; + chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.read_page = micron_nand_read_page_on_die_ecc; + chip->ecc.write_page = micron_nand_write_page_on_die_ecc; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + + mtd_set_ooblayout(mtd, µn_nand_on_die_ooblayout_ops); + } + + return 0; +} + +const struct nand_manufacturer_ops micron_nand_manuf_ops = { + .init = micron_nand_init, +}; diff --git a/drivers/mtd/nand/raw/nand_samsung.c b/drivers/mtd/nand/raw/nand_samsung.c new file mode 100644 index 000000000000..ef022f62f74c --- /dev/null +++ b/drivers/mtd/nand/raw/nand_samsung.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 Free Electrons + * Copyright (C) 2017 NextThing Co + * + * Author: Boris Brezillon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +static void samsung_nand_decode_id(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + /* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */ + if (chip->id.len == 6 && !nand_is_slc(chip) && + chip->id.data[5] != 0x00) { + u8 extid = chip->id.data[3]; + + /* Get pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + + extid >>= 2; + + /* Get oobsize */ + switch (((extid >> 2) & 0x4) | (extid & 0x3)) { + case 1: + mtd->oobsize = 128; + break; + case 2: + mtd->oobsize = 218; + break; + case 3: + mtd->oobsize = 400; + break; + case 4: + mtd->oobsize = 436; + break; + case 5: + mtd->oobsize = 512; + break; + case 6: + mtd->oobsize = 640; + break; + default: + /* + * We should never reach this case, but if that + * happens, this probably means Samsung decided to use + * a different extended ID format, and we should find + * a way to support it. + */ + WARN(1, "Invalid OOB size value"); + break; + } + + /* Get blocksize */ + extid >>= 2; + mtd->erasesize = (128 * 1024) << + (((extid >> 1) & 0x04) | (extid & 0x03)); + + /* Extract ECC requirements from 5th id byte*/ + extid = (chip->id.data[4] >> 4) & 0x07; + if (extid < 5) { + chip->ecc_step_ds = 512; + chip->ecc_strength_ds = 1 << extid; + } else { + chip->ecc_step_ds = 1024; + switch (extid) { + case 5: + chip->ecc_strength_ds = 24; + break; + case 6: + chip->ecc_strength_ds = 40; + break; + case 7: + chip->ecc_strength_ds = 60; + break; + default: + WARN(1, "Could not decode ECC info"); + chip->ecc_step_ds = 0; + } + } + } else { + nand_decode_ext_id(chip); + + if (nand_is_slc(chip)) { + switch (chip->id.data[1]) { + /* K9F4G08U0D-S[I|C]B0(T00) */ + case 0xDC: + chip->ecc_step_ds = 512; + chip->ecc_strength_ds = 1; + break; + + /* K9F1G08U0E 21nm chips do not support subpage write */ + case 0xF1: + if (chip->id.len > 4 && + (chip->id.data[4] & GENMASK(1, 0)) == 0x1) + chip->options |= NAND_NO_SUBPAGE_WRITE; + break; + default: + break; + } + } + } +} + +static int samsung_nand_init(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (mtd->writesize > 512) + chip->options |= NAND_SAMSUNG_LP_OPTIONS; + + if (!nand_is_slc(chip)) + chip->bbt_options |= NAND_BBT_SCANLASTPAGE; + else + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; + + return 0; +} + +const struct nand_manufacturer_ops samsung_nand_manuf_ops = { + .detect = samsung_nand_decode_id, + .init = samsung_nand_init, +}; diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c new file mode 100644 index 000000000000..9400d039ddbd --- /dev/null +++ b/drivers/mtd/nand/raw/nand_timings.c @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2014 Free Electrons + * + * Author: Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include + +static const struct nand_data_interface onfi_sdr_timings[] = { + /* Mode 0 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 20000, + .tALS_min = 50000, + .tAR_min = 25000, + .tCEA_max = 100000, + .tCEH_min = 20000, + .tCH_min = 20000, + .tCHZ_max = 100000, + .tCLH_min = 20000, + .tCLR_min = 20000, + .tCLS_min = 50000, + .tCOH_min = 0, + .tCS_min = 70000, + .tDH_min = 20000, + .tDS_min = 40000, + .tFEAT_max = 1000000, + .tIR_min = 10000, + .tITC_max = 1000000, + .tRC_min = 100000, + .tREA_max = 40000, + .tREH_min = 30000, + .tRHOH_min = 0, + .tRHW_min = 200000, + .tRHZ_max = 200000, + .tRLOH_min = 0, + .tRP_min = 50000, + .tRR_min = 40000, + .tRST_max = 250000000000ULL, + .tWB_max = 200000, + .tWC_min = 100000, + .tWH_min = 30000, + .tWHR_min = 120000, + .tWP_min = 50000, + .tWW_min = 100000, + }, + }, + /* Mode 1 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 10000, + .tALS_min = 25000, + .tAR_min = 10000, + .tCEA_max = 45000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 25000, + .tCOH_min = 15000, + .tCS_min = 35000, + .tDH_min = 10000, + .tDS_min = 20000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 50000, + .tREA_max = 30000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 25000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 45000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 25000, + .tWW_min = 100000, + }, + }, + /* Mode 2 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 10000, + .tALS_min = 15000, + .tAR_min = 10000, + .tCEA_max = 30000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 15000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 15000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 35000, + .tREA_max = 25000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tRP_min = 17000, + .tWC_min = 35000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 17000, + .tWW_min = 100000, + }, + }, + /* Mode 3 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 50000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 30000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 15000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 30000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 15000, + .tWW_min = 100000, + }, + }, + /* Mode 4 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 20000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 25000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 12000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 25000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 12000, + .tWW_min = 100000, + }, + }, + /* Mode 5 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 15000, + .tDH_min = 5000, + .tDS_min = 7000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 20000, + .tREA_max = 16000, + .tREH_min = 7000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 10000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 20000, + .tWH_min = 7000, + .tWHR_min = 80000, + .tWP_min = 10000, + .tWW_min = 100000, + }, + }, +}; + +/** + * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND + * timings according to the given ONFI timing mode + * @mode: ONFI timing mode + */ +const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode) +{ + if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings)) + return ERR_PTR(-EINVAL); + + return &onfi_sdr_timings[mode].timings.sdr; +} +EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings); + +/** + * onfi_fill_data_interface - [NAND Interface] Initialize a data interface from + * given ONFI mode + * @mode: The ONFI timing mode + */ +int onfi_fill_data_interface(struct nand_chip *chip, + enum nand_data_interface_type type, + int timing_mode) +{ + struct nand_data_interface *iface = &chip->data_interface; + + if (type != NAND_SDR_IFACE) + return -EINVAL; + + if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings)) + return -EINVAL; + + *iface = onfi_sdr_timings[timing_mode]; + + /* + * Initialize timings that cannot be deduced from timing mode: + * tR, tPROG, tCCS, ... + * These information are part of the ONFI parameter page. + */ + if (chip->onfi_version) { + struct nand_onfi_params *params = &chip->onfi_params; + struct nand_sdr_timings *timings = &iface->timings.sdr; + + /* microseconds -> picoseconds */ + timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog); + timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers); + timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r); + + /* nanoseconds -> picoseconds */ + timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); + } + + return 0; +} +EXPORT_SYMBOL(onfi_fill_data_interface); diff --git a/drivers/mtd/nand/raw/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c new file mode 100644 index 000000000000..ab43f027cd23 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_toshiba.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 Free Electrons + * Copyright (C) 2017 NextThing Co + * + * Author: Boris Brezillon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +static void toshiba_nand_decode_id(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + nand_decode_ext_id(chip); + + /* + * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per + * 512B page. For Toshiba SLC, we decode the 5th/6th byte as + * follows: + * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, + * 110b -> 24nm + * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC + */ + if (chip->id.len >= 6 && nand_is_slc(chip) && + (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ && + !(chip->id.data[4] & 0x80) /* !BENAND */) + mtd->oobsize = 32 * mtd->writesize >> 9; + + /* + * Extract ECC requirements from 6th id byte. + * For Toshiba SLC, ecc requrements are as follows: + * - 43nm: 1 bit ECC for each 512Byte is required. + * - 32nm: 4 bit ECC for each 512Byte is required. + * - 24nm: 8 bit ECC for each 512Byte is required. + */ + if (chip->id.len >= 6 && nand_is_slc(chip)) { + chip->ecc_step_ds = 512; + switch (chip->id.data[5] & 0x7) { + case 0x4: + chip->ecc_strength_ds = 1; + break; + case 0x5: + chip->ecc_strength_ds = 4; + break; + case 0x6: + chip->ecc_strength_ds = 8; + break; + default: + WARN(1, "Could not get ECC info"); + chip->ecc_step_ds = 0; + break; + } + } +} + +static int toshiba_nand_init(struct nand_chip *chip) +{ + if (nand_is_slc(chip)) + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; + + return 0; +} + +const struct nand_manufacturer_ops toshiba_nand_manuf_ops = { + .detect = toshiba_nand_decode_id, + .init = toshiba_nand_init, +}; diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c new file mode 100644 index 000000000000..44322a363ba5 --- /dev/null +++ b/drivers/mtd/nand/raw/nandsim.c @@ -0,0 +1,2392 @@ +/* + * NAND flash simulator. + * + * Author: Artem B. Bityuckiy , + * + * Copyright (C) 2004 Nokia Corporation + * + * Note: NS means "NAND Simulator". + * Note: Input means input TO flash chip, output means output FROM chip. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Default simulator parameters values */ +#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE) +#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98 +#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39 +#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */ +#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */ +#endif + +#ifndef CONFIG_NANDSIM_ACCESS_DELAY +#define CONFIG_NANDSIM_ACCESS_DELAY 25 +#endif +#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY +#define CONFIG_NANDSIM_PROGRAMM_DELAY 200 +#endif +#ifndef CONFIG_NANDSIM_ERASE_DELAY +#define CONFIG_NANDSIM_ERASE_DELAY 2 +#endif +#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE +#define CONFIG_NANDSIM_OUTPUT_CYCLE 40 +#endif +#ifndef CONFIG_NANDSIM_INPUT_CYCLE +#define CONFIG_NANDSIM_INPUT_CYCLE 50 +#endif +#ifndef CONFIG_NANDSIM_BUS_WIDTH +#define CONFIG_NANDSIM_BUS_WIDTH 8 +#endif +#ifndef CONFIG_NANDSIM_DO_DELAYS +#define CONFIG_NANDSIM_DO_DELAYS 0 +#endif +#ifndef CONFIG_NANDSIM_LOG +#define CONFIG_NANDSIM_LOG 0 +#endif +#ifndef CONFIG_NANDSIM_DBG +#define CONFIG_NANDSIM_DBG 0 +#endif +#ifndef CONFIG_NANDSIM_MAX_PARTS +#define CONFIG_NANDSIM_MAX_PARTS 32 +#endif + +static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY; +static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY; +static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY; +static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE; +static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE; +static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; +static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; +static uint log = CONFIG_NANDSIM_LOG; +static uint dbg = CONFIG_NANDSIM_DBG; +static unsigned long parts[CONFIG_NANDSIM_MAX_PARTS]; +static unsigned int parts_num; +static char *badblocks = NULL; +static char *weakblocks = NULL; +static char *weakpages = NULL; +static unsigned int bitflips = 0; +static char *gravepages = NULL; +static unsigned int overridesize = 0; +static char *cache_file = NULL; +static unsigned int bbt; +static unsigned int bch; +static u_char id_bytes[8] = { + [0] = CONFIG_NANDSIM_FIRST_ID_BYTE, + [1] = CONFIG_NANDSIM_SECOND_ID_BYTE, + [2] = CONFIG_NANDSIM_THIRD_ID_BYTE, + [3] = CONFIG_NANDSIM_FOURTH_ID_BYTE, + [4 ... 7] = 0xFF, +}; + +module_param_array(id_bytes, byte, NULL, 0400); +module_param_named(first_id_byte, id_bytes[0], byte, 0400); +module_param_named(second_id_byte, id_bytes[1], byte, 0400); +module_param_named(third_id_byte, id_bytes[2], byte, 0400); +module_param_named(fourth_id_byte, id_bytes[3], byte, 0400); +module_param(access_delay, uint, 0400); +module_param(programm_delay, uint, 0400); +module_param(erase_delay, uint, 0400); +module_param(output_cycle, uint, 0400); +module_param(input_cycle, uint, 0400); +module_param(bus_width, uint, 0400); +module_param(do_delays, uint, 0400); +module_param(log, uint, 0400); +module_param(dbg, uint, 0400); +module_param_array(parts, ulong, &parts_num, 0400); +module_param(badblocks, charp, 0400); +module_param(weakblocks, charp, 0400); +module_param(weakpages, charp, 0400); +module_param(bitflips, uint, 0400); +module_param(gravepages, charp, 0400); +module_param(overridesize, uint, 0400); +module_param(cache_file, charp, 0400); +module_param(bbt, uint, 0400); +module_param(bch, uint, 0400); + +MODULE_PARM_DESC(id_bytes, "The ID bytes returned by NAND Flash 'read ID' command"); +MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID) (obsolete)"); +MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID) (obsolete)"); +MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command (obsolete)"); +MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command (obsolete)"); +MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)"); +MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); +MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); +MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanoseconds)"); +MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanoseconds)"); +MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); +MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); +MODULE_PARM_DESC(log, "Perform logging if not zero"); +MODULE_PARM_DESC(dbg, "Output debug information if not zero"); +MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas"); +/* Page and erase block positions for the following parameters are independent of any partitions */ +MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas"); +MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]" + " separated by commas e.g. 113:2 means eb 113" + " can be erased only twice before failing"); +MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]" + " separated by commas e.g. 1401:2 means page 1401" + " can be written only twice before failing"); +MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)"); +MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" + " separated by commas e.g. 1401:2 means page 1401" + " can be read only twice before failing"); +MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " + "The size is specified in erase blocks and as the exponent of a power of two" + " e.g. 5 means a size of 32 erase blocks"); +MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); +MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area"); +MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " + "be correctable in 512-byte blocks"); + +/* The largest possible page size */ +#define NS_LARGEST_PAGE_SIZE 4096 + +/* The prefix for simulator output */ +#define NS_OUTPUT_PREFIX "[nandsim]" + +/* Simulator's output macros (logging, debugging, warning, error) */ +#define NS_LOG(args...) \ + do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0) +#define NS_DBG(args...) \ + do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) +#define NS_WARN(args...) \ + do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0) +#define NS_ERR(args...) \ + do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0) +#define NS_INFO(args...) \ + do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0) + +/* Busy-wait delay macros (microseconds, milliseconds) */ +#define NS_UDELAY(us) \ + do { if (do_delays) udelay(us); } while(0) +#define NS_MDELAY(us) \ + do { if (do_delays) mdelay(us); } while(0) + +/* Is the nandsim structure initialized ? */ +#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0) + +/* Good operation completion status */ +#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0))) + +/* Operation failed completion status */ +#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) + +/* Calculate the page offset in flash RAM image by (row, column) address */ +#define NS_RAW_OFFSET(ns) \ + (((ns)->regs.row * (ns)->geom.pgszoob) + (ns)->regs.column) + +/* Calculate the OOB offset in flash RAM image by (row, column) address */ +#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) + +/* After a command is input, the simulator goes to one of the following states */ +#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */ +#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */ +#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */ +#define STATE_CMD_PAGEPROG 0x00000004 /* start page program */ +#define STATE_CMD_READOOB 0x00000005 /* read OOB area */ +#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */ +#define STATE_CMD_STATUS 0x00000007 /* read status */ +#define STATE_CMD_SEQIN 0x00000009 /* sequential data input */ +#define STATE_CMD_READID 0x0000000A /* read ID */ +#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */ +#define STATE_CMD_RESET 0x0000000C /* reset */ +#define STATE_CMD_RNDOUT 0x0000000D /* random output command */ +#define STATE_CMD_RNDOUTSTART 0x0000000E /* random output start command */ +#define STATE_CMD_MASK 0x0000000F /* command states mask */ + +/* After an address is input, the simulator goes to one of these states */ +#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */ +#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */ +#define STATE_ADDR_COLUMN 0x00000030 /* column address was accepted */ +#define STATE_ADDR_ZERO 0x00000040 /* one byte zero address was accepted */ +#define STATE_ADDR_MASK 0x00000070 /* address states mask */ + +/* During data input/output the simulator is in these states */ +#define STATE_DATAIN 0x00000100 /* waiting for data input */ +#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */ + +#define STATE_DATAOUT 0x00001000 /* waiting for page data output */ +#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ +#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ +#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ + +/* Previous operation is done, ready to accept new requests */ +#define STATE_READY 0x00000000 + +/* This state is used to mark that the next state isn't known yet */ +#define STATE_UNKNOWN 0x10000000 + +/* Simulator's actions bit masks */ +#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */ +#define ACTION_PRGPAGE 0x00200000 /* program the internal buffer to flash */ +#define ACTION_SECERASE 0x00300000 /* erase sector */ +#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */ +#define ACTION_HALFOFF 0x00500000 /* add to address half of page */ +#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */ +#define ACTION_MASK 0x00700000 /* action mask */ + +#define NS_OPER_NUM 13 /* Number of operations supported by the simulator */ +#define NS_OPER_STATES 6 /* Maximum number of states in operation */ + +#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ +#define OPT_PAGE512 0x00000002 /* 512-byte page chips */ +#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ +#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ +#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ +#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ +#define OPT_SMALLPAGE (OPT_PAGE512) /* 512-byte page chips */ + +/* Remove action bits from state */ +#define NS_STATE(x) ((x) & ~ACTION_MASK) + +/* + * Maximum previous states which need to be saved. Currently saving is + * only needed for page program operation with preceded read command + * (which is only valid for 512-byte pages). + */ +#define NS_MAX_PREVSTATES 1 + +/* Maximum page cache pages needed to read or write a NAND page to the cache_file */ +#define NS_MAX_HELD_PAGES 16 + +/* + * A union to represent flash memory contents and flash buffer. + */ +union ns_mem { + u_char *byte; /* for byte access */ + uint16_t *word; /* for 16-bit word access */ +}; + +/* + * The structure which describes all the internal simulator data. + */ +struct nandsim { + struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS]; + unsigned int nbparts; + + uint busw; /* flash chip bus width (8 or 16) */ + u_char ids[8]; /* chip's ID bytes */ + uint32_t options; /* chip's characteristic bits */ + uint32_t state; /* current chip state */ + uint32_t nxstate; /* next expected state */ + + uint32_t *op; /* current operation, NULL operations isn't known yet */ + uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */ + uint16_t npstates; /* number of previous states saved */ + uint16_t stateidx; /* current state index */ + + /* The simulated NAND flash pages array */ + union ns_mem *pages; + + /* Slab allocator for nand pages */ + struct kmem_cache *nand_pages_slab; + + /* Internal buffer of page + OOB size bytes */ + union ns_mem buf; + + /* NAND flash "geometry" */ + struct { + uint64_t totsz; /* total flash size, bytes */ + uint32_t secsz; /* flash sector (erase block) size, bytes */ + uint pgsz; /* NAND flash page size, bytes */ + uint oobsz; /* page OOB area size, bytes */ + uint64_t totszoob; /* total flash size including OOB, bytes */ + uint pgszoob; /* page size including OOB , bytes*/ + uint secszoob; /* sector size including OOB, bytes */ + uint pgnum; /* total number of pages */ + uint pgsec; /* number of pages per sector */ + uint secshift; /* bits number in sector size */ + uint pgshift; /* bits number in page size */ + uint pgaddrbytes; /* bytes per page address */ + uint secaddrbytes; /* bytes per sector address */ + uint idbytes; /* the number ID bytes that this chip outputs */ + } geom; + + /* NAND flash internal registers */ + struct { + unsigned command; /* the command register */ + u_char status; /* the status register */ + uint row; /* the page number */ + uint column; /* the offset within page */ + uint count; /* internal counter */ + uint num; /* number of bytes which must be processed */ + uint off; /* fixed page offset */ + } regs; + + /* NAND flash lines state */ + struct { + int ce; /* chip Enable */ + int cle; /* command Latch Enable */ + int ale; /* address Latch Enable */ + int wp; /* write Protect */ + } lines; + + /* Fields needed when using a cache file */ + struct file *cfile; /* Open file */ + unsigned long *pages_written; /* Which pages have been written */ + void *file_buf; + struct page *held_pages[NS_MAX_HELD_PAGES]; + int held_cnt; +}; + +/* + * Operations array. To perform any operation the simulator must pass + * through the correspondent states chain. + */ +static struct nandsim_operations { + uint32_t reqopts; /* options which are required to perform the operation */ + uint32_t states[NS_OPER_STATES]; /* operation's states */ +} ops[NS_OPER_NUM] = { + /* Read page + OOB from the beginning */ + {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Read page + OOB from the second half */ + {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Read OOB */ + {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Program page starting from the beginning */ + {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN, + STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Program page starting from the beginning */ + {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Program page starting from the second half */ + {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Program OOB */ + {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Erase sector */ + {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}}, + /* Read status */ + {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, + /* Read ID */ + {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}}, + /* Large page devices read page */ + {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Large page devices random page read */ + {OPT_LARGEPAGE, {STATE_CMD_RNDOUT, STATE_ADDR_COLUMN, STATE_CMD_RNDOUTSTART | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, +}; + +struct weak_block { + struct list_head list; + unsigned int erase_block_no; + unsigned int max_erases; + unsigned int erases_done; +}; + +static LIST_HEAD(weak_blocks); + +struct weak_page { + struct list_head list; + unsigned int page_no; + unsigned int max_writes; + unsigned int writes_done; +}; + +static LIST_HEAD(weak_pages); + +struct grave_page { + struct list_head list; + unsigned int page_no; + unsigned int max_reads; + unsigned int reads_done; +}; + +static LIST_HEAD(grave_pages); + +static unsigned long *erase_block_wear = NULL; +static unsigned int wear_eb_count = 0; +static unsigned long total_wear = 0; + +/* MTD structure for NAND controller */ +static struct mtd_info *nsmtd; + +static int nandsim_debugfs_show(struct seq_file *m, void *private) +{ + unsigned long wmin = -1, wmax = 0, avg; + unsigned long deciles[10], decile_max[10], tot = 0; + unsigned int i; + + /* Calc wear stats */ + for (i = 0; i < wear_eb_count; ++i) { + unsigned long wear = erase_block_wear[i]; + if (wear < wmin) + wmin = wear; + if (wear > wmax) + wmax = wear; + tot += wear; + } + + for (i = 0; i < 9; ++i) { + deciles[i] = 0; + decile_max[i] = (wmax * (i + 1) + 5) / 10; + } + deciles[9] = 0; + decile_max[9] = wmax; + for (i = 0; i < wear_eb_count; ++i) { + int d; + unsigned long wear = erase_block_wear[i]; + for (d = 0; d < 10; ++d) + if (wear <= decile_max[d]) { + deciles[d] += 1; + break; + } + } + avg = tot / wear_eb_count; + + /* Output wear report */ + seq_printf(m, "Total numbers of erases: %lu\n", tot); + seq_printf(m, "Number of erase blocks: %u\n", wear_eb_count); + seq_printf(m, "Average number of erases: %lu\n", avg); + seq_printf(m, "Maximum number of erases: %lu\n", wmax); + seq_printf(m, "Minimum number of erases: %lu\n", wmin); + for (i = 0; i < 10; ++i) { + unsigned long from = (i ? decile_max[i - 1] + 1 : 0); + if (from > decile_max[i]) + continue; + seq_printf(m, "Number of ebs with erase counts from %lu to %lu : %lu\n", + from, + decile_max[i], + deciles[i]); + } + + return 0; +} + +static int nandsim_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, nandsim_debugfs_show, inode->i_private); +} + +static const struct file_operations dfs_fops = { + .open = nandsim_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * nandsim_debugfs_create - initialize debugfs + * @dev: nandsim device description object + * + * This function creates all debugfs files for UBI device @ubi. Returns zero in + * case of success and a negative error code in case of failure. + */ +static int nandsim_debugfs_create(struct nandsim *dev) +{ + struct dentry *root = nsmtd->dbg.dfs_dir; + struct dentry *dent; + + /* + * Just skip debugfs initialization when the debugfs directory is + * missing. + */ + if (IS_ERR_OR_NULL(root)) { + if (IS_ENABLED(CONFIG_DEBUG_FS) && + !IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) + NS_WARN("CONFIG_MTD_PARTITIONED_MASTER must be enabled to expose debugfs stuff\n"); + return 0; + } + + dent = debugfs_create_file("nandsim_wear_report", S_IRUSR, + root, dev, &dfs_fops); + if (IS_ERR_OR_NULL(dent)) { + NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n"); + return -1; + } + + return 0; +} + +/* + * Allocate array of page pointers, create slab allocation for an array + * and initialize the array by NULL pointers. + * + * RETURNS: 0 if success, -ENOMEM if memory alloc fails. + */ +static int __init alloc_device(struct nandsim *ns) +{ + struct file *cfile; + int i, err; + + if (cache_file) { + cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600); + if (IS_ERR(cfile)) + return PTR_ERR(cfile); + if (!(cfile->f_mode & FMODE_CAN_READ)) { + NS_ERR("alloc_device: cache file not readable\n"); + err = -EINVAL; + goto err_close; + } + if (!(cfile->f_mode & FMODE_CAN_WRITE)) { + NS_ERR("alloc_device: cache file not writeable\n"); + err = -EINVAL; + goto err_close; + } + ns->pages_written = vzalloc(BITS_TO_LONGS(ns->geom.pgnum) * + sizeof(unsigned long)); + if (!ns->pages_written) { + NS_ERR("alloc_device: unable to allocate pages written array\n"); + err = -ENOMEM; + goto err_close; + } + ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + if (!ns->file_buf) { + NS_ERR("alloc_device: unable to allocate file buf\n"); + err = -ENOMEM; + goto err_free; + } + ns->cfile = cfile; + return 0; + } + + ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); + if (!ns->pages) { + NS_ERR("alloc_device: unable to allocate page array\n"); + return -ENOMEM; + } + for (i = 0; i < ns->geom.pgnum; i++) { + ns->pages[i].byte = NULL; + } + ns->nand_pages_slab = kmem_cache_create("nandsim", + ns->geom.pgszoob, 0, 0, NULL); + if (!ns->nand_pages_slab) { + NS_ERR("cache_create: unable to create kmem_cache\n"); + return -ENOMEM; + } + + return 0; + +err_free: + vfree(ns->pages_written); +err_close: + filp_close(cfile, NULL); + return err; +} + +/* + * Free any allocated pages, and free the array of page pointers. + */ +static void free_device(struct nandsim *ns) +{ + int i; + + if (ns->cfile) { + kfree(ns->file_buf); + vfree(ns->pages_written); + filp_close(ns->cfile, NULL); + return; + } + + if (ns->pages) { + for (i = 0; i < ns->geom.pgnum; i++) { + if (ns->pages[i].byte) + kmem_cache_free(ns->nand_pages_slab, + ns->pages[i].byte); + } + kmem_cache_destroy(ns->nand_pages_slab); + vfree(ns->pages); + } +} + +static char __init *get_partition_name(int i) +{ + return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i); +} + +/* + * Initialize the nandsim structure. + * + * RETURNS: 0 if success, -ERRNO if failure. + */ +static int __init init_nandsim(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nandsim *ns = nand_get_controller_data(chip); + int i, ret = 0; + uint64_t remains; + uint64_t next_offset; + + if (NS_IS_INITIALIZED(ns)) { + NS_ERR("init_nandsim: nandsim is already initialized\n"); + return -EIO; + } + + /* Force mtd to not do delays */ + chip->chip_delay = 0; + + /* Initialize the NAND flash parameters */ + ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8; + ns->geom.totsz = mtd->size; + ns->geom.pgsz = mtd->writesize; + ns->geom.oobsz = mtd->oobsize; + ns->geom.secsz = mtd->erasesize; + ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; + ns->geom.pgnum = div_u64(ns->geom.totsz, ns->geom.pgsz); + ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; + ns->geom.secshift = ffs(ns->geom.secsz) - 1; + ns->geom.pgshift = chip->page_shift; + ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; + ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; + ns->options = 0; + + if (ns->geom.pgsz == 512) { + ns->options |= OPT_PAGE512; + if (ns->busw == 8) + ns->options |= OPT_PAGE512_8BIT; + } else if (ns->geom.pgsz == 2048) { + ns->options |= OPT_PAGE2048; + } else if (ns->geom.pgsz == 4096) { + ns->options |= OPT_PAGE4096; + } else { + NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); + return -EIO; + } + + if (ns->options & OPT_SMALLPAGE) { + if (ns->geom.totsz <= (32 << 20)) { + ns->geom.pgaddrbytes = 3; + ns->geom.secaddrbytes = 2; + } else { + ns->geom.pgaddrbytes = 4; + ns->geom.secaddrbytes = 3; + } + } else { + if (ns->geom.totsz <= (128 << 20)) { + ns->geom.pgaddrbytes = 4; + ns->geom.secaddrbytes = 2; + } else { + ns->geom.pgaddrbytes = 5; + ns->geom.secaddrbytes = 3; + } + } + + /* Fill the partition_info structure */ + if (parts_num > ARRAY_SIZE(ns->partitions)) { + NS_ERR("too many partitions.\n"); + return -EINVAL; + } + remains = ns->geom.totsz; + next_offset = 0; + for (i = 0; i < parts_num; ++i) { + uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz; + + if (!part_sz || part_sz > remains) { + NS_ERR("bad partition size.\n"); + return -EINVAL; + } + ns->partitions[i].name = get_partition_name(i); + if (!ns->partitions[i].name) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + ns->partitions[i].offset = next_offset; + ns->partitions[i].size = part_sz; + next_offset += ns->partitions[i].size; + remains -= ns->partitions[i].size; + } + ns->nbparts = parts_num; + if (remains) { + if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) { + NS_ERR("too many partitions.\n"); + return -EINVAL; + } + ns->partitions[i].name = get_partition_name(i); + if (!ns->partitions[i].name) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + ns->partitions[i].offset = next_offset; + ns->partitions[i].size = remains; + ns->nbparts += 1; + } + + if (ns->busw == 16) + NS_WARN("16-bit flashes support wasn't tested\n"); + + printk("flash size: %llu MiB\n", + (unsigned long long)ns->geom.totsz >> 20); + printk("page size: %u bytes\n", ns->geom.pgsz); + printk("OOB area size: %u bytes\n", ns->geom.oobsz); + printk("sector size: %u KiB\n", ns->geom.secsz >> 10); + printk("pages number: %u\n", ns->geom.pgnum); + printk("pages per sector: %u\n", ns->geom.pgsec); + printk("bus width: %u\n", ns->busw); + printk("bits in sector size: %u\n", ns->geom.secshift); + printk("bits in page size: %u\n", ns->geom.pgshift); + printk("bits in OOB size: %u\n", ffs(ns->geom.oobsz) - 1); + printk("flash size with OOB: %llu KiB\n", + (unsigned long long)ns->geom.totszoob >> 10); + printk("page address bytes: %u\n", ns->geom.pgaddrbytes); + printk("sector address bytes: %u\n", ns->geom.secaddrbytes); + printk("options: %#x\n", ns->options); + + if ((ret = alloc_device(ns)) != 0) + return ret; + + /* Allocate / initialize the internal buffer */ + ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + if (!ns->buf.byte) { + NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", + ns->geom.pgszoob); + return -ENOMEM; + } + memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); + + return 0; +} + +/* + * Free the nandsim structure. + */ +static void free_nandsim(struct nandsim *ns) +{ + kfree(ns->buf.byte); + free_device(ns); + + return; +} + +static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd) +{ + char *w; + int zero_ok; + unsigned int erase_block_no; + loff_t offset; + + if (!badblocks) + return 0; + w = badblocks; + do { + zero_ok = (*w == '0' ? 1 : 0); + erase_block_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !erase_block_no) { + NS_ERR("invalid badblocks.\n"); + return -EINVAL; + } + offset = (loff_t)erase_block_no * ns->geom.secsz; + if (mtd_block_markbad(mtd, offset)) { + NS_ERR("invalid badblocks.\n"); + return -EINVAL; + } + if (*w == ',') + w += 1; + } while (*w); + return 0; +} + +static int parse_weakblocks(void) +{ + char *w; + int zero_ok; + unsigned int erase_block_no; + unsigned int max_erases; + struct weak_block *wb; + + if (!weakblocks) + return 0; + w = weakblocks; + do { + zero_ok = (*w == '0' ? 1 : 0); + erase_block_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !erase_block_no) { + NS_ERR("invalid weakblocks.\n"); + return -EINVAL; + } + max_erases = 3; + if (*w == ':') { + w += 1; + max_erases = simple_strtoul(w, &w, 0); + } + if (*w == ',') + w += 1; + wb = kzalloc(sizeof(*wb), GFP_KERNEL); + if (!wb) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + wb->erase_block_no = erase_block_no; + wb->max_erases = max_erases; + list_add(&wb->list, &weak_blocks); + } while (*w); + return 0; +} + +static int erase_error(unsigned int erase_block_no) +{ + struct weak_block *wb; + + list_for_each_entry(wb, &weak_blocks, list) + if (wb->erase_block_no == erase_block_no) { + if (wb->erases_done >= wb->max_erases) + return 1; + wb->erases_done += 1; + return 0; + } + return 0; +} + +static int parse_weakpages(void) +{ + char *w; + int zero_ok; + unsigned int page_no; + unsigned int max_writes; + struct weak_page *wp; + + if (!weakpages) + return 0; + w = weakpages; + do { + zero_ok = (*w == '0' ? 1 : 0); + page_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !page_no) { + NS_ERR("invalid weakpages.\n"); + return -EINVAL; + } + max_writes = 3; + if (*w == ':') { + w += 1; + max_writes = simple_strtoul(w, &w, 0); + } + if (*w == ',') + w += 1; + wp = kzalloc(sizeof(*wp), GFP_KERNEL); + if (!wp) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + wp->page_no = page_no; + wp->max_writes = max_writes; + list_add(&wp->list, &weak_pages); + } while (*w); + return 0; +} + +static int write_error(unsigned int page_no) +{ + struct weak_page *wp; + + list_for_each_entry(wp, &weak_pages, list) + if (wp->page_no == page_no) { + if (wp->writes_done >= wp->max_writes) + return 1; + wp->writes_done += 1; + return 0; + } + return 0; +} + +static int parse_gravepages(void) +{ + char *g; + int zero_ok; + unsigned int page_no; + unsigned int max_reads; + struct grave_page *gp; + + if (!gravepages) + return 0; + g = gravepages; + do { + zero_ok = (*g == '0' ? 1 : 0); + page_no = simple_strtoul(g, &g, 0); + if (!zero_ok && !page_no) { + NS_ERR("invalid gravepagess.\n"); + return -EINVAL; + } + max_reads = 3; + if (*g == ':') { + g += 1; + max_reads = simple_strtoul(g, &g, 0); + } + if (*g == ',') + g += 1; + gp = kzalloc(sizeof(*gp), GFP_KERNEL); + if (!gp) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + gp->page_no = page_no; + gp->max_reads = max_reads; + list_add(&gp->list, &grave_pages); + } while (*g); + return 0; +} + +static int read_error(unsigned int page_no) +{ + struct grave_page *gp; + + list_for_each_entry(gp, &grave_pages, list) + if (gp->page_no == page_no) { + if (gp->reads_done >= gp->max_reads) + return 1; + gp->reads_done += 1; + return 0; + } + return 0; +} + +static void free_lists(void) +{ + struct list_head *pos, *n; + list_for_each_safe(pos, n, &weak_blocks) { + list_del(pos); + kfree(list_entry(pos, struct weak_block, list)); + } + list_for_each_safe(pos, n, &weak_pages) { + list_del(pos); + kfree(list_entry(pos, struct weak_page, list)); + } + list_for_each_safe(pos, n, &grave_pages) { + list_del(pos); + kfree(list_entry(pos, struct grave_page, list)); + } + kfree(erase_block_wear); +} + +static int setup_wear_reporting(struct mtd_info *mtd) +{ + size_t mem; + + wear_eb_count = div_u64(mtd->size, mtd->erasesize); + mem = wear_eb_count * sizeof(unsigned long); + if (mem / sizeof(unsigned long) != wear_eb_count) { + NS_ERR("Too many erase blocks for wear reporting\n"); + return -ENOMEM; + } + erase_block_wear = kzalloc(mem, GFP_KERNEL); + if (!erase_block_wear) { + NS_ERR("Too many erase blocks for wear reporting\n"); + return -ENOMEM; + } + return 0; +} + +static void update_wear(unsigned int erase_block_no) +{ + if (!erase_block_wear) + return; + total_wear += 1; + /* + * TODO: Notify this through a debugfs entry, + * instead of showing an error message. + */ + if (total_wear == 0) + NS_ERR("Erase counter total overflow\n"); + erase_block_wear[erase_block_no] += 1; + if (erase_block_wear[erase_block_no] == 0) + NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); +} + +/* + * Returns the string representation of 'state' state. + */ +static char *get_state_name(uint32_t state) +{ + switch (NS_STATE(state)) { + case STATE_CMD_READ0: + return "STATE_CMD_READ0"; + case STATE_CMD_READ1: + return "STATE_CMD_READ1"; + case STATE_CMD_PAGEPROG: + return "STATE_CMD_PAGEPROG"; + case STATE_CMD_READOOB: + return "STATE_CMD_READOOB"; + case STATE_CMD_READSTART: + return "STATE_CMD_READSTART"; + case STATE_CMD_ERASE1: + return "STATE_CMD_ERASE1"; + case STATE_CMD_STATUS: + return "STATE_CMD_STATUS"; + case STATE_CMD_SEQIN: + return "STATE_CMD_SEQIN"; + case STATE_CMD_READID: + return "STATE_CMD_READID"; + case STATE_CMD_ERASE2: + return "STATE_CMD_ERASE2"; + case STATE_CMD_RESET: + return "STATE_CMD_RESET"; + case STATE_CMD_RNDOUT: + return "STATE_CMD_RNDOUT"; + case STATE_CMD_RNDOUTSTART: + return "STATE_CMD_RNDOUTSTART"; + case STATE_ADDR_PAGE: + return "STATE_ADDR_PAGE"; + case STATE_ADDR_SEC: + return "STATE_ADDR_SEC"; + case STATE_ADDR_ZERO: + return "STATE_ADDR_ZERO"; + case STATE_ADDR_COLUMN: + return "STATE_ADDR_COLUMN"; + case STATE_DATAIN: + return "STATE_DATAIN"; + case STATE_DATAOUT: + return "STATE_DATAOUT"; + case STATE_DATAOUT_ID: + return "STATE_DATAOUT_ID"; + case STATE_DATAOUT_STATUS: + return "STATE_DATAOUT_STATUS"; + case STATE_READY: + return "STATE_READY"; + case STATE_UNKNOWN: + return "STATE_UNKNOWN"; + } + + NS_ERR("get_state_name: unknown state, BUG\n"); + return NULL; +} + +/* + * Check if command is valid. + * + * RETURNS: 1 if wrong command, 0 if right. + */ +static int check_command(int cmd) +{ + switch (cmd) { + + case NAND_CMD_READ0: + case NAND_CMD_READ1: + case NAND_CMD_READSTART: + case NAND_CMD_PAGEPROG: + case NAND_CMD_READOOB: + case NAND_CMD_ERASE1: + case NAND_CMD_STATUS: + case NAND_CMD_SEQIN: + case NAND_CMD_READID: + case NAND_CMD_ERASE2: + case NAND_CMD_RESET: + case NAND_CMD_RNDOUT: + case NAND_CMD_RNDOUTSTART: + return 0; + + default: + return 1; + } +} + +/* + * Returns state after command is accepted by command number. + */ +static uint32_t get_state_by_command(unsigned command) +{ + switch (command) { + case NAND_CMD_READ0: + return STATE_CMD_READ0; + case NAND_CMD_READ1: + return STATE_CMD_READ1; + case NAND_CMD_PAGEPROG: + return STATE_CMD_PAGEPROG; + case NAND_CMD_READSTART: + return STATE_CMD_READSTART; + case NAND_CMD_READOOB: + return STATE_CMD_READOOB; + case NAND_CMD_ERASE1: + return STATE_CMD_ERASE1; + case NAND_CMD_STATUS: + return STATE_CMD_STATUS; + case NAND_CMD_SEQIN: + return STATE_CMD_SEQIN; + case NAND_CMD_READID: + return STATE_CMD_READID; + case NAND_CMD_ERASE2: + return STATE_CMD_ERASE2; + case NAND_CMD_RESET: + return STATE_CMD_RESET; + case NAND_CMD_RNDOUT: + return STATE_CMD_RNDOUT; + case NAND_CMD_RNDOUTSTART: + return STATE_CMD_RNDOUTSTART; + } + + NS_ERR("get_state_by_command: unknown command, BUG\n"); + return 0; +} + +/* + * Move an address byte to the correspondent internal register. + */ +static inline void accept_addr_byte(struct nandsim *ns, u_char bt) +{ + uint byte = (uint)bt; + + if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) + ns->regs.column |= (byte << 8 * ns->regs.count); + else { + ns->regs.row |= (byte << 8 * (ns->regs.count - + ns->geom.pgaddrbytes + + ns->geom.secaddrbytes)); + } + + return; +} + +/* + * Switch to STATE_READY state. + */ +static inline void switch_to_ready_state(struct nandsim *ns, u_char status) +{ + NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY)); + + ns->state = STATE_READY; + ns->nxstate = STATE_UNKNOWN; + ns->op = NULL; + ns->npstates = 0; + ns->stateidx = 0; + ns->regs.num = 0; + ns->regs.count = 0; + ns->regs.off = 0; + ns->regs.row = 0; + ns->regs.column = 0; + ns->regs.status = status; +} + +/* + * If the operation isn't known yet, try to find it in the global array + * of supported operations. + * + * Operation can be unknown because of the following. + * 1. New command was accepted and this is the first call to find the + * correspondent states chain. In this case ns->npstates = 0; + * 2. There are several operations which begin with the same command(s) + * (for example program from the second half and read from the + * second half operations both begin with the READ1 command). In this + * case the ns->pstates[] array contains previous states. + * + * Thus, the function tries to find operation containing the following + * states (if the 'flag' parameter is 0): + * ns->pstates[0], ... ns->pstates[ns->npstates], ns->state + * + * If (one and only one) matching operation is found, it is accepted ( + * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is + * zeroed). + * + * If there are several matches, the current state is pushed to the + * ns->pstates. + * + * The operation can be unknown only while commands are input to the chip. + * As soon as address command is accepted, the operation must be known. + * In such situation the function is called with 'flag' != 0, and the + * operation is searched using the following pattern: + * ns->pstates[0], ... ns->pstates[ns->npstates],
+ * + * It is supposed that this pattern must either match one operation or + * none. There can't be ambiguity in that case. + * + * If no matches found, the function does the following: + * 1. if there are saved states present, try to ignore them and search + * again only using the last command. If nothing was found, switch + * to the STATE_READY state. + * 2. if there are no saved states, switch to the STATE_READY state. + * + * RETURNS: -2 - no matched operations found. + * -1 - several matches. + * 0 - operation is found. + */ +static int find_operation(struct nandsim *ns, uint32_t flag) +{ + int opsfound = 0; + int i, j, idx = 0; + + for (i = 0; i < NS_OPER_NUM; i++) { + + int found = 1; + + if (!(ns->options & ops[i].reqopts)) + /* Ignore operations we can't perform */ + continue; + + if (flag) { + if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK)) + continue; + } else { + if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates])) + continue; + } + + for (j = 0; j < ns->npstates; j++) + if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j]) + && (ns->options & ops[idx].reqopts)) { + found = 0; + break; + } + + if (found) { + idx = i; + opsfound += 1; + } + } + + if (opsfound == 1) { + /* Exact match */ + ns->op = &ops[idx].states[0]; + if (flag) { + /* + * In this case the find_operation function was + * called when address has just began input. But it isn't + * yet fully input and the current state must + * not be one of STATE_ADDR_*, but the STATE_ADDR_* + * state must be the next state (ns->nxstate). + */ + ns->stateidx = ns->npstates - 1; + } else { + ns->stateidx = ns->npstates; + } + ns->npstates = 0; + ns->state = ns->op[ns->stateidx]; + ns->nxstate = ns->op[ns->stateidx + 1]; + NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n", + idx, get_state_name(ns->state), get_state_name(ns->nxstate)); + return 0; + } + + if (opsfound == 0) { + /* Nothing was found. Try to ignore previous commands (if any) and search again */ + if (ns->npstates != 0) { + NS_DBG("find_operation: no operation found, try again with state %s\n", + get_state_name(ns->state)); + ns->npstates = 0; + return find_operation(ns, 0); + + } + NS_DBG("find_operation: no operations found\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return -2; + } + + if (flag) { + /* This shouldn't happen */ + NS_DBG("find_operation: BUG, operation must be known if address is input\n"); + return -2; + } + + NS_DBG("find_operation: there is still ambiguity\n"); + + ns->pstates[ns->npstates++] = ns->state; + + return -1; +} + +static void put_pages(struct nandsim *ns) +{ + int i; + + for (i = 0; i < ns->held_cnt; i++) + put_page(ns->held_pages[i]); +} + +/* Get page cache pages in advance to provide NOFS memory allocation */ +static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos) +{ + pgoff_t index, start_index, end_index; + struct page *page; + struct address_space *mapping = file->f_mapping; + + start_index = pos >> PAGE_SHIFT; + end_index = (pos + count - 1) >> PAGE_SHIFT; + if (end_index - start_index + 1 > NS_MAX_HELD_PAGES) + return -EINVAL; + ns->held_cnt = 0; + for (index = start_index; index <= end_index; index++) { + page = find_get_page(mapping, index); + if (page == NULL) { + page = find_or_create_page(mapping, index, GFP_NOFS); + if (page == NULL) { + write_inode_now(mapping->host, 1); + page = find_or_create_page(mapping, index, GFP_NOFS); + } + if (page == NULL) { + put_pages(ns); + return -ENOMEM; + } + unlock_page(page); + } + ns->held_pages[ns->held_cnt++] = page; + } + return 0; +} + +static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) +{ + ssize_t tx; + int err; + unsigned int noreclaim_flag; + + err = get_pages(ns, file, count, pos); + if (err) + return err; + noreclaim_flag = memalloc_noreclaim_save(); + tx = kernel_read(file, buf, count, &pos); + memalloc_noreclaim_restore(noreclaim_flag); + put_pages(ns); + return tx; +} + +static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) +{ + ssize_t tx; + int err; + unsigned int noreclaim_flag; + + err = get_pages(ns, file, count, pos); + if (err) + return err; + noreclaim_flag = memalloc_noreclaim_save(); + tx = kernel_write(file, buf, count, &pos); + memalloc_noreclaim_restore(noreclaim_flag); + put_pages(ns); + return tx; +} + +/* + * Returns a pointer to the current page. + */ +static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns) +{ + return &(ns->pages[ns->regs.row]); +} + +/* + * Retuns a pointer to the current byte, within the current page. + */ +static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) +{ + return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; +} + +static int do_read_error(struct nandsim *ns, int num) +{ + unsigned int page_no = ns->regs.row; + + if (read_error(page_no)) { + prandom_bytes(ns->buf.byte, num); + NS_WARN("simulating read error in page %u\n", page_no); + return 1; + } + return 0; +} + +static void do_bit_flips(struct nandsim *ns, int num) +{ + if (bitflips && prandom_u32() < (1 << 22)) { + int flips = 1; + if (bitflips > 1) + flips = (prandom_u32() % (int) bitflips) + 1; + while (flips--) { + int pos = prandom_u32() % (num * 8); + ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); + NS_WARN("read_page: flipping bit %d in page %d " + "reading from %d ecc: corrected=%u failed=%u\n", + pos, ns->regs.row, ns->regs.column + ns->regs.off, + nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); + } + } +} + +/* + * Fill the NAND buffer with data read from the specified page. + */ +static void read_page(struct nandsim *ns, int num) +{ + union ns_mem *mypage; + + if (ns->cfile) { + if (!test_bit(ns->regs.row, ns->pages_written)) { + NS_DBG("read_page: page %d not written\n", ns->regs.row); + memset(ns->buf.byte, 0xFF, num); + } else { + loff_t pos; + ssize_t tx; + + NS_DBG("read_page: page %d written, reading from %d\n", + ns->regs.row, ns->regs.column + ns->regs.off); + if (do_read_error(ns, num)) + return; + pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; + tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos); + if (tx != num) { + NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); + return; + } + do_bit_flips(ns, num); + } + return; + } + + mypage = NS_GET_PAGE(ns); + if (mypage->byte == NULL) { + NS_DBG("read_page: page %d not allocated\n", ns->regs.row); + memset(ns->buf.byte, 0xFF, num); + } else { + NS_DBG("read_page: page %d allocated, reading from %d\n", + ns->regs.row, ns->regs.column + ns->regs.off); + if (do_read_error(ns, num)) + return; + memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); + do_bit_flips(ns, num); + } +} + +/* + * Erase all pages in the specified sector. + */ +static void erase_sector(struct nandsim *ns) +{ + union ns_mem *mypage; + int i; + + if (ns->cfile) { + for (i = 0; i < ns->geom.pgsec; i++) + if (__test_and_clear_bit(ns->regs.row + i, + ns->pages_written)) { + NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i); + } + return; + } + + mypage = NS_GET_PAGE(ns); + for (i = 0; i < ns->geom.pgsec; i++) { + if (mypage->byte != NULL) { + NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); + kmem_cache_free(ns->nand_pages_slab, mypage->byte); + mypage->byte = NULL; + } + mypage++; + } +} + +/* + * Program the specified page with the contents from the NAND buffer. + */ +static int prog_page(struct nandsim *ns, int num) +{ + int i; + union ns_mem *mypage; + u_char *pg_off; + + if (ns->cfile) { + loff_t off; + ssize_t tx; + int all; + + NS_DBG("prog_page: writing page %d\n", ns->regs.row); + pg_off = ns->file_buf + ns->regs.column + ns->regs.off; + off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; + if (!test_bit(ns->regs.row, ns->pages_written)) { + all = 1; + memset(ns->file_buf, 0xff, ns->geom.pgszoob); + } else { + all = 0; + tx = read_file(ns, ns->cfile, pg_off, num, off); + if (tx != num) { + NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); + return -1; + } + } + for (i = 0; i < num; i++) + pg_off[i] &= ns->buf.byte[i]; + if (all) { + loff_t pos = (loff_t)ns->regs.row * ns->geom.pgszoob; + tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, pos); + if (tx != ns->geom.pgszoob) { + NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); + return -1; + } + __set_bit(ns->regs.row, ns->pages_written); + } else { + tx = write_file(ns, ns->cfile, pg_off, num, off); + if (tx != num) { + NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); + return -1; + } + } + return 0; + } + + mypage = NS_GET_PAGE(ns); + if (mypage->byte == NULL) { + NS_DBG("prog_page: allocating page %d\n", ns->regs.row); + /* + * We allocate memory with GFP_NOFS because a flash FS may + * utilize this. If it is holding an FS lock, then gets here, + * then kernel memory alloc runs writeback which goes to the FS + * again and deadlocks. This was seen in practice. + */ + mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS); + if (mypage->byte == NULL) { + NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); + return -1; + } + memset(mypage->byte, 0xFF, ns->geom.pgszoob); + } + + pg_off = NS_PAGE_BYTE_OFF(ns); + for (i = 0; i < num; i++) + pg_off[i] &= ns->buf.byte[i]; + + return 0; +} + +/* + * If state has any action bit, perform this action. + * + * RETURNS: 0 if success, -1 if error. + */ +static int do_state_action(struct nandsim *ns, uint32_t action) +{ + int num; + int busdiv = ns->busw == 8 ? 1 : 2; + unsigned int erase_block_no, page_no; + + action &= ACTION_MASK; + + /* Check that page address input is correct */ + if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) { + NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row); + return -1; + } + + switch (action) { + + case ACTION_CPY: + /* + * Copy page data to the internal buffer. + */ + + /* Column shouldn't be very large */ + if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) { + NS_ERR("do_state_action: column number is too large\n"); + break; + } + num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + read_page(ns, num); + + NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", + num, NS_RAW_OFFSET(ns) + ns->regs.off); + + if (ns->regs.off == 0) + NS_LOG("read page %d\n", ns->regs.row); + else if (ns->regs.off < ns->geom.pgsz) + NS_LOG("read page %d (second half)\n", ns->regs.row); + else + NS_LOG("read OOB of page %d\n", ns->regs.row); + + NS_UDELAY(access_delay); + NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv); + + break; + + case ACTION_SECERASE: + /* + * Erase sector. + */ + + if (ns->lines.wp) { + NS_ERR("do_state_action: device is write-protected, ignore sector erase\n"); + return -1; + } + + if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec + || (ns->regs.row & ~(ns->geom.secsz - 1))) { + NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row); + return -1; + } + + ns->regs.row = (ns->regs.row << + 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; + ns->regs.column = 0; + + erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift); + + NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", + ns->regs.row, NS_RAW_OFFSET(ns)); + NS_LOG("erase sector %u\n", erase_block_no); + + erase_sector(ns); + + NS_MDELAY(erase_delay); + + if (erase_block_wear) + update_wear(erase_block_no); + + if (erase_error(erase_block_no)) { + NS_WARN("simulating erase failure in erase block %u\n", erase_block_no); + return -1; + } + + break; + + case ACTION_PRGPAGE: + /* + * Program page - move internal buffer data to the page. + */ + + if (ns->lines.wp) { + NS_WARN("do_state_action: device is write-protected, programm\n"); + return -1; + } + + num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + if (num != ns->regs.count) { + NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n", + ns->regs.count, num); + return -1; + } + + if (prog_page(ns, num) == -1) + return -1; + + page_no = ns->regs.row; + + NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", + num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); + NS_LOG("programm page %d\n", ns->regs.row); + + NS_UDELAY(programm_delay); + NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); + + if (write_error(page_no)) { + NS_WARN("simulating write failure in page %u\n", page_no); + return -1; + } + + break; + + case ACTION_ZEROOFF: + NS_DBG("do_state_action: set internal offset to 0\n"); + ns->regs.off = 0; + break; + + case ACTION_HALFOFF: + if (!(ns->options & OPT_PAGE512_8BIT)) { + NS_ERR("do_state_action: BUG! can't skip half of page for non-512" + "byte page size 8x chips\n"); + return -1; + } + NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2); + ns->regs.off = ns->geom.pgsz/2; + break; + + case ACTION_OOBOFF: + NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz); + ns->regs.off = ns->geom.pgsz; + break; + + default: + NS_DBG("do_state_action: BUG! unknown action\n"); + } + + return 0; +} + +/* + * Switch simulator's state. + */ +static void switch_state(struct nandsim *ns) +{ + if (ns->op) { + /* + * The current operation have already been identified. + * Just follow the states chain. + */ + + ns->stateidx += 1; + ns->state = ns->nxstate; + ns->nxstate = ns->op[ns->stateidx + 1]; + + NS_DBG("switch_state: operation is known, switch to the next state, " + "state: %s, nxstate: %s\n", + get_state_name(ns->state), get_state_name(ns->nxstate)); + + /* See, whether we need to do some action */ + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + } else { + /* + * We don't yet know which operation we perform. + * Try to identify it. + */ + + /* + * The only event causing the switch_state function to + * be called with yet unknown operation is new command. + */ + ns->state = get_state_by_command(ns->regs.command); + + NS_DBG("switch_state: operation is unknown, try to find it\n"); + + if (find_operation(ns, 0) != 0) + return; + + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + } + + /* For 16x devices column means the page offset in words */ + if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) { + NS_DBG("switch_state: double the column number for 16x device\n"); + ns->regs.column <<= 1; + } + + if (NS_STATE(ns->nxstate) == STATE_READY) { + /* + * The current state is the last. Return to STATE_READY + */ + + u_char status = NS_STATUS_OK(ns); + + /* In case of data states, see if all bytes were input/output */ + if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) + && ns->regs.count != ns->regs.num) { + NS_WARN("switch_state: not all bytes were processed, %d left\n", + ns->regs.num - ns->regs.count); + status = NS_STATUS_FAILED(ns); + } + + NS_DBG("switch_state: operation complete, switch to STATE_READY state\n"); + + switch_to_ready_state(ns, status); + + return; + } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) { + /* + * If the next state is data input/output, switch to it now + */ + + ns->state = ns->nxstate; + ns->nxstate = ns->op[++ns->stateidx + 1]; + ns->regs.num = ns->regs.count = 0; + + NS_DBG("switch_state: the next state is data I/O, switch, " + "state: %s, nxstate: %s\n", + get_state_name(ns->state), get_state_name(ns->nxstate)); + + /* + * Set the internal register to the count of bytes which + * are expected to be input or output + */ + switch (NS_STATE(ns->state)) { + case STATE_DATAIN: + case STATE_DATAOUT: + ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + break; + + case STATE_DATAOUT_ID: + ns->regs.num = ns->geom.idbytes; + break; + + case STATE_DATAOUT_STATUS: + ns->regs.count = ns->regs.num = 0; + break; + + default: + NS_ERR("switch_state: BUG! unknown data state\n"); + } + + } else if (ns->nxstate & STATE_ADDR_MASK) { + /* + * If the next state is address input, set the internal + * register to the number of expected address bytes + */ + + ns->regs.count = 0; + + switch (NS_STATE(ns->nxstate)) { + case STATE_ADDR_PAGE: + ns->regs.num = ns->geom.pgaddrbytes; + + break; + case STATE_ADDR_SEC: + ns->regs.num = ns->geom.secaddrbytes; + break; + + case STATE_ADDR_ZERO: + ns->regs.num = 1; + break; + + case STATE_ADDR_COLUMN: + /* Column address is always 2 bytes */ + ns->regs.num = ns->geom.pgaddrbytes - ns->geom.secaddrbytes; + break; + + default: + NS_ERR("switch_state: BUG! unknown address state\n"); + } + } else { + /* + * Just reset internal counters. + */ + + ns->regs.num = 0; + ns->regs.count = 0; + } +} + +static u_char ns_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nandsim *ns = nand_get_controller_data(chip); + u_char outb = 0x00; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb); + return outb; + } + if (ns->lines.ale || ns->lines.cle) { + NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb); + return outb; + } + if (!(ns->state & STATE_DATAOUT_MASK)) { + NS_WARN("read_byte: unexpected data output cycle, state is %s " + "return %#x\n", get_state_name(ns->state), (uint)outb); + return outb; + } + + /* Status register may be read as many times as it is wanted */ + if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) { + NS_DBG("read_byte: return %#x status\n", ns->regs.status); + return ns->regs.status; + } + + /* Check if there is any data in the internal buffer which may be read */ + if (ns->regs.count == ns->regs.num) { + NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb); + return outb; + } + + switch (NS_STATE(ns->state)) { + case STATE_DATAOUT: + if (ns->busw == 8) { + outb = ns->buf.byte[ns->regs.count]; + ns->regs.count += 1; + } else { + outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]); + ns->regs.count += 2; + } + break; + case STATE_DATAOUT_ID: + NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num); + outb = ns->ids[ns->regs.count]; + ns->regs.count += 1; + break; + default: + BUG(); + } + + if (ns->regs.count == ns->regs.num) { + NS_DBG("read_byte: all bytes were read\n"); + + if (NS_STATE(ns->nxstate) == STATE_READY) + switch_state(ns); + } + + return outb; +} + +static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nandsim *ns = nand_get_controller_data(chip); + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("write_byte: chip is disabled, ignore write\n"); + return; + } + if (ns->lines.ale && ns->lines.cle) { + NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n"); + return; + } + + if (ns->lines.cle == 1) { + /* + * The byte written is a command. + */ + + if (byte == NAND_CMD_RESET) { + NS_LOG("reset chip\n"); + switch_to_ready_state(ns, NS_STATUS_OK(ns)); + return; + } + + /* Check that the command byte is correct */ + if (check_command(byte)) { + NS_ERR("write_byte: unknown command %#x\n", (uint)byte); + return; + } + + if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS + || NS_STATE(ns->state) == STATE_DATAOUT) { + int row = ns->regs.row; + + switch_state(ns); + if (byte == NAND_CMD_RNDOUT) + ns->regs.row = row; + } + + /* Check if chip is expecting command */ + if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { + /* Do not warn if only 2 id bytes are read */ + if (!(ns->regs.command == NAND_CMD_READID && + NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) { + /* + * We are in situation when something else (not command) + * was expected but command was input. In this case ignore + * previous command(s)/state(s) and accept the last one. + */ + NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " + "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); + } + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + } + + NS_DBG("command byte corresponding to %s state accepted\n", + get_state_name(get_state_by_command(byte))); + ns->regs.command = byte; + switch_state(ns); + + } else if (ns->lines.ale == 1) { + /* + * The byte written is an address. + */ + + if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) { + + NS_DBG("write_byte: operation isn't known yet, identify it\n"); + + if (find_operation(ns, 1) < 0) + return; + + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + ns->regs.count = 0; + switch (NS_STATE(ns->nxstate)) { + case STATE_ADDR_PAGE: + ns->regs.num = ns->geom.pgaddrbytes; + break; + case STATE_ADDR_SEC: + ns->regs.num = ns->geom.secaddrbytes; + break; + case STATE_ADDR_ZERO: + ns->regs.num = 1; + break; + default: + BUG(); + } + } + + /* Check that chip is expecting address */ + if (!(ns->nxstate & STATE_ADDR_MASK)) { + NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, " + "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if this is expected byte */ + if (ns->regs.count == ns->regs.num) { + NS_ERR("write_byte: no more address bytes expected\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + accept_addr_byte(ns, byte); + + ns->regs.count += 1; + + NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n", + (uint)byte, ns->regs.count, ns->regs.num); + + if (ns->regs.count == ns->regs.num) { + NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column); + switch_state(ns); + } + + } else { + /* + * The byte written is an input data. + */ + + /* Check that chip is expecting data input */ + if (!(ns->state & STATE_DATAIN_MASK)) { + NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, " + "switch to %s\n", (uint)byte, + get_state_name(ns->state), get_state_name(STATE_READY)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if this is expected byte */ + if (ns->regs.count == ns->regs.num) { + NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n", + ns->regs.num); + return; + } + + if (ns->busw == 8) { + ns->buf.byte[ns->regs.count] = byte; + ns->regs.count += 1; + } else { + ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte); + ns->regs.count += 2; + } + } + + return; +} + +static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nandsim *ns = nand_get_controller_data(chip); + + ns->lines.cle = bitmask & NAND_CLE ? 1 : 0; + ns->lines.ale = bitmask & NAND_ALE ? 1 : 0; + ns->lines.ce = bitmask & NAND_NCE ? 1 : 0; + + if (cmd != NAND_CMD_NONE) + ns_nand_write_byte(mtd, cmd); +} + +static int ns_device_ready(struct mtd_info *mtd) +{ + NS_DBG("device_ready\n"); + return 1; +} + +static uint16_t ns_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + NS_DBG("read_word\n"); + + return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); +} + +static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nandsim *ns = nand_get_controller_data(chip); + + /* Check that chip is expecting data input */ + if (!(ns->state & STATE_DATAIN_MASK)) { + NS_ERR("write_buf: data input isn't expected, state is %s, " + "switch to STATE_READY\n", get_state_name(ns->state)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if these are expected bytes */ + if (ns->regs.count + len > ns->regs.num) { + NS_ERR("write_buf: too many input bytes\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + memcpy(ns->buf.byte + ns->regs.count, buf, len); + ns->regs.count += len; + + if (ns->regs.count == ns->regs.num) { + NS_DBG("write_buf: %d bytes were written\n", ns->regs.count); + } +} + +static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nandsim *ns = nand_get_controller_data(chip); + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("read_buf: chip is disabled\n"); + return; + } + if (ns->lines.ale || ns->lines.cle) { + NS_ERR("read_buf: ALE or CLE pin is high\n"); + return; + } + if (!(ns->state & STATE_DATAOUT_MASK)) { + NS_WARN("read_buf: unexpected data output cycle, current state is %s\n", + get_state_name(ns->state)); + return; + } + + if (NS_STATE(ns->state) != STATE_DATAOUT) { + int i; + + for (i = 0; i < len; i++) + buf[i] = mtd_to_nand(mtd)->read_byte(mtd); + + return; + } + + /* Check if these are expected bytes */ + if (ns->regs.count + len > ns->regs.num) { + NS_ERR("read_buf: too many bytes to read\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + memcpy(buf, ns->buf.byte + ns->regs.count, len); + ns->regs.count += len; + + if (ns->regs.count == ns->regs.num) { + if (NS_STATE(ns->nxstate) == STATE_READY) + switch_state(ns); + } + + return; +} + +/* + * Module initialization function + */ +static int __init ns_init_module(void) +{ + struct nand_chip *chip; + struct nandsim *nand; + int retval = -ENOMEM, i; + + if (bus_width != 8 && bus_width != 16) { + NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width); + return -EINVAL; + } + + /* Allocate and initialize mtd_info, nand_chip and nandsim structures */ + chip = kzalloc(sizeof(struct nand_chip) + sizeof(struct nandsim), + GFP_KERNEL); + if (!chip) { + NS_ERR("unable to allocate core structures.\n"); + return -ENOMEM; + } + nsmtd = nand_to_mtd(chip); + nand = (struct nandsim *)(chip + 1); + nand_set_controller_data(chip, (void *)nand); + + /* + * Register simulator's callbacks. + */ + chip->cmd_ctrl = ns_hwcontrol; + chip->read_byte = ns_nand_read_byte; + chip->dev_ready = ns_device_ready; + chip->write_buf = ns_nand_write_buf; + chip->read_buf = ns_nand_read_buf; + chip->read_word = ns_nand_read_word; + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */ + /* and 'badblocks' parameters to work */ + chip->options |= NAND_SKIP_BBTSCAN; + + switch (bbt) { + case 2: + chip->bbt_options |= NAND_BBT_NO_OOB; + case 1: + chip->bbt_options |= NAND_BBT_USE_FLASH; + case 0: + break; + default: + NS_ERR("bbt has to be 0..2\n"); + retval = -EINVAL; + goto error; + } + /* + * Perform minimum nandsim structure initialization to handle + * the initial ID read command correctly + */ + if (id_bytes[6] != 0xFF || id_bytes[7] != 0xFF) + nand->geom.idbytes = 8; + else if (id_bytes[4] != 0xFF || id_bytes[5] != 0xFF) + nand->geom.idbytes = 6; + else if (id_bytes[2] != 0xFF || id_bytes[3] != 0xFF) + nand->geom.idbytes = 4; + else + nand->geom.idbytes = 2; + nand->regs.status = NS_STATUS_OK(nand); + nand->nxstate = STATE_UNKNOWN; + nand->options |= OPT_PAGE512; /* temporary value */ + memcpy(nand->ids, id_bytes, sizeof(nand->ids)); + if (bus_width == 16) { + nand->busw = 16; + chip->options |= NAND_BUSWIDTH_16; + } + + nsmtd->owner = THIS_MODULE; + + if ((retval = parse_weakblocks()) != 0) + goto error; + + if ((retval = parse_weakpages()) != 0) + goto error; + + if ((retval = parse_gravepages()) != 0) + goto error; + + retval = nand_scan_ident(nsmtd, 1, NULL); + if (retval) { + NS_ERR("cannot scan NAND Simulator device\n"); + goto error; + } + + if (bch) { + unsigned int eccsteps, eccbytes; + if (!mtd_nand_has_bch()) { + NS_ERR("BCH ECC support is disabled\n"); + retval = -EINVAL; + goto error; + } + /* use 512-byte ecc blocks */ + eccsteps = nsmtd->writesize/512; + eccbytes = (bch*13+7)/8; + /* do not bother supporting small page devices */ + if ((nsmtd->oobsize < 64) || !eccsteps) { + NS_ERR("bch not available on small page devices\n"); + retval = -EINVAL; + goto error; + } + if ((eccbytes*eccsteps+2) > nsmtd->oobsize) { + NS_ERR("invalid bch value %u\n", bch); + retval = -EINVAL; + goto error; + } + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.size = 512; + chip->ecc.strength = bch; + chip->ecc.bytes = eccbytes; + NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size); + } + + retval = nand_scan_tail(nsmtd); + if (retval) { + NS_ERR("can't register NAND Simulator\n"); + goto error; + } + + if (overridesize) { + uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize; + if (new_size >> overridesize != nsmtd->erasesize) { + NS_ERR("overridesize is too big\n"); + retval = -EINVAL; + goto err_exit; + } + /* N.B. This relies on nand_scan not doing anything with the size before we change it */ + nsmtd->size = new_size; + chip->chipsize = new_size; + chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1; + chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; + } + + if ((retval = setup_wear_reporting(nsmtd)) != 0) + goto err_exit; + + if ((retval = init_nandsim(nsmtd)) != 0) + goto err_exit; + + if ((retval = chip->scan_bbt(nsmtd)) != 0) + goto err_exit; + + if ((retval = parse_badblocks(nand, nsmtd)) != 0) + goto err_exit; + + /* Register NAND partitions */ + retval = mtd_device_register(nsmtd, &nand->partitions[0], + nand->nbparts); + if (retval != 0) + goto err_exit; + + if ((retval = nandsim_debugfs_create(nand)) != 0) + goto err_exit; + + return 0; + +err_exit: + free_nandsim(nand); + nand_release(nsmtd); + for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i) + kfree(nand->partitions[i].name); +error: + kfree(chip); + free_lists(); + + return retval; +} + +module_init(ns_init_module); + +/* + * Module clean-up function + */ +static void __exit ns_cleanup_module(void) +{ + struct nand_chip *chip = mtd_to_nand(nsmtd); + struct nandsim *ns = nand_get_controller_data(chip); + int i; + + free_nandsim(ns); /* Free nandsim private resources */ + nand_release(nsmtd); /* Unregister driver */ + for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i) + kfree(ns->partitions[i].name); + kfree(mtd_to_nand(nsmtd)); /* Free other structures */ + free_lists(); +} + +module_exit(ns_cleanup_module); + +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Artem B. Bityuckiy"); +MODULE_DESCRIPTION ("The NAND flash simulator"); diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c new file mode 100644 index 000000000000..d8a806894937 --- /dev/null +++ b/drivers/mtd/nand/raw/ndfc.c @@ -0,0 +1,286 @@ +/* + * Overview: + * Platform independent driver for NDFC (NanD Flash Controller) + * integrated into EP440 cores + * + * Ported to an OF platform driver by Sean MacLennan + * + * The NDFC supports multiple chips, but this driver only supports a + * single chip since I do not have access to any boards with + * multiple chips. + * + * Author: Thomas Gleixner + * + * Copyright 2006 IBM + * Copyright 2008 PIKA Technologies + * Sean MacLennan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NDFC_MAX_CS 4 + +struct ndfc_controller { + struct platform_device *ofdev; + void __iomem *ndfcbase; + struct nand_chip chip; + int chip_select; + struct nand_hw_control ndfc_control; +}; + +static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS]; + +static void ndfc_select_chip(struct mtd_info *mtd, int chip) +{ + uint32_t ccr; + struct nand_chip *nchip = mtd_to_nand(mtd); + struct ndfc_controller *ndfc = nand_get_controller_data(nchip); + + ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); + if (chip >= 0) { + ccr &= ~NDFC_CCR_BS_MASK; + ccr |= NDFC_CCR_BS(chip + ndfc->chip_select); + } else + ccr |= NDFC_CCR_RESET_CE; + out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); +} + +static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct ndfc_controller *ndfc = nand_get_controller_data(chip); + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_CMD); + else + writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_ALE); +} + +static int ndfc_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct ndfc_controller *ndfc = nand_get_controller_data(chip); + + return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; +} + +static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + uint32_t ccr; + struct nand_chip *chip = mtd_to_nand(mtd); + struct ndfc_controller *ndfc = nand_get_controller_data(chip); + + ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); + ccr |= NDFC_CCR_RESET_ECC; + out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); + wmb(); +} + +static int ndfc_calculate_ecc(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct ndfc_controller *ndfc = nand_get_controller_data(chip); + uint32_t ecc; + uint8_t *p = (uint8_t *)&ecc; + + wmb(); + ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); + /* The NDFC uses Smart Media (SMC) bytes order */ + ecc_code[0] = p[1]; + ecc_code[1] = p[2]; + ecc_code[2] = p[3]; + + return 0; +} + +/* + * Speedups for buffer read/write/verify + * + * NDFC allows 32bit read/write of data. So we can speed up the buffer + * functions. No further checking, as nand_base will always read/write + * page aligned. + */ +static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct ndfc_controller *ndfc = nand_get_controller_data(chip); + uint32_t *p = (uint32_t *) buf; + + for(;len > 0; len -= 4) + *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA); +} + +static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct ndfc_controller *ndfc = nand_get_controller_data(chip); + uint32_t *p = (uint32_t *) buf; + + for(;len > 0; len -= 4) + out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); +} + +/* + * Initialize chip structure + */ +static int ndfc_chip_init(struct ndfc_controller *ndfc, + struct device_node *node) +{ + struct device_node *flash_np; + struct nand_chip *chip = &ndfc->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; + chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; + chip->cmd_ctrl = ndfc_hwcontrol; + chip->dev_ready = ndfc_ready; + chip->select_chip = ndfc_select_chip; + chip->chip_delay = 50; + chip->controller = &ndfc->ndfc_control; + chip->read_buf = ndfc_read_buf; + chip->write_buf = ndfc_write_buf; + chip->ecc.correct = nand_correct_data; + chip->ecc.hwctl = ndfc_enable_hwecc; + chip->ecc.calculate = ndfc_calculate_ecc; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 256; + chip->ecc.bytes = 3; + chip->ecc.strength = 1; + nand_set_controller_data(chip, ndfc); + + mtd->dev.parent = &ndfc->ofdev->dev; + + flash_np = of_get_next_child(node, NULL); + if (!flash_np) + return -ENODEV; + nand_set_flash_node(chip, flash_np); + + mtd->name = kasprintf(GFP_KERNEL, "%s.%s", dev_name(&ndfc->ofdev->dev), + flash_np->name); + if (!mtd->name) { + ret = -ENOMEM; + goto err; + } + + ret = nand_scan(mtd, 1); + if (ret) + goto err; + + ret = mtd_device_register(mtd, NULL, 0); + +err: + of_node_put(flash_np); + if (ret) + kfree(mtd->name); + return ret; +} + +static int ndfc_probe(struct platform_device *ofdev) +{ + struct ndfc_controller *ndfc; + const __be32 *reg; + u32 ccr; + u32 cs; + int err, len; + + /* Read the reg property to get the chip select */ + reg = of_get_property(ofdev->dev.of_node, "reg", &len); + if (reg == NULL || len != 12) { + dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); + return -ENOENT; + } + + cs = be32_to_cpu(reg[0]); + if (cs >= NDFC_MAX_CS) { + dev_err(&ofdev->dev, "invalid CS number (%d)\n", cs); + return -EINVAL; + } + + ndfc = &ndfc_ctrl[cs]; + ndfc->chip_select = cs; + + nand_hw_control_init(&ndfc->ndfc_control); + ndfc->ofdev = ofdev; + dev_set_drvdata(&ofdev->dev, ndfc); + + ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0); + if (!ndfc->ndfcbase) { + dev_err(&ofdev->dev, "failed to get memory\n"); + return -EIO; + } + + ccr = NDFC_CCR_BS(ndfc->chip_select); + + /* It is ok if ccr does not exist - just default to 0 */ + reg = of_get_property(ofdev->dev.of_node, "ccr", NULL); + if (reg) + ccr |= be32_to_cpup(reg); + + out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); + + /* Set the bank settings if given */ + reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL); + if (reg) { + int offset = NDFC_BCFG0 + (ndfc->chip_select << 2); + out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg)); + } + + err = ndfc_chip_init(ndfc, ofdev->dev.of_node); + if (err) { + iounmap(ndfc->ndfcbase); + return err; + } + + return 0; +} + +static int ndfc_remove(struct platform_device *ofdev) +{ + struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); + struct mtd_info *mtd = nand_to_mtd(&ndfc->chip); + + nand_release(mtd); + kfree(mtd->name); + + return 0; +} + +static const struct of_device_id ndfc_match[] = { + { .compatible = "ibm,ndfc", }, + {} +}; +MODULE_DEVICE_TABLE(of, ndfc_match); + +static struct platform_driver ndfc_driver = { + .driver = { + .name = "ndfc", + .of_match_table = ndfc_match, + }, + .probe = ndfc_probe, + .remove = ndfc_remove, +}; + +module_platform_driver(ndfc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Gleixner "); +MODULE_DESCRIPTION("OF Platform driver for NDFC"); diff --git a/drivers/mtd/nand/raw/nuc900_nand.c b/drivers/mtd/nand/raw/nuc900_nand.c new file mode 100644 index 000000000000..af5b32c9a791 --- /dev/null +++ b/drivers/mtd/nand/raw/nuc900_nand.c @@ -0,0 +1,306 @@ +/* + * Copyright © 2009 Nuvoton technology corporation. + * + * Wan ZongShun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation;version 2 of the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define REG_FMICSR 0x00 +#define REG_SMCSR 0xa0 +#define REG_SMISR 0xac +#define REG_SMCMD 0xb0 +#define REG_SMADDR 0xb4 +#define REG_SMDATA 0xb8 + +#define RESET_FMI 0x01 +#define NAND_EN 0x08 +#define READYBUSY (0x01 << 18) + +#define SWRST 0x01 +#define PSIZE (0x01 << 3) +#define DMARWEN (0x03 << 1) +#define BUSWID (0x01 << 4) +#define ECC4EN (0x01 << 5) +#define WP (0x01 << 24) +#define NANDCS (0x01 << 25) +#define ENDADDR (0x01 << 31) + +#define read_data_reg(dev) \ + __raw_readl((dev)->reg + REG_SMDATA) + +#define write_data_reg(dev, val) \ + __raw_writel((val), (dev)->reg + REG_SMDATA) + +#define write_cmd_reg(dev, val) \ + __raw_writel((val), (dev)->reg + REG_SMCMD) + +#define write_addr_reg(dev, val) \ + __raw_writel((val), (dev)->reg + REG_SMADDR) + +struct nuc900_nand { + struct nand_chip chip; + void __iomem *reg; + struct clk *clk; + spinlock_t lock; +}; + +static inline struct nuc900_nand *mtd_to_nuc900(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct nuc900_nand, chip); +} + +static const struct mtd_partition partitions[] = { + { + .name = "NAND FS 0", + .offset = 0, + .size = 8 * 1024 * 1024 + }, + { + .name = "NAND FS 1", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +}; + +static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd) +{ + unsigned char ret; + struct nuc900_nand *nand = mtd_to_nuc900(mtd); + + ret = (unsigned char)read_data_reg(nand); + + return ret; +} + +static void nuc900_nand_read_buf(struct mtd_info *mtd, + unsigned char *buf, int len) +{ + int i; + struct nuc900_nand *nand = mtd_to_nuc900(mtd); + + for (i = 0; i < len; i++) + buf[i] = (unsigned char)read_data_reg(nand); +} + +static void nuc900_nand_write_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) +{ + int i; + struct nuc900_nand *nand = mtd_to_nuc900(mtd); + + for (i = 0; i < len; i++) + write_data_reg(nand, buf[i]); +} + +static int nuc900_check_rb(struct nuc900_nand *nand) +{ + unsigned int val; + spin_lock(&nand->lock); + val = __raw_readl(nand->reg + REG_SMISR); + val &= READYBUSY; + spin_unlock(&nand->lock); + + return val; +} + +static int nuc900_nand_devready(struct mtd_info *mtd) +{ + struct nuc900_nand *nand = mtd_to_nuc900(mtd); + int ready; + + ready = (nuc900_check_rb(nand)) ? 1 : 0; + return ready; +} + +static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + struct nuc900_nand *nand = mtd_to_nuc900(mtd); + + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + write_cmd_reg(nand, command & 0xff); + + if (column != -1 || page_addr != -1) { + + if (column != -1) { + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + write_addr_reg(nand, column); + write_addr_reg(nand, column >> 8 | ENDADDR); + } + if (page_addr != -1) { + write_addr_reg(nand, page_addr); + + if (chip->options & NAND_ROW_ADDR_3) { + write_addr_reg(nand, page_addr >> 8); + write_addr_reg(nand, page_addr >> 16 | ENDADDR); + } else { + write_addr_reg(nand, page_addr >> 8 | ENDADDR); + } + } + } + + switch (command) { + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + + write_cmd_reg(nand, NAND_CMD_STATUS); + write_cmd_reg(nand, command); + + while (!nuc900_check_rb(nand)) + ; + + return; + + case NAND_CMD_RNDOUT: + write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); + return; + + case NAND_CMD_READ0: + + write_cmd_reg(nand, NAND_CMD_READSTART); + default: + + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + while (!chip->dev_ready(mtd)) + ; +} + + +static void nuc900_nand_enable(struct nuc900_nand *nand) +{ + unsigned int val; + spin_lock(&nand->lock); + __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); + + val = __raw_readl(nand->reg + REG_FMICSR); + + if (!(val & NAND_EN)) + __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); + + val = __raw_readl(nand->reg + REG_SMCSR); + + val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); + val |= WP; + + __raw_writel(val, nand->reg + REG_SMCSR); + + spin_unlock(&nand->lock); +} + +static int nuc900_nand_probe(struct platform_device *pdev) +{ + struct nuc900_nand *nuc900_nand; + struct nand_chip *chip; + struct mtd_info *mtd; + struct resource *res; + + nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand), + GFP_KERNEL); + if (!nuc900_nand) + return -ENOMEM; + chip = &(nuc900_nand->chip); + mtd = nand_to_mtd(chip); + + mtd->dev.parent = &pdev->dev; + spin_lock_init(&nuc900_nand->lock); + + nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(nuc900_nand->clk)) + return -ENOENT; + clk_enable(nuc900_nand->clk); + + chip->cmdfunc = nuc900_nand_command_lp; + chip->dev_ready = nuc900_nand_devready; + chip->read_byte = nuc900_nand_read_byte; + chip->write_buf = nuc900_nand_write_buf; + chip->read_buf = nuc900_nand_read_buf; + chip->chip_delay = 50; + chip->options = 0; + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nuc900_nand->reg)) + return PTR_ERR(nuc900_nand->reg); + + nuc900_nand_enable(nuc900_nand); + + if (nand_scan(mtd, 1)) + return -ENXIO; + + mtd_device_register(mtd, partitions, ARRAY_SIZE(partitions)); + + platform_set_drvdata(pdev, nuc900_nand); + + return 0; +} + +static int nuc900_nand_remove(struct platform_device *pdev) +{ + struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); + + nand_release(nand_to_mtd(&nuc900_nand->chip)); + clk_disable(nuc900_nand->clk); + + return 0; +} + +static struct platform_driver nuc900_nand_driver = { + .probe = nuc900_nand_probe, + .remove = nuc900_nand_remove, + .driver = { + .name = "nuc900-fmi", + }, +}; + +module_platform_driver(nuc900_nand_driver); + +MODULE_AUTHOR("Wan ZongShun "); +MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:nuc900-fmi"); diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c new file mode 100644 index 000000000000..8cdf7d3d8fa7 --- /dev/null +++ b/drivers/mtd/nand/raw/omap2.c @@ -0,0 +1,2316 @@ +/* + * Copyright © 2004 Texas Instruments, Jian Zhang + * Copyright © 2004 Micron Technology Inc. + * Copyright © 2004 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define DRIVER_NAME "omap2-nand" +#define OMAP_NAND_TIMEOUT_MS 5000 + +#define NAND_Ecc_P1e (1 << 0) +#define NAND_Ecc_P2e (1 << 1) +#define NAND_Ecc_P4e (1 << 2) +#define NAND_Ecc_P8e (1 << 3) +#define NAND_Ecc_P16e (1 << 4) +#define NAND_Ecc_P32e (1 << 5) +#define NAND_Ecc_P64e (1 << 6) +#define NAND_Ecc_P128e (1 << 7) +#define NAND_Ecc_P256e (1 << 8) +#define NAND_Ecc_P512e (1 << 9) +#define NAND_Ecc_P1024e (1 << 10) +#define NAND_Ecc_P2048e (1 << 11) + +#define NAND_Ecc_P1o (1 << 16) +#define NAND_Ecc_P2o (1 << 17) +#define NAND_Ecc_P4o (1 << 18) +#define NAND_Ecc_P8o (1 << 19) +#define NAND_Ecc_P16o (1 << 20) +#define NAND_Ecc_P32o (1 << 21) +#define NAND_Ecc_P64o (1 << 22) +#define NAND_Ecc_P128o (1 << 23) +#define NAND_Ecc_P256o (1 << 24) +#define NAND_Ecc_P512o (1 << 25) +#define NAND_Ecc_P1024o (1 << 26) +#define NAND_Ecc_P2048o (1 << 27) + +#define TF(value) (value ? 1 : 0) + +#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0) +#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1) +#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2) +#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3) +#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4) +#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5) +#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6) +#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7) + +#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0) +#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1) +#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2) +#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3) +#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4) +#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5) +#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6) +#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7) + +#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0) +#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1) +#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2) +#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3) +#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4) +#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5) +#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6) +#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7) + +#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0) +#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1) +#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2) +#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3) +#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4) +#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5) +#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6) +#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7) + +#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) +#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) + +#define PREFETCH_CONFIG1_CS_SHIFT 24 +#define ECC_CONFIG_CS_SHIFT 1 +#define CS_MASK 0x7 +#define ENABLE_PREFETCH (0x1 << 7) +#define DMA_MPU_MODE_SHIFT 2 +#define ECCSIZE0_SHIFT 12 +#define ECCSIZE1_SHIFT 22 +#define ECC1RESULTSIZE 0x1 +#define ECCCLEAR 0x100 +#define ECC1 0x1 +#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 +#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) +#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) +#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) +#define STATUS_BUFF_EMPTY 0x00000001 + +#define SECTOR_BYTES 512 +/* 4 bit padding to make byte aligned, 56 = 52 + 4 */ +#define BCH4_BIT_PAD 4 + +/* GPMC ecc engine settings for read */ +#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ +#define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */ +#define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */ +#define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */ +#define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */ + +/* GPMC ecc engine settings for write */ +#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */ +#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ +#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ + +#define BADBLOCK_MARKER_LENGTH 2 + +static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55, + 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78, + 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93, + 0x07, 0x0e}; +static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, + 0xac, 0x6b, 0xff, 0x99, 0x7b}; +static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; + +/* Shared among all NAND instances to synchronize access to the ECC Engine */ +static struct nand_hw_control omap_gpmc_controller = { + .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock), + .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq), +}; + +struct omap_nand_info { + struct nand_chip nand; + struct platform_device *pdev; + + int gpmc_cs; + bool dev_ready; + enum nand_io xfer_type; + int devsize; + enum omap_ecc ecc_opt; + struct device_node *elm_of_node; + + unsigned long phys_base; + struct completion comp; + struct dma_chan *dma; + int gpmc_irq_fifo; + int gpmc_irq_count; + enum { + OMAP_NAND_IO_READ = 0, /* read */ + OMAP_NAND_IO_WRITE, /* write */ + } iomode; + u_char *buf; + int buf_len; + /* Interface to GPMC */ + struct gpmc_nand_regs reg; + struct gpmc_nand_ops *ops; + bool flash_bbt; + /* fields specific for BCHx_HW ECC scheme */ + struct device *elm_dev; + /* NAND ready gpio */ + struct gpio_desc *ready_gpiod; +}; + +static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand); +} + +/** + * omap_prefetch_enable - configures and starts prefetch transfer + * @cs: cs (chip select) number + * @fifo_th: fifo threshold to be used for read/ write + * @dma_mode: dma mode enable (1) or disable (0) + * @u32_count: number of bytes to be transferred + * @is_write: prefetch read(0) or write post(1) mode + */ +static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode, + unsigned int u32_count, int is_write, struct omap_nand_info *info) +{ + u32 val; + + if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) + return -1; + + if (readl(info->reg.gpmc_prefetch_control)) + return -EBUSY; + + /* Set the amount of bytes to be prefetched */ + writel(u32_count, info->reg.gpmc_prefetch_config2); + + /* Set dma/mpu mode, the prefetch read / post write and + * enable the engine. Set which cs is has requested for. + */ + val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) | + PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH | + (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1)); + writel(val, info->reg.gpmc_prefetch_config1); + + /* Start the prefetch engine */ + writel(0x1, info->reg.gpmc_prefetch_control); + + return 0; +} + +/** + * omap_prefetch_reset - disables and stops the prefetch engine + */ +static int omap_prefetch_reset(int cs, struct omap_nand_info *info) +{ + u32 config1; + + /* check if the same module/cs is trying to reset */ + config1 = readl(info->reg.gpmc_prefetch_config1); + if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs) + return -EINVAL; + + /* Stop the PFPW engine */ + writel(0x0, info->reg.gpmc_prefetch_control); + + /* Reset/disable the PFPW engine */ + writel(0x0, info->reg.gpmc_prefetch_config1); + + return 0; +} + +/** + * omap_hwcontrol - hardware specific access to control-lines + * @mtd: MTD device structure + * @cmd: command to device + * @ctrl: + * NAND_NCE: bit 0 -> don't care + * NAND_CLE: bit 1 -> Command Latch + * NAND_ALE: bit 2 -> Address Latch + * + * NOTE: boards may use different bits for these!! + */ +static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + + if (cmd != NAND_CMD_NONE) { + if (ctrl & NAND_CLE) + writeb(cmd, info->reg.gpmc_nand_command); + + else if (ctrl & NAND_ALE) + writeb(cmd, info->reg.gpmc_nand_address); + + else /* NAND_NCE */ + writeb(cmd, info->reg.gpmc_nand_data); + } +} + +/** + * omap_read_buf8 - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + + ioread8_rep(nand->IO_ADDR_R, buf, len); +} + +/** + * omap_write_buf8 - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + u_char *p = (u_char *)buf; + bool status; + + while (len--) { + iowrite8(*p++, info->nand.IO_ADDR_W); + /* wait until buffer is available for write */ + do { + status = info->ops->nand_writebuffer_empty(); + } while (!status); + } +} + +/** + * omap_read_buf16 - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + + ioread16_rep(nand->IO_ADDR_R, buf, len / 2); +} + +/** + * omap_write_buf16 - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + u16 *p = (u16 *) buf; + bool status; + /* FIXME try bursts of writesw() or DMA ... */ + len >>= 1; + + while (len--) { + iowrite16(*p++, info->nand.IO_ADDR_W); + /* wait until buffer is available for write */ + do { + status = info->ops->nand_writebuffer_empty(); + } while (!status); + } +} + +/** + * omap_read_buf_pref - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + uint32_t r_count = 0; + int ret = 0; + u32 *p = (u32 *)buf; + + /* take care of subpage reads */ + if (len % 4) { + if (info->nand.options & NAND_BUSWIDTH_16) + omap_read_buf16(mtd, buf, len % 4); + else + omap_read_buf8(mtd, buf, len % 4); + p = (u32 *) (buf + len % 4); + len -= len % 4; + } + + /* configure and start prefetch transfer */ + ret = omap_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info); + if (ret) { + /* PFPW engine is busy, use cpu copy method */ + if (info->nand.options & NAND_BUSWIDTH_16) + omap_read_buf16(mtd, (u_char *)p, len); + else + omap_read_buf8(mtd, (u_char *)p, len); + } else { + do { + r_count = readl(info->reg.gpmc_prefetch_status); + r_count = PREFETCH_STATUS_FIFO_CNT(r_count); + r_count = r_count >> 2; + ioread32_rep(info->nand.IO_ADDR_R, p, r_count); + p += r_count; + len -= r_count << 2; + } while (len); + /* disable and stop the PFPW engine */ + omap_prefetch_reset(info->gpmc_cs, info); + } +} + +/** + * omap_write_buf_pref - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf_pref(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + uint32_t w_count = 0; + int i = 0, ret = 0; + u16 *p = (u16 *)buf; + unsigned long tim, limit; + u32 val; + + /* take care of subpage writes */ + if (len % 2 != 0) { + writeb(*buf, info->nand.IO_ADDR_W); + p = (u16 *)(buf + 1); + len--; + } + + /* configure and start prefetch transfer */ + ret = omap_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info); + if (ret) { + /* PFPW engine is busy, use cpu copy method */ + if (info->nand.options & NAND_BUSWIDTH_16) + omap_write_buf16(mtd, (u_char *)p, len); + else + omap_write_buf8(mtd, (u_char *)p, len); + } else { + while (len) { + w_count = readl(info->reg.gpmc_prefetch_status); + w_count = PREFETCH_STATUS_FIFO_CNT(w_count); + w_count = w_count >> 1; + for (i = 0; (i < w_count) && len; i++, len -= 2) + iowrite16(*p++, info->nand.IO_ADDR_W); + } + /* wait for data to flushed-out before reset the prefetch */ + tim = 0; + limit = (loops_per_jiffy * + msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); + do { + cpu_relax(); + val = readl(info->reg.gpmc_prefetch_status); + val = PREFETCH_STATUS_COUNT(val); + } while (val && (tim++ < limit)); + + /* disable and stop the PFPW engine */ + omap_prefetch_reset(info->gpmc_cs, info); + } +} + +/* + * omap_nand_dma_callback: callback on the completion of dma transfer + * @data: pointer to completion data structure + */ +static void omap_nand_dma_callback(void *data) +{ + complete((struct completion *) data); +} + +/* + * omap_nand_dma_transfer: configure and start dma transfer + * @mtd: MTD device structure + * @addr: virtual address in RAM of source/destination + * @len: number of data bytes to be transferred + * @is_write: flag for read/write operation + */ +static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, + unsigned int len, int is_write) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + struct dma_async_tx_descriptor *tx; + enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : + DMA_FROM_DEVICE; + struct scatterlist sg; + unsigned long tim, limit; + unsigned n; + int ret; + u32 val; + + if (!virt_addr_valid(addr)) + goto out_copy; + + sg_init_one(&sg, addr, len); + n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); + if (n == 0) { + dev_err(&info->pdev->dev, + "Couldn't DMA map a %d byte buffer\n", len); + goto out_copy; + } + + tx = dmaengine_prep_slave_sg(info->dma, &sg, n, + is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx) + goto out_copy_unmap; + + tx->callback = omap_nand_dma_callback; + tx->callback_param = &info->comp; + dmaengine_submit(tx); + + init_completion(&info->comp); + + /* setup and start DMA using dma_addr */ + dma_async_issue_pending(info->dma); + + /* configure and start prefetch transfer */ + ret = omap_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info); + if (ret) + /* PFPW engine is busy, use cpu copy method */ + goto out_copy_unmap; + + wait_for_completion(&info->comp); + tim = 0; + limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); + + do { + cpu_relax(); + val = readl(info->reg.gpmc_prefetch_status); + val = PREFETCH_STATUS_COUNT(val); + } while (val && (tim++ < limit)); + + /* disable and stop the PFPW engine */ + omap_prefetch_reset(info->gpmc_cs, info); + + dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); + return 0; + +out_copy_unmap: + dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); +out_copy: + if (info->nand.options & NAND_BUSWIDTH_16) + is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) + : omap_write_buf16(mtd, (u_char *) addr, len); + else + is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) + : omap_write_buf8(mtd, (u_char *) addr, len); + return 0; +} + +/** + * omap_read_buf_dma_pref - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len) +{ + if (len <= mtd->oobsize) + omap_read_buf_pref(mtd, buf, len); + else + /* start transfer in DMA mode */ + omap_nand_dma_transfer(mtd, buf, len, 0x0); +} + +/** + * omap_write_buf_dma_pref - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf_dma_pref(struct mtd_info *mtd, + const u_char *buf, int len) +{ + if (len <= mtd->oobsize) + omap_write_buf_pref(mtd, buf, len); + else + /* start transfer in DMA mode */ + omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1); +} + +/* + * omap_nand_irq - GPMC irq handler + * @this_irq: gpmc irq number + * @dev: omap_nand_info structure pointer is passed here + */ +static irqreturn_t omap_nand_irq(int this_irq, void *dev) +{ + struct omap_nand_info *info = (struct omap_nand_info *) dev; + u32 bytes; + + bytes = readl(info->reg.gpmc_prefetch_status); + bytes = PREFETCH_STATUS_FIFO_CNT(bytes); + bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */ + if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */ + if (this_irq == info->gpmc_irq_count) + goto done; + + if (info->buf_len && (info->buf_len < bytes)) + bytes = info->buf_len; + else if (!info->buf_len) + bytes = 0; + iowrite32_rep(info->nand.IO_ADDR_W, + (u32 *)info->buf, bytes >> 2); + info->buf = info->buf + bytes; + info->buf_len -= bytes; + + } else { + ioread32_rep(info->nand.IO_ADDR_R, + (u32 *)info->buf, bytes >> 2); + info->buf = info->buf + bytes; + + if (this_irq == info->gpmc_irq_count) + goto done; + } + + return IRQ_HANDLED; + +done: + complete(&info->comp); + + disable_irq_nosync(info->gpmc_irq_fifo); + disable_irq_nosync(info->gpmc_irq_count); + + return IRQ_HANDLED; +} + +/* + * omap_read_buf_irq_pref - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + int ret = 0; + + if (len <= mtd->oobsize) { + omap_read_buf_pref(mtd, buf, len); + return; + } + + info->iomode = OMAP_NAND_IO_READ; + info->buf = buf; + init_completion(&info->comp); + + /* configure and start prefetch transfer */ + ret = omap_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info); + if (ret) + /* PFPW engine is busy, use cpu copy method */ + goto out_copy; + + info->buf_len = len; + + enable_irq(info->gpmc_irq_count); + enable_irq(info->gpmc_irq_fifo); + + /* waiting for read to complete */ + wait_for_completion(&info->comp); + + /* disable and stop the PFPW engine */ + omap_prefetch_reset(info->gpmc_cs, info); + return; + +out_copy: + if (info->nand.options & NAND_BUSWIDTH_16) + omap_read_buf16(mtd, buf, len); + else + omap_read_buf8(mtd, buf, len); +} + +/* + * omap_write_buf_irq_pref - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf_irq_pref(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + int ret = 0; + unsigned long tim, limit; + u32 val; + + if (len <= mtd->oobsize) { + omap_write_buf_pref(mtd, buf, len); + return; + } + + info->iomode = OMAP_NAND_IO_WRITE; + info->buf = (u_char *) buf; + init_completion(&info->comp); + + /* configure and start prefetch transfer : size=24 */ + ret = omap_prefetch_enable(info->gpmc_cs, + (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info); + if (ret) + /* PFPW engine is busy, use cpu copy method */ + goto out_copy; + + info->buf_len = len; + + enable_irq(info->gpmc_irq_count); + enable_irq(info->gpmc_irq_fifo); + + /* waiting for write to complete */ + wait_for_completion(&info->comp); + + /* wait for data to flushed-out before reset the prefetch */ + tim = 0; + limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); + do { + val = readl(info->reg.gpmc_prefetch_status); + val = PREFETCH_STATUS_COUNT(val); + cpu_relax(); + } while (val && (tim++ < limit)); + + /* disable and stop the PFPW engine */ + omap_prefetch_reset(info->gpmc_cs, info); + return; + +out_copy: + if (info->nand.options & NAND_BUSWIDTH_16) + omap_write_buf16(mtd, buf, len); + else + omap_write_buf8(mtd, buf, len); +} + +/** + * gen_true_ecc - This function will generate true ECC value + * @ecc_buf: buffer to store ecc code + * + * This generated true ECC value can be used when correcting + * data read from NAND flash memory core + */ +static void gen_true_ecc(u8 *ecc_buf) +{ + u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | + ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); + + ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | + P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp)); + ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | + P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp)); + ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | + P1e(tmp) | P2048o(tmp) | P2048e(tmp)); +} + +/** + * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data + * @ecc_data1: ecc code from nand spare area + * @ecc_data2: ecc code from hardware register obtained from hardware ecc + * @page_data: page data + * + * This function compares two ECC's and indicates if there is an error. + * If the error can be corrected it will be corrected to the buffer. + * If there is no error, %0 is returned. If there is an error but it + * was corrected, %1 is returned. Otherwise, %-1 is returned. + */ +static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ + u8 *ecc_data2, /* read from register */ + u8 *page_data) +{ + uint i; + u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; + u8 comp0_bit[8], comp1_bit[8], comp2_bit[8]; + u8 ecc_bit[24]; + u8 ecc_sum = 0; + u8 find_bit = 0; + uint find_byte = 0; + int isEccFF; + + isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF); + + gen_true_ecc(ecc_data1); + gen_true_ecc(ecc_data2); + + for (i = 0; i <= 2; i++) { + *(ecc_data1 + i) = ~(*(ecc_data1 + i)); + *(ecc_data2 + i) = ~(*(ecc_data2 + i)); + } + + for (i = 0; i < 8; i++) { + tmp0_bit[i] = *ecc_data1 % 2; + *ecc_data1 = *ecc_data1 / 2; + } + + for (i = 0; i < 8; i++) { + tmp1_bit[i] = *(ecc_data1 + 1) % 2; + *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2; + } + + for (i = 0; i < 8; i++) { + tmp2_bit[i] = *(ecc_data1 + 2) % 2; + *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2; + } + + for (i = 0; i < 8; i++) { + comp0_bit[i] = *ecc_data2 % 2; + *ecc_data2 = *ecc_data2 / 2; + } + + for (i = 0; i < 8; i++) { + comp1_bit[i] = *(ecc_data2 + 1) % 2; + *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2; + } + + for (i = 0; i < 8; i++) { + comp2_bit[i] = *(ecc_data2 + 2) % 2; + *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2; + } + + for (i = 0; i < 6; i++) + ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2]; + + for (i = 0; i < 8; i++) + ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i]; + + for (i = 0; i < 8; i++) + ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i]; + + ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0]; + ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1]; + + for (i = 0; i < 24; i++) + ecc_sum += ecc_bit[i]; + + switch (ecc_sum) { + case 0: + /* Not reached because this function is not called if + * ECC values are equal + */ + return 0; + + case 1: + /* Uncorrectable error */ + pr_debug("ECC UNCORRECTED_ERROR 1\n"); + return -EBADMSG; + + case 11: + /* UN-Correctable error */ + pr_debug("ECC UNCORRECTED_ERROR B\n"); + return -EBADMSG; + + case 12: + /* Correctable error */ + find_byte = (ecc_bit[23] << 8) + + (ecc_bit[21] << 7) + + (ecc_bit[19] << 6) + + (ecc_bit[17] << 5) + + (ecc_bit[15] << 4) + + (ecc_bit[13] << 3) + + (ecc_bit[11] << 2) + + (ecc_bit[9] << 1) + + ecc_bit[7]; + + find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; + + pr_debug("Correcting single bit ECC error at offset: " + "%d, bit: %d\n", find_byte, find_bit); + + page_data[find_byte] ^= (1 << find_bit); + + return 1; + default: + if (isEccFF) { + if (ecc_data2[0] == 0 && + ecc_data2[1] == 0 && + ecc_data2[2] == 0) + return 0; + } + pr_debug("UNCORRECTED_ERROR default\n"); + return -EBADMSG; + } +} + +/** + * omap_correct_data - Compares the ECC read with HW generated ECC + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + * + * Compares the ecc read from nand spare area with ECC registers values + * and if ECC's mismatched, it will call 'omap_compare_ecc' for error + * detection and correction. If there are no errors, %0 is returned. If + * there were errors and all of the errors were corrected, the number of + * corrected errors is returned. If uncorrectable errors exist, %-1 is + * returned. + */ +static int omap_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + int blockCnt = 0, i = 0, ret = 0; + int stat = 0; + + /* Ex NAND_ECC_HW12_2048 */ + if ((info->nand.ecc.mode == NAND_ECC_HW) && + (info->nand.ecc.size == 2048)) + blockCnt = 4; + else + blockCnt = 1; + + for (i = 0; i < blockCnt; i++) { + if (memcmp(read_ecc, calc_ecc, 3) != 0) { + ret = omap_compare_ecc(read_ecc, calc_ecc, dat); + if (ret < 0) + return ret; + /* keep track of the number of corrected errors */ + stat += ret; + } + read_ecc += 3; + calc_ecc += 3; + dat += 512; + } + return stat; +} + +/** + * omap_calcuate_ecc - Generate non-inverted ECC bytes. + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + * + * Using noninverted ECC can be considered ugly since writing a blank + * page ie. padding will clear the ECC bytes. This is no problem as long + * nobody is trying to write data on the seemingly unused page. Reading + * an erased page will produce an ECC mismatch between generated and read + * ECC bytes that has to be dealt with separately. + */ +static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + u32 val; + + val = readl(info->reg.gpmc_ecc_config); + if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs) + return -EINVAL; + + /* read ecc result */ + val = readl(info->reg.gpmc_ecc1_result); + *ecc_code++ = val; /* P128e, ..., P1e */ + *ecc_code++ = val >> 16; /* P128o, ..., P1o */ + /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ + *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); + + return 0; +} + +/** + * omap_enable_hwecc - This function enables the hardware ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; + u32 val; + + /* clear ecc and enable bits */ + val = ECCCLEAR | ECC1; + writel(val, info->reg.gpmc_ecc_control); + + /* program ecc and result sizes */ + val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) | + ECC1RESULTSIZE); + writel(val, info->reg.gpmc_ecc_size_config); + + switch (mode) { + case NAND_ECC_READ: + case NAND_ECC_WRITE: + writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); + break; + case NAND_ECC_READSYN: + writel(ECCCLEAR, info->reg.gpmc_ecc_control); + break; + default: + dev_info(&info->pdev->dev, + "error: unrecognized Mode[%d]!\n", mode); + break; + } + + /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ + val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); + writel(val, info->reg.gpmc_ecc_config); +} + +/** + * omap_wait - wait until the command is done + * @mtd: MTD device structure + * @chip: NAND Chip structure + * + * Wait function is called during Program and erase operations and + * the way it is called from MTD layer, we should wait till the NAND + * chip is ready after the programming/erase operation has completed. + * + * Erase can take up to 400ms and program up to 20ms according to + * general NAND and SmartMedia specs + */ +static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct omap_nand_info *info = mtd_to_omap(mtd); + unsigned long timeo = jiffies; + int status, state = this->state; + + if (state == FL_ERASING) + timeo += msecs_to_jiffies(400); + else + timeo += msecs_to_jiffies(20); + + writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command); + while (time_before(jiffies, timeo)) { + status = readb(info->reg.gpmc_nand_data); + if (status & NAND_STATUS_READY) + break; + cond_resched(); + } + + status = readb(info->reg.gpmc_nand_data); + return status; +} + +/** + * omap_dev_ready - checks the NAND Ready GPIO line + * @mtd: MTD device structure + * + * Returns true if ready and false if busy. + */ +static int omap_dev_ready(struct mtd_info *mtd) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + + return gpiod_get_value(info->ready_gpiod); +} + +/** + * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation + * @mtd: MTD device structure + * @mode: Read/Write mode + * + * When using BCH with SW correction (i.e. no ELM), sector size is set + * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode + * for both reading and writing with: + * eccsize0 = 0 (no additional protected byte in spare area) + * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) + */ +static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) +{ + unsigned int bch_type; + unsigned int dev_width, nsectors; + struct omap_nand_info *info = mtd_to_omap(mtd); + enum omap_ecc ecc_opt = info->ecc_opt; + struct nand_chip *chip = mtd_to_nand(mtd); + u32 val, wr_mode; + unsigned int ecc_size1, ecc_size0; + + /* GPMC configurations for calculating ECC */ + switch (ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + bch_type = 0; + nsectors = 1; + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + break; + case OMAP_ECC_BCH4_CODE_HW: + bch_type = 0; + nsectors = chip->ecc.steps; + if (mode == NAND_ECC_READ) { + wr_mode = BCH_WRAPMODE_1; + ecc_size0 = BCH4R_ECC_SIZE0; + ecc_size1 = BCH4R_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + bch_type = 1; + nsectors = 1; + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + break; + case OMAP_ECC_BCH8_CODE_HW: + bch_type = 1; + nsectors = chip->ecc.steps; + if (mode == NAND_ECC_READ) { + wr_mode = BCH_WRAPMODE_1; + ecc_size0 = BCH8R_ECC_SIZE0; + ecc_size1 = BCH8R_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } + break; + case OMAP_ECC_BCH16_CODE_HW: + bch_type = 0x2; + nsectors = chip->ecc.steps; + if (mode == NAND_ECC_READ) { + wr_mode = 0x01; + ecc_size0 = 52; /* ECC bits in nibbles per sector */ + ecc_size1 = 0; /* non-ECC bits in nibbles per sector */ + } else { + wr_mode = 0x01; + ecc_size0 = 0; /* extra bits in nibbles per sector */ + ecc_size1 = 52; /* OOB bits in nibbles per sector */ + } + break; + default: + return; + } + + writel(ECC1, info->reg.gpmc_ecc_control); + + /* Configure ecc size for BCH */ + val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT); + writel(val, info->reg.gpmc_ecc_size_config); + + dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; + + /* BCH configuration */ + val = ((1 << 16) | /* enable BCH */ + (bch_type << 12) | /* BCH4/BCH8/BCH16 */ + (wr_mode << 8) | /* wrap mode */ + (dev_width << 7) | /* bus width */ + (((nsectors-1) & 0x7) << 4) | /* number of sectors */ + (info->gpmc_cs << 1) | /* ECC CS */ + (0x1)); /* enable ECC */ + + writel(val, info->reg.gpmc_ecc_config); + + /* Clear ecc and enable bits */ + writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); +} + +static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; +static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, + 0x97, 0x79, 0xe5, 0x24, 0xb5}; + +/** + * _omap_calculate_ecc_bch - Generate ECC bytes for one sector + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + * @i: The sector number (for a multi sector page) + * + * Support calculating of BCH4/8/16 ECC vectors for one sector + * within a page. Sector number is in @i. + */ +static int _omap_calculate_ecc_bch(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_calc, int i) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + int eccbytes = info->nand.ecc.bytes; + struct gpmc_nand_regs *gpmc_regs = &info->reg; + u8 *ecc_code; + unsigned long bch_val1, bch_val2, bch_val3, bch_val4; + u32 val; + int j; + + ecc_code = ecc_calc; + switch (info->ecc_opt) { + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH8_CODE_HW: + bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); + bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); + bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]); + bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]); + *ecc_code++ = (bch_val4 & 0xFF); + *ecc_code++ = ((bch_val3 >> 24) & 0xFF); + *ecc_code++ = ((bch_val3 >> 16) & 0xFF); + *ecc_code++ = ((bch_val3 >> 8) & 0xFF); + *ecc_code++ = (bch_val3 & 0xFF); + *ecc_code++ = ((bch_val2 >> 24) & 0xFF); + *ecc_code++ = ((bch_val2 >> 16) & 0xFF); + *ecc_code++ = ((bch_val2 >> 8) & 0xFF); + *ecc_code++ = (bch_val2 & 0xFF); + *ecc_code++ = ((bch_val1 >> 24) & 0xFF); + *ecc_code++ = ((bch_val1 >> 16) & 0xFF); + *ecc_code++ = ((bch_val1 >> 8) & 0xFF); + *ecc_code++ = (bch_val1 & 0xFF); + break; + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH4_CODE_HW: + bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); + bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); + *ecc_code++ = ((bch_val2 >> 12) & 0xFF); + *ecc_code++ = ((bch_val2 >> 4) & 0xFF); + *ecc_code++ = ((bch_val2 & 0xF) << 4) | + ((bch_val1 >> 28) & 0xF); + *ecc_code++ = ((bch_val1 >> 20) & 0xFF); + *ecc_code++ = ((bch_val1 >> 12) & 0xFF); + *ecc_code++ = ((bch_val1 >> 4) & 0xFF); + *ecc_code++ = ((bch_val1 & 0xF) << 4); + break; + case OMAP_ECC_BCH16_CODE_HW: + val = readl(gpmc_regs->gpmc_bch_result6[i]); + ecc_code[0] = ((val >> 8) & 0xFF); + ecc_code[1] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result5[i]); + ecc_code[2] = ((val >> 24) & 0xFF); + ecc_code[3] = ((val >> 16) & 0xFF); + ecc_code[4] = ((val >> 8) & 0xFF); + ecc_code[5] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result4[i]); + ecc_code[6] = ((val >> 24) & 0xFF); + ecc_code[7] = ((val >> 16) & 0xFF); + ecc_code[8] = ((val >> 8) & 0xFF); + ecc_code[9] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result3[i]); + ecc_code[10] = ((val >> 24) & 0xFF); + ecc_code[11] = ((val >> 16) & 0xFF); + ecc_code[12] = ((val >> 8) & 0xFF); + ecc_code[13] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result2[i]); + ecc_code[14] = ((val >> 24) & 0xFF); + ecc_code[15] = ((val >> 16) & 0xFF); + ecc_code[16] = ((val >> 8) & 0xFF); + ecc_code[17] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result1[i]); + ecc_code[18] = ((val >> 24) & 0xFF); + ecc_code[19] = ((val >> 16) & 0xFF); + ecc_code[20] = ((val >> 8) & 0xFF); + ecc_code[21] = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result0[i]); + ecc_code[22] = ((val >> 24) & 0xFF); + ecc_code[23] = ((val >> 16) & 0xFF); + ecc_code[24] = ((val >> 8) & 0xFF); + ecc_code[25] = ((val >> 0) & 0xFF); + break; + default: + return -EINVAL; + } + + /* ECC scheme specific syndrome customizations */ + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + /* Add constant polynomial to remainder, so that + * ECC of blank pages results in 0x0 on reading back + */ + for (j = 0; j < eccbytes; j++) + ecc_calc[j] ^= bch4_polynomial[j]; + break; + case OMAP_ECC_BCH4_CODE_HW: + /* Set 8th ECC byte as 0x0 for ROM compatibility */ + ecc_calc[eccbytes - 1] = 0x0; + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + /* Add constant polynomial to remainder, so that + * ECC of blank pages results in 0x0 on reading back + */ + for (j = 0; j < eccbytes; j++) + ecc_calc[j] ^= bch8_polynomial[j]; + break; + case OMAP_ECC_BCH8_CODE_HW: + /* Set 14th ECC byte as 0x0 for ROM compatibility */ + ecc_calc[eccbytes - 1] = 0x0; + break; + case OMAP_ECC_BCH16_CODE_HW: + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + * + * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used + * when SW based correction is required as ECC is required for one sector + * at a time. + */ +static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_calc) +{ + return _omap_calculate_ecc_bch(mtd, dat, ecc_calc, 0); +} + +/** + * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + * + * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go. + */ +static int omap_calculate_ecc_bch_multi(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_calc) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + int eccbytes = info->nand.ecc.bytes; + unsigned long nsectors; + int i, ret; + + nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; + for (i = 0; i < nsectors; i++) { + ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i); + if (ret) + return ret; + + ecc_calc += eccbytes; + } + + return 0; +} + +/** + * erased_sector_bitflips - count bit flips + * @data: data sector buffer + * @oob: oob buffer + * @info: omap_nand_info + * + * Check the bit flips in erased page falls below correctable level. + * If falls below, report the page as erased with correctable bit + * flip, else report as uncorrectable page. + */ +static int erased_sector_bitflips(u_char *data, u_char *oob, + struct omap_nand_info *info) +{ + int flip_bits = 0, i; + + for (i = 0; i < info->nand.ecc.size; i++) { + flip_bits += hweight8(~data[i]); + if (flip_bits > info->nand.ecc.strength) + return 0; + } + + for (i = 0; i < info->nand.ecc.bytes - 1; i++) { + flip_bits += hweight8(~oob[i]); + if (flip_bits > info->nand.ecc.strength) + return 0; + } + + /* + * Bit flips falls in correctable level. + * Fill data area with 0xFF + */ + if (flip_bits) { + memset(data, 0xFF, info->nand.ecc.size); + memset(oob, 0xFF, info->nand.ecc.bytes); + } + + return flip_bits; +} + +/** + * omap_elm_correct_data - corrects page data area in case error reported + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + * + * Calculated ecc vector reported as zero in case of non-error pages. + * In case of non-zero ecc vector, first filter out erased-pages, and + * then process data via ELM to detect bit-flips. + */ +static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + struct nand_ecc_ctrl *ecc = &info->nand.ecc; + int eccsteps = info->nand.ecc.steps; + int i , j, stat = 0; + int eccflag, actual_eccbytes; + struct elm_errorvec err_vec[ERROR_VECTOR_MAX]; + u_char *ecc_vec = calc_ecc; + u_char *spare_ecc = read_ecc; + u_char *erased_ecc_vec; + u_char *buf; + int bitflip_count; + bool is_error_reported = false; + u32 bit_pos, byte_pos, error_max, pos; + int err; + + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW: + /* omit 7th ECC byte reserved for ROM code compatibility */ + actual_eccbytes = ecc->bytes - 1; + erased_ecc_vec = bch4_vector; + break; + case OMAP_ECC_BCH8_CODE_HW: + /* omit 14th ECC byte reserved for ROM code compatibility */ + actual_eccbytes = ecc->bytes - 1; + erased_ecc_vec = bch8_vector; + break; + case OMAP_ECC_BCH16_CODE_HW: + actual_eccbytes = ecc->bytes; + erased_ecc_vec = bch16_vector; + break; + default: + dev_err(&info->pdev->dev, "invalid driver configuration\n"); + return -EINVAL; + } + + /* Initialize elm error vector to zero */ + memset(err_vec, 0, sizeof(err_vec)); + + for (i = 0; i < eccsteps ; i++) { + eccflag = 0; /* initialize eccflag */ + + /* + * Check any error reported, + * In case of error, non zero ecc reported. + */ + for (j = 0; j < actual_eccbytes; j++) { + if (calc_ecc[j] != 0) { + eccflag = 1; /* non zero ecc, error present */ + break; + } + } + + if (eccflag == 1) { + if (memcmp(calc_ecc, erased_ecc_vec, + actual_eccbytes) == 0) { + /* + * calc_ecc[] matches pattern for ECC(all 0xff) + * so this is definitely an erased-page + */ + } else { + buf = &data[info->nand.ecc.size * i]; + /* + * count number of 0-bits in read_buf. + * This check can be removed once a similar + * check is introduced in generic NAND driver + */ + bitflip_count = erased_sector_bitflips( + buf, read_ecc, info); + if (bitflip_count) { + /* + * number of 0-bits within ECC limits + * So this may be an erased-page + */ + stat += bitflip_count; + } else { + /* + * Too many 0-bits. It may be a + * - programmed-page, OR + * - erased-page with many bit-flips + * So this page requires check by ELM + */ + err_vec[i].error_reported = true; + is_error_reported = true; + } + } + } + + /* Update the ecc vector */ + calc_ecc += ecc->bytes; + read_ecc += ecc->bytes; + } + + /* Check if any error reported */ + if (!is_error_reported) + return stat; + + /* Decode BCH error using ELM module */ + elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); + + err = 0; + for (i = 0; i < eccsteps; i++) { + if (err_vec[i].error_uncorrectable) { + dev_err(&info->pdev->dev, + "uncorrectable bit-flips found\n"); + err = -EBADMSG; + } else if (err_vec[i].error_reported) { + for (j = 0; j < err_vec[i].error_count; j++) { + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW: + /* Add 4 bits to take care of padding */ + pos = err_vec[i].error_loc[j] + + BCH4_BIT_PAD; + break; + case OMAP_ECC_BCH8_CODE_HW: + case OMAP_ECC_BCH16_CODE_HW: + pos = err_vec[i].error_loc[j]; + break; + default: + return -EINVAL; + } + error_max = (ecc->size + actual_eccbytes) * 8; + /* Calculate bit position of error */ + bit_pos = pos % 8; + + /* Calculate byte position of error */ + byte_pos = (error_max - pos - 1) / 8; + + if (pos < error_max) { + if (byte_pos < 512) { + pr_debug("bitflip@dat[%d]=%x\n", + byte_pos, data[byte_pos]); + data[byte_pos] ^= 1 << bit_pos; + } else { + pr_debug("bitflip@oob[%d]=%x\n", + (byte_pos - 512), + spare_ecc[byte_pos - 512]); + spare_ecc[byte_pos - 512] ^= + 1 << bit_pos; + } + } else { + dev_err(&info->pdev->dev, + "invalid bit-flip @ %d:%d\n", + byte_pos, bit_pos); + err = -EBADMSG; + } + } + } + + /* Update number of correctable errors */ + stat += err_vec[i].error_count; + + /* Update page data with sector size */ + data += ecc->size; + spare_ecc += ecc->bytes; + } + + return (err) ? err : stat; +} + +/** + * omap_write_page_bch - BCH ecc based write page function for entire page + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page + * + * Custom write page method evolved to support multi sector writing in one shot + */ +static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + int ret; + uint8_t *ecc_calc = chip->ecc.calc_buf; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + /* Enable GPMC ecc engine */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + /* Write data */ + chip->write_buf(mtd, buf, mtd->writesize); + + /* Update ecc vector from GPMC result registers */ + omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]); + + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + /* Write ecc vector to OOB area */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +/** + * omap_write_subpage_bch - BCH hardware ECC based subpage write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @offset: column address of subpage within the page + * @data_len: data length + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * OMAP optimized subpage write method. + */ +static int omap_write_subpage_bch(struct mtd_info *mtd, + struct nand_chip *chip, u32 offset, + u32 data_len, const u8 *buf, + int oob_required, int page) +{ + u8 *ecc_calc = chip->ecc.calc_buf; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int ecc_steps = chip->ecc.steps; + u32 start_step = offset / ecc_size; + u32 end_step = (offset + data_len - 1) / ecc_size; + int step, ret = 0; + + /* + * Write entire page at one go as it would be optimal + * as ECC is calculated by hardware. + * ECC is calculated for all subpages but we choose + * only what we want. + */ + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + /* Enable GPMC ECC engine */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + /* Write data */ + chip->write_buf(mtd, buf, mtd->writesize); + + for (step = 0; step < ecc_steps; step++) { + /* mask ECC of un-touched subpages by padding 0xFF */ + if (step < start_step || step > end_step) + memset(ecc_calc, 0xff, ecc_bytes); + else + ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step); + + if (ret) + return ret; + + buf += ecc_size; + ecc_calc += ecc_bytes; + } + + /* copy calculated ECC for whole page to chip->buffer->oob */ + /* this include masked-value(0xFF) for unwritten subpages */ + ecc_calc = chip->ecc.calc_buf; + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + /* write OOB buffer to NAND device */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +/** + * omap_read_page_bch - BCH ecc based page read function for entire page + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module + * used for error correction. + * Custom method evolved to support ELM error correction & multi sector + * reading. On reading page data area is read along with OOB data with + * ecc engine enabled. ecc vector updated after read of OOB data. + * For non error pages ecc vector reported as zero. + */ +static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + uint8_t *ecc_calc = chip->ecc.calc_buf; + uint8_t *ecc_code = chip->ecc.code_buf; + int stat, ret; + unsigned int max_bitflips = 0; + + nand_read_page_op(chip, page, 0, NULL, 0); + + /* Enable GPMC ecc engine */ + chip->ecc.hwctl(mtd, NAND_ECC_READ); + + /* Read data */ + chip->read_buf(mtd, buf, mtd->writesize); + + /* Read oob bytes */ + nand_change_read_column_op(chip, + mtd->writesize + BADBLOCK_MARKER_LENGTH, + chip->oob_poi + BADBLOCK_MARKER_LENGTH, + chip->ecc.total, false); + + /* Calculate ecc bytes */ + omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc); + + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc); + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + + return max_bitflips; +} + +/** + * is_elm_present - checks for presence of ELM module by scanning DT nodes + * @omap_nand_info: NAND device structure containing platform data + */ +static bool is_elm_present(struct omap_nand_info *info, + struct device_node *elm_node) +{ + struct platform_device *pdev; + + /* check whether elm-id is passed via DT */ + if (!elm_node) { + dev_err(&info->pdev->dev, "ELM devicetree node not found\n"); + return false; + } + pdev = of_find_device_by_node(elm_node); + /* check whether ELM device is registered */ + if (!pdev) { + dev_err(&info->pdev->dev, "ELM device not found\n"); + return false; + } + /* ELM module available, now configure it */ + info->elm_dev = &pdev->dev; + return true; +} + +static bool omap2_nand_ecc_check(struct omap_nand_info *info) +{ + bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm; + + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + ecc_needs_omap_bch = false; + ecc_needs_bch = true; + ecc_needs_elm = false; + break; + case OMAP_ECC_BCH4_CODE_HW: + case OMAP_ECC_BCH8_CODE_HW: + case OMAP_ECC_BCH16_CODE_HW: + ecc_needs_omap_bch = true; + ecc_needs_bch = false; + ecc_needs_elm = true; + break; + default: + ecc_needs_omap_bch = false; + ecc_needs_bch = false; + ecc_needs_elm = false; + break; + } + + if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_BCH)) { + dev_err(&info->pdev->dev, + "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + return false; + } + if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) { + dev_err(&info->pdev->dev, + "CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); + return false; + } + if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) { + dev_err(&info->pdev->dev, "ELM not available\n"); + return false; + } + + return true; +} + +static const char * const nand_xfer_types[] = { + [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", + [NAND_OMAP_POLLED] = "polled", + [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", + [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", +}; + +static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) +{ + struct device_node *child = dev->of_node; + int i; + const char *s; + u32 cs; + + if (of_property_read_u32(child, "reg", &cs) < 0) { + dev_err(dev, "reg not found in DT\n"); + return -EINVAL; + } + + info->gpmc_cs = cs; + + /* detect availability of ELM module. Won't be present pre-OMAP4 */ + info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); + if (!info->elm_of_node) { + info->elm_of_node = of_parse_phandle(child, "elm_id", 0); + if (!info->elm_of_node) + dev_dbg(dev, "ti,elm-id not in DT\n"); + } + + /* select ecc-scheme for NAND */ + if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { + dev_err(dev, "ti,nand-ecc-opt not found\n"); + return -EINVAL; + } + + if (!strcmp(s, "sw")) { + info->ecc_opt = OMAP_ECC_HAM1_CODE_SW; + } else if (!strcmp(s, "ham1") || + !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) { + info->ecc_opt = OMAP_ECC_HAM1_CODE_HW; + } else if (!strcmp(s, "bch4")) { + if (info->elm_of_node) + info->ecc_opt = OMAP_ECC_BCH4_CODE_HW; + else + info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; + } else if (!strcmp(s, "bch8")) { + if (info->elm_of_node) + info->ecc_opt = OMAP_ECC_BCH8_CODE_HW; + else + info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; + } else if (!strcmp(s, "bch16")) { + info->ecc_opt = OMAP_ECC_BCH16_CODE_HW; + } else { + dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n"); + return -EINVAL; + } + + /* select data transfer mode */ + if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) { + for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { + if (!strcasecmp(s, nand_xfer_types[i])) { + info->xfer_type = i; + return 0; + } + } + + dev_err(dev, "unrecognized value for ti,nand-xfer-type\n"); + return -EINVAL; + } + + return 0; +} + +static int omap_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + struct nand_chip *chip = &info->nand; + int off = BADBLOCK_MARKER_LENGTH; + + if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && + !(chip->options & NAND_BUSWIDTH_16)) + off = 1; + + if (section) + return -ERANGE; + + oobregion->offset = off; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int omap_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + struct nand_chip *chip = &info->nand; + int off = BADBLOCK_MARKER_LENGTH; + + if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && + !(chip->options & NAND_BUSWIDTH_16)) + off = 1; + + if (section) + return -ERANGE; + + off += chip->ecc.total; + if (off >= mtd->oobsize) + return -ERANGE; + + oobregion->offset = off; + oobregion->length = mtd->oobsize - off; + + return 0; +} + +static const struct mtd_ooblayout_ops omap_ooblayout_ops = { + .ecc = omap_ooblayout_ecc, + .free = omap_ooblayout_free, +}; + +static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int off = BADBLOCK_MARKER_LENGTH; + + if (section >= chip->ecc.steps) + return -ERANGE; + + /* + * When SW correction is employed, one OMAP specific marker byte is + * reserved after each ECC step. + */ + oobregion->offset = off + (section * (chip->ecc.bytes + 1)); + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int off = BADBLOCK_MARKER_LENGTH; + + if (section) + return -ERANGE; + + /* + * When SW correction is employed, one OMAP specific marker byte is + * reserved after each ECC step. + */ + off += ((chip->ecc.bytes + 1) * chip->ecc.steps); + if (off >= mtd->oobsize) + return -ERANGE; + + oobregion->offset = off; + oobregion->length = mtd->oobsize - off; + + return 0; +} + +static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = { + .ecc = omap_sw_ooblayout_ecc, + .free = omap_sw_ooblayout_free, +}; + +static int omap_nand_probe(struct platform_device *pdev) +{ + struct omap_nand_info *info; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + int err; + dma_cap_mask_t mask; + struct resource *res; + struct device *dev = &pdev->dev; + int min_oobbytes = BADBLOCK_MARKER_LENGTH; + int oobbytes_per_step; + + info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdev = pdev; + + err = omap_get_dt_info(dev, info); + if (err) + return err; + + info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); + if (!info->ops) { + dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); + return -ENODEV; + } + + nand_chip = &info->nand; + mtd = nand_to_mtd(nand_chip); + mtd->dev.parent = &pdev->dev; + nand_chip->ecc.priv = NULL; + nand_set_flash_node(nand_chip, dev->of_node); + + if (!mtd->name) { + mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "omap2-nand.%d", info->gpmc_cs); + if (!mtd->name) { + dev_err(&pdev->dev, "Failed to set MTD name\n"); + return -ENOMEM; + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nand_chip->IO_ADDR_R)) + return PTR_ERR(nand_chip->IO_ADDR_R); + + info->phys_base = res->start; + + nand_chip->controller = &omap_gpmc_controller; + + nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; + nand_chip->cmd_ctrl = omap_hwcontrol; + + info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", + GPIOD_IN); + if (IS_ERR(info->ready_gpiod)) { + dev_err(dev, "failed to get ready gpio\n"); + return PTR_ERR(info->ready_gpiod); + } + + /* + * If RDY/BSY line is connected to OMAP then use the omap ready + * function and the generic nand_wait function which reads the status + * register after monitoring the RDY/BSY line. Otherwise use a standard + * chip delay which is slightly more than tR (AC Timing) of the NAND + * device and read status register until you get a failure or success + */ + if (info->ready_gpiod) { + nand_chip->dev_ready = omap_dev_ready; + nand_chip->chip_delay = 0; + } else { + nand_chip->waitfunc = omap_wait; + nand_chip->chip_delay = 50; + } + + if (info->flash_bbt) + nand_chip->bbt_options |= NAND_BBT_USE_FLASH; + + /* scan NAND device connected to chip controller */ + nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; + err = nand_scan_ident(mtd, 1, NULL); + if (err) { + dev_err(&info->pdev->dev, + "scan failed, may be bus-width mismatch\n"); + goto return_error; + } + + if (nand_chip->bbt_options & NAND_BBT_USE_FLASH) + nand_chip->bbt_options |= NAND_BBT_NO_OOB; + else + nand_chip->options |= NAND_SKIP_BBTSCAN; + + /* re-populate low-level callbacks based on xfer modes */ + switch (info->xfer_type) { + case NAND_OMAP_PREFETCH_POLLED: + nand_chip->read_buf = omap_read_buf_pref; + nand_chip->write_buf = omap_write_buf_pref; + break; + + case NAND_OMAP_POLLED: + /* Use nand_base defaults for {read,write}_buf */ + break; + + case NAND_OMAP_PREFETCH_DMA: + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + info->dma = dma_request_chan(pdev->dev.parent, "rxtx"); + + if (IS_ERR(info->dma)) { + dev_err(&pdev->dev, "DMA engine request failed\n"); + err = PTR_ERR(info->dma); + goto return_error; + } else { + struct dma_slave_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.src_addr = info->phys_base; + cfg.dst_addr = info->phys_base; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_maxburst = 16; + cfg.dst_maxburst = 16; + err = dmaengine_slave_config(info->dma, &cfg); + if (err) { + dev_err(&pdev->dev, "DMA engine slave config failed: %d\n", + err); + goto return_error; + } + nand_chip->read_buf = omap_read_buf_dma_pref; + nand_chip->write_buf = omap_write_buf_dma_pref; + } + break; + + case NAND_OMAP_PREFETCH_IRQ: + info->gpmc_irq_fifo = platform_get_irq(pdev, 0); + if (info->gpmc_irq_fifo <= 0) { + dev_err(&pdev->dev, "error getting fifo irq\n"); + err = -ENODEV; + goto return_error; + } + err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-fifo", info); + if (err) { + dev_err(&pdev->dev, "requesting irq(%d) error:%d", + info->gpmc_irq_fifo, err); + info->gpmc_irq_fifo = 0; + goto return_error; + } + + info->gpmc_irq_count = platform_get_irq(pdev, 1); + if (info->gpmc_irq_count <= 0) { + dev_err(&pdev->dev, "error getting count irq\n"); + err = -ENODEV; + goto return_error; + } + err = devm_request_irq(&pdev->dev, info->gpmc_irq_count, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-count", info); + if (err) { + dev_err(&pdev->dev, "requesting irq(%d) error:%d", + info->gpmc_irq_count, err); + info->gpmc_irq_count = 0; + goto return_error; + } + + nand_chip->read_buf = omap_read_buf_irq_pref; + nand_chip->write_buf = omap_write_buf_irq_pref; + + break; + + default: + dev_err(&pdev->dev, + "xfer_type(%d) not supported!\n", info->xfer_type); + err = -EINVAL; + goto return_error; + } + + if (!omap2_nand_ecc_check(info)) { + err = -EINVAL; + goto return_error; + } + + /* + * Bail out earlier to let NAND_ECC_SOFT code create its own + * ooblayout instead of using ours. + */ + if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { + nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.algo = NAND_ECC_HAMMING; + goto scan_tail; + } + + /* populate MTD interface based on ECC scheme */ + switch (info->ecc_opt) { + case OMAP_ECC_HAM1_CODE_HW: + pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.bytes = 3; + nand_chip->ecc.size = 512; + nand_chip->ecc.strength = 1; + nand_chip->ecc.calculate = omap_calculate_ecc; + nand_chip->ecc.hwctl = omap_enable_hwecc; + nand_chip->ecc.correct = omap_correct_data; + mtd_set_ooblayout(mtd, &omap_ooblayout_ops); + oobbytes_per_step = nand_chip->ecc.bytes; + + if (!(nand_chip->options & NAND_BUSWIDTH_16)) + min_oobbytes = 1; + + break; + + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 7; + nand_chip->ecc.strength = 4; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = nand_bch_correct_data; + nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; + mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); + /* Reserve one byte for the OMAP marker */ + oobbytes_per_step = nand_chip->ecc.bytes + 1; + /* software bch library is used for locating errors */ + nand_chip->ecc.priv = nand_bch_init(mtd); + if (!nand_chip->ecc.priv) { + dev_err(&info->pdev->dev, "unable to use BCH library\n"); + err = -EINVAL; + goto return_error; + } + break; + + case OMAP_ECC_BCH4_CODE_HW: + pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + /* 14th bit is kept reserved for ROM-code compatibility */ + nand_chip->ecc.bytes = 7 + 1; + nand_chip->ecc.strength = 4; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + nand_chip->ecc.write_subpage = omap_write_subpage_bch; + mtd_set_ooblayout(mtd, &omap_ooblayout_ops); + oobbytes_per_step = nand_chip->ecc.bytes; + + err = elm_config(info->elm_dev, BCH4_ECC, + mtd->writesize / nand_chip->ecc.size, + nand_chip->ecc.size, nand_chip->ecc.bytes); + if (err < 0) + goto return_error; + break; + + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 13; + nand_chip->ecc.strength = 8; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = nand_bch_correct_data; + nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; + mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); + /* Reserve one byte for the OMAP marker */ + oobbytes_per_step = nand_chip->ecc.bytes + 1; + /* software bch library is used for locating errors */ + nand_chip->ecc.priv = nand_bch_init(mtd); + if (!nand_chip->ecc.priv) { + dev_err(&info->pdev->dev, "unable to use BCH library\n"); + err = -EINVAL; + goto return_error; + } + break; + + case OMAP_ECC_BCH8_CODE_HW: + pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + /* 14th bit is kept reserved for ROM-code compatibility */ + nand_chip->ecc.bytes = 13 + 1; + nand_chip->ecc.strength = 8; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + nand_chip->ecc.write_subpage = omap_write_subpage_bch; + mtd_set_ooblayout(mtd, &omap_ooblayout_ops); + oobbytes_per_step = nand_chip->ecc.bytes; + + err = elm_config(info->elm_dev, BCH8_ECC, + mtd->writesize / nand_chip->ecc.size, + nand_chip->ecc.size, nand_chip->ecc.bytes); + if (err < 0) + goto return_error; + + break; + + case OMAP_ECC_BCH16_CODE_HW: + pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 26; + nand_chip->ecc.strength = 16; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + nand_chip->ecc.write_subpage = omap_write_subpage_bch; + mtd_set_ooblayout(mtd, &omap_ooblayout_ops); + oobbytes_per_step = nand_chip->ecc.bytes; + + err = elm_config(info->elm_dev, BCH16_ECC, + mtd->writesize / nand_chip->ecc.size, + nand_chip->ecc.size, nand_chip->ecc.bytes); + if (err < 0) + goto return_error; + + break; + default: + dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n"); + err = -EINVAL; + goto return_error; + } + + /* check if NAND device's OOB is enough to store ECC signatures */ + min_oobbytes += (oobbytes_per_step * + (mtd->writesize / nand_chip->ecc.size)); + if (mtd->oobsize < min_oobbytes) { + dev_err(&info->pdev->dev, + "not enough OOB bytes required = %d, available=%d\n", + min_oobbytes, mtd->oobsize); + err = -EINVAL; + goto return_error; + } + +scan_tail: + /* second phase scan */ + err = nand_scan_tail(mtd); + if (err) + goto return_error; + + err = mtd_device_register(mtd, NULL, 0); + if (err) + goto return_error; + + platform_set_drvdata(pdev, mtd); + + return 0; + +return_error: + if (!IS_ERR_OR_NULL(info->dma)) + dma_release_channel(info->dma); + if (nand_chip->ecc.priv) { + nand_bch_free(nand_chip->ecc.priv); + nand_chip->ecc.priv = NULL; + } + return err; +} + +static int omap_nand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct omap_nand_info *info = mtd_to_omap(mtd); + if (nand_chip->ecc.priv) { + nand_bch_free(nand_chip->ecc.priv); + nand_chip->ecc.priv = NULL; + } + if (info->dma) + dma_release_channel(info->dma); + nand_release(mtd); + return 0; +} + +static const struct of_device_id omap_nand_ids[] = { + { .compatible = "ti,omap2-nand", }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_nand_ids); + +static struct platform_driver omap_nand_driver = { + .probe = omap_nand_probe, + .remove = omap_nand_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(omap_nand_ids), + }, +}; + +module_platform_driver(omap_nand_driver); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c new file mode 100644 index 000000000000..a3f32f939cc1 --- /dev/null +++ b/drivers/mtd/nand/raw/omap_elm.c @@ -0,0 +1,578 @@ +/* + * Error Location Module + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define DRIVER_NAME "omap-elm" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ELM_SYSCONFIG 0x010 +#define ELM_IRQSTATUS 0x018 +#define ELM_IRQENABLE 0x01c +#define ELM_LOCATION_CONFIG 0x020 +#define ELM_PAGE_CTRL 0x080 +#define ELM_SYNDROME_FRAGMENT_0 0x400 +#define ELM_SYNDROME_FRAGMENT_1 0x404 +#define ELM_SYNDROME_FRAGMENT_2 0x408 +#define ELM_SYNDROME_FRAGMENT_3 0x40c +#define ELM_SYNDROME_FRAGMENT_4 0x410 +#define ELM_SYNDROME_FRAGMENT_5 0x414 +#define ELM_SYNDROME_FRAGMENT_6 0x418 +#define ELM_LOCATION_STATUS 0x800 +#define ELM_ERROR_LOCATION_0 0x880 + +/* ELM Interrupt Status Register */ +#define INTR_STATUS_PAGE_VALID BIT(8) + +/* ELM Interrupt Enable Register */ +#define INTR_EN_PAGE_MASK BIT(8) + +/* ELM Location Configuration Register */ +#define ECC_BCH_LEVEL_MASK 0x3 + +/* ELM syndrome */ +#define ELM_SYNDROME_VALID BIT(16) + +/* ELM_LOCATION_STATUS Register */ +#define ECC_CORRECTABLE_MASK BIT(8) +#define ECC_NB_ERRORS_MASK 0x1f + +/* ELM_ERROR_LOCATION_0-15 Registers */ +#define ECC_ERROR_LOCATION_MASK 0x1fff + +#define ELM_ECC_SIZE 0x7ff + +#define SYNDROME_FRAGMENT_REG_SIZE 0x40 +#define ERROR_LOCATION_SIZE 0x100 + +struct elm_registers { + u32 elm_irqenable; + u32 elm_sysconfig; + u32 elm_location_config; + u32 elm_page_ctrl; + u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX]; +}; + +struct elm_info { + struct device *dev; + void __iomem *elm_base; + struct completion elm_completion; + struct list_head list; + enum bch_ecc bch_type; + struct elm_registers elm_regs; + int ecc_steps; + int ecc_syndrome_size; +}; + +static LIST_HEAD(elm_devices); + +static void elm_write_reg(struct elm_info *info, int offset, u32 val) +{ + writel(val, info->elm_base + offset); +} + +static u32 elm_read_reg(struct elm_info *info, int offset) +{ + return readl(info->elm_base + offset); +} + +/** + * elm_config - Configure ELM module + * @dev: ELM device + * @bch_type: Type of BCH ecc + */ +int elm_config(struct device *dev, enum bch_ecc bch_type, + int ecc_steps, int ecc_step_size, int ecc_syndrome_size) +{ + u32 reg_val; + struct elm_info *info = dev_get_drvdata(dev); + + if (!info) { + dev_err(dev, "Unable to configure elm - device not probed?\n"); + return -EPROBE_DEFER; + } + /* ELM cannot detect ECC errors for chunks > 1KB */ + if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) { + dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size); + return -EINVAL; + } + /* ELM support 8 error syndrome process */ + if (ecc_steps > ERROR_VECTOR_MAX) { + dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps); + return -EINVAL; + } + + reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16); + elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val); + info->bch_type = bch_type; + info->ecc_steps = ecc_steps; + info->ecc_syndrome_size = ecc_syndrome_size; + + return 0; +} +EXPORT_SYMBOL(elm_config); + +/** + * elm_configure_page_mode - Enable/Disable page mode + * @info: elm info + * @index: index number of syndrome fragment vector + * @enable: enable/disable flag for page mode + * + * Enable page mode for syndrome fragment index + */ +static void elm_configure_page_mode(struct elm_info *info, int index, + bool enable) +{ + u32 reg_val; + + reg_val = elm_read_reg(info, ELM_PAGE_CTRL); + if (enable) + reg_val |= BIT(index); /* enable page mode */ + else + reg_val &= ~BIT(index); /* disable page mode */ + + elm_write_reg(info, ELM_PAGE_CTRL, reg_val); +} + +/** + * elm_load_syndrome - Load ELM syndrome reg + * @info: elm info + * @err_vec: elm error vectors + * @ecc: buffer with calculated ecc + * + * Load syndrome fragment registers with calculated ecc in reverse order. + */ +static void elm_load_syndrome(struct elm_info *info, + struct elm_errorvec *err_vec, u8 *ecc) +{ + int i, offset; + u32 val; + + for (i = 0; i < info->ecc_steps; i++) { + + /* Check error reported */ + if (err_vec[i].error_reported) { + elm_configure_page_mode(info, i, true); + offset = ELM_SYNDROME_FRAGMENT_0 + + SYNDROME_FRAGMENT_REG_SIZE * i; + switch (info->bch_type) { + case BCH8_ECC: + /* syndrome fragment 0 = ecc[9-12B] */ + val = cpu_to_be32(*(u32 *) &ecc[9]); + elm_write_reg(info, offset, val); + + /* syndrome fragment 1 = ecc[5-8B] */ + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[5]); + elm_write_reg(info, offset, val); + + /* syndrome fragment 2 = ecc[1-4B] */ + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[1]); + elm_write_reg(info, offset, val); + + /* syndrome fragment 3 = ecc[0B] */ + offset += 4; + val = ecc[0]; + elm_write_reg(info, offset, val); + break; + case BCH4_ECC: + /* syndrome fragment 0 = ecc[20-52b] bits */ + val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) | + ((ecc[2] & 0xf) << 28); + elm_write_reg(info, offset, val); + + /* syndrome fragment 1 = ecc[0-20b] bits */ + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; + elm_write_reg(info, offset, val); + break; + case BCH16_ECC: + val = cpu_to_be32(*(u32 *) &ecc[22]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[18]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[14]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[10]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[6]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[2]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16; + elm_write_reg(info, offset, val); + break; + default: + pr_err("invalid config bch_type\n"); + } + } + + /* Update ecc pointer with ecc byte size */ + ecc += info->ecc_syndrome_size; + } +} + +/** + * elm_start_processing - start elm syndrome processing + * @info: elm info + * @err_vec: elm error vectors + * + * Set syndrome valid bit for syndrome fragment registers for which + * elm syndrome fragment registers are loaded. This enables elm module + * to start processing syndrome vectors. + */ +static void elm_start_processing(struct elm_info *info, + struct elm_errorvec *err_vec) +{ + int i, offset; + u32 reg_val; + + /* + * Set syndrome vector valid, so that ELM module + * will process it for vectors error is reported + */ + for (i = 0; i < info->ecc_steps; i++) { + if (err_vec[i].error_reported) { + offset = ELM_SYNDROME_FRAGMENT_6 + + SYNDROME_FRAGMENT_REG_SIZE * i; + reg_val = elm_read_reg(info, offset); + reg_val |= ELM_SYNDROME_VALID; + elm_write_reg(info, offset, reg_val); + } + } +} + +/** + * elm_error_correction - locate correctable error position + * @info: elm info + * @err_vec: elm error vectors + * + * On completion of processing by elm module, error location status + * register updated with correctable/uncorrectable error information. + * In case of correctable errors, number of errors located from + * elm location status register & read the positions from + * elm error location register. + */ +static void elm_error_correction(struct elm_info *info, + struct elm_errorvec *err_vec) +{ + int i, j, errors = 0; + int offset; + u32 reg_val; + + for (i = 0; i < info->ecc_steps; i++) { + + /* Check error reported */ + if (err_vec[i].error_reported) { + offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i; + reg_val = elm_read_reg(info, offset); + + /* Check correctable error or not */ + if (reg_val & ECC_CORRECTABLE_MASK) { + offset = ELM_ERROR_LOCATION_0 + + ERROR_LOCATION_SIZE * i; + + /* Read count of correctable errors */ + err_vec[i].error_count = reg_val & + ECC_NB_ERRORS_MASK; + + /* Update the error locations in error vector */ + for (j = 0; j < err_vec[i].error_count; j++) { + + reg_val = elm_read_reg(info, offset); + err_vec[i].error_loc[j] = reg_val & + ECC_ERROR_LOCATION_MASK; + + /* Update error location register */ + offset += 4; + } + + errors += err_vec[i].error_count; + } else { + err_vec[i].error_uncorrectable = true; + } + + /* Clearing interrupts for processed error vectors */ + elm_write_reg(info, ELM_IRQSTATUS, BIT(i)); + + /* Disable page mode */ + elm_configure_page_mode(info, i, false); + } + } +} + +/** + * elm_decode_bch_error_page - Locate error position + * @dev: device pointer + * @ecc_calc: calculated ECC bytes from GPMC + * @err_vec: elm error vectors + * + * Called with one or more error reported vectors & vectors with + * error reported is updated in err_vec[].error_reported + */ +void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, + struct elm_errorvec *err_vec) +{ + struct elm_info *info = dev_get_drvdata(dev); + u32 reg_val; + + /* Enable page mode interrupt */ + reg_val = elm_read_reg(info, ELM_IRQSTATUS); + elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID); + elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK); + + /* Load valid ecc byte to syndrome fragment register */ + elm_load_syndrome(info, err_vec, ecc_calc); + + /* Enable syndrome processing for which syndrome fragment is updated */ + elm_start_processing(info, err_vec); + + /* Wait for ELM module to finish locating error correction */ + wait_for_completion(&info->elm_completion); + + /* Disable page mode interrupt */ + reg_val = elm_read_reg(info, ELM_IRQENABLE); + elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK); + elm_error_correction(info, err_vec); +} +EXPORT_SYMBOL(elm_decode_bch_error_page); + +static irqreturn_t elm_isr(int this_irq, void *dev_id) +{ + u32 reg_val; + struct elm_info *info = dev_id; + + reg_val = elm_read_reg(info, ELM_IRQSTATUS); + + /* All error vectors processed */ + if (reg_val & INTR_STATUS_PAGE_VALID) { + elm_write_reg(info, ELM_IRQSTATUS, + reg_val & INTR_STATUS_PAGE_VALID); + complete(&info->elm_completion); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int elm_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res, *irq; + struct elm_info *info; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = &pdev->dev; + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource defined\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + info->elm_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->elm_base)) + return PTR_ERR(info->elm_base); + + ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, + pdev->name, info); + if (ret) { + dev_err(&pdev->dev, "failure requesting %pr\n", irq); + return ret; + } + + pm_runtime_enable(&pdev->dev); + if (pm_runtime_get_sync(&pdev->dev) < 0) { + ret = -EINVAL; + pm_runtime_disable(&pdev->dev); + dev_err(&pdev->dev, "can't enable clock\n"); + return ret; + } + + init_completion(&info->elm_completion); + INIT_LIST_HEAD(&info->list); + list_add(&info->list, &elm_devices); + platform_set_drvdata(pdev, info); + return ret; +} + +static int elm_remove(struct platform_device *pdev) +{ + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +/** + * elm_context_save + * saves ELM configurations to preserve them across Hardware powered-down + */ +static int elm_context_save(struct elm_info *info) +{ + struct elm_registers *regs = &info->elm_regs; + enum bch_ecc bch_type = info->bch_type; + u32 offset = 0, i; + + regs->elm_irqenable = elm_read_reg(info, ELM_IRQENABLE); + regs->elm_sysconfig = elm_read_reg(info, ELM_SYSCONFIG); + regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG); + regs->elm_page_ctrl = elm_read_reg(info, ELM_PAGE_CTRL); + for (i = 0; i < ERROR_VECTOR_MAX; i++) { + offset = i * SYNDROME_FRAGMENT_REG_SIZE; + switch (bch_type) { + case BCH16_ECC: + regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_6 + offset); + regs->elm_syndrome_fragment_5[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_5 + offset); + regs->elm_syndrome_fragment_4[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_4 + offset); + case BCH8_ECC: + regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_3 + offset); + regs->elm_syndrome_fragment_2[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_2 + offset); + case BCH4_ECC: + regs->elm_syndrome_fragment_1[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_1 + offset); + regs->elm_syndrome_fragment_0[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_0 + offset); + break; + default: + return -EINVAL; + } + /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs + * to be saved for all BCH schemes*/ + regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_6 + offset); + } + return 0; +} + +/** + * elm_context_restore + * writes configurations saved duing power-down back into ELM registers + */ +static int elm_context_restore(struct elm_info *info) +{ + struct elm_registers *regs = &info->elm_regs; + enum bch_ecc bch_type = info->bch_type; + u32 offset = 0, i; + + elm_write_reg(info, ELM_IRQENABLE, regs->elm_irqenable); + elm_write_reg(info, ELM_SYSCONFIG, regs->elm_sysconfig); + elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config); + elm_write_reg(info, ELM_PAGE_CTRL, regs->elm_page_ctrl); + for (i = 0; i < ERROR_VECTOR_MAX; i++) { + offset = i * SYNDROME_FRAGMENT_REG_SIZE; + switch (bch_type) { + case BCH16_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, + regs->elm_syndrome_fragment_6[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset, + regs->elm_syndrome_fragment_5[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset, + regs->elm_syndrome_fragment_4[i]); + case BCH8_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, + regs->elm_syndrome_fragment_3[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset, + regs->elm_syndrome_fragment_2[i]); + case BCH4_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset, + regs->elm_syndrome_fragment_1[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset, + regs->elm_syndrome_fragment_0[i]); + break; + default: + return -EINVAL; + } + /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */ + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, + regs->elm_syndrome_fragment_6[i] & + ELM_SYNDROME_VALID); + } + return 0; +} + +static int elm_suspend(struct device *dev) +{ + struct elm_info *info = dev_get_drvdata(dev); + elm_context_save(info); + pm_runtime_put_sync(dev); + return 0; +} + +static int elm_resume(struct device *dev) +{ + struct elm_info *info = dev_get_drvdata(dev); + pm_runtime_get_sync(dev); + elm_context_restore(info); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); + +#ifdef CONFIG_OF +static const struct of_device_id elm_of_match[] = { + { .compatible = "ti,am3352-elm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, elm_of_match); +#endif + +static struct platform_driver elm_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(elm_of_match), + .pm = &elm_pm_ops, + }, + .probe = elm_probe, + .remove = elm_remove, +}; + +module_platform_driver(elm_driver); + +MODULE_DESCRIPTION("ELM driver for BCH error correction"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c new file mode 100644 index 000000000000..7825fd3ce66b --- /dev/null +++ b/drivers/mtd/nand/raw/orion_nand.c @@ -0,0 +1,232 @@ +/* + * NAND support for Marvell Orion SoC platforms + * + * Tzachi Perelstein + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct orion_nand_info { + struct nand_chip chip; + struct clk *clk; +}; + +static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *nc = mtd_to_nand(mtd); + struct orion_nand_data *board = nand_get_controller_data(nc); + u32 offs; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + offs = (1 << board->cle); + else if (ctrl & NAND_ALE) + offs = (1 << board->ale); + else + return; + + if (nc->options & NAND_BUSWIDTH_16) + offs <<= 1; + + writeb(cmd, nc->IO_ADDR_W + offs); +} + +static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + void __iomem *io_base = chip->IO_ADDR_R; +#if __LINUX_ARM_ARCH__ >= 5 + uint64_t *buf64; +#endif + int i = 0; + + while (len && (unsigned long)buf & 7) { + *buf++ = readb(io_base); + len--; + } +#if __LINUX_ARM_ARCH__ >= 5 + buf64 = (uint64_t *)buf; + while (i < len/8) { + /* + * Since GCC has no proper constraint (PR 43518) + * force x variable to r2/r3 registers as ldrd instruction + * requires first register to be even. + */ + register uint64_t x asm ("r2"); + + asm volatile ("ldrd\t%0, [%1]" : "=&r" (x) : "r" (io_base)); + buf64[i++] = x; + } + i *= 8; +#else + readsl(io_base, buf, len/4); + i = len / 4 * 4; +#endif + while (i < len) + buf[i++] = readb(io_base); +} + +static int __init orion_nand_probe(struct platform_device *pdev) +{ + struct orion_nand_info *info; + struct mtd_info *mtd; + struct nand_chip *nc; + struct orion_nand_data *board; + struct resource *res; + void __iomem *io_base; + int ret = 0; + u32 val = 0; + + info = devm_kzalloc(&pdev->dev, + sizeof(struct orion_nand_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + nc = &info->chip; + mtd = nand_to_mtd(nc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + io_base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + if (pdev->dev.of_node) { + board = devm_kzalloc(&pdev->dev, sizeof(struct orion_nand_data), + GFP_KERNEL); + if (!board) + return -ENOMEM; + if (!of_property_read_u32(pdev->dev.of_node, "cle", &val)) + board->cle = (u8)val; + else + board->cle = 0; + if (!of_property_read_u32(pdev->dev.of_node, "ale", &val)) + board->ale = (u8)val; + else + board->ale = 1; + if (!of_property_read_u32(pdev->dev.of_node, + "bank-width", &val)) + board->width = (u8)val * 8; + else + board->width = 8; + if (!of_property_read_u32(pdev->dev.of_node, + "chip-delay", &val)) + board->chip_delay = (u8)val; + } else { + board = dev_get_platdata(&pdev->dev); + } + + mtd->dev.parent = &pdev->dev; + + nand_set_controller_data(nc, board); + nand_set_flash_node(nc, pdev->dev.of_node); + nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; + nc->cmd_ctrl = orion_nand_cmd_ctrl; + nc->read_buf = orion_nand_read_buf; + nc->ecc.mode = NAND_ECC_SOFT; + nc->ecc.algo = NAND_ECC_HAMMING; + + if (board->chip_delay) + nc->chip_delay = board->chip_delay; + + WARN(board->width > 16, + "%d bit bus width out of range", + board->width); + + if (board->width == 16) + nc->options |= NAND_BUSWIDTH_16; + + if (board->dev_ready) + nc->dev_ready = board->dev_ready; + + platform_set_drvdata(pdev, info); + + /* Not all platforms can gate the clock, so it is not + an error if the clock does not exists. */ + info->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->clk)) { + ret = PTR_ERR(info->clk); + if (ret == -ENOENT) { + info->clk = NULL; + } else { + dev_err(&pdev->dev, "failed to get clock!\n"); + return ret; + } + } + + ret = clk_prepare_enable(info->clk); + if (ret) { + dev_err(&pdev->dev, "failed to prepare clock!\n"); + return ret; + } + + ret = nand_scan(mtd, 1); + if (ret) + goto no_dev; + + mtd->name = "orion_nand"; + ret = mtd_device_register(mtd, board->parts, board->nr_parts); + if (ret) { + nand_release(mtd); + goto no_dev; + } + + return 0; + +no_dev: + clk_disable_unprepare(info->clk); + return ret; +} + +static int orion_nand_remove(struct platform_device *pdev) +{ + struct orion_nand_info *info = platform_get_drvdata(pdev); + struct nand_chip *chip = &info->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + + nand_release(mtd); + + clk_disable_unprepare(info->clk); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id orion_nand_of_match_table[] = { + { .compatible = "marvell,orion-nand", }, + {}, +}; +MODULE_DEVICE_TABLE(of, orion_nand_of_match_table); +#endif + +static struct platform_driver orion_nand_driver = { + .remove = orion_nand_remove, + .driver = { + .name = "orion_nand", + .of_match_table = of_match_ptr(orion_nand_of_match_table), + }, +}; + +module_platform_driver_probe(orion_nand_driver, orion_nand_probe); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tzachi Perelstein"); +MODULE_DESCRIPTION("NAND glue for Orion platforms"); +MODULE_ALIAS("platform:orion_nand"); diff --git a/drivers/mtd/nand/raw/oxnas_nand.c b/drivers/mtd/nand/raw/oxnas_nand.c new file mode 100644 index 000000000000..d649d5944826 --- /dev/null +++ b/drivers/mtd/nand/raw/oxnas_nand.c @@ -0,0 +1,206 @@ +/* + * Oxford Semiconductor OXNAS NAND driver + + * Copyright (C) 2016 Neil Armstrong + * Heavily based on plat_nand.c : + * Author: Vitaly Wool + * Copyright (C) 2013 Ma Haijun + * Copyright (C) 2012 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Nand commands */ +#define OXNAS_NAND_CMD_ALE BIT(18) +#define OXNAS_NAND_CMD_CLE BIT(19) + +#define OXNAS_NAND_MAX_CHIPS 1 + +struct oxnas_nand_ctrl { + struct nand_hw_control base; + void __iomem *io_base; + struct clk *clk; + struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS]; +}; + +static uint8_t oxnas_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); + + return readb(oxnas->io_base); +} + +static void oxnas_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); + + ioread8_rep(oxnas->io_base, buf, len); +} + +static void oxnas_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); + + iowrite8_rep(oxnas->io_base, buf, len); +} + +/* Single CS command control */ +static void oxnas_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); + + if (ctrl & NAND_CLE) + writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_CLE); + else if (ctrl & NAND_ALE) + writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_ALE); +} + +/* + * Probe for the NAND device. + */ +static int oxnas_nand_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *nand_np; + struct oxnas_nand_ctrl *oxnas; + struct nand_chip *chip; + struct mtd_info *mtd; + struct resource *res; + int nchips = 0; + int count = 0; + int err = 0; + + /* Allocate memory for the device structure (and zero it) */ + oxnas = devm_kzalloc(&pdev->dev, sizeof(*oxnas), + GFP_KERNEL); + if (!oxnas) + return -ENOMEM; + + nand_hw_control_init(&oxnas->base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + oxnas->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(oxnas->io_base)) + return PTR_ERR(oxnas->io_base); + + oxnas->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(oxnas->clk)) + oxnas->clk = NULL; + + /* Only a single chip node is supported */ + count = of_get_child_count(np); + if (count > 1) + return -EINVAL; + + err = clk_prepare_enable(oxnas->clk); + if (err) + return err; + + device_reset_optional(&pdev->dev); + + for_each_child_of_node(np, nand_np) { + chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), + GFP_KERNEL); + if (!chip) { + err = -ENOMEM; + goto err_clk_unprepare; + } + + chip->controller = &oxnas->base; + + nand_set_flash_node(chip, nand_np); + nand_set_controller_data(chip, oxnas); + + mtd = nand_to_mtd(chip); + mtd->dev.parent = &pdev->dev; + mtd->priv = chip; + + chip->cmd_ctrl = oxnas_nand_cmd_ctrl; + chip->read_buf = oxnas_nand_read_buf; + chip->read_byte = oxnas_nand_read_byte; + chip->write_buf = oxnas_nand_write_buf; + chip->chip_delay = 30; + + /* Scan to find existence of the device */ + err = nand_scan(mtd, 1); + if (err) + goto err_clk_unprepare; + + err = mtd_device_register(mtd, NULL, 0); + if (err) { + nand_release(mtd); + goto err_clk_unprepare; + } + + oxnas->chips[nchips] = chip; + ++nchips; + } + + /* Exit if no chips found */ + if (!nchips) { + err = -ENODEV; + goto err_clk_unprepare; + } + + platform_set_drvdata(pdev, oxnas); + + return 0; + +err_clk_unprepare: + clk_disable_unprepare(oxnas->clk); + return err; +} + +static int oxnas_nand_remove(struct platform_device *pdev) +{ + struct oxnas_nand_ctrl *oxnas = platform_get_drvdata(pdev); + + if (oxnas->chips[0]) + nand_release(nand_to_mtd(oxnas->chips[0])); + + clk_disable_unprepare(oxnas->clk); + + return 0; +} + +static const struct of_device_id oxnas_nand_match[] = { + { .compatible = "oxsemi,ox820-nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, oxnas_nand_match); + +static struct platform_driver oxnas_nand_driver = { + .probe = oxnas_nand_probe, + .remove = oxnas_nand_remove, + .driver = { + .name = "oxnas_nand", + .of_match_table = oxnas_nand_match, + }, +}; + +module_platform_driver(oxnas_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Neil Armstrong "); +MODULE_DESCRIPTION("Oxnas NAND driver"); +MODULE_ALIAS("platform:oxnas_nand"); diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c new file mode 100644 index 000000000000..a47a7e4bd25a --- /dev/null +++ b/drivers/mtd/nand/raw/pasemi_nand.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Author: Egor Martovetsky + * Maintained by: Olof Johansson + * + * Driver for the PWRficient onchip NAND flash interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LBICTRL_LPCCTL_NR 0x00004000 +#define CLE_PIN_CTL 15 +#define ALE_PIN_CTL 14 + +static unsigned int lpcctl; +static struct mtd_info *pasemi_nand_mtd; +static const char driver_name[] = "pasemi-nand"; + +static void pasemi_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + while (len > 0x800) { + memcpy_fromio(buf, chip->IO_ADDR_R, 0x800); + buf += 0x800; + len -= 0x800; + } + memcpy_fromio(buf, chip->IO_ADDR_R, len); +} + +static void pasemi_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + while (len > 0x800) { + memcpy_toio(chip->IO_ADDR_R, buf, 0x800); + buf += 0x800; + len -= 0x800; + } + memcpy_toio(chip->IO_ADDR_R, buf, len); +} + +static void pasemi_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + out_8(chip->IO_ADDR_W + (1 << CLE_PIN_CTL), cmd); + else + out_8(chip->IO_ADDR_W + (1 << ALE_PIN_CTL), cmd); + + /* Push out posted writes */ + eieio(); + inl(lpcctl); +} + +int pasemi_device_ready(struct mtd_info *mtd) +{ + return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); +} + +static int pasemi_nand_probe(struct platform_device *ofdev) +{ + struct device *dev = &ofdev->dev; + struct pci_dev *pdev; + struct device_node *np = dev->of_node; + struct resource res; + struct nand_chip *chip; + int err = 0; + + err = of_address_to_resource(np, 0, &res); + + if (err) + return -EINVAL; + + /* We only support one device at the moment */ + if (pasemi_nand_mtd) + return -ENODEV; + + dev_dbg(dev, "pasemi_nand at %pR\n", &res); + + /* Allocate memory for MTD device structure and private data */ + chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); + if (!chip) { + err = -ENOMEM; + goto out; + } + + pasemi_nand_mtd = nand_to_mtd(chip); + + /* Link the private data with the MTD structure */ + pasemi_nand_mtd->dev.parent = dev; + + chip->IO_ADDR_R = of_iomap(np, 0); + chip->IO_ADDR_W = chip->IO_ADDR_R; + + if (!chip->IO_ADDR_R) { + err = -EIO; + goto out_mtd; + } + + pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa008, NULL); + if (!pdev) { + err = -ENODEV; + goto out_ior; + } + + lpcctl = pci_resource_start(pdev, 0); + pci_dev_put(pdev); + + if (!request_region(lpcctl, 4, driver_name)) { + err = -EBUSY; + goto out_ior; + } + + chip->cmd_ctrl = pasemi_hwcontrol; + chip->dev_ready = pasemi_device_ready; + chip->read_buf = pasemi_read_buf; + chip->write_buf = pasemi_write_buf; + chip->chip_delay = 0; + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + + /* Enable the following for a flash based bad block table */ + chip->bbt_options = NAND_BBT_USE_FLASH; + + /* Scan to find existence of the device */ + err = nand_scan(pasemi_nand_mtd, 1); + if (err) + goto out_lpc; + + if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) { + dev_err(dev, "Unable to register MTD device\n"); + err = -ENODEV; + goto out_lpc; + } + + dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res, + lpcctl); + + return 0; + + out_lpc: + release_region(lpcctl, 4); + out_ior: + iounmap(chip->IO_ADDR_R); + out_mtd: + kfree(chip); + out: + return err; +} + +static int pasemi_nand_remove(struct platform_device *ofdev) +{ + struct nand_chip *chip; + + if (!pasemi_nand_mtd) + return 0; + + chip = mtd_to_nand(pasemi_nand_mtd); + + /* Release resources, unregister device */ + nand_release(pasemi_nand_mtd); + + release_region(lpcctl, 4); + + iounmap(chip->IO_ADDR_R); + + /* Free the MTD device structure */ + kfree(chip); + + pasemi_nand_mtd = NULL; + + return 0; +} + +static const struct of_device_id pasemi_nand_match[] = +{ + { + .compatible = "pasemi,localbus-nand", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, pasemi_nand_match); + +static struct platform_driver pasemi_nand_driver = +{ + .driver = { + .name = driver_name, + .of_match_table = pasemi_nand_match, + }, + .probe = pasemi_nand_probe, + .remove = pasemi_nand_remove, +}; + +module_platform_driver(pasemi_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Egor Martovetsky "); +MODULE_DESCRIPTION("NAND flash interface driver for PA Semi PWRficient"); diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c new file mode 100644 index 000000000000..925a1323604d --- /dev/null +++ b/drivers/mtd/nand/raw/plat_nand.c @@ -0,0 +1,144 @@ +/* + * Generic NAND driver + * + * Author: Vitaly Wool + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct plat_nand_data { + struct nand_chip chip; + void __iomem *io_base; +}; + +/* + * Probe for the NAND device. + */ +static int plat_nand_probe(struct platform_device *pdev) +{ + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); + struct plat_nand_data *data; + struct mtd_info *mtd; + struct resource *res; + const char **part_types; + int err = 0; + + if (!pdata) { + dev_err(&pdev->dev, "platform_nand_data is missing\n"); + return -EINVAL; + } + + if (pdata->chip.nr_chips < 1) { + dev_err(&pdev->dev, "invalid number of chips specified\n"); + return -EINVAL; + } + + /* Allocate memory for the device structure (and zero it) */ + data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->io_base)) + return PTR_ERR(data->io_base); + + nand_set_flash_node(&data->chip, pdev->dev.of_node); + mtd = nand_to_mtd(&data->chip); + mtd->dev.parent = &pdev->dev; + + data->chip.IO_ADDR_R = data->io_base; + data->chip.IO_ADDR_W = data->io_base; + data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; + data->chip.dev_ready = pdata->ctrl.dev_ready; + data->chip.select_chip = pdata->ctrl.select_chip; + data->chip.write_buf = pdata->ctrl.write_buf; + data->chip.read_buf = pdata->ctrl.read_buf; + data->chip.read_byte = pdata->ctrl.read_byte; + data->chip.chip_delay = pdata->chip.chip_delay; + data->chip.options |= pdata->chip.options; + data->chip.bbt_options |= pdata->chip.bbt_options; + + data->chip.ecc.hwctl = pdata->ctrl.hwcontrol; + data->chip.ecc.mode = NAND_ECC_SOFT; + data->chip.ecc.algo = NAND_ECC_HAMMING; + + platform_set_drvdata(pdev, data); + + /* Handle any platform specific setup */ + if (pdata->ctrl.probe) { + err = pdata->ctrl.probe(pdev); + if (err) + goto out; + } + + /* Scan to find existence of the device */ + err = nand_scan(mtd, pdata->chip.nr_chips); + if (err) + goto out; + + part_types = pdata->chip.part_probe_types; + + err = mtd_device_parse_register(mtd, part_types, NULL, + pdata->chip.partitions, + pdata->chip.nr_partitions); + + if (!err) + return err; + + nand_release(mtd); +out: + if (pdata->ctrl.remove) + pdata->ctrl.remove(pdev); + return err; +} + +/* + * Remove a NAND device. + */ +static int plat_nand_remove(struct platform_device *pdev) +{ + struct plat_nand_data *data = platform_get_drvdata(pdev); + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); + + nand_release(nand_to_mtd(&data->chip)); + if (pdata->ctrl.remove) + pdata->ctrl.remove(pdev); + + return 0; +} + +static const struct of_device_id plat_nand_match[] = { + { .compatible = "gen_nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, plat_nand_match); + +static struct platform_driver plat_nand_driver = { + .probe = plat_nand_probe, + .remove = plat_nand_remove, + .driver = { + .name = "gen_nand", + .of_match_table = plat_nand_match, + }, +}; + +module_platform_driver(plat_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vitaly Wool"); +MODULE_DESCRIPTION("Simple generic NAND driver"); +MODULE_ALIAS("platform:gen_nand"); diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.c b/drivers/mtd/nand/raw/pxa3xx_nand.c new file mode 100644 index 000000000000..d75f30263d21 --- /dev/null +++ b/drivers/mtd/nand/raw/pxa3xx_nand.c @@ -0,0 +1,2103 @@ +/* + * Copyright © 2005 Intel Corporation + * Copyright © 2006 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * See Documentation/mtd/nand/pxa3xx-nand.txt for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200) +#define NAND_STOP_DELAY msecs_to_jiffies(40) +#define PAGE_CHUNK_SIZE (2048) + +/* + * Define a buffer size for the initial command that detects the flash device: + * STATUS, READID and PARAM. + * ONFI param page is 256 bytes, and there are three redundant copies + * to be read. JEDEC param page is 512 bytes, and there are also three + * redundant copies to be read. + * Hence this buffer should be at least 512 x 3. Let's pick 2048. + */ +#define INIT_BUFFER_SIZE 2048 + +/* System control register and bit to enable NAND on some SoCs */ +#define GENCONF_SOC_DEVICE_MUX 0x208 +#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0) + +/* registers and bit definitions */ +#define NDCR (0x00) /* Control register */ +#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ +#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ +#define NDSR (0x14) /* Status Register */ +#define NDPCR (0x18) /* Page Count Register */ +#define NDBDR0 (0x1C) /* Bad Block Register 0 */ +#define NDBDR1 (0x20) /* Bad Block Register 1 */ +#define NDECCCTRL (0x28) /* ECC control */ +#define NDDB (0x40) /* Data Buffer */ +#define NDCB0 (0x48) /* Command Buffer0 */ +#define NDCB1 (0x4C) /* Command Buffer1 */ +#define NDCB2 (0x50) /* Command Buffer2 */ + +#define NDCR_SPARE_EN (0x1 << 31) +#define NDCR_ECC_EN (0x1 << 30) +#define NDCR_DMA_EN (0x1 << 29) +#define NDCR_ND_RUN (0x1 << 28) +#define NDCR_DWIDTH_C (0x1 << 27) +#define NDCR_DWIDTH_M (0x1 << 26) +#define NDCR_PAGE_SZ (0x1 << 24) +#define NDCR_NCSX (0x1 << 23) +#define NDCR_ND_MODE (0x3 << 21) +#define NDCR_NAND_MODE (0x0) +#define NDCR_CLR_PG_CNT (0x1 << 20) +#define NFCV1_NDCR_ARB_CNTL (0x1 << 19) +#define NFCV2_NDCR_STOP_ON_UNCOR (0x1 << 19) +#define NDCR_RD_ID_CNT_MASK (0x7 << 16) +#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) + +#define NDCR_RA_START (0x1 << 15) +#define NDCR_PG_PER_BLK (0x1 << 14) +#define NDCR_ND_ARB_EN (0x1 << 12) +#define NDCR_INT_MASK (0xFFF) + +#define NDSR_MASK (0xfff) +#define NDSR_ERR_CNT_OFF (16) +#define NDSR_ERR_CNT_MASK (0x1f) +#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK) +#define NDSR_RDY (0x1 << 12) +#define NDSR_FLASH_RDY (0x1 << 11) +#define NDSR_CS0_PAGED (0x1 << 10) +#define NDSR_CS1_PAGED (0x1 << 9) +#define NDSR_CS0_CMDD (0x1 << 8) +#define NDSR_CS1_CMDD (0x1 << 7) +#define NDSR_CS0_BBD (0x1 << 6) +#define NDSR_CS1_BBD (0x1 << 5) +#define NDSR_UNCORERR (0x1 << 4) +#define NDSR_CORERR (0x1 << 3) +#define NDSR_WRDREQ (0x1 << 2) +#define NDSR_RDDREQ (0x1 << 1) +#define NDSR_WRCMDREQ (0x1) + +#define NDCB0_LEN_OVRD (0x1 << 28) +#define NDCB0_ST_ROW_EN (0x1 << 26) +#define NDCB0_AUTO_RS (0x1 << 25) +#define NDCB0_CSEL (0x1 << 24) +#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29) +#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK) +#define NDCB0_CMD_TYPE_MASK (0x7 << 21) +#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) +#define NDCB0_NC (0x1 << 20) +#define NDCB0_DBC (0x1 << 19) +#define NDCB0_ADDR_CYC_MASK (0x7 << 16) +#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) +#define NDCB0_CMD2_MASK (0xff << 8) +#define NDCB0_CMD1_MASK (0xff) +#define NDCB0_ADDR_CYC_SHIFT (16) + +#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */ +#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */ +#define EXT_CMD_TYPE_READ 4 /* Read */ +#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */ +#define EXT_CMD_TYPE_FINAL 3 /* Final command */ +#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ +#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */ + +/* + * This should be large enough to read 'ONFI' and 'JEDEC'. + * Let's use 7 bytes, which is the maximum ID count supported + * by the controller (see NDCR_RD_ID_CNT_MASK). + */ +#define READ_ID_BYTES 7 + +/* macros for registers read/write */ +#define nand_writel(info, off, val) \ + do { \ + dev_vdbg(&info->pdev->dev, \ + "%s():%d nand_writel(0x%x, 0x%04x)\n", \ + __func__, __LINE__, (val), (off)); \ + writel_relaxed((val), (info)->mmio_base + (off)); \ + } while (0) + +#define nand_readl(info, off) \ + ({ \ + unsigned int _v; \ + _v = readl_relaxed((info)->mmio_base + (off)); \ + dev_vdbg(&info->pdev->dev, \ + "%s():%d nand_readl(0x%04x) = 0x%x\n", \ + __func__, __LINE__, (off), _v); \ + _v; \ + }) + +/* error code and state */ +enum { + ERR_NONE = 0, + ERR_DMABUSERR = -1, + ERR_SENDCMD = -2, + ERR_UNCORERR = -3, + ERR_BBERR = -4, + ERR_CORERR = -5, +}; + +enum { + STATE_IDLE = 0, + STATE_PREPARED, + STATE_CMD_HANDLE, + STATE_DMA_READING, + STATE_DMA_WRITING, + STATE_DMA_DONE, + STATE_PIO_READING, + STATE_PIO_WRITING, + STATE_CMD_DONE, + STATE_READY, +}; + +enum pxa3xx_nand_variant { + PXA3XX_NAND_VARIANT_PXA, + PXA3XX_NAND_VARIANT_ARMADA370, + PXA3XX_NAND_VARIANT_ARMADA_8K, +}; + +struct pxa3xx_nand_host { + struct nand_chip chip; + void *info_data; + + /* page size of attached chip */ + int use_ecc; + int cs; + + /* calculated from pxa3xx_nand_flash data */ + unsigned int col_addr_cycles; + unsigned int row_addr_cycles; +}; + +struct pxa3xx_nand_info { + struct nand_hw_control controller; + struct platform_device *pdev; + + struct clk *clk; + void __iomem *mmio_base; + unsigned long mmio_phys; + struct completion cmd_complete, dev_ready; + + unsigned int buf_start; + unsigned int buf_count; + unsigned int buf_size; + unsigned int data_buff_pos; + unsigned int oob_buff_pos; + + /* DMA information */ + struct scatterlist sg; + enum dma_data_direction dma_dir; + struct dma_chan *dma_chan; + dma_cookie_t dma_cookie; + int drcmr_dat; + + unsigned char *data_buff; + unsigned char *oob_buff; + dma_addr_t data_buff_phys; + int data_dma_ch; + + struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; + unsigned int state; + + /* + * This driver supports NFCv1 (as found in PXA SoC) + * and NFCv2 (as found in Armada 370/XP SoC). + */ + enum pxa3xx_nand_variant variant; + + int cs; + int use_ecc; /* use HW ECC ? */ + int ecc_bch; /* using BCH ECC? */ + int use_dma; /* use DMA ? */ + int use_spare; /* use spare ? */ + int need_wait; + + /* Amount of real data per full chunk */ + unsigned int chunk_size; + + /* Amount of spare data per full chunk */ + unsigned int spare_size; + + /* Number of full chunks (i.e chunk_size + spare_size) */ + unsigned int nfullchunks; + + /* + * Total number of chunks. If equal to nfullchunks, then there + * are only full chunks. Otherwise, there is one last chunk of + * size (last_chunk_size + last_spare_size) + */ + unsigned int ntotalchunks; + + /* Amount of real data in the last chunk */ + unsigned int last_chunk_size; + + /* Amount of spare data in the last chunk */ + unsigned int last_spare_size; + + unsigned int ecc_size; + unsigned int ecc_err_cnt; + unsigned int max_bitflips; + int retcode; + + /* + * Variables only valid during command + * execution. step_chunk_size and step_spare_size is the + * amount of real data and spare data in the current + * chunk. cur_chunk is the current chunk being + * read/programmed. + */ + unsigned int step_chunk_size; + unsigned int step_spare_size; + unsigned int cur_chunk; + + /* cached register value */ + uint32_t reg_ndcr; + uint32_t ndtr0cs0; + uint32_t ndtr1cs0; + + /* generated NDCBx register values */ + uint32_t ndcb0; + uint32_t ndcb1; + uint32_t ndcb2; + uint32_t ndcb3; +}; + +static bool use_dma = 1; +module_param(use_dma, bool, 0444); +MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW"); + +struct pxa3xx_nand_timing { + unsigned int tCH; /* Enable signal hold time */ + unsigned int tCS; /* Enable signal setup time */ + unsigned int tWH; /* ND_nWE high duration */ + unsigned int tWP; /* ND_nWE pulse time */ + unsigned int tRH; /* ND_nRE high duration */ + unsigned int tRP; /* ND_nRE pulse width */ + unsigned int tR; /* ND_nWE high to ND_nRE low for read */ + unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ + unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ +}; + +struct pxa3xx_nand_flash { + uint32_t chip_id; + unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ + unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ + struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ +}; + +static struct pxa3xx_nand_timing timing[] = { + { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, + { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, + { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, + { 10, 35, 15, 25, 15, 25, 25000, 60, 10, }, +}; + +static struct pxa3xx_nand_flash builtin_flash_types[] = { + { 0x46ec, 16, 16, &timing[1] }, + { 0xdaec, 8, 8, &timing[1] }, + { 0xd7ec, 8, 8, &timing[1] }, + { 0xa12c, 8, 8, &timing[2] }, + { 0xb12c, 16, 16, &timing[2] }, + { 0xdc2c, 8, 8, &timing[2] }, + { 0xcc2c, 16, 16, &timing[2] }, + { 0xba20, 16, 16, &timing[3] }, +}; + +static int pxa3xx_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int nchunks = mtd->writesize / info->chunk_size; + + if (section >= nchunks) + return -ERANGE; + + oobregion->offset = ((info->ecc_size + info->spare_size) * section) + + info->spare_size; + oobregion->length = info->ecc_size; + + return 0; +} + +static int pxa3xx_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int nchunks = mtd->writesize / info->chunk_size; + + if (section >= nchunks) + return -ERANGE; + + if (!info->spare_size) + return 0; + + oobregion->offset = section * (info->ecc_size + info->spare_size); + oobregion->length = info->spare_size; + if (!section) { + /* + * Bootrom looks in bytes 0 & 5 for bad blocks for the + * 4KB page / 4bit BCH combination. + */ + if (mtd->writesize == 4096 && info->chunk_size == 2048) { + oobregion->offset += 6; + oobregion->length -= 6; + } else { + oobregion->offset += 2; + oobregion->length -= 2; + } + } + + return 0; +} + +static const struct mtd_ooblayout_ops pxa3xx_ooblayout_ops = { + .ecc = pxa3xx_ooblayout_ecc, + .free = pxa3xx_ooblayout_free, +}; + +static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; +static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_mirror_pattern +}; + +#define NDTR0_tCH(c) (min((c), 7) << 19) +#define NDTR0_tCS(c) (min((c), 7) << 16) +#define NDTR0_tWH(c) (min((c), 7) << 11) +#define NDTR0_tWP(c) (min((c), 7) << 8) +#define NDTR0_tRH(c) (min((c), 7) << 3) +#define NDTR0_tRP(c) (min((c), 7) << 0) + +#define NDTR1_tR(c) (min((c), 65535) << 16) +#define NDTR1_tWHR(c) (min((c), 15) << 4) +#define NDTR1_tAR(c) (min((c), 15) << 0) + +/* convert nano-seconds to nand flash controller clock cycles */ +#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000) + +static const struct of_device_id pxa3xx_nand_dt_ids[] = { + { + .compatible = "marvell,pxa3xx-nand", + .data = (void *)PXA3XX_NAND_VARIANT_PXA, + }, + { + .compatible = "marvell,armada370-nand", + .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370, + }, + { + .compatible = "marvell,armada-8k-nand", + .data = (void *)PXA3XX_NAND_VARIANT_ARMADA_8K, + }, + {} +}; +MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); + +static enum pxa3xx_nand_variant +pxa3xx_nand_get_variant(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); + if (!of_id) + return PXA3XX_NAND_VARIANT_PXA; + return (enum pxa3xx_nand_variant)of_id->data; +} + +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, + const struct pxa3xx_nand_timing *t) +{ + struct pxa3xx_nand_info *info = host->info_data; + unsigned long nand_clk = clk_get_rate(info->clk); + uint32_t ndtr0, ndtr1; + + ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | + NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | + NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | + NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | + NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | + NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); + + ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | + NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | + NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); + + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; + nand_writel(info, NDTR0CS0, ndtr0); + nand_writel(info, NDTR1CS0, ndtr1); +} + +static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host, + const struct nand_sdr_timings *t) +{ + struct pxa3xx_nand_info *info = host->info_data; + struct nand_chip *chip = &host->chip; + unsigned long nand_clk = clk_get_rate(info->clk); + uint32_t ndtr0, ndtr1; + + u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000); + u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000); + u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000); + u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000); + u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000); + u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000); + u32 tR = chip->chip_delay * 1000; + u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000); + u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000); + + /* fallback to a default value if tR = 0 */ + if (!tR) + tR = 20000; + + ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) | + NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) | + NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) | + NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) | + NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) | + NDTR0_tRP(ns2cycle(tRP_min, nand_clk)); + + ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) | + NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) | + NDTR1_tAR(ns2cycle(tAR_min, nand_clk)); + + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; + nand_writel(info, NDTR0CS0, ndtr0); + nand_writel(info, NDTR1CS0, ndtr1); +} + +static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host, + unsigned int *flash_width, + unsigned int *dfc_width) +{ + struct nand_chip *chip = &host->chip; + struct pxa3xx_nand_info *info = host->info_data; + const struct pxa3xx_nand_flash *f = NULL; + int i, id, ntypes; + u8 idbuf[2]; + + ntypes = ARRAY_SIZE(builtin_flash_types); + + nand_readid_op(chip, 0, idbuf, sizeof(idbuf)); + id = idbuf[0] | (idbuf[1] << 8); + + for (i = 0; i < ntypes; i++) { + f = &builtin_flash_types[i]; + + if (f->chip_id == id) + break; + } + + if (i == ntypes) { + dev_err(&info->pdev->dev, "Error: timings not found\n"); + return -EINVAL; + } + + pxa3xx_nand_set_timing(host, f->timing); + + *flash_width = f->flash_width; + *dfc_width = f->dfc_width; + + return 0; +} + +static int pxa3xx_nand_init_timings_onfi(struct pxa3xx_nand_host *host, + int mode) +{ + const struct nand_sdr_timings *timings; + + mode = fls(mode) - 1; + if (mode < 0) + mode = 0; + + timings = onfi_async_timing_mode_to_sdr_timings(mode); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + pxa3xx_nand_set_sdr_timing(host, timings); + + return 0; +} + +static int pxa3xx_nand_init(struct pxa3xx_nand_host *host) +{ + struct nand_chip *chip = &host->chip; + struct pxa3xx_nand_info *info = host->info_data; + unsigned int flash_width = 0, dfc_width = 0; + int mode, err; + + mode = onfi_get_async_timing_mode(chip); + if (mode == ONFI_TIMING_MODE_UNKNOWN) { + err = pxa3xx_nand_init_timings_compat(host, &flash_width, + &dfc_width); + if (err) + return err; + + if (flash_width == 16) { + info->reg_ndcr |= NDCR_DWIDTH_M; + chip->options |= NAND_BUSWIDTH_16; + } + + info->reg_ndcr |= (dfc_width == 16) ? NDCR_DWIDTH_C : 0; + } else { + err = pxa3xx_nand_init_timings_onfi(host, mode); + if (err) + return err; + } + + return 0; +} + +/** + * NOTE: it is a must to set ND_RUN firstly, then write + * command buffer, otherwise, it does not work. + * We enable all the interrupt at the same time, and + * let pxa3xx_nand_irq to handle all logic. + */ +static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr; + + ndcr = info->reg_ndcr; + + if (info->use_ecc) { + ndcr |= NDCR_ECC_EN; + if (info->ecc_bch) + nand_writel(info, NDECCCTRL, 0x1); + } else { + ndcr &= ~NDCR_ECC_EN; + if (info->ecc_bch) + nand_writel(info, NDECCCTRL, 0x0); + } + + if (info->use_dma) + ndcr |= NDCR_DMA_EN; + else + ndcr &= ~NDCR_DMA_EN; + + if (info->use_spare) + ndcr |= NDCR_SPARE_EN; + else + ndcr &= ~NDCR_SPARE_EN; + + ndcr |= NDCR_ND_RUN; + + /* clear status bits and run */ + nand_writel(info, NDSR, NDSR_MASK); + nand_writel(info, NDCR, 0); + nand_writel(info, NDCR, ndcr); +} + +static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr; + int timeout = NAND_STOP_DELAY; + + /* wait RUN bit in NDCR become 0 */ + ndcr = nand_readl(info, NDCR); + while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) { + ndcr = nand_readl(info, NDCR); + udelay(1); + } + + if (timeout <= 0) { + ndcr &= ~NDCR_ND_RUN; + nand_writel(info, NDCR, ndcr); + } + if (info->dma_chan) + dmaengine_terminate_all(info->dma_chan); + + /* clear status bits */ + nand_writel(info, NDSR, NDSR_MASK); +} + +static void __maybe_unused +enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +{ + uint32_t ndcr; + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~int_mask); +} + +static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +{ + uint32_t ndcr; + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr | int_mask); +} + +static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) +{ + if (info->ecc_bch) { + u32 val; + int ret; + + /* + * According to the datasheet, when reading from NDDB + * with BCH enabled, after each 32 bytes reads, we + * have to make sure that the NDSR.RDDREQ bit is set. + * + * Drain the FIFO 8 32 bits reads at a time, and skip + * the polling on the last read. + */ + while (len > 8) { + ioread32_rep(info->mmio_base + NDDB, data, 8); + + ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val, + val & NDSR_RDDREQ, 1000, 5000); + if (ret) { + dev_err(&info->pdev->dev, + "Timeout on RDDREQ while draining the FIFO\n"); + return; + } + + data += 32; + len -= 8; + } + } + + ioread32_rep(info->mmio_base + NDDB, data, len); +} + +static void handle_data_pio(struct pxa3xx_nand_info *info) +{ + switch (info->state) { + case STATE_PIO_WRITING: + if (info->step_chunk_size) + writesl(info->mmio_base + NDDB, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(info->step_chunk_size, 4)); + + if (info->step_spare_size) + writesl(info->mmio_base + NDDB, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->step_spare_size, 4)); + break; + case STATE_PIO_READING: + if (info->step_chunk_size) + drain_fifo(info, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(info->step_chunk_size, 4)); + + if (info->step_spare_size) + drain_fifo(info, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->step_spare_size, 4)); + break; + default: + dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, + info->state); + BUG(); + } + + /* Update buffer pointers for multi-page read/write */ + info->data_buff_pos += info->step_chunk_size; + info->oob_buff_pos += info->step_spare_size; +} + +static void pxa3xx_nand_data_dma_irq(void *data) +{ + struct pxa3xx_nand_info *info = data; + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(info->dma_chan, info->dma_cookie, &state); + if (likely(status == DMA_COMPLETE)) { + info->state = STATE_DMA_DONE; + } else { + dev_err(&info->pdev->dev, "DMA error on data channel\n"); + info->retcode = ERR_DMABUSERR; + } + dma_unmap_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); + + nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); + enable_int(info, NDCR_INT_MASK); +} + +static void start_data_dma(struct pxa3xx_nand_info *info) +{ + enum dma_transfer_direction direction; + struct dma_async_tx_descriptor *tx; + + switch (info->state) { + case STATE_DMA_WRITING: + info->dma_dir = DMA_TO_DEVICE; + direction = DMA_MEM_TO_DEV; + break; + case STATE_DMA_READING: + info->dma_dir = DMA_FROM_DEVICE; + direction = DMA_DEV_TO_MEM; + break; + default: + dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, + info->state); + BUG(); + } + info->sg.length = info->chunk_size; + if (info->use_spare) + info->sg.length += info->spare_size + info->ecc_size; + dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); + + tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(&info->pdev->dev, "prep_slave_sg() failed\n"); + return; + } + tx->callback = pxa3xx_nand_data_dma_irq; + tx->callback_param = info; + info->dma_cookie = dmaengine_submit(tx); + dma_async_issue_pending(info->dma_chan); + dev_dbg(&info->pdev->dev, "%s(dir=%d cookie=%x size=%u)\n", + __func__, direction, info->dma_cookie, info->sg.length); +} + +static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data) +{ + struct pxa3xx_nand_info *info = data; + + handle_data_pio(info); + + info->state = STATE_CMD_DONE; + nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); + + return IRQ_HANDLED; +} + +static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) +{ + struct pxa3xx_nand_info *info = devid; + unsigned int status, is_completed = 0, is_ready = 0; + unsigned int ready, cmd_done; + irqreturn_t ret = IRQ_HANDLED; + + if (info->cs == 0) { + ready = NDSR_FLASH_RDY; + cmd_done = NDSR_CS0_CMDD; + } else { + ready = NDSR_RDY; + cmd_done = NDSR_CS1_CMDD; + } + + status = nand_readl(info, NDSR); + + if (status & NDSR_UNCORERR) + info->retcode = ERR_UNCORERR; + if (status & NDSR_CORERR) { + info->retcode = ERR_CORERR; + if ((info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || + info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) && + info->ecc_bch) + info->ecc_err_cnt = NDSR_ERR_CNT(status); + else + info->ecc_err_cnt = 1; + + /* + * Each chunk composing a page is corrected independently, + * and we need to store maximum number of corrected bitflips + * to return it to the MTD layer in ecc.read_page(). + */ + info->max_bitflips = max_t(unsigned int, + info->max_bitflips, + info->ecc_err_cnt); + } + if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { + /* whether use dma to transfer data */ + if (info->use_dma) { + disable_int(info, NDCR_INT_MASK); + info->state = (status & NDSR_RDDREQ) ? + STATE_DMA_READING : STATE_DMA_WRITING; + start_data_dma(info); + goto NORMAL_IRQ_EXIT; + } else { + info->state = (status & NDSR_RDDREQ) ? + STATE_PIO_READING : STATE_PIO_WRITING; + ret = IRQ_WAKE_THREAD; + goto NORMAL_IRQ_EXIT; + } + } + if (status & cmd_done) { + info->state = STATE_CMD_DONE; + is_completed = 1; + } + if (status & ready) { + info->state = STATE_READY; + is_ready = 1; + } + + /* + * Clear all status bit before issuing the next command, which + * can and will alter the status bits and will deserve a new + * interrupt on its own. This lets the controller exit the IRQ + */ + nand_writel(info, NDSR, status); + + if (status & NDSR_WRCMDREQ) { + status &= ~NDSR_WRCMDREQ; + info->state = STATE_CMD_HANDLE; + + /* + * Command buffer registers NDCB{0-2} (and optionally NDCB3) + * must be loaded by writing directly either 12 or 16 + * bytes directly to NDCB0, four bytes at a time. + * + * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored + * but each NDCBx register can be read. + */ + nand_writel(info, NDCB0, info->ndcb0); + nand_writel(info, NDCB0, info->ndcb1); + nand_writel(info, NDCB0, info->ndcb2); + + /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || + info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) + nand_writel(info, NDCB0, info->ndcb3); + } + + if (is_completed) + complete(&info->cmd_complete); + if (is_ready) + complete(&info->dev_ready); +NORMAL_IRQ_EXIT: + return ret; +} + +static inline int is_buf_blank(uint8_t *buf, size_t len) +{ + for (; len > 0; len--) + if (*buf++ != 0xff) + return 0; + return 1; +} + +static void set_command_address(struct pxa3xx_nand_info *info, + unsigned int page_size, uint16_t column, int page_addr) +{ + /* small page addr setting */ + if (page_size < PAGE_CHUNK_SIZE) { + info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) + | (column & 0xFF); + + info->ndcb2 = 0; + } else { + info->ndcb1 = ((page_addr & 0xFFFF) << 16) + | (column & 0xFFFF); + + if (page_addr & 0xFF0000) + info->ndcb2 = (page_addr & 0xFF0000) >> 16; + else + info->ndcb2 = 0; + } +} + +static void prepare_start_command(struct pxa3xx_nand_info *info, int command) +{ + struct pxa3xx_nand_host *host = info->host[info->cs]; + struct mtd_info *mtd = nand_to_mtd(&host->chip); + + /* reset data and oob column point to handle data */ + info->buf_start = 0; + info->buf_count = 0; + info->data_buff_pos = 0; + info->oob_buff_pos = 0; + info->step_chunk_size = 0; + info->step_spare_size = 0; + info->cur_chunk = 0; + info->use_ecc = 0; + info->use_spare = 1; + info->retcode = ERR_NONE; + info->ecc_err_cnt = 0; + info->ndcb3 = 0; + info->need_wait = 0; + + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + case NAND_CMD_PAGEPROG: + info->use_ecc = 1; + break; + case NAND_CMD_PARAM: + info->use_spare = 0; + break; + default: + info->ndcb1 = 0; + info->ndcb2 = 0; + break; + } + + /* + * If we are about to issue a read command, or about to set + * the write address, then clean the data buffer. + */ + if (command == NAND_CMD_READ0 || + command == NAND_CMD_READOOB || + command == NAND_CMD_SEQIN) { + + info->buf_count = mtd->writesize + mtd->oobsize; + memset(info->data_buff, 0xFF, info->buf_count); + } + +} + +static int prepare_set_command(struct pxa3xx_nand_info *info, int command, + int ext_cmd_type, uint16_t column, int page_addr) +{ + int addr_cycle, exec_cmd; + struct pxa3xx_nand_host *host; + struct mtd_info *mtd; + + host = info->host[info->cs]; + mtd = nand_to_mtd(&host->chip); + addr_cycle = 0; + exec_cmd = 1; + + if (info->cs != 0) + info->ndcb0 = NDCB0_CSEL; + else + info->ndcb0 = 0; + + if (command == NAND_CMD_SEQIN) + exec_cmd = 0; + + addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles + + host->col_addr_cycles); + + switch (command) { + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + info->buf_start = column; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | addr_cycle + | NAND_CMD_READ0; + + if (command == NAND_CMD_READOOB) + info->buf_start += mtd->writesize; + + if (info->cur_chunk < info->nfullchunks) { + info->step_chunk_size = info->chunk_size; + info->step_spare_size = info->spare_size; + } else { + info->step_chunk_size = info->last_chunk_size; + info->step_spare_size = info->last_spare_size; + } + + /* + * Multiple page read needs an 'extended command type' field, + * which is either naked-read or last-read according to the + * state. + */ + if (mtd->writesize == PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); + } else if (mtd->writesize > PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) + | NDCB0_LEN_OVRD + | NDCB0_EXT_CMD_TYPE(ext_cmd_type); + info->ndcb3 = info->step_chunk_size + + info->step_spare_size; + } + + set_command_address(info, mtd->writesize, column, page_addr); + break; + + case NAND_CMD_SEQIN: + + info->buf_start = column; + set_command_address(info, mtd->writesize, 0, page_addr); + + /* + * Multiple page programming needs to execute the initial + * SEQIN command that sets the page address. + */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_EXT_CMD_TYPE(ext_cmd_type) + | addr_cycle + | command; + exec_cmd = 1; + } + break; + + case NAND_CMD_PAGEPROG: + if (is_buf_blank(info->data_buff, + (mtd->writesize + mtd->oobsize))) { + exec_cmd = 0; + break; + } + + if (info->cur_chunk < info->nfullchunks) { + info->step_chunk_size = info->chunk_size; + info->step_spare_size = info->spare_size; + } else { + info->step_chunk_size = info->last_chunk_size; + info->step_spare_size = info->last_spare_size; + } + + /* Second command setting for large pages */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { + /* + * Multiple page write uses the 'extended command' + * field. This can be used to issue a command dispatch + * or a naked-write depending on the current stage. + */ + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_LEN_OVRD + | NDCB0_EXT_CMD_TYPE(ext_cmd_type); + info->ndcb3 = info->step_chunk_size + + info->step_spare_size; + + /* + * This is the command dispatch that completes a chunked + * page program operation. + */ + if (info->cur_chunk == info->ntotalchunks) { + info->ndcb0 = NDCB0_CMD_TYPE(0x1) + | NDCB0_EXT_CMD_TYPE(ext_cmd_type) + | command; + info->ndcb1 = 0; + info->ndcb2 = 0; + info->ndcb3 = 0; + } + } else { + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_AUTO_RS + | NDCB0_ST_ROW_EN + | NDCB0_DBC + | (NAND_CMD_PAGEPROG << 8) + | NAND_CMD_SEQIN + | addr_cycle; + } + break; + + case NAND_CMD_PARAM: + info->buf_count = INIT_BUFFER_SIZE; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | NDCB0_ADDR_CYC(1) + | NDCB0_LEN_OVRD + | command; + info->ndcb1 = (column & 0xFF); + info->ndcb3 = INIT_BUFFER_SIZE; + info->step_chunk_size = INIT_BUFFER_SIZE; + break; + + case NAND_CMD_READID: + info->buf_count = READ_ID_BYTES; + info->ndcb0 |= NDCB0_CMD_TYPE(3) + | NDCB0_ADDR_CYC(1) + | command; + info->ndcb1 = (column & 0xFF); + + info->step_chunk_size = 8; + break; + case NAND_CMD_STATUS: + info->buf_count = 1; + info->ndcb0 |= NDCB0_CMD_TYPE(4) + | NDCB0_ADDR_CYC(1) + | command; + + info->step_chunk_size = 8; + break; + + case NAND_CMD_ERASE1: + info->ndcb0 |= NDCB0_CMD_TYPE(2) + | NDCB0_AUTO_RS + | NDCB0_ADDR_CYC(3) + | NDCB0_DBC + | (NAND_CMD_ERASE2 << 8) + | NAND_CMD_ERASE1; + info->ndcb1 = page_addr; + info->ndcb2 = 0; + + break; + case NAND_CMD_RESET: + info->ndcb0 |= NDCB0_CMD_TYPE(5) + | command; + + break; + + case NAND_CMD_ERASE2: + exec_cmd = 0; + break; + + default: + exec_cmd = 0; + dev_err(&info->pdev->dev, "non-supported command %x\n", + command); + break; + } + + return exec_cmd; +} + +static void nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int exec_cmd; + + /* + * if this is a x16 device ,then convert the input + * "byte" address into a "word" address appropriate + * for indexing a word-oriented device + */ + if (info->reg_ndcr & NDCR_DWIDTH_M) + column /= 2; + + /* + * There may be different NAND chip hooked to + * different chip select, so check whether + * chip select has been changed, if yes, reset the timing + */ + if (info->cs != host->cs) { + info->cs = host->cs; + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); + } + + prepare_start_command(info, command); + + info->state = STATE_PREPARED; + exec_cmd = prepare_set_command(info, command, 0, column, page_addr); + + if (exec_cmd) { + init_completion(&info->cmd_complete); + init_completion(&info->dev_ready); + info->need_wait = 1; + pxa3xx_nand_start(info); + + if (!wait_for_completion_timeout(&info->cmd_complete, + CHIP_DELAY_TIMEOUT)) { + dev_err(&info->pdev->dev, "Wait time out!!!\n"); + /* Stop State Machine for next command cycle */ + pxa3xx_nand_stop(info); + } + } + info->state = STATE_IDLE; +} + +static void nand_cmdfunc_extended(struct mtd_info *mtd, + const unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int exec_cmd, ext_cmd_type; + + /* + * if this is a x16 device then convert the input + * "byte" address into a "word" address appropriate + * for indexing a word-oriented device + */ + if (info->reg_ndcr & NDCR_DWIDTH_M) + column /= 2; + + /* + * There may be different NAND chip hooked to + * different chip select, so check whether + * chip select has been changed, if yes, reset the timing + */ + if (info->cs != host->cs) { + info->cs = host->cs; + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); + } + + /* Select the extended command for the first command */ + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + ext_cmd_type = EXT_CMD_TYPE_MONO; + break; + case NAND_CMD_SEQIN: + ext_cmd_type = EXT_CMD_TYPE_DISPATCH; + break; + case NAND_CMD_PAGEPROG: + ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; + break; + default: + ext_cmd_type = 0; + break; + } + + prepare_start_command(info, command); + + /* + * Prepare the "is ready" completion before starting a command + * transaction sequence. If the command is not executed the + * completion will be completed, see below. + * + * We can do that inside the loop because the command variable + * is invariant and thus so is the exec_cmd. + */ + info->need_wait = 1; + init_completion(&info->dev_ready); + do { + info->state = STATE_PREPARED; + + exec_cmd = prepare_set_command(info, command, ext_cmd_type, + column, page_addr); + if (!exec_cmd) { + info->need_wait = 0; + complete(&info->dev_ready); + break; + } + + init_completion(&info->cmd_complete); + pxa3xx_nand_start(info); + + if (!wait_for_completion_timeout(&info->cmd_complete, + CHIP_DELAY_TIMEOUT)) { + dev_err(&info->pdev->dev, "Wait time out!!!\n"); + /* Stop State Machine for next command cycle */ + pxa3xx_nand_stop(info); + break; + } + + /* Only a few commands need several steps */ + if (command != NAND_CMD_PAGEPROG && + command != NAND_CMD_READ0 && + command != NAND_CMD_READOOB) + break; + + info->cur_chunk++; + + /* Check if the sequence is complete */ + if (info->cur_chunk == info->ntotalchunks && command != NAND_CMD_PAGEPROG) + break; + + /* + * After a splitted program command sequence has issued + * the command dispatch, the command sequence is complete. + */ + if (info->cur_chunk == (info->ntotalchunks + 1) && + command == NAND_CMD_PAGEPROG && + ext_cmd_type == EXT_CMD_TYPE_DISPATCH) + break; + + if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { + /* Last read: issue a 'last naked read' */ + if (info->cur_chunk == info->ntotalchunks - 1) + ext_cmd_type = EXT_CMD_TYPE_LAST_RW; + else + ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; + + /* + * If a splitted program command has no more data to transfer, + * the command dispatch must be issued to complete. + */ + } else if (command == NAND_CMD_PAGEPROG && + info->cur_chunk == info->ntotalchunks) { + ext_cmd_type = EXT_CMD_TYPE_DISPATCH; + } + } while (1); + + info->state = STATE_IDLE; +} + +static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) +{ + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return nand_prog_page_end_op(chip); +} + +static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, + int page) +{ + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + + nand_read_page_op(chip, page, 0, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (info->retcode == ERR_CORERR && info->use_ecc) { + mtd->ecc_stats.corrected += info->ecc_err_cnt; + + } else if (info->retcode == ERR_UNCORERR) { + /* + * for blank page (all 0xff), HW will calculate its ECC as + * 0, which is different from the ECC information within + * OOB, ignore such uncorrectable errors + */ + if (is_buf_blank(buf, mtd->writesize)) + info->retcode = ERR_NONE; + else + mtd->ecc_stats.failed++; + } + + return info->max_bitflips; +} + +static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + char retval = 0xFF; + + if (info->buf_start < info->buf_count) + /* Has just send a new command? */ + retval = info->data_buff[info->buf_start++]; + + return retval; +} + +static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + u16 retval = 0xFFFF; + + if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { + retval = *((u16 *)(info->data_buff+info->buf_start)); + info->buf_start += 2; + } + return retval; +} + +static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(buf, info->data_buff + info->buf_start, real_len); + info->buf_start += real_len; +} + +static void pxa3xx_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(info->data_buff + info->buf_start, buf, real_len); + info->buf_start += real_len; +} + +static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) +{ + return; +} + +static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + + if (info->need_wait) { + info->need_wait = 0; + if (!wait_for_completion_timeout(&info->dev_ready, + CHIP_DELAY_TIMEOUT)) { + dev_err(&info->pdev->dev, "Ready time out!!!\n"); + return NAND_STATUS_FAIL; + } + } + + /* pxa3xx_nand_send_command has waited for command complete */ + if (this->state == FL_WRITING || this->state == FL_ERASING) { + if (info->retcode == ERR_NONE) + return 0; + else + return NAND_STATUS_FAIL; + } + + return NAND_STATUS_READY; +} + +static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_host *host = info->host[info->cs]; + struct platform_device *pdev = info->pdev; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + const struct nand_sdr_timings *timings; + + /* Configure default flash values */ + info->chunk_size = PAGE_CHUNK_SIZE; + info->reg_ndcr = 0x0; /* enable all interrupts */ + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); + info->reg_ndcr |= NDCR_SPARE_EN; + + /* use the common timing to make a try */ + timings = onfi_async_timing_mode_to_sdr_timings(0); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + pxa3xx_nand_set_sdr_timing(host, timings); + return 0; +} + +static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_host *host = info->host[info->cs]; + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + + info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; + info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0; + info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; +} + +static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) +{ + struct platform_device *pdev = info->pdev; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + uint32_t ndcr = nand_readl(info, NDCR); + + /* Set an initial chunk size */ + info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; + info->reg_ndcr = ndcr & + ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL); + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + info->ndtr0cs0 = nand_readl(info, NDTR0CS0); + info->ndtr1cs0 = nand_readl(info, NDTR1CS0); +} + +static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) +{ + struct platform_device *pdev = info->pdev; + struct dma_slave_config config; + dma_cap_mask_t mask; + struct pxad_param param; + int ret; + + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) + return -ENOMEM; + if (use_dma == 0) + return 0; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + sg_init_one(&info->sg, info->data_buff, info->buf_size); + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + param.prio = PXAD_PRIO_LOWEST; + param.drcmr = info->drcmr_dat; + info->dma_chan = dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m, &pdev->dev, + "data"); + if (!info->dma_chan) { + dev_err(&pdev->dev, "unable to request data dma channel\n"); + return -ENODEV; + } + + memset(&config, 0, sizeof(config)); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.src_addr = info->mmio_phys + NDDB; + config.dst_addr = info->mmio_phys + NDDB; + config.src_maxburst = 32; + config.dst_maxburst = 32; + ret = dmaengine_slave_config(info->dma_chan, &config); + if (ret < 0) { + dev_err(&info->pdev->dev, + "dma channel configuration failed: %d\n", + ret); + return ret; + } + + /* + * Now that DMA buffers are allocated we turn on + * DMA proper for I/O operations. + */ + info->use_dma = 1; + return 0; +} + +static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) +{ + if (info->use_dma) { + dmaengine_terminate_all(info->dma_chan); + dma_release_channel(info->dma_chan); + } + kfree(info->data_buff); +} + +static int pxa_ecc_init(struct pxa3xx_nand_info *info, + struct mtd_info *mtd, + int strength, int ecc_stepsize, int page_size) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { + info->nfullchunks = 1; + info->ntotalchunks = 1; + info->chunk_size = 2048; + info->spare_size = 40; + info->ecc_size = 24; + ecc->mode = NAND_ECC_HW; + ecc->size = 512; + ecc->strength = 1; + + } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { + info->nfullchunks = 1; + info->ntotalchunks = 1; + info->chunk_size = 512; + info->spare_size = 8; + info->ecc_size = 8; + ecc->mode = NAND_ECC_HW; + ecc->size = 512; + ecc->strength = 1; + + /* + * Required ECC: 4-bit correction per 512 bytes + * Select: 16-bit correction per 2048 bytes + */ + } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { + info->ecc_bch = 1; + info->nfullchunks = 1; + info->ntotalchunks = 1; + info->chunk_size = 2048; + info->spare_size = 32; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); + ecc->strength = 16; + + } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { + info->ecc_bch = 1; + info->nfullchunks = 2; + info->ntotalchunks = 2; + info->chunk_size = 2048; + info->spare_size = 32; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); + ecc->strength = 16; + + /* + * Required ECC: 8-bit correction per 512 bytes + * Select: 16-bit correction per 1024 bytes + */ + } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { + info->ecc_bch = 1; + info->nfullchunks = 4; + info->ntotalchunks = 5; + info->chunk_size = 1024; + info->spare_size = 0; + info->last_chunk_size = 0; + info->last_spare_size = 64; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); + ecc->strength = 16; + } else { + dev_err(&info->pdev->dev, + "ECC strength %d at page size %d is not supported\n", + strength, page_size); + return -ENODEV; + } + + dev_info(&info->pdev->dev, "ECC strength %d, ECC step size %d\n", + ecc->strength, ecc->size); + return 0; +} + +static int pxa3xx_nand_scan(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + struct platform_device *pdev = info->pdev; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + int ret; + uint16_t ecc_strength, ecc_step; + + if (pdata->keep_config) { + pxa3xx_nand_detect_config(info); + } else { + ret = pxa3xx_nand_config_ident(info); + if (ret) + return ret; + } + + if (info->reg_ndcr & NDCR_DWIDTH_M) + chip->options |= NAND_BUSWIDTH_16; + + /* Device detection must be done with ECC disabled */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || + info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) + nand_writel(info, NDECCCTRL, 0x0); + + if (pdata->flash_bbt) + chip->bbt_options |= NAND_BBT_USE_FLASH; + + chip->ecc.strength = pdata->ecc_strength; + chip->ecc.size = pdata->ecc_step_size; + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + if (!pdata->keep_config) { + ret = pxa3xx_nand_init(host); + if (ret) { + dev_err(&info->pdev->dev, "Failed to init nand: %d\n", + ret); + return ret; + } + } + + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + /* + * We'll use a bad block table stored in-flash and don't + * allow writing the bad block marker to the flash. + */ + chip->bbt_options |= NAND_BBT_NO_OOB_BBM; + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + } + + /* + * If the page size is bigger than the FIFO size, let's check + * we are given the right variant and then switch to the extended + * (aka splitted) command handling, + */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || + info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) { + chip->cmdfunc = nand_cmdfunc_extended; + } else { + dev_err(&info->pdev->dev, + "unsupported page size on this variant\n"); + return -ENODEV; + } + } + + ecc_strength = chip->ecc.strength; + ecc_step = chip->ecc.size; + if (!ecc_strength || !ecc_step) { + ecc_strength = chip->ecc_strength_ds; + ecc_step = chip->ecc_step_ds; + } + + /* Set default ECC strength requirements on non-ONFI devices */ + if (ecc_strength < 1 && ecc_step < 1) { + ecc_strength = 1; + ecc_step = 512; + } + + ret = pxa_ecc_init(info, mtd, ecc_strength, + ecc_step, mtd->writesize); + if (ret) + return ret; + + /* calculate addressing information */ + if (mtd->writesize >= 2048) + host->col_addr_cycles = 2; + else + host->col_addr_cycles = 1; + + /* release the initial buffer */ + kfree(info->data_buff); + + /* allocate the real data + oob buffer */ + info->buf_size = mtd->writesize + mtd->oobsize; + ret = pxa3xx_nand_init_buff(info); + if (ret) + return ret; + info->oob_buff = info->data_buff + mtd->writesize; + + if ((mtd->size >> chip->page_shift) > 65536) + host->row_addr_cycles = 3; + else + host->row_addr_cycles = 2; + + if (!pdata->keep_config) + pxa3xx_nand_config_tail(info); + + return nand_scan_tail(mtd); +} + +static int alloc_nand_resource(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct pxa3xx_nand_platform_data *pdata; + struct pxa3xx_nand_info *info; + struct pxa3xx_nand_host *host; + struct nand_chip *chip = NULL; + struct mtd_info *mtd; + struct resource *r; + int ret, irq, cs; + + pdata = dev_get_platdata(&pdev->dev); + if (pdata->num_cs <= 0) { + dev_err(&pdev->dev, "invalid number of chip selects\n"); + return -ENODEV; + } + + info = devm_kzalloc(&pdev->dev, + sizeof(*info) + sizeof(*host) * pdata->num_cs, + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdev = pdev; + info->variant = pxa3xx_nand_get_variant(pdev); + for (cs = 0; cs < pdata->num_cs; cs++) { + host = (void *)&info[1] + sizeof(*host) * cs; + chip = &host->chip; + nand_set_controller_data(chip, host); + mtd = nand_to_mtd(chip); + info->host[cs] = host; + host->cs = cs; + host->info_data = info; + mtd->dev.parent = &pdev->dev; + /* FIXME: all chips use the same device tree partitions */ + nand_set_flash_node(chip, np); + + nand_set_controller_data(chip, host); + chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; + chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; + chip->controller = &info->controller; + chip->waitfunc = pxa3xx_nand_waitfunc; + chip->select_chip = pxa3xx_nand_select_chip; + chip->read_word = pxa3xx_nand_read_word; + chip->read_byte = pxa3xx_nand_read_byte; + chip->read_buf = pxa3xx_nand_read_buf; + chip->write_buf = pxa3xx_nand_write_buf; + chip->options |= NAND_NO_SUBPAGE_WRITE; + chip->cmdfunc = nand_cmdfunc; + chip->onfi_set_features = nand_onfi_get_set_features_notsupp; + chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + } + + nand_hw_control_init(chip->controller); + info->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->clk)) { + ret = PTR_ERR(info->clk); + dev_err(&pdev->dev, "failed to get nand clock: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(info->clk); + if (ret < 0) + return ret; + + if (!np && use_dma) { + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r == NULL) { + dev_err(&pdev->dev, + "no resource defined for data DMA\n"); + ret = -ENXIO; + goto fail_disable_clk; + } + info->drcmr_dat = r->start; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + ret = -ENXIO; + goto fail_disable_clk; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + info->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(info->mmio_base)) { + ret = PTR_ERR(info->mmio_base); + dev_err(&pdev->dev, "failed to map register space: %d\n", ret); + goto fail_disable_clk; + } + info->mmio_phys = r->start; + + /* Allocate a buffer to allow flash detection */ + info->buf_size = INIT_BUFFER_SIZE; + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) { + ret = -ENOMEM; + goto fail_disable_clk; + } + + /* initialize all interrupts to be disabled */ + disable_int(info, NDSR_MASK); + + ret = request_threaded_irq(irq, pxa3xx_nand_irq, + pxa3xx_nand_irq_thread, IRQF_ONESHOT, + pdev->name, info); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); + goto fail_free_buf; + } + + platform_set_drvdata(pdev, info); + + return 0; + +fail_free_buf: + free_irq(irq, info); + kfree(info->data_buff); +fail_disable_clk: + clk_disable_unprepare(info->clk); + return ret; +} + +static int pxa3xx_nand_remove(struct platform_device *pdev) +{ + struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); + struct pxa3xx_nand_platform_data *pdata; + int irq, cs; + + if (!info) + return 0; + + pdata = dev_get_platdata(&pdev->dev); + + irq = platform_get_irq(pdev, 0); + if (irq >= 0) + free_irq(irq, info); + pxa3xx_nand_free_buff(info); + + /* + * In the pxa3xx case, the DFI bus is shared between the SMC and NFC. + * In order to prevent a lockup of the system bus, the DFI bus + * arbitration is granted to SMC upon driver removal. This is done by + * setting the x_ARB_CNTL bit, which also prevents the NAND to have + * access to the bus anymore. + */ + nand_writel(info, NDCR, + (nand_readl(info, NDCR) & ~NDCR_ND_ARB_EN) | + NFCV1_NDCR_ARB_CNTL); + clk_disable_unprepare(info->clk); + + for (cs = 0; cs < pdata->num_cs; cs++) + nand_release(nand_to_mtd(&info->host[cs]->chip)); + return 0; +} + +static int pxa3xx_nand_probe_dt(struct platform_device *pdev) +{ + struct pxa3xx_nand_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = + of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); + + if (!of_id) + return 0; + + /* + * Some SoCs like A7k/A8k need to enable manually the NAND + * controller to avoid being bootloader dependent. This is done + * through the use of a single bit in the System Functions registers. + */ + if (pxa3xx_nand_get_variant(pdev) == PXA3XX_NAND_VARIANT_ARMADA_8K) { + struct regmap *sysctrl_base = syscon_regmap_lookup_by_phandle( + pdev->dev.of_node, "marvell,system-controller"); + u32 reg; + + if (IS_ERR(sysctrl_base)) + return PTR_ERR(sysctrl_base); + + regmap_read(sysctrl_base, GENCONF_SOC_DEVICE_MUX, ®); + reg |= GENCONF_SOC_DEVICE_MUX_NFC_EN; + regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg); + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (of_get_property(np, "marvell,nand-enable-arbiter", NULL)) + pdata->enable_arbiter = 1; + if (of_get_property(np, "marvell,nand-keep-config", NULL)) + pdata->keep_config = 1; + of_property_read_u32(np, "num-cs", &pdata->num_cs); + + pdev->dev.platform_data = pdata; + + return 0; +} + +static int pxa3xx_nand_probe(struct platform_device *pdev) +{ + struct pxa3xx_nand_platform_data *pdata; + struct pxa3xx_nand_info *info; + int ret, cs, probe_success, dma_available; + + dma_available = IS_ENABLED(CONFIG_ARM) && + (IS_ENABLED(CONFIG_ARCH_PXA) || IS_ENABLED(CONFIG_ARCH_MMP)); + if (use_dma && !dma_available) { + use_dma = 0; + dev_warn(&pdev->dev, + "This platform can't do DMA on this device\n"); + } + + ret = pxa3xx_nand_probe_dt(pdev); + if (ret) + return ret; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -ENODEV; + } + + ret = alloc_nand_resource(pdev); + if (ret) + return ret; + + info = platform_get_drvdata(pdev); + probe_success = 0; + for (cs = 0; cs < pdata->num_cs; cs++) { + struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip); + + /* + * The mtd name matches the one used in 'mtdparts' kernel + * parameter. This name cannot be changed or otherwise + * user's mtd partitions configuration would get broken. + */ + mtd->name = "pxa3xx_nand-0"; + info->cs = cs; + ret = pxa3xx_nand_scan(mtd); + if (ret) { + dev_warn(&pdev->dev, "failed to scan nand at cs %d\n", + cs); + continue; + } + + ret = mtd_device_register(mtd, pdata->parts[cs], + pdata->nr_parts[cs]); + if (!ret) + probe_success = 1; + } + + if (!probe_success) { + pxa3xx_nand_remove(pdev); + return -ENODEV; + } + + return 0; +} + +#ifdef CONFIG_PM +static int pxa3xx_nand_suspend(struct device *dev) +{ + struct pxa3xx_nand_info *info = dev_get_drvdata(dev); + + if (info->state) { + dev_err(dev, "driver busy, state = %d\n", info->state); + return -EAGAIN; + } + + clk_disable(info->clk); + return 0; +} + +static int pxa3xx_nand_resume(struct device *dev) +{ + struct pxa3xx_nand_info *info = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(info->clk); + if (ret < 0) + return ret; + + /* We don't want to handle interrupt without calling mtd routine */ + disable_int(info, NDCR_INT_MASK); + + /* + * Directly set the chip select to a invalid value, + * then the driver would reset the timing according + * to current chip select at the beginning of cmdfunc + */ + info->cs = 0xff; + + /* + * As the spec says, the NDSR would be updated to 0x1800 when + * doing the nand_clk disable/enable. + * To prevent it damaging state machine of the driver, clear + * all status before resume + */ + nand_writel(info, NDSR, NDSR_MASK); + + return 0; +} +#else +#define pxa3xx_nand_suspend NULL +#define pxa3xx_nand_resume NULL +#endif + +static const struct dev_pm_ops pxa3xx_nand_pm_ops = { + .suspend = pxa3xx_nand_suspend, + .resume = pxa3xx_nand_resume, +}; + +static struct platform_driver pxa3xx_nand_driver = { + .driver = { + .name = "pxa3xx-nand", + .of_match_table = pxa3xx_nand_dt_ids, + .pm = &pxa3xx_nand_pm_ops, + }, + .probe = pxa3xx_nand_probe, + .remove = pxa3xx_nand_remove, +}; + +module_platform_driver(pxa3xx_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PXA3xx NAND controller driver"); diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c new file mode 100644 index 000000000000..563b759ffca6 --- /dev/null +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -0,0 +1,2921 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* XXX: drivers shall never use this directly! */ + +/* NANDc reg offsets */ +#define NAND_FLASH_CMD 0x00 +#define NAND_ADDR0 0x04 +#define NAND_ADDR1 0x08 +#define NAND_FLASH_CHIP_SELECT 0x0c +#define NAND_EXEC_CMD 0x10 +#define NAND_FLASH_STATUS 0x14 +#define NAND_BUFFER_STATUS 0x18 +#define NAND_DEV0_CFG0 0x20 +#define NAND_DEV0_CFG1 0x24 +#define NAND_DEV0_ECC_CFG 0x28 +#define NAND_DEV1_ECC_CFG 0x2c +#define NAND_DEV1_CFG0 0x30 +#define NAND_DEV1_CFG1 0x34 +#define NAND_READ_ID 0x40 +#define NAND_READ_STATUS 0x44 +#define NAND_DEV_CMD0 0xa0 +#define NAND_DEV_CMD1 0xa4 +#define NAND_DEV_CMD2 0xa8 +#define NAND_DEV_CMD_VLD 0xac +#define SFLASHC_BURST_CFG 0xe0 +#define NAND_ERASED_CW_DETECT_CFG 0xe8 +#define NAND_ERASED_CW_DETECT_STATUS 0xec +#define NAND_EBI2_ECC_BUF_CFG 0xf0 +#define FLASH_BUF_ACC 0x100 + +#define NAND_CTRL 0xf00 +#define NAND_VERSION 0xf08 +#define NAND_READ_LOCATION_0 0xf20 +#define NAND_READ_LOCATION_1 0xf24 +#define NAND_READ_LOCATION_2 0xf28 +#define NAND_READ_LOCATION_3 0xf2c + +/* dummy register offsets, used by write_reg_dma */ +#define NAND_DEV_CMD1_RESTORE 0xdead +#define NAND_DEV_CMD_VLD_RESTORE 0xbeef + +/* NAND_FLASH_CMD bits */ +#define PAGE_ACC BIT(4) +#define LAST_PAGE BIT(5) + +/* NAND_FLASH_CHIP_SELECT bits */ +#define NAND_DEV_SEL 0 +#define DM_EN BIT(2) + +/* NAND_FLASH_STATUS bits */ +#define FS_OP_ERR BIT(4) +#define FS_READY_BSY_N BIT(5) +#define FS_MPU_ERR BIT(8) +#define FS_DEVICE_STS_ERR BIT(16) +#define FS_DEVICE_WP BIT(23) + +/* NAND_BUFFER_STATUS bits */ +#define BS_UNCORRECTABLE_BIT BIT(8) +#define BS_CORRECTABLE_ERR_MSK 0x1f + +/* NAND_DEVn_CFG0 bits */ +#define DISABLE_STATUS_AFTER_WRITE 4 +#define CW_PER_PAGE 6 +#define UD_SIZE_BYTES 9 +#define ECC_PARITY_SIZE_BYTES_RS 19 +#define SPARE_SIZE_BYTES 23 +#define NUM_ADDR_CYCLES 27 +#define STATUS_BFR_READ 30 +#define SET_RD_MODE_AFTER_STATUS 31 + +/* NAND_DEVn_CFG0 bits */ +#define DEV0_CFG1_ECC_DISABLE 0 +#define WIDE_FLASH 1 +#define NAND_RECOVERY_CYCLES 2 +#define CS_ACTIVE_BSY 5 +#define BAD_BLOCK_BYTE_NUM 6 +#define BAD_BLOCK_IN_SPARE_AREA 16 +#define WR_RD_BSY_GAP 17 +#define ENABLE_BCH_ECC 27 + +/* NAND_DEV0_ECC_CFG bits */ +#define ECC_CFG_ECC_DISABLE 0 +#define ECC_SW_RESET 1 +#define ECC_MODE 4 +#define ECC_PARITY_SIZE_BYTES_BCH 8 +#define ECC_NUM_DATA_BYTES 16 +#define ECC_FORCE_CLK_OPEN 30 + +/* NAND_DEV_CMD1 bits */ +#define READ_ADDR 0 + +/* NAND_DEV_CMD_VLD bits */ +#define READ_START_VLD BIT(0) +#define READ_STOP_VLD BIT(1) +#define WRITE_START_VLD BIT(2) +#define ERASE_START_VLD BIT(3) +#define SEQ_READ_START_VLD BIT(4) + +/* NAND_EBI2_ECC_BUF_CFG bits */ +#define NUM_STEPS 0 + +/* NAND_ERASED_CW_DETECT_CFG bits */ +#define ERASED_CW_ECC_MASK 1 +#define AUTO_DETECT_RES 0 +#define MASK_ECC (1 << ERASED_CW_ECC_MASK) +#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) +#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) +#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) +#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) + +/* NAND_ERASED_CW_DETECT_STATUS bits */ +#define PAGE_ALL_ERASED BIT(7) +#define CODEWORD_ALL_ERASED BIT(6) +#define PAGE_ERASED BIT(5) +#define CODEWORD_ERASED BIT(4) +#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) +#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) + +/* NAND_READ_LOCATION_n bits */ +#define READ_LOCATION_OFFSET 0 +#define READ_LOCATION_SIZE 16 +#define READ_LOCATION_LAST 31 + +/* Version Mask */ +#define NAND_VERSION_MAJOR_MASK 0xf0000000 +#define NAND_VERSION_MAJOR_SHIFT 28 +#define NAND_VERSION_MINOR_MASK 0x0fff0000 +#define NAND_VERSION_MINOR_SHIFT 16 + +/* NAND OP_CMDs */ +#define PAGE_READ 0x2 +#define PAGE_READ_WITH_ECC 0x3 +#define PAGE_READ_WITH_ECC_SPARE 0x4 +#define PROGRAM_PAGE 0x6 +#define PAGE_PROGRAM_WITH_ECC 0x7 +#define PROGRAM_PAGE_SPARE 0x9 +#define BLOCK_ERASE 0xa +#define FETCH_ID 0xb +#define RESET_DEVICE 0xd + +/* Default Value for NAND_DEV_CMD_VLD */ +#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ + ERASE_START_VLD | SEQ_READ_START_VLD) + +/* NAND_CTRL bits */ +#define BAM_MODE_EN BIT(0) + +/* + * the NAND controller performs reads/writes with ECC in 516 byte chunks. + * the driver calls the chunks 'step' or 'codeword' interchangeably + */ +#define NANDC_STEP_SIZE 512 + +/* + * the largest page size we support is 8K, this will have 16 steps/codewords + * of 512 bytes each + */ +#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) + +/* we read at most 3 registers per codeword scan */ +#define MAX_REG_RD (3 * MAX_NUM_STEPS) + +/* ECC modes supported by the controller */ +#define ECC_NONE BIT(0) +#define ECC_RS_4BIT BIT(1) +#define ECC_BCH_4BIT BIT(2) +#define ECC_BCH_8BIT BIT(3) + +#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \ +nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ + ((offset) << READ_LOCATION_OFFSET) | \ + ((size) << READ_LOCATION_SIZE) | \ + ((is_last) << READ_LOCATION_LAST)) + +/* + * Returns the actual register address for all NAND_DEV_ registers + * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) + */ +#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) + +/* Returns the NAND register physical address */ +#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) + +/* Returns the dma address for reg read buffer */ +#define reg_buf_dma_addr(chip, vaddr) \ + ((chip)->reg_read_dma + \ + ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf)) + +#define QPIC_PER_CW_CMD_ELEMENTS 32 +#define QPIC_PER_CW_CMD_SGL 32 +#define QPIC_PER_CW_DATA_SGL 8 + +/* + * Flags used in DMA descriptor preparation helper functions + * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) + */ +/* Don't set the EOT in current tx BAM sgl */ +#define NAND_BAM_NO_EOT BIT(0) +/* Set the NWD flag in current BAM sgl */ +#define NAND_BAM_NWD BIT(1) +/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ +#define NAND_BAM_NEXT_SGL BIT(2) +/* + * Erased codeword status is being used two times in single transfer so this + * flag will determine the current value of erased codeword status register + */ +#define NAND_ERASED_CW_SET BIT(4) + +/* + * This data type corresponds to the BAM transaction which will be used for all + * NAND transfers. + * @bam_ce - the array of BAM command elements + * @cmd_sgl - sgl for NAND BAM command pipe + * @data_sgl - sgl for NAND BAM consumer/producer pipe + * @bam_ce_pos - the index in bam_ce which is available for next sgl + * @bam_ce_start - the index in bam_ce which marks the start position ce + * for current sgl. It will be used for size calculation + * for current sgl + * @cmd_sgl_pos - current index in command sgl. + * @cmd_sgl_start - start index in command sgl. + * @tx_sgl_pos - current index in data sgl for tx. + * @tx_sgl_start - start index in data sgl for tx. + * @rx_sgl_pos - current index in data sgl for rx. + * @rx_sgl_start - start index in data sgl for rx. + */ +struct bam_transaction { + struct bam_cmd_element *bam_ce; + struct scatterlist *cmd_sgl; + struct scatterlist *data_sgl; + u32 bam_ce_pos; + u32 bam_ce_start; + u32 cmd_sgl_pos; + u32 cmd_sgl_start; + u32 tx_sgl_pos; + u32 tx_sgl_start; + u32 rx_sgl_pos; + u32 rx_sgl_start; +}; + +/* + * This data type corresponds to the nand dma descriptor + * @list - list for desc_info + * @dir - DMA transfer direction + * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by + * ADM + * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM + * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM + * @dma_desc - low level DMA engine descriptor + */ +struct desc_info { + struct list_head node; + + enum dma_data_direction dir; + union { + struct scatterlist adm_sgl; + struct { + struct scatterlist *bam_sgl; + int sgl_cnt; + }; + }; + struct dma_async_tx_descriptor *dma_desc; +}; + +/* + * holds the current register values that we want to write. acts as a contiguous + * chunk of memory which we use to write the controller registers through DMA. + */ +struct nandc_regs { + __le32 cmd; + __le32 addr0; + __le32 addr1; + __le32 chip_sel; + __le32 exec; + + __le32 cfg0; + __le32 cfg1; + __le32 ecc_bch_cfg; + + __le32 clrflashstatus; + __le32 clrreadstatus; + + __le32 cmd1; + __le32 vld; + + __le32 orig_cmd1; + __le32 orig_vld; + + __le32 ecc_buf_cfg; + __le32 read_location0; + __le32 read_location1; + __le32 read_location2; + __le32 read_location3; + + __le32 erased_cw_detect_cfg_clr; + __le32 erased_cw_detect_cfg_set; +}; + +/* + * NAND controller data struct + * + * @controller: base controller structure + * @host_list: list containing all the chips attached to the + * controller + * @dev: parent device + * @base: MMIO base + * @base_phys: physical base address of controller registers + * @base_dma: dma base address of controller registers + * @core_clk: controller clock + * @aon_clk: another controller clock + * + * @chan: dma channel + * @cmd_crci: ADM DMA CRCI for command flow control + * @data_crci: ADM DMA CRCI for data flow control + * @desc_list: DMA descriptor list (list of desc_infos) + * + * @data_buffer: our local DMA buffer for page read/writes, + * used when we can't use the buffer provided + * by upper layers directly + * @buf_size/count/start: markers for chip->read_buf/write_buf functions + * @reg_read_buf: local buffer for reading back registers via DMA + * @reg_read_dma: contains dma address for register read buffer + * @reg_read_pos: marker for data read in reg_read_buf + * + * @regs: a contiguous chunk of memory for DMA register + * writes. contains the register values to be + * written to controller + * @cmd1/vld: some fixed controller register values + * @props: properties of current NAND controller, + * initialized via DT match data + * @max_cwperpage: maximum QPIC codewords required. calculated + * from all connected NAND devices pagesize + */ +struct qcom_nand_controller { + struct nand_hw_control controller; + struct list_head host_list; + + struct device *dev; + + void __iomem *base; + phys_addr_t base_phys; + dma_addr_t base_dma; + + struct clk *core_clk; + struct clk *aon_clk; + + union { + /* will be used only by QPIC for BAM DMA */ + struct { + struct dma_chan *tx_chan; + struct dma_chan *rx_chan; + struct dma_chan *cmd_chan; + }; + + /* will be used only by EBI2 for ADM DMA */ + struct { + struct dma_chan *chan; + unsigned int cmd_crci; + unsigned int data_crci; + }; + }; + + struct list_head desc_list; + struct bam_transaction *bam_txn; + + u8 *data_buffer; + int buf_size; + int buf_count; + int buf_start; + unsigned int max_cwperpage; + + __le32 *reg_read_buf; + dma_addr_t reg_read_dma; + int reg_read_pos; + + struct nandc_regs *regs; + + u32 cmd1, vld; + const struct qcom_nandc_props *props; +}; + +/* + * NAND chip structure + * + * @chip: base NAND chip structure + * @node: list node to add itself to host_list in + * qcom_nand_controller + * + * @cs: chip select value for this chip + * @cw_size: the number of bytes in a single step/codeword + * of a page, consisting of all data, ecc, spare + * and reserved bytes + * @cw_data: the number of bytes within a codeword protected + * by ECC + * @use_ecc: request the controller to use ECC for the + * upcoming read/write + * @bch_enabled: flag to tell whether BCH ECC mode is used + * @ecc_bytes_hw: ECC bytes used by controller hardware for this + * chip + * @status: value to be returned if NAND_CMD_STATUS command + * is executed + * @last_command: keeps track of last command on this chip. used + * for reading correct status + * + * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for + * ecc/non-ecc mode for the current nand flash + * device + */ +struct qcom_nand_host { + struct nand_chip chip; + struct list_head node; + + int cs; + int cw_size; + int cw_data; + bool use_ecc; + bool bch_enabled; + int ecc_bytes_hw; + int spare_bytes; + int bbm_size; + u8 status; + int last_command; + + u32 cfg0, cfg1; + u32 cfg0_raw, cfg1_raw; + u32 ecc_buf_cfg; + u32 ecc_bch_cfg; + u32 clrflashstatus; + u32 clrreadstatus; +}; + +/* + * This data type corresponds to the NAND controller properties which varies + * among different NAND controllers. + * @ecc_modes - ecc mode for NAND + * @is_bam - whether NAND controller is using BAM + * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset + */ +struct qcom_nandc_props { + u32 ecc_modes; + bool is_bam; + u32 dev_cmd_reg_start; +}; + +/* Frees the BAM transaction memory */ +static void free_bam_transaction(struct qcom_nand_controller *nandc) +{ + struct bam_transaction *bam_txn = nandc->bam_txn; + + devm_kfree(nandc->dev, bam_txn); +} + +/* Allocates and Initializes the BAM transaction */ +static struct bam_transaction * +alloc_bam_transaction(struct qcom_nand_controller *nandc) +{ + struct bam_transaction *bam_txn; + size_t bam_txn_size; + unsigned int num_cw = nandc->max_cwperpage; + void *bam_txn_buf; + + bam_txn_size = + sizeof(*bam_txn) + num_cw * + ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + + (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + + (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); + + bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); + if (!bam_txn_buf) + return NULL; + + bam_txn = bam_txn_buf; + bam_txn_buf += sizeof(*bam_txn); + + bam_txn->bam_ce = bam_txn_buf; + bam_txn_buf += + sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; + + bam_txn->cmd_sgl = bam_txn_buf; + bam_txn_buf += + sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; + + bam_txn->data_sgl = bam_txn_buf; + + return bam_txn; +} + +/* Clears the BAM transaction indexes */ +static void clear_bam_transaction(struct qcom_nand_controller *nandc) +{ + struct bam_transaction *bam_txn = nandc->bam_txn; + + if (!nandc->props->is_bam) + return; + + bam_txn->bam_ce_pos = 0; + bam_txn->bam_ce_start = 0; + bam_txn->cmd_sgl_pos = 0; + bam_txn->cmd_sgl_start = 0; + bam_txn->tx_sgl_pos = 0; + bam_txn->tx_sgl_start = 0; + bam_txn->rx_sgl_pos = 0; + bam_txn->rx_sgl_start = 0; + + sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * + QPIC_PER_CW_CMD_SGL); + sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * + QPIC_PER_CW_DATA_SGL); +} + +static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) +{ + return container_of(chip, struct qcom_nand_host, chip); +} + +static inline struct qcom_nand_controller * +get_qcom_nand_controller(struct nand_chip *chip) +{ + return container_of(chip->controller, struct qcom_nand_controller, + controller); +} + +static inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset) +{ + return ioread32(nandc->base + offset); +} + +static inline void nandc_write(struct qcom_nand_controller *nandc, int offset, + u32 val) +{ + iowrite32(val, nandc->base + offset); +} + +static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, + bool is_cpu) +{ + if (!nandc->props->is_bam) + return; + + if (is_cpu) + dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); + else + dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); +} + +static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) +{ + switch (offset) { + case NAND_FLASH_CMD: + return ®s->cmd; + case NAND_ADDR0: + return ®s->addr0; + case NAND_ADDR1: + return ®s->addr1; + case NAND_FLASH_CHIP_SELECT: + return ®s->chip_sel; + case NAND_EXEC_CMD: + return ®s->exec; + case NAND_FLASH_STATUS: + return ®s->clrflashstatus; + case NAND_DEV0_CFG0: + return ®s->cfg0; + case NAND_DEV0_CFG1: + return ®s->cfg1; + case NAND_DEV0_ECC_CFG: + return ®s->ecc_bch_cfg; + case NAND_READ_STATUS: + return ®s->clrreadstatus; + case NAND_DEV_CMD1: + return ®s->cmd1; + case NAND_DEV_CMD1_RESTORE: + return ®s->orig_cmd1; + case NAND_DEV_CMD_VLD: + return ®s->vld; + case NAND_DEV_CMD_VLD_RESTORE: + return ®s->orig_vld; + case NAND_EBI2_ECC_BUF_CFG: + return ®s->ecc_buf_cfg; + case NAND_READ_LOCATION_0: + return ®s->read_location0; + case NAND_READ_LOCATION_1: + return ®s->read_location1; + case NAND_READ_LOCATION_2: + return ®s->read_location2; + case NAND_READ_LOCATION_3: + return ®s->read_location3; + default: + return NULL; + } +} + +static void nandc_set_reg(struct qcom_nand_controller *nandc, int offset, + u32 val) +{ + struct nandc_regs *regs = nandc->regs; + __le32 *reg; + + reg = offset_to_nandc_reg(regs, offset); + + if (reg) + *reg = cpu_to_le32(val); +} + +/* helper to configure address register values */ +static void set_address(struct qcom_nand_host *host, u16 column, int page) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + + nandc_set_reg(nandc, NAND_ADDR0, page << 16 | column); + nandc_set_reg(nandc, NAND_ADDR1, page >> 16 & 0xff); +} + +/* + * update_rw_regs: set up read/write register values, these will be + * written to the NAND controller registers via DMA + * + * @num_cw: number of steps for the read/write operation + * @read: read or write operation + */ +static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + u32 cmd, cfg0, cfg1, ecc_bch_cfg; + + if (read) { + if (host->use_ecc) + cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; + else + cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; + } else { + cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; + } + + if (host->use_ecc) { + cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) | + (num_cw - 1) << CW_PER_PAGE; + + cfg1 = host->cfg1; + ecc_bch_cfg = host->ecc_bch_cfg; + } else { + cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) | + (num_cw - 1) << CW_PER_PAGE; + + cfg1 = host->cfg1_raw; + ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; + } + + nandc_set_reg(nandc, NAND_FLASH_CMD, cmd); + nandc_set_reg(nandc, NAND_DEV0_CFG0, cfg0); + nandc_set_reg(nandc, NAND_DEV0_CFG1, cfg1); + nandc_set_reg(nandc, NAND_DEV0_ECC_CFG, ecc_bch_cfg); + nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg); + nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); + nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); + nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + + if (read) + nandc_set_read_loc(nandc, 0, 0, host->use_ecc ? + host->cw_data : host->cw_size, 1); +} + +/* + * Maps the scatter gather list for DMA transfer and forms the DMA descriptor + * for BAM. This descriptor will be added in the NAND DMA descriptor queue + * which will be submitted to DMA engine. + */ +static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, + struct dma_chan *chan, + unsigned long flags) +{ + struct desc_info *desc; + struct scatterlist *sgl; + unsigned int sgl_cnt; + int ret; + struct bam_transaction *bam_txn = nandc->bam_txn; + enum dma_transfer_direction dir_eng; + struct dma_async_tx_descriptor *dma_desc; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + if (chan == nandc->cmd_chan) { + sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; + sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; + bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; + dir_eng = DMA_MEM_TO_DEV; + desc->dir = DMA_TO_DEVICE; + } else if (chan == nandc->tx_chan) { + sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; + sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; + bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; + dir_eng = DMA_MEM_TO_DEV; + desc->dir = DMA_TO_DEVICE; + } else { + sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; + sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; + bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; + dir_eng = DMA_DEV_TO_MEM; + desc->dir = DMA_FROM_DEVICE; + } + + sg_mark_end(sgl + sgl_cnt - 1); + ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); + if (ret == 0) { + dev_err(nandc->dev, "failure in mapping desc\n"); + kfree(desc); + return -ENOMEM; + } + + desc->sgl_cnt = sgl_cnt; + desc->bam_sgl = sgl; + + dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, + flags); + + if (!dma_desc) { + dev_err(nandc->dev, "failure in prep desc\n"); + dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); + kfree(desc); + return -EINVAL; + } + + desc->dma_desc = dma_desc; + + list_add_tail(&desc->node, &nandc->desc_list); + + return 0; +} + +/* + * Prepares the command descriptor for BAM DMA which will be used for NAND + * register reads and writes. The command descriptor requires the command + * to be formed in command element type so this function uses the command + * element from bam transaction ce array and fills the same with required + * data. A single SGL can contain multiple command elements so + * NAND_BAM_NEXT_SGL will be used for starting the separate SGL + * after the current command element. + */ +static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, + int reg_off, const void *vaddr, + int size, unsigned int flags) +{ + int bam_ce_size; + int i, ret; + struct bam_cmd_element *bam_ce_buffer; + struct bam_transaction *bam_txn = nandc->bam_txn; + + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; + + /* fill the command desc */ + for (i = 0; i < size; i++) { + if (read) + bam_prep_ce(&bam_ce_buffer[i], + nandc_reg_phys(nandc, reg_off + 4 * i), + BAM_READ_COMMAND, + reg_buf_dma_addr(nandc, + (__le32 *)vaddr + i)); + else + bam_prep_ce_le32(&bam_ce_buffer[i], + nandc_reg_phys(nandc, reg_off + 4 * i), + BAM_WRITE_COMMAND, + *((__le32 *)vaddr + i)); + } + + bam_txn->bam_ce_pos += size; + + /* use the separate sgl after this command */ + if (flags & NAND_BAM_NEXT_SGL) { + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; + bam_ce_size = (bam_txn->bam_ce_pos - + bam_txn->bam_ce_start) * + sizeof(struct bam_cmd_element); + sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], + bam_ce_buffer, bam_ce_size); + bam_txn->cmd_sgl_pos++; + bam_txn->bam_ce_start = bam_txn->bam_ce_pos; + + if (flags & NAND_BAM_NWD) { + ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, + DMA_PREP_FENCE | + DMA_PREP_CMD); + if (ret) + return ret; + } + } + + return 0; +} + +/* + * Prepares the data descriptor for BAM DMA which will be used for NAND + * data reads and writes. + */ +static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, + const void *vaddr, + int size, unsigned int flags) +{ + int ret; + struct bam_transaction *bam_txn = nandc->bam_txn; + + if (read) { + sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], + vaddr, size); + bam_txn->rx_sgl_pos++; + } else { + sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], + vaddr, size); + bam_txn->tx_sgl_pos++; + + /* + * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag + * is not set, form the DMA descriptor + */ + if (!(flags & NAND_BAM_NO_EOT)) { + ret = prepare_bam_async_desc(nandc, nandc->tx_chan, + DMA_PREP_INTERRUPT); + if (ret) + return ret; + } + } + + return 0; +} + +static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, + int reg_off, const void *vaddr, int size, + bool flow_control) +{ + struct desc_info *desc; + struct dma_async_tx_descriptor *dma_desc; + struct scatterlist *sgl; + struct dma_slave_config slave_conf; + enum dma_transfer_direction dir_eng; + int ret; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + sgl = &desc->adm_sgl; + + sg_init_one(sgl, vaddr, size); + + if (read) { + dir_eng = DMA_DEV_TO_MEM; + desc->dir = DMA_FROM_DEVICE; + } else { + dir_eng = DMA_MEM_TO_DEV; + desc->dir = DMA_TO_DEVICE; + } + + ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); + if (ret == 0) { + ret = -ENOMEM; + goto err; + } + + memset(&slave_conf, 0x00, sizeof(slave_conf)); + + slave_conf.device_fc = flow_control; + if (read) { + slave_conf.src_maxburst = 16; + slave_conf.src_addr = nandc->base_dma + reg_off; + slave_conf.slave_id = nandc->data_crci; + } else { + slave_conf.dst_maxburst = 16; + slave_conf.dst_addr = nandc->base_dma + reg_off; + slave_conf.slave_id = nandc->cmd_crci; + } + + ret = dmaengine_slave_config(nandc->chan, &slave_conf); + if (ret) { + dev_err(nandc->dev, "failed to configure dma channel\n"); + goto err; + } + + dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); + if (!dma_desc) { + dev_err(nandc->dev, "failed to prepare desc\n"); + ret = -EINVAL; + goto err; + } + + desc->dma_desc = dma_desc; + + list_add_tail(&desc->node, &nandc->desc_list); + + return 0; +err: + kfree(desc); + + return ret; +} + +/* + * read_reg_dma: prepares a descriptor to read a given number of + * contiguous registers to the reg_read_buf pointer + * + * @first: offset of the first register in the contiguous block + * @num_regs: number of registers to read + * @flags: flags to control DMA descriptor preparation + */ +static int read_reg_dma(struct qcom_nand_controller *nandc, int first, + int num_regs, unsigned int flags) +{ + bool flow_control = false; + void *vaddr; + + vaddr = nandc->reg_read_buf + nandc->reg_read_pos; + nandc->reg_read_pos += num_regs; + + if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) + first = dev_cmd_reg_addr(nandc, first); + + if (nandc->props->is_bam) + return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, + num_regs, flags); + + if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) + flow_control = true; + + return prep_adm_dma_desc(nandc, true, first, vaddr, + num_regs * sizeof(u32), flow_control); +} + +/* + * write_reg_dma: prepares a descriptor to write a given number of + * contiguous registers + * + * @first: offset of the first register in the contiguous block + * @num_regs: number of registers to write + * @flags: flags to control DMA descriptor preparation + */ +static int write_reg_dma(struct qcom_nand_controller *nandc, int first, + int num_regs, unsigned int flags) +{ + bool flow_control = false; + struct nandc_regs *regs = nandc->regs; + void *vaddr; + + vaddr = offset_to_nandc_reg(regs, first); + + if (first == NAND_ERASED_CW_DETECT_CFG) { + if (flags & NAND_ERASED_CW_SET) + vaddr = ®s->erased_cw_detect_cfg_set; + else + vaddr = ®s->erased_cw_detect_cfg_clr; + } + + if (first == NAND_EXEC_CMD) + flags |= NAND_BAM_NWD; + + if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); + + if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); + + if (nandc->props->is_bam) + return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, + num_regs, flags); + + if (first == NAND_FLASH_CMD) + flow_control = true; + + return prep_adm_dma_desc(nandc, false, first, vaddr, + num_regs * sizeof(u32), flow_control); +} + +/* + * read_data_dma: prepares a DMA descriptor to transfer data from the + * controller's internal buffer to the buffer 'vaddr' + * + * @reg_off: offset within the controller's data buffer + * @vaddr: virtual address of the buffer we want to write to + * @size: DMA transaction size in bytes + * @flags: flags to control DMA descriptor preparation + */ +static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, + const u8 *vaddr, int size, unsigned int flags) +{ + if (nandc->props->is_bam) + return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); + + return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); +} + +/* + * write_data_dma: prepares a DMA descriptor to transfer data from + * 'vaddr' to the controller's internal buffer + * + * @reg_off: offset within the controller's data buffer + * @vaddr: virtual address of the buffer we want to read from + * @size: DMA transaction size in bytes + * @flags: flags to control DMA descriptor preparation + */ +static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, + const u8 *vaddr, int size, unsigned int flags) +{ + if (nandc->props->is_bam) + return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); + + return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); +} + +/* + * Helper to prepare DMA descriptors for configuring registers + * before reading a NAND page. + */ +static void config_nand_page_read(struct qcom_nand_controller *nandc) +{ + write_reg_dma(nandc, NAND_ADDR0, 2, 0); + write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); + write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); + write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); + write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, + NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); +} + +/* + * Helper to prepare DMA descriptors for configuring registers + * before reading each codeword in NAND page. + */ +static void config_nand_cw_read(struct qcom_nand_controller *nandc) +{ + if (nandc->props->is_bam) + write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, + NAND_BAM_NEXT_SGL); + + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); + read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, + NAND_BAM_NEXT_SGL); +} + +/* + * Helper to prepare dma descriptors to configure registers needed for reading a + * single codeword in page + */ +static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc) +{ + config_nand_page_read(nandc); + config_nand_cw_read(nandc); +} + +/* + * Helper to prepare DMA descriptors used to configure registers needed for + * before writing a NAND page. + */ +static void config_nand_page_write(struct qcom_nand_controller *nandc) +{ + write_reg_dma(nandc, NAND_ADDR0, 2, 0); + write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); + write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, + NAND_BAM_NEXT_SGL); +} + +/* + * Helper to prepare DMA descriptors for configuring registers + * before writing each codeword in NAND page. + */ +static void config_nand_cw_write(struct qcom_nand_controller *nandc) +{ + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); + write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); +} + +/* + * the following functions are used within chip->cmdfunc() to perform different + * NAND_CMD_* commands + */ + +/* sets up descriptors for NAND_CMD_PARAM */ +static int nandc_param(struct qcom_nand_host *host) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + /* + * NAND_CMD_PARAM is called before we know much about the FLASH chip + * in use. we configure the controller to perform a raw read of 512 + * bytes to read onfi params + */ + nandc_set_reg(nandc, NAND_FLASH_CMD, PAGE_READ | PAGE_ACC | LAST_PAGE); + nandc_set_reg(nandc, NAND_ADDR0, 0); + nandc_set_reg(nandc, NAND_ADDR1, 0); + nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE + | 512 << UD_SIZE_BYTES + | 5 << NUM_ADDR_CYCLES + | 0 << SPARE_SIZE_BYTES); + nandc_set_reg(nandc, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES + | 0 << CS_ACTIVE_BSY + | 17 << BAD_BLOCK_BYTE_NUM + | 1 << BAD_BLOCK_IN_SPARE_AREA + | 2 << WR_RD_BSY_GAP + | 0 << WIDE_FLASH + | 1 << DEV0_CFG1_ECC_DISABLE); + nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); + + /* configure CMD1 and VLD for ONFI param probing */ + nandc_set_reg(nandc, NAND_DEV_CMD_VLD, + (nandc->vld & ~READ_START_VLD)); + nandc_set_reg(nandc, NAND_DEV_CMD1, + (nandc->cmd1 & ~(0xFF << READ_ADDR)) + | NAND_CMD_PARAM << READ_ADDR); + + nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + + nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); + nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); + nandc_set_read_loc(nandc, 0, 0, 512, 1); + + write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); + write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); + + nandc->buf_count = 512; + memset(nandc->data_buffer, 0xff, nandc->buf_count); + + config_nand_single_cw_page_read(nandc); + + read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, + nandc->buf_count, 0); + + /* restore CMD1 and VLD regs */ + write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); + write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); + + return 0; +} + +/* sets up descriptors for NAND_CMD_ERASE1 */ +static int erase_block(struct qcom_nand_host *host, int page_addr) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + nandc_set_reg(nandc, NAND_FLASH_CMD, + BLOCK_ERASE | PAGE_ACC | LAST_PAGE); + nandc_set_reg(nandc, NAND_ADDR0, page_addr); + nandc_set_reg(nandc, NAND_ADDR1, 0); + nandc_set_reg(nandc, NAND_DEV0_CFG0, + host->cfg0_raw & ~(7 << CW_PER_PAGE)); + nandc_set_reg(nandc, NAND_DEV0_CFG1, host->cfg1_raw); + nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); + nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); + + write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); + write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); + + return 0; +} + +/* sets up descriptors for NAND_CMD_READID */ +static int read_id(struct qcom_nand_host *host, int column) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + if (column == -1) + return 0; + + nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID); + nandc_set_reg(nandc, NAND_ADDR0, column); + nandc_set_reg(nandc, NAND_ADDR1, 0); + nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, + nandc->props->is_bam ? 0 : DM_EN); + nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + + write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); + + return 0; +} + +/* sets up descriptors for NAND_CMD_RESET */ +static int reset(struct qcom_nand_host *host) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE); + nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + return 0; +} + +/* helpers to submit/free our list of dma descriptors */ +static int submit_descs(struct qcom_nand_controller *nandc) +{ + struct desc_info *desc; + dma_cookie_t cookie = 0; + struct bam_transaction *bam_txn = nandc->bam_txn; + int r; + + if (nandc->props->is_bam) { + if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { + r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); + if (r) + return r; + } + + if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { + r = prepare_bam_async_desc(nandc, nandc->tx_chan, + DMA_PREP_INTERRUPT); + if (r) + return r; + } + + if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { + r = prepare_bam_async_desc(nandc, nandc->cmd_chan, + DMA_PREP_CMD); + if (r) + return r; + } + } + + list_for_each_entry(desc, &nandc->desc_list, node) + cookie = dmaengine_submit(desc->dma_desc); + + if (nandc->props->is_bam) { + dma_async_issue_pending(nandc->tx_chan); + dma_async_issue_pending(nandc->rx_chan); + + if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE) + return -ETIMEDOUT; + } else { + if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) + return -ETIMEDOUT; + } + + return 0; +} + +static void free_descs(struct qcom_nand_controller *nandc) +{ + struct desc_info *desc, *n; + + list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { + list_del(&desc->node); + + if (nandc->props->is_bam) + dma_unmap_sg(nandc->dev, desc->bam_sgl, + desc->sgl_cnt, desc->dir); + else + dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, + desc->dir); + + kfree(desc); + } +} + +/* reset the register read buffer for next NAND operation */ +static void clear_read_regs(struct qcom_nand_controller *nandc) +{ + nandc->reg_read_pos = 0; + nandc_read_buffer_sync(nandc, false); +} + +static void pre_command(struct qcom_nand_host *host, int command) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; + host->last_command = command; + + clear_read_regs(nandc); + + if (command == NAND_CMD_RESET || command == NAND_CMD_READID || + command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) + clear_bam_transaction(nandc); +} + +/* + * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our + * privately maintained status byte, this status byte can be read after + * NAND_CMD_STATUS is called + */ +static void parse_erase_write_errors(struct qcom_nand_host *host, int command) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int num_cw; + int i; + + num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; + nandc_read_buffer_sync(nandc, true); + + for (i = 0; i < num_cw; i++) { + u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); + + if (flash_status & FS_MPU_ERR) + host->status &= ~NAND_STATUS_WP; + + if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && + (flash_status & + FS_DEVICE_STS_ERR))) + host->status |= NAND_STATUS_FAIL; + } +} + +static void post_command(struct qcom_nand_host *host, int command) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + switch (command) { + case NAND_CMD_READID: + nandc_read_buffer_sync(nandc, true); + memcpy(nandc->data_buffer, nandc->reg_read_buf, + nandc->buf_count); + break; + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + parse_erase_write_errors(host, command); + break; + default: + break; + } +} + +/* + * Implements chip->cmdfunc. It's only used for a limited set of commands. + * The rest of the commands wouldn't be called by upper layers. For example, + * NAND_CMD_READOOB would never be called because we have our own versions + * of read_oob ops for nand_ecc_ctrl. + */ +static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + bool wait = false; + int ret = 0; + + pre_command(host, command); + + switch (command) { + case NAND_CMD_RESET: + ret = reset(host); + wait = true; + break; + + case NAND_CMD_READID: + nandc->buf_count = 4; + ret = read_id(host, column); + wait = true; + break; + + case NAND_CMD_PARAM: + ret = nandc_param(host); + wait = true; + break; + + case NAND_CMD_ERASE1: + ret = erase_block(host, page_addr); + wait = true; + break; + + case NAND_CMD_READ0: + /* we read the entire page for now */ + WARN_ON(column != 0); + + host->use_ecc = true; + set_address(host, 0, page_addr); + update_rw_regs(host, ecc->steps, true); + break; + + case NAND_CMD_SEQIN: + WARN_ON(column != 0); + set_address(host, 0, page_addr); + break; + + case NAND_CMD_PAGEPROG: + case NAND_CMD_STATUS: + case NAND_CMD_NONE: + default: + break; + } + + if (ret) { + dev_err(nandc->dev, "failure executing command %d\n", + command); + free_descs(nandc); + return; + } + + if (wait) { + ret = submit_descs(nandc); + if (ret) + dev_err(nandc->dev, + "failure submitting descs for command %d\n", + command); + } + + free_descs(nandc); + + post_command(host, command); +} + +/* + * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read + * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS. + * + * when using RS ECC, the HW reports the same erros when reading an erased CW, + * but it notifies that it is an erased CW by placing special characters at + * certain offsets in the buffer. + * + * verify if the page is erased or not, and fix up the page for RS ECC by + * replacing the special characters with 0xff. + */ +static bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len) +{ + u8 empty1, empty2; + + /* + * an erased page flags an error in NAND_FLASH_STATUS, check if the page + * is erased by looking for 0x54s at offsets 3 and 175 from the + * beginning of each codeword + */ + + empty1 = data_buf[3]; + empty2 = data_buf[175]; + + /* + * if the erased codework markers, if they exist override them with + * 0xffs + */ + if ((empty1 == 0x54 && empty2 == 0xff) || + (empty1 == 0xff && empty2 == 0x54)) { + data_buf[3] = 0xff; + data_buf[175] = 0xff; + } + + /* + * check if the entire chunk contains 0xffs or not. if it doesn't, then + * restore the original values at the special offsets + */ + if (memchr_inv(data_buf, 0xff, data_len)) { + data_buf[3] = empty1; + data_buf[175] = empty2; + + return false; + } + + return true; +} + +struct read_stats { + __le32 flash; + __le32 buffer; + __le32 erased_cw; +}; + +/* + * reads back status registers set by the controller to notify page read + * errors. this is equivalent to what 'ecc->correct()' would do. + */ +static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, + u8 *oob_buf) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + unsigned int max_bitflips = 0; + struct read_stats *buf; + int i; + + buf = (struct read_stats *)nandc->reg_read_buf; + nandc_read_buffer_sync(nandc, true); + + for (i = 0; i < ecc->steps; i++, buf++) { + u32 flash, buffer, erased_cw; + int data_len, oob_len; + + if (i == (ecc->steps - 1)) { + data_len = ecc->size - ((ecc->steps - 1) << 2); + oob_len = ecc->steps << 2; + } else { + data_len = host->cw_data; + oob_len = 0; + } + + flash = le32_to_cpu(buf->flash); + buffer = le32_to_cpu(buf->buffer); + erased_cw = le32_to_cpu(buf->erased_cw); + + if (flash & (FS_OP_ERR | FS_MPU_ERR)) { + bool erased; + + /* ignore erased codeword errors */ + if (host->bch_enabled) { + erased = (erased_cw & ERASED_CW) == ERASED_CW ? + true : false; + } else { + erased = erased_chunk_check_and_fixup(data_buf, + data_len); + } + + if (erased) { + data_buf += data_len; + if (oob_buf) + oob_buf += oob_len + ecc->bytes; + continue; + } + + if (buffer & BS_UNCORRECTABLE_BIT) { + int ret, ecclen, extraooblen; + void *eccbuf; + + eccbuf = oob_buf ? oob_buf + oob_len : NULL; + ecclen = oob_buf ? host->ecc_bytes_hw : 0; + extraooblen = oob_buf ? oob_len : 0; + + /* + * make sure it isn't an erased page reported + * as not-erased by HW because of a few bitflips + */ + ret = nand_check_erased_ecc_chunk(data_buf, + data_len, eccbuf, ecclen, oob_buf, + extraooblen, ecc->strength); + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + max_bitflips = + max_t(unsigned int, max_bitflips, ret); + } + } + } else { + unsigned int stat; + + stat = buffer & BS_CORRECTABLE_ERR_MSK; + mtd->ecc_stats.corrected += stat; + max_bitflips = max(max_bitflips, stat); + } + + data_buf += data_len; + if (oob_buf) + oob_buf += oob_len + ecc->bytes; + } + + return max_bitflips; +} + +/* + * helper to perform the actual page read operation, used by ecc->read_page(), + * ecc->read_oob() + */ +static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, + u8 *oob_buf) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int i, ret; + + config_nand_page_read(nandc); + + /* queue cmd descs for each codeword */ + for (i = 0; i < ecc->steps; i++) { + int data_size, oob_size; + + if (i == (ecc->steps - 1)) { + data_size = ecc->size - ((ecc->steps - 1) << 2); + oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + + host->spare_bytes; + } else { + data_size = host->cw_data; + oob_size = host->ecc_bytes_hw + host->spare_bytes; + } + + if (nandc->props->is_bam) { + if (data_buf && oob_buf) { + nandc_set_read_loc(nandc, 0, 0, data_size, 0); + nandc_set_read_loc(nandc, 1, data_size, + oob_size, 1); + } else if (data_buf) { + nandc_set_read_loc(nandc, 0, 0, data_size, 1); + } else { + nandc_set_read_loc(nandc, 0, data_size, + oob_size, 1); + } + } + + config_nand_cw_read(nandc); + + if (data_buf) + read_data_dma(nandc, FLASH_BUF_ACC, data_buf, + data_size, 0); + + /* + * when ecc is enabled, the controller doesn't read the real + * or dummy bad block markers in each chunk. To maintain a + * consistent layout across RAW and ECC reads, we just + * leave the real/dummy BBM offsets empty (i.e, filled with + * 0xffs) + */ + if (oob_buf) { + int j; + + for (j = 0; j < host->bbm_size; j++) + *oob_buf++ = 0xff; + + read_data_dma(nandc, FLASH_BUF_ACC + data_size, + oob_buf, oob_size, 0); + } + + if (data_buf) + data_buf += data_size; + if (oob_buf) + oob_buf += oob_size; + } + + ret = submit_descs(nandc); + if (ret) + dev_err(nandc->dev, "failure to read page/oob\n"); + + free_descs(nandc); + + return ret; +} + +/* + * a helper that copies the last step/codeword of a page (containing free oob) + * into our local buffer + */ +static int copy_last_cw(struct qcom_nand_host *host, int page) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int size; + int ret; + + clear_read_regs(nandc); + + size = host->use_ecc ? host->cw_data : host->cw_size; + + /* prepare a clean read buffer */ + memset(nandc->data_buffer, 0xff, size); + + set_address(host, host->cw_size * (ecc->steps - 1), page); + update_rw_regs(host, 1, true); + + config_nand_single_cw_page_read(nandc); + + read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); + + ret = submit_descs(nandc); + if (ret) + dev_err(nandc->dev, "failed to copy last codeword\n"); + + free_descs(nandc); + + return ret; +} + +/* implements ecc->read_page() */ +static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + u8 *data_buf, *oob_buf = NULL; + int ret; + + nand_read_page_op(chip, page, 0, NULL, 0); + data_buf = buf; + oob_buf = oob_required ? chip->oob_poi : NULL; + + clear_bam_transaction(nandc); + ret = read_page_ecc(host, data_buf, oob_buf); + if (ret) { + dev_err(nandc->dev, "failure to read page\n"); + return ret; + } + + return parse_read_errors(host, data_buf, oob_buf); +} + +/* implements ecc->read_page_raw() */ +static int qcom_nandc_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + u8 *data_buf, *oob_buf; + struct nand_ecc_ctrl *ecc = &chip->ecc; + int i, ret; + int read_loc; + + nand_read_page_op(chip, page, 0, NULL, 0); + data_buf = buf; + oob_buf = chip->oob_poi; + + host->use_ecc = false; + + clear_bam_transaction(nandc); + update_rw_regs(host, ecc->steps, true); + config_nand_page_read(nandc); + + for (i = 0; i < ecc->steps; i++) { + int data_size1, data_size2, oob_size1, oob_size2; + int reg_off = FLASH_BUF_ACC; + + data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); + oob_size1 = host->bbm_size; + + if (i == (ecc->steps - 1)) { + data_size2 = ecc->size - data_size1 - + ((ecc->steps - 1) << 2); + oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + + host->spare_bytes; + } else { + data_size2 = host->cw_data - data_size1; + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + + if (nandc->props->is_bam) { + read_loc = 0; + nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); + read_loc += data_size1; + + nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); + read_loc += oob_size1; + + nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); + read_loc += data_size2; + + nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); + } + + config_nand_cw_read(nandc); + + read_data_dma(nandc, reg_off, data_buf, data_size1, 0); + reg_off += data_size1; + data_buf += data_size1; + + read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); + reg_off += oob_size1; + oob_buf += oob_size1; + + read_data_dma(nandc, reg_off, data_buf, data_size2, 0); + reg_off += data_size2; + data_buf += data_size2; + + read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); + oob_buf += oob_size2; + } + + ret = submit_descs(nandc); + if (ret) + dev_err(nandc->dev, "failure to read raw page\n"); + + free_descs(nandc); + + return 0; +} + +/* implements ecc->read_oob() */ +static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret; + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + host->use_ecc = true; + set_address(host, 0, page); + update_rw_regs(host, ecc->steps, true); + + ret = read_page_ecc(host, NULL, chip->oob_poi); + if (ret) + dev_err(nandc->dev, "failure to read oob\n"); + + return ret; +} + +/* implements ecc->write_page() */ +static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + u8 *data_buf, *oob_buf; + int i, ret; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + data_buf = (u8 *)buf; + oob_buf = chip->oob_poi; + + host->use_ecc = true; + update_rw_regs(host, ecc->steps, false); + config_nand_page_write(nandc); + + for (i = 0; i < ecc->steps; i++) { + int data_size, oob_size; + + if (i == (ecc->steps - 1)) { + data_size = ecc->size - ((ecc->steps - 1) << 2); + oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + + host->spare_bytes; + } else { + data_size = host->cw_data; + oob_size = ecc->bytes; + } + + + write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, + i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); + + /* + * when ECC is enabled, we don't really need to write anything + * to oob for the first n - 1 codewords since these oob regions + * just contain ECC bytes that's written by the controller + * itself. For the last codeword, we skip the bbm positions and + * write to the free oob area. + */ + if (i == (ecc->steps - 1)) { + oob_buf += host->bbm_size; + + write_data_dma(nandc, FLASH_BUF_ACC + data_size, + oob_buf, oob_size, 0); + } + + config_nand_cw_write(nandc); + + data_buf += data_size; + oob_buf += oob_size; + } + + ret = submit_descs(nandc); + if (ret) + dev_err(nandc->dev, "failure to write page\n"); + + free_descs(nandc); + + if (!ret) + ret = nand_prog_page_end_op(chip); + + return ret; +} + +/* implements ecc->write_page_raw() */ +static int qcom_nandc_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required, int page) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + u8 *data_buf, *oob_buf; + int i, ret; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + data_buf = (u8 *)buf; + oob_buf = chip->oob_poi; + + host->use_ecc = false; + update_rw_regs(host, ecc->steps, false); + config_nand_page_write(nandc); + + for (i = 0; i < ecc->steps; i++) { + int data_size1, data_size2, oob_size1, oob_size2; + int reg_off = FLASH_BUF_ACC; + + data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); + oob_size1 = host->bbm_size; + + if (i == (ecc->steps - 1)) { + data_size2 = ecc->size - data_size1 - + ((ecc->steps - 1) << 2); + oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + + host->spare_bytes; + } else { + data_size2 = host->cw_data - data_size1; + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + + write_data_dma(nandc, reg_off, data_buf, data_size1, + NAND_BAM_NO_EOT); + reg_off += data_size1; + data_buf += data_size1; + + write_data_dma(nandc, reg_off, oob_buf, oob_size1, + NAND_BAM_NO_EOT); + reg_off += oob_size1; + oob_buf += oob_size1; + + write_data_dma(nandc, reg_off, data_buf, data_size2, + NAND_BAM_NO_EOT); + reg_off += data_size2; + data_buf += data_size2; + + write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); + oob_buf += oob_size2; + + config_nand_cw_write(nandc); + } + + ret = submit_descs(nandc); + if (ret) + dev_err(nandc->dev, "failure to write raw page\n"); + + free_descs(nandc); + + if (!ret) + ret = nand_prog_page_end_op(chip); + + return ret; +} + +/* + * implements ecc->write_oob() + * + * the NAND controller cannot write only data or only oob within a codeword, + * since ecc is calculated for the combined codeword. we first copy the + * entire contents for the last codeword(data + oob), replace the old oob + * with the new one in chip->oob_poi, and then write the entire codeword. + * this read-copy-write operation results in a slight performance loss. + */ +static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + u8 *oob = chip->oob_poi; + int data_size, oob_size; + int ret; + + host->use_ecc = true; + + clear_bam_transaction(nandc); + ret = copy_last_cw(host, page); + if (ret) + return ret; + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + /* calculate the data and oob size for the last codeword/step */ + data_size = ecc->size - ((ecc->steps - 1) << 2); + oob_size = mtd->oobavail; + + /* override new oob content to last codeword */ + mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, + 0, mtd->oobavail); + + set_address(host, host->cw_size * (ecc->steps - 1), page); + update_rw_regs(host, 1, false); + + config_nand_page_write(nandc); + write_data_dma(nandc, FLASH_BUF_ACC, + nandc->data_buffer, data_size + oob_size, 0); + config_nand_cw_write(nandc); + + ret = submit_descs(nandc); + + free_descs(nandc); + + if (ret) { + dev_err(nandc->dev, "failure to write oob\n"); + return -EIO; + } + + return nand_prog_page_end_op(chip); +} + +static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int page, ret, bbpos, bad = 0; + u32 flash_status; + + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + + /* + * configure registers for a raw sub page read, the address is set to + * the beginning of the last codeword, we don't care about reading ecc + * portion of oob. we just want the first few bytes from this codeword + * that contains the BBM + */ + host->use_ecc = false; + + clear_bam_transaction(nandc); + ret = copy_last_cw(host, page); + if (ret) + goto err; + + flash_status = le32_to_cpu(nandc->reg_read_buf[0]); + + if (flash_status & (FS_OP_ERR | FS_MPU_ERR)) { + dev_warn(nandc->dev, "error when trying to read BBM\n"); + goto err; + } + + bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); + + bad = nandc->data_buffer[bbpos] != 0xff; + + if (chip->options & NAND_BUSWIDTH_16) + bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); +err: + return bad; +} + +static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int page, ret; + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + /* + * to mark the BBM as bad, we flash the entire last codeword with 0s. + * we don't care about the rest of the content in the codeword since + * we aren't going to use this block again + */ + memset(nandc->data_buffer, 0x00, host->cw_size); + + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + + /* prepare write */ + host->use_ecc = false; + set_address(host, host->cw_size * (ecc->steps - 1), page); + update_rw_regs(host, 1, false); + + config_nand_page_write(nandc); + write_data_dma(nandc, FLASH_BUF_ACC, + nandc->data_buffer, host->cw_size, 0); + config_nand_cw_write(nandc); + + ret = submit_descs(nandc); + + free_descs(nandc); + + if (ret) { + dev_err(nandc->dev, "failure to update BBM\n"); + return -EIO; + } + + return nand_prog_page_end_op(chip); +} + +/* + * the three functions below implement chip->read_byte(), chip->read_buf() + * and chip->write_buf() respectively. these aren't used for + * reading/writing page data, they are used for smaller data like reading + * id, status etc + */ +static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + u8 *buf = nandc->data_buffer; + u8 ret = 0x0; + + if (host->last_command == NAND_CMD_STATUS) { + ret = host->status; + + host->status = NAND_STATUS_READY | NAND_STATUS_WP; + + return ret; + } + + if (nandc->buf_start < nandc->buf_count) + ret = buf[nandc->buf_start++]; + + return ret; +} + +static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); + + memcpy(buf, nandc->data_buffer + nandc->buf_start, real_len); + nandc->buf_start += real_len; +} + +static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); + + memcpy(nandc->data_buffer + nandc->buf_start, buf, real_len); + + nandc->buf_start += real_len; +} + +/* we support only one external chip for now */ +static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + if (chipnr <= 0) + return; + + dev_warn(nandc->dev, "invalid chip select\n"); +} + +/* + * NAND controller page layout info + * + * Layout with ECC enabled: + * + * |----------------------| |---------------------------------| + * | xx.......yy| | *********xx.......yy| + * | DATA xx..ECC..yy| | DATA **SPARE**xx..ECC..yy| + * | (516) xx.......yy| | (516-n*4) **(n*4)**xx.......yy| + * | xx.......yy| | *********xx.......yy| + * |----------------------| |---------------------------------| + * codeword 1,2..n-1 codeword n + * <---(528/532 Bytes)--> <-------(528/532 Bytes)---------> + * + * n = Number of codewords in the page + * . = ECC bytes + * * = Spare/free bytes + * x = Unused byte(s) + * y = Reserved byte(s) + * + * 2K page: n = 4, spare = 16 bytes + * 4K page: n = 8, spare = 32 bytes + * 8K page: n = 16, spare = 64 bytes + * + * the qcom nand controller operates at a sub page/codeword level. each + * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively. + * the number of ECC bytes vary based on the ECC strength and the bus width. + * + * the first n - 1 codewords contains 516 bytes of user data, the remaining + * 12/16 bytes consist of ECC and reserved data. The nth codeword contains + * both user data and spare(oobavail) bytes that sum up to 516 bytes. + * + * When we access a page with ECC enabled, the reserved bytes(s) are not + * accessible at all. When reading, we fill up these unreadable positions + * with 0xffs. When writing, the controller skips writing the inaccessible + * bytes. + * + * Layout with ECC disabled: + * + * |------------------------------| |---------------------------------------| + * | yy xx.......| | bb *********xx.......| + * | DATA1 yy DATA2 xx..ECC..| | DATA1 bb DATA2 **SPARE**xx..ECC..| + * | (size1) yy (size2) xx.......| | (size1) bb (size2) **(n*4)**xx.......| + * | yy xx.......| | bb *********xx.......| + * |------------------------------| |---------------------------------------| + * codeword 1,2..n-1 codeword n + * <-------(528/532 Bytes)------> <-----------(528/532 Bytes)-----------> + * + * n = Number of codewords in the page + * . = ECC bytes + * * = Spare/free bytes + * x = Unused byte(s) + * y = Dummy Bad Bock byte(s) + * b = Real Bad Block byte(s) + * size1/size2 = function of codeword size and 'n' + * + * when the ECC block is disabled, one reserved byte (or two for 16 bit bus + * width) is now accessible. For the first n - 1 codewords, these are dummy Bad + * Block Markers. In the last codeword, this position contains the real BBM + * + * In order to have a consistent layout between RAW and ECC modes, we assume + * the following OOB layout arrangement: + * + * |-----------| |--------------------| + * |yyxx.......| |bb*********xx.......| + * |yyxx..ECC..| |bb*FREEOOB*xx..ECC..| + * |yyxx.......| |bb*********xx.......| + * |yyxx.......| |bb*********xx.......| + * |-----------| |--------------------| + * first n - 1 nth OOB region + * OOB regions + * + * n = Number of codewords in the page + * . = ECC bytes + * * = FREE OOB bytes + * y = Dummy bad block byte(s) (inaccessible when ECC enabled) + * x = Unused byte(s) + * b = Real bad block byte(s) (inaccessible when ECC enabled) + * + * This layout is read as is when ECC is disabled. When ECC is enabled, the + * inaccessible Bad Block byte(s) are ignored when we write to a page/oob, + * and assumed as 0xffs when we read a page/oob. The ECC, unused and + * dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is + * the sum of the three). + */ +static int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section > 1) + return -ERANGE; + + if (!section) { + oobregion->length = (ecc->bytes * (ecc->steps - 1)) + + host->bbm_size; + oobregion->offset = 0; + } else { + oobregion->length = host->ecc_bytes_hw + host->spare_bytes; + oobregion->offset = mtd->oobsize - oobregion->length; + } + + return 0; +} + +static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + oobregion->length = ecc->steps * 4; + oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size; + + return 0; +} + +static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = { + .ecc = qcom_nand_ooblayout_ecc, + .free = qcom_nand_ooblayout_free, +}; + +static int qcom_nand_host_setup(struct qcom_nand_host *host) +{ + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int cwperpage, bad_block_byte; + bool wide_bus; + int ecc_mode = 1; + + /* + * the controller requires each step consists of 512 bytes of data. + * bail out if DT has populated a wrong step size. + */ + if (ecc->size != NANDC_STEP_SIZE) { + dev_err(nandc->dev, "invalid ecc size\n"); + return -EINVAL; + } + + wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; + + if (ecc->strength >= 8) { + /* 8 bit ECC defaults to BCH ECC on all platforms */ + host->bch_enabled = true; + ecc_mode = 1; + + if (wide_bus) { + host->ecc_bytes_hw = 14; + host->spare_bytes = 0; + host->bbm_size = 2; + } else { + host->ecc_bytes_hw = 13; + host->spare_bytes = 2; + host->bbm_size = 1; + } + } else { + /* + * if the controller supports BCH for 4 bit ECC, the controller + * uses lesser bytes for ECC. If RS is used, the ECC bytes is + * always 10 bytes + */ + if (nandc->props->ecc_modes & ECC_BCH_4BIT) { + /* BCH */ + host->bch_enabled = true; + ecc_mode = 0; + + if (wide_bus) { + host->ecc_bytes_hw = 8; + host->spare_bytes = 2; + host->bbm_size = 2; + } else { + host->ecc_bytes_hw = 7; + host->spare_bytes = 4; + host->bbm_size = 1; + } + } else { + /* RS */ + host->ecc_bytes_hw = 10; + + if (wide_bus) { + host->spare_bytes = 0; + host->bbm_size = 2; + } else { + host->spare_bytes = 1; + host->bbm_size = 1; + } + } + } + + /* + * we consider ecc->bytes as the sum of all the non-data content in a + * step. It gives us a clean representation of the oob area (even if + * all the bytes aren't used for ECC).It is always 16 bytes for 8 bit + * ECC and 12 bytes for 4 bit ECC + */ + ecc->bytes = host->ecc_bytes_hw + host->spare_bytes + host->bbm_size; + + ecc->read_page = qcom_nandc_read_page; + ecc->read_page_raw = qcom_nandc_read_page_raw; + ecc->read_oob = qcom_nandc_read_oob; + ecc->write_page = qcom_nandc_write_page; + ecc->write_page_raw = qcom_nandc_write_page_raw; + ecc->write_oob = qcom_nandc_write_oob; + + ecc->mode = NAND_ECC_HW; + + mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); + + cwperpage = mtd->writesize / ecc->size; + nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, + cwperpage); + + /* + * DATA_UD_BYTES varies based on whether the read/write command protects + * spare data with ECC too. We protect spare data by default, so we set + * it to main + spare data, which are 512 and 4 bytes respectively. + */ + host->cw_data = 516; + + /* + * total bytes in a step, either 528 bytes for 4 bit ECC, or 532 bytes + * for 8 bit ECC + */ + host->cw_size = host->cw_data + ecc->bytes; + + if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) { + dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n"); + return -EINVAL; + } + + bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; + + host->cfg0 = (cwperpage - 1) << CW_PER_PAGE + | host->cw_data << UD_SIZE_BYTES + | 0 << DISABLE_STATUS_AFTER_WRITE + | 5 << NUM_ADDR_CYCLES + | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS + | 0 << STATUS_BFR_READ + | 1 << SET_RD_MODE_AFTER_STATUS + | host->spare_bytes << SPARE_SIZE_BYTES; + + host->cfg1 = 7 << NAND_RECOVERY_CYCLES + | 0 << CS_ACTIVE_BSY + | bad_block_byte << BAD_BLOCK_BYTE_NUM + | 0 << BAD_BLOCK_IN_SPARE_AREA + | 2 << WR_RD_BSY_GAP + | wide_bus << WIDE_FLASH + | host->bch_enabled << ENABLE_BCH_ECC; + + host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE + | host->cw_size << UD_SIZE_BYTES + | 5 << NUM_ADDR_CYCLES + | 0 << SPARE_SIZE_BYTES; + + host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES + | 0 << CS_ACTIVE_BSY + | 17 << BAD_BLOCK_BYTE_NUM + | 1 << BAD_BLOCK_IN_SPARE_AREA + | 2 << WR_RD_BSY_GAP + | wide_bus << WIDE_FLASH + | 1 << DEV0_CFG1_ECC_DISABLE; + + host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE + | 0 << ECC_SW_RESET + | host->cw_data << ECC_NUM_DATA_BYTES + | 1 << ECC_FORCE_CLK_OPEN + | ecc_mode << ECC_MODE + | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH; + + host->ecc_buf_cfg = 0x203 << NUM_STEPS; + + host->clrflashstatus = FS_READY_BSY_N; + host->clrreadstatus = 0xc0; + nandc->regs->erased_cw_detect_cfg_clr = + cpu_to_le32(CLR_ERASED_PAGE_DET); + nandc->regs->erased_cw_detect_cfg_set = + cpu_to_le32(SET_ERASED_PAGE_DET); + + dev_dbg(nandc->dev, + "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", + host->cfg0, host->cfg1, host->ecc_buf_cfg, host->ecc_bch_cfg, + host->cw_size, host->cw_data, ecc->strength, ecc->bytes, + cwperpage); + + return 0; +} + +static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) +{ + int ret; + + ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(nandc->dev, "failed to set DMA mask\n"); + return ret; + } + + /* + * we use the internal buffer for reading ONFI params, reading small + * data like ID and status, and preforming read-copy-write operations + * when writing to a codeword partially. 532 is the maximum possible + * size of a codeword for our nand controller + */ + nandc->buf_size = 532; + + nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, + GFP_KERNEL); + if (!nandc->data_buffer) + return -ENOMEM; + + nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), + GFP_KERNEL); + if (!nandc->regs) + return -ENOMEM; + + nandc->reg_read_buf = devm_kzalloc(nandc->dev, + MAX_REG_RD * sizeof(*nandc->reg_read_buf), + GFP_KERNEL); + if (!nandc->reg_read_buf) + return -ENOMEM; + + if (nandc->props->is_bam) { + nandc->reg_read_dma = + dma_map_single(nandc->dev, nandc->reg_read_buf, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); + if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { + dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); + return -EIO; + } + + nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx"); + if (!nandc->tx_chan) { + dev_err(nandc->dev, "failed to request tx channel\n"); + return -ENODEV; + } + + nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx"); + if (!nandc->rx_chan) { + dev_err(nandc->dev, "failed to request rx channel\n"); + return -ENODEV; + } + + nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd"); + if (!nandc->cmd_chan) { + dev_err(nandc->dev, "failed to request cmd channel\n"); + return -ENODEV; + } + + /* + * Initially allocate BAM transaction to read ONFI param page. + * After detecting all the devices, this BAM transaction will + * be freed and the next BAM tranasction will be allocated with + * maximum codeword size + */ + nandc->max_cwperpage = 1; + nandc->bam_txn = alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, + "failed to allocate bam transaction\n"); + return -ENOMEM; + } + } else { + nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); + if (!nandc->chan) { + dev_err(nandc->dev, + "failed to request slave channel\n"); + return -ENODEV; + } + } + + INIT_LIST_HEAD(&nandc->desc_list); + INIT_LIST_HEAD(&nandc->host_list); + + nand_hw_control_init(&nandc->controller); + + return 0; +} + +static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) +{ + if (nandc->props->is_bam) { + if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) + dma_unmap_single(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); + + if (nandc->tx_chan) + dma_release_channel(nandc->tx_chan); + + if (nandc->rx_chan) + dma_release_channel(nandc->rx_chan); + + if (nandc->cmd_chan) + dma_release_channel(nandc->cmd_chan); + } else { + if (nandc->chan) + dma_release_channel(nandc->chan); + } +} + +/* one time setup of a few nand controller registers */ +static int qcom_nandc_setup(struct qcom_nand_controller *nandc) +{ + u32 nand_ctrl; + + /* kill onenand */ + nandc_write(nandc, SFLASHC_BURST_CFG, 0); + nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), + NAND_DEV_CMD_VLD_VAL); + + /* enable ADM or BAM DMA */ + if (nandc->props->is_bam) { + nand_ctrl = nandc_read(nandc, NAND_CTRL); + nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); + } else { + nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); + } + + /* save the original values of these registers */ + nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1)); + nandc->vld = NAND_DEV_CMD_VLD_VAL; + + return 0; +} + +static int qcom_nand_host_init(struct qcom_nand_controller *nandc, + struct qcom_nand_host *host, + struct device_node *dn) +{ + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct device *dev = nandc->dev; + int ret; + + ret = of_property_read_u32(dn, "reg", &host->cs); + if (ret) { + dev_err(dev, "can't get chip-select\n"); + return -ENXIO; + } + + nand_set_flash_node(chip, dn); + mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs); + if (!mtd->name) + return -ENOMEM; + + mtd->owner = THIS_MODULE; + mtd->dev.parent = dev; + + chip->cmdfunc = qcom_nandc_command; + chip->select_chip = qcom_nandc_select_chip; + chip->read_byte = qcom_nandc_read_byte; + chip->read_buf = qcom_nandc_read_buf; + chip->write_buf = qcom_nandc_write_buf; + chip->onfi_set_features = nand_onfi_get_set_features_notsupp; + chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + + /* + * the bad block marker is readable only when we read the last codeword + * of a page with ECC disabled. currently, the nand_base and nand_bbt + * helpers don't allow us to read BB from a nand chip with ECC + * disabled (MTD_OPS_PLACE_OOB is set by default). use the block_bad + * and block_markbad helpers until we permanently switch to using + * MTD_OPS_RAW for all drivers (with the help of badblockbits) + */ + chip->block_bad = qcom_nandc_block_bad; + chip->block_markbad = qcom_nandc_block_markbad; + + chip->controller = &nandc->controller; + chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER | + NAND_SKIP_BBTSCAN; + + /* set up initial status value */ + host->status = NAND_STATUS_READY | NAND_STATUS_WP; + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + ret = qcom_nand_host_setup(host); + + return ret; +} + +static int qcom_nand_mtd_register(struct qcom_nand_controller *nandc, + struct qcom_nand_host *host, + struct device_node *dn) +{ + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + nand_cleanup(mtd_to_nand(mtd)); + + return ret; +} + +static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc) +{ + struct device *dev = nandc->dev; + struct device_node *dn = dev->of_node, *child; + struct qcom_nand_host *host, *tmp; + int ret; + + for_each_available_child_of_node(dn, child) { + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) { + of_node_put(child); + return -ENOMEM; + } + + ret = qcom_nand_host_init(nandc, host, child); + if (ret) { + devm_kfree(dev, host); + continue; + } + + list_add_tail(&host->node, &nandc->host_list); + } + + if (list_empty(&nandc->host_list)) + return -ENODEV; + + if (nandc->props->is_bam) { + free_bam_transaction(nandc); + nandc->bam_txn = alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, + "failed to allocate bam transaction\n"); + return -ENOMEM; + } + } + + list_for_each_entry_safe(host, tmp, &nandc->host_list, node) { + ret = qcom_nand_mtd_register(nandc, host, child); + if (ret) { + list_del(&host->node); + devm_kfree(dev, host); + } + } + + if (list_empty(&nandc->host_list)) + return -ENODEV; + + return 0; +} + +/* parse custom DT properties here */ +static int qcom_nandc_parse_dt(struct platform_device *pdev) +{ + struct qcom_nand_controller *nandc = platform_get_drvdata(pdev); + struct device_node *np = nandc->dev->of_node; + int ret; + + if (!nandc->props->is_bam) { + ret = of_property_read_u32(np, "qcom,cmd-crci", + &nandc->cmd_crci); + if (ret) { + dev_err(nandc->dev, "command CRCI unspecified\n"); + return ret; + } + + ret = of_property_read_u32(np, "qcom,data-crci", + &nandc->data_crci); + if (ret) { + dev_err(nandc->dev, "data CRCI unspecified\n"); + return ret; + } + } + + return 0; +} + +static int qcom_nandc_probe(struct platform_device *pdev) +{ + struct qcom_nand_controller *nandc; + const void *dev_data; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL); + if (!nandc) + return -ENOMEM; + + platform_set_drvdata(pdev, nandc); + nandc->dev = dev; + + dev_data = of_device_get_match_data(dev); + if (!dev_data) { + dev_err(&pdev->dev, "failed to get device data\n"); + return -ENODEV; + } + + nandc->props = dev_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nandc->base = devm_ioremap_resource(dev, res); + if (IS_ERR(nandc->base)) + return PTR_ERR(nandc->base); + + nandc->base_phys = res->start; + nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start); + + nandc->core_clk = devm_clk_get(dev, "core"); + if (IS_ERR(nandc->core_clk)) + return PTR_ERR(nandc->core_clk); + + nandc->aon_clk = devm_clk_get(dev, "aon"); + if (IS_ERR(nandc->aon_clk)) + return PTR_ERR(nandc->aon_clk); + + ret = qcom_nandc_parse_dt(pdev); + if (ret) + return ret; + + ret = qcom_nandc_alloc(nandc); + if (ret) + goto err_core_clk; + + ret = clk_prepare_enable(nandc->core_clk); + if (ret) + goto err_core_clk; + + ret = clk_prepare_enable(nandc->aon_clk); + if (ret) + goto err_aon_clk; + + ret = qcom_nandc_setup(nandc); + if (ret) + goto err_setup; + + ret = qcom_probe_nand_devices(nandc); + if (ret) + goto err_setup; + + return 0; + +err_setup: + clk_disable_unprepare(nandc->aon_clk); +err_aon_clk: + clk_disable_unprepare(nandc->core_clk); +err_core_clk: + qcom_nandc_unalloc(nandc); + + return ret; +} + +static int qcom_nandc_remove(struct platform_device *pdev) +{ + struct qcom_nand_controller *nandc = platform_get_drvdata(pdev); + struct qcom_nand_host *host; + + list_for_each_entry(host, &nandc->host_list, node) + nand_release(nand_to_mtd(&host->chip)); + + qcom_nandc_unalloc(nandc); + + clk_disable_unprepare(nandc->aon_clk); + clk_disable_unprepare(nandc->core_clk); + + return 0; +} + +static const struct qcom_nandc_props ipq806x_nandc_props = { + .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), + .is_bam = false, + .dev_cmd_reg_start = 0x0, +}; + +static const struct qcom_nandc_props ipq4019_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), + .is_bam = true, + .dev_cmd_reg_start = 0x0, +}; + +static const struct qcom_nandc_props ipq8074_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), + .is_bam = true, + .dev_cmd_reg_start = 0x7000, +}; + +/* + * data will hold a struct pointer containing more differences once we support + * more controller variants + */ +static const struct of_device_id qcom_nandc_of_match[] = { + { + .compatible = "qcom,ipq806x-nand", + .data = &ipq806x_nandc_props, + }, + { + .compatible = "qcom,ipq4019-nand", + .data = &ipq4019_nandc_props, + }, + { + .compatible = "qcom,ipq8074-nand", + .data = &ipq8074_nandc_props, + }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_nandc_of_match); + +static struct platform_driver qcom_nandc_driver = { + .driver = { + .name = "qcom-nandc", + .of_match_table = qcom_nandc_of_match, + }, + .probe = qcom_nandc_probe, + .remove = qcom_nandc_remove, +}; +module_platform_driver(qcom_nandc_driver); + +MODULE_AUTHOR("Archit Taneja "); +MODULE_DESCRIPTION("Qualcomm NAND Controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c new file mode 100644 index 000000000000..595635b9e9de --- /dev/null +++ b/drivers/mtd/nand/raw/r852.c @@ -0,0 +1,1079 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * driver for Ricoh xD readers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sm_common.h" +#include "r852.h" + + +static bool r852_enable_dma = 1; +module_param(r852_enable_dma, bool, S_IRUGO); +MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)"); + +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +/* read register */ +static inline uint8_t r852_read_reg(struct r852_device *dev, int address) +{ + uint8_t reg = readb(dev->mmio + address); + return reg; +} + +/* write register */ +static inline void r852_write_reg(struct r852_device *dev, + int address, uint8_t value) +{ + writeb(value, dev->mmio + address); + mmiowb(); +} + + +/* read dword sized register */ +static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address) +{ + uint32_t reg = le32_to_cpu(readl(dev->mmio + address)); + return reg; +} + +/* write dword sized register */ +static inline void r852_write_reg_dword(struct r852_device *dev, + int address, uint32_t value) +{ + writel(cpu_to_le32(value), dev->mmio + address); + mmiowb(); +} + +/* returns pointer to our private structure */ +static inline struct r852_device *r852_get_dev(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + return nand_get_controller_data(chip); +} + + +/* check if controller supports dma */ +static void r852_dma_test(struct r852_device *dev) +{ + dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) & + (R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2); + + if (!dev->dma_usable) + message("Non dma capable device detected, dma disabled"); + + if (!r852_enable_dma) { + message("disabling dma on user request"); + dev->dma_usable = 0; + } +} + +/* + * Enable dma. Enables ether first or second stage of the DMA, + * Expects dev->dma_dir and dev->dma_state be set + */ +static void r852_dma_enable(struct r852_device *dev) +{ + uint8_t dma_reg, dma_irq_reg; + + /* Set up dma settings */ + dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS); + dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY); + + if (dev->dma_dir) + dma_reg |= R852_DMA_READ; + + if (dev->dma_state == DMA_INTERNAL) { + dma_reg |= R852_DMA_INTERNAL; + /* Precaution to make sure HW doesn't write */ + /* to random kernel memory */ + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_bounce_buffer)); + } else { + dma_reg |= R852_DMA_MEMORY; + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_dma_addr)); + } + + /* Precaution: make sure write reached the device */ + r852_read_reg_dword(dev, R852_DMA_ADDR); + + r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg); + + /* Set dma irq */ + dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, + dma_irq_reg | + R852_DMA_IRQ_INTERNAL | + R852_DMA_IRQ_ERROR | + R852_DMA_IRQ_MEMORY); +} + +/* + * Disable dma, called from the interrupt handler, which specifies + * success of the operation via 'error' argument + */ +static void r852_dma_done(struct r852_device *dev, int error) +{ + WARN_ON(dev->dma_stage == 0); + + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, + r852_read_reg_dword(dev, R852_DMA_IRQ_STA)); + + r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0); + + /* Precaution to make sure HW doesn't write to random kernel memory */ + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_bounce_buffer)); + r852_read_reg_dword(dev, R852_DMA_ADDR); + + dev->dma_error = error; + dev->dma_stage = 0; + + if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer) + pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN, + dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); +} + +/* + * Wait, till dma is done, which includes both phases of it + */ +static int r852_dma_wait(struct r852_device *dev) +{ + long timeout = wait_for_completion_timeout(&dev->dma_done, + msecs_to_jiffies(1000)); + if (!timeout) { + dbg("timeout waiting for DMA interrupt"); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * Read/Write one page using dma. Only pages can be read (512 bytes) +*/ +static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) +{ + int bounce = 0; + unsigned long flags; + int error; + + dev->dma_error = 0; + + /* Set dma direction */ + dev->dma_dir = do_read; + dev->dma_stage = 1; + reinit_completion(&dev->dma_done); + + dbg_verbose("doing dma %s ", do_read ? "read" : "write"); + + /* Set initial dma state: for reading first fill on board buffer, + from device, for writes first fill the buffer from memory*/ + dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY; + + /* if incoming buffer is not page aligned, we should do bounce */ + if ((unsigned long)buf & (R852_DMA_LEN-1)) + bounce = 1; + + if (!bounce) { + dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf, + R852_DMA_LEN, + (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE)); + + if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr)) + bounce = 1; + } + + if (bounce) { + dbg_verbose("dma: using bounce buffer"); + dev->phys_dma_addr = dev->phys_bounce_buffer; + if (!do_read) + memcpy(dev->bounce_buffer, buf, R852_DMA_LEN); + } + + /* Enable DMA */ + spin_lock_irqsave(&dev->irqlock, flags); + r852_dma_enable(dev); + spin_unlock_irqrestore(&dev->irqlock, flags); + + /* Wait till complete */ + error = r852_dma_wait(dev); + + if (error) { + r852_dma_done(dev, error); + return; + } + + if (do_read && bounce) + memcpy((void *)buf, dev->bounce_buffer, R852_DMA_LEN); +} + +/* + * Program data lines of the nand chip to send data to it + */ +static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + uint32_t reg; + + /* Don't allow any access to hardware if we suspect card removal */ + if (dev->card_unstable) + return; + + /* Special case for whole sector read */ + if (len == R852_DMA_LEN && dev->dma_usable) { + r852_do_dma(dev, (uint8_t *)buf, 0); + return; + } + + /* write DWORD chinks - faster */ + while (len >= 4) { + reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; + r852_write_reg_dword(dev, R852_DATALINE, reg); + buf += 4; + len -= 4; + + } + + /* write rest */ + while (len > 0) { + r852_write_reg(dev, R852_DATALINE, *buf++); + len--; + } +} + +/* + * Read data lines of the nand chip to retrieve data + */ +static void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + uint32_t reg; + + if (dev->card_unstable) { + /* since we can't signal error here, at least, return + predictable buffer */ + memset(buf, 0, len); + return; + } + + /* special case for whole sector read */ + if (len == R852_DMA_LEN && dev->dma_usable) { + r852_do_dma(dev, buf, 1); + return; + } + + /* read in dword sized chunks */ + while (len >= 4) { + + reg = r852_read_reg_dword(dev, R852_DATALINE); + *buf++ = reg & 0xFF; + *buf++ = (reg >> 8) & 0xFF; + *buf++ = (reg >> 16) & 0xFF; + *buf++ = (reg >> 24) & 0xFF; + len -= 4; + } + + /* read the reset by bytes */ + while (len--) + *buf++ = r852_read_reg(dev, R852_DATALINE); +} + +/* + * Read one byte from nand chip + */ +static uint8_t r852_read_byte(struct mtd_info *mtd) +{ + struct r852_device *dev = r852_get_dev(mtd); + + /* Same problem as in r852_read_buf.... */ + if (dev->card_unstable) + return 0; + + return r852_read_reg(dev, R852_DATALINE); +} + +/* + * Control several chip lines & send commands + */ +static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return; + + if (ctrl & NAND_CTRL_CHANGE) { + + dev->ctlreg &= ~(R852_CTL_DATA | R852_CTL_COMMAND | + R852_CTL_ON | R852_CTL_CARDENABLE); + + if (ctrl & NAND_ALE) + dev->ctlreg |= R852_CTL_DATA; + + if (ctrl & NAND_CLE) + dev->ctlreg |= R852_CTL_COMMAND; + + if (ctrl & NAND_NCE) + dev->ctlreg |= (R852_CTL_CARDENABLE | R852_CTL_ON); + else + dev->ctlreg &= ~R852_CTL_WRITE; + + /* when write is stareted, enable write access */ + if (dat == NAND_CMD_ERASE1) + dev->ctlreg |= R852_CTL_WRITE; + + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } + + /* HACK: NAND_CMD_SEQIN is called without NAND_CTRL_CHANGE, but we need + to set write mode */ + if (dat == NAND_CMD_SEQIN && (dev->ctlreg & R852_CTL_COMMAND)) { + dev->ctlreg |= R852_CTL_WRITE; + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } + + if (dat != NAND_CMD_NONE) + r852_write_reg(dev, R852_DATALINE, dat); +} + +/* + * Wait till card is ready. + * based on nand_wait, but returns errors on DMA error + */ +static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct r852_device *dev = nand_get_controller_data(chip); + + unsigned long timeout; + u8 status; + + timeout = jiffies + (chip->state == FL_ERASING ? + msecs_to_jiffies(400) : msecs_to_jiffies(20)); + + while (time_before(jiffies, timeout)) + if (chip->dev_ready(mtd)) + break; + + nand_status_op(chip, &status); + + /* Unfortunelly, no way to send detailed error status... */ + if (dev->dma_error) { + status |= NAND_STATUS_FAIL; + dev->dma_error = 0; + } + return status; +} + +/* + * Check if card is ready + */ + +static int r852_ready(struct mtd_info *mtd) +{ + struct r852_device *dev = r852_get_dev(mtd); + return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY); +} + + +/* + * Set ECC engine mode +*/ + +static void r852_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return; + + switch (mode) { + case NAND_ECC_READ: + case NAND_ECC_WRITE: + /* enable ecc generation/check*/ + dev->ctlreg |= R852_CTL_ECC_ENABLE; + + /* flush ecc buffer */ + r852_write_reg(dev, R852_CTL, + dev->ctlreg | R852_CTL_ECC_ACCESS); + + r852_read_reg_dword(dev, R852_DATALINE); + r852_write_reg(dev, R852_CTL, dev->ctlreg); + return; + + case NAND_ECC_READSYN: + /* disable ecc generation */ + dev->ctlreg &= ~R852_CTL_ECC_ENABLE; + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } +} + +/* + * Calculate ECC, only used for writes + */ + +static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct r852_device *dev = r852_get_dev(mtd); + struct sm_oob *oob = (struct sm_oob *)ecc_code; + uint32_t ecc1, ecc2; + + if (dev->card_unstable) + return 0; + + dev->ctlreg &= ~R852_CTL_ECC_ENABLE; + r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); + + ecc1 = r852_read_reg_dword(dev, R852_DATALINE); + ecc2 = r852_read_reg_dword(dev, R852_DATALINE); + + oob->ecc1[0] = (ecc1) & 0xFF; + oob->ecc1[1] = (ecc1 >> 8) & 0xFF; + oob->ecc1[2] = (ecc1 >> 16) & 0xFF; + + oob->ecc2[0] = (ecc2) & 0xFF; + oob->ecc2[1] = (ecc2 >> 8) & 0xFF; + oob->ecc2[2] = (ecc2 >> 16) & 0xFF; + + r852_write_reg(dev, R852_CTL, dev->ctlreg); + return 0; +} + +/* + * Correct the data using ECC, hw did almost everything for us + */ + +static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + uint32_t ecc_reg; + uint8_t ecc_status, err_byte; + int i, error = 0; + + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return 0; + + if (dev->dma_error) { + dev->dma_error = 0; + return -EIO; + } + + r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); + ecc_reg = r852_read_reg_dword(dev, R852_DATALINE); + r852_write_reg(dev, R852_CTL, dev->ctlreg); + + for (i = 0 ; i <= 1 ; i++) { + + ecc_status = (ecc_reg >> 8) & 0xFF; + + /* ecc uncorrectable error */ + if (ecc_status & R852_ECC_FAIL) { + dbg("ecc: unrecoverable error, in half %d", i); + error = -EBADMSG; + goto exit; + } + + /* correctable error */ + if (ecc_status & R852_ECC_CORRECTABLE) { + + err_byte = ecc_reg & 0xFF; + dbg("ecc: recoverable error, " + "in half %d, byte %d, bit %d", i, + err_byte, ecc_status & R852_ECC_ERR_BIT_MSK); + + dat[err_byte] ^= + 1 << (ecc_status & R852_ECC_ERR_BIT_MSK); + error++; + } + + dat += 256; + ecc_reg >>= 16; + } +exit: + return error; +} + +/* + * This is copy of nand_read_oob_std + * nand_read_oob_syndrome assumes we can send column address - we can't + */ +static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); +} + +/* + * Start the nand engine + */ + +static void r852_engine_enable(struct r852_device *dev) +{ + if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) { + r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); + r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); + } else { + r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); + r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); + } + msleep(300); + r852_write_reg(dev, R852_CTL, 0); +} + + +/* + * Stop the nand engine + */ + +static void r852_engine_disable(struct r852_device *dev) +{ + r852_write_reg_dword(dev, R852_HW, 0); + r852_write_reg(dev, R852_CTL, R852_CTL_RESET); +} + +/* + * Test if card is present + */ + +static void r852_card_update_present(struct r852_device *dev) +{ + unsigned long flags; + uint8_t reg; + + spin_lock_irqsave(&dev->irqlock, flags); + reg = r852_read_reg(dev, R852_CARD_STA); + dev->card_detected = !!(reg & R852_CARD_STA_PRESENT); + spin_unlock_irqrestore(&dev->irqlock, flags); +} + +/* + * Update card detection IRQ state according to current card state + * which is read in r852_card_update_present + */ +static void r852_update_card_detect(struct r852_device *dev) +{ + int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); + dev->card_unstable = 0; + + card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT); + card_detect_reg |= R852_CARD_IRQ_GENABLE; + + card_detect_reg |= dev->card_detected ? + R852_CARD_IRQ_REMOVE : R852_CARD_IRQ_INSERT; + + r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg); +} + +static ssize_t r852_media_type_show(struct device *sys_dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev); + struct r852_device *dev = r852_get_dev(mtd); + char *data = dev->sm ? "smartmedia" : "xd"; + + strcpy(buf, data); + return strlen(data); +} + +static DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); + + +/* Detect properties of card in slot */ +static void r852_update_media_status(struct r852_device *dev) +{ + uint8_t reg; + unsigned long flags; + int readonly; + + spin_lock_irqsave(&dev->irqlock, flags); + if (!dev->card_detected) { + message("card removed"); + spin_unlock_irqrestore(&dev->irqlock, flags); + return ; + } + + readonly = r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_RO; + reg = r852_read_reg(dev, R852_DMA_CAP); + dev->sm = (reg & (R852_DMA1 | R852_DMA2)) && (reg & R852_SMBIT); + + message("detected %s %s card in slot", + dev->sm ? "SmartMedia" : "xD", + readonly ? "readonly" : "writeable"); + + dev->readonly = readonly; + spin_unlock_irqrestore(&dev->irqlock, flags); +} + +/* + * Register the nand device + * Called when the card is detected + */ +static int r852_register_nand_device(struct r852_device *dev) +{ + struct mtd_info *mtd = nand_to_mtd(dev->chip); + + WARN_ON(dev->card_registred); + + mtd->dev.parent = &dev->pci_dev->dev; + + if (dev->readonly) + dev->chip->options |= NAND_ROM; + + r852_engine_enable(dev); + + if (sm_register_device(mtd, dev->sm)) + goto error1; + + if (device_create_file(&mtd->dev, &dev_attr_media_type)) { + message("can't create media type sysfs attribute"); + goto error3; + } + + dev->card_registred = 1; + return 0; +error3: + nand_release(mtd); +error1: + /* Force card redetect */ + dev->card_detected = 0; + return -1; +} + +/* + * Unregister the card + */ + +static void r852_unregister_nand_device(struct r852_device *dev) +{ + struct mtd_info *mtd = nand_to_mtd(dev->chip); + + if (!dev->card_registred) + return; + + device_remove_file(&mtd->dev, &dev_attr_media_type); + nand_release(mtd); + r852_engine_disable(dev); + dev->card_registred = 0; +} + +/* Card state updater */ +static void r852_card_detect_work(struct work_struct *work) +{ + struct r852_device *dev = + container_of(work, struct r852_device, card_detect_work.work); + + r852_card_update_present(dev); + r852_update_card_detect(dev); + dev->card_unstable = 0; + + /* False alarm */ + if (dev->card_detected == dev->card_registred) + goto exit; + + /* Read media properties */ + r852_update_media_status(dev); + + /* Register the card */ + if (dev->card_detected) + r852_register_nand_device(dev); + else + r852_unregister_nand_device(dev); +exit: + r852_update_card_detect(dev); +} + +/* Ack + disable IRQ generation */ +static void r852_disable_irqs(struct r852_device *dev) +{ + uint8_t reg; + reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); + r852_write_reg(dev, R852_CARD_IRQ_ENABLE, reg & ~R852_CARD_IRQ_MASK); + + reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, + reg & ~R852_DMA_IRQ_MASK); + + r852_write_reg(dev, R852_CARD_IRQ_STA, R852_CARD_IRQ_MASK); + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, R852_DMA_IRQ_MASK); +} + +/* Interrupt handler */ +static irqreturn_t r852_irq(int irq, void *data) +{ + struct r852_device *dev = (struct r852_device *)data; + + uint8_t card_status, dma_status; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&dev->irqlock, flags); + + /* handle card detection interrupts first */ + card_status = r852_read_reg(dev, R852_CARD_IRQ_STA); + r852_write_reg(dev, R852_CARD_IRQ_STA, card_status); + + if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) { + + ret = IRQ_HANDLED; + dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT); + + /* we shouldn't receive any interrupts if we wait for card + to settle */ + WARN_ON(dev->card_unstable); + + /* disable irqs while card is unstable */ + /* this will timeout DMA if active, but better that garbage */ + r852_disable_irqs(dev); + + if (dev->card_unstable) + goto out; + + /* let, card state to settle a bit, and then do the work */ + dev->card_unstable = 1; + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, msecs_to_jiffies(100)); + goto out; + } + + + /* Handle dma interrupts */ + dma_status = r852_read_reg_dword(dev, R852_DMA_IRQ_STA); + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, dma_status); + + if (dma_status & R852_DMA_IRQ_MASK) { + + ret = IRQ_HANDLED; + + if (dma_status & R852_DMA_IRQ_ERROR) { + dbg("received dma error IRQ"); + r852_dma_done(dev, -EIO); + complete(&dev->dma_done); + goto out; + } + + /* received DMA interrupt out of nowhere? */ + WARN_ON_ONCE(dev->dma_stage == 0); + + if (dev->dma_stage == 0) + goto out; + + /* done device access */ + if (dev->dma_state == DMA_INTERNAL && + (dma_status & R852_DMA_IRQ_INTERNAL)) { + + dev->dma_state = DMA_MEMORY; + dev->dma_stage++; + } + + /* done memory DMA */ + if (dev->dma_state == DMA_MEMORY && + (dma_status & R852_DMA_IRQ_MEMORY)) { + dev->dma_state = DMA_INTERNAL; + dev->dma_stage++; + } + + /* Enable 2nd half of dma dance */ + if (dev->dma_stage == 2) + r852_dma_enable(dev); + + /* Operation done */ + if (dev->dma_stage == 3) { + r852_dma_done(dev, 0); + complete(&dev->dma_done); + } + goto out; + } + + /* Handle unknown interrupts */ + if (dma_status) + dbg("bad dma IRQ status = %x", dma_status); + + if (card_status & ~R852_CARD_STA_CD) + dbg("strange card status = %x", card_status); + +out: + spin_unlock_irqrestore(&dev->irqlock, flags); + return ret; +} + +static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +{ + int error; + struct nand_chip *chip; + struct r852_device *dev; + + /* pci initialization */ + error = pci_enable_device(pci_dev); + + if (error) + goto error1; + + pci_set_master(pci_dev); + + error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (error) + goto error2; + + error = pci_request_regions(pci_dev, DRV_NAME); + + if (error) + goto error3; + + error = -ENOMEM; + + /* init nand chip, but register it only on card insert */ + chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); + + if (!chip) + goto error4; + + /* commands */ + chip->cmd_ctrl = r852_cmdctl; + chip->waitfunc = r852_wait; + chip->dev_ready = r852_ready; + + /* I/O */ + chip->read_byte = r852_read_byte; + chip->read_buf = r852_read_buf; + chip->write_buf = r852_write_buf; + + /* ecc */ + chip->ecc.mode = NAND_ECC_HW_SYNDROME; + chip->ecc.size = R852_DMA_LEN; + chip->ecc.bytes = SM_OOB_SIZE; + chip->ecc.strength = 2; + chip->ecc.hwctl = r852_ecc_hwctl; + chip->ecc.calculate = r852_ecc_calculate; + chip->ecc.correct = r852_ecc_correct; + + /* TODO: hack */ + chip->ecc.read_oob = r852_read_oob; + + /* init our device structure */ + dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL); + + if (!dev) + goto error5; + + nand_set_controller_data(chip, dev); + dev->chip = chip; + dev->pci_dev = pci_dev; + pci_set_drvdata(pci_dev, dev); + + dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN, + &dev->phys_bounce_buffer); + + if (!dev->bounce_buffer) + goto error6; + + + error = -ENODEV; + dev->mmio = pci_ioremap_bar(pci_dev, 0); + + if (!dev->mmio) + goto error7; + + error = -ENOMEM; + dev->tmp_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); + + if (!dev->tmp_buffer) + goto error8; + + init_completion(&dev->dma_done); + + dev->card_workqueue = create_freezable_workqueue(DRV_NAME); + + if (!dev->card_workqueue) + goto error9; + + INIT_DELAYED_WORK(&dev->card_detect_work, r852_card_detect_work); + + /* shutdown everything - precation */ + r852_engine_disable(dev); + r852_disable_irqs(dev); + + r852_dma_test(dev); + + dev->irq = pci_dev->irq; + spin_lock_init(&dev->irqlock); + + dev->card_detected = 0; + r852_card_update_present(dev); + + /*register irq handler*/ + error = -ENODEV; + if (request_irq(pci_dev->irq, &r852_irq, IRQF_SHARED, + DRV_NAME, dev)) + goto error10; + + /* kick initial present test */ + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, 0); + + + printk(KERN_NOTICE DRV_NAME ": driver loaded successfully\n"); + return 0; + +error10: + destroy_workqueue(dev->card_workqueue); +error9: + kfree(dev->tmp_buffer); +error8: + pci_iounmap(pci_dev, dev->mmio); +error7: + pci_free_consistent(pci_dev, R852_DMA_LEN, + dev->bounce_buffer, dev->phys_bounce_buffer); +error6: + kfree(dev); +error5: + kfree(chip); +error4: + pci_release_regions(pci_dev); +error3: +error2: + pci_disable_device(pci_dev); +error1: + return error; +} + +static void r852_remove(struct pci_dev *pci_dev) +{ + struct r852_device *dev = pci_get_drvdata(pci_dev); + + /* Stop detect workqueue - + we are going to unregister the device anyway*/ + cancel_delayed_work_sync(&dev->card_detect_work); + destroy_workqueue(dev->card_workqueue); + + /* Unregister the device, this might make more IO */ + r852_unregister_nand_device(dev); + + /* Stop interrupts */ + r852_disable_irqs(dev); + free_irq(dev->irq, dev); + + /* Cleanup */ + kfree(dev->tmp_buffer); + pci_iounmap(pci_dev, dev->mmio); + pci_free_consistent(pci_dev, R852_DMA_LEN, + dev->bounce_buffer, dev->phys_bounce_buffer); + + kfree(dev->chip); + kfree(dev); + + /* Shutdown the PCI device */ + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); +} + +static void r852_shutdown(struct pci_dev *pci_dev) +{ + struct r852_device *dev = pci_get_drvdata(pci_dev); + + cancel_delayed_work_sync(&dev->card_detect_work); + r852_disable_irqs(dev); + synchronize_irq(dev->irq); + pci_disable_device(pci_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int r852_suspend(struct device *device) +{ + struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + + if (dev->ctlreg & R852_CTL_CARDENABLE) + return -EBUSY; + + /* First make sure the detect work is gone */ + cancel_delayed_work_sync(&dev->card_detect_work); + + /* Turn off the interrupts and stop the device */ + r852_disable_irqs(dev); + r852_engine_disable(dev); + + /* If card was pulled off just during the suspend, which is very + unlikely, we will remove it on resume, it too late now + anyway... */ + dev->card_unstable = 0; + return 0; +} + +static int r852_resume(struct device *device) +{ + struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + struct mtd_info *mtd = nand_to_mtd(dev->chip); + + r852_disable_irqs(dev); + r852_card_update_present(dev); + r852_engine_disable(dev); + + + /* If card status changed, just do the work */ + if (dev->card_detected != dev->card_registred) { + dbg("card was %s during low power state", + dev->card_detected ? "added" : "removed"); + + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, msecs_to_jiffies(1000)); + return 0; + } + + /* Otherwise, initialize the card */ + if (dev->card_registred) { + r852_engine_enable(dev); + dev->chip->select_chip(mtd, 0); + nand_reset_op(dev->chip); + dev->chip->select_chip(mtd, -1); + } + + /* Program card detection IRQ */ + r852_update_card_detect(dev); + return 0; +} +#endif + +static const struct pci_device_id r852_pci_id_tbl[] = { + + { PCI_VDEVICE(RICOH, 0x0852), }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl); + +static SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume); + +static struct pci_driver r852_pci_driver = { + .name = DRV_NAME, + .id_table = r852_pci_id_tbl, + .probe = r852_probe, + .remove = r852_remove, + .shutdown = r852_shutdown, + .driver.pm = &r852_pm_ops, +}; + +module_pci_driver(r852_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky "); +MODULE_DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver"); diff --git a/drivers/mtd/nand/raw/r852.h b/drivers/mtd/nand/raw/r852.h new file mode 100644 index 000000000000..8713c57f6207 --- /dev/null +++ b/drivers/mtd/nand/raw/r852.h @@ -0,0 +1,160 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * driver for Ricoh xD readers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + + +/* nand interface + ecc + byte write/read does one cycle on nand data lines. + dword write/read does 4 cycles + if R852_CTL_ECC_ACCESS is set in R852_CTL, then dword read reads + results of ecc correction, if DMA read was done before. + If write was done two dword reads read generated ecc checksums +*/ +#define R852_DATALINE 0x00 + +/* control register */ +#define R852_CTL 0x04 +#define R852_CTL_COMMAND 0x01 /* send command (#CLE)*/ +#define R852_CTL_DATA 0x02 /* read/write data (#ALE)*/ +#define R852_CTL_ON 0x04 /* only seem to controls the hd led, */ + /* but has to be set on start...*/ +#define R852_CTL_RESET 0x08 /* unknown, set only on start once*/ +#define R852_CTL_CARDENABLE 0x10 /* probably (#CE) - always set*/ +#define R852_CTL_ECC_ENABLE 0x20 /* enable ecc engine */ +#define R852_CTL_ECC_ACCESS 0x40 /* read/write ecc via reg #0*/ +#define R852_CTL_WRITE 0x80 /* set when performing writes (#WP) */ + +/* card detection status */ +#define R852_CARD_STA 0x05 + +#define R852_CARD_STA_CD 0x01 /* state of #CD line, same as 0x04 */ +#define R852_CARD_STA_RO 0x02 /* card is readonly */ +#define R852_CARD_STA_PRESENT 0x04 /* card is present (#CD) */ +#define R852_CARD_STA_ABSENT 0x08 /* card is absent */ +#define R852_CARD_STA_BUSY 0x80 /* card is busy - (#R/B) */ + +/* card detection irq status & enable*/ +#define R852_CARD_IRQ_STA 0x06 /* IRQ status */ +#define R852_CARD_IRQ_ENABLE 0x07 /* IRQ enable */ + +#define R852_CARD_IRQ_CD 0x01 /* fire when #CD lights, same as 0x04*/ +#define R852_CARD_IRQ_REMOVE 0x04 /* detect card removal */ +#define R852_CARD_IRQ_INSERT 0x08 /* detect card insert */ +#define R852_CARD_IRQ_UNK1 0x10 /* unknown */ +#define R852_CARD_IRQ_GENABLE 0x80 /* general enable */ +#define R852_CARD_IRQ_MASK 0x1D + + + +/* hardware enable */ +#define R852_HW 0x08 +#define R852_HW_ENABLED 0x01 /* hw enabled */ +#define R852_HW_UNKNOWN 0x80 + + +/* dma capabilities */ +#define R852_DMA_CAP 0x09 +#define R852_SMBIT 0x20 /* if set with bit #6 or bit #7, then */ + /* hw is smartmedia */ +#define R852_DMA1 0x40 /* if set w/bit #7, dma is supported */ +#define R852_DMA2 0x80 /* if set w/bit #6, dma is supported */ + + +/* physical DMA address - 32 bit value*/ +#define R852_DMA_ADDR 0x0C + + +/* dma settings */ +#define R852_DMA_SETTINGS 0x10 +#define R852_DMA_MEMORY 0x01 /* (memory <-> internal hw buffer) */ +#define R852_DMA_READ 0x02 /* 0 = write, 1 = read */ +#define R852_DMA_INTERNAL 0x04 /* (internal hw buffer <-> card) */ + +/* dma IRQ status */ +#define R852_DMA_IRQ_STA 0x14 + +/* dma IRQ enable */ +#define R852_DMA_IRQ_ENABLE 0x18 + +#define R852_DMA_IRQ_MEMORY 0x01 /* (memory <-> internal hw buffer) */ +#define R852_DMA_IRQ_ERROR 0x02 /* error did happen */ +#define R852_DMA_IRQ_INTERNAL 0x04 /* (internal hw buffer <-> card) */ +#define R852_DMA_IRQ_MASK 0x07 /* mask of all IRQ bits */ + + +/* ECC syndrome format - read from reg #0 will return two copies of these for + each half of the page. + first byte is error byte location, and second, bit location + flags */ +#define R852_ECC_ERR_BIT_MSK 0x07 /* error bit location */ +#define R852_ECC_CORRECT 0x10 /* no errors - (guessed) */ +#define R852_ECC_CORRECTABLE 0x20 /* correctable error exist */ +#define R852_ECC_FAIL 0x40 /* non correctable error detected */ + +#define R852_DMA_LEN 512 + +#define DMA_INTERNAL 0 +#define DMA_MEMORY 1 + +struct r852_device { + void __iomem *mmio; /* mmio */ + struct nand_chip *chip; /* nand chip backpointer */ + struct pci_dev *pci_dev; /* pci backpointer */ + + /* dma area */ + dma_addr_t phys_dma_addr; /* bus address of buffer*/ + struct completion dma_done; /* data transfer done */ + + dma_addr_t phys_bounce_buffer; /* bus address of bounce buffer */ + uint8_t *bounce_buffer; /* virtual address of bounce buffer */ + + int dma_dir; /* 1 = read, 0 = write */ + int dma_stage; /* 0 - idle, 1 - first step, + 2 - second step */ + + int dma_state; /* 0 = internal, 1 = memory */ + int dma_error; /* dma errors */ + int dma_usable; /* is it possible to use dma */ + + /* card status area */ + struct delayed_work card_detect_work; + struct workqueue_struct *card_workqueue; + int card_registred; /* card registered with mtd */ + int card_detected; /* card detected in slot */ + int card_unstable; /* whenever the card is inserted, + is not known yet */ + int readonly; /* card is readonly */ + int sm; /* Is card smartmedia */ + + /* interrupt handling */ + spinlock_t irqlock; /* IRQ protecting lock */ + int irq; /* irq num */ + /* misc */ + void *tmp_buffer; /* temporary buffer */ + uint8_t ctlreg; /* cached contents of control reg */ +}; + +#define DRV_NAME "r852" + + +#define dbg(format, ...) \ + if (debug) \ + printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + +#define dbg_verbose(format, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + + +#define message(format, ...) \ + printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__) diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c new file mode 100644 index 000000000000..b5bc5f106c09 --- /dev/null +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -0,0 +1,1295 @@ +/* + * Copyright © 2004-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * Samsung S3C2410/S3C2440/S3C2412 NAND driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define pr_fmt(fmt) "nand-s3c2410: " fmt + +#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define S3C2410_NFREG(x) (x) + +#define S3C2410_NFCONF S3C2410_NFREG(0x00) +#define S3C2410_NFCMD S3C2410_NFREG(0x04) +#define S3C2410_NFADDR S3C2410_NFREG(0x08) +#define S3C2410_NFDATA S3C2410_NFREG(0x0C) +#define S3C2410_NFSTAT S3C2410_NFREG(0x10) +#define S3C2410_NFECC S3C2410_NFREG(0x14) +#define S3C2440_NFCONT S3C2410_NFREG(0x04) +#define S3C2440_NFCMD S3C2410_NFREG(0x08) +#define S3C2440_NFADDR S3C2410_NFREG(0x0C) +#define S3C2440_NFDATA S3C2410_NFREG(0x10) +#define S3C2440_NFSTAT S3C2410_NFREG(0x20) +#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) +#define S3C2412_NFSTAT S3C2410_NFREG(0x28) +#define S3C2412_NFMECC0 S3C2410_NFREG(0x34) +#define S3C2410_NFCONF_EN (1<<15) +#define S3C2410_NFCONF_INITECC (1<<12) +#define S3C2410_NFCONF_nFCE (1<<11) +#define S3C2410_NFCONF_TACLS(x) ((x)<<8) +#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4) +#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0) +#define S3C2410_NFSTAT_BUSY (1<<0) +#define S3C2440_NFCONF_TACLS(x) ((x)<<12) +#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) +#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) +#define S3C2440_NFCONT_INITECC (1<<4) +#define S3C2440_NFCONT_nFCE (1<<1) +#define S3C2440_NFCONT_ENABLE (1<<0) +#define S3C2440_NFSTAT_READY (1<<0) +#define S3C2412_NFCONF_NANDBOOT (1<<31) +#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5) +#define S3C2412_NFCONT_nFCE0 (1<<1) +#define S3C2412_NFSTAT_READY (1<<0) + +/* new oob placement block for use with hardware ecc generation + */ +static int s3c2410_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = 3; + + return 0; +} + +static int s3c2410_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 8; + oobregion->length = 8; + + return 0; +} + +static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = { + .ecc = s3c2410_ooblayout_ecc, + .free = s3c2410_ooblayout_free, +}; + +/* controller and mtd information */ + +struct s3c2410_nand_info; + +/** + * struct s3c2410_nand_mtd - driver MTD structure + * @mtd: The MTD instance to pass to the MTD layer. + * @chip: The NAND chip information. + * @set: The platform information supplied for this set of NAND chips. + * @info: Link back to the hardware information. + * @scan_res: The result from calling nand_scan_ident(). +*/ +struct s3c2410_nand_mtd { + struct nand_chip chip; + struct s3c2410_nand_set *set; + struct s3c2410_nand_info *info; + int scan_res; +}; + +enum s3c_cpu_type { + TYPE_S3C2410, + TYPE_S3C2412, + TYPE_S3C2440, +}; + +enum s3c_nand_clk_state { + CLOCK_DISABLE = 0, + CLOCK_ENABLE, + CLOCK_SUSPEND, +}; + +/* overview of the s3c2410 nand state */ + +/** + * struct s3c2410_nand_info - NAND controller state. + * @mtds: An array of MTD instances on this controoler. + * @platform: The platform data for this board. + * @device: The platform device we bound to. + * @clk: The clock resource for this controller. + * @regs: The area mapped for the hardware registers. + * @sel_reg: Pointer to the register controlling the NAND selection. + * @sel_bit: The bit in @sel_reg to select the NAND chip. + * @mtd_count: The number of MTDs created from this controller. + * @save_sel: The contents of @sel_reg to be saved over suspend. + * @clk_rate: The clock rate from @clk. + * @clk_state: The current clock state. + * @cpu_type: The exact type of this controller. + */ +struct s3c2410_nand_info { + /* mtd info */ + struct nand_hw_control controller; + struct s3c2410_nand_mtd *mtds; + struct s3c2410_platform_nand *platform; + + /* device info */ + struct device *device; + struct clk *clk; + void __iomem *regs; + void __iomem *sel_reg; + int sel_bit; + int mtd_count; + unsigned long save_sel; + unsigned long clk_rate; + enum s3c_nand_clk_state clk_state; + + enum s3c_cpu_type cpu_type; + +#ifdef CONFIG_ARM_S3C24XX_CPUFREQ + struct notifier_block freq_transition; +#endif +}; + +struct s3c24XX_nand_devtype_data { + enum s3c_cpu_type type; +}; + +static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = { + .type = TYPE_S3C2410, +}; + +static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = { + .type = TYPE_S3C2412, +}; + +static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = { + .type = TYPE_S3C2440, +}; + +/* conversion functions */ + +static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct s3c2410_nand_mtd, + chip); +} + +static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) +{ + return s3c2410_nand_mtd_toours(mtd)->info; +} + +static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) +{ + return platform_get_drvdata(dev); +} + +static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) +{ + return dev_get_platdata(&dev->dev); +} + +static inline int allow_clk_suspend(struct s3c2410_nand_info *info) +{ +#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP + return 1; +#else + return 0; +#endif +} + +/** + * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock. + * @info: The controller instance. + * @new_state: State to which clock should be set. + */ +static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info, + enum s3c_nand_clk_state new_state) +{ + if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND) + return; + + if (info->clk_state == CLOCK_ENABLE) { + if (new_state != CLOCK_ENABLE) + clk_disable_unprepare(info->clk); + } else { + if (new_state == CLOCK_ENABLE) + clk_prepare_enable(info->clk); + } + + info->clk_state = new_state; +} + +/* timing calculations */ + +#define NS_IN_KHZ 1000000 + +/** + * s3c_nand_calc_rate - calculate timing data. + * @wanted: The cycle time in nanoseconds. + * @clk: The clock rate in kHz. + * @max: The maximum divider value. + * + * Calculate the timing value from the given parameters. + */ +static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) +{ + int result; + + result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ); + + pr_debug("result %d from %ld, %d\n", result, clk, wanted); + + if (result > max) { + pr_err("%d ns is too big for current clock rate %ld\n", + wanted, clk); + return -1; + } + + if (result < 1) + result = 1; + + return result; +} + +#define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) + +/* controller setup */ + +/** + * s3c2410_nand_setrate - setup controller timing information. + * @info: The controller instance. + * + * Given the information supplied by the platform, calculate and set + * the necessary timing registers in the hardware to generate the + * necessary timing cycles to the hardware. + */ +static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) +{ + struct s3c2410_platform_nand *plat = info->platform; + int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; + int tacls, twrph0, twrph1; + unsigned long clkrate = clk_get_rate(info->clk); + unsigned long uninitialized_var(set), cfg, uninitialized_var(mask); + unsigned long flags; + + /* calculate the timing information for the controller */ + + info->clk_rate = clkrate; + clkrate /= 1000; /* turn clock into kHz for ease of use */ + + if (plat != NULL) { + tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max); + twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8); + twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8); + } else { + /* default timings */ + tacls = tacls_max; + twrph0 = 8; + twrph1 = 8; + } + + if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { + dev_err(info->device, "cannot get suitable timings\n"); + return -EINVAL; + } + + dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", + tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), + twrph1, to_ns(twrph1, clkrate)); + + switch (info->cpu_type) { + case TYPE_S3C2410: + mask = (S3C2410_NFCONF_TACLS(3) | + S3C2410_NFCONF_TWRPH0(7) | + S3C2410_NFCONF_TWRPH1(7)); + set = S3C2410_NFCONF_EN; + set |= S3C2410_NFCONF_TACLS(tacls - 1); + set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); + set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); + break; + + case TYPE_S3C2440: + case TYPE_S3C2412: + mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | + S3C2440_NFCONF_TWRPH0(7) | + S3C2440_NFCONF_TWRPH1(7)); + + set = S3C2440_NFCONF_TACLS(tacls - 1); + set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); + set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); + break; + + default: + BUG(); + } + + local_irq_save(flags); + + cfg = readl(info->regs + S3C2410_NFCONF); + cfg &= ~mask; + cfg |= set; + writel(cfg, info->regs + S3C2410_NFCONF); + + local_irq_restore(flags); + + dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); + + return 0; +} + +/** + * s3c2410_nand_inithw - basic hardware initialisation + * @info: The hardware state. + * + * Do the basic initialisation of the hardware, using s3c2410_nand_setrate() + * to setup the hardware access speeds and set the controller to be enabled. +*/ +static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) +{ + int ret; + + ret = s3c2410_nand_setrate(info); + if (ret < 0) + return ret; + + switch (info->cpu_type) { + case TYPE_S3C2410: + default: + break; + + case TYPE_S3C2440: + case TYPE_S3C2412: + /* enable the controller and de-assert nFCE */ + + writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); + } + + return 0; +} + +/** + * s3c2410_nand_select_chip - select the given nand chip + * @mtd: The MTD instance for this chip. + * @chip: The chip number. + * + * This is called by the MTD layer to either select a given chip for the + * @mtd instance, or to indicate that the access has finished and the + * chip can be de-selected. + * + * The routine ensures that the nFCE line is correctly setup, and any + * platform specific selection code is called to route nFCE to the specific + * chip. + */ +static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct s3c2410_nand_info *info; + struct s3c2410_nand_mtd *nmtd; + struct nand_chip *this = mtd_to_nand(mtd); + unsigned long cur; + + nmtd = nand_get_controller_data(this); + info = nmtd->info; + + if (chip != -1) + s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); + + cur = readl(info->sel_reg); + + if (chip == -1) { + cur |= info->sel_bit; + } else { + if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { + dev_err(info->device, "invalid chip %d\n", chip); + return; + } + + if (info->platform != NULL) { + if (info->platform->select_chip != NULL) + (info->platform->select_chip) (nmtd->set, chip); + } + + cur &= ~info->sel_bit; + } + + writel(cur, info->sel_reg); + + if (chip == -1) + s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); +} + +/* s3c2410_nand_hwcontrol + * + * Issue command and address cycles to the chip +*/ + +static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writeb(cmd, info->regs + S3C2410_NFCMD); + else + writeb(cmd, info->regs + S3C2410_NFADDR); +} + +/* command and control functions */ + +static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writeb(cmd, info->regs + S3C2440_NFCMD); + else + writeb(cmd, info->regs + S3C2440_NFADDR); +} + +/* s3c2410_nand_devready() + * + * returns 0 if the nand is busy, 1 if it is ready +*/ + +static int s3c2410_nand_devready(struct mtd_info *mtd) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; +} + +static int s3c2440_nand_devready(struct mtd_info *mtd) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; +} + +static int s3c2412_nand_devready(struct mtd_info *mtd) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY; +} + +/* ECC handling functions */ + +static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned int diff0, diff1, diff2; + unsigned int bit, byte; + + pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); + + diff0 = read_ecc[0] ^ calc_ecc[0]; + diff1 = read_ecc[1] ^ calc_ecc[1]; + diff2 = read_ecc[2] ^ calc_ecc[2]; + + pr_debug("%s: rd %*phN calc %*phN diff %02x%02x%02x\n", + __func__, 3, read_ecc, 3, calc_ecc, + diff0, diff1, diff2); + + if (diff0 == 0 && diff1 == 0 && diff2 == 0) + return 0; /* ECC is ok */ + + /* sometimes people do not think about using the ECC, so check + * to see if we have an 0xff,0xff,0xff read ECC and then ignore + * the error, on the assumption that this is an un-eccd page. + */ + if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff + && info->platform->ignore_unset_ecc) + return 0; + + /* Can we correct this ECC (ie, one row and column change). + * Note, this is similar to the 256 error code on smartmedia */ + + if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && + ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && + ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { + /* calculate the bit position of the error */ + + bit = ((diff2 >> 3) & 1) | + ((diff2 >> 4) & 2) | + ((diff2 >> 5) & 4); + + /* calculate the byte position of the error */ + + byte = ((diff2 << 7) & 0x100) | + ((diff1 << 0) & 0x80) | + ((diff1 << 1) & 0x40) | + ((diff1 << 2) & 0x20) | + ((diff1 << 3) & 0x10) | + ((diff0 >> 4) & 0x08) | + ((diff0 >> 3) & 0x04) | + ((diff0 >> 2) & 0x02) | + ((diff0 >> 1) & 0x01); + + dev_dbg(info->device, "correcting error bit %d, byte %d\n", + bit, byte); + + dat[byte] ^= (1 << bit); + return 1; + } + + /* if there is only one bit difference in the ECC, then + * one of only a row or column parity has changed, which + * means the error is most probably in the ECC itself */ + + diff0 |= (diff1 << 8); + diff0 |= (diff2 << 16); + + /* equal to "(diff0 & ~(1 << __ffs(diff0)))" */ + if ((diff0 & (diff0 - 1)) == 0) + return 1; + + return -1; +} + +/* ECC functions + * + * These allow the s3c2410 and s3c2440 to use the controller's ECC + * generator block to ECC the data as it passes through] +*/ + +static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ctrl; + + ctrl = readl(info->regs + S3C2410_NFCONF); + ctrl |= S3C2410_NFCONF_INITECC; + writel(ctrl, info->regs + S3C2410_NFCONF); +} + +static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ctrl; + + ctrl = readl(info->regs + S3C2440_NFCONT); + writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, + info->regs + S3C2440_NFCONT); +} + +static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ctrl; + + ctrl = readl(info->regs + S3C2440_NFCONT); + writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); +} + +static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + + ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); + ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); + ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); + + pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); + + return 0; +} + +static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); + + ecc_code[0] = ecc; + ecc_code[1] = ecc >> 8; + ecc_code[2] = ecc >> 16; + + pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); + + return 0; +} + +static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); + + ecc_code[0] = ecc; + ecc_code[1] = ecc >> 8; + ecc_code[2] = ecc >> 16; + + pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff); + + return 0; +} + +/* over-ride the standard functions for a little more speed. We can + * use read/write block to move the data buffers to/from the controller +*/ + +static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + readsb(this->IO_ADDR_R, buf, len); +} + +static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + + readsl(info->regs + S3C2440_NFDATA, buf, len >> 2); + + /* cleanup if we've got less than a word to do */ + if (len & 3) { + buf += len & ~3; + + for (; len & 3; len--) + *buf++ = readb(info->regs + S3C2440_NFDATA); + } +} + +static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, + int len) +{ + struct nand_chip *this = mtd_to_nand(mtd); + writesb(this->IO_ADDR_W, buf, len); +} + +static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, + int len) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + + writesl(info->regs + S3C2440_NFDATA, buf, len >> 2); + + /* cleanup any fractional write */ + if (len & 3) { + buf += len & ~3; + + for (; len & 3; len--, buf++) + writeb(*buf, info->regs + S3C2440_NFDATA); + } +} + +/* cpufreq driver support */ + +#ifdef CONFIG_ARM_S3C24XX_CPUFREQ + +static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct s3c2410_nand_info *info; + unsigned long newclk; + + info = container_of(nb, struct s3c2410_nand_info, freq_transition); + newclk = clk_get_rate(info->clk); + + if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) || + (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) { + s3c2410_nand_setrate(info); + } + + return 0; +} + +static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) +{ + info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition; + + return cpufreq_register_notifier(&info->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void +s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) +{ + cpufreq_unregister_notifier(&info->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) +{ + return 0; +} + +static inline void +s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) +{ +} +#endif + +/* device management functions */ + +static int s3c24xx_nand_remove(struct platform_device *pdev) +{ + struct s3c2410_nand_info *info = to_nand_info(pdev); + + if (info == NULL) + return 0; + + s3c2410_nand_cpufreq_deregister(info); + + /* Release all our mtds and their partitions, then go through + * freeing the resources used + */ + + if (info->mtds != NULL) { + struct s3c2410_nand_mtd *ptr = info->mtds; + int mtdno; + + for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { + pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); + nand_release(nand_to_mtd(&ptr->chip)); + } + } + + /* free the common resources */ + + if (!IS_ERR(info->clk)) + s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); + + return 0; +} + +static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *mtd, + struct s3c2410_nand_set *set) +{ + if (set) { + struct mtd_info *mtdinfo = nand_to_mtd(&mtd->chip); + + mtdinfo->name = set->name; + + return mtd_device_parse_register(mtdinfo, NULL, NULL, + set->partitions, set->nr_partitions); + } + + return -ENODEV; +} + +static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + struct s3c2410_platform_nand *pdata = info->platform; + const struct nand_sdr_timings *timings; + int tacls; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + + tacls = timings->tCLS_min - timings->tWP_min; + if (tacls < 0) + tacls = 0; + + pdata->tacls = DIV_ROUND_UP(tacls, 1000); + pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000); + pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000); + + return s3c2410_nand_setrate(info); +} + +/** + * s3c2410_nand_init_chip - initialise a single instance of an chip + * @info: The base NAND controller the chip is on. + * @nmtd: The new controller MTD instance to fill in. + * @set: The information passed from the board specific platform data. + * + * Initialise the given @nmtd from the information in @info and @set. This + * readies the structure for use with the MTD layer functions by ensuring + * all pointers are setup and the necessary control routines selected. + */ +static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *nmtd, + struct s3c2410_nand_set *set) +{ + struct device_node *np = info->device->of_node; + struct nand_chip *chip = &nmtd->chip; + void __iomem *regs = info->regs; + + nand_set_flash_node(chip, set->of_node); + + chip->write_buf = s3c2410_nand_write_buf; + chip->read_buf = s3c2410_nand_read_buf; + chip->select_chip = s3c2410_nand_select_chip; + chip->chip_delay = 50; + nand_set_controller_data(chip, nmtd); + chip->options = set->options; + chip->controller = &info->controller; + + /* + * let's keep behavior unchanged for legacy boards booting via pdata and + * auto-detect timings only when booting with a device tree. + */ + if (np) + chip->setup_data_interface = s3c2410_nand_setup_data_interface; + + switch (info->cpu_type) { + case TYPE_S3C2410: + chip->IO_ADDR_W = regs + S3C2410_NFDATA; + info->sel_reg = regs + S3C2410_NFCONF; + info->sel_bit = S3C2410_NFCONF_nFCE; + chip->cmd_ctrl = s3c2410_nand_hwcontrol; + chip->dev_ready = s3c2410_nand_devready; + break; + + case TYPE_S3C2440: + chip->IO_ADDR_W = regs + S3C2440_NFDATA; + info->sel_reg = regs + S3C2440_NFCONT; + info->sel_bit = S3C2440_NFCONT_nFCE; + chip->cmd_ctrl = s3c2440_nand_hwcontrol; + chip->dev_ready = s3c2440_nand_devready; + chip->read_buf = s3c2440_nand_read_buf; + chip->write_buf = s3c2440_nand_write_buf; + break; + + case TYPE_S3C2412: + chip->IO_ADDR_W = regs + S3C2440_NFDATA; + info->sel_reg = regs + S3C2440_NFCONT; + info->sel_bit = S3C2412_NFCONT_nFCE0; + chip->cmd_ctrl = s3c2440_nand_hwcontrol; + chip->dev_ready = s3c2412_nand_devready; + + if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT) + dev_info(info->device, "System booted from NAND\n"); + + break; + } + + chip->IO_ADDR_R = chip->IO_ADDR_W; + + nmtd->info = info; + nmtd->set = set; + + chip->ecc.mode = info->platform->ecc_mode; + + /* + * If you use u-boot BBT creation code, specifying this flag will + * let the kernel fish out the BBT from the NAND. + */ + if (set->flash_bbt) + chip->bbt_options |= NAND_BBT_USE_FLASH; +} + +/** + * s3c2410_nand_update_chip - post probe update + * @info: The controller instance. + * @nmtd: The driver version of the MTD instance. + * + * This routine is called after the chip probe has successfully completed + * and the relevant per-chip information updated. This call ensure that + * we update the internal state accordingly. + * + * The internal state is currently limited to the ECC state information. +*/ +static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *nmtd) +{ + struct nand_chip *chip = &nmtd->chip; + + switch (chip->ecc.mode) { + + case NAND_ECC_NONE: + dev_info(info->device, "ECC disabled\n"); + break; + + case NAND_ECC_SOFT: + /* + * This driver expects Hamming based ECC when ecc_mode is set + * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to + * avoid adding an extra ecc_algo field to + * s3c2410_platform_nand. + */ + chip->ecc.algo = NAND_ECC_HAMMING; + dev_info(info->device, "soft ECC\n"); + break; + + case NAND_ECC_HW: + chip->ecc.calculate = s3c2410_nand_calculate_ecc; + chip->ecc.correct = s3c2410_nand_correct_data; + chip->ecc.strength = 1; + + switch (info->cpu_type) { + case TYPE_S3C2410: + chip->ecc.hwctl = s3c2410_nand_enable_hwecc; + chip->ecc.calculate = s3c2410_nand_calculate_ecc; + break; + + case TYPE_S3C2412: + chip->ecc.hwctl = s3c2412_nand_enable_hwecc; + chip->ecc.calculate = s3c2412_nand_calculate_ecc; + break; + + case TYPE_S3C2440: + chip->ecc.hwctl = s3c2440_nand_enable_hwecc; + chip->ecc.calculate = s3c2440_nand_calculate_ecc; + break; + } + + dev_dbg(info->device, "chip %p => page shift %d\n", + chip, chip->page_shift); + + /* change the behaviour depending on whether we are using + * the large or small page nand device */ + if (chip->page_shift > 10) { + chip->ecc.size = 256; + chip->ecc.bytes = 3; + } else { + chip->ecc.size = 512; + chip->ecc.bytes = 3; + mtd_set_ooblayout(nand_to_mtd(chip), + &s3c2410_ooblayout_ops); + } + + dev_info(info->device, "hardware ECC\n"); + break; + + default: + dev_err(info->device, "invalid ECC mode!\n"); + return -EINVAL; + } + + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->options |= NAND_SKIP_BBTSCAN; + + return 0; +} + +static const struct of_device_id s3c24xx_nand_dt_ids[] = { + { + .compatible = "samsung,s3c2410-nand", + .data = &s3c2410_nand_devtype_data, + }, { + /* also compatible with s3c6400 */ + .compatible = "samsung,s3c2412-nand", + .data = &s3c2412_nand_devtype_data, + }, { + .compatible = "samsung,s3c2440-nand", + .data = &s3c2440_nand_devtype_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s3c24xx_nand_dt_ids); + +static int s3c24xx_nand_probe_dt(struct platform_device *pdev) +{ + const struct s3c24XX_nand_devtype_data *devtype_data; + struct s3c2410_platform_nand *pdata; + struct s3c2410_nand_info *info = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node, *child; + struct s3c2410_nand_set *sets; + + devtype_data = of_device_get_match_data(&pdev->dev); + if (!devtype_data) + return -ENODEV; + + info->cpu_type = devtype_data->type; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdev->dev.platform_data = pdata; + + pdata->nr_sets = of_get_child_count(np); + if (!pdata->nr_sets) + return 0; + + sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets, + GFP_KERNEL); + if (!sets) + return -ENOMEM; + + pdata->sets = sets; + + for_each_available_child_of_node(np, child) { + sets->name = (char *)child->name; + sets->of_node = child; + sets->nr_chips = 1; + + of_node_get(child); + + sets++; + } + + return 0; +} + +static int s3c24xx_nand_probe_pdata(struct platform_device *pdev) +{ + struct s3c2410_nand_info *info = platform_get_drvdata(pdev); + + info->cpu_type = platform_get_device_id(pdev)->driver_data; + + return 0; +} + +/* s3c24xx_nand_probe + * + * called by device layer when it finds a device matching + * one our driver can handled. This code checks to see if + * it can allocate all necessary resources then calls the + * nand layer to look for devices +*/ +static int s3c24xx_nand_probe(struct platform_device *pdev) +{ + struct s3c2410_platform_nand *plat; + struct s3c2410_nand_info *info; + struct s3c2410_nand_mtd *nmtd; + struct s3c2410_nand_set *sets; + struct resource *res; + int err = 0; + int size; + int nr_sets; + int setno; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto exit_error; + } + + platform_set_drvdata(pdev, info); + + nand_hw_control_init(&info->controller); + + /* get the clock source and enable it */ + + info->clk = devm_clk_get(&pdev->dev, "nand"); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + err = -ENOENT; + goto exit_error; + } + + s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); + + if (pdev->dev.of_node) + err = s3c24xx_nand_probe_dt(pdev); + else + err = s3c24xx_nand_probe_pdata(pdev); + + if (err) + goto exit_error; + + plat = to_nand_plat(pdev); + + /* allocate and map the resource */ + + /* currently we assume we have the one resource */ + res = pdev->resource; + size = resource_size(res); + + info->device = &pdev->dev; + info->platform = plat; + + info->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->regs)) { + err = PTR_ERR(info->regs); + goto exit_error; + } + + dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs); + + sets = (plat != NULL) ? plat->sets : NULL; + nr_sets = (plat != NULL) ? plat->nr_sets : 1; + + info->mtd_count = nr_sets; + + /* allocate our information */ + + size = nr_sets * sizeof(*info->mtds); + info->mtds = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (info->mtds == NULL) { + err = -ENOMEM; + goto exit_error; + } + + /* initialise all possible chips */ + + nmtd = info->mtds; + + for (setno = 0; setno < nr_sets; setno++, nmtd++) { + struct mtd_info *mtd = nand_to_mtd(&nmtd->chip); + + pr_debug("initialising set %d (%p, info %p)\n", + setno, nmtd, info); + + mtd->dev.parent = &pdev->dev; + s3c2410_nand_init_chip(info, nmtd, sets); + + nmtd->scan_res = nand_scan_ident(mtd, + (sets) ? sets->nr_chips : 1, + NULL); + + if (nmtd->scan_res == 0) { + err = s3c2410_nand_update_chip(info, nmtd); + if (err < 0) + goto exit_error; + nand_scan_tail(mtd); + s3c2410_nand_add_partition(info, nmtd, sets); + } + + if (sets != NULL) + sets++; + } + + /* initialise the hardware */ + err = s3c2410_nand_inithw(info); + if (err != 0) + goto exit_error; + + err = s3c2410_nand_cpufreq_register(info); + if (err < 0) { + dev_err(&pdev->dev, "failed to init cpufreq support\n"); + goto exit_error; + } + + if (allow_clk_suspend(info)) { + dev_info(&pdev->dev, "clock idle support enabled\n"); + s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); + } + + return 0; + + exit_error: + s3c24xx_nand_remove(pdev); + + if (err == 0) + err = -EINVAL; + return err; +} + +/* PM Support */ +#ifdef CONFIG_PM + +static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) +{ + struct s3c2410_nand_info *info = platform_get_drvdata(dev); + + if (info) { + info->save_sel = readl(info->sel_reg); + + /* For the moment, we must ensure nFCE is high during + * the time we are suspended. This really should be + * handled by suspending the MTDs we are using, but + * that is currently not the case. */ + + writel(info->save_sel | info->sel_bit, info->sel_reg); + + s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); + } + + return 0; +} + +static int s3c24xx_nand_resume(struct platform_device *dev) +{ + struct s3c2410_nand_info *info = platform_get_drvdata(dev); + unsigned long sel; + + if (info) { + s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); + s3c2410_nand_inithw(info); + + /* Restore the state of the nFCE line. */ + + sel = readl(info->sel_reg); + sel &= ~info->sel_bit; + sel |= info->save_sel & info->sel_bit; + writel(sel, info->sel_reg); + + s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); + } + + return 0; +} + +#else +#define s3c24xx_nand_suspend NULL +#define s3c24xx_nand_resume NULL +#endif + +/* driver device registration */ + +static const struct platform_device_id s3c24xx_driver_ids[] = { + { + .name = "s3c2410-nand", + .driver_data = TYPE_S3C2410, + }, { + .name = "s3c2440-nand", + .driver_data = TYPE_S3C2440, + }, { + .name = "s3c2412-nand", + .driver_data = TYPE_S3C2412, + }, { + .name = "s3c6400-nand", + .driver_data = TYPE_S3C2412, /* compatible with 2412 */ + }, + { } +}; + +MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); + +static struct platform_driver s3c24xx_nand_driver = { + .probe = s3c24xx_nand_probe, + .remove = s3c24xx_nand_remove, + .suspend = s3c24xx_nand_suspend, + .resume = s3c24xx_nand_resume, + .id_table = s3c24xx_driver_ids, + .driver = { + .name = "s3c24xx-nand", + .of_match_table = s3c24xx_nand_dt_ids, + }, +}; + +module_platform_driver(s3c24xx_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c new file mode 100644 index 000000000000..c4e7755448e6 --- /dev/null +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -0,0 +1,1250 @@ +/* + * SuperH FLCTL nand controller + * + * Copyright (c) 2008 Renesas Solutions Corp. + * Copyright (c) 2008 Atom Create Engineering Co., Ltd. + * + * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 12; + oobregion->length = 4; + + return 0; +} + +static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = { + .ecc = flctl_4secc_ooblayout_sp_ecc, + .free = flctl_4secc_ooblayout_sp_free, +}; + +static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 6; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = section * 16; + oobregion->length = 6; + + if (!section) { + oobregion->offset += 2; + oobregion->length -= 2; + } + + return 0; +} + +static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = { + .ecc = flctl_4secc_ooblayout_lp_ecc, + .free = flctl_4secc_ooblayout_lp_free, +}; + +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr flctl_4secc_smallpage = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 11, + .len = 1, + .pattern = scan_ff_pattern, +}; + +static struct nand_bbt_descr flctl_4secc_largepage = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern, +}; + +static void empty_fifo(struct sh_flctl *flctl) +{ + writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl)); + writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); +} + +static void start_translation(struct sh_flctl *flctl) +{ + writeb(TRSTRT, FLTRCR(flctl)); +} + +static void timeout_error(struct sh_flctl *flctl, const char *str) +{ + dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str); +} + +static void wait_completion(struct sh_flctl *flctl) +{ + uint32_t timeout = LOOP_TIMEOUT_MAX; + + while (timeout--) { + if (readb(FLTRCR(flctl)) & TREND) { + writeb(0x0, FLTRCR(flctl)); + return; + } + udelay(1); + } + + timeout_error(flctl, __func__); + writeb(0x0, FLTRCR(flctl)); +} + +static void flctl_dma_complete(void *param) +{ + struct sh_flctl *flctl = param; + + complete(&flctl->dma_complete); +} + +static void flctl_release_dma(struct sh_flctl *flctl) +{ + if (flctl->chan_fifo0_rx) { + dma_release_channel(flctl->chan_fifo0_rx); + flctl->chan_fifo0_rx = NULL; + } + if (flctl->chan_fifo0_tx) { + dma_release_channel(flctl->chan_fifo0_tx); + flctl->chan_fifo0_tx = NULL; + } +} + +static void flctl_setup_dma(struct sh_flctl *flctl) +{ + dma_cap_mask_t mask; + struct dma_slave_config cfg; + struct platform_device *pdev = flctl->pdev; + struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev); + int ret; + + if (!pdata) + return; + + if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0) + return; + + /* We can only either use DMA for both Tx and Rx or not use it at all */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter, + (void *)(uintptr_t)pdata->slave_id_fifo0_tx); + dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__, + flctl->chan_fifo0_tx); + + if (!flctl->chan_fifo0_tx) + return; + + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = DMA_MEM_TO_DEV; + cfg.dst_addr = flctl->fifo; + cfg.src_addr = 0; + ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg); + if (ret < 0) + goto err; + + flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter, + (void *)(uintptr_t)pdata->slave_id_fifo0_rx); + dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__, + flctl->chan_fifo0_rx); + + if (!flctl->chan_fifo0_rx) + goto err; + + cfg.direction = DMA_DEV_TO_MEM; + cfg.dst_addr = 0; + cfg.src_addr = flctl->fifo; + ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg); + if (ret < 0) + goto err; + + init_completion(&flctl->dma_complete); + + return; + +err: + flctl_release_dma(flctl); +} + +static void set_addr(struct mtd_info *mtd, int column, int page_addr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint32_t addr = 0; + + if (column == -1) { + addr = page_addr; /* ERASE1 */ + } else if (page_addr != -1) { + /* SEQIN, READ0, etc.. */ + if (flctl->chip.options & NAND_BUSWIDTH_16) + column >>= 1; + if (flctl->page_size) { + addr = column & 0x0FFF; + addr |= (page_addr & 0xff) << 16; + addr |= ((page_addr >> 8) & 0xff) << 24; + /* big than 128MB */ + if (flctl->rw_ADRCNT == ADRCNT2_E) { + uint32_t addr2; + addr2 = (page_addr >> 16) & 0xff; + writel(addr2, FLADR2(flctl)); + } + } else { + addr = column; + addr |= (page_addr & 0xff) << 8; + addr |= ((page_addr >> 8) & 0xff) << 16; + addr |= ((page_addr >> 16) & 0xff) << 24; + } + } + writel(addr, FLADR(flctl)); +} + +static void wait_rfifo_ready(struct sh_flctl *flctl) +{ + uint32_t timeout = LOOP_TIMEOUT_MAX; + + while (timeout--) { + uint32_t val; + /* check FIFO */ + val = readl(FLDTCNTR(flctl)) >> 16; + if (val & 0xFF) + return; + udelay(1); + } + timeout_error(flctl, __func__); +} + +static void wait_wfifo_ready(struct sh_flctl *flctl) +{ + uint32_t len, timeout = LOOP_TIMEOUT_MAX; + + while (timeout--) { + /* check FIFO */ + len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF; + if (len >= 4) + return; + udelay(1); + } + timeout_error(flctl, __func__); +} + +static enum flctl_ecc_res_t wait_recfifo_ready + (struct sh_flctl *flctl, int sector_number) +{ + uint32_t timeout = LOOP_TIMEOUT_MAX; + void __iomem *ecc_reg[4]; + int i; + int state = FL_SUCCESS; + uint32_t data, size; + + /* + * First this loops checks in FLDTCNTR if we are ready to read out the + * oob data. This is the case if either all went fine without errors or + * if the bottom part of the loop corrected the errors or marked them as + * uncorrectable and the controller is given time to push the data into + * the FIFO. + */ + while (timeout--) { + /* check if all is ok and we can read out the OOB */ + size = readl(FLDTCNTR(flctl)) >> 24; + if ((size & 0xFF) == 4) + return state; + + /* check if a correction code has been calculated */ + if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) { + /* + * either we wait for the fifo to be filled or a + * correction pattern is being generated + */ + udelay(1); + continue; + } + + /* check for an uncorrectable error */ + if (readl(FL4ECCCR(flctl)) & _4ECCFA) { + /* check if we face a non-empty page */ + for (i = 0; i < 512; i++) { + if (flctl->done_buff[i] != 0xff) { + state = FL_ERROR; /* can't correct */ + break; + } + } + + if (state == FL_SUCCESS) + dev_dbg(&flctl->pdev->dev, + "reading empty sector %d, ecc error ignored\n", + sector_number); + + writel(0, FL4ECCCR(flctl)); + continue; + } + + /* start error correction */ + ecc_reg[0] = FL4ECCRESULT0(flctl); + ecc_reg[1] = FL4ECCRESULT1(flctl); + ecc_reg[2] = FL4ECCRESULT2(flctl); + ecc_reg[3] = FL4ECCRESULT3(flctl); + + for (i = 0; i < 3; i++) { + uint8_t org; + unsigned int index; + + data = readl(ecc_reg[i]); + + if (flctl->page_size) + index = (512 * sector_number) + + (data >> 16); + else + index = data >> 16; + + org = flctl->done_buff[index]; + flctl->done_buff[index] = org ^ (data & 0xFF); + } + state = FL_REPAIRABLE; + writel(0, FL4ECCCR(flctl)); + } + + timeout_error(flctl, __func__); + return FL_TIMEOUT; /* timeout */ +} + +static void wait_wecfifo_ready(struct sh_flctl *flctl) +{ + uint32_t timeout = LOOP_TIMEOUT_MAX; + uint32_t len; + + while (timeout--) { + /* check FLECFIFO */ + len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF; + if (len >= 4) + return; + udelay(1); + } + timeout_error(flctl, __func__); +} + +static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, + int len, enum dma_data_direction dir) +{ + struct dma_async_tx_descriptor *desc = NULL; + struct dma_chan *chan; + enum dma_transfer_direction tr_dir; + dma_addr_t dma_addr; + dma_cookie_t cookie; + uint32_t reg; + int ret; + + if (dir == DMA_FROM_DEVICE) { + chan = flctl->chan_fifo0_rx; + tr_dir = DMA_DEV_TO_MEM; + } else { + chan = flctl->chan_fifo0_tx; + tr_dir = DMA_MEM_TO_DEV; + } + + dma_addr = dma_map_single(chan->device->dev, buf, len, dir); + + if (!dma_mapping_error(chan->device->dev, dma_addr)) + desc = dmaengine_prep_slave_single(chan, dma_addr, len, + tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (desc) { + reg = readl(FLINTDMACR(flctl)); + reg |= DREQ0EN; + writel(reg, FLINTDMACR(flctl)); + + desc->callback = flctl_dma_complete; + desc->callback_param = flctl; + cookie = dmaengine_submit(desc); + if (dma_submit_error(cookie)) { + ret = dma_submit_error(cookie); + dev_warn(&flctl->pdev->dev, + "DMA submit failed, falling back to PIO\n"); + goto out; + } + + dma_async_issue_pending(chan); + } else { + /* DMA failed, fall back to PIO */ + flctl_release_dma(flctl); + dev_warn(&flctl->pdev->dev, + "DMA failed, falling back to PIO\n"); + ret = -EIO; + goto out; + } + + ret = + wait_for_completion_timeout(&flctl->dma_complete, + msecs_to_jiffies(3000)); + + if (ret <= 0) { + dmaengine_terminate_all(chan); + dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n"); + } + +out: + reg = readl(FLINTDMACR(flctl)); + reg &= ~DREQ0EN; + writel(reg, FLINTDMACR(flctl)); + + dma_unmap_single(chan->device->dev, dma_addr, len, dir); + + /* ret > 0 is success */ + return ret; +} + +static void read_datareg(struct sh_flctl *flctl, int offset) +{ + unsigned long data; + unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; + + wait_completion(flctl); + + data = readl(FLDATAR(flctl)); + *buf = le32_to_cpu(data); +} + +static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) +{ + int i, len_4align; + unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; + + len_4align = (rlen + 3) / 4; + + /* initiate DMA transfer */ + if (flctl->chan_fifo0_rx && rlen >= 32 && + flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_DEV_TO_MEM) > 0) + goto convert; /* DMA success */ + + /* do polling transfer */ + for (i = 0; i < len_4align; i++) { + wait_rfifo_ready(flctl); + buf[i] = readl(FLDTFIFO(flctl)); + } + +convert: + for (i = 0; i < len_4align; i++) + buf[i] = be32_to_cpu(buf[i]); +} + +static enum flctl_ecc_res_t read_ecfiforeg + (struct sh_flctl *flctl, uint8_t *buff, int sector) +{ + int i; + enum flctl_ecc_res_t res; + unsigned long *ecc_buf = (unsigned long *)buff; + + res = wait_recfifo_ready(flctl , sector); + + if (res != FL_ERROR) { + for (i = 0; i < 4; i++) { + ecc_buf[i] = readl(FLECFIFO(flctl)); + ecc_buf[i] = be32_to_cpu(ecc_buf[i]); + } + } + + return res; +} + +static void write_fiforeg(struct sh_flctl *flctl, int rlen, + unsigned int offset) +{ + int i, len_4align; + unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; + + len_4align = (rlen + 3) / 4; + for (i = 0; i < len_4align; i++) { + wait_wfifo_ready(flctl); + writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl)); + } +} + +static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen, + unsigned int offset) +{ + int i, len_4align; + unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; + + len_4align = (rlen + 3) / 4; + + for (i = 0; i < len_4align; i++) + buf[i] = cpu_to_be32(buf[i]); + + /* initiate DMA transfer */ + if (flctl->chan_fifo0_tx && rlen >= 32 && + flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_MEM_TO_DEV) > 0) + return; /* DMA success */ + + /* do polling transfer */ + for (i = 0; i < len_4align; i++) { + wait_wecfifo_ready(flctl); + writel(buf[i], FLECFIFO(flctl)); + } +} + +static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT; + uint32_t flcmdcr_val, addr_len_bytes = 0; + + /* Set SNAND bit if page size is 2048byte */ + if (flctl->page_size) + flcmncr_val |= SNAND_E; + else + flcmncr_val &= ~SNAND_E; + + /* default FLCMDCR val */ + flcmdcr_val = DOCMD1_E | DOADR_E; + + /* Set for FLCMDCR */ + switch (cmd) { + case NAND_CMD_ERASE1: + addr_len_bytes = flctl->erase_ADRCNT; + flcmdcr_val |= DOCMD2_E; + break; + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + case NAND_CMD_RNDOUT: + addr_len_bytes = flctl->rw_ADRCNT; + flcmdcr_val |= CDSRC_E; + if (flctl->chip.options & NAND_BUSWIDTH_16) + flcmncr_val |= SEL_16BIT; + break; + case NAND_CMD_SEQIN: + /* This case is that cmd is READ0 or READ1 or READ00 */ + flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */ + break; + case NAND_CMD_PAGEPROG: + addr_len_bytes = flctl->rw_ADRCNT; + flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW; + if (flctl->chip.options & NAND_BUSWIDTH_16) + flcmncr_val |= SEL_16BIT; + break; + case NAND_CMD_READID: + flcmncr_val &= ~SNAND_E; + flcmdcr_val |= CDSRC_E; + addr_len_bytes = ADRCNT_1; + break; + case NAND_CMD_STATUS: + case NAND_CMD_RESET: + flcmncr_val &= ~SNAND_E; + flcmdcr_val &= ~(DOADR_E | DOSR_E); + break; + default: + break; + } + + /* Set address bytes parameter */ + flcmdcr_val |= addr_len_bytes; + + /* Now actually write */ + writel(flcmncr_val, FLCMNCR(flctl)); + writel(flcmdcr_val, FLCMDCR(flctl)); + writel(flcmcdr_val, FLCMCDR(flctl)); +} + +static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + nand_read_page_op(chip, page, 0, buf, mtd->writesize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + return nand_prog_page_end_op(chip); +} + +static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int sector, page_sectors; + enum flctl_ecc_res_t ecc_result; + + page_sectors = flctl->page_size ? 4 : 1; + + set_cmd_regs(mtd, NAND_CMD_READ0, + (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); + + writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT, + FLCMNCR(flctl)); + writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); + writel(page_addr << 2, FLADR(flctl)); + + empty_fifo(flctl); + start_translation(flctl); + + for (sector = 0; sector < page_sectors; sector++) { + read_fiforeg(flctl, 512, 512 * sector); + + ecc_result = read_ecfiforeg(flctl, + &flctl->done_buff[mtd->writesize + 16 * sector], + sector); + + switch (ecc_result) { + case FL_REPAIRABLE: + dev_info(&flctl->pdev->dev, + "applied ecc on page 0x%x", page_addr); + mtd->ecc_stats.corrected++; + break; + case FL_ERROR: + dev_warn(&flctl->pdev->dev, + "page 0x%x contains corrupted data\n", + page_addr); + mtd->ecc_stats.failed++; + break; + default: + ; + } + } + + wait_completion(flctl); + + writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT), + FLCMNCR(flctl)); +} + +static void execmd_read_oob(struct mtd_info *mtd, int page_addr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int page_sectors = flctl->page_size ? 4 : 1; + int i; + + set_cmd_regs(mtd, NAND_CMD_READ0, + (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); + + empty_fifo(flctl); + + for (i = 0; i < page_sectors; i++) { + set_addr(mtd, (512 + 16) * i + 512 , page_addr); + writel(16, FLDTCNTR(flctl)); + + start_translation(flctl); + read_fiforeg(flctl, 16, 16 * i); + wait_completion(flctl); + } +} + +static void execmd_write_page_sector(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int page_addr = flctl->seqin_page_addr; + int sector, page_sectors; + + page_sectors = flctl->page_size ? 4 : 1; + + set_cmd_regs(mtd, NAND_CMD_PAGEPROG, + (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); + + empty_fifo(flctl); + writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl)); + writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); + writel(page_addr << 2, FLADR(flctl)); + start_translation(flctl); + + for (sector = 0; sector < page_sectors; sector++) { + write_fiforeg(flctl, 512, 512 * sector); + write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector); + } + + wait_completion(flctl); + writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl)); +} + +static void execmd_write_oob(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int page_addr = flctl->seqin_page_addr; + int sector, page_sectors; + + page_sectors = flctl->page_size ? 4 : 1; + + set_cmd_regs(mtd, NAND_CMD_PAGEPROG, + (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); + + for (sector = 0; sector < page_sectors; sector++) { + empty_fifo(flctl); + set_addr(mtd, sector * 528 + 512, page_addr); + writel(16, FLDTCNTR(flctl)); /* set read size */ + + start_translation(flctl); + write_fiforeg(flctl, 16, 16 * sector); + wait_completion(flctl); + } +} + +static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint32_t read_cmd = 0; + + pm_runtime_get_sync(&flctl->pdev->dev); + + flctl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + flctl->index = 0; + + switch (command) { + case NAND_CMD_READ1: + case NAND_CMD_READ0: + if (flctl->hwecc) { + /* read page with hwecc */ + execmd_read_page_sector(mtd, page_addr); + break; + } + if (flctl->page_size) + set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) + | command); + else + set_cmd_regs(mtd, command, command); + + set_addr(mtd, 0, page_addr); + + flctl->read_bytes = mtd->writesize + mtd->oobsize; + if (flctl->chip.options & NAND_BUSWIDTH_16) + column >>= 1; + flctl->index += column; + goto read_normal_exit; + + case NAND_CMD_READOOB: + if (flctl->hwecc) { + /* read page with hwecc */ + execmd_read_oob(mtd, page_addr); + break; + } + + if (flctl->page_size) { + set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) + | NAND_CMD_READ0); + set_addr(mtd, mtd->writesize, page_addr); + } else { + set_cmd_regs(mtd, command, command); + set_addr(mtd, 0, page_addr); + } + flctl->read_bytes = mtd->oobsize; + goto read_normal_exit; + + case NAND_CMD_RNDOUT: + if (flctl->hwecc) + break; + + if (flctl->page_size) + set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8) + | command); + else + set_cmd_regs(mtd, command, command); + + set_addr(mtd, column, 0); + + flctl->read_bytes = mtd->writesize + mtd->oobsize - column; + goto read_normal_exit; + + case NAND_CMD_READID: + set_cmd_regs(mtd, command, command); + + /* READID is always performed using an 8-bit bus */ + if (flctl->chip.options & NAND_BUSWIDTH_16) + column <<= 1; + set_addr(mtd, column, 0); + + flctl->read_bytes = 8; + writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + empty_fifo(flctl); + start_translation(flctl); + read_fiforeg(flctl, flctl->read_bytes, 0); + wait_completion(flctl); + break; + + case NAND_CMD_ERASE1: + flctl->erase1_page_addr = page_addr; + break; + + case NAND_CMD_ERASE2: + set_cmd_regs(mtd, NAND_CMD_ERASE1, + (command << 8) | NAND_CMD_ERASE1); + set_addr(mtd, -1, flctl->erase1_page_addr); + start_translation(flctl); + wait_completion(flctl); + break; + + case NAND_CMD_SEQIN: + if (!flctl->page_size) { + /* output read command */ + if (column >= mtd->writesize) { + column -= mtd->writesize; + read_cmd = NAND_CMD_READOOB; + } else if (column < 256) { + read_cmd = NAND_CMD_READ0; + } else { + column -= 256; + read_cmd = NAND_CMD_READ1; + } + } + flctl->seqin_column = column; + flctl->seqin_page_addr = page_addr; + flctl->seqin_read_cmd = read_cmd; + break; + + case NAND_CMD_PAGEPROG: + empty_fifo(flctl); + if (!flctl->page_size) { + set_cmd_regs(mtd, NAND_CMD_SEQIN, + flctl->seqin_read_cmd); + set_addr(mtd, -1, -1); + writel(0, FLDTCNTR(flctl)); /* set 0 size */ + start_translation(flctl); + wait_completion(flctl); + } + if (flctl->hwecc) { + /* write page with hwecc */ + if (flctl->seqin_column == mtd->writesize) + execmd_write_oob(mtd); + else if (!flctl->seqin_column) + execmd_write_page_sector(mtd); + else + printk(KERN_ERR "Invalid address !?\n"); + break; + } + set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN); + set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr); + writel(flctl->index, FLDTCNTR(flctl)); /* set write size */ + start_translation(flctl); + write_fiforeg(flctl, flctl->index, 0); + wait_completion(flctl); + break; + + case NAND_CMD_STATUS: + set_cmd_regs(mtd, command, command); + set_addr(mtd, -1, -1); + + flctl->read_bytes = 1; + writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + start_translation(flctl); + read_datareg(flctl, 0); /* read and end */ + break; + + case NAND_CMD_RESET: + set_cmd_regs(mtd, command, command); + set_addr(mtd, -1, -1); + + writel(0, FLDTCNTR(flctl)); /* set 0 size */ + start_translation(flctl); + wait_completion(flctl); + break; + + default: + break; + } + goto runtime_exit; + +read_normal_exit: + writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + empty_fifo(flctl); + start_translation(flctl); + read_fiforeg(flctl, flctl->read_bytes, 0); + wait_completion(flctl); +runtime_exit: + pm_runtime_put_sync(&flctl->pdev->dev); + return; +} + +static void flctl_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int ret; + + switch (chipnr) { + case -1: + flctl->flcmncr_base &= ~CE0_ENABLE; + + pm_runtime_get_sync(&flctl->pdev->dev); + writel(flctl->flcmncr_base, FLCMNCR(flctl)); + + if (flctl->qos_request) { + dev_pm_qos_remove_request(&flctl->pm_qos); + flctl->qos_request = 0; + } + + pm_runtime_put_sync(&flctl->pdev->dev); + break; + case 0: + flctl->flcmncr_base |= CE0_ENABLE; + + if (!flctl->qos_request) { + ret = dev_pm_qos_add_request(&flctl->pdev->dev, + &flctl->pm_qos, + DEV_PM_QOS_RESUME_LATENCY, + 100); + if (ret < 0) + dev_err(&flctl->pdev->dev, + "PM QoS request failed: %d\n", ret); + flctl->qos_request = 1; + } + + if (flctl->holden) { + pm_runtime_get_sync(&flctl->pdev->dev); + writel(HOLDEN, FLHOLDCR(flctl)); + pm_runtime_put_sync(&flctl->pdev->dev); + } + break; + default: + BUG(); + } +} + +static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + + memcpy(&flctl->done_buff[flctl->index], buf, len); + flctl->index += len; +} + +static uint8_t flctl_read_byte(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint8_t data; + + data = flctl->done_buff[flctl->index]; + flctl->index++; + return data; +} + +static uint16_t flctl_read_word(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint16_t *buf = (uint16_t *)&flctl->done_buff[flctl->index]; + + flctl->index += 2; + return *buf; +} + +static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + + memcpy(buf, &flctl->done_buff[flctl->index], len); + flctl->index += len; +} + +static int flctl_chip_init_tail(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + struct nand_chip *chip = &flctl->chip; + + if (mtd->writesize == 512) { + flctl->page_size = 0; + if (chip->chipsize > (32 << 20)) { + /* big than 32MB */ + flctl->rw_ADRCNT = ADRCNT_4; + flctl->erase_ADRCNT = ADRCNT_3; + } else if (chip->chipsize > (2 << 16)) { + /* big than 128KB */ + flctl->rw_ADRCNT = ADRCNT_3; + flctl->erase_ADRCNT = ADRCNT_2; + } else { + flctl->rw_ADRCNT = ADRCNT_2; + flctl->erase_ADRCNT = ADRCNT_1; + } + } else { + flctl->page_size = 1; + if (chip->chipsize > (128 << 20)) { + /* big than 128MB */ + flctl->rw_ADRCNT = ADRCNT2_E; + flctl->erase_ADRCNT = ADRCNT_3; + } else if (chip->chipsize > (8 << 16)) { + /* big than 512KB */ + flctl->rw_ADRCNT = ADRCNT_4; + flctl->erase_ADRCNT = ADRCNT_2; + } else { + flctl->rw_ADRCNT = ADRCNT_3; + flctl->erase_ADRCNT = ADRCNT_1; + } + } + + if (flctl->hwecc) { + if (mtd->writesize == 512) { + mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops); + chip->badblock_pattern = &flctl_4secc_smallpage; + } else { + mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops); + chip->badblock_pattern = &flctl_4secc_largepage; + } + + chip->ecc.size = 512; + chip->ecc.bytes = 10; + chip->ecc.strength = 4; + chip->ecc.read_page = flctl_read_page_hwecc; + chip->ecc.write_page = flctl_write_page_hwecc; + chip->ecc.mode = NAND_ECC_HW; + + /* 4 symbols ECC enabled */ + flctl->flcmncr_base |= _4ECCEN; + } else { + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + } + + return 0; +} + +static irqreturn_t flctl_handle_flste(int irq, void *dev_id) +{ + struct sh_flctl *flctl = dev_id; + + dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl))); + writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); + + return IRQ_HANDLED; +} + +struct flctl_soc_config { + unsigned long flcmncr_val; + unsigned has_hwecc:1; + unsigned use_holden:1; +}; + +static struct flctl_soc_config flctl_sh7372_config = { + .flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL, + .has_hwecc = 1, + .use_holden = 1, +}; + +static const struct of_device_id of_flctl_match[] = { + { .compatible = "renesas,shmobile-flctl-sh7372", + .data = &flctl_sh7372_config }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_flctl_match); + +static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) +{ + const struct flctl_soc_config *config; + struct sh_flctl_platform_data *pdata; + + config = of_device_get_match_data(dev); + if (!config) { + dev_err(dev, "%s: no OF configuration attached\n", __func__); + return NULL; + } + + pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data), + GFP_KERNEL); + if (!pdata) + return NULL; + + /* set SoC specific options */ + pdata->flcmncr_val = config->flcmncr_val; + pdata->has_hwecc = config->has_hwecc; + pdata->use_holden = config->use_holden; + + return pdata; +} + +static int flctl_probe(struct platform_device *pdev) +{ + struct resource *res; + struct sh_flctl *flctl; + struct mtd_info *flctl_mtd; + struct nand_chip *nand; + struct sh_flctl_platform_data *pdata; + int ret; + int irq; + + flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL); + if (!flctl) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + flctl->reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(flctl->reg)) + return PTR_ERR(flctl->reg); + flctl->fifo = res->start + 0x24; /* FLDTFIFO */ + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get flste irq data: %d\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED, + "flste", flctl); + if (ret) { + dev_err(&pdev->dev, "request interrupt failed.\n"); + return ret; + } + + if (pdev->dev.of_node) + pdata = flctl_parse_dt(&pdev->dev); + else + pdata = dev_get_platdata(&pdev->dev); + + if (!pdata) { + dev_err(&pdev->dev, "no setup data defined\n"); + return -EINVAL; + } + + platform_set_drvdata(pdev, flctl); + nand = &flctl->chip; + flctl_mtd = nand_to_mtd(nand); + nand_set_flash_node(nand, pdev->dev.of_node); + flctl_mtd->dev.parent = &pdev->dev; + flctl->pdev = pdev; + flctl->hwecc = pdata->has_hwecc; + flctl->holden = pdata->use_holden; + flctl->flcmncr_base = pdata->flcmncr_val; + flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE; + + /* Set address of hardware control function */ + /* 20 us command delay time */ + nand->chip_delay = 20; + + nand->read_byte = flctl_read_byte; + nand->read_word = flctl_read_word; + nand->write_buf = flctl_write_buf; + nand->read_buf = flctl_read_buf; + nand->select_chip = flctl_select_chip; + nand->cmdfunc = flctl_cmdfunc; + nand->onfi_set_features = nand_onfi_get_set_features_notsupp; + nand->onfi_get_features = nand_onfi_get_set_features_notsupp; + + if (pdata->flcmncr_val & SEL_16BIT) + nand->options |= NAND_BUSWIDTH_16; + + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + flctl_setup_dma(flctl); + + ret = nand_scan_ident(flctl_mtd, 1, NULL); + if (ret) + goto err_chip; + + if (nand->options & NAND_BUSWIDTH_16) { + /* + * NAND_BUSWIDTH_16 may have been set by nand_scan_ident(). + * Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign + * flctl->flcmncr_base to pdata->flcmncr_val. + */ + pdata->flcmncr_val |= SEL_16BIT; + flctl->flcmncr_base = pdata->flcmncr_val; + } + + ret = flctl_chip_init_tail(flctl_mtd); + if (ret) + goto err_chip; + + ret = nand_scan_tail(flctl_mtd); + if (ret) + goto err_chip; + + ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); + + return 0; + +err_chip: + flctl_release_dma(flctl); + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int flctl_remove(struct platform_device *pdev) +{ + struct sh_flctl *flctl = platform_get_drvdata(pdev); + + flctl_release_dma(flctl); + nand_release(nand_to_mtd(&flctl->chip)); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver flctl_driver = { + .remove = flctl_remove, + .driver = { + .name = "sh_flctl", + .of_match_table = of_match_ptr(of_flctl_match), + }, +}; + +module_platform_driver_probe(flctl_driver, flctl_probe); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yoshihiro Shimoda"); +MODULE_DESCRIPTION("SuperH FLCTL driver"); +MODULE_ALIAS("platform:sh_flctl"); diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c new file mode 100644 index 000000000000..e93df02c825e --- /dev/null +++ b/drivers/mtd/nand/raw/sharpsl.c @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2004 Richard Purdie + * Copyright (C) 2008 Dmitry Baryshkov + * + * Based on Sharp's NAND driver sharp_sl.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct sharpsl_nand { + struct nand_chip chip; + + void __iomem *io; +}; + +static inline struct sharpsl_nand *mtd_to_sharpsl(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct sharpsl_nand, chip); +} + +/* register offset */ +#define ECCLPLB 0x00 /* line parity 7 - 0 bit */ +#define ECCLPUB 0x04 /* line parity 15 - 8 bit */ +#define ECCCP 0x08 /* column parity 5 - 0 bit */ +#define ECCCNTR 0x0C /* ECC byte counter */ +#define ECCCLRR 0x10 /* cleare ECC */ +#define FLASHIO 0x14 /* Flash I/O */ +#define FLASHCTL 0x18 /* Flash Control */ + +/* Flash control bit */ +#define FLRYBY (1 << 5) +#define FLCE1 (1 << 4) +#define FLWP (1 << 3) +#define FLALE (1 << 2) +#define FLCLE (1 << 1) +#define FLCE0 (1 << 0) + +/* + * hardware specific access to control-lines + * ctrl: + * NAND_CNE: bit 0 -> ! bit 0 & 4 + * NAND_CLE: bit 1 -> bit 1 + * NAND_ALE: bit 2 -> bit 2 + * + */ +static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + if (ctrl & NAND_CTRL_CHANGE) { + unsigned char bits = ctrl & 0x07; + + bits |= (ctrl & 0x01) << 4; + + bits ^= 0x11; + + writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL); + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); +} + +static int sharpsl_nand_dev_ready(struct mtd_info *mtd) +{ + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0); +} + +static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + writeb(0, sharpsl->io + ECCCLRR); +} + +static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) +{ + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + ecc_code[0] = ~readb(sharpsl->io + ECCLPUB); + ecc_code[1] = ~readb(sharpsl->io + ECCLPLB); + ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03; + return readb(sharpsl->io + ECCCNTR) != 0; +} + +/* + * Main initialization routine + */ +static int sharpsl_nand_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct resource *r; + int err = 0; + struct sharpsl_nand *sharpsl; + struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev); + + if (!data) { + dev_err(&pdev->dev, "no platform data!\n"); + return -EINVAL; + } + + /* Allocate memory for MTD device structure and private data */ + sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL); + if (!sharpsl) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no io memory resource defined!\n"); + err = -ENODEV; + goto err_get_res; + } + + /* map physical address */ + sharpsl->io = ioremap(r->start, resource_size(r)); + if (!sharpsl->io) { + dev_err(&pdev->dev, "ioremap to access Sharp SL NAND chip failed\n"); + err = -EIO; + goto err_ioremap; + } + + /* Get pointer to private data */ + this = (struct nand_chip *)(&sharpsl->chip); + + /* Link the private data with the MTD structure */ + mtd = nand_to_mtd(this); + mtd->dev.parent = &pdev->dev; + mtd_set_ooblayout(mtd, data->ecc_layout); + + platform_set_drvdata(pdev, sharpsl); + + /* + * PXA initialize + */ + writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL); + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = sharpsl->io + FLASHIO; + this->IO_ADDR_W = sharpsl->io + FLASHIO; + /* Set address of hardware control function */ + this->cmd_ctrl = sharpsl_nand_hwcontrol; + this->dev_ready = sharpsl_nand_dev_ready; + /* 15 us command delay time */ + this->chip_delay = 15; + /* set eccmode using hardware ECC */ + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 256; + this->ecc.bytes = 3; + this->ecc.strength = 1; + this->badblock_pattern = data->badblock_pattern; + this->ecc.hwctl = sharpsl_nand_enable_hwecc; + this->ecc.calculate = sharpsl_nand_calculate_ecc; + this->ecc.correct = nand_correct_data; + + /* Scan to find existence of the device */ + err = nand_scan(mtd, 1); + if (err) + goto err_scan; + + /* Register the partitions */ + mtd->name = "sharpsl-nand"; + + err = mtd_device_parse_register(mtd, data->part_parsers, NULL, + data->partitions, data->nr_partitions); + if (err) + goto err_add; + + /* Return happy */ + return 0; + +err_add: + nand_release(mtd); + +err_scan: + iounmap(sharpsl->io); +err_ioremap: +err_get_res: + kfree(sharpsl); + return err; +} + +/* + * Clean up routine + */ +static int sharpsl_nand_remove(struct platform_device *pdev) +{ + struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); + + /* Release resources, unregister device */ + nand_release(nand_to_mtd(&sharpsl->chip)); + + iounmap(sharpsl->io); + + /* Free the MTD device structure */ + kfree(sharpsl); + + return 0; +} + +static struct platform_driver sharpsl_nand_driver = { + .driver = { + .name = "sharpsl-nand", + }, + .probe = sharpsl_nand_probe, + .remove = sharpsl_nand_remove, +}; + +module_platform_driver(sharpsl_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series"); diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c new file mode 100644 index 000000000000..c378705c6e2b --- /dev/null +++ b/drivers/mtd/nand/raw/sm_common.c @@ -0,0 +1,202 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * Common routines & support for xD format + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include "sm_common.h" + +static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + oobregion->length = 3; + oobregion->offset = ((section + 1) * 8) - 3; + + return 0; +} + +static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + switch (section) { + case 0: + /* reserved */ + oobregion->offset = 0; + oobregion->length = 4; + break; + case 1: + /* LBA1 */ + oobregion->offset = 6; + oobregion->length = 2; + break; + case 2: + /* LBA2 */ + oobregion->offset = 11; + oobregion->length = 2; + break; + default: + return -ERANGE; + } + + return 0; +} + +static const struct mtd_ooblayout_ops oob_sm_ops = { + .ecc = oob_sm_ooblayout_ecc, + .free = oob_sm_ooblayout_free, +}; + +/* NOTE: This layout is is not compatabable with SmartMedia, */ +/* because the 256 byte devices have page depenent oob layout */ +/* However it does preserve the bad block markers */ +/* If you use smftl, it will bypass this and work correctly */ +/* If you not, then you break SmartMedia compliance anyway */ + +static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->length = 3; + oobregion->offset = 0; + + return 0; +} + +static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + switch (section) { + case 0: + /* reserved */ + oobregion->offset = 3; + oobregion->length = 2; + break; + case 1: + /* LBA1 */ + oobregion->offset = 6; + oobregion->length = 2; + break; + default: + return -ERANGE; + } + + return 0; +} + +static const struct mtd_ooblayout_ops oob_sm_small_ops = { + .ecc = oob_sm_small_ooblayout_ecc, + .free = oob_sm_small_ooblayout_free, +}; + +static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_oob_ops ops; + struct sm_oob oob; + int ret; + + memset(&oob, -1, SM_OOB_SIZE); + oob.block_status = 0x0F; + + /* As long as this function is called on erase block boundaries + it will work correctly for 256 byte nand */ + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = (void *)&oob; + ops.datbuf = NULL; + + + ret = mtd_write_oob(mtd, ofs, &ops); + if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { + printk(KERN_NOTICE + "sm_common: can't mark sector at %i as bad\n", + (int)ofs); + return -EIO; + } + + return 0; +} + +static struct nand_flash_dev nand_smartmedia_flash_ids[] = { + LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM", 0x5d, 2, SZ_8K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 4MiB 3,3V", 0xe3, 4, SZ_8K, 0), + LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V", 0xe5, 4, SZ_8K, 0), + LEGACY_ID_NAND("SmartMedia 4MiB 5V", 0x6b, 4, SZ_8K, 0), + LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM", 0xd5, 4, SZ_8K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 8MiB 3,3V", 0xe6, 8, SZ_8K, 0), + LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM", 0xd6, 8, SZ_8K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 16MiB 3,3V", 0x73, 16, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM", 0x57, 16, SZ_16K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 32MiB 3,3V", 0x75, 32, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM", 0x58, 32, SZ_16K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 64MiB 3,3V", 0x76, 64, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM", 0xd9, 64, SZ_16K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 128MiB 3,3V", 0x79, 128, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM), + LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V", 0x71, 256, SZ_16K, 0), + LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM), + {NULL} +}; + +static struct nand_flash_dev nand_xd_flash_ids[] = { + LEGACY_ID_NAND("xD 16MiB 3,3V", 0x73, 16, SZ_16K, 0), + LEGACY_ID_NAND("xD 32MiB 3,3V", 0x75, 32, SZ_16K, 0), + LEGACY_ID_NAND("xD 64MiB 3,3V", 0x76, 64, SZ_16K, 0), + LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128, SZ_16K, 0), + LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256, SZ_16K, NAND_BROKEN_XD), + LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512, SZ_16K, NAND_BROKEN_XD), + LEGACY_ID_NAND("xD 1GiB 3,3V", 0xd3, 1024, SZ_16K, NAND_BROKEN_XD), + LEGACY_ID_NAND("xD 2GiB 3,3V", 0xd5, 2048, SZ_16K, NAND_BROKEN_XD), + {NULL} +}; + +int sm_register_device(struct mtd_info *mtd, int smartmedia) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret; + + chip->options |= NAND_SKIP_BBTSCAN; + + /* Scan for card properties */ + ret = nand_scan_ident(mtd, 1, smartmedia ? + nand_smartmedia_flash_ids : nand_xd_flash_ids); + + if (ret) + return ret; + + /* Bad block marker position */ + chip->badblockpos = 0x05; + chip->badblockbits = 7; + chip->block_markbad = sm_block_markbad; + + /* ECC layout */ + if (mtd->writesize == SM_SECTOR_SIZE) + mtd_set_ooblayout(mtd, &oob_sm_ops); + else if (mtd->writesize == SM_SMALL_PAGE) + mtd_set_ooblayout(mtd, &oob_sm_small_ops); + else + return -ENODEV; + + ret = nand_scan_tail(mtd); + + if (ret) + return ret; + + return mtd_device_register(mtd, NULL, 0); +} +EXPORT_SYMBOL_GPL(sm_register_device); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky "); +MODULE_DESCRIPTION("Common SmartMedia/xD functions"); diff --git a/drivers/mtd/nand/raw/sm_common.h b/drivers/mtd/nand/raw/sm_common.h new file mode 100644 index 000000000000..1581671b05ae --- /dev/null +++ b/drivers/mtd/nand/raw/sm_common.h @@ -0,0 +1,61 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * Common routines & support for SmartMedia/xD format + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include + +/* Full oob structure as written on the flash */ +struct sm_oob { + uint32_t reserved; + uint8_t data_status; + uint8_t block_status; + uint8_t lba_copy1[2]; + uint8_t ecc2[3]; + uint8_t lba_copy2[2]; + uint8_t ecc1[3]; +} __packed; + + +/* one sector is always 512 bytes, but it can consist of two nand pages */ +#define SM_SECTOR_SIZE 512 + +/* oob area is also 16 bytes, but might be from two pages */ +#define SM_OOB_SIZE 16 + +/* This is maximum zone size, and all devices that have more that one zone + have this size */ +#define SM_MAX_ZONE_SIZE 1024 + +/* support for small page nand */ +#define SM_SMALL_PAGE 256 +#define SM_SMALL_OOB_SIZE 8 + + +int sm_register_device(struct mtd_info *mtd, int smartmedia); + + +static inline int sm_sector_valid(struct sm_oob *oob) +{ + return hweight16(oob->data_status) >= 5; +} + +static inline int sm_block_valid(struct sm_oob *oob) +{ + return hweight16(oob->block_status) >= 7; +} + +static inline int sm_block_erased(struct sm_oob *oob) +{ + static const uint32_t erased_pattern[4] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + + /* First test for erased block */ + if (!memcmp(oob, erased_pattern, sizeof(*oob))) + return 1; + return 0; +} diff --git a/drivers/mtd/nand/raw/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c new file mode 100644 index 000000000000..9824a9923583 --- /dev/null +++ b/drivers/mtd/nand/raw/socrates_nand.c @@ -0,0 +1,241 @@ +/* + * Copyright © 2008 Ilya Yanok, Emcraft Systems + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FPGA_NAND_CMD_MASK (0x7 << 28) +#define FPGA_NAND_CMD_COMMAND (0x0 << 28) +#define FPGA_NAND_CMD_ADDR (0x1 << 28) +#define FPGA_NAND_CMD_READ (0x2 << 28) +#define FPGA_NAND_CMD_WRITE (0x3 << 28) +#define FPGA_NAND_BUSY (0x1 << 15) +#define FPGA_NAND_ENABLE (0x1 << 31) +#define FPGA_NAND_DATA_SHIFT 16 + +struct socrates_nand_host { + struct nand_chip nand_chip; + void __iomem *io_base; + struct device *dev; +}; + +/** + * socrates_nand_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void socrates_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + int i; + struct nand_chip *this = mtd_to_nand(mtd); + struct socrates_nand_host *host = nand_get_controller_data(this); + + for (i = 0; i < len; i++) { + out_be32(host->io_base, FPGA_NAND_ENABLE | + FPGA_NAND_CMD_WRITE | + (buf[i] << FPGA_NAND_DATA_SHIFT)); + } +} + +/** + * socrates_nand_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *this = mtd_to_nand(mtd); + struct socrates_nand_host *host = nand_get_controller_data(this); + uint32_t val; + + val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; + + out_be32(host->io_base, val); + for (i = 0; i < len; i++) { + buf[i] = (in_be32(host->io_base) >> + FPGA_NAND_DATA_SHIFT) & 0xff; + } +} + +/** + * socrates_nand_read_byte - read one byte from the chip + * @mtd: MTD device structure + */ +static uint8_t socrates_nand_read_byte(struct mtd_info *mtd) +{ + uint8_t byte; + socrates_nand_read_buf(mtd, &byte, sizeof(byte)); + return byte; +} + +/** + * socrates_nand_read_word - read one word from the chip + * @mtd: MTD device structure + */ +static uint16_t socrates_nand_read_word(struct mtd_info *mtd) +{ + uint16_t word; + socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word)); + return word; +} + +/* + * Hardware specific access to control-lines + */ +static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct socrates_nand_host *host = nand_get_controller_data(nand_chip); + uint32_t val; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + val = FPGA_NAND_CMD_COMMAND; + else + val = FPGA_NAND_CMD_ADDR; + + if (ctrl & NAND_NCE) + val |= FPGA_NAND_ENABLE; + + val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; + + out_be32(host->io_base, val); +} + +/* + * Read the Device Ready pin. + */ +static int socrates_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct socrates_nand_host *host = nand_get_controller_data(nand_chip); + + if (in_be32(host->io_base) & FPGA_NAND_BUSY) + return 0; /* busy */ + return 1; +} + +/* + * Probe for the NAND device. + */ +static int socrates_nand_probe(struct platform_device *ofdev) +{ + struct socrates_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + int res; + + /* Allocate memory for the device structure (and zero it) */ + host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + host->io_base = of_iomap(ofdev->dev.of_node, 0); + if (host->io_base == NULL) { + dev_err(&ofdev->dev, "ioremap failed\n"); + return -EIO; + } + + nand_chip = &host->nand_chip; + mtd = nand_to_mtd(nand_chip); + host->dev = &ofdev->dev; + + /* link the private data structures */ + nand_set_controller_data(nand_chip, host); + nand_set_flash_node(nand_chip, ofdev->dev.of_node); + mtd->name = "socrates_nand"; + mtd->dev.parent = &ofdev->dev; + + /*should never be accessed directly */ + nand_chip->IO_ADDR_R = (void *)0xdeadbeef; + nand_chip->IO_ADDR_W = (void *)0xdeadbeef; + + nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl; + nand_chip->read_byte = socrates_nand_read_byte; + nand_chip->read_word = socrates_nand_read_word; + nand_chip->write_buf = socrates_nand_write_buf; + nand_chip->read_buf = socrates_nand_read_buf; + nand_chip->dev_ready = socrates_nand_device_ready; + + nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ + nand_chip->ecc.algo = NAND_ECC_HAMMING; + + /* TODO: I have no idea what real delay is. */ + nand_chip->chip_delay = 20; /* 20us command delay time */ + + dev_set_drvdata(&ofdev->dev, host); + + res = nand_scan(mtd, 1); + if (res) + goto out; + + res = mtd_device_register(mtd, NULL, 0); + if (!res) + return res; + + nand_release(mtd); + +out: + iounmap(host->io_base); + return res; +} + +/* + * Remove a NAND device. + */ +static int socrates_nand_remove(struct platform_device *ofdev) +{ + struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); + struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); + + nand_release(mtd); + + iounmap(host->io_base); + + return 0; +} + +static const struct of_device_id socrates_nand_match[] = +{ + { + .compatible = "abb,socrates-nand", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, socrates_nand_match); + +static struct platform_driver socrates_nand_driver = { + .driver = { + .name = "socrates_nand", + .of_match_table = socrates_nand_match, + }, + .probe = socrates_nand_probe, + .remove = socrates_nand_remove, +}; + +module_platform_driver(socrates_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ilya Yanok"); +MODULE_DESCRIPTION("NAND driver for Socrates board"); diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c new file mode 100644 index 000000000000..f5a55c63935c --- /dev/null +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -0,0 +1,2321 @@ +/* + * Copyright (C) 2013 Boris BREZILLON + * + * Derived from: + * https://github.com/yuq/sunxi-nfc-mtd + * Copyright (C) 2013 Qiang Yu + * + * https://github.com/hno/Allwinner-Info + * Copyright (C) 2013 Henrik Nordström + * + * Copyright (C) 2013 Dmitriy B. + * Copyright (C) 2013 Sergey Lapin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NFC_REG_CTL 0x0000 +#define NFC_REG_ST 0x0004 +#define NFC_REG_INT 0x0008 +#define NFC_REG_TIMING_CTL 0x000C +#define NFC_REG_TIMING_CFG 0x0010 +#define NFC_REG_ADDR_LOW 0x0014 +#define NFC_REG_ADDR_HIGH 0x0018 +#define NFC_REG_SECTOR_NUM 0x001C +#define NFC_REG_CNT 0x0020 +#define NFC_REG_CMD 0x0024 +#define NFC_REG_RCMD_SET 0x0028 +#define NFC_REG_WCMD_SET 0x002C +#define NFC_REG_IO_DATA 0x0030 +#define NFC_REG_ECC_CTL 0x0034 +#define NFC_REG_ECC_ST 0x0038 +#define NFC_REG_DEBUG 0x003C +#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) +#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) +#define NFC_REG_SPARE_AREA 0x00A0 +#define NFC_REG_PAT_ID 0x00A4 +#define NFC_RAM0_BASE 0x0400 +#define NFC_RAM1_BASE 0x0800 + +/* define bit use in NFC_CTL */ +#define NFC_EN BIT(0) +#define NFC_RESET BIT(1) +#define NFC_BUS_WIDTH_MSK BIT(2) +#define NFC_BUS_WIDTH_8 (0 << 2) +#define NFC_BUS_WIDTH_16 (1 << 2) +#define NFC_RB_SEL_MSK BIT(3) +#define NFC_RB_SEL(x) ((x) << 3) +#define NFC_CE_SEL_MSK GENMASK(26, 24) +#define NFC_CE_SEL(x) ((x) << 24) +#define NFC_CE_CTL BIT(6) +#define NFC_PAGE_SHIFT_MSK GENMASK(11, 8) +#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) +#define NFC_SAM BIT(12) +#define NFC_RAM_METHOD BIT(14) +#define NFC_DEBUG_CTL BIT(31) + +/* define bit use in NFC_ST */ +#define NFC_RB_B2R BIT(0) +#define NFC_CMD_INT_FLAG BIT(1) +#define NFC_DMA_INT_FLAG BIT(2) +#define NFC_CMD_FIFO_STATUS BIT(3) +#define NFC_STA BIT(4) +#define NFC_NATCH_INT_FLAG BIT(5) +#define NFC_RB_STATE(x) BIT(x + 8) + +/* define bit use in NFC_INT */ +#define NFC_B2R_INT_ENABLE BIT(0) +#define NFC_CMD_INT_ENABLE BIT(1) +#define NFC_DMA_INT_ENABLE BIT(2) +#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ + NFC_CMD_INT_ENABLE | \ + NFC_DMA_INT_ENABLE) + +/* define bit use in NFC_TIMING_CTL */ +#define NFC_TIMING_CTL_EDO BIT(8) + +/* define NFC_TIMING_CFG register layout */ +#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ + (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ + (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ + (((tCAD) & 0x7) << 8)) + +/* define bit use in NFC_CMD */ +#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0) +#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) +#define NFC_CMD(x) (x) +#define NFC_ADR_NUM_MSK GENMASK(18, 16) +#define NFC_ADR_NUM(x) (((x) - 1) << 16) +#define NFC_SEND_ADR BIT(19) +#define NFC_ACCESS_DIR BIT(20) +#define NFC_DATA_TRANS BIT(21) +#define NFC_SEND_CMD1 BIT(22) +#define NFC_WAIT_FLAG BIT(23) +#define NFC_SEND_CMD2 BIT(24) +#define NFC_SEQ BIT(25) +#define NFC_DATA_SWAP_METHOD BIT(26) +#define NFC_ROW_AUTO_INC BIT(27) +#define NFC_SEND_CMD3 BIT(28) +#define NFC_SEND_CMD4 BIT(29) +#define NFC_CMD_TYPE_MSK GENMASK(31, 30) +#define NFC_NORMAL_OP (0 << 30) +#define NFC_ECC_OP (1 << 30) +#define NFC_PAGE_OP (2 << 30) + +/* define bit use in NFC_RCMD_SET */ +#define NFC_READ_CMD_MSK GENMASK(7, 0) +#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8) +#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16) + +/* define bit use in NFC_WCMD_SET */ +#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0) +#define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8) +#define NFC_READ_CMD0_MSK GENMASK(23, 16) +#define NFC_READ_CMD1_MSK GENMASK(31, 24) + +/* define bit use in NFC_ECC_CTL */ +#define NFC_ECC_EN BIT(0) +#define NFC_ECC_PIPELINE BIT(3) +#define NFC_ECC_EXCEPTION BIT(4) +#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) +#define NFC_ECC_BLOCK_512 BIT(5) +#define NFC_RANDOM_EN BIT(9) +#define NFC_RANDOM_DIRECTION BIT(10) +#define NFC_ECC_MODE_MSK GENMASK(15, 12) +#define NFC_ECC_MODE(x) ((x) << 12) +#define NFC_RANDOM_SEED_MSK GENMASK(30, 16) +#define NFC_RANDOM_SEED(x) ((x) << 16) + +/* define bit use in NFC_ECC_ST */ +#define NFC_ECC_ERR(x) BIT(x) +#define NFC_ECC_ERR_MSK GENMASK(15, 0) +#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) +#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) + +#define NFC_DEFAULT_TIMEOUT_MS 1000 + +#define NFC_SRAM_SIZE 1024 + +#define NFC_MAX_CS 7 + +/* + * Ready/Busy detection type: describes the Ready/Busy detection modes + * + * @RB_NONE: no external detection available, rely on STATUS command + * and software timeouts + * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy + * pin of the NAND flash chip must be connected to one of the + * native NAND R/B pins (those which can be muxed to the NAND + * Controller) + * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy + * pin of the NAND flash chip must be connected to a GPIO capable + * pin. + */ +enum sunxi_nand_rb_type { + RB_NONE, + RB_NATIVE, + RB_GPIO, +}; + +/* + * Ready/Busy structure: stores information related to Ready/Busy detection + * + * @type: the Ready/Busy detection mode + * @info: information related to the R/B detection mode. Either a gpio + * id or a native R/B id (those supported by the NAND controller). + */ +struct sunxi_nand_rb { + enum sunxi_nand_rb_type type; + union { + int gpio; + int nativeid; + } info; +}; + +/* + * Chip Select structure: stores information related to NAND Chip Select + * + * @cs: the NAND CS id used to communicate with a NAND Chip + * @rb: the Ready/Busy description + */ +struct sunxi_nand_chip_sel { + u8 cs; + struct sunxi_nand_rb rb; +}; + +/* + * sunxi HW ECC infos: stores information related to HW ECC support + * + * @mode: the sunxi ECC mode field deduced from ECC requirements + */ +struct sunxi_nand_hw_ecc { + int mode; +}; + +/* + * NAND chip structure: stores NAND chip device related information + * + * @node: used to store NAND chips into a list + * @nand: base NAND chip structure + * @mtd: base MTD structure + * @clk_rate: clk_rate required for this NAND chip + * @timing_cfg TIMING_CFG register value for this NAND chip + * @selected: current active CS + * @nsels: number of CS lines required by the NAND chip + * @sels: array of CS lines descriptions + */ +struct sunxi_nand_chip { + struct list_head node; + struct nand_chip nand; + unsigned long clk_rate; + u32 timing_cfg; + u32 timing_ctl; + int selected; + int addr_cycles; + u32 addr[2]; + int cmd_cycles; + u8 cmd[2]; + int nsels; + struct sunxi_nand_chip_sel sels[0]; +}; + +static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) +{ + return container_of(nand, struct sunxi_nand_chip, nand); +} + +/* + * NAND Controller structure: stores sunxi NAND controller information + * + * @controller: base controller structure + * @dev: parent device (used to print error messages) + * @regs: NAND controller registers + * @ahb_clk: NAND Controller AHB clock + * @mod_clk: NAND Controller mod clock + * @assigned_cs: bitmask describing already assigned CS lines + * @clk_rate: NAND controller current clock rate + * @chips: a list containing all the NAND chips attached to + * this NAND controller + * @complete: a completion object used to wait for NAND + * controller events + */ +struct sunxi_nfc { + struct nand_hw_control controller; + struct device *dev; + void __iomem *regs; + struct clk *ahb_clk; + struct clk *mod_clk; + struct reset_control *reset; + unsigned long assigned_cs; + unsigned long clk_rate; + struct list_head chips; + struct completion complete; + struct dma_chan *dmac; +}; + +static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) +{ + return container_of(ctrl, struct sunxi_nfc, controller); +} + +static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id) +{ + struct sunxi_nfc *nfc = dev_id; + u32 st = readl(nfc->regs + NFC_REG_ST); + u32 ien = readl(nfc->regs + NFC_REG_INT); + + if (!(ien & st)) + return IRQ_NONE; + + if ((ien & st) == ien) + complete(&nfc->complete); + + writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST); + writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT); + + return IRQ_HANDLED; +} + +static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events, + bool use_polling, unsigned int timeout_ms) +{ + int ret; + + if (events & ~NFC_INT_MASK) + return -EINVAL; + + if (!timeout_ms) + timeout_ms = NFC_DEFAULT_TIMEOUT_MS; + + if (!use_polling) { + init_completion(&nfc->complete); + + writel(events, nfc->regs + NFC_REG_INT); + + ret = wait_for_completion_timeout(&nfc->complete, + msecs_to_jiffies(timeout_ms)); + if (!ret) + ret = -ETIMEDOUT; + else + ret = 0; + + writel(0, nfc->regs + NFC_REG_INT); + } else { + u32 status; + + ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, + (status & events) == events, 1, + timeout_ms * 1000); + } + + writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST); + + if (ret) + dev_err(nfc->dev, "wait interrupt timedout\n"); + + return ret; +} + +static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) +{ + u32 status; + int ret; + + ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, + !(status & NFC_CMD_FIFO_STATUS), 1, + NFC_DEFAULT_TIMEOUT_MS * 1000); + if (ret) + dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); + + return ret; +} + +static int sunxi_nfc_rst(struct sunxi_nfc *nfc) +{ + u32 ctl; + int ret; + + writel(0, nfc->regs + NFC_REG_ECC_CTL); + writel(NFC_RESET, nfc->regs + NFC_REG_CTL); + + ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl, + !(ctl & NFC_RESET), 1, + NFC_DEFAULT_TIMEOUT_MS * 1000); + if (ret) + dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); + + return ret; +} + +static int sunxi_nfc_dma_op_prepare(struct mtd_info *mtd, const void *buf, + int chunksize, int nchunks, + enum dma_data_direction ddir, + struct scatterlist *sg) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct dma_async_tx_descriptor *dmad; + enum dma_transfer_direction tdir; + dma_cookie_t dmat; + int ret; + + if (ddir == DMA_FROM_DEVICE) + tdir = DMA_DEV_TO_MEM; + else + tdir = DMA_MEM_TO_DEV; + + sg_init_one(sg, buf, nchunks * chunksize); + ret = dma_map_sg(nfc->dev, sg, 1, ddir); + if (!ret) + return -ENOMEM; + + dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); + if (!dmad) { + ret = -EINVAL; + goto err_unmap_buf; + } + + writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, + nfc->regs + NFC_REG_CTL); + writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); + writel(chunksize, nfc->regs + NFC_REG_CNT); + dmat = dmaengine_submit(dmad); + + ret = dma_submit_error(dmat); + if (ret) + goto err_clr_dma_flag; + + return 0; + +err_clr_dma_flag: + writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, + nfc->regs + NFC_REG_CTL); + +err_unmap_buf: + dma_unmap_sg(nfc->dev, sg, 1, ddir); + return ret; +} + +static void sunxi_nfc_dma_op_cleanup(struct mtd_info *mtd, + enum dma_data_direction ddir, + struct scatterlist *sg) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + dma_unmap_sg(nfc->dev, sg, 1, ddir); + writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, + nfc->regs + NFC_REG_CTL); +} + +static int sunxi_nfc_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + struct sunxi_nand_rb *rb; + int ret; + + if (sunxi_nand->selected < 0) + return 0; + + rb = &sunxi_nand->sels[sunxi_nand->selected].rb; + + switch (rb->type) { + case RB_NATIVE: + ret = !!(readl(nfc->regs + NFC_REG_ST) & + NFC_RB_STATE(rb->info.nativeid)); + break; + case RB_GPIO: + ret = gpio_get_value(rb->info.gpio); + break; + case RB_NONE: + default: + ret = 0; + dev_err(nfc->dev, "cannot check R/B NAND status!\n"); + break; + } + + return ret; +} + +static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + struct sunxi_nand_chip_sel *sel; + u32 ctl; + + if (chip > 0 && chip >= sunxi_nand->nsels) + return; + + if (chip == sunxi_nand->selected) + return; + + ctl = readl(nfc->regs + NFC_REG_CTL) & + ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); + + if (chip >= 0) { + sel = &sunxi_nand->sels[chip]; + + ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | + NFC_PAGE_SHIFT(nand->page_shift); + if (sel->rb.type == RB_NONE) { + nand->dev_ready = NULL; + } else { + nand->dev_ready = sunxi_nfc_dev_ready; + if (sel->rb.type == RB_NATIVE) + ctl |= NFC_RB_SEL(sel->rb.info.nativeid); + } + + writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); + + if (nfc->clk_rate != sunxi_nand->clk_rate) { + clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate); + nfc->clk_rate = sunxi_nand->clk_rate; + } + } + + writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); + writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); + writel(ctl, nfc->regs + NFC_REG_CTL); + + sunxi_nand->selected = chip; +} + +static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + int ret; + int cnt; + int offs = 0; + u32 tmp; + + while (len > offs) { + bool poll = false; + + cnt = min(len - offs, NFC_SRAM_SIZE); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + break; + + writel(cnt, nfc->regs + NFC_REG_CNT); + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; + writel(tmp, nfc->regs + NFC_REG_CMD); + + /* Arbitrary limit for polling mode */ + if (cnt < 64) + poll = true; + + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); + if (ret) + break; + + if (buf) + memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, + cnt); + offs += cnt; + } +} + +static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + int ret; + int cnt; + int offs = 0; + u32 tmp; + + while (len > offs) { + bool poll = false; + + cnt = min(len - offs, NFC_SRAM_SIZE); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + break; + + writel(cnt, nfc->regs + NFC_REG_CNT); + memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt); + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | + NFC_ACCESS_DIR; + writel(tmp, nfc->regs + NFC_REG_CMD); + + /* Arbitrary limit for polling mode */ + if (cnt < 64) + poll = true; + + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); + if (ret) + break; + + offs += cnt; + } +} + +static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) +{ + uint8_t ret; + + sunxi_nfc_read_buf(mtd, &ret, 1); + + return ret; +} + +static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + int ret; + + if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) && + !(ctrl & (NAND_CLE | NAND_ALE))) { + u32 cmd = 0; + + if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles) + return; + + if (sunxi_nand->cmd_cycles--) + cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0]; + + if (sunxi_nand->cmd_cycles--) { + cmd |= NFC_SEND_CMD2; + writel(sunxi_nand->cmd[1], + nfc->regs + NFC_REG_RCMD_SET); + } + + sunxi_nand->cmd_cycles = 0; + + if (sunxi_nand->addr_cycles) { + cmd |= NFC_SEND_ADR | + NFC_ADR_NUM(sunxi_nand->addr_cycles); + writel(sunxi_nand->addr[0], + nfc->regs + NFC_REG_ADDR_LOW); + } + + if (sunxi_nand->addr_cycles > 4) + writel(sunxi_nand->addr[1], + nfc->regs + NFC_REG_ADDR_HIGH); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return; + + writel(cmd, nfc->regs + NFC_REG_CMD); + sunxi_nand->addr[0] = 0; + sunxi_nand->addr[1] = 0; + sunxi_nand->addr_cycles = 0; + sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); + } + + if (ctrl & NAND_CLE) { + sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat; + } else if (ctrl & NAND_ALE) { + sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |= + dat << ((sunxi_nand->addr_cycles % 4) * 8); + sunxi_nand->addr_cycles++; + } +} + +/* These seed values have been extracted from Allwinner's BSP */ +static const u16 sunxi_nfc_randomizer_page_seeds[] = { + 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, + 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, + 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, + 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, + 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, + 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, + 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, + 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, + 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, + 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, + 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, + 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, + 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, + 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, + 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, + 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, +}; + +/* + * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds + * have been generated using + * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what + * the randomizer engine does internally before de/scrambling OOB data. + * + * Those tables are statically defined to avoid calculating randomizer state + * at runtime. + */ +static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = { + 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64, + 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409, + 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617, + 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d, + 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91, + 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d, + 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab, + 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8, + 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8, + 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b, + 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5, + 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a, + 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891, + 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36, + 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd, + 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0, +}; + +static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = { + 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6, + 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982, + 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9, + 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07, + 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e, + 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2, + 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c, + 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f, + 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc, + 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e, + 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8, + 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68, + 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d, + 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179, + 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601, + 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd, +}; + +static u16 sunxi_nfc_randomizer_step(u16 state, int count) +{ + state &= 0x7fff; + + /* + * This loop is just a simple implementation of a Fibonacci LFSR using + * the x16 + x15 + 1 polynomial. + */ + while (count--) + state = ((state >> 1) | + (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc) +{ + const u16 *seeds = sunxi_nfc_randomizer_page_seeds; + int mod = mtd_div_by_ws(mtd->erasesize, mtd); + + if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) + mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); + + if (ecc) { + if (mtd->ecc_step_size == 512) + seeds = sunxi_nfc_randomizer_ecc512_seeds; + else + seeds = sunxi_nfc_randomizer_ecc1024_seeds; + } + + return seeds[page % mod]; +} + +static void sunxi_nfc_randomizer_config(struct mtd_info *mtd, + int page, bool ecc) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + u16 state; + + if (!(nand->options & NAND_NEED_SCRAMBLING)) + return; + + ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + state = sunxi_nfc_randomizer_state(mtd, page, ecc); + ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; + writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + if (!(nand->options & NAND_NEED_SCRAMBLING)) + return; + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + if (!(nand->options & NAND_NEED_SCRAMBLING)) + return; + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm) +{ + u16 state = sunxi_nfc_randomizer_state(mtd, page, true); + + bbm[0] ^= state; + bbm[1] ^= sunxi_nfc_randomizer_step(state, 8); +} + +static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len, + bool ecc, int page) +{ + sunxi_nfc_randomizer_config(mtd, page, ecc); + sunxi_nfc_randomizer_enable(mtd); + sunxi_nfc_write_buf(mtd, buf, len); + sunxi_nfc_randomizer_disable(mtd); +} + +static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf, + int len, bool ecc, int page) +{ + sunxi_nfc_randomizer_config(mtd, page, ecc); + sunxi_nfc_randomizer_enable(mtd); + sunxi_nfc_read_buf(mtd, buf, len); + sunxi_nfc_randomizer_disable(mtd); +} + +static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_hw_ecc *data = nand->ecc.priv; + u32 ecc_ctl; + + ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | + NFC_ECC_BLOCK_SIZE_MSK); + ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION | + NFC_ECC_PIPELINE; + + if (nand->ecc.size == 512) + ecc_ctl |= NFC_ECC_BLOCK_512; + + writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, + nfc->regs + NFC_REG_ECC_CTL); +} + +static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) +{ + buf[0] = user_data; + buf[1] = user_data >> 8; + buf[2] = user_data >> 16; + buf[3] = user_data >> 24; +} + +static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) +{ + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + +static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob, + int step, bool bbm, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), + oob); + + /* De-randomize the Bad Block Marker. */ + if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) + sunxi_nfc_randomize_bbm(mtd, page, oob); +} + +static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd, + const u8 *oob, int step, + bool bbm, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + u8 user_data[4]; + + /* Randomize the Bad Block Marker. */ + if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { + memcpy(user_data, oob, sizeof(user_data)); + sunxi_nfc_randomize_bbm(mtd, page, user_data); + oob = user_data; + } + + writel(sunxi_nfc_buf_to_user_data(oob), + nfc->regs + NFC_REG_USER_DATA(step)); +} + +static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd, + unsigned int *max_bitflips, int ret) +{ + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + *max_bitflips = max_t(unsigned int, *max_bitflips, ret); + } +} + +static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob, + int step, u32 status, bool *erased) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + u32 tmp; + + *erased = false; + + if (status & NFC_ECC_ERR(step)) + return -EBADMSG; + + if (status & NFC_ECC_PAT_FOUND(step)) { + u8 pattern; + + if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { + pattern = 0x0; + } else { + pattern = 0xff; + *erased = true; + } + + if (data) + memset(data, pattern, ecc->size); + + if (oob) + memset(oob, pattern, ecc->bytes + 4); + + return 0; + } + + tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); + + return NFC_ECC_ERR_CNT(step, tmp); +} + +static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, + u8 *data, int data_off, + u8 *oob, int oob_off, + int *cur_off, + unsigned int *max_bitflips, + bool bbm, bool oob_required, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + int raw_mode = 0; + bool erased; + int ret; + + if (*cur_off != data_off) + nand_change_read_column_op(nand, data_off, NULL, 0, false); + + sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page); + + if (data_off + ecc->size != oob_off) + nand_change_read_column_op(nand, oob_off, NULL, 0, false); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return ret; + + sunxi_nfc_randomizer_enable(mtd); + writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, + nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); + sunxi_nfc_randomizer_disable(mtd); + if (ret) + return ret; + + *cur_off = oob_off + ecc->bytes + 4; + + ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0, + readl(nfc->regs + NFC_REG_ECC_ST), + &erased); + if (erased) + return 1; + + if (ret < 0) { + /* + * Re-read the data with the randomizer disabled to identify + * bitflips in erased pages. + */ + if (nand->options & NAND_NEED_SCRAMBLING) + nand_change_read_column_op(nand, data_off, data, + ecc->size, false); + else + memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, + ecc->size); + + nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4, + false); + + ret = nand_check_erased_ecc_chunk(data, ecc->size, + oob, ecc->bytes + 4, + NULL, 0, ecc->strength); + if (ret >= 0) + raw_mode = 1; + } else { + memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); + + if (oob_required) { + nand_change_read_column_op(nand, oob_off, NULL, 0, + false); + sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, + true, page); + + sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0, + bbm, page); + } + } + + sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret); + + return raw_mode; +} + +static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, + u8 *oob, int *cur_off, + bool randomize, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &nand->ecc; + int offset = ((ecc->bytes + 4) * ecc->steps); + int len = mtd->oobsize - offset; + + if (len <= 0) + return; + + if (!cur_off || *cur_off != offset) + nand_change_read_column_op(nand, mtd->writesize, NULL, 0, + false); + + if (!randomize) + sunxi_nfc_read_buf(mtd, oob + offset, len); + else + sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len, + false, page); + + if (cur_off) + *cur_off = mtd->oobsize + mtd->writesize; +} + +static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf, + int oob_required, int page, + int nchunks) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + bool randomized = nand->options & NAND_NEED_SCRAMBLING; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + unsigned int max_bitflips = 0; + int ret, i, raw_mode = 0; + struct scatterlist sg; + u32 status; + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return ret; + + ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, nchunks, + DMA_FROM_DEVICE, &sg); + if (ret) + return ret; + + sunxi_nfc_hw_ecc_enable(mtd); + sunxi_nfc_randomizer_config(mtd, page, false); + sunxi_nfc_randomizer_enable(mtd); + + writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) | + NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET); + + dma_async_issue_pending(nfc->dmac); + + writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS, + nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); + if (ret) + dmaengine_terminate_all(nfc->dmac); + + sunxi_nfc_randomizer_disable(mtd); + sunxi_nfc_hw_ecc_disable(mtd); + + sunxi_nfc_dma_op_cleanup(mtd, DMA_FROM_DEVICE, &sg); + + if (ret) + return ret; + + status = readl(nfc->regs + NFC_REG_ECC_ST); + + for (i = 0; i < nchunks; i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + u8 *data = buf + data_off; + u8 *oob = nand->oob_poi + oob_off; + bool erased; + + ret = sunxi_nfc_hw_ecc_correct(mtd, randomized ? data : NULL, + oob_required ? oob : NULL, + i, status, &erased); + + /* ECC errors are handled in the second loop. */ + if (ret < 0) + continue; + + if (oob_required && !erased) { + /* TODO: use DMA to retrieve OOB */ + nand_change_read_column_op(nand, + mtd->writesize + oob_off, + oob, ecc->bytes + 4, false); + + sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, i, + !i, page); + } + + if (erased) + raw_mode = 1; + + sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret); + } + + if (status & NFC_ECC_ERR_MSK) { + for (i = 0; i < nchunks; i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + u8 *data = buf + data_off; + u8 *oob = nand->oob_poi + oob_off; + + if (!(status & NFC_ECC_ERR(i))) + continue; + + /* + * Re-read the data with the randomizer disabled to + * identify bitflips in erased pages. + * TODO: use DMA to read page in raw mode + */ + if (randomized) + nand_change_read_column_op(nand, data_off, + data, ecc->size, + false); + + /* TODO: use DMA to retrieve OOB */ + nand_change_read_column_op(nand, + mtd->writesize + oob_off, + oob, ecc->bytes + 4, false); + + ret = nand_check_erased_ecc_chunk(data, ecc->size, + oob, ecc->bytes + 4, + NULL, 0, + ecc->strength); + if (ret >= 0) + raw_mode = 1; + + sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret); + } + } + + if (oob_required) + sunxi_nfc_hw_ecc_read_extra_oob(mtd, nand->oob_poi, + NULL, !raw_mode, + page); + + return max_bitflips; +} + +static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, + const u8 *data, int data_off, + const u8 *oob, int oob_off, + int *cur_off, bool bbm, + int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + int ret; + + if (data_off != *cur_off) + nand_change_write_column_op(nand, data_off, NULL, 0, false); + + sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page); + + if (data_off + ecc->size != oob_off) + nand_change_write_column_op(nand, oob_off, NULL, 0, false); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return ret; + + sunxi_nfc_randomizer_enable(mtd); + sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page); + + writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | + NFC_ACCESS_DIR | NFC_ECC_OP, + nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); + sunxi_nfc_randomizer_disable(mtd); + if (ret) + return ret; + + *cur_off = oob_off + ecc->bytes + 4; + + return 0; +} + +static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, + u8 *oob, int *cur_off, + int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &nand->ecc; + int offset = ((ecc->bytes + 4) * ecc->steps); + int len = mtd->oobsize - offset; + + if (len <= 0) + return; + + if (!cur_off || *cur_off != offset) + nand_change_write_column_op(nand, offset + mtd->writesize, + NULL, 0, false); + + sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page); + + if (cur_off) + *cur_off = mtd->oobsize + mtd->writesize; +} + +static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + unsigned int max_bitflips = 0; + int ret, i, cur_off = 0; + bool raw_mode = false; + + nand_read_page_op(chip, page, 0, NULL, 0); + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = 0; i < ecc->steps; i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + u8 *data = buf + data_off; + u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, + oob_off + mtd->writesize, + &cur_off, &max_bitflips, + !i, oob_required, page); + if (ret < 0) + return ret; + else if (ret) + raw_mode = true; + } + + if (oob_required) + sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, + !raw_mode, page); + + sunxi_nfc_hw_ecc_disable(mtd); + + return max_bitflips; +} + +static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + int ret; + + nand_read_page_op(chip, page, 0, NULL, 0); + + ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, oob_required, page, + chip->ecc.steps); + if (ret >= 0) + return ret; + + /* Fallback to PIO mode */ + return sunxi_nfc_hw_ecc_read_page(mtd, chip, buf, oob_required, page); +} + +static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, + struct nand_chip *chip, + u32 data_offs, u32 readlen, + u8 *bufpoi, int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i, cur_off = 0; + unsigned int max_bitflips = 0; + + nand_read_page_op(chip, page, 0, NULL, 0); + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = data_offs / ecc->size; + i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + u8 *data = bufpoi + data_off; + u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, + oob, + oob_off + mtd->writesize, + &cur_off, &max_bitflips, !i, + false, page); + if (ret < 0) + return ret; + } + + sunxi_nfc_hw_ecc_disable(mtd); + + return max_bitflips; +} + +static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd, + struct nand_chip *chip, + u32 data_offs, u32 readlen, + u8 *buf, int page) +{ + int nchunks = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); + int ret; + + nand_read_page_op(chip, page, 0, NULL, 0); + + ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, false, page, nchunks); + if (ret >= 0) + return ret; + + /* Fallback to PIO mode */ + return sunxi_nfc_hw_ecc_read_subpage(mtd, chip, data_offs, readlen, + buf, page); +} + +static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i, cur_off = 0; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = 0; i < ecc->steps; i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + const u8 *data = buf + data_off; + const u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, + oob_off + mtd->writesize, + &cur_off, !i, page); + if (ret) + return ret; + } + + if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) + sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, + &cur_off, page); + + sunxi_nfc_hw_ecc_disable(mtd); + + return nand_prog_page_end_op(chip); +} + +static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, + struct nand_chip *chip, + u32 data_offs, u32 data_len, + const u8 *buf, int oob_required, + int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i, cur_off = 0; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = data_offs / ecc->size; + i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + const u8 *data = buf + data_off; + const u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, + oob_off + mtd->writesize, + &cur_off, !i, page); + if (ret) + return ret; + } + + sunxi_nfc_hw_ecc_disable(mtd); + + return nand_prog_page_end_op(chip); +} + +static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd, + struct nand_chip *chip, + const u8 *buf, + int oob_required, + int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + struct scatterlist sg; + int ret, i; + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return ret; + + ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, ecc->steps, + DMA_TO_DEVICE, &sg); + if (ret) + goto pio_fallback; + + for (i = 0; i < ecc->steps; i++) { + const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4)); + + sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, i, !i, page); + } + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + sunxi_nfc_hw_ecc_enable(mtd); + sunxi_nfc_randomizer_config(mtd, page, false); + sunxi_nfc_randomizer_enable(mtd); + + writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG, + nfc->regs + NFC_REG_RCMD_SET); + + dma_async_issue_pending(nfc->dmac); + + writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | + NFC_DATA_TRANS | NFC_ACCESS_DIR, + nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); + if (ret) + dmaengine_terminate_all(nfc->dmac); + + sunxi_nfc_randomizer_disable(mtd); + sunxi_nfc_hw_ecc_disable(mtd); + + sunxi_nfc_dma_op_cleanup(mtd, DMA_TO_DEVICE, &sg); + + if (ret) + return ret; + + if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) + /* TODO: use DMA to transfer extra OOB bytes ? */ + sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, + NULL, page); + + return nand_prog_page_end_op(chip); + +pio_fallback: + return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page); +} + +static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int oob_required, + int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + unsigned int max_bitflips = 0; + int ret, i, cur_off = 0; + bool raw_mode = false; + + nand_read_page_op(chip, page, 0, NULL, 0); + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = 0; i < ecc->steps; i++) { + int data_off = i * (ecc->size + ecc->bytes + 4); + int oob_off = data_off + ecc->size; + u8 *data = buf + (i * ecc->size); + u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); + + ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, + oob_off, &cur_off, + &max_bitflips, !i, + oob_required, + page); + if (ret < 0) + return ret; + else if (ret) + raw_mode = true; + } + + if (oob_required) + sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, + !raw_mode, page); + + sunxi_nfc_hw_ecc_disable(mtd); + + return max_bitflips; +} + +static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required, int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i, cur_off = 0; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = 0; i < ecc->steps; i++) { + int data_off = i * (ecc->size + ecc->bytes + 4); + int oob_off = data_off + ecc->size; + const u8 *data = buf + (i * ecc->size); + const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); + + ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, + oob, oob_off, &cur_off, + false, page); + if (ret) + return ret; + } + + if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) + sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, + &cur_off, page); + + sunxi_nfc_hw_ecc_disable(mtd); + + return nand_prog_page_end_op(chip); +} + +static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd, + struct nand_chip *chip, + int page) +{ + chip->pagebuf = -1; + + return chip->ecc.read_page(mtd, chip, chip->data_buf, 1, page); +} + +static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, + struct nand_chip *chip, + int page) +{ + int ret; + + chip->pagebuf = -1; + + memset(chip->data_buf, 0xff, mtd->writesize); + ret = chip->ecc.write_page(mtd, chip, chip->data_buf, 1, page); + if (ret) + return ret; + + /* Send command to program the OOB data */ + return nand_prog_page_end_op(chip); +} + +static const s32 tWB_lut[] = {6, 12, 16, 20}; +static const s32 tRHW_lut[] = {4, 8, 12, 20}; + +static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, + u32 clk_period) +{ + u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); + int i; + + for (i = 0; i < lut_size; i++) { + if (clk_cycles <= lut[i]) + return i; + } + + /* Doesn't fit */ + return -EINVAL; +} + +#define sunxi_nand_lookup_timing(l, p, c) \ + _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) + +static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *chip = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller); + const struct nand_sdr_timings *timings; + u32 min_clk_period = 0; + s32 tWB, tADL, tWHR, tRHW, tCAD; + long real_clk_rate; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + + /* T1 <=> tCLS */ + if (timings->tCLS_min > min_clk_period) + min_clk_period = timings->tCLS_min; + + /* T2 <=> tCLH */ + if (timings->tCLH_min > min_clk_period) + min_clk_period = timings->tCLH_min; + + /* T3 <=> tCS */ + if (timings->tCS_min > min_clk_period) + min_clk_period = timings->tCS_min; + + /* T4 <=> tCH */ + if (timings->tCH_min > min_clk_period) + min_clk_period = timings->tCH_min; + + /* T5 <=> tWP */ + if (timings->tWP_min > min_clk_period) + min_clk_period = timings->tWP_min; + + /* T6 <=> tWH */ + if (timings->tWH_min > min_clk_period) + min_clk_period = timings->tWH_min; + + /* T7 <=> tALS */ + if (timings->tALS_min > min_clk_period) + min_clk_period = timings->tALS_min; + + /* T8 <=> tDS */ + if (timings->tDS_min > min_clk_period) + min_clk_period = timings->tDS_min; + + /* T9 <=> tDH */ + if (timings->tDH_min > min_clk_period) + min_clk_period = timings->tDH_min; + + /* T10 <=> tRR */ + if (timings->tRR_min > (min_clk_period * 3)) + min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); + + /* T11 <=> tALH */ + if (timings->tALH_min > min_clk_period) + min_clk_period = timings->tALH_min; + + /* T12 <=> tRP */ + if (timings->tRP_min > min_clk_period) + min_clk_period = timings->tRP_min; + + /* T13 <=> tREH */ + if (timings->tREH_min > min_clk_period) + min_clk_period = timings->tREH_min; + + /* T14 <=> tRC */ + if (timings->tRC_min > (min_clk_period * 2)) + min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2); + + /* T15 <=> tWC */ + if (timings->tWC_min > (min_clk_period * 2)) + min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); + + /* T16 - T19 + tCAD */ + if (timings->tWB_max > (min_clk_period * 20)) + min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20); + + if (timings->tADL_min > (min_clk_period * 32)) + min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32); + + if (timings->tWHR_min > (min_clk_period * 32)) + min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32); + + if (timings->tRHW_min > (min_clk_period * 20)) + min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20); + + tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, + min_clk_period); + if (tWB < 0) { + dev_err(nfc->dev, "unsupported tWB\n"); + return tWB; + } + + tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; + if (tADL > 3) { + dev_err(nfc->dev, "unsupported tADL\n"); + return -EINVAL; + } + + tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; + if (tWHR > 3) { + dev_err(nfc->dev, "unsupported tWHR\n"); + return -EINVAL; + } + + tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, + min_clk_period); + if (tRHW < 0) { + dev_err(nfc->dev, "unsupported tRHW\n"); + return tRHW; + } + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + /* + * TODO: according to ONFI specs this value only applies for DDR NAND, + * but Allwinner seems to set this to 0x7. Mimic them for now. + */ + tCAD = 0x7; + + /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ + chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); + + /* Convert min_clk_period from picoseconds to nanoseconds */ + min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); + + /* + * Unlike what is stated in Allwinner datasheet, the clk_rate should + * be set to (1 / min_clk_period), and not (2 / min_clk_period). + * This new formula was verified with a scope and validated by + * Allwinner engineers. + */ + chip->clk_rate = NSEC_PER_SEC / min_clk_period; + real_clk_rate = clk_round_rate(nfc->mod_clk, chip->clk_rate); + if (real_clk_rate <= 0) { + dev_err(nfc->dev, "Unable to round clk %lu\n", chip->clk_rate); + return -EINVAL; + } + + /* + * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data + * output cycle timings shall be used if the host drives tRC less than + * 30 ns. + */ + min_clk_period = NSEC_PER_SEC / real_clk_rate; + chip->timing_ctl = ((min_clk_period * 2) < 30) ? + NFC_TIMING_CTL_EDO : 0; + + return 0; +} + +static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &nand->ecc; + + if (section >= ecc->steps) + return -ERANGE; + + oobregion->offset = section * (ecc->bytes + 4) + 4; + oobregion->length = ecc->bytes; + + return 0; +} + +static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &nand->ecc; + + if (section > ecc->steps) + return -ERANGE; + + /* + * The first 2 bytes are used for BB markers, hence we + * only have 2 bytes available in the first user data + * section. + */ + if (!section && ecc->mode == NAND_ECC_HW) { + oobregion->offset = 2; + oobregion->length = 2; + + return 0; + } + + oobregion->offset = section * (ecc->bytes + 4); + + if (section < ecc->steps) + oobregion->length = 4; + else + oobregion->offset = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { + .ecc = sunxi_nand_ooblayout_ecc, + .free = sunxi_nand_ooblayout_free, +}; + +static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc, + struct device_node *np) +{ + static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + struct sunxi_nand_hw_ecc *data; + int nsectors; + int ret; + int i; + + if (ecc->options & NAND_ECC_MAXIMIZE) { + int bytes; + + ecc->size = 1024; + nsectors = mtd->writesize / ecc->size; + + /* Reserve 2 bytes for the BBM */ + bytes = (mtd->oobsize - 2) / nsectors; + + /* 4 non-ECC bytes are added before each ECC bytes section */ + bytes -= 4; + + /* and bytes has to be even. */ + if (bytes % 2) + bytes--; + + ecc->strength = bytes * 8 / fls(8 * ecc->size); + + for (i = 0; i < ARRAY_SIZE(strengths); i++) { + if (strengths[i] > ecc->strength) + break; + } + + if (!i) + ecc->strength = 0; + else + ecc->strength = strengths[i - 1]; + } + + if (ecc->size != 512 && ecc->size != 1024) + return -EINVAL; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* Prefer 1k ECC chunk over 512 ones */ + if (ecc->size == 512 && mtd->writesize > 512) { + ecc->size = 1024; + ecc->strength *= 2; + } + + /* Add ECC info retrieval from DT */ + for (i = 0; i < ARRAY_SIZE(strengths); i++) { + if (ecc->strength <= strengths[i]) { + /* + * Update ecc->strength value with the actual strength + * that will be used by the ECC engine. + */ + ecc->strength = strengths[i]; + break; + } + } + + if (i >= ARRAY_SIZE(strengths)) { + dev_err(nfc->dev, "unsupported strength\n"); + ret = -ENOTSUPP; + goto err; + } + + data->mode = i; + + /* HW ECC always request ECC bytes for 1024 bytes blocks */ + ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); + + /* HW ECC always work with even numbers of ECC bytes */ + ecc->bytes = ALIGN(ecc->bytes, 2); + + nsectors = mtd->writesize / ecc->size; + + if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { + ret = -EINVAL; + goto err; + } + + ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob; + ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob; + mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); + ecc->priv = data; + + return 0; + +err: + kfree(data); + + return ret; +} + +static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) +{ + kfree(ecc->priv); +} + +static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc, + struct device_node *np) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + int ret; + + ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); + if (ret) + return ret; + + if (nfc->dmac) { + ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; + ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; + ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma; + nand->options |= NAND_USE_BOUNCE_BUFFER; + } else { + ecc->read_page = sunxi_nfc_hw_ecc_read_page; + ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; + ecc->write_page = sunxi_nfc_hw_ecc_write_page; + } + + /* TODO: support DMA for raw accesses and subpage write */ + ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage; + ecc->read_oob_raw = nand_read_oob_std; + ecc->write_oob_raw = nand_write_oob_std; + + return 0; +} + +static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc, + struct device_node *np) +{ + int ret; + + ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); + if (ret) + return ret; + + ecc->prepad = 4; + ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; + ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; + ecc->read_oob_raw = nand_read_oob_syndrome; + ecc->write_oob_raw = nand_write_oob_syndrome; + + return 0; +} + +static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) +{ + switch (ecc->mode) { + case NAND_ECC_HW: + case NAND_ECC_HW_SYNDROME: + sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc); + break; + case NAND_ECC_NONE: + default: + break; + } +} + +static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, + struct device_node *np) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + int ret; + + if (!ecc->size) { + ecc->size = nand->ecc_step_ds; + ecc->strength = nand->ecc_strength_ds; + } + + if (!ecc->size || !ecc->strength) + return -EINVAL; + + switch (ecc->mode) { + case NAND_ECC_HW: + ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np); + if (ret) + return ret; + break; + case NAND_ECC_HW_SYNDROME: + ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc, np); + if (ret) + return ret; + break; + case NAND_ECC_NONE: + case NAND_ECC_SOFT: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, + struct device_node *np) +{ + struct sunxi_nand_chip *chip; + struct mtd_info *mtd; + struct nand_chip *nand; + int nsels; + int ret; + int i; + u32 tmp; + + if (!of_get_property(np, "reg", &nsels)) + return -EINVAL; + + nsels /= sizeof(u32); + if (!nsels) { + dev_err(dev, "invalid reg property size\n"); + return -EINVAL; + } + + chip = devm_kzalloc(dev, + sizeof(*chip) + + (nsels * sizeof(struct sunxi_nand_chip_sel)), + GFP_KERNEL); + if (!chip) { + dev_err(dev, "could not allocate chip\n"); + return -ENOMEM; + } + + chip->nsels = nsels; + chip->selected = -1; + + for (i = 0; i < nsels; i++) { + ret = of_property_read_u32_index(np, "reg", i, &tmp); + if (ret) { + dev_err(dev, "could not retrieve reg property: %d\n", + ret); + return ret; + } + + if (tmp > NFC_MAX_CS) { + dev_err(dev, + "invalid reg value: %u (max CS = 7)\n", + tmp); + return -EINVAL; + } + + if (test_and_set_bit(tmp, &nfc->assigned_cs)) { + dev_err(dev, "CS %d already assigned\n", tmp); + return -EINVAL; + } + + chip->sels[i].cs = tmp; + + if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) && + tmp < 2) { + chip->sels[i].rb.type = RB_NATIVE; + chip->sels[i].rb.info.nativeid = tmp; + } else { + ret = of_get_named_gpio(np, "rb-gpios", i); + if (ret >= 0) { + tmp = ret; + chip->sels[i].rb.type = RB_GPIO; + chip->sels[i].rb.info.gpio = tmp; + ret = devm_gpio_request(dev, tmp, "nand-rb"); + if (ret) + return ret; + + ret = gpio_direction_input(tmp); + if (ret) + return ret; + } else { + chip->sels[i].rb.type = RB_NONE; + } + } + } + + nand = &chip->nand; + /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ + nand->chip_delay = 200; + nand->controller = &nfc->controller; + /* + * Set the ECC mode to the default value in case nothing is specified + * in the DT. + */ + nand->ecc.mode = NAND_ECC_HW; + nand_set_flash_node(nand, np); + nand->select_chip = sunxi_nfc_select_chip; + nand->cmd_ctrl = sunxi_nfc_cmd_ctrl; + nand->read_buf = sunxi_nfc_read_buf; + nand->write_buf = sunxi_nfc_write_buf; + nand->read_byte = sunxi_nfc_read_byte; + nand->setup_data_interface = sunxi_nfc_setup_data_interface; + + mtd = nand_to_mtd(nand); + mtd->dev.parent = dev; + + ret = nand_scan_ident(mtd, nsels, NULL); + if (ret) + return ret; + + if (nand->bbt_options & NAND_BBT_USE_FLASH) + nand->bbt_options |= NAND_BBT_NO_OOB; + + if (nand->options & NAND_NEED_SCRAMBLING) + nand->options |= NAND_NO_SUBPAGE_WRITE; + + nand->options |= NAND_SUBPAGE_READ; + + ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np); + if (ret) { + dev_err(dev, "ECC init failed: %d\n", ret); + return ret; + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "nand_scan_tail failed: %d\n", ret); + return ret; + } + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(dev, "failed to register mtd device: %d\n", ret); + nand_release(mtd); + return ret; + } + + list_add_tail(&chip->node, &nfc->chips); + + return 0; +} + +static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) +{ + struct device_node *np = dev->of_node; + struct device_node *nand_np; + int nchips = of_get_child_count(np); + int ret; + + if (nchips > 8) { + dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips); + return -EINVAL; + } + + for_each_child_of_node(np, nand_np) { + ret = sunxi_nand_chip_init(dev, nfc, nand_np); + if (ret) { + of_node_put(nand_np); + return ret; + } + } + + return 0; +} + +static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) +{ + struct sunxi_nand_chip *chip; + + while (!list_empty(&nfc->chips)) { + chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip, + node); + nand_release(nand_to_mtd(&chip->nand)); + sunxi_nand_ecc_cleanup(&chip->nand.ecc); + list_del(&chip->node); + } +} + +static int sunxi_nfc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *r; + struct sunxi_nfc *nfc; + int irq; + int ret; + + nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->dev = dev; + nand_hw_control_init(&nfc->controller); + INIT_LIST_HEAD(&nfc->chips); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->regs = devm_ioremap_resource(dev, r); + if (IS_ERR(nfc->regs)) + return PTR_ERR(nfc->regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "failed to retrieve irq\n"); + return irq; + } + + nfc->ahb_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(nfc->ahb_clk)) { + dev_err(dev, "failed to retrieve ahb clk\n"); + return PTR_ERR(nfc->ahb_clk); + } + + ret = clk_prepare_enable(nfc->ahb_clk); + if (ret) + return ret; + + nfc->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(nfc->mod_clk)) { + dev_err(dev, "failed to retrieve mod clk\n"); + ret = PTR_ERR(nfc->mod_clk); + goto out_ahb_clk_unprepare; + } + + ret = clk_prepare_enable(nfc->mod_clk); + if (ret) + goto out_ahb_clk_unprepare; + + nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); + if (IS_ERR(nfc->reset)) { + ret = PTR_ERR(nfc->reset); + goto out_mod_clk_unprepare; + } + + ret = reset_control_deassert(nfc->reset); + if (ret) { + dev_err(dev, "reset err %d\n", ret); + goto out_mod_clk_unprepare; + } + + ret = sunxi_nfc_rst(nfc); + if (ret) + goto out_ahb_reset_reassert; + + writel(0, nfc->regs + NFC_REG_INT); + ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt, + 0, "sunxi-nand", nfc); + if (ret) + goto out_ahb_reset_reassert; + + nfc->dmac = dma_request_slave_channel(dev, "rxtx"); + if (nfc->dmac) { + struct dma_slave_config dmac_cfg = { }; + + dmac_cfg.src_addr = r->start + NFC_REG_IO_DATA; + dmac_cfg.dst_addr = dmac_cfg.src_addr; + dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width; + dmac_cfg.src_maxburst = 4; + dmac_cfg.dst_maxburst = 4; + dmaengine_slave_config(nfc->dmac, &dmac_cfg); + } else { + dev_warn(dev, "failed to request rxtx DMA channel\n"); + } + + platform_set_drvdata(pdev, nfc); + + ret = sunxi_nand_chips_init(dev, nfc); + if (ret) { + dev_err(dev, "failed to init nand chips\n"); + goto out_release_dmac; + } + + return 0; + +out_release_dmac: + if (nfc->dmac) + dma_release_channel(nfc->dmac); +out_ahb_reset_reassert: + reset_control_assert(nfc->reset); +out_mod_clk_unprepare: + clk_disable_unprepare(nfc->mod_clk); +out_ahb_clk_unprepare: + clk_disable_unprepare(nfc->ahb_clk); + + return ret; +} + +static int sunxi_nfc_remove(struct platform_device *pdev) +{ + struct sunxi_nfc *nfc = platform_get_drvdata(pdev); + + sunxi_nand_chips_cleanup(nfc); + + reset_control_assert(nfc->reset); + + if (nfc->dmac) + dma_release_channel(nfc->dmac); + clk_disable_unprepare(nfc->mod_clk); + clk_disable_unprepare(nfc->ahb_clk); + + return 0; +} + +static const struct of_device_id sunxi_nfc_ids[] = { + { .compatible = "allwinner,sun4i-a10-nand" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sunxi_nfc_ids); + +static struct platform_driver sunxi_nfc_driver = { + .driver = { + .name = "sunxi_nand", + .of_match_table = sunxi_nfc_ids, + }, + .probe = sunxi_nfc_probe, + .remove = sunxi_nfc_remove, +}; +module_platform_driver(sunxi_nfc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Boris BREZILLON"); +MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); +MODULE_ALIAS("platform:sunxi_nand"); diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c new file mode 100644 index 000000000000..c5bee00b7f5e --- /dev/null +++ b/drivers/mtd/nand/raw/tango_nand.c @@ -0,0 +1,688 @@ +/* + * Copyright (C) 2016 Sigma Designs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Offsets relative to chip->base */ +#define PBUS_CMD 0 +#define PBUS_ADDR 4 +#define PBUS_DATA 8 + +/* Offsets relative to reg_base */ +#define NFC_STATUS 0x00 +#define NFC_FLASH_CMD 0x04 +#define NFC_DEVICE_CFG 0x08 +#define NFC_TIMING1 0x0c +#define NFC_TIMING2 0x10 +#define NFC_XFER_CFG 0x14 +#define NFC_PKT_0_CFG 0x18 +#define NFC_PKT_N_CFG 0x1c +#define NFC_BB_CFG 0x20 +#define NFC_ADDR_PAGE 0x24 +#define NFC_ADDR_OFFSET 0x28 +#define NFC_XFER_STATUS 0x2c + +/* NFC_STATUS values */ +#define CMD_READY BIT(31) + +/* NFC_FLASH_CMD values */ +#define NFC_READ 1 +#define NFC_WRITE 2 + +/* NFC_XFER_STATUS values */ +#define PAGE_IS_EMPTY BIT(16) + +/* Offsets relative to mem_base */ +#define METADATA 0x000 +#define ERROR_REPORT 0x1c0 + +/* + * Error reports are split in two bytes: + * byte 0 for the first packet in the page (PKT_0) + * byte 1 for other packets in the page (PKT_N, for N > 0) + * ERR_COUNT_PKT_N is the max error count over all but the first packet. + */ +#define ERR_COUNT_PKT_0(v) (((v) >> 0) & 0x3f) +#define ERR_COUNT_PKT_N(v) (((v) >> 8) & 0x3f) +#define DECODE_FAIL_PKT_0(v) (((v) & BIT(7)) == 0) +#define DECODE_FAIL_PKT_N(v) (((v) & BIT(15)) == 0) + +/* Offsets relative to pbus_base */ +#define PBUS_CS_CTRL 0x83c +#define PBUS_PAD_MODE 0x8f0 + +/* PBUS_CS_CTRL values */ +#define PBUS_IORDY BIT(31) + +/* + * PBUS_PAD_MODE values + * In raw mode, the driver communicates directly with the NAND chips. + * In NFC mode, the NAND Flash controller manages the communication. + * We use NFC mode for read and write; raw mode for everything else. + */ +#define MODE_RAW 0 +#define MODE_NFC BIT(31) + +#define METADATA_SIZE 4 +#define BBM_SIZE 6 +#define FIELD_ORDER 15 + +#define MAX_CS 4 + +struct tango_nfc { + struct nand_hw_control hw; + void __iomem *reg_base; + void __iomem *mem_base; + void __iomem *pbus_base; + struct tango_chip *chips[MAX_CS]; + struct dma_chan *chan; + int freq_kHz; +}; + +#define to_tango_nfc(ptr) container_of(ptr, struct tango_nfc, hw) + +struct tango_chip { + struct nand_chip nand_chip; + void __iomem *base; + u32 timing1; + u32 timing2; + u32 xfer_cfg; + u32 pkt_0_cfg; + u32 pkt_n_cfg; + u32 bb_cfg; +}; + +#define to_tango_chip(ptr) container_of(ptr, struct tango_chip, nand_chip) + +#define XFER_CFG(cs, page_count, steps, metadata_size) \ + ((cs) << 24 | (page_count) << 16 | (steps) << 8 | (metadata_size)) + +#define PKT_CFG(size, strength) ((size) << 16 | (strength)) + +#define BB_CFG(bb_offset, bb_size) ((bb_offset) << 16 | (bb_size)) + +#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3)) + +static void tango_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); + + if (ctrl & NAND_CLE) + writeb_relaxed(dat, tchip->base + PBUS_CMD); + + if (ctrl & NAND_ALE) + writeb_relaxed(dat, tchip->base + PBUS_ADDR); +} + +static int tango_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + + return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY; +} + +static u8 tango_read_byte(struct mtd_info *mtd) +{ + struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); + + return readb_relaxed(tchip->base + PBUS_DATA); +} + +static void tango_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); + + ioread8_rep(tchip->base + PBUS_DATA, buf, len); +} + +static void tango_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); + + iowrite8_rep(tchip->base + PBUS_DATA, buf, len); +} + +static void tango_select_chip(struct mtd_info *mtd, int idx) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + struct tango_chip *tchip = to_tango_chip(chip); + + if (idx < 0) + return; /* No "chip unselect" function */ + + writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1); + writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2); + writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG); + writel_relaxed(tchip->pkt_0_cfg, nfc->reg_base + NFC_PKT_0_CFG); + writel_relaxed(tchip->pkt_n_cfg, nfc->reg_base + NFC_PKT_N_CFG); + writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG); +} + +/* + * The controller does not check for bitflips in erased pages, + * therefore software must check instead. + */ +static int check_erased_page(struct nand_chip *chip, u8 *buf) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u8 *meta = chip->oob_poi + BBM_SIZE; + u8 *ecc = chip->oob_poi + BBM_SIZE + METADATA_SIZE; + const int ecc_size = chip->ecc.bytes; + const int pkt_size = chip->ecc.size; + int i, res, meta_len, bitflips = 0; + + for (i = 0; i < chip->ecc.steps; ++i) { + meta_len = i ? 0 : METADATA_SIZE; + res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, + meta, meta_len, + chip->ecc.strength); + if (res < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += res; + + bitflips = max(res, bitflips); + buf += pkt_size; + ecc += ecc_size; + } + + return bitflips; +} + +static int decode_error_report(struct nand_chip *chip) +{ + u32 status, res; + struct mtd_info *mtd = nand_to_mtd(chip); + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + + status = readl_relaxed(nfc->reg_base + NFC_XFER_STATUS); + if (status & PAGE_IS_EMPTY) + return 0; + + res = readl_relaxed(nfc->mem_base + ERROR_REPORT); + + if (DECODE_FAIL_PKT_0(res) || DECODE_FAIL_PKT_N(res)) + return -EBADMSG; + + /* ERR_COUNT_PKT_N is max, not sum, but that's all we have */ + mtd->ecc_stats.corrected += + ERR_COUNT_PKT_0(res) + ERR_COUNT_PKT_N(res); + + return max(ERR_COUNT_PKT_0(res), ERR_COUNT_PKT_N(res)); +} + +static void tango_dma_callback(void *arg) +{ + complete(arg); +} + +static int do_dma(struct tango_nfc *nfc, enum dma_data_direction dir, int cmd, + const void *buf, int len, int page) +{ + void __iomem *addr = nfc->reg_base + NFC_STATUS; + struct dma_chan *chan = nfc->chan; + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction tdir; + struct scatterlist sg; + struct completion tx_done; + int err = -EIO; + u32 res, val; + + sg_init_one(&sg, buf, len); + if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1) + return -EIO; + + tdir = dir == DMA_TO_DEVICE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + desc = dmaengine_prep_slave_sg(chan, &sg, 1, tdir, DMA_PREP_INTERRUPT); + if (!desc) + goto dma_unmap; + + desc->callback = tango_dma_callback; + desc->callback_param = &tx_done; + init_completion(&tx_done); + + writel_relaxed(MODE_NFC, nfc->pbus_base + PBUS_PAD_MODE); + + writel_relaxed(page, nfc->reg_base + NFC_ADDR_PAGE); + writel_relaxed(0, nfc->reg_base + NFC_ADDR_OFFSET); + writel_relaxed(cmd, nfc->reg_base + NFC_FLASH_CMD); + + dmaengine_submit(desc); + dma_async_issue_pending(chan); + + res = wait_for_completion_timeout(&tx_done, HZ); + if (res > 0) + err = readl_poll_timeout(addr, val, val & CMD_READY, 0, 1000); + + writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE); + +dma_unmap: + dma_unmap_sg(chan->device->dev, &sg, 1, dir); + + return err; +} + +static int tango_read_page(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + int err, res, len = mtd->writesize; + + if (oob_required) + chip->ecc.read_oob(mtd, chip, page); + + err = do_dma(nfc, DMA_FROM_DEVICE, NFC_READ, buf, len, page); + if (err) + return err; + + res = decode_error_report(chip); + if (res < 0) { + chip->ecc.read_oob_raw(mtd, chip, page); + res = check_erased_page(chip, buf); + } + + return res; +} + +static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + int err, status, len = mtd->writesize; + + /* Calling tango_write_oob() would send PAGEPROG twice */ + if (oob_required) + return -ENOTSUPP; + + writel_relaxed(0xffffffff, nfc->mem_base + METADATA); + err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page); + if (err) + return err; + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + *pos += len; + + if (!*buf) { + /* skip over "len" bytes */ + nand_change_read_column_op(chip, *pos, NULL, 0, false); + } else { + tango_read_buf(mtd, *buf, len); + *buf += len; + } +} + +static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + *pos += len; + + if (!*buf) { + /* skip over "len" bytes */ + nand_change_write_column_op(chip, *pos, NULL, 0, false); + } else { + tango_write_buf(mtd, *buf, len); + *buf += len; + } +} + +/* + * Physical page layout (not drawn to scale) + * + * NB: Bad Block Marker area splits PKT_N in two (N1, N2). + * + * +---+-----------------+-------+-----+-----------+-----+----+-------+ + * | M | PKT_0 | ECC_0 | ... | N1 | BBM | N2 | ECC_N | + * +---+-----------------+-------+-----+-----------+-----+----+-------+ + * + * Logical page layout: + * + * +-----+---+-------+-----+-------+ + * oob = | BBM | M | ECC_0 | ... | ECC_N | + * +-----+---+-------+-----+-------+ + * + * +-----------------+-----+-----------------+ + * buf = | PKT_0 | ... | PKT_N | + * +-----------------+-----+-----------------+ + */ +static void raw_read(struct nand_chip *chip, u8 *buf, u8 *oob) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u8 *oob_orig = oob; + const int page_size = mtd->writesize; + const int ecc_size = chip->ecc.bytes; + const int pkt_size = chip->ecc.size; + int pos = 0; /* position within physical page */ + int rem = page_size; /* bytes remaining until BBM area */ + + if (oob) + oob += BBM_SIZE; + + aux_read(chip, &oob, METADATA_SIZE, &pos); + + while (rem > pkt_size) { + aux_read(chip, &buf, pkt_size, &pos); + aux_read(chip, &oob, ecc_size, &pos); + rem = page_size - pos; + } + + aux_read(chip, &buf, rem, &pos); + aux_read(chip, &oob_orig, BBM_SIZE, &pos); + aux_read(chip, &buf, pkt_size - rem, &pos); + aux_read(chip, &oob, ecc_size, &pos); +} + +static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const u8 *oob_orig = oob; + const int page_size = mtd->writesize; + const int ecc_size = chip->ecc.bytes; + const int pkt_size = chip->ecc.size; + int pos = 0; /* position within physical page */ + int rem = page_size; /* bytes remaining until BBM area */ + + if (oob) + oob += BBM_SIZE; + + aux_write(chip, &oob, METADATA_SIZE, &pos); + + while (rem > pkt_size) { + aux_write(chip, &buf, pkt_size, &pos); + aux_write(chip, &oob, ecc_size, &pos); + rem = page_size - pos; + } + + aux_write(chip, &buf, rem, &pos); + aux_write(chip, &oob_orig, BBM_SIZE, &pos); + aux_write(chip, &buf, pkt_size - rem, &pos); + aux_write(chip, &oob, ecc_size, &pos); +} + +static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + nand_read_page_op(chip, page, 0, NULL, 0); + raw_read(chip, buf, chip->oob_poi); + return 0; +} + +static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + raw_write(chip, buf, chip->oob_poi); + return nand_prog_page_end_op(chip); +} + +static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + nand_read_page_op(chip, page, 0, NULL, 0); + raw_read(chip, NULL, chip->oob_poi); + return 0; +} + +static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + raw_write(chip, NULL, chip->oob_poi); + return nand_prog_page_end_op(chip); +} + +static int oob_ecc(struct mtd_info *mtd, int idx, struct mtd_oob_region *res) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (idx >= ecc->steps) + return -ERANGE; + + res->offset = BBM_SIZE + METADATA_SIZE + ecc->bytes * idx; + res->length = ecc->bytes; + + return 0; +} + +static int oob_free(struct mtd_info *mtd, int idx, struct mtd_oob_region *res) +{ + return -ERANGE; /* no free space in spare area */ +} + +static const struct mtd_ooblayout_ops tango_nand_ooblayout_ops = { + .ecc = oob_ecc, + .free = oob_free, +}; + +static u32 to_ticks(int kHz, int ps) +{ + return DIV_ROUND_UP_ULL((u64)kHz * ps, NSEC_PER_SEC); +} + +static int tango_set_timings(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf) +{ + const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf); + struct nand_chip *chip = mtd_to_nand(mtd); + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + struct tango_chip *tchip = to_tango_chip(chip); + u32 Trdy, Textw, Twc, Twpw, Tacc, Thold, Trpw, Textr; + int kHz = nfc->freq_kHz; + + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + Trdy = to_ticks(kHz, sdr->tCEA_max - sdr->tREA_max); + Textw = to_ticks(kHz, sdr->tWB_max); + Twc = to_ticks(kHz, sdr->tWC_min); + Twpw = to_ticks(kHz, sdr->tWC_min - sdr->tWP_min); + + Tacc = to_ticks(kHz, sdr->tREA_max); + Thold = to_ticks(kHz, sdr->tREH_min); + Trpw = to_ticks(kHz, sdr->tRC_min - sdr->tREH_min); + Textr = to_ticks(kHz, sdr->tRHZ_max); + + tchip->timing1 = TIMING(Trdy, Textw, Twc, Twpw); + tchip->timing2 = TIMING(Tacc, Thold, Trpw, Textr); + + return 0; +} + +static int chip_init(struct device *dev, struct device_node *np) +{ + u32 cs; + int err, res; + struct mtd_info *mtd; + struct nand_chip *chip; + struct tango_chip *tchip; + struct nand_ecc_ctrl *ecc; + struct tango_nfc *nfc = dev_get_drvdata(dev); + + tchip = devm_kzalloc(dev, sizeof(*tchip), GFP_KERNEL); + if (!tchip) + return -ENOMEM; + + res = of_property_count_u32_elems(np, "reg"); + if (res < 0) + return res; + + if (res != 1) + return -ENOTSUPP; /* Multi-CS chips are not supported */ + + err = of_property_read_u32_index(np, "reg", 0, &cs); + if (err) + return err; + + if (cs >= MAX_CS) + return -EINVAL; + + chip = &tchip->nand_chip; + ecc = &chip->ecc; + mtd = nand_to_mtd(chip); + + chip->read_byte = tango_read_byte; + chip->write_buf = tango_write_buf; + chip->read_buf = tango_read_buf; + chip->select_chip = tango_select_chip; + chip->cmd_ctrl = tango_cmd_ctrl; + chip->dev_ready = tango_dev_ready; + chip->setup_data_interface = tango_set_timings; + chip->options = NAND_USE_BOUNCE_BUFFER | + NAND_NO_SUBPAGE_WRITE | + NAND_WAIT_TCCS; + chip->controller = &nfc->hw; + tchip->base = nfc->pbus_base + (cs * 256); + + nand_set_flash_node(chip, np); + mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops); + mtd->dev.parent = dev; + + err = nand_scan_ident(mtd, 1, NULL); + if (err) + return err; + + ecc->mode = NAND_ECC_HW; + ecc->algo = NAND_ECC_BCH; + ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE); + + ecc->read_page_raw = tango_read_page_raw; + ecc->write_page_raw = tango_write_page_raw; + ecc->read_page = tango_read_page; + ecc->write_page = tango_write_page; + ecc->read_oob = tango_read_oob; + ecc->write_oob = tango_write_oob; + + err = nand_scan_tail(mtd); + if (err) + return err; + + tchip->xfer_cfg = XFER_CFG(cs, 1, ecc->steps, METADATA_SIZE); + tchip->pkt_0_cfg = PKT_CFG(ecc->size + METADATA_SIZE, ecc->strength); + tchip->pkt_n_cfg = PKT_CFG(ecc->size, ecc->strength); + tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE); + + err = mtd_device_register(mtd, NULL, 0); + if (err) + return err; + + nfc->chips[cs] = tchip; + + return 0; +} + +static int tango_nand_remove(struct platform_device *pdev) +{ + int cs; + struct tango_nfc *nfc = platform_get_drvdata(pdev); + + dma_release_channel(nfc->chan); + + for (cs = 0; cs < MAX_CS; ++cs) { + if (nfc->chips[cs]) + nand_release(nand_to_mtd(&nfc->chips[cs]->nand_chip)); + } + + return 0; +} + +static int tango_nand_probe(struct platform_device *pdev) +{ + int err; + struct clk *clk; + struct resource *res; + struct tango_nfc *nfc; + struct device_node *np; + + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nfc->reg_base)) + return PTR_ERR(nfc->reg_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + nfc->mem_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nfc->mem_base)) + return PTR_ERR(nfc->mem_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + nfc->pbus_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nfc->pbus_base)) + return PTR_ERR(nfc->pbus_base); + + writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE); + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + nfc->chan = dma_request_chan(&pdev->dev, "rxtx"); + if (IS_ERR(nfc->chan)) + return PTR_ERR(nfc->chan); + + platform_set_drvdata(pdev, nfc); + nand_hw_control_init(&nfc->hw); + nfc->freq_kHz = clk_get_rate(clk) / 1000; + + for_each_child_of_node(pdev->dev.of_node, np) { + err = chip_init(&pdev->dev, np); + if (err) { + tango_nand_remove(pdev); + return err; + } + } + + return 0; +} + +static const struct of_device_id tango_nand_ids[] = { + { .compatible = "sigma,smp8758-nand" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tango_nand_ids); + +static struct platform_driver tango_nand_driver = { + .probe = tango_nand_probe, + .remove = tango_nand_remove, + .driver = { + .name = "tango-nand", + .of_match_table = tango_nand_ids, + }, +}; + +module_platform_driver(tango_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sigma Designs"); +MODULE_DESCRIPTION("Tango4 NAND Flash controller driver"); diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c new file mode 100644 index 000000000000..dcaa924502de --- /dev/null +++ b/drivers/mtd/nand/raw/tmio_nand.c @@ -0,0 +1,513 @@ +/* + * Toshiba TMIO NAND flash controller driver + * + * Slightly murky pre-git history of the driver: + * + * Copyright (c) Ian Molton 2004, 2005, 2008 + * Original work, independent of sharps code. Included hardware ECC support. + * Hard ECC did not work for writes in the early revisions. + * Copyright (c) Dirk Opfer 2005. + * Modifications developed from sharps code but + * NOT containing any, ported onto Ians base. + * Copyright (c) Chris Humbert 2005 + * Copyright (c) Dmitry Baryshkov 2008 + * Minor fixes + * + * Parts copyright Sebastian Carlier + * + * This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*--------------------------------------------------------------------------*/ + +/* + * NAND Flash Host Controller Configuration Register + */ +#define CCR_COMMAND 0x04 /* w Command */ +#define CCR_BASE 0x10 /* l NAND Flash Control Reg Base Addr */ +#define CCR_INTP 0x3d /* b Interrupt Pin */ +#define CCR_INTE 0x48 /* b Interrupt Enable */ +#define CCR_EC 0x4a /* b Event Control */ +#define CCR_ICC 0x4c /* b Internal Clock Control */ +#define CCR_ECCC 0x5b /* b ECC Control */ +#define CCR_NFTC 0x60 /* b NAND Flash Transaction Control */ +#define CCR_NFM 0x61 /* b NAND Flash Monitor */ +#define CCR_NFPSC 0x62 /* b NAND Flash Power Supply Control */ +#define CCR_NFDC 0x63 /* b NAND Flash Detect Control */ + +/* + * NAND Flash Control Register + */ +#define FCR_DATA 0x00 /* bwl Data Register */ +#define FCR_MODE 0x04 /* b Mode Register */ +#define FCR_STATUS 0x05 /* b Status Register */ +#define FCR_ISR 0x06 /* b Interrupt Status Register */ +#define FCR_IMR 0x07 /* b Interrupt Mask Register */ + +/* FCR_MODE Register Command List */ +#define FCR_MODE_DATA 0x94 /* Data Data_Mode */ +#define FCR_MODE_COMMAND 0x95 /* Data Command_Mode */ +#define FCR_MODE_ADDRESS 0x96 /* Data Address_Mode */ + +#define FCR_MODE_HWECC_CALC 0xB4 /* HW-ECC Data */ +#define FCR_MODE_HWECC_RESULT 0xD4 /* HW-ECC Calc result Read_Mode */ +#define FCR_MODE_HWECC_RESET 0xF4 /* HW-ECC Reset */ + +#define FCR_MODE_POWER_ON 0x0C /* Power Supply ON to SSFDC card */ +#define FCR_MODE_POWER_OFF 0x08 /* Power Supply OFF to SSFDC card */ + +#define FCR_MODE_LED_OFF 0x00 /* LED OFF */ +#define FCR_MODE_LED_ON 0x04 /* LED ON */ + +#define FCR_MODE_EJECT_ON 0x68 /* Ejection events active */ +#define FCR_MODE_EJECT_OFF 0x08 /* Ejection events ignored */ + +#define FCR_MODE_LOCK 0x6C /* Lock_Mode. Eject Switch Invalid */ +#define FCR_MODE_UNLOCK 0x0C /* UnLock_Mode. Eject Switch is valid */ + +#define FCR_MODE_CONTROLLER_ID 0x40 /* Controller ID Read */ +#define FCR_MODE_STANDBY 0x00 /* SSFDC card Changes Standby State */ + +#define FCR_MODE_WE 0x80 +#define FCR_MODE_ECC1 0x40 +#define FCR_MODE_ECC0 0x20 +#define FCR_MODE_CE 0x10 +#define FCR_MODE_PCNT1 0x08 +#define FCR_MODE_PCNT0 0x04 +#define FCR_MODE_ALE 0x02 +#define FCR_MODE_CLE 0x01 + +#define FCR_STATUS_BUSY 0x80 + +/*--------------------------------------------------------------------------*/ + +struct tmio_nand { + struct nand_chip chip; + + struct platform_device *dev; + + void __iomem *ccr; + void __iomem *fcr; + unsigned long fcr_base; + + unsigned int irq; + + /* for tmio_nand_read_byte */ + u8 read; + unsigned read_good:1; +}; + +static inline struct tmio_nand *mtd_to_tmio(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct tmio_nand, chip); +} + + +/*--------------------------------------------------------------------------*/ + +static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct tmio_nand *tmio = mtd_to_tmio(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + if (ctrl & NAND_CTRL_CHANGE) { + u8 mode; + + if (ctrl & NAND_NCE) { + mode = FCR_MODE_DATA; + + if (ctrl & NAND_CLE) + mode |= FCR_MODE_CLE; + else + mode &= ~FCR_MODE_CLE; + + if (ctrl & NAND_ALE) + mode |= FCR_MODE_ALE; + else + mode &= ~FCR_MODE_ALE; + } else { + mode = FCR_MODE_STANDBY; + } + + tmio_iowrite8(mode, tmio->fcr + FCR_MODE); + tmio->read_good = 0; + } + + if (cmd != NAND_CMD_NONE) + tmio_iowrite8(cmd, chip->IO_ADDR_W); +} + +static int tmio_nand_dev_ready(struct mtd_info *mtd) +{ + struct tmio_nand *tmio = mtd_to_tmio(mtd); + + return !(tmio_ioread8(tmio->fcr + FCR_STATUS) & FCR_STATUS_BUSY); +} + +static irqreturn_t tmio_irq(int irq, void *__tmio) +{ + struct tmio_nand *tmio = __tmio; + struct nand_chip *nand_chip = &tmio->chip; + + /* disable RDYREQ interrupt */ + tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); + + if (unlikely(!waitqueue_active(&nand_chip->controller->wq))) + dev_warn(&tmio->dev->dev, "spurious interrupt\n"); + + wake_up(&nand_chip->controller->wq); + return IRQ_HANDLED; +} + +/* + *The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. + *This interrupt is normally disabled, but for long operations like + *erase and write, we enable it to wake us up. The irq handler + *disables the interrupt. + */ +static int +tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip) +{ + struct tmio_nand *tmio = mtd_to_tmio(mtd); + long timeout; + u8 status; + + /* enable RDYREQ interrupt */ + tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); + tmio_iowrite8(0x81, tmio->fcr + FCR_IMR); + + timeout = wait_event_timeout(nand_chip->controller->wq, + tmio_nand_dev_ready(mtd), + msecs_to_jiffies(nand_chip->state == FL_ERASING ? 400 : 20)); + + if (unlikely(!tmio_nand_dev_ready(mtd))) { + tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); + dev_warn(&tmio->dev->dev, "still busy with %s after %d ms\n", + nand_chip->state == FL_ERASING ? "erase" : "program", + nand_chip->state == FL_ERASING ? 400 : 20); + + } else if (unlikely(!timeout)) { + tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); + dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n"); + } + + nand_status_op(nand_chip, &status); + return status; +} + +/* + *The TMIO controller combines two 8-bit data bytes into one 16-bit + *word. This function separates them so nand_base.c works as expected, + *especially its NAND_CMD_READID routines. + * + *To prevent stale data from being read, tmio_nand_hwcontrol() clears + *tmio->read_good. + */ +static u_char tmio_nand_read_byte(struct mtd_info *mtd) +{ + struct tmio_nand *tmio = mtd_to_tmio(mtd); + unsigned int data; + + if (tmio->read_good--) + return tmio->read; + + data = tmio_ioread16(tmio->fcr + FCR_DATA); + tmio->read = data >> 8; + return data; +} + +/* + *The TMIO controller converts an 8-bit NAND interface to a 16-bit + *bus interface, so all data reads and writes must be 16-bit wide. + *Thus, we implement 16-bit versions of the read, write, and verify + *buffer functions. + */ +static void +tmio_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct tmio_nand *tmio = mtd_to_tmio(mtd); + + tmio_iowrite16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); +} + +static void tmio_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct tmio_nand *tmio = mtd_to_tmio(mtd); + + tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); +} + +static void tmio_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct tmio_nand *tmio = mtd_to_tmio(mtd); + + tmio_iowrite8(FCR_MODE_HWECC_RESET, tmio->fcr + FCR_MODE); + tmio_ioread8(tmio->fcr + FCR_DATA); /* dummy read */ + tmio_iowrite8(FCR_MODE_HWECC_CALC, tmio->fcr + FCR_MODE); +} + +static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct tmio_nand *tmio = mtd_to_tmio(mtd); + unsigned int ecc; + + tmio_iowrite8(FCR_MODE_HWECC_RESULT, tmio->fcr + FCR_MODE); + + ecc = tmio_ioread16(tmio->fcr + FCR_DATA); + ecc_code[1] = ecc; /* 000-255 LP7-0 */ + ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */ + ecc = tmio_ioread16(tmio->fcr + FCR_DATA); + ecc_code[2] = ecc; /* 000-255 CP5-0,11b */ + ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */ + ecc = tmio_ioread16(tmio->fcr + FCR_DATA); + ecc_code[3] = ecc; /* 256-511 LP15-8 */ + ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */ + + tmio_iowrite8(FCR_MODE_DATA, tmio->fcr + FCR_MODE); + return 0; +} + +static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + int r0, r1; + + /* assume ecc.size = 512 and ecc.bytes = 6 */ + r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256); + if (r0 < 0) + return r0; + r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256); + if (r1 < 0) + return r1; + return r0 + r1; +} + +static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) +{ + const struct mfd_cell *cell = mfd_get_cell(dev); + int ret; + + if (cell->enable) { + ret = cell->enable(dev); + if (ret) + return ret; + } + + /* (4Ch) CLKRUN Enable 1st spcrunc */ + tmio_iowrite8(0x81, tmio->ccr + CCR_ICC); + + /* (10h)BaseAddress 0x1000 spba.spba2 */ + tmio_iowrite16(tmio->fcr_base, tmio->ccr + CCR_BASE); + tmio_iowrite16(tmio->fcr_base >> 16, tmio->ccr + CCR_BASE + 2); + + /* (04h)Command Register I/O spcmd */ + tmio_iowrite8(0x02, tmio->ccr + CCR_COMMAND); + + /* (62h) Power Supply Control ssmpwc */ + /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ + tmio_iowrite8(0x02, tmio->ccr + CCR_NFPSC); + + /* (63h) Detect Control ssmdtc */ + tmio_iowrite8(0x02, tmio->ccr + CCR_NFDC); + + /* Interrupt status register clear sintst */ + tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); + + /* After power supply, Media are reset smode */ + tmio_iowrite8(FCR_MODE_POWER_ON, tmio->fcr + FCR_MODE); + tmio_iowrite8(FCR_MODE_COMMAND, tmio->fcr + FCR_MODE); + tmio_iowrite8(NAND_CMD_RESET, tmio->fcr + FCR_DATA); + + /* Standby Mode smode */ + tmio_iowrite8(FCR_MODE_STANDBY, tmio->fcr + FCR_MODE); + + mdelay(5); + + return 0; +} + +static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) +{ + const struct mfd_cell *cell = mfd_get_cell(dev); + + tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); + if (cell->disable) + cell->disable(dev); +} + +static int tmio_probe(struct platform_device *dev) +{ + struct tmio_nand_data *data = dev_get_platdata(&dev->dev); + struct resource *fcr = platform_get_resource(dev, + IORESOURCE_MEM, 0); + struct resource *ccr = platform_get_resource(dev, + IORESOURCE_MEM, 1); + int irq = platform_get_irq(dev, 0); + struct tmio_nand *tmio; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + int retval; + + if (data == NULL) + dev_warn(&dev->dev, "NULL platform data!\n"); + + tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL); + if (!tmio) + return -ENOMEM; + + tmio->dev = dev; + + platform_set_drvdata(dev, tmio); + nand_chip = &tmio->chip; + mtd = nand_to_mtd(nand_chip); + mtd->name = "tmio-nand"; + mtd->dev.parent = &dev->dev; + + tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr)); + if (!tmio->ccr) + return -EIO; + + tmio->fcr_base = fcr->start & 0xfffff; + tmio->fcr = devm_ioremap(&dev->dev, fcr->start, resource_size(fcr)); + if (!tmio->fcr) + return -EIO; + + retval = tmio_hw_init(dev, tmio); + if (retval) + return retval; + + /* Set address of NAND IO lines */ + nand_chip->IO_ADDR_R = tmio->fcr; + nand_chip->IO_ADDR_W = tmio->fcr; + + /* Set address of hardware control function */ + nand_chip->cmd_ctrl = tmio_nand_hwcontrol; + nand_chip->dev_ready = tmio_nand_dev_ready; + nand_chip->read_byte = tmio_nand_read_byte; + nand_chip->write_buf = tmio_nand_write_buf; + nand_chip->read_buf = tmio_nand_read_buf; + + /* set eccmode using hardware ECC */ + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 6; + nand_chip->ecc.strength = 2; + nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; + nand_chip->ecc.calculate = tmio_nand_calculate_ecc; + nand_chip->ecc.correct = tmio_nand_correct_data; + + if (data) + nand_chip->badblock_pattern = data->badblock_pattern; + + /* 15 us command delay time */ + nand_chip->chip_delay = 15; + + retval = devm_request_irq(&dev->dev, irq, &tmio_irq, 0, + dev_name(&dev->dev), tmio); + if (retval) { + dev_err(&dev->dev, "request_irq error %d\n", retval); + goto err_irq; + } + + tmio->irq = irq; + nand_chip->waitfunc = tmio_nand_wait; + + /* Scan to find existence of the device */ + retval = nand_scan(mtd, 1); + if (retval) + goto err_irq; + + /* Register the partitions */ + retval = mtd_device_parse_register(mtd, + data ? data->part_parsers : NULL, + NULL, + data ? data->partition : NULL, + data ? data->num_partitions : 0); + if (!retval) + return retval; + + nand_release(mtd); + +err_irq: + tmio_hw_stop(dev, tmio); + return retval; +} + +static int tmio_remove(struct platform_device *dev) +{ + struct tmio_nand *tmio = platform_get_drvdata(dev); + + nand_release(nand_to_mtd(&tmio->chip)); + tmio_hw_stop(dev, tmio); + return 0; +} + +#ifdef CONFIG_PM +static int tmio_suspend(struct platform_device *dev, pm_message_t state) +{ + const struct mfd_cell *cell = mfd_get_cell(dev); + + if (cell->suspend) + cell->suspend(dev); + + tmio_hw_stop(dev, platform_get_drvdata(dev)); + return 0; +} + +static int tmio_resume(struct platform_device *dev) +{ + const struct mfd_cell *cell = mfd_get_cell(dev); + + /* FIXME - is this required or merely another attack of the broken + * SHARP platform? Looks suspicious. + */ + tmio_hw_init(dev, platform_get_drvdata(dev)); + + if (cell->resume) + cell->resume(dev); + + return 0; +} +#else +#define tmio_suspend NULL +#define tmio_resume NULL +#endif + +static struct platform_driver tmio_driver = { + .driver.name = "tmio-nand", + .driver.owner = THIS_MODULE, + .probe = tmio_probe, + .remove = tmio_remove, + .suspend = tmio_suspend, + .resume = tmio_resume, +}; + +module_platform_driver(tmio_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ian Molton, Dirk Opfer, Chris Humbert, Dmitry Baryshkov"); +MODULE_DESCRIPTION("NAND flash driver on Toshiba Mobile IO controller"); +MODULE_ALIAS("platform:tmio-nand"); diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c new file mode 100644 index 000000000000..b567d212fe7d --- /dev/null +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -0,0 +1,423 @@ +/* + * TXx9 NAND flash memory controller driver + * Based on RBTX49xx patch from CELF patch archive. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * (C) Copyright TOSHIBA CORPORATION 2004-2007 + * All Rights Reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* TXX9 NDFMC Registers */ +#define TXX9_NDFDTR 0x00 +#define TXX9_NDFMCR 0x04 +#define TXX9_NDFSR 0x08 +#define TXX9_NDFISR 0x0c +#define TXX9_NDFIMR 0x10 +#define TXX9_NDFSPR 0x14 +#define TXX9_NDFRSTR 0x18 /* not TX4939 */ + +/* NDFMCR : NDFMC Mode Control */ +#define TXX9_NDFMCR_WE 0x80 +#define TXX9_NDFMCR_ECC_ALL 0x60 +#define TXX9_NDFMCR_ECC_RESET 0x60 +#define TXX9_NDFMCR_ECC_READ 0x40 +#define TXX9_NDFMCR_ECC_ON 0x20 +#define TXX9_NDFMCR_ECC_OFF 0x00 +#define TXX9_NDFMCR_CE 0x10 +#define TXX9_NDFMCR_BSPRT 0x04 /* TX4925/TX4926 only */ +#define TXX9_NDFMCR_ALE 0x02 +#define TXX9_NDFMCR_CLE 0x01 +/* TX4939 only */ +#define TXX9_NDFMCR_X16 0x0400 +#define TXX9_NDFMCR_DMAREQ_MASK 0x0300 +#define TXX9_NDFMCR_DMAREQ_NODMA 0x0000 +#define TXX9_NDFMCR_DMAREQ_128 0x0100 +#define TXX9_NDFMCR_DMAREQ_256 0x0200 +#define TXX9_NDFMCR_DMAREQ_512 0x0300 +#define TXX9_NDFMCR_CS_MASK 0x0c +#define TXX9_NDFMCR_CS(ch) ((ch) << 2) + +/* NDFMCR : NDFMC Status */ +#define TXX9_NDFSR_BUSY 0x80 +/* TX4939 only */ +#define TXX9_NDFSR_DMARUN 0x40 + +/* NDFMCR : NDFMC Reset */ +#define TXX9_NDFRSTR_RST 0x01 + +struct txx9ndfmc_priv { + struct platform_device *dev; + struct nand_chip chip; + int cs; + const char *mtdname; +}; + +#define MAX_TXX9NDFMC_DEV 4 +struct txx9ndfmc_drvdata { + struct mtd_info *mtds[MAX_TXX9NDFMC_DEV]; + void __iomem *base; + unsigned char hold; /* in gbusclock */ + unsigned char spw; /* in gbusclock */ + struct nand_hw_control hw_control; +}; + +static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); + return txx9_priv->dev; +} + +static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) +{ + struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); + + return drvdata->base + (reg << plat->shift); +} + +static u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg) +{ + return __raw_readl(ndregaddr(dev, reg)); +} + +static void txx9ndfmc_write(struct platform_device *dev, + u32 val, unsigned int reg) +{ + __raw_writel(val, ndregaddr(dev, reg)); +} + +static uint8_t txx9ndfmc_read_byte(struct mtd_info *mtd) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + + return txx9ndfmc_read(dev, TXX9_NDFDTR); +} + +static void txx9ndfmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); + u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); + + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR); + while (len--) + __raw_writel(*buf++, ndfdtr); + txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); +} + +static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); + + while (len--) + *buf++ = __raw_readl(ndfdtr); +} + +static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); + struct platform_device *dev = txx9_priv->dev; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); + + if (ctrl & NAND_CTRL_CHANGE) { + u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); + + mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE); + mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0; + mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0; + /* TXX9_NDFMCR_CE bit is 0:high 1:low */ + mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0; + if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) { + mcr &= ~TXX9_NDFMCR_CS_MASK; + mcr |= TXX9_NDFMCR_CS(txx9_priv->cs); + } + txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); + } + if (cmd != NAND_CMD_NONE) + txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR); + if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) { + /* dummy write to update external latch */ + if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE) + txx9ndfmc_write(dev, 0, TXX9_NDFDTR); + } + mmiowb(); +} + +static int txx9ndfmc_dev_ready(struct mtd_info *mtd) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + + return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY); +} + +static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + int eccbytes; + u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); + + mcr &= ~TXX9_NDFMCR_ECC_ALL; + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); + for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) { + ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); + ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); + ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); + ecc_code += 3; + } + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); + return 0; +} + +static int txx9ndfmc_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int eccsize; + int corrected = 0; + int stat; + + for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) { + stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256); + if (stat < 0) + return stat; + corrected += stat; + buf += 256; + read_ecc += 3; + calc_ecc += 3; + } + return corrected; +} + +static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); + + mcr &= ~TXX9_NDFMCR_ECC_ALL; + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR); + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR); +} + +static void txx9ndfmc_initialize(struct platform_device *dev) +{ + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); + struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); + int tmout = 100; + + if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR) + ; /* no NDFRSTR. Write to NDFSPR resets the NDFMC. */ + else { + /* reset NDFMC */ + txx9ndfmc_write(dev, + txx9ndfmc_read(dev, TXX9_NDFRSTR) | + TXX9_NDFRSTR_RST, + TXX9_NDFRSTR); + while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) { + if (--tmout == 0) { + dev_err(&dev->dev, "reset failed.\n"); + break; + } + udelay(1); + } + } + /* setup Hold Time, Strobe Pulse Width */ + txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR); + txx9ndfmc_write(dev, + (plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ? + TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR); +} + +#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ + DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) + +static int txx9ndfmc_nand_scan(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret; + + ret = nand_scan_ident(mtd, 1, NULL); + if (!ret) { + if (mtd->writesize >= 512) { + /* Hardware ECC 6 byte ECC per 512 Byte data */ + chip->ecc.size = 512; + chip->ecc.bytes = 6; + } + ret = nand_scan_tail(mtd); + } + return ret; +} + +static int __init txx9ndfmc_probe(struct platform_device *dev) +{ + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); + int hold, spw; + int i; + struct txx9ndfmc_drvdata *drvdata; + unsigned long gbusclk = plat->gbus_clock; + struct resource *res; + + drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + drvdata->base = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(drvdata->base)) + return PTR_ERR(drvdata->base); + + hold = plat->hold ?: 20; /* tDH */ + spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */ + + hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold); + spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw); + if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD) + hold -= 2; /* actual hold time : (HOLD + 2) BUSCLK */ + spw -= 1; /* actual wait time : (SPW + 1) BUSCLK */ + hold = clamp(hold, 1, 15); + drvdata->hold = hold; + spw = clamp(spw, 1, 15); + drvdata->spw = spw; + dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", + (gbusclk + 500000) / 1000000, hold, spw); + + nand_hw_control_init(&drvdata->hw_control); + + platform_set_drvdata(dev, drvdata); + txx9ndfmc_initialize(dev); + + for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { + struct txx9ndfmc_priv *txx9_priv; + struct nand_chip *chip; + struct mtd_info *mtd; + + if (!(plat->ch_mask & (1 << i))) + continue; + txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv), + GFP_KERNEL); + if (!txx9_priv) + continue; + chip = &txx9_priv->chip; + mtd = nand_to_mtd(chip); + mtd->dev.parent = &dev->dev; + + chip->read_byte = txx9ndfmc_read_byte; + chip->read_buf = txx9ndfmc_read_buf; + chip->write_buf = txx9ndfmc_write_buf; + chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; + chip->dev_ready = txx9ndfmc_dev_ready; + chip->ecc.calculate = txx9ndfmc_calculate_ecc; + chip->ecc.correct = txx9ndfmc_correct_data; + chip->ecc.hwctl = txx9ndfmc_enable_hwecc; + chip->ecc.mode = NAND_ECC_HW; + /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ + chip->ecc.size = 256; + chip->ecc.bytes = 3; + chip->ecc.strength = 1; + chip->chip_delay = 100; + chip->controller = &drvdata->hw_control; + + nand_set_controller_data(chip, txx9_priv); + txx9_priv->dev = dev; + + if (plat->ch_mask != 1) { + txx9_priv->cs = i; + txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u", + dev_name(&dev->dev), i); + } else { + txx9_priv->cs = -1; + txx9_priv->mtdname = kstrdup(dev_name(&dev->dev), + GFP_KERNEL); + } + if (!txx9_priv->mtdname) { + kfree(txx9_priv); + dev_err(&dev->dev, "Unable to allocate MTD name.\n"); + continue; + } + if (plat->wide_mask & (1 << i)) + chip->options |= NAND_BUSWIDTH_16; + + if (txx9ndfmc_nand_scan(mtd)) { + kfree(txx9_priv->mtdname); + kfree(txx9_priv); + continue; + } + mtd->name = txx9_priv->mtdname; + + mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); + drvdata->mtds[i] = mtd; + } + + return 0; +} + +static int __exit txx9ndfmc_remove(struct platform_device *dev) +{ + struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); + int i; + + if (!drvdata) + return 0; + for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { + struct mtd_info *mtd = drvdata->mtds[i]; + struct nand_chip *chip; + struct txx9ndfmc_priv *txx9_priv; + + if (!mtd) + continue; + chip = mtd_to_nand(mtd); + txx9_priv = nand_get_controller_data(chip); + + nand_release(mtd); + kfree(txx9_priv->mtdname); + kfree(txx9_priv); + } + return 0; +} + +#ifdef CONFIG_PM +static int txx9ndfmc_resume(struct platform_device *dev) +{ + if (platform_get_drvdata(dev)) + txx9ndfmc_initialize(dev); + return 0; +} +#else +#define txx9ndfmc_resume NULL +#endif + +static struct platform_driver txx9ndfmc_driver = { + .remove = __exit_p(txx9ndfmc_remove), + .resume = txx9ndfmc_resume, + .driver = { + .name = "txx9ndfmc", + }, +}; + +module_platform_driver_probe(txx9ndfmc_driver, txx9ndfmc_probe); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); +MODULE_ALIAS("platform:txx9ndfmc"); diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c new file mode 100644 index 000000000000..5d7a1f8f580f --- /dev/null +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -0,0 +1,835 @@ +/* + * Copyright 2009-2015 Freescale Semiconductor, Inc. and others + * + * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver. + * Jason ported to M54418TWR and MVFA5 (VF610). + * Authors: Stefan Agner + * Bill Pringlemeir + * Shaohui Xie + * Jason Jin + * + * Based on original driver mpc5121_nfc.c. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Limitations: + * - Untested on MPC5125 and M54418. + * - DMA and pipelining not used. + * - 2K pages or less. + * - HW ECC: Only 2K page with 64+ OOB. + * - HW ECC: Only 24 and 32-bit error correction implemented. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "vf610_nfc" + +/* Register Offsets */ +#define NFC_FLASH_CMD1 0x3F00 +#define NFC_FLASH_CMD2 0x3F04 +#define NFC_COL_ADDR 0x3F08 +#define NFC_ROW_ADDR 0x3F0c +#define NFC_ROW_ADDR_INC 0x3F14 +#define NFC_FLASH_STATUS1 0x3F18 +#define NFC_FLASH_STATUS2 0x3F1c +#define NFC_CACHE_SWAP 0x3F28 +#define NFC_SECTOR_SIZE 0x3F2c +#define NFC_FLASH_CONFIG 0x3F30 +#define NFC_IRQ_STATUS 0x3F38 + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x1000) + +#define PAGE_2K 0x0800 +#define OOB_64 0x0040 +#define OOB_MAX 0x0100 + +/* + * NFC_CMD2[CODE] values. See section: + * - 31.4.7 Flash Command Code Description, Vybrid manual + * - 23.8.6 Flash Command Sequencer, MPC5125 manual + * + * Briefly these are bitmasks of controller cycles. + */ +#define READ_PAGE_CMD_CODE 0x7EE0 +#define READ_ONFI_PARAM_CMD_CODE 0x4860 +#define PROGRAM_PAGE_CMD_CODE 0x7FC0 +#define ERASE_CMD_CODE 0x4EC0 +#define READ_ID_CMD_CODE 0x4804 +#define RESET_CMD_CODE 0x4040 +#define STATUS_READ_CMD_CODE 0x4068 + +/* NFC ECC mode define */ +#define ECC_BYPASS 0 +#define ECC_45_BYTE 6 +#define ECC_60_BYTE 7 + +/*** Register Mask and bit definitions */ + +/* NFC_FLASH_CMD1 Field */ +#define CMD_BYTE2_MASK 0xFF000000 +#define CMD_BYTE2_SHIFT 24 + +/* NFC_FLASH_CM2 Field */ +#define CMD_BYTE1_MASK 0xFF000000 +#define CMD_BYTE1_SHIFT 24 +#define CMD_CODE_MASK 0x00FFFF00 +#define CMD_CODE_SHIFT 8 +#define BUFNO_MASK 0x00000006 +#define BUFNO_SHIFT 1 +#define START_BIT BIT(0) + +/* NFC_COL_ADDR Field */ +#define COL_ADDR_MASK 0x0000FFFF +#define COL_ADDR_SHIFT 0 + +/* NFC_ROW_ADDR Field */ +#define ROW_ADDR_MASK 0x00FFFFFF +#define ROW_ADDR_SHIFT 0 +#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000 +#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28 +#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000 +#define ROW_ADDR_CHIP_SEL_SHIFT 24 + +/* NFC_FLASH_STATUS2 Field */ +#define STATUS_BYTE1_MASK 0x000000FF + +/* NFC_FLASH_CONFIG Field */ +#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000 +#define CONFIG_ECC_SRAM_ADDR_SHIFT 22 +#define CONFIG_ECC_SRAM_REQ_BIT BIT(21) +#define CONFIG_DMA_REQ_BIT BIT(20) +#define CONFIG_ECC_MODE_MASK 0x000E0000 +#define CONFIG_ECC_MODE_SHIFT 17 +#define CONFIG_FAST_FLASH_BIT BIT(16) +#define CONFIG_16BIT BIT(7) +#define CONFIG_BOOT_MODE_BIT BIT(6) +#define CONFIG_ADDR_AUTO_INCR_BIT BIT(5) +#define CONFIG_BUFNO_AUTO_INCR_BIT BIT(4) +#define CONFIG_PAGE_CNT_MASK 0xF +#define CONFIG_PAGE_CNT_SHIFT 0 + +/* NFC_IRQ_STATUS Field */ +#define IDLE_IRQ_BIT BIT(29) +#define IDLE_EN_BIT BIT(20) +#define CMD_DONE_CLEAR_BIT BIT(18) +#define IDLE_CLEAR_BIT BIT(17) + +/* + * ECC status - seems to consume 8 bytes (double word). The documented + * status byte is located in the lowest byte of the second word (which is + * the 4th or 7th byte depending on endianness). + * Calculate an offset to store the ECC status at the end of the buffer. + */ +#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8) + +#define ECC_STATUS 0x4 +#define ECC_STATUS_MASK 0x80 +#define ECC_STATUS_ERR_COUNT 0x3F + +enum vf610_nfc_alt_buf { + ALT_BUF_DATA = 0, + ALT_BUF_ID = 1, + ALT_BUF_STAT = 2, + ALT_BUF_ONFI = 3, +}; + +enum vf610_nfc_variant { + NFC_VFC610 = 1, +}; + +struct vf610_nfc { + struct nand_chip chip; + struct device *dev; + void __iomem *regs; + struct completion cmd_done; + uint buf_offset; + int write_sz; + /* Status and ID are in alternate locations. */ + enum vf610_nfc_alt_buf alt_buf; + enum vf610_nfc_variant variant; + struct clk *clk; + bool use_hw_ecc; + u32 ecc_mode; +}; + +static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip); +} + +static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg) +{ + return readl(nfc->regs + reg); +} + +static inline void vf610_nfc_write(struct vf610_nfc *nfc, uint reg, u32 val) +{ + writel(val, nfc->regs + reg); +} + +static inline void vf610_nfc_set(struct vf610_nfc *nfc, uint reg, u32 bits) +{ + vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) | bits); +} + +static inline void vf610_nfc_clear(struct vf610_nfc *nfc, uint reg, u32 bits) +{ + vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) & ~bits); +} + +static inline void vf610_nfc_set_field(struct vf610_nfc *nfc, u32 reg, + u32 mask, u32 shift, u32 val) +{ + vf610_nfc_write(nfc, reg, + (vf610_nfc_read(nfc, reg) & (~mask)) | val << shift); +} + +static inline void vf610_nfc_memcpy(void *dst, const void __iomem *src, + size_t n) +{ + /* + * Use this accessor for the internal SRAM buffers. On the ARM + * Freescale Vybrid SoC it's known that the driver can treat + * the SRAM buffer as if it's memory. Other platform might need + * to treat the buffers differently. + * + * For the time being, use memcpy + */ + memcpy(dst, src, n); +} + +/* Clear flags for upcoming command */ +static inline void vf610_nfc_clear_status(struct vf610_nfc *nfc) +{ + u32 tmp = vf610_nfc_read(nfc, NFC_IRQ_STATUS); + + tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT; + vf610_nfc_write(nfc, NFC_IRQ_STATUS, tmp); +} + +static void vf610_nfc_done(struct vf610_nfc *nfc) +{ + unsigned long timeout = msecs_to_jiffies(100); + + /* + * Barrier is needed after this write. This write need + * to be done before reading the next register the first + * time. + * vf610_nfc_set implicates such a barrier by using writel + * to write to the register. + */ + vf610_nfc_set(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); + vf610_nfc_set(nfc, NFC_FLASH_CMD2, START_BIT); + + if (!wait_for_completion_timeout(&nfc->cmd_done, timeout)) + dev_warn(nfc->dev, "Timeout while waiting for BUSY.\n"); + + vf610_nfc_clear_status(nfc); +} + +static u8 vf610_nfc_get_id(struct vf610_nfc *nfc, int col) +{ + u32 flash_id; + + if (col < 4) { + flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS1); + flash_id >>= (3 - col) * 8; + } else { + flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS2); + flash_id >>= 24; + } + + return flash_id & 0xff; +} + +static u8 vf610_nfc_get_status(struct vf610_nfc *nfc) +{ + return vf610_nfc_read(nfc, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK; +} + +static void vf610_nfc_send_command(struct vf610_nfc *nfc, u32 cmd_byte1, + u32 cmd_code) +{ + u32 tmp; + + vf610_nfc_clear_status(nfc); + + tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD2); + tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK); + tmp |= cmd_byte1 << CMD_BYTE1_SHIFT; + tmp |= cmd_code << CMD_CODE_SHIFT; + vf610_nfc_write(nfc, NFC_FLASH_CMD2, tmp); +} + +static void vf610_nfc_send_commands(struct vf610_nfc *nfc, u32 cmd_byte1, + u32 cmd_byte2, u32 cmd_code) +{ + u32 tmp; + + vf610_nfc_send_command(nfc, cmd_byte1, cmd_code); + + tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD1); + tmp &= ~CMD_BYTE2_MASK; + tmp |= cmd_byte2 << CMD_BYTE2_SHIFT; + vf610_nfc_write(nfc, NFC_FLASH_CMD1, tmp); +} + +static irqreturn_t vf610_nfc_irq(int irq, void *data) +{ + struct mtd_info *mtd = data; + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); + complete(&nfc->cmd_done); + + return IRQ_HANDLED; +} + +static void vf610_nfc_addr_cycle(struct vf610_nfc *nfc, int column, int page) +{ + if (column != -1) { + if (nfc->chip.options & NAND_BUSWIDTH_16) + column = column / 2; + vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK, + COL_ADDR_SHIFT, column); + } + if (page != -1) + vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK, + ROW_ADDR_SHIFT, page); +} + +static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode) +{ + vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, + CONFIG_ECC_MODE_MASK, + CONFIG_ECC_MODE_SHIFT, ecc_mode); +} + +static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size) +{ + vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size); +} + +static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0; + + nfc->buf_offset = max(column, 0); + nfc->alt_buf = ALT_BUF_DATA; + + switch (command) { + case NAND_CMD_SEQIN: + /* Use valid column/page from preread... */ + vf610_nfc_addr_cycle(nfc, column, page); + nfc->buf_offset = 0; + + /* + * SEQIN => data => PAGEPROG sequence is done by the controller + * hence we do not need to issue the command here... + */ + return; + case NAND_CMD_PAGEPROG: + trfr_sz += nfc->write_sz; + vf610_nfc_transfer_size(nfc, trfr_sz); + vf610_nfc_send_commands(nfc, NAND_CMD_SEQIN, + command, PROGRAM_PAGE_CMD_CODE); + if (nfc->use_hw_ecc) + vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); + else + vf610_nfc_ecc_mode(nfc, ECC_BYPASS); + break; + + case NAND_CMD_RESET: + vf610_nfc_transfer_size(nfc, 0); + vf610_nfc_send_command(nfc, command, RESET_CMD_CODE); + break; + + case NAND_CMD_READOOB: + trfr_sz += mtd->oobsize; + column = mtd->writesize; + vf610_nfc_transfer_size(nfc, trfr_sz); + vf610_nfc_send_commands(nfc, NAND_CMD_READ0, + NAND_CMD_READSTART, READ_PAGE_CMD_CODE); + vf610_nfc_addr_cycle(nfc, column, page); + vf610_nfc_ecc_mode(nfc, ECC_BYPASS); + break; + + case NAND_CMD_READ0: + trfr_sz += mtd->writesize + mtd->oobsize; + vf610_nfc_transfer_size(nfc, trfr_sz); + vf610_nfc_send_commands(nfc, NAND_CMD_READ0, + NAND_CMD_READSTART, READ_PAGE_CMD_CODE); + vf610_nfc_addr_cycle(nfc, column, page); + vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); + break; + + case NAND_CMD_PARAM: + nfc->alt_buf = ALT_BUF_ONFI; + trfr_sz = 3 * sizeof(struct nand_onfi_params); + vf610_nfc_transfer_size(nfc, trfr_sz); + vf610_nfc_send_command(nfc, command, READ_ONFI_PARAM_CMD_CODE); + vf610_nfc_addr_cycle(nfc, -1, column); + vf610_nfc_ecc_mode(nfc, ECC_BYPASS); + break; + + case NAND_CMD_ERASE1: + vf610_nfc_transfer_size(nfc, 0); + vf610_nfc_send_commands(nfc, command, + NAND_CMD_ERASE2, ERASE_CMD_CODE); + vf610_nfc_addr_cycle(nfc, column, page); + break; + + case NAND_CMD_READID: + nfc->alt_buf = ALT_BUF_ID; + nfc->buf_offset = 0; + vf610_nfc_transfer_size(nfc, 0); + vf610_nfc_send_command(nfc, command, READ_ID_CMD_CODE); + vf610_nfc_addr_cycle(nfc, -1, column); + break; + + case NAND_CMD_STATUS: + nfc->alt_buf = ALT_BUF_STAT; + vf610_nfc_transfer_size(nfc, 0); + vf610_nfc_send_command(nfc, command, STATUS_READ_CMD_CODE); + break; + default: + return; + } + + vf610_nfc_done(nfc); + + nfc->use_hw_ecc = false; + nfc->write_sz = 0; +} + +static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + uint c = nfc->buf_offset; + + /* Alternate buffers are only supported through read_byte */ + WARN_ON(nfc->alt_buf); + + vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len); + + nfc->buf_offset += len; +} + +static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + uint c = nfc->buf_offset; + uint l; + + l = min_t(uint, len, mtd->writesize + mtd->oobsize - c); + vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l); + + nfc->write_sz += l; + nfc->buf_offset += l; +} + +static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + u8 tmp; + uint c = nfc->buf_offset; + + switch (nfc->alt_buf) { + case ALT_BUF_ID: + tmp = vf610_nfc_get_id(nfc, c); + break; + case ALT_BUF_STAT: + tmp = vf610_nfc_get_status(nfc); + break; +#ifdef __LITTLE_ENDIAN + case ALT_BUF_ONFI: + /* Reverse byte since the controller uses big endianness */ + c = nfc->buf_offset ^ 0x3; + /* fall-through */ +#endif + default: + tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c)); + break; + } + nfc->buf_offset++; + return tmp; +} + +static u16 vf610_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); + return tmp; +} + +/* If not provided, upper layers apply a fixed delay. */ +static int vf610_nfc_dev_ready(struct mtd_info *mtd) +{ + /* NFC handles R/B internally; always ready. */ + return 1; +} + +/* + * This function supports Vybrid only (MPC5125 would have full RB and four CS) + */ +static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + u32 tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR); + + /* Vybrid only (MPC5125 would have full RB and four CS) */ + if (nfc->variant != NFC_VFC610) + return; + + tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK); + + if (chip >= 0) { + tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; + tmp |= BIT(chip) << ROW_ADDR_CHIP_SEL_SHIFT; + } + + vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp); +} + +static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *oob, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS; + u8 ecc_status; + u8 ecc_count; + int flips_threshold = nfc->chip.ecc.strength / 2; + + ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff; + ecc_count = ecc_status & ECC_STATUS_ERR_COUNT; + + if (!(ecc_status & ECC_STATUS_MASK)) + return ecc_count; + + /* Read OOB without ECC unit enabled */ + vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page); + vf610_nfc_read_buf(mtd, oob, mtd->oobsize); + + /* + * On an erased page, bit count (including OOB) should be zero or + * at least less then half of the ECC strength. + */ + return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob, + mtd->oobsize, NULL, 0, + flips_threshold); +} + +static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int eccsize = chip->ecc.size; + int stat; + + nand_read_page_op(chip, page, 0, buf, eccsize); + if (oob_required) + vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page); + + if (stat < 0) { + mtd->ecc_stats.failed++; + return 0; + } else { + mtd->ecc_stats.corrected += stat; + return stat; + } +} + +static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); + if (oob_required) + vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Always write whole page including OOB due to HW ECC */ + nfc->use_hw_ecc = true; + nfc->write_sz = mtd->writesize + mtd->oobsize; + + return nand_prog_page_end_op(chip); +} + +static const struct of_device_id vf610_nfc_dt_ids[] = { + { .compatible = "fsl,vf610-nfc", .data = (void *)NFC_VFC610 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vf610_nfc_dt_ids); + +static void vf610_nfc_preinit_controller(struct vf610_nfc *nfc) +{ + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT); + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT); + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT); + vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT); + + /* Disable virtual pages, only one elementary transfer unit */ + vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, + CONFIG_PAGE_CNT_SHIFT, 1); +} + +static void vf610_nfc_init_controller(struct vf610_nfc *nfc) +{ + if (nfc->chip.options & NAND_BUSWIDTH_16) + vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); + else + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); + + if (nfc->chip.ecc.mode == NAND_ECC_HW) { + /* Set ECC status offset in SRAM */ + vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, + CONFIG_ECC_SRAM_ADDR_MASK, + CONFIG_ECC_SRAM_ADDR_SHIFT, + ECC_SRAM_ADDR >> 3); + + /* Enable ECC status in SRAM */ + vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT); + } +} + +static int vf610_nfc_probe(struct platform_device *pdev) +{ + struct vf610_nfc *nfc; + struct resource *res; + struct mtd_info *mtd; + struct nand_chip *chip; + struct device_node *child; + const struct of_device_id *of_id; + int err; + int irq; + + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->dev = &pdev->dev; + chip = &nfc->chip; + mtd = nand_to_mtd(chip); + + mtd->owner = THIS_MODULE; + mtd->dev.parent = nfc->dev; + mtd->name = DRV_NAME; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->regs = devm_ioremap_resource(nfc->dev, res); + if (IS_ERR(nfc->regs)) + return PTR_ERR(nfc->regs); + + nfc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(nfc->clk)) + return PTR_ERR(nfc->clk); + + err = clk_prepare_enable(nfc->clk); + if (err) { + dev_err(nfc->dev, "Unable to enable clock!\n"); + return err; + } + + of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev); + nfc->variant = (enum vf610_nfc_variant)of_id->data; + + for_each_available_child_of_node(nfc->dev->of_node, child) { + if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) { + + if (nand_get_flash_node(chip)) { + dev_err(nfc->dev, + "Only one NAND chip supported!\n"); + err = -EINVAL; + goto err_disable_clk; + } + + nand_set_flash_node(chip, child); + } + } + + if (!nand_get_flash_node(chip)) { + dev_err(nfc->dev, "NAND chip sub-node missing!\n"); + err = -ENODEV; + goto err_disable_clk; + } + + chip->dev_ready = vf610_nfc_dev_ready; + chip->cmdfunc = vf610_nfc_command; + chip->read_byte = vf610_nfc_read_byte; + chip->read_word = vf610_nfc_read_word; + chip->read_buf = vf610_nfc_read_buf; + chip->write_buf = vf610_nfc_write_buf; + chip->select_chip = vf610_nfc_select_chip; + chip->onfi_set_features = nand_onfi_get_set_features_notsupp; + chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + + init_completion(&nfc->cmd_done); + + err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd); + if (err) { + dev_err(nfc->dev, "Error requesting IRQ!\n"); + goto err_disable_clk; + } + + vf610_nfc_preinit_controller(nfc); + + /* first scan to find the device and get the page size */ + err = nand_scan_ident(mtd, 1, NULL); + if (err) + goto err_disable_clk; + + vf610_nfc_init_controller(nfc); + + /* Bad block options. */ + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + /* Single buffer only, max 256 OOB minus ECC status */ + if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { + dev_err(nfc->dev, "Unsupported flash page size\n"); + err = -ENXIO; + goto err_disable_clk; + } + + if (chip->ecc.mode == NAND_ECC_HW) { + if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { + dev_err(nfc->dev, "Unsupported flash with hwecc\n"); + err = -ENXIO; + goto err_disable_clk; + } + + if (chip->ecc.size != mtd->writesize) { + dev_err(nfc->dev, "Step size needs to be page size\n"); + err = -ENXIO; + goto err_disable_clk; + } + + /* Only 64 byte ECC layouts known */ + if (mtd->oobsize > 64) + mtd->oobsize = 64; + + /* + * mtd->ecclayout is not specified here because we're using the + * default large page ECC layout defined in NAND core. + */ + if (chip->ecc.strength == 32) { + nfc->ecc_mode = ECC_60_BYTE; + chip->ecc.bytes = 60; + } else if (chip->ecc.strength == 24) { + nfc->ecc_mode = ECC_45_BYTE; + chip->ecc.bytes = 45; + } else { + dev_err(nfc->dev, "Unsupported ECC strength\n"); + err = -ENXIO; + goto err_disable_clk; + } + + chip->ecc.read_page = vf610_nfc_read_page; + chip->ecc.write_page = vf610_nfc_write_page; + + chip->ecc.size = PAGE_2K; + } + + /* second phase scan */ + err = nand_scan_tail(mtd); + if (err) + goto err_disable_clk; + + platform_set_drvdata(pdev, mtd); + + /* Register device in MTD */ + err = mtd_device_register(mtd, NULL, 0); + if (err) + goto err_cleanup_nand; + return 0; + +err_cleanup_nand: + nand_cleanup(chip); +err_disable_clk: + clk_disable_unprepare(nfc->clk); + return err; +} + +static int vf610_nfc_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + nand_release(mtd); + clk_disable_unprepare(nfc->clk); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int vf610_nfc_suspend(struct device *dev) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + clk_disable_unprepare(nfc->clk); + return 0; +} + +static int vf610_nfc_resume(struct device *dev) +{ + int err; + + struct mtd_info *mtd = dev_get_drvdata(dev); + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + err = clk_prepare_enable(nfc->clk); + if (err) + return err; + + vf610_nfc_preinit_controller(nfc); + vf610_nfc_init_controller(nfc); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(vf610_nfc_pm_ops, vf610_nfc_suspend, vf610_nfc_resume); + +static struct platform_driver vf610_nfc_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = vf610_nfc_dt_ids, + .pm = &vf610_nfc_pm_ops, + }, + .probe = vf610_nfc_probe, + .remove = vf610_nfc_remove, +}; + +module_platform_driver(vf610_nfc_driver); + +MODULE_AUTHOR("Stefan Agner "); +MODULE_DESCRIPTION("Freescale VF610/MPC5125 NFC MTD NAND driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/raw/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c new file mode 100644 index 000000000000..9926b4e3d69d --- /dev/null +++ b/drivers/mtd/nand/raw/xway_nand.c @@ -0,0 +1,245 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright © 2012 John Crispin + * Copyright © 2016 Hauke Mehrtens + */ + +#include +#include +#include + +#include + +/* nand registers */ +#define EBU_ADDSEL1 0x24 +#define EBU_NAND_CON 0xB0 +#define EBU_NAND_WAIT 0xB4 +#define NAND_WAIT_RD BIT(0) /* NAND flash status output */ +#define NAND_WAIT_WR_C BIT(3) /* NAND Write/Read complete */ +#define EBU_NAND_ECC0 0xB8 +#define EBU_NAND_ECC_AC 0xBC + +/* + * nand commands + * The pins of the NAND chip are selected based on the address bits of the + * "register" read and write. There are no special registers, but an + * address range and the lower address bits are used to activate the + * correct line. For example when the bit (1 << 2) is set in the address + * the ALE pin will be activated. + */ +#define NAND_CMD_ALE BIT(2) /* address latch enable */ +#define NAND_CMD_CLE BIT(3) /* command latch enable */ +#define NAND_CMD_CS BIT(4) /* chip select */ +#define NAND_CMD_SE BIT(5) /* spare area access latch */ +#define NAND_CMD_WP BIT(6) /* write protect */ +#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE) +#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE) +#define NAND_WRITE_DATA (NAND_CMD_CS) +#define NAND_READ_DATA (NAND_CMD_CS) + +/* we need to tel the ebu which addr we mapped the nand to */ +#define ADDSEL1_MASK(x) (x << 4) +#define ADDSEL1_REGEN 1 + +/* we need to tell the EBU that we have nand attached and set it up properly */ +#define BUSCON1_SETUP (1 << 22) +#define BUSCON1_BCGEN_RES (0x3 << 12) +#define BUSCON1_WAITWRC2 (2 << 8) +#define BUSCON1_WAITRDC2 (2 << 6) +#define BUSCON1_HOLDC1 (1 << 4) +#define BUSCON1_RECOVC1 (1 << 2) +#define BUSCON1_CMULT4 1 + +#define NAND_CON_CE (1 << 20) +#define NAND_CON_OUT_CS1 (1 << 10) +#define NAND_CON_IN_CS1 (1 << 8) +#define NAND_CON_PRE_P (1 << 7) +#define NAND_CON_WP_P (1 << 6) +#define NAND_CON_SE_P (1 << 5) +#define NAND_CON_CS_P (1 << 4) +#define NAND_CON_CSMUX (1 << 1) +#define NAND_CON_NANDM 1 + +struct xway_nand_data { + struct nand_chip chip; + unsigned long csflags; + void __iomem *nandaddr; +}; + +static u8 xway_readb(struct mtd_info *mtd, int op) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct xway_nand_data *data = nand_get_controller_data(chip); + + return readb(data->nandaddr + op); +} + +static void xway_writeb(struct mtd_info *mtd, int op, u8 value) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct xway_nand_data *data = nand_get_controller_data(chip); + + writeb(value, data->nandaddr + op); +} + +static void xway_select_chip(struct mtd_info *mtd, int select) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct xway_nand_data *data = nand_get_controller_data(chip); + + switch (select) { + case -1: + ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON); + ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON); + spin_unlock_irqrestore(&ebu_lock, data->csflags); + break; + case 0: + spin_lock_irqsave(&ebu_lock, data->csflags); + ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON); + ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON); + break; + default: + BUG(); + } +} + +static void xway_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + xway_writeb(mtd, NAND_WRITE_CMD, cmd); + else if (ctrl & NAND_ALE) + xway_writeb(mtd, NAND_WRITE_ADDR, cmd); + + while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) + ; +} + +static int xway_dev_ready(struct mtd_info *mtd) +{ + return ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_RD; +} + +static unsigned char xway_read_byte(struct mtd_info *mtd) +{ + return xway_readb(mtd, NAND_READ_DATA); +} + +static void xway_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + buf[i] = xway_readb(mtd, NAND_WRITE_DATA); +} + +static void xway_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + xway_writeb(mtd, NAND_WRITE_DATA, buf[i]); +} + +/* + * Probe for the NAND device. + */ +static int xway_nand_probe(struct platform_device *pdev) +{ + struct xway_nand_data *data; + struct mtd_info *mtd; + struct resource *res; + int err; + u32 cs; + u32 cs_flag = 0; + + /* Allocate memory for the device structure (and zero it) */ + data = devm_kzalloc(&pdev->dev, sizeof(struct xway_nand_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->nandaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->nandaddr)) + return PTR_ERR(data->nandaddr); + + nand_set_flash_node(&data->chip, pdev->dev.of_node); + mtd = nand_to_mtd(&data->chip); + mtd->dev.parent = &pdev->dev; + + data->chip.cmd_ctrl = xway_cmd_ctrl; + data->chip.dev_ready = xway_dev_ready; + data->chip.select_chip = xway_select_chip; + data->chip.write_buf = xway_write_buf; + data->chip.read_buf = xway_read_buf; + data->chip.read_byte = xway_read_byte; + data->chip.chip_delay = 30; + + data->chip.ecc.mode = NAND_ECC_SOFT; + data->chip.ecc.algo = NAND_ECC_HAMMING; + + platform_set_drvdata(pdev, data); + nand_set_controller_data(&data->chip, data); + + /* load our CS from the DT. Either we find a valid 1 or default to 0 */ + err = of_property_read_u32(pdev->dev.of_node, "lantiq,cs", &cs); + if (!err && cs == 1) + cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1; + + /* setup the EBU to run in NAND mode on our base addr */ + ltq_ebu_w32(CPHYSADDR(data->nandaddr) + | ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1); + + ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2 + | BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1 + | BUSCON1_CMULT4, LTQ_EBU_BUSCON1); + + ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P + | NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P + | cs_flag, EBU_NAND_CON); + + /* Scan to find existence of the device */ + err = nand_scan(mtd, 1); + if (err) + return err; + + err = mtd_device_register(mtd, NULL, 0); + if (err) + nand_release(mtd); + + return err; +} + +/* + * Remove a NAND device. + */ +static int xway_nand_remove(struct platform_device *pdev) +{ + struct xway_nand_data *data = platform_get_drvdata(pdev); + + nand_release(nand_to_mtd(&data->chip)); + + return 0; +} + +static const struct of_device_id xway_nand_match[] = { + { .compatible = "lantiq,nand-xway" }, + {}, +}; + +static struct platform_driver xway_nand_driver = { + .probe = xway_nand_probe, + .remove = xway_nand_remove, + .driver = { + .name = "lantiq,nand-xway", + .of_match_table = xway_nand_match, + }, +}; + +builtin_platform_driver(xway_nand_driver); diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c deleted file mode 100644 index b5bc5f106c09..000000000000 --- a/drivers/mtd/nand/s3c2410.c +++ /dev/null @@ -1,1295 +0,0 @@ -/* - * Copyright © 2004-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * Samsung S3C2410/S3C2440/S3C2412 NAND driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#define pr_fmt(fmt) "nand-s3c2410: " fmt - -#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG -#define DEBUG -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define S3C2410_NFREG(x) (x) - -#define S3C2410_NFCONF S3C2410_NFREG(0x00) -#define S3C2410_NFCMD S3C2410_NFREG(0x04) -#define S3C2410_NFADDR S3C2410_NFREG(0x08) -#define S3C2410_NFDATA S3C2410_NFREG(0x0C) -#define S3C2410_NFSTAT S3C2410_NFREG(0x10) -#define S3C2410_NFECC S3C2410_NFREG(0x14) -#define S3C2440_NFCONT S3C2410_NFREG(0x04) -#define S3C2440_NFCMD S3C2410_NFREG(0x08) -#define S3C2440_NFADDR S3C2410_NFREG(0x0C) -#define S3C2440_NFDATA S3C2410_NFREG(0x10) -#define S3C2440_NFSTAT S3C2410_NFREG(0x20) -#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) -#define S3C2412_NFSTAT S3C2410_NFREG(0x28) -#define S3C2412_NFMECC0 S3C2410_NFREG(0x34) -#define S3C2410_NFCONF_EN (1<<15) -#define S3C2410_NFCONF_INITECC (1<<12) -#define S3C2410_NFCONF_nFCE (1<<11) -#define S3C2410_NFCONF_TACLS(x) ((x)<<8) -#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4) -#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0) -#define S3C2410_NFSTAT_BUSY (1<<0) -#define S3C2440_NFCONF_TACLS(x) ((x)<<12) -#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) -#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) -#define S3C2440_NFCONT_INITECC (1<<4) -#define S3C2440_NFCONT_nFCE (1<<1) -#define S3C2440_NFCONT_ENABLE (1<<0) -#define S3C2440_NFSTAT_READY (1<<0) -#define S3C2412_NFCONF_NANDBOOT (1<<31) -#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5) -#define S3C2412_NFCONT_nFCE0 (1<<1) -#define S3C2412_NFSTAT_READY (1<<0) - -/* new oob placement block for use with hardware ecc generation - */ -static int s3c2410_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->offset = 0; - oobregion->length = 3; - - return 0; -} - -static int s3c2410_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->offset = 8; - oobregion->length = 8; - - return 0; -} - -static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = { - .ecc = s3c2410_ooblayout_ecc, - .free = s3c2410_ooblayout_free, -}; - -/* controller and mtd information */ - -struct s3c2410_nand_info; - -/** - * struct s3c2410_nand_mtd - driver MTD structure - * @mtd: The MTD instance to pass to the MTD layer. - * @chip: The NAND chip information. - * @set: The platform information supplied for this set of NAND chips. - * @info: Link back to the hardware information. - * @scan_res: The result from calling nand_scan_ident(). -*/ -struct s3c2410_nand_mtd { - struct nand_chip chip; - struct s3c2410_nand_set *set; - struct s3c2410_nand_info *info; - int scan_res; -}; - -enum s3c_cpu_type { - TYPE_S3C2410, - TYPE_S3C2412, - TYPE_S3C2440, -}; - -enum s3c_nand_clk_state { - CLOCK_DISABLE = 0, - CLOCK_ENABLE, - CLOCK_SUSPEND, -}; - -/* overview of the s3c2410 nand state */ - -/** - * struct s3c2410_nand_info - NAND controller state. - * @mtds: An array of MTD instances on this controoler. - * @platform: The platform data for this board. - * @device: The platform device we bound to. - * @clk: The clock resource for this controller. - * @regs: The area mapped for the hardware registers. - * @sel_reg: Pointer to the register controlling the NAND selection. - * @sel_bit: The bit in @sel_reg to select the NAND chip. - * @mtd_count: The number of MTDs created from this controller. - * @save_sel: The contents of @sel_reg to be saved over suspend. - * @clk_rate: The clock rate from @clk. - * @clk_state: The current clock state. - * @cpu_type: The exact type of this controller. - */ -struct s3c2410_nand_info { - /* mtd info */ - struct nand_hw_control controller; - struct s3c2410_nand_mtd *mtds; - struct s3c2410_platform_nand *platform; - - /* device info */ - struct device *device; - struct clk *clk; - void __iomem *regs; - void __iomem *sel_reg; - int sel_bit; - int mtd_count; - unsigned long save_sel; - unsigned long clk_rate; - enum s3c_nand_clk_state clk_state; - - enum s3c_cpu_type cpu_type; - -#ifdef CONFIG_ARM_S3C24XX_CPUFREQ - struct notifier_block freq_transition; -#endif -}; - -struct s3c24XX_nand_devtype_data { - enum s3c_cpu_type type; -}; - -static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = { - .type = TYPE_S3C2410, -}; - -static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = { - .type = TYPE_S3C2412, -}; - -static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = { - .type = TYPE_S3C2440, -}; - -/* conversion functions */ - -static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct s3c2410_nand_mtd, - chip); -} - -static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) -{ - return s3c2410_nand_mtd_toours(mtd)->info; -} - -static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) -{ - return platform_get_drvdata(dev); -} - -static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) -{ - return dev_get_platdata(&dev->dev); -} - -static inline int allow_clk_suspend(struct s3c2410_nand_info *info) -{ -#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP - return 1; -#else - return 0; -#endif -} - -/** - * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock. - * @info: The controller instance. - * @new_state: State to which clock should be set. - */ -static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info, - enum s3c_nand_clk_state new_state) -{ - if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND) - return; - - if (info->clk_state == CLOCK_ENABLE) { - if (new_state != CLOCK_ENABLE) - clk_disable_unprepare(info->clk); - } else { - if (new_state == CLOCK_ENABLE) - clk_prepare_enable(info->clk); - } - - info->clk_state = new_state; -} - -/* timing calculations */ - -#define NS_IN_KHZ 1000000 - -/** - * s3c_nand_calc_rate - calculate timing data. - * @wanted: The cycle time in nanoseconds. - * @clk: The clock rate in kHz. - * @max: The maximum divider value. - * - * Calculate the timing value from the given parameters. - */ -static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) -{ - int result; - - result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ); - - pr_debug("result %d from %ld, %d\n", result, clk, wanted); - - if (result > max) { - pr_err("%d ns is too big for current clock rate %ld\n", - wanted, clk); - return -1; - } - - if (result < 1) - result = 1; - - return result; -} - -#define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) - -/* controller setup */ - -/** - * s3c2410_nand_setrate - setup controller timing information. - * @info: The controller instance. - * - * Given the information supplied by the platform, calculate and set - * the necessary timing registers in the hardware to generate the - * necessary timing cycles to the hardware. - */ -static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) -{ - struct s3c2410_platform_nand *plat = info->platform; - int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; - int tacls, twrph0, twrph1; - unsigned long clkrate = clk_get_rate(info->clk); - unsigned long uninitialized_var(set), cfg, uninitialized_var(mask); - unsigned long flags; - - /* calculate the timing information for the controller */ - - info->clk_rate = clkrate; - clkrate /= 1000; /* turn clock into kHz for ease of use */ - - if (plat != NULL) { - tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max); - twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8); - twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8); - } else { - /* default timings */ - tacls = tacls_max; - twrph0 = 8; - twrph1 = 8; - } - - if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { - dev_err(info->device, "cannot get suitable timings\n"); - return -EINVAL; - } - - dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", - tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), - twrph1, to_ns(twrph1, clkrate)); - - switch (info->cpu_type) { - case TYPE_S3C2410: - mask = (S3C2410_NFCONF_TACLS(3) | - S3C2410_NFCONF_TWRPH0(7) | - S3C2410_NFCONF_TWRPH1(7)); - set = S3C2410_NFCONF_EN; - set |= S3C2410_NFCONF_TACLS(tacls - 1); - set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); - set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); - break; - - case TYPE_S3C2440: - case TYPE_S3C2412: - mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | - S3C2440_NFCONF_TWRPH0(7) | - S3C2440_NFCONF_TWRPH1(7)); - - set = S3C2440_NFCONF_TACLS(tacls - 1); - set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); - set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); - break; - - default: - BUG(); - } - - local_irq_save(flags); - - cfg = readl(info->regs + S3C2410_NFCONF); - cfg &= ~mask; - cfg |= set; - writel(cfg, info->regs + S3C2410_NFCONF); - - local_irq_restore(flags); - - dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); - - return 0; -} - -/** - * s3c2410_nand_inithw - basic hardware initialisation - * @info: The hardware state. - * - * Do the basic initialisation of the hardware, using s3c2410_nand_setrate() - * to setup the hardware access speeds and set the controller to be enabled. -*/ -static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) -{ - int ret; - - ret = s3c2410_nand_setrate(info); - if (ret < 0) - return ret; - - switch (info->cpu_type) { - case TYPE_S3C2410: - default: - break; - - case TYPE_S3C2440: - case TYPE_S3C2412: - /* enable the controller and de-assert nFCE */ - - writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); - } - - return 0; -} - -/** - * s3c2410_nand_select_chip - select the given nand chip - * @mtd: The MTD instance for this chip. - * @chip: The chip number. - * - * This is called by the MTD layer to either select a given chip for the - * @mtd instance, or to indicate that the access has finished and the - * chip can be de-selected. - * - * The routine ensures that the nFCE line is correctly setup, and any - * platform specific selection code is called to route nFCE to the specific - * chip. - */ -static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) -{ - struct s3c2410_nand_info *info; - struct s3c2410_nand_mtd *nmtd; - struct nand_chip *this = mtd_to_nand(mtd); - unsigned long cur; - - nmtd = nand_get_controller_data(this); - info = nmtd->info; - - if (chip != -1) - s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); - - cur = readl(info->sel_reg); - - if (chip == -1) { - cur |= info->sel_bit; - } else { - if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { - dev_err(info->device, "invalid chip %d\n", chip); - return; - } - - if (info->platform != NULL) { - if (info->platform->select_chip != NULL) - (info->platform->select_chip) (nmtd->set, chip); - } - - cur &= ~info->sel_bit; - } - - writel(cur, info->sel_reg); - - if (chip == -1) - s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); -} - -/* s3c2410_nand_hwcontrol - * - * Issue command and address cycles to the chip -*/ - -static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writeb(cmd, info->regs + S3C2410_NFCMD); - else - writeb(cmd, info->regs + S3C2410_NFADDR); -} - -/* command and control functions */ - -static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writeb(cmd, info->regs + S3C2440_NFCMD); - else - writeb(cmd, info->regs + S3C2440_NFADDR); -} - -/* s3c2410_nand_devready() - * - * returns 0 if the nand is busy, 1 if it is ready -*/ - -static int s3c2410_nand_devready(struct mtd_info *mtd) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; -} - -static int s3c2440_nand_devready(struct mtd_info *mtd) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; -} - -static int s3c2412_nand_devready(struct mtd_info *mtd) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY; -} - -/* ECC handling functions */ - -static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned int diff0, diff1, diff2; - unsigned int bit, byte; - - pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); - - diff0 = read_ecc[0] ^ calc_ecc[0]; - diff1 = read_ecc[1] ^ calc_ecc[1]; - diff2 = read_ecc[2] ^ calc_ecc[2]; - - pr_debug("%s: rd %*phN calc %*phN diff %02x%02x%02x\n", - __func__, 3, read_ecc, 3, calc_ecc, - diff0, diff1, diff2); - - if (diff0 == 0 && diff1 == 0 && diff2 == 0) - return 0; /* ECC is ok */ - - /* sometimes people do not think about using the ECC, so check - * to see if we have an 0xff,0xff,0xff read ECC and then ignore - * the error, on the assumption that this is an un-eccd page. - */ - if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff - && info->platform->ignore_unset_ecc) - return 0; - - /* Can we correct this ECC (ie, one row and column change). - * Note, this is similar to the 256 error code on smartmedia */ - - if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && - ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && - ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { - /* calculate the bit position of the error */ - - bit = ((diff2 >> 3) & 1) | - ((diff2 >> 4) & 2) | - ((diff2 >> 5) & 4); - - /* calculate the byte position of the error */ - - byte = ((diff2 << 7) & 0x100) | - ((diff1 << 0) & 0x80) | - ((diff1 << 1) & 0x40) | - ((diff1 << 2) & 0x20) | - ((diff1 << 3) & 0x10) | - ((diff0 >> 4) & 0x08) | - ((diff0 >> 3) & 0x04) | - ((diff0 >> 2) & 0x02) | - ((diff0 >> 1) & 0x01); - - dev_dbg(info->device, "correcting error bit %d, byte %d\n", - bit, byte); - - dat[byte] ^= (1 << bit); - return 1; - } - - /* if there is only one bit difference in the ECC, then - * one of only a row or column parity has changed, which - * means the error is most probably in the ECC itself */ - - diff0 |= (diff1 << 8); - diff0 |= (diff2 << 16); - - /* equal to "(diff0 & ~(1 << __ffs(diff0)))" */ - if ((diff0 & (diff0 - 1)) == 0) - return 1; - - return -1; -} - -/* ECC functions - * - * These allow the s3c2410 and s3c2440 to use the controller's ECC - * generator block to ECC the data as it passes through] -*/ - -static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned long ctrl; - - ctrl = readl(info->regs + S3C2410_NFCONF); - ctrl |= S3C2410_NFCONF_INITECC; - writel(ctrl, info->regs + S3C2410_NFCONF); -} - -static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned long ctrl; - - ctrl = readl(info->regs + S3C2440_NFCONT); - writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, - info->regs + S3C2440_NFCONT); -} - -static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned long ctrl; - - ctrl = readl(info->regs + S3C2440_NFCONT); - writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); -} - -static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); - ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); - ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); - - pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); - - return 0; -} - -static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); - - ecc_code[0] = ecc; - ecc_code[1] = ecc >> 8; - ecc_code[2] = ecc >> 16; - - pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); - - return 0; -} - -static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); - - ecc_code[0] = ecc; - ecc_code[1] = ecc >> 8; - ecc_code[2] = ecc >> 16; - - pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff); - - return 0; -} - -/* over-ride the standard functions for a little more speed. We can - * use read/write block to move the data buffers to/from the controller -*/ - -static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - readsb(this->IO_ADDR_R, buf, len); -} - -static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - readsl(info->regs + S3C2440_NFDATA, buf, len >> 2); - - /* cleanup if we've got less than a word to do */ - if (len & 3) { - buf += len & ~3; - - for (; len & 3; len--) - *buf++ = readb(info->regs + S3C2440_NFDATA); - } -} - -static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, - int len) -{ - struct nand_chip *this = mtd_to_nand(mtd); - writesb(this->IO_ADDR_W, buf, len); -} - -static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, - int len) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - - writesl(info->regs + S3C2440_NFDATA, buf, len >> 2); - - /* cleanup any fractional write */ - if (len & 3) { - buf += len & ~3; - - for (; len & 3; len--, buf++) - writeb(*buf, info->regs + S3C2440_NFDATA); - } -} - -/* cpufreq driver support */ - -#ifdef CONFIG_ARM_S3C24XX_CPUFREQ - -static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb, - unsigned long val, void *data) -{ - struct s3c2410_nand_info *info; - unsigned long newclk; - - info = container_of(nb, struct s3c2410_nand_info, freq_transition); - newclk = clk_get_rate(info->clk); - - if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) || - (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) { - s3c2410_nand_setrate(info); - } - - return 0; -} - -static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) -{ - info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition; - - return cpufreq_register_notifier(&info->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -static inline void -s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) -{ - cpufreq_unregister_notifier(&info->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -#else -static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) -{ - return 0; -} - -static inline void -s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) -{ -} -#endif - -/* device management functions */ - -static int s3c24xx_nand_remove(struct platform_device *pdev) -{ - struct s3c2410_nand_info *info = to_nand_info(pdev); - - if (info == NULL) - return 0; - - s3c2410_nand_cpufreq_deregister(info); - - /* Release all our mtds and their partitions, then go through - * freeing the resources used - */ - - if (info->mtds != NULL) { - struct s3c2410_nand_mtd *ptr = info->mtds; - int mtdno; - - for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { - pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); - nand_release(nand_to_mtd(&ptr->chip)); - } - } - - /* free the common resources */ - - if (!IS_ERR(info->clk)) - s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); - - return 0; -} - -static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, - struct s3c2410_nand_mtd *mtd, - struct s3c2410_nand_set *set) -{ - if (set) { - struct mtd_info *mtdinfo = nand_to_mtd(&mtd->chip); - - mtdinfo->name = set->name; - - return mtd_device_parse_register(mtdinfo, NULL, NULL, - set->partitions, set->nr_partitions); - } - - return -ENODEV; -} - -static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd, int csline, - const struct nand_data_interface *conf) -{ - struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - struct s3c2410_platform_nand *pdata = info->platform; - const struct nand_sdr_timings *timings; - int tacls; - - timings = nand_get_sdr_timings(conf); - if (IS_ERR(timings)) - return -ENOTSUPP; - - tacls = timings->tCLS_min - timings->tWP_min; - if (tacls < 0) - tacls = 0; - - pdata->tacls = DIV_ROUND_UP(tacls, 1000); - pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000); - pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000); - - return s3c2410_nand_setrate(info); -} - -/** - * s3c2410_nand_init_chip - initialise a single instance of an chip - * @info: The base NAND controller the chip is on. - * @nmtd: The new controller MTD instance to fill in. - * @set: The information passed from the board specific platform data. - * - * Initialise the given @nmtd from the information in @info and @set. This - * readies the structure for use with the MTD layer functions by ensuring - * all pointers are setup and the necessary control routines selected. - */ -static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, - struct s3c2410_nand_mtd *nmtd, - struct s3c2410_nand_set *set) -{ - struct device_node *np = info->device->of_node; - struct nand_chip *chip = &nmtd->chip; - void __iomem *regs = info->regs; - - nand_set_flash_node(chip, set->of_node); - - chip->write_buf = s3c2410_nand_write_buf; - chip->read_buf = s3c2410_nand_read_buf; - chip->select_chip = s3c2410_nand_select_chip; - chip->chip_delay = 50; - nand_set_controller_data(chip, nmtd); - chip->options = set->options; - chip->controller = &info->controller; - - /* - * let's keep behavior unchanged for legacy boards booting via pdata and - * auto-detect timings only when booting with a device tree. - */ - if (np) - chip->setup_data_interface = s3c2410_nand_setup_data_interface; - - switch (info->cpu_type) { - case TYPE_S3C2410: - chip->IO_ADDR_W = regs + S3C2410_NFDATA; - info->sel_reg = regs + S3C2410_NFCONF; - info->sel_bit = S3C2410_NFCONF_nFCE; - chip->cmd_ctrl = s3c2410_nand_hwcontrol; - chip->dev_ready = s3c2410_nand_devready; - break; - - case TYPE_S3C2440: - chip->IO_ADDR_W = regs + S3C2440_NFDATA; - info->sel_reg = regs + S3C2440_NFCONT; - info->sel_bit = S3C2440_NFCONT_nFCE; - chip->cmd_ctrl = s3c2440_nand_hwcontrol; - chip->dev_ready = s3c2440_nand_devready; - chip->read_buf = s3c2440_nand_read_buf; - chip->write_buf = s3c2440_nand_write_buf; - break; - - case TYPE_S3C2412: - chip->IO_ADDR_W = regs + S3C2440_NFDATA; - info->sel_reg = regs + S3C2440_NFCONT; - info->sel_bit = S3C2412_NFCONT_nFCE0; - chip->cmd_ctrl = s3c2440_nand_hwcontrol; - chip->dev_ready = s3c2412_nand_devready; - - if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT) - dev_info(info->device, "System booted from NAND\n"); - - break; - } - - chip->IO_ADDR_R = chip->IO_ADDR_W; - - nmtd->info = info; - nmtd->set = set; - - chip->ecc.mode = info->platform->ecc_mode; - - /* - * If you use u-boot BBT creation code, specifying this flag will - * let the kernel fish out the BBT from the NAND. - */ - if (set->flash_bbt) - chip->bbt_options |= NAND_BBT_USE_FLASH; -} - -/** - * s3c2410_nand_update_chip - post probe update - * @info: The controller instance. - * @nmtd: The driver version of the MTD instance. - * - * This routine is called after the chip probe has successfully completed - * and the relevant per-chip information updated. This call ensure that - * we update the internal state accordingly. - * - * The internal state is currently limited to the ECC state information. -*/ -static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, - struct s3c2410_nand_mtd *nmtd) -{ - struct nand_chip *chip = &nmtd->chip; - - switch (chip->ecc.mode) { - - case NAND_ECC_NONE: - dev_info(info->device, "ECC disabled\n"); - break; - - case NAND_ECC_SOFT: - /* - * This driver expects Hamming based ECC when ecc_mode is set - * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to - * avoid adding an extra ecc_algo field to - * s3c2410_platform_nand. - */ - chip->ecc.algo = NAND_ECC_HAMMING; - dev_info(info->device, "soft ECC\n"); - break; - - case NAND_ECC_HW: - chip->ecc.calculate = s3c2410_nand_calculate_ecc; - chip->ecc.correct = s3c2410_nand_correct_data; - chip->ecc.strength = 1; - - switch (info->cpu_type) { - case TYPE_S3C2410: - chip->ecc.hwctl = s3c2410_nand_enable_hwecc; - chip->ecc.calculate = s3c2410_nand_calculate_ecc; - break; - - case TYPE_S3C2412: - chip->ecc.hwctl = s3c2412_nand_enable_hwecc; - chip->ecc.calculate = s3c2412_nand_calculate_ecc; - break; - - case TYPE_S3C2440: - chip->ecc.hwctl = s3c2440_nand_enable_hwecc; - chip->ecc.calculate = s3c2440_nand_calculate_ecc; - break; - } - - dev_dbg(info->device, "chip %p => page shift %d\n", - chip, chip->page_shift); - - /* change the behaviour depending on whether we are using - * the large or small page nand device */ - if (chip->page_shift > 10) { - chip->ecc.size = 256; - chip->ecc.bytes = 3; - } else { - chip->ecc.size = 512; - chip->ecc.bytes = 3; - mtd_set_ooblayout(nand_to_mtd(chip), - &s3c2410_ooblayout_ops); - } - - dev_info(info->device, "hardware ECC\n"); - break; - - default: - dev_err(info->device, "invalid ECC mode!\n"); - return -EINVAL; - } - - if (chip->bbt_options & NAND_BBT_USE_FLASH) - chip->options |= NAND_SKIP_BBTSCAN; - - return 0; -} - -static const struct of_device_id s3c24xx_nand_dt_ids[] = { - { - .compatible = "samsung,s3c2410-nand", - .data = &s3c2410_nand_devtype_data, - }, { - /* also compatible with s3c6400 */ - .compatible = "samsung,s3c2412-nand", - .data = &s3c2412_nand_devtype_data, - }, { - .compatible = "samsung,s3c2440-nand", - .data = &s3c2440_nand_devtype_data, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, s3c24xx_nand_dt_ids); - -static int s3c24xx_nand_probe_dt(struct platform_device *pdev) -{ - const struct s3c24XX_nand_devtype_data *devtype_data; - struct s3c2410_platform_nand *pdata; - struct s3c2410_nand_info *info = platform_get_drvdata(pdev); - struct device_node *np = pdev->dev.of_node, *child; - struct s3c2410_nand_set *sets; - - devtype_data = of_device_get_match_data(&pdev->dev); - if (!devtype_data) - return -ENODEV; - - info->cpu_type = devtype_data->type; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - pdev->dev.platform_data = pdata; - - pdata->nr_sets = of_get_child_count(np); - if (!pdata->nr_sets) - return 0; - - sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets, - GFP_KERNEL); - if (!sets) - return -ENOMEM; - - pdata->sets = sets; - - for_each_available_child_of_node(np, child) { - sets->name = (char *)child->name; - sets->of_node = child; - sets->nr_chips = 1; - - of_node_get(child); - - sets++; - } - - return 0; -} - -static int s3c24xx_nand_probe_pdata(struct platform_device *pdev) -{ - struct s3c2410_nand_info *info = platform_get_drvdata(pdev); - - info->cpu_type = platform_get_device_id(pdev)->driver_data; - - return 0; -} - -/* s3c24xx_nand_probe - * - * called by device layer when it finds a device matching - * one our driver can handled. This code checks to see if - * it can allocate all necessary resources then calls the - * nand layer to look for devices -*/ -static int s3c24xx_nand_probe(struct platform_device *pdev) -{ - struct s3c2410_platform_nand *plat; - struct s3c2410_nand_info *info; - struct s3c2410_nand_mtd *nmtd; - struct s3c2410_nand_set *sets; - struct resource *res; - int err = 0; - int size; - int nr_sets; - int setno; - - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); - if (info == NULL) { - err = -ENOMEM; - goto exit_error; - } - - platform_set_drvdata(pdev, info); - - nand_hw_control_init(&info->controller); - - /* get the clock source and enable it */ - - info->clk = devm_clk_get(&pdev->dev, "nand"); - if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "failed to get clock\n"); - err = -ENOENT; - goto exit_error; - } - - s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); - - if (pdev->dev.of_node) - err = s3c24xx_nand_probe_dt(pdev); - else - err = s3c24xx_nand_probe_pdata(pdev); - - if (err) - goto exit_error; - - plat = to_nand_plat(pdev); - - /* allocate and map the resource */ - - /* currently we assume we have the one resource */ - res = pdev->resource; - size = resource_size(res); - - info->device = &pdev->dev; - info->platform = plat; - - info->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(info->regs)) { - err = PTR_ERR(info->regs); - goto exit_error; - } - - dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs); - - sets = (plat != NULL) ? plat->sets : NULL; - nr_sets = (plat != NULL) ? plat->nr_sets : 1; - - info->mtd_count = nr_sets; - - /* allocate our information */ - - size = nr_sets * sizeof(*info->mtds); - info->mtds = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (info->mtds == NULL) { - err = -ENOMEM; - goto exit_error; - } - - /* initialise all possible chips */ - - nmtd = info->mtds; - - for (setno = 0; setno < nr_sets; setno++, nmtd++) { - struct mtd_info *mtd = nand_to_mtd(&nmtd->chip); - - pr_debug("initialising set %d (%p, info %p)\n", - setno, nmtd, info); - - mtd->dev.parent = &pdev->dev; - s3c2410_nand_init_chip(info, nmtd, sets); - - nmtd->scan_res = nand_scan_ident(mtd, - (sets) ? sets->nr_chips : 1, - NULL); - - if (nmtd->scan_res == 0) { - err = s3c2410_nand_update_chip(info, nmtd); - if (err < 0) - goto exit_error; - nand_scan_tail(mtd); - s3c2410_nand_add_partition(info, nmtd, sets); - } - - if (sets != NULL) - sets++; - } - - /* initialise the hardware */ - err = s3c2410_nand_inithw(info); - if (err != 0) - goto exit_error; - - err = s3c2410_nand_cpufreq_register(info); - if (err < 0) { - dev_err(&pdev->dev, "failed to init cpufreq support\n"); - goto exit_error; - } - - if (allow_clk_suspend(info)) { - dev_info(&pdev->dev, "clock idle support enabled\n"); - s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); - } - - return 0; - - exit_error: - s3c24xx_nand_remove(pdev); - - if (err == 0) - err = -EINVAL; - return err; -} - -/* PM Support */ -#ifdef CONFIG_PM - -static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) -{ - struct s3c2410_nand_info *info = platform_get_drvdata(dev); - - if (info) { - info->save_sel = readl(info->sel_reg); - - /* For the moment, we must ensure nFCE is high during - * the time we are suspended. This really should be - * handled by suspending the MTDs we are using, but - * that is currently not the case. */ - - writel(info->save_sel | info->sel_bit, info->sel_reg); - - s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); - } - - return 0; -} - -static int s3c24xx_nand_resume(struct platform_device *dev) -{ - struct s3c2410_nand_info *info = platform_get_drvdata(dev); - unsigned long sel; - - if (info) { - s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); - s3c2410_nand_inithw(info); - - /* Restore the state of the nFCE line. */ - - sel = readl(info->sel_reg); - sel &= ~info->sel_bit; - sel |= info->save_sel & info->sel_bit; - writel(sel, info->sel_reg); - - s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); - } - - return 0; -} - -#else -#define s3c24xx_nand_suspend NULL -#define s3c24xx_nand_resume NULL -#endif - -/* driver device registration */ - -static const struct platform_device_id s3c24xx_driver_ids[] = { - { - .name = "s3c2410-nand", - .driver_data = TYPE_S3C2410, - }, { - .name = "s3c2440-nand", - .driver_data = TYPE_S3C2440, - }, { - .name = "s3c2412-nand", - .driver_data = TYPE_S3C2412, - }, { - .name = "s3c6400-nand", - .driver_data = TYPE_S3C2412, /* compatible with 2412 */ - }, - { } -}; - -MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); - -static struct platform_driver s3c24xx_nand_driver = { - .probe = s3c24xx_nand_probe, - .remove = s3c24xx_nand_remove, - .suspend = s3c24xx_nand_suspend, - .resume = s3c24xx_nand_resume, - .id_table = s3c24xx_driver_ids, - .driver = { - .name = "s3c24xx-nand", - .of_match_table = s3c24xx_nand_dt_ids, - }, -}; - -module_platform_driver(s3c24xx_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c deleted file mode 100644 index c4e7755448e6..000000000000 --- a/drivers/mtd/nand/sh_flctl.c +++ /dev/null @@ -1,1250 +0,0 @@ -/* - * SuperH FLCTL nand controller - * - * Copyright (c) 2008 Renesas Solutions Corp. - * Copyright (c) 2008 Atom Create Engineering Co., Ltd. - * - * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section) - return -ERANGE; - - oobregion->offset = 0; - oobregion->length = chip->ecc.bytes; - - return 0; -} - -static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->offset = 12; - oobregion->length = 4; - - return 0; -} - -static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = { - .ecc = flctl_4secc_ooblayout_sp_ecc, - .free = flctl_4secc_ooblayout_sp_free, -}; - -static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section >= chip->ecc.steps) - return -ERANGE; - - oobregion->offset = (section * 16) + 6; - oobregion->length = chip->ecc.bytes; - - return 0; -} - -static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section >= chip->ecc.steps) - return -ERANGE; - - oobregion->offset = section * 16; - oobregion->length = 6; - - if (!section) { - oobregion->offset += 2; - oobregion->length -= 2; - } - - return 0; -} - -static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = { - .ecc = flctl_4secc_ooblayout_lp_ecc, - .free = flctl_4secc_ooblayout_lp_free, -}; - -static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; - -static struct nand_bbt_descr flctl_4secc_smallpage = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = 11, - .len = 1, - .pattern = scan_ff_pattern, -}; - -static struct nand_bbt_descr flctl_4secc_largepage = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = 0, - .len = 2, - .pattern = scan_ff_pattern, -}; - -static void empty_fifo(struct sh_flctl *flctl) -{ - writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl)); - writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); -} - -static void start_translation(struct sh_flctl *flctl) -{ - writeb(TRSTRT, FLTRCR(flctl)); -} - -static void timeout_error(struct sh_flctl *flctl, const char *str) -{ - dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str); -} - -static void wait_completion(struct sh_flctl *flctl) -{ - uint32_t timeout = LOOP_TIMEOUT_MAX; - - while (timeout--) { - if (readb(FLTRCR(flctl)) & TREND) { - writeb(0x0, FLTRCR(flctl)); - return; - } - udelay(1); - } - - timeout_error(flctl, __func__); - writeb(0x0, FLTRCR(flctl)); -} - -static void flctl_dma_complete(void *param) -{ - struct sh_flctl *flctl = param; - - complete(&flctl->dma_complete); -} - -static void flctl_release_dma(struct sh_flctl *flctl) -{ - if (flctl->chan_fifo0_rx) { - dma_release_channel(flctl->chan_fifo0_rx); - flctl->chan_fifo0_rx = NULL; - } - if (flctl->chan_fifo0_tx) { - dma_release_channel(flctl->chan_fifo0_tx); - flctl->chan_fifo0_tx = NULL; - } -} - -static void flctl_setup_dma(struct sh_flctl *flctl) -{ - dma_cap_mask_t mask; - struct dma_slave_config cfg; - struct platform_device *pdev = flctl->pdev; - struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev); - int ret; - - if (!pdata) - return; - - if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0) - return; - - /* We can only either use DMA for both Tx and Rx or not use it at all */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter, - (void *)(uintptr_t)pdata->slave_id_fifo0_tx); - dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__, - flctl->chan_fifo0_tx); - - if (!flctl->chan_fifo0_tx) - return; - - memset(&cfg, 0, sizeof(cfg)); - cfg.direction = DMA_MEM_TO_DEV; - cfg.dst_addr = flctl->fifo; - cfg.src_addr = 0; - ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg); - if (ret < 0) - goto err; - - flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter, - (void *)(uintptr_t)pdata->slave_id_fifo0_rx); - dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__, - flctl->chan_fifo0_rx); - - if (!flctl->chan_fifo0_rx) - goto err; - - cfg.direction = DMA_DEV_TO_MEM; - cfg.dst_addr = 0; - cfg.src_addr = flctl->fifo; - ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg); - if (ret < 0) - goto err; - - init_completion(&flctl->dma_complete); - - return; - -err: - flctl_release_dma(flctl); -} - -static void set_addr(struct mtd_info *mtd, int column, int page_addr) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t addr = 0; - - if (column == -1) { - addr = page_addr; /* ERASE1 */ - } else if (page_addr != -1) { - /* SEQIN, READ0, etc.. */ - if (flctl->chip.options & NAND_BUSWIDTH_16) - column >>= 1; - if (flctl->page_size) { - addr = column & 0x0FFF; - addr |= (page_addr & 0xff) << 16; - addr |= ((page_addr >> 8) & 0xff) << 24; - /* big than 128MB */ - if (flctl->rw_ADRCNT == ADRCNT2_E) { - uint32_t addr2; - addr2 = (page_addr >> 16) & 0xff; - writel(addr2, FLADR2(flctl)); - } - } else { - addr = column; - addr |= (page_addr & 0xff) << 8; - addr |= ((page_addr >> 8) & 0xff) << 16; - addr |= ((page_addr >> 16) & 0xff) << 24; - } - } - writel(addr, FLADR(flctl)); -} - -static void wait_rfifo_ready(struct sh_flctl *flctl) -{ - uint32_t timeout = LOOP_TIMEOUT_MAX; - - while (timeout--) { - uint32_t val; - /* check FIFO */ - val = readl(FLDTCNTR(flctl)) >> 16; - if (val & 0xFF) - return; - udelay(1); - } - timeout_error(flctl, __func__); -} - -static void wait_wfifo_ready(struct sh_flctl *flctl) -{ - uint32_t len, timeout = LOOP_TIMEOUT_MAX; - - while (timeout--) { - /* check FIFO */ - len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF; - if (len >= 4) - return; - udelay(1); - } - timeout_error(flctl, __func__); -} - -static enum flctl_ecc_res_t wait_recfifo_ready - (struct sh_flctl *flctl, int sector_number) -{ - uint32_t timeout = LOOP_TIMEOUT_MAX; - void __iomem *ecc_reg[4]; - int i; - int state = FL_SUCCESS; - uint32_t data, size; - - /* - * First this loops checks in FLDTCNTR if we are ready to read out the - * oob data. This is the case if either all went fine without errors or - * if the bottom part of the loop corrected the errors or marked them as - * uncorrectable and the controller is given time to push the data into - * the FIFO. - */ - while (timeout--) { - /* check if all is ok and we can read out the OOB */ - size = readl(FLDTCNTR(flctl)) >> 24; - if ((size & 0xFF) == 4) - return state; - - /* check if a correction code has been calculated */ - if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) { - /* - * either we wait for the fifo to be filled or a - * correction pattern is being generated - */ - udelay(1); - continue; - } - - /* check for an uncorrectable error */ - if (readl(FL4ECCCR(flctl)) & _4ECCFA) { - /* check if we face a non-empty page */ - for (i = 0; i < 512; i++) { - if (flctl->done_buff[i] != 0xff) { - state = FL_ERROR; /* can't correct */ - break; - } - } - - if (state == FL_SUCCESS) - dev_dbg(&flctl->pdev->dev, - "reading empty sector %d, ecc error ignored\n", - sector_number); - - writel(0, FL4ECCCR(flctl)); - continue; - } - - /* start error correction */ - ecc_reg[0] = FL4ECCRESULT0(flctl); - ecc_reg[1] = FL4ECCRESULT1(flctl); - ecc_reg[2] = FL4ECCRESULT2(flctl); - ecc_reg[3] = FL4ECCRESULT3(flctl); - - for (i = 0; i < 3; i++) { - uint8_t org; - unsigned int index; - - data = readl(ecc_reg[i]); - - if (flctl->page_size) - index = (512 * sector_number) + - (data >> 16); - else - index = data >> 16; - - org = flctl->done_buff[index]; - flctl->done_buff[index] = org ^ (data & 0xFF); - } - state = FL_REPAIRABLE; - writel(0, FL4ECCCR(flctl)); - } - - timeout_error(flctl, __func__); - return FL_TIMEOUT; /* timeout */ -} - -static void wait_wecfifo_ready(struct sh_flctl *flctl) -{ - uint32_t timeout = LOOP_TIMEOUT_MAX; - uint32_t len; - - while (timeout--) { - /* check FLECFIFO */ - len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF; - if (len >= 4) - return; - udelay(1); - } - timeout_error(flctl, __func__); -} - -static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, - int len, enum dma_data_direction dir) -{ - struct dma_async_tx_descriptor *desc = NULL; - struct dma_chan *chan; - enum dma_transfer_direction tr_dir; - dma_addr_t dma_addr; - dma_cookie_t cookie; - uint32_t reg; - int ret; - - if (dir == DMA_FROM_DEVICE) { - chan = flctl->chan_fifo0_rx; - tr_dir = DMA_DEV_TO_MEM; - } else { - chan = flctl->chan_fifo0_tx; - tr_dir = DMA_MEM_TO_DEV; - } - - dma_addr = dma_map_single(chan->device->dev, buf, len, dir); - - if (!dma_mapping_error(chan->device->dev, dma_addr)) - desc = dmaengine_prep_slave_single(chan, dma_addr, len, - tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - - if (desc) { - reg = readl(FLINTDMACR(flctl)); - reg |= DREQ0EN; - writel(reg, FLINTDMACR(flctl)); - - desc->callback = flctl_dma_complete; - desc->callback_param = flctl; - cookie = dmaengine_submit(desc); - if (dma_submit_error(cookie)) { - ret = dma_submit_error(cookie); - dev_warn(&flctl->pdev->dev, - "DMA submit failed, falling back to PIO\n"); - goto out; - } - - dma_async_issue_pending(chan); - } else { - /* DMA failed, fall back to PIO */ - flctl_release_dma(flctl); - dev_warn(&flctl->pdev->dev, - "DMA failed, falling back to PIO\n"); - ret = -EIO; - goto out; - } - - ret = - wait_for_completion_timeout(&flctl->dma_complete, - msecs_to_jiffies(3000)); - - if (ret <= 0) { - dmaengine_terminate_all(chan); - dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n"); - } - -out: - reg = readl(FLINTDMACR(flctl)); - reg &= ~DREQ0EN; - writel(reg, FLINTDMACR(flctl)); - - dma_unmap_single(chan->device->dev, dma_addr, len, dir); - - /* ret > 0 is success */ - return ret; -} - -static void read_datareg(struct sh_flctl *flctl, int offset) -{ - unsigned long data; - unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; - - wait_completion(flctl); - - data = readl(FLDATAR(flctl)); - *buf = le32_to_cpu(data); -} - -static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) -{ - int i, len_4align; - unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; - - len_4align = (rlen + 3) / 4; - - /* initiate DMA transfer */ - if (flctl->chan_fifo0_rx && rlen >= 32 && - flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_DEV_TO_MEM) > 0) - goto convert; /* DMA success */ - - /* do polling transfer */ - for (i = 0; i < len_4align; i++) { - wait_rfifo_ready(flctl); - buf[i] = readl(FLDTFIFO(flctl)); - } - -convert: - for (i = 0; i < len_4align; i++) - buf[i] = be32_to_cpu(buf[i]); -} - -static enum flctl_ecc_res_t read_ecfiforeg - (struct sh_flctl *flctl, uint8_t *buff, int sector) -{ - int i; - enum flctl_ecc_res_t res; - unsigned long *ecc_buf = (unsigned long *)buff; - - res = wait_recfifo_ready(flctl , sector); - - if (res != FL_ERROR) { - for (i = 0; i < 4; i++) { - ecc_buf[i] = readl(FLECFIFO(flctl)); - ecc_buf[i] = be32_to_cpu(ecc_buf[i]); - } - } - - return res; -} - -static void write_fiforeg(struct sh_flctl *flctl, int rlen, - unsigned int offset) -{ - int i, len_4align; - unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; - - len_4align = (rlen + 3) / 4; - for (i = 0; i < len_4align; i++) { - wait_wfifo_ready(flctl); - writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl)); - } -} - -static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen, - unsigned int offset) -{ - int i, len_4align; - unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; - - len_4align = (rlen + 3) / 4; - - for (i = 0; i < len_4align; i++) - buf[i] = cpu_to_be32(buf[i]); - - /* initiate DMA transfer */ - if (flctl->chan_fifo0_tx && rlen >= 32 && - flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_MEM_TO_DEV) > 0) - return; /* DMA success */ - - /* do polling transfer */ - for (i = 0; i < len_4align; i++) { - wait_wecfifo_ready(flctl); - writel(buf[i], FLECFIFO(flctl)); - } -} - -static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT; - uint32_t flcmdcr_val, addr_len_bytes = 0; - - /* Set SNAND bit if page size is 2048byte */ - if (flctl->page_size) - flcmncr_val |= SNAND_E; - else - flcmncr_val &= ~SNAND_E; - - /* default FLCMDCR val */ - flcmdcr_val = DOCMD1_E | DOADR_E; - - /* Set for FLCMDCR */ - switch (cmd) { - case NAND_CMD_ERASE1: - addr_len_bytes = flctl->erase_ADRCNT; - flcmdcr_val |= DOCMD2_E; - break; - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - case NAND_CMD_RNDOUT: - addr_len_bytes = flctl->rw_ADRCNT; - flcmdcr_val |= CDSRC_E; - if (flctl->chip.options & NAND_BUSWIDTH_16) - flcmncr_val |= SEL_16BIT; - break; - case NAND_CMD_SEQIN: - /* This case is that cmd is READ0 or READ1 or READ00 */ - flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */ - break; - case NAND_CMD_PAGEPROG: - addr_len_bytes = flctl->rw_ADRCNT; - flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW; - if (flctl->chip.options & NAND_BUSWIDTH_16) - flcmncr_val |= SEL_16BIT; - break; - case NAND_CMD_READID: - flcmncr_val &= ~SNAND_E; - flcmdcr_val |= CDSRC_E; - addr_len_bytes = ADRCNT_1; - break; - case NAND_CMD_STATUS: - case NAND_CMD_RESET: - flcmncr_val &= ~SNAND_E; - flcmdcr_val &= ~(DOADR_E | DOSR_E); - break; - default: - break; - } - - /* Set address bytes parameter */ - flcmdcr_val |= addr_len_bytes; - - /* Now actually write */ - writel(flcmncr_val, FLCMNCR(flctl)); - writel(flcmdcr_val, FLCMDCR(flctl)); - writel(flcmcdr_val, FLCMCDR(flctl)); -} - -static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - nand_read_page_op(chip, page, 0, buf, mtd->writesize); - if (oob_required) - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; -} - -static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - return nand_prog_page_end_op(chip); -} - -static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - int sector, page_sectors; - enum flctl_ecc_res_t ecc_result; - - page_sectors = flctl->page_size ? 4 : 1; - - set_cmd_regs(mtd, NAND_CMD_READ0, - (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); - - writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT, - FLCMNCR(flctl)); - writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); - writel(page_addr << 2, FLADR(flctl)); - - empty_fifo(flctl); - start_translation(flctl); - - for (sector = 0; sector < page_sectors; sector++) { - read_fiforeg(flctl, 512, 512 * sector); - - ecc_result = read_ecfiforeg(flctl, - &flctl->done_buff[mtd->writesize + 16 * sector], - sector); - - switch (ecc_result) { - case FL_REPAIRABLE: - dev_info(&flctl->pdev->dev, - "applied ecc on page 0x%x", page_addr); - mtd->ecc_stats.corrected++; - break; - case FL_ERROR: - dev_warn(&flctl->pdev->dev, - "page 0x%x contains corrupted data\n", - page_addr); - mtd->ecc_stats.failed++; - break; - default: - ; - } - } - - wait_completion(flctl); - - writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT), - FLCMNCR(flctl)); -} - -static void execmd_read_oob(struct mtd_info *mtd, int page_addr) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - int page_sectors = flctl->page_size ? 4 : 1; - int i; - - set_cmd_regs(mtd, NAND_CMD_READ0, - (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); - - empty_fifo(flctl); - - for (i = 0; i < page_sectors; i++) { - set_addr(mtd, (512 + 16) * i + 512 , page_addr); - writel(16, FLDTCNTR(flctl)); - - start_translation(flctl); - read_fiforeg(flctl, 16, 16 * i); - wait_completion(flctl); - } -} - -static void execmd_write_page_sector(struct mtd_info *mtd) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - int page_addr = flctl->seqin_page_addr; - int sector, page_sectors; - - page_sectors = flctl->page_size ? 4 : 1; - - set_cmd_regs(mtd, NAND_CMD_PAGEPROG, - (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); - - empty_fifo(flctl); - writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl)); - writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); - writel(page_addr << 2, FLADR(flctl)); - start_translation(flctl); - - for (sector = 0; sector < page_sectors; sector++) { - write_fiforeg(flctl, 512, 512 * sector); - write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector); - } - - wait_completion(flctl); - writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl)); -} - -static void execmd_write_oob(struct mtd_info *mtd) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - int page_addr = flctl->seqin_page_addr; - int sector, page_sectors; - - page_sectors = flctl->page_size ? 4 : 1; - - set_cmd_regs(mtd, NAND_CMD_PAGEPROG, - (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); - - for (sector = 0; sector < page_sectors; sector++) { - empty_fifo(flctl); - set_addr(mtd, sector * 528 + 512, page_addr); - writel(16, FLDTCNTR(flctl)); /* set read size */ - - start_translation(flctl); - write_fiforeg(flctl, 16, 16 * sector); - wait_completion(flctl); - } -} - -static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t read_cmd = 0; - - pm_runtime_get_sync(&flctl->pdev->dev); - - flctl->read_bytes = 0; - if (command != NAND_CMD_PAGEPROG) - flctl->index = 0; - - switch (command) { - case NAND_CMD_READ1: - case NAND_CMD_READ0: - if (flctl->hwecc) { - /* read page with hwecc */ - execmd_read_page_sector(mtd, page_addr); - break; - } - if (flctl->page_size) - set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) - | command); - else - set_cmd_regs(mtd, command, command); - - set_addr(mtd, 0, page_addr); - - flctl->read_bytes = mtd->writesize + mtd->oobsize; - if (flctl->chip.options & NAND_BUSWIDTH_16) - column >>= 1; - flctl->index += column; - goto read_normal_exit; - - case NAND_CMD_READOOB: - if (flctl->hwecc) { - /* read page with hwecc */ - execmd_read_oob(mtd, page_addr); - break; - } - - if (flctl->page_size) { - set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) - | NAND_CMD_READ0); - set_addr(mtd, mtd->writesize, page_addr); - } else { - set_cmd_regs(mtd, command, command); - set_addr(mtd, 0, page_addr); - } - flctl->read_bytes = mtd->oobsize; - goto read_normal_exit; - - case NAND_CMD_RNDOUT: - if (flctl->hwecc) - break; - - if (flctl->page_size) - set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8) - | command); - else - set_cmd_regs(mtd, command, command); - - set_addr(mtd, column, 0); - - flctl->read_bytes = mtd->writesize + mtd->oobsize - column; - goto read_normal_exit; - - case NAND_CMD_READID: - set_cmd_regs(mtd, command, command); - - /* READID is always performed using an 8-bit bus */ - if (flctl->chip.options & NAND_BUSWIDTH_16) - column <<= 1; - set_addr(mtd, column, 0); - - flctl->read_bytes = 8; - writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ - empty_fifo(flctl); - start_translation(flctl); - read_fiforeg(flctl, flctl->read_bytes, 0); - wait_completion(flctl); - break; - - case NAND_CMD_ERASE1: - flctl->erase1_page_addr = page_addr; - break; - - case NAND_CMD_ERASE2: - set_cmd_regs(mtd, NAND_CMD_ERASE1, - (command << 8) | NAND_CMD_ERASE1); - set_addr(mtd, -1, flctl->erase1_page_addr); - start_translation(flctl); - wait_completion(flctl); - break; - - case NAND_CMD_SEQIN: - if (!flctl->page_size) { - /* output read command */ - if (column >= mtd->writesize) { - column -= mtd->writesize; - read_cmd = NAND_CMD_READOOB; - } else if (column < 256) { - read_cmd = NAND_CMD_READ0; - } else { - column -= 256; - read_cmd = NAND_CMD_READ1; - } - } - flctl->seqin_column = column; - flctl->seqin_page_addr = page_addr; - flctl->seqin_read_cmd = read_cmd; - break; - - case NAND_CMD_PAGEPROG: - empty_fifo(flctl); - if (!flctl->page_size) { - set_cmd_regs(mtd, NAND_CMD_SEQIN, - flctl->seqin_read_cmd); - set_addr(mtd, -1, -1); - writel(0, FLDTCNTR(flctl)); /* set 0 size */ - start_translation(flctl); - wait_completion(flctl); - } - if (flctl->hwecc) { - /* write page with hwecc */ - if (flctl->seqin_column == mtd->writesize) - execmd_write_oob(mtd); - else if (!flctl->seqin_column) - execmd_write_page_sector(mtd); - else - printk(KERN_ERR "Invalid address !?\n"); - break; - } - set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN); - set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr); - writel(flctl->index, FLDTCNTR(flctl)); /* set write size */ - start_translation(flctl); - write_fiforeg(flctl, flctl->index, 0); - wait_completion(flctl); - break; - - case NAND_CMD_STATUS: - set_cmd_regs(mtd, command, command); - set_addr(mtd, -1, -1); - - flctl->read_bytes = 1; - writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ - start_translation(flctl); - read_datareg(flctl, 0); /* read and end */ - break; - - case NAND_CMD_RESET: - set_cmd_regs(mtd, command, command); - set_addr(mtd, -1, -1); - - writel(0, FLDTCNTR(flctl)); /* set 0 size */ - start_translation(flctl); - wait_completion(flctl); - break; - - default: - break; - } - goto runtime_exit; - -read_normal_exit: - writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ - empty_fifo(flctl); - start_translation(flctl); - read_fiforeg(flctl, flctl->read_bytes, 0); - wait_completion(flctl); -runtime_exit: - pm_runtime_put_sync(&flctl->pdev->dev); - return; -} - -static void flctl_select_chip(struct mtd_info *mtd, int chipnr) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - int ret; - - switch (chipnr) { - case -1: - flctl->flcmncr_base &= ~CE0_ENABLE; - - pm_runtime_get_sync(&flctl->pdev->dev); - writel(flctl->flcmncr_base, FLCMNCR(flctl)); - - if (flctl->qos_request) { - dev_pm_qos_remove_request(&flctl->pm_qos); - flctl->qos_request = 0; - } - - pm_runtime_put_sync(&flctl->pdev->dev); - break; - case 0: - flctl->flcmncr_base |= CE0_ENABLE; - - if (!flctl->qos_request) { - ret = dev_pm_qos_add_request(&flctl->pdev->dev, - &flctl->pm_qos, - DEV_PM_QOS_RESUME_LATENCY, - 100); - if (ret < 0) - dev_err(&flctl->pdev->dev, - "PM QoS request failed: %d\n", ret); - flctl->qos_request = 1; - } - - if (flctl->holden) { - pm_runtime_get_sync(&flctl->pdev->dev); - writel(HOLDEN, FLHOLDCR(flctl)); - pm_runtime_put_sync(&flctl->pdev->dev); - } - break; - default: - BUG(); - } -} - -static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - - memcpy(&flctl->done_buff[flctl->index], buf, len); - flctl->index += len; -} - -static uint8_t flctl_read_byte(struct mtd_info *mtd) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint8_t data; - - data = flctl->done_buff[flctl->index]; - flctl->index++; - return data; -} - -static uint16_t flctl_read_word(struct mtd_info *mtd) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint16_t *buf = (uint16_t *)&flctl->done_buff[flctl->index]; - - flctl->index += 2; - return *buf; -} - -static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - - memcpy(buf, &flctl->done_buff[flctl->index], len); - flctl->index += len; -} - -static int flctl_chip_init_tail(struct mtd_info *mtd) -{ - struct sh_flctl *flctl = mtd_to_flctl(mtd); - struct nand_chip *chip = &flctl->chip; - - if (mtd->writesize == 512) { - flctl->page_size = 0; - if (chip->chipsize > (32 << 20)) { - /* big than 32MB */ - flctl->rw_ADRCNT = ADRCNT_4; - flctl->erase_ADRCNT = ADRCNT_3; - } else if (chip->chipsize > (2 << 16)) { - /* big than 128KB */ - flctl->rw_ADRCNT = ADRCNT_3; - flctl->erase_ADRCNT = ADRCNT_2; - } else { - flctl->rw_ADRCNT = ADRCNT_2; - flctl->erase_ADRCNT = ADRCNT_1; - } - } else { - flctl->page_size = 1; - if (chip->chipsize > (128 << 20)) { - /* big than 128MB */ - flctl->rw_ADRCNT = ADRCNT2_E; - flctl->erase_ADRCNT = ADRCNT_3; - } else if (chip->chipsize > (8 << 16)) { - /* big than 512KB */ - flctl->rw_ADRCNT = ADRCNT_4; - flctl->erase_ADRCNT = ADRCNT_2; - } else { - flctl->rw_ADRCNT = ADRCNT_3; - flctl->erase_ADRCNT = ADRCNT_1; - } - } - - if (flctl->hwecc) { - if (mtd->writesize == 512) { - mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops); - chip->badblock_pattern = &flctl_4secc_smallpage; - } else { - mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops); - chip->badblock_pattern = &flctl_4secc_largepage; - } - - chip->ecc.size = 512; - chip->ecc.bytes = 10; - chip->ecc.strength = 4; - chip->ecc.read_page = flctl_read_page_hwecc; - chip->ecc.write_page = flctl_write_page_hwecc; - chip->ecc.mode = NAND_ECC_HW; - - /* 4 symbols ECC enabled */ - flctl->flcmncr_base |= _4ECCEN; - } else { - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - } - - return 0; -} - -static irqreturn_t flctl_handle_flste(int irq, void *dev_id) -{ - struct sh_flctl *flctl = dev_id; - - dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl))); - writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); - - return IRQ_HANDLED; -} - -struct flctl_soc_config { - unsigned long flcmncr_val; - unsigned has_hwecc:1; - unsigned use_holden:1; -}; - -static struct flctl_soc_config flctl_sh7372_config = { - .flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL, - .has_hwecc = 1, - .use_holden = 1, -}; - -static const struct of_device_id of_flctl_match[] = { - { .compatible = "renesas,shmobile-flctl-sh7372", - .data = &flctl_sh7372_config }, - {}, -}; -MODULE_DEVICE_TABLE(of, of_flctl_match); - -static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) -{ - const struct flctl_soc_config *config; - struct sh_flctl_platform_data *pdata; - - config = of_device_get_match_data(dev); - if (!config) { - dev_err(dev, "%s: no OF configuration attached\n", __func__); - return NULL; - } - - pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data), - GFP_KERNEL); - if (!pdata) - return NULL; - - /* set SoC specific options */ - pdata->flcmncr_val = config->flcmncr_val; - pdata->has_hwecc = config->has_hwecc; - pdata->use_holden = config->use_holden; - - return pdata; -} - -static int flctl_probe(struct platform_device *pdev) -{ - struct resource *res; - struct sh_flctl *flctl; - struct mtd_info *flctl_mtd; - struct nand_chip *nand; - struct sh_flctl_platform_data *pdata; - int ret; - int irq; - - flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL); - if (!flctl) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - flctl->reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(flctl->reg)) - return PTR_ERR(flctl->reg); - flctl->fifo = res->start + 0x24; /* FLDTFIFO */ - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get flste irq data: %d\n", irq); - return irq; - } - - ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED, - "flste", flctl); - if (ret) { - dev_err(&pdev->dev, "request interrupt failed.\n"); - return ret; - } - - if (pdev->dev.of_node) - pdata = flctl_parse_dt(&pdev->dev); - else - pdata = dev_get_platdata(&pdev->dev); - - if (!pdata) { - dev_err(&pdev->dev, "no setup data defined\n"); - return -EINVAL; - } - - platform_set_drvdata(pdev, flctl); - nand = &flctl->chip; - flctl_mtd = nand_to_mtd(nand); - nand_set_flash_node(nand, pdev->dev.of_node); - flctl_mtd->dev.parent = &pdev->dev; - flctl->pdev = pdev; - flctl->hwecc = pdata->has_hwecc; - flctl->holden = pdata->use_holden; - flctl->flcmncr_base = pdata->flcmncr_val; - flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE; - - /* Set address of hardware control function */ - /* 20 us command delay time */ - nand->chip_delay = 20; - - nand->read_byte = flctl_read_byte; - nand->read_word = flctl_read_word; - nand->write_buf = flctl_write_buf; - nand->read_buf = flctl_read_buf; - nand->select_chip = flctl_select_chip; - nand->cmdfunc = flctl_cmdfunc; - nand->onfi_set_features = nand_onfi_get_set_features_notsupp; - nand->onfi_get_features = nand_onfi_get_set_features_notsupp; - - if (pdata->flcmncr_val & SEL_16BIT) - nand->options |= NAND_BUSWIDTH_16; - - pm_runtime_enable(&pdev->dev); - pm_runtime_resume(&pdev->dev); - - flctl_setup_dma(flctl); - - ret = nand_scan_ident(flctl_mtd, 1, NULL); - if (ret) - goto err_chip; - - if (nand->options & NAND_BUSWIDTH_16) { - /* - * NAND_BUSWIDTH_16 may have been set by nand_scan_ident(). - * Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign - * flctl->flcmncr_base to pdata->flcmncr_val. - */ - pdata->flcmncr_val |= SEL_16BIT; - flctl->flcmncr_base = pdata->flcmncr_val; - } - - ret = flctl_chip_init_tail(flctl_mtd); - if (ret) - goto err_chip; - - ret = nand_scan_tail(flctl_mtd); - if (ret) - goto err_chip; - - ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); - - return 0; - -err_chip: - flctl_release_dma(flctl); - pm_runtime_disable(&pdev->dev); - return ret; -} - -static int flctl_remove(struct platform_device *pdev) -{ - struct sh_flctl *flctl = platform_get_drvdata(pdev); - - flctl_release_dma(flctl); - nand_release(nand_to_mtd(&flctl->chip)); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static struct platform_driver flctl_driver = { - .remove = flctl_remove, - .driver = { - .name = "sh_flctl", - .of_match_table = of_match_ptr(of_flctl_match), - }, -}; - -module_platform_driver_probe(flctl_driver, flctl_probe); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Yoshihiro Shimoda"); -MODULE_DESCRIPTION("SuperH FLCTL driver"); -MODULE_ALIAS("platform:sh_flctl"); diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c deleted file mode 100644 index e93df02c825e..000000000000 --- a/drivers/mtd/nand/sharpsl.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2004 Richard Purdie - * Copyright (C) 2008 Dmitry Baryshkov - * - * Based on Sharp's NAND driver sharp_sl.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -struct sharpsl_nand { - struct nand_chip chip; - - void __iomem *io; -}; - -static inline struct sharpsl_nand *mtd_to_sharpsl(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct sharpsl_nand, chip); -} - -/* register offset */ -#define ECCLPLB 0x00 /* line parity 7 - 0 bit */ -#define ECCLPUB 0x04 /* line parity 15 - 8 bit */ -#define ECCCP 0x08 /* column parity 5 - 0 bit */ -#define ECCCNTR 0x0C /* ECC byte counter */ -#define ECCCLRR 0x10 /* cleare ECC */ -#define FLASHIO 0x14 /* Flash I/O */ -#define FLASHCTL 0x18 /* Flash Control */ - -/* Flash control bit */ -#define FLRYBY (1 << 5) -#define FLCE1 (1 << 4) -#define FLWP (1 << 3) -#define FLALE (1 << 2) -#define FLCLE (1 << 1) -#define FLCE0 (1 << 0) - -/* - * hardware specific access to control-lines - * ctrl: - * NAND_CNE: bit 0 -> ! bit 0 & 4 - * NAND_CLE: bit 1 -> bit 1 - * NAND_ALE: bit 2 -> bit 2 - * - */ -static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - - if (ctrl & NAND_CTRL_CHANGE) { - unsigned char bits = ctrl & 0x07; - - bits |= (ctrl & 0x01) << 4; - - bits ^= 0x11; - - writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -static int sharpsl_nand_dev_ready(struct mtd_info *mtd) -{ - struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); - return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0); -} - -static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); - writeb(0, sharpsl->io + ECCCLRR); -} - -static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) -{ - struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); - ecc_code[0] = ~readb(sharpsl->io + ECCLPUB); - ecc_code[1] = ~readb(sharpsl->io + ECCLPLB); - ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03; - return readb(sharpsl->io + ECCCNTR) != 0; -} - -/* - * Main initialization routine - */ -static int sharpsl_nand_probe(struct platform_device *pdev) -{ - struct nand_chip *this; - struct mtd_info *mtd; - struct resource *r; - int err = 0; - struct sharpsl_nand *sharpsl; - struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev); - - if (!data) { - dev_err(&pdev->dev, "no platform data!\n"); - return -EINVAL; - } - - /* Allocate memory for MTD device structure and private data */ - sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL); - if (!sharpsl) - return -ENOMEM; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - dev_err(&pdev->dev, "no io memory resource defined!\n"); - err = -ENODEV; - goto err_get_res; - } - - /* map physical address */ - sharpsl->io = ioremap(r->start, resource_size(r)); - if (!sharpsl->io) { - dev_err(&pdev->dev, "ioremap to access Sharp SL NAND chip failed\n"); - err = -EIO; - goto err_ioremap; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&sharpsl->chip); - - /* Link the private data with the MTD structure */ - mtd = nand_to_mtd(this); - mtd->dev.parent = &pdev->dev; - mtd_set_ooblayout(mtd, data->ecc_layout); - - platform_set_drvdata(pdev, sharpsl); - - /* - * PXA initialize - */ - writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL); - - /* Set address of NAND IO lines */ - this->IO_ADDR_R = sharpsl->io + FLASHIO; - this->IO_ADDR_W = sharpsl->io + FLASHIO; - /* Set address of hardware control function */ - this->cmd_ctrl = sharpsl_nand_hwcontrol; - this->dev_ready = sharpsl_nand_dev_ready; - /* 15 us command delay time */ - this->chip_delay = 15; - /* set eccmode using hardware ECC */ - this->ecc.mode = NAND_ECC_HW; - this->ecc.size = 256; - this->ecc.bytes = 3; - this->ecc.strength = 1; - this->badblock_pattern = data->badblock_pattern; - this->ecc.hwctl = sharpsl_nand_enable_hwecc; - this->ecc.calculate = sharpsl_nand_calculate_ecc; - this->ecc.correct = nand_correct_data; - - /* Scan to find existence of the device */ - err = nand_scan(mtd, 1); - if (err) - goto err_scan; - - /* Register the partitions */ - mtd->name = "sharpsl-nand"; - - err = mtd_device_parse_register(mtd, data->part_parsers, NULL, - data->partitions, data->nr_partitions); - if (err) - goto err_add; - - /* Return happy */ - return 0; - -err_add: - nand_release(mtd); - -err_scan: - iounmap(sharpsl->io); -err_ioremap: -err_get_res: - kfree(sharpsl); - return err; -} - -/* - * Clean up routine - */ -static int sharpsl_nand_remove(struct platform_device *pdev) -{ - struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); - - /* Release resources, unregister device */ - nand_release(nand_to_mtd(&sharpsl->chip)); - - iounmap(sharpsl->io); - - /* Free the MTD device structure */ - kfree(sharpsl); - - return 0; -} - -static struct platform_driver sharpsl_nand_driver = { - .driver = { - .name = "sharpsl-nand", - }, - .probe = sharpsl_nand_probe, - .remove = sharpsl_nand_remove, -}; - -module_platform_driver(sharpsl_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Richard Purdie "); -MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series"); diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c deleted file mode 100644 index c378705c6e2b..000000000000 --- a/drivers/mtd/nand/sm_common.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright © 2009 - Maxim Levitsky - * Common routines & support for xD format - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include "sm_common.h" - -static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 1) - return -ERANGE; - - oobregion->length = 3; - oobregion->offset = ((section + 1) * 8) - 3; - - return 0; -} - -static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - switch (section) { - case 0: - /* reserved */ - oobregion->offset = 0; - oobregion->length = 4; - break; - case 1: - /* LBA1 */ - oobregion->offset = 6; - oobregion->length = 2; - break; - case 2: - /* LBA2 */ - oobregion->offset = 11; - oobregion->length = 2; - break; - default: - return -ERANGE; - } - - return 0; -} - -static const struct mtd_ooblayout_ops oob_sm_ops = { - .ecc = oob_sm_ooblayout_ecc, - .free = oob_sm_ooblayout_free, -}; - -/* NOTE: This layout is is not compatabable with SmartMedia, */ -/* because the 256 byte devices have page depenent oob layout */ -/* However it does preserve the bad block markers */ -/* If you use smftl, it will bypass this and work correctly */ -/* If you not, then you break SmartMedia compliance anyway */ - -static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section) - return -ERANGE; - - oobregion->length = 3; - oobregion->offset = 0; - - return 0; -} - -static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - switch (section) { - case 0: - /* reserved */ - oobregion->offset = 3; - oobregion->length = 2; - break; - case 1: - /* LBA1 */ - oobregion->offset = 6; - oobregion->length = 2; - break; - default: - return -ERANGE; - } - - return 0; -} - -static const struct mtd_ooblayout_ops oob_sm_small_ops = { - .ecc = oob_sm_small_ooblayout_ecc, - .free = oob_sm_small_ooblayout_free, -}; - -static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct mtd_oob_ops ops; - struct sm_oob oob; - int ret; - - memset(&oob, -1, SM_OOB_SIZE); - oob.block_status = 0x0F; - - /* As long as this function is called on erase block boundaries - it will work correctly for 256 byte nand */ - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooboffs = 0; - ops.ooblen = mtd->oobsize; - ops.oobbuf = (void *)&oob; - ops.datbuf = NULL; - - - ret = mtd_write_oob(mtd, ofs, &ops); - if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { - printk(KERN_NOTICE - "sm_common: can't mark sector at %i as bad\n", - (int)ofs); - return -EIO; - } - - return 0; -} - -static struct nand_flash_dev nand_smartmedia_flash_ids[] = { - LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM", 0x5d, 2, SZ_8K, NAND_ROM), - LEGACY_ID_NAND("SmartMedia 4MiB 3,3V", 0xe3, 4, SZ_8K, 0), - LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V", 0xe5, 4, SZ_8K, 0), - LEGACY_ID_NAND("SmartMedia 4MiB 5V", 0x6b, 4, SZ_8K, 0), - LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM", 0xd5, 4, SZ_8K, NAND_ROM), - LEGACY_ID_NAND("SmartMedia 8MiB 3,3V", 0xe6, 8, SZ_8K, 0), - LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM", 0xd6, 8, SZ_8K, NAND_ROM), - LEGACY_ID_NAND("SmartMedia 16MiB 3,3V", 0x73, 16, SZ_16K, 0), - LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM", 0x57, 16, SZ_16K, NAND_ROM), - LEGACY_ID_NAND("SmartMedia 32MiB 3,3V", 0x75, 32, SZ_16K, 0), - LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM", 0x58, 32, SZ_16K, NAND_ROM), - LEGACY_ID_NAND("SmartMedia 64MiB 3,3V", 0x76, 64, SZ_16K, 0), - LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM", 0xd9, 64, SZ_16K, NAND_ROM), - LEGACY_ID_NAND("SmartMedia 128MiB 3,3V", 0x79, 128, SZ_16K, 0), - LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM), - LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V", 0x71, 256, SZ_16K, 0), - LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM), - {NULL} -}; - -static struct nand_flash_dev nand_xd_flash_ids[] = { - LEGACY_ID_NAND("xD 16MiB 3,3V", 0x73, 16, SZ_16K, 0), - LEGACY_ID_NAND("xD 32MiB 3,3V", 0x75, 32, SZ_16K, 0), - LEGACY_ID_NAND("xD 64MiB 3,3V", 0x76, 64, SZ_16K, 0), - LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128, SZ_16K, 0), - LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256, SZ_16K, NAND_BROKEN_XD), - LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512, SZ_16K, NAND_BROKEN_XD), - LEGACY_ID_NAND("xD 1GiB 3,3V", 0xd3, 1024, SZ_16K, NAND_BROKEN_XD), - LEGACY_ID_NAND("xD 2GiB 3,3V", 0xd5, 2048, SZ_16K, NAND_BROKEN_XD), - {NULL} -}; - -int sm_register_device(struct mtd_info *mtd, int smartmedia) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - chip->options |= NAND_SKIP_BBTSCAN; - - /* Scan for card properties */ - ret = nand_scan_ident(mtd, 1, smartmedia ? - nand_smartmedia_flash_ids : nand_xd_flash_ids); - - if (ret) - return ret; - - /* Bad block marker position */ - chip->badblockpos = 0x05; - chip->badblockbits = 7; - chip->block_markbad = sm_block_markbad; - - /* ECC layout */ - if (mtd->writesize == SM_SECTOR_SIZE) - mtd_set_ooblayout(mtd, &oob_sm_ops); - else if (mtd->writesize == SM_SMALL_PAGE) - mtd_set_ooblayout(mtd, &oob_sm_small_ops); - else - return -ENODEV; - - ret = nand_scan_tail(mtd); - - if (ret) - return ret; - - return mtd_device_register(mtd, NULL, 0); -} -EXPORT_SYMBOL_GPL(sm_register_device); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Maxim Levitsky "); -MODULE_DESCRIPTION("Common SmartMedia/xD functions"); diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h deleted file mode 100644 index 1581671b05ae..000000000000 --- a/drivers/mtd/nand/sm_common.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2009 - Maxim Levitsky - * Common routines & support for SmartMedia/xD format - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include - -/* Full oob structure as written on the flash */ -struct sm_oob { - uint32_t reserved; - uint8_t data_status; - uint8_t block_status; - uint8_t lba_copy1[2]; - uint8_t ecc2[3]; - uint8_t lba_copy2[2]; - uint8_t ecc1[3]; -} __packed; - - -/* one sector is always 512 bytes, but it can consist of two nand pages */ -#define SM_SECTOR_SIZE 512 - -/* oob area is also 16 bytes, but might be from two pages */ -#define SM_OOB_SIZE 16 - -/* This is maximum zone size, and all devices that have more that one zone - have this size */ -#define SM_MAX_ZONE_SIZE 1024 - -/* support for small page nand */ -#define SM_SMALL_PAGE 256 -#define SM_SMALL_OOB_SIZE 8 - - -int sm_register_device(struct mtd_info *mtd, int smartmedia); - - -static inline int sm_sector_valid(struct sm_oob *oob) -{ - return hweight16(oob->data_status) >= 5; -} - -static inline int sm_block_valid(struct sm_oob *oob) -{ - return hweight16(oob->block_status) >= 7; -} - -static inline int sm_block_erased(struct sm_oob *oob) -{ - static const uint32_t erased_pattern[4] = { - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; - - /* First test for erased block */ - if (!memcmp(oob, erased_pattern, sizeof(*oob))) - return 1; - return 0; -} diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c deleted file mode 100644 index 9824a9923583..000000000000 --- a/drivers/mtd/nand/socrates_nand.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright © 2008 Ilya Yanok, Emcraft Systems - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define FPGA_NAND_CMD_MASK (0x7 << 28) -#define FPGA_NAND_CMD_COMMAND (0x0 << 28) -#define FPGA_NAND_CMD_ADDR (0x1 << 28) -#define FPGA_NAND_CMD_READ (0x2 << 28) -#define FPGA_NAND_CMD_WRITE (0x3 << 28) -#define FPGA_NAND_BUSY (0x1 << 15) -#define FPGA_NAND_ENABLE (0x1 << 31) -#define FPGA_NAND_DATA_SHIFT 16 - -struct socrates_nand_host { - struct nand_chip nand_chip; - void __iomem *io_base; - struct device *dev; -}; - -/** - * socrates_nand_write_buf - write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void socrates_nand_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - int i; - struct nand_chip *this = mtd_to_nand(mtd); - struct socrates_nand_host *host = nand_get_controller_data(this); - - for (i = 0; i < len; i++) { - out_be32(host->io_base, FPGA_NAND_ENABLE | - FPGA_NAND_CMD_WRITE | - (buf[i] << FPGA_NAND_DATA_SHIFT)); - } -} - -/** - * socrates_nand_read_buf - read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - int i; - struct nand_chip *this = mtd_to_nand(mtd); - struct socrates_nand_host *host = nand_get_controller_data(this); - uint32_t val; - - val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; - - out_be32(host->io_base, val); - for (i = 0; i < len; i++) { - buf[i] = (in_be32(host->io_base) >> - FPGA_NAND_DATA_SHIFT) & 0xff; - } -} - -/** - * socrates_nand_read_byte - read one byte from the chip - * @mtd: MTD device structure - */ -static uint8_t socrates_nand_read_byte(struct mtd_info *mtd) -{ - uint8_t byte; - socrates_nand_read_buf(mtd, &byte, sizeof(byte)); - return byte; -} - -/** - * socrates_nand_read_word - read one word from the chip - * @mtd: MTD device structure - */ -static uint16_t socrates_nand_read_word(struct mtd_info *mtd) -{ - uint16_t word; - socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word)); - return word; -} - -/* - * Hardware specific access to control-lines - */ -static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct socrates_nand_host *host = nand_get_controller_data(nand_chip); - uint32_t val; - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - val = FPGA_NAND_CMD_COMMAND; - else - val = FPGA_NAND_CMD_ADDR; - - if (ctrl & NAND_NCE) - val |= FPGA_NAND_ENABLE; - - val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; - - out_be32(host->io_base, val); -} - -/* - * Read the Device Ready pin. - */ -static int socrates_nand_device_ready(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct socrates_nand_host *host = nand_get_controller_data(nand_chip); - - if (in_be32(host->io_base) & FPGA_NAND_BUSY) - return 0; /* busy */ - return 1; -} - -/* - * Probe for the NAND device. - */ -static int socrates_nand_probe(struct platform_device *ofdev) -{ - struct socrates_nand_host *host; - struct mtd_info *mtd; - struct nand_chip *nand_chip; - int res; - - /* Allocate memory for the device structure (and zero it) */ - host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); - if (!host) - return -ENOMEM; - - host->io_base = of_iomap(ofdev->dev.of_node, 0); - if (host->io_base == NULL) { - dev_err(&ofdev->dev, "ioremap failed\n"); - return -EIO; - } - - nand_chip = &host->nand_chip; - mtd = nand_to_mtd(nand_chip); - host->dev = &ofdev->dev; - - /* link the private data structures */ - nand_set_controller_data(nand_chip, host); - nand_set_flash_node(nand_chip, ofdev->dev.of_node); - mtd->name = "socrates_nand"; - mtd->dev.parent = &ofdev->dev; - - /*should never be accessed directly */ - nand_chip->IO_ADDR_R = (void *)0xdeadbeef; - nand_chip->IO_ADDR_W = (void *)0xdeadbeef; - - nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl; - nand_chip->read_byte = socrates_nand_read_byte; - nand_chip->read_word = socrates_nand_read_word; - nand_chip->write_buf = socrates_nand_write_buf; - nand_chip->read_buf = socrates_nand_read_buf; - nand_chip->dev_ready = socrates_nand_device_ready; - - nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ - nand_chip->ecc.algo = NAND_ECC_HAMMING; - - /* TODO: I have no idea what real delay is. */ - nand_chip->chip_delay = 20; /* 20us command delay time */ - - dev_set_drvdata(&ofdev->dev, host); - - res = nand_scan(mtd, 1); - if (res) - goto out; - - res = mtd_device_register(mtd, NULL, 0); - if (!res) - return res; - - nand_release(mtd); - -out: - iounmap(host->io_base); - return res; -} - -/* - * Remove a NAND device. - */ -static int socrates_nand_remove(struct platform_device *ofdev) -{ - struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); - struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); - - nand_release(mtd); - - iounmap(host->io_base); - - return 0; -} - -static const struct of_device_id socrates_nand_match[] = -{ - { - .compatible = "abb,socrates-nand", - }, - {}, -}; - -MODULE_DEVICE_TABLE(of, socrates_nand_match); - -static struct platform_driver socrates_nand_driver = { - .driver = { - .name = "socrates_nand", - .of_match_table = socrates_nand_match, - }, - .probe = socrates_nand_probe, - .remove = socrates_nand_remove, -}; - -module_platform_driver(socrates_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ilya Yanok"); -MODULE_DESCRIPTION("NAND driver for Socrates board"); diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c deleted file mode 100644 index f5a55c63935c..000000000000 --- a/drivers/mtd/nand/sunxi_nand.c +++ /dev/null @@ -1,2321 +0,0 @@ -/* - * Copyright (C) 2013 Boris BREZILLON - * - * Derived from: - * https://github.com/yuq/sunxi-nfc-mtd - * Copyright (C) 2013 Qiang Yu - * - * https://github.com/hno/Allwinner-Info - * Copyright (C) 2013 Henrik Nordström - * - * Copyright (C) 2013 Dmitriy B. - * Copyright (C) 2013 Sergey Lapin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NFC_REG_CTL 0x0000 -#define NFC_REG_ST 0x0004 -#define NFC_REG_INT 0x0008 -#define NFC_REG_TIMING_CTL 0x000C -#define NFC_REG_TIMING_CFG 0x0010 -#define NFC_REG_ADDR_LOW 0x0014 -#define NFC_REG_ADDR_HIGH 0x0018 -#define NFC_REG_SECTOR_NUM 0x001C -#define NFC_REG_CNT 0x0020 -#define NFC_REG_CMD 0x0024 -#define NFC_REG_RCMD_SET 0x0028 -#define NFC_REG_WCMD_SET 0x002C -#define NFC_REG_IO_DATA 0x0030 -#define NFC_REG_ECC_CTL 0x0034 -#define NFC_REG_ECC_ST 0x0038 -#define NFC_REG_DEBUG 0x003C -#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) -#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) -#define NFC_REG_SPARE_AREA 0x00A0 -#define NFC_REG_PAT_ID 0x00A4 -#define NFC_RAM0_BASE 0x0400 -#define NFC_RAM1_BASE 0x0800 - -/* define bit use in NFC_CTL */ -#define NFC_EN BIT(0) -#define NFC_RESET BIT(1) -#define NFC_BUS_WIDTH_MSK BIT(2) -#define NFC_BUS_WIDTH_8 (0 << 2) -#define NFC_BUS_WIDTH_16 (1 << 2) -#define NFC_RB_SEL_MSK BIT(3) -#define NFC_RB_SEL(x) ((x) << 3) -#define NFC_CE_SEL_MSK GENMASK(26, 24) -#define NFC_CE_SEL(x) ((x) << 24) -#define NFC_CE_CTL BIT(6) -#define NFC_PAGE_SHIFT_MSK GENMASK(11, 8) -#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) -#define NFC_SAM BIT(12) -#define NFC_RAM_METHOD BIT(14) -#define NFC_DEBUG_CTL BIT(31) - -/* define bit use in NFC_ST */ -#define NFC_RB_B2R BIT(0) -#define NFC_CMD_INT_FLAG BIT(1) -#define NFC_DMA_INT_FLAG BIT(2) -#define NFC_CMD_FIFO_STATUS BIT(3) -#define NFC_STA BIT(4) -#define NFC_NATCH_INT_FLAG BIT(5) -#define NFC_RB_STATE(x) BIT(x + 8) - -/* define bit use in NFC_INT */ -#define NFC_B2R_INT_ENABLE BIT(0) -#define NFC_CMD_INT_ENABLE BIT(1) -#define NFC_DMA_INT_ENABLE BIT(2) -#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ - NFC_CMD_INT_ENABLE | \ - NFC_DMA_INT_ENABLE) - -/* define bit use in NFC_TIMING_CTL */ -#define NFC_TIMING_CTL_EDO BIT(8) - -/* define NFC_TIMING_CFG register layout */ -#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ - (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ - (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ - (((tCAD) & 0x7) << 8)) - -/* define bit use in NFC_CMD */ -#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0) -#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) -#define NFC_CMD(x) (x) -#define NFC_ADR_NUM_MSK GENMASK(18, 16) -#define NFC_ADR_NUM(x) (((x) - 1) << 16) -#define NFC_SEND_ADR BIT(19) -#define NFC_ACCESS_DIR BIT(20) -#define NFC_DATA_TRANS BIT(21) -#define NFC_SEND_CMD1 BIT(22) -#define NFC_WAIT_FLAG BIT(23) -#define NFC_SEND_CMD2 BIT(24) -#define NFC_SEQ BIT(25) -#define NFC_DATA_SWAP_METHOD BIT(26) -#define NFC_ROW_AUTO_INC BIT(27) -#define NFC_SEND_CMD3 BIT(28) -#define NFC_SEND_CMD4 BIT(29) -#define NFC_CMD_TYPE_MSK GENMASK(31, 30) -#define NFC_NORMAL_OP (0 << 30) -#define NFC_ECC_OP (1 << 30) -#define NFC_PAGE_OP (2 << 30) - -/* define bit use in NFC_RCMD_SET */ -#define NFC_READ_CMD_MSK GENMASK(7, 0) -#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8) -#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16) - -/* define bit use in NFC_WCMD_SET */ -#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0) -#define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8) -#define NFC_READ_CMD0_MSK GENMASK(23, 16) -#define NFC_READ_CMD1_MSK GENMASK(31, 24) - -/* define bit use in NFC_ECC_CTL */ -#define NFC_ECC_EN BIT(0) -#define NFC_ECC_PIPELINE BIT(3) -#define NFC_ECC_EXCEPTION BIT(4) -#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) -#define NFC_ECC_BLOCK_512 BIT(5) -#define NFC_RANDOM_EN BIT(9) -#define NFC_RANDOM_DIRECTION BIT(10) -#define NFC_ECC_MODE_MSK GENMASK(15, 12) -#define NFC_ECC_MODE(x) ((x) << 12) -#define NFC_RANDOM_SEED_MSK GENMASK(30, 16) -#define NFC_RANDOM_SEED(x) ((x) << 16) - -/* define bit use in NFC_ECC_ST */ -#define NFC_ECC_ERR(x) BIT(x) -#define NFC_ECC_ERR_MSK GENMASK(15, 0) -#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) -#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) - -#define NFC_DEFAULT_TIMEOUT_MS 1000 - -#define NFC_SRAM_SIZE 1024 - -#define NFC_MAX_CS 7 - -/* - * Ready/Busy detection type: describes the Ready/Busy detection modes - * - * @RB_NONE: no external detection available, rely on STATUS command - * and software timeouts - * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy - * pin of the NAND flash chip must be connected to one of the - * native NAND R/B pins (those which can be muxed to the NAND - * Controller) - * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy - * pin of the NAND flash chip must be connected to a GPIO capable - * pin. - */ -enum sunxi_nand_rb_type { - RB_NONE, - RB_NATIVE, - RB_GPIO, -}; - -/* - * Ready/Busy structure: stores information related to Ready/Busy detection - * - * @type: the Ready/Busy detection mode - * @info: information related to the R/B detection mode. Either a gpio - * id or a native R/B id (those supported by the NAND controller). - */ -struct sunxi_nand_rb { - enum sunxi_nand_rb_type type; - union { - int gpio; - int nativeid; - } info; -}; - -/* - * Chip Select structure: stores information related to NAND Chip Select - * - * @cs: the NAND CS id used to communicate with a NAND Chip - * @rb: the Ready/Busy description - */ -struct sunxi_nand_chip_sel { - u8 cs; - struct sunxi_nand_rb rb; -}; - -/* - * sunxi HW ECC infos: stores information related to HW ECC support - * - * @mode: the sunxi ECC mode field deduced from ECC requirements - */ -struct sunxi_nand_hw_ecc { - int mode; -}; - -/* - * NAND chip structure: stores NAND chip device related information - * - * @node: used to store NAND chips into a list - * @nand: base NAND chip structure - * @mtd: base MTD structure - * @clk_rate: clk_rate required for this NAND chip - * @timing_cfg TIMING_CFG register value for this NAND chip - * @selected: current active CS - * @nsels: number of CS lines required by the NAND chip - * @sels: array of CS lines descriptions - */ -struct sunxi_nand_chip { - struct list_head node; - struct nand_chip nand; - unsigned long clk_rate; - u32 timing_cfg; - u32 timing_ctl; - int selected; - int addr_cycles; - u32 addr[2]; - int cmd_cycles; - u8 cmd[2]; - int nsels; - struct sunxi_nand_chip_sel sels[0]; -}; - -static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) -{ - return container_of(nand, struct sunxi_nand_chip, nand); -} - -/* - * NAND Controller structure: stores sunxi NAND controller information - * - * @controller: base controller structure - * @dev: parent device (used to print error messages) - * @regs: NAND controller registers - * @ahb_clk: NAND Controller AHB clock - * @mod_clk: NAND Controller mod clock - * @assigned_cs: bitmask describing already assigned CS lines - * @clk_rate: NAND controller current clock rate - * @chips: a list containing all the NAND chips attached to - * this NAND controller - * @complete: a completion object used to wait for NAND - * controller events - */ -struct sunxi_nfc { - struct nand_hw_control controller; - struct device *dev; - void __iomem *regs; - struct clk *ahb_clk; - struct clk *mod_clk; - struct reset_control *reset; - unsigned long assigned_cs; - unsigned long clk_rate; - struct list_head chips; - struct completion complete; - struct dma_chan *dmac; -}; - -static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) -{ - return container_of(ctrl, struct sunxi_nfc, controller); -} - -static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id) -{ - struct sunxi_nfc *nfc = dev_id; - u32 st = readl(nfc->regs + NFC_REG_ST); - u32 ien = readl(nfc->regs + NFC_REG_INT); - - if (!(ien & st)) - return IRQ_NONE; - - if ((ien & st) == ien) - complete(&nfc->complete); - - writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST); - writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT); - - return IRQ_HANDLED; -} - -static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events, - bool use_polling, unsigned int timeout_ms) -{ - int ret; - - if (events & ~NFC_INT_MASK) - return -EINVAL; - - if (!timeout_ms) - timeout_ms = NFC_DEFAULT_TIMEOUT_MS; - - if (!use_polling) { - init_completion(&nfc->complete); - - writel(events, nfc->regs + NFC_REG_INT); - - ret = wait_for_completion_timeout(&nfc->complete, - msecs_to_jiffies(timeout_ms)); - if (!ret) - ret = -ETIMEDOUT; - else - ret = 0; - - writel(0, nfc->regs + NFC_REG_INT); - } else { - u32 status; - - ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, - (status & events) == events, 1, - timeout_ms * 1000); - } - - writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST); - - if (ret) - dev_err(nfc->dev, "wait interrupt timedout\n"); - - return ret; -} - -static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) -{ - u32 status; - int ret; - - ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, - !(status & NFC_CMD_FIFO_STATUS), 1, - NFC_DEFAULT_TIMEOUT_MS * 1000); - if (ret) - dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); - - return ret; -} - -static int sunxi_nfc_rst(struct sunxi_nfc *nfc) -{ - u32 ctl; - int ret; - - writel(0, nfc->regs + NFC_REG_ECC_CTL); - writel(NFC_RESET, nfc->regs + NFC_REG_CTL); - - ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl, - !(ctl & NFC_RESET), 1, - NFC_DEFAULT_TIMEOUT_MS * 1000); - if (ret) - dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); - - return ret; -} - -static int sunxi_nfc_dma_op_prepare(struct mtd_info *mtd, const void *buf, - int chunksize, int nchunks, - enum dma_data_direction ddir, - struct scatterlist *sg) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct dma_async_tx_descriptor *dmad; - enum dma_transfer_direction tdir; - dma_cookie_t dmat; - int ret; - - if (ddir == DMA_FROM_DEVICE) - tdir = DMA_DEV_TO_MEM; - else - tdir = DMA_MEM_TO_DEV; - - sg_init_one(sg, buf, nchunks * chunksize); - ret = dma_map_sg(nfc->dev, sg, 1, ddir); - if (!ret) - return -ENOMEM; - - dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); - if (!dmad) { - ret = -EINVAL; - goto err_unmap_buf; - } - - writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, - nfc->regs + NFC_REG_CTL); - writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); - writel(chunksize, nfc->regs + NFC_REG_CNT); - dmat = dmaengine_submit(dmad); - - ret = dma_submit_error(dmat); - if (ret) - goto err_clr_dma_flag; - - return 0; - -err_clr_dma_flag: - writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, - nfc->regs + NFC_REG_CTL); - -err_unmap_buf: - dma_unmap_sg(nfc->dev, sg, 1, ddir); - return ret; -} - -static void sunxi_nfc_dma_op_cleanup(struct mtd_info *mtd, - enum dma_data_direction ddir, - struct scatterlist *sg) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - - dma_unmap_sg(nfc->dev, sg, 1, ddir); - writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, - nfc->regs + NFC_REG_CTL); -} - -static int sunxi_nfc_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - struct sunxi_nand_rb *rb; - int ret; - - if (sunxi_nand->selected < 0) - return 0; - - rb = &sunxi_nand->sels[sunxi_nand->selected].rb; - - switch (rb->type) { - case RB_NATIVE: - ret = !!(readl(nfc->regs + NFC_REG_ST) & - NFC_RB_STATE(rb->info.nativeid)); - break; - case RB_GPIO: - ret = gpio_get_value(rb->info.gpio); - break; - case RB_NONE: - default: - ret = 0; - dev_err(nfc->dev, "cannot check R/B NAND status!\n"); - break; - } - - return ret; -} - -static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - struct sunxi_nand_chip_sel *sel; - u32 ctl; - - if (chip > 0 && chip >= sunxi_nand->nsels) - return; - - if (chip == sunxi_nand->selected) - return; - - ctl = readl(nfc->regs + NFC_REG_CTL) & - ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); - - if (chip >= 0) { - sel = &sunxi_nand->sels[chip]; - - ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | - NFC_PAGE_SHIFT(nand->page_shift); - if (sel->rb.type == RB_NONE) { - nand->dev_ready = NULL; - } else { - nand->dev_ready = sunxi_nfc_dev_ready; - if (sel->rb.type == RB_NATIVE) - ctl |= NFC_RB_SEL(sel->rb.info.nativeid); - } - - writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); - - if (nfc->clk_rate != sunxi_nand->clk_rate) { - clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate); - nfc->clk_rate = sunxi_nand->clk_rate; - } - } - - writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); - writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); - writel(ctl, nfc->regs + NFC_REG_CTL); - - sunxi_nand->selected = chip; -} - -static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - int ret; - int cnt; - int offs = 0; - u32 tmp; - - while (len > offs) { - bool poll = false; - - cnt = min(len - offs, NFC_SRAM_SIZE); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - break; - - writel(cnt, nfc->regs + NFC_REG_CNT); - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; - writel(tmp, nfc->regs + NFC_REG_CMD); - - /* Arbitrary limit for polling mode */ - if (cnt < 64) - poll = true; - - ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); - if (ret) - break; - - if (buf) - memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, - cnt); - offs += cnt; - } -} - -static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - int ret; - int cnt; - int offs = 0; - u32 tmp; - - while (len > offs) { - bool poll = false; - - cnt = min(len - offs, NFC_SRAM_SIZE); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - break; - - writel(cnt, nfc->regs + NFC_REG_CNT); - memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt); - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | - NFC_ACCESS_DIR; - writel(tmp, nfc->regs + NFC_REG_CMD); - - /* Arbitrary limit for polling mode */ - if (cnt < 64) - poll = true; - - ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); - if (ret) - break; - - offs += cnt; - } -} - -static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) -{ - uint8_t ret; - - sunxi_nfc_read_buf(mtd, &ret, 1); - - return ret; -} - -static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, - unsigned int ctrl) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - int ret; - - if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) && - !(ctrl & (NAND_CLE | NAND_ALE))) { - u32 cmd = 0; - - if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles) - return; - - if (sunxi_nand->cmd_cycles--) - cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0]; - - if (sunxi_nand->cmd_cycles--) { - cmd |= NFC_SEND_CMD2; - writel(sunxi_nand->cmd[1], - nfc->regs + NFC_REG_RCMD_SET); - } - - sunxi_nand->cmd_cycles = 0; - - if (sunxi_nand->addr_cycles) { - cmd |= NFC_SEND_ADR | - NFC_ADR_NUM(sunxi_nand->addr_cycles); - writel(sunxi_nand->addr[0], - nfc->regs + NFC_REG_ADDR_LOW); - } - - if (sunxi_nand->addr_cycles > 4) - writel(sunxi_nand->addr[1], - nfc->regs + NFC_REG_ADDR_HIGH); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return; - - writel(cmd, nfc->regs + NFC_REG_CMD); - sunxi_nand->addr[0] = 0; - sunxi_nand->addr[1] = 0; - sunxi_nand->addr_cycles = 0; - sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); - } - - if (ctrl & NAND_CLE) { - sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat; - } else if (ctrl & NAND_ALE) { - sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |= - dat << ((sunxi_nand->addr_cycles % 4) * 8); - sunxi_nand->addr_cycles++; - } -} - -/* These seed values have been extracted from Allwinner's BSP */ -static const u16 sunxi_nfc_randomizer_page_seeds[] = { - 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, - 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, - 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, - 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, - 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, - 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, - 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, - 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, - 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, - 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, - 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, - 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, - 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, - 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, - 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, - 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, -}; - -/* - * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds - * have been generated using - * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what - * the randomizer engine does internally before de/scrambling OOB data. - * - * Those tables are statically defined to avoid calculating randomizer state - * at runtime. - */ -static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = { - 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64, - 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409, - 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617, - 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d, - 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91, - 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d, - 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab, - 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8, - 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8, - 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b, - 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5, - 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a, - 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891, - 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36, - 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd, - 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0, -}; - -static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = { - 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6, - 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982, - 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9, - 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07, - 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e, - 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2, - 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c, - 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f, - 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc, - 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e, - 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8, - 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68, - 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d, - 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179, - 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601, - 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd, -}; - -static u16 sunxi_nfc_randomizer_step(u16 state, int count) -{ - state &= 0x7fff; - - /* - * This loop is just a simple implementation of a Fibonacci LFSR using - * the x16 + x15 + 1 polynomial. - */ - while (count--) - state = ((state >> 1) | - (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff; - - return state; -} - -static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc) -{ - const u16 *seeds = sunxi_nfc_randomizer_page_seeds; - int mod = mtd_div_by_ws(mtd->erasesize, mtd); - - if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) - mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); - - if (ecc) { - if (mtd->ecc_step_size == 512) - seeds = sunxi_nfc_randomizer_ecc512_seeds; - else - seeds = sunxi_nfc_randomizer_ecc1024_seeds; - } - - return seeds[page % mod]; -} - -static void sunxi_nfc_randomizer_config(struct mtd_info *mtd, - int page, bool ecc) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); - u16 state; - - if (!(nand->options & NAND_NEED_SCRAMBLING)) - return; - - ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); - state = sunxi_nfc_randomizer_state(mtd, page, ecc); - ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; - writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); -} - -static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - - if (!(nand->options & NAND_NEED_SCRAMBLING)) - return; - - writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, - nfc->regs + NFC_REG_ECC_CTL); -} - -static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - - if (!(nand->options & NAND_NEED_SCRAMBLING)) - return; - - writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, - nfc->regs + NFC_REG_ECC_CTL); -} - -static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm) -{ - u16 state = sunxi_nfc_randomizer_state(mtd, page, true); - - bbm[0] ^= state; - bbm[1] ^= sunxi_nfc_randomizer_step(state, 8); -} - -static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len, - bool ecc, int page) -{ - sunxi_nfc_randomizer_config(mtd, page, ecc); - sunxi_nfc_randomizer_enable(mtd); - sunxi_nfc_write_buf(mtd, buf, len); - sunxi_nfc_randomizer_disable(mtd); -} - -static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf, - int len, bool ecc, int page) -{ - sunxi_nfc_randomizer_config(mtd, page, ecc); - sunxi_nfc_randomizer_enable(mtd); - sunxi_nfc_read_buf(mtd, buf, len); - sunxi_nfc_randomizer_disable(mtd); -} - -static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct sunxi_nand_hw_ecc *data = nand->ecc.priv; - u32 ecc_ctl; - - ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); - ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | - NFC_ECC_BLOCK_SIZE_MSK); - ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION | - NFC_ECC_PIPELINE; - - if (nand->ecc.size == 512) - ecc_ctl |= NFC_ECC_BLOCK_512; - - writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); -} - -static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - - writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, - nfc->regs + NFC_REG_ECC_CTL); -} - -static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) -{ - buf[0] = user_data; - buf[1] = user_data >> 8; - buf[2] = user_data >> 16; - buf[3] = user_data >> 24; -} - -static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) -{ - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); -} - -static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob, - int step, bool bbm, int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - - sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), - oob); - - /* De-randomize the Bad Block Marker. */ - if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) - sunxi_nfc_randomize_bbm(mtd, page, oob); -} - -static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd, - const u8 *oob, int step, - bool bbm, int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - u8 user_data[4]; - - /* Randomize the Bad Block Marker. */ - if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { - memcpy(user_data, oob, sizeof(user_data)); - sunxi_nfc_randomize_bbm(mtd, page, user_data); - oob = user_data; - } - - writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(step)); -} - -static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd, - unsigned int *max_bitflips, int ret) -{ - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - *max_bitflips = max_t(unsigned int, *max_bitflips, ret); - } -} - -static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob, - int step, u32 status, bool *erased) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct nand_ecc_ctrl *ecc = &nand->ecc; - u32 tmp; - - *erased = false; - - if (status & NFC_ECC_ERR(step)) - return -EBADMSG; - - if (status & NFC_ECC_PAT_FOUND(step)) { - u8 pattern; - - if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { - pattern = 0x0; - } else { - pattern = 0xff; - *erased = true; - } - - if (data) - memset(data, pattern, ecc->size); - - if (oob) - memset(oob, pattern, ecc->bytes + 4); - - return 0; - } - - tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); - - return NFC_ECC_ERR_CNT(step, tmp); -} - -static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, - u8 *data, int data_off, - u8 *oob, int oob_off, - int *cur_off, - unsigned int *max_bitflips, - bool bbm, bool oob_required, int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct nand_ecc_ctrl *ecc = &nand->ecc; - int raw_mode = 0; - bool erased; - int ret; - - if (*cur_off != data_off) - nand_change_read_column_op(nand, data_off, NULL, 0, false); - - sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page); - - if (data_off + ecc->size != oob_off) - nand_change_read_column_op(nand, oob_off, NULL, 0, false); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return ret; - - sunxi_nfc_randomizer_enable(mtd); - writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, - nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); - sunxi_nfc_randomizer_disable(mtd); - if (ret) - return ret; - - *cur_off = oob_off + ecc->bytes + 4; - - ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0, - readl(nfc->regs + NFC_REG_ECC_ST), - &erased); - if (erased) - return 1; - - if (ret < 0) { - /* - * Re-read the data with the randomizer disabled to identify - * bitflips in erased pages. - */ - if (nand->options & NAND_NEED_SCRAMBLING) - nand_change_read_column_op(nand, data_off, data, - ecc->size, false); - else - memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, - ecc->size); - - nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4, - false); - - ret = nand_check_erased_ecc_chunk(data, ecc->size, - oob, ecc->bytes + 4, - NULL, 0, ecc->strength); - if (ret >= 0) - raw_mode = 1; - } else { - memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - - if (oob_required) { - nand_change_read_column_op(nand, oob_off, NULL, 0, - false); - sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, - true, page); - - sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0, - bbm, page); - } - } - - sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret); - - return raw_mode; -} - -static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, - u8 *oob, int *cur_off, - bool randomize, int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + 4) * ecc->steps); - int len = mtd->oobsize - offset; - - if (len <= 0) - return; - - if (!cur_off || *cur_off != offset) - nand_change_read_column_op(nand, mtd->writesize, NULL, 0, - false); - - if (!randomize) - sunxi_nfc_read_buf(mtd, oob + offset, len); - else - sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len, - false, page); - - if (cur_off) - *cur_off = mtd->oobsize + mtd->writesize; -} - -static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf, - int oob_required, int page, - int nchunks) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - bool randomized = nand->options & NAND_NEED_SCRAMBLING; - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct nand_ecc_ctrl *ecc = &nand->ecc; - unsigned int max_bitflips = 0; - int ret, i, raw_mode = 0; - struct scatterlist sg; - u32 status; - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return ret; - - ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, nchunks, - DMA_FROM_DEVICE, &sg); - if (ret) - return ret; - - sunxi_nfc_hw_ecc_enable(mtd); - sunxi_nfc_randomizer_config(mtd, page, false); - sunxi_nfc_randomizer_enable(mtd); - - writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) | - NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET); - - dma_async_issue_pending(nfc->dmac); - - writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS, - nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); - if (ret) - dmaengine_terminate_all(nfc->dmac); - - sunxi_nfc_randomizer_disable(mtd); - sunxi_nfc_hw_ecc_disable(mtd); - - sunxi_nfc_dma_op_cleanup(mtd, DMA_FROM_DEVICE, &sg); - - if (ret) - return ret; - - status = readl(nfc->regs + NFC_REG_ECC_ST); - - for (i = 0; i < nchunks; i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - u8 *data = buf + data_off; - u8 *oob = nand->oob_poi + oob_off; - bool erased; - - ret = sunxi_nfc_hw_ecc_correct(mtd, randomized ? data : NULL, - oob_required ? oob : NULL, - i, status, &erased); - - /* ECC errors are handled in the second loop. */ - if (ret < 0) - continue; - - if (oob_required && !erased) { - /* TODO: use DMA to retrieve OOB */ - nand_change_read_column_op(nand, - mtd->writesize + oob_off, - oob, ecc->bytes + 4, false); - - sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, i, - !i, page); - } - - if (erased) - raw_mode = 1; - - sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret); - } - - if (status & NFC_ECC_ERR_MSK) { - for (i = 0; i < nchunks; i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - u8 *data = buf + data_off; - u8 *oob = nand->oob_poi + oob_off; - - if (!(status & NFC_ECC_ERR(i))) - continue; - - /* - * Re-read the data with the randomizer disabled to - * identify bitflips in erased pages. - * TODO: use DMA to read page in raw mode - */ - if (randomized) - nand_change_read_column_op(nand, data_off, - data, ecc->size, - false); - - /* TODO: use DMA to retrieve OOB */ - nand_change_read_column_op(nand, - mtd->writesize + oob_off, - oob, ecc->bytes + 4, false); - - ret = nand_check_erased_ecc_chunk(data, ecc->size, - oob, ecc->bytes + 4, - NULL, 0, - ecc->strength); - if (ret >= 0) - raw_mode = 1; - - sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret); - } - } - - if (oob_required) - sunxi_nfc_hw_ecc_read_extra_oob(mtd, nand->oob_poi, - NULL, !raw_mode, - page); - - return max_bitflips; -} - -static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, - const u8 *data, int data_off, - const u8 *oob, int oob_off, - int *cur_off, bool bbm, - int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct nand_ecc_ctrl *ecc = &nand->ecc; - int ret; - - if (data_off != *cur_off) - nand_change_write_column_op(nand, data_off, NULL, 0, false); - - sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page); - - if (data_off + ecc->size != oob_off) - nand_change_write_column_op(nand, oob_off, NULL, 0, false); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return ret; - - sunxi_nfc_randomizer_enable(mtd); - sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page); - - writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | - NFC_ACCESS_DIR | NFC_ECC_OP, - nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); - sunxi_nfc_randomizer_disable(mtd); - if (ret) - return ret; - - *cur_off = oob_off + ecc->bytes + 4; - - return 0; -} - -static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, - u8 *oob, int *cur_off, - int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + 4) * ecc->steps); - int len = mtd->oobsize - offset; - - if (len <= 0) - return; - - if (!cur_off || *cur_off != offset) - nand_change_write_column_op(nand, offset + mtd->writesize, - NULL, 0, false); - - sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page); - - if (cur_off) - *cur_off = mtd->oobsize + mtd->writesize; -} - -static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - unsigned int max_bitflips = 0; - int ret, i, cur_off = 0; - bool raw_mode = false; - - nand_read_page_op(chip, page, 0, NULL, 0); - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - u8 *data = buf + data_off; - u8 *oob = chip->oob_poi + oob_off; - - ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, - oob_off + mtd->writesize, - &cur_off, &max_bitflips, - !i, oob_required, page); - if (ret < 0) - return ret; - else if (ret) - raw_mode = true; - } - - if (oob_required) - sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, - !raw_mode, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return max_bitflips; -} - -static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, - int oob_required, int page) -{ - int ret; - - nand_read_page_op(chip, page, 0, NULL, 0); - - ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, oob_required, page, - chip->ecc.steps); - if (ret >= 0) - return ret; - - /* Fallback to PIO mode */ - return sunxi_nfc_hw_ecc_read_page(mtd, chip, buf, oob_required, page); -} - -static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, - struct nand_chip *chip, - u32 data_offs, u32 readlen, - u8 *bufpoi, int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i, cur_off = 0; - unsigned int max_bitflips = 0; - - nand_read_page_op(chip, page, 0, NULL, 0); - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = data_offs / ecc->size; - i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - u8 *data = bufpoi + data_off; - u8 *oob = chip->oob_poi + oob_off; - - ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, - oob, - oob_off + mtd->writesize, - &cur_off, &max_bitflips, !i, - false, page); - if (ret < 0) - return ret; - } - - sunxi_nfc_hw_ecc_disable(mtd); - - return max_bitflips; -} - -static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd, - struct nand_chip *chip, - u32 data_offs, u32 readlen, - u8 *buf, int page) -{ - int nchunks = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); - int ret; - - nand_read_page_op(chip, page, 0, NULL, 0); - - ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, false, page, nchunks); - if (ret >= 0) - return ret; - - /* Fallback to PIO mode */ - return sunxi_nfc_hw_ecc_read_subpage(mtd, chip, data_offs, readlen, - buf, page); -} - -static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i, cur_off = 0; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - const u8 *data = buf + data_off; - const u8 *oob = chip->oob_poi + oob_off; - - ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, - oob_off + mtd->writesize, - &cur_off, !i, page); - if (ret) - return ret; - } - - if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) - sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, - &cur_off, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return nand_prog_page_end_op(chip); -} - -static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, - struct nand_chip *chip, - u32 data_offs, u32 data_len, - const u8 *buf, int oob_required, - int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i, cur_off = 0; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = data_offs / ecc->size; - i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - const u8 *data = buf + data_off; - const u8 *oob = chip->oob_poi + oob_off; - - ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, - oob_off + mtd->writesize, - &cur_off, !i, page); - if (ret) - return ret; - } - - sunxi_nfc_hw_ecc_disable(mtd); - - return nand_prog_page_end_op(chip); -} - -static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd, - struct nand_chip *chip, - const u8 *buf, - int oob_required, - int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct nand_ecc_ctrl *ecc = &nand->ecc; - struct scatterlist sg; - int ret, i; - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return ret; - - ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, ecc->steps, - DMA_TO_DEVICE, &sg); - if (ret) - goto pio_fallback; - - for (i = 0; i < ecc->steps; i++) { - const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4)); - - sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, i, !i, page); - } - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - sunxi_nfc_hw_ecc_enable(mtd); - sunxi_nfc_randomizer_config(mtd, page, false); - sunxi_nfc_randomizer_enable(mtd); - - writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG, - nfc->regs + NFC_REG_RCMD_SET); - - dma_async_issue_pending(nfc->dmac); - - writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | - NFC_DATA_TRANS | NFC_ACCESS_DIR, - nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); - if (ret) - dmaengine_terminate_all(nfc->dmac); - - sunxi_nfc_randomizer_disable(mtd); - sunxi_nfc_hw_ecc_disable(mtd); - - sunxi_nfc_dma_op_cleanup(mtd, DMA_TO_DEVICE, &sg); - - if (ret) - return ret; - - if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) - /* TODO: use DMA to transfer extra OOB bytes ? */ - sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, - NULL, page); - - return nand_prog_page_end_op(chip); - -pio_fallback: - return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page); -} - -static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int oob_required, - int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - unsigned int max_bitflips = 0; - int ret, i, cur_off = 0; - bool raw_mode = false; - - nand_read_page_op(chip, page, 0, NULL, 0); - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + 4); - int oob_off = data_off + ecc->size; - u8 *data = buf + (i * ecc->size); - u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); - - ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, - oob_off, &cur_off, - &max_bitflips, !i, - oob_required, - page); - if (ret < 0) - return ret; - else if (ret) - raw_mode = true; - } - - if (oob_required) - sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, - !raw_mode, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return max_bitflips; -} - -static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required, int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i, cur_off = 0; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + 4); - int oob_off = data_off + ecc->size; - const u8 *data = buf + (i * ecc->size); - const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); - - ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, - oob, oob_off, &cur_off, - false, page); - if (ret) - return ret; - } - - if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) - sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, - &cur_off, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return nand_prog_page_end_op(chip); -} - -static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd, - struct nand_chip *chip, - int page) -{ - chip->pagebuf = -1; - - return chip->ecc.read_page(mtd, chip, chip->data_buf, 1, page); -} - -static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, - struct nand_chip *chip, - int page) -{ - int ret; - - chip->pagebuf = -1; - - memset(chip->data_buf, 0xff, mtd->writesize); - ret = chip->ecc.write_page(mtd, chip, chip->data_buf, 1, page); - if (ret) - return ret; - - /* Send command to program the OOB data */ - return nand_prog_page_end_op(chip); -} - -static const s32 tWB_lut[] = {6, 12, 16, 20}; -static const s32 tRHW_lut[] = {4, 8, 12, 20}; - -static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, - u32 clk_period) -{ - u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); - int i; - - for (i = 0; i < lut_size; i++) { - if (clk_cycles <= lut[i]) - return i; - } - - /* Doesn't fit */ - return -EINVAL; -} - -#define sunxi_nand_lookup_timing(l, p, c) \ - _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) - -static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd, int csline, - const struct nand_data_interface *conf) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *chip = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller); - const struct nand_sdr_timings *timings; - u32 min_clk_period = 0; - s32 tWB, tADL, tWHR, tRHW, tCAD; - long real_clk_rate; - - timings = nand_get_sdr_timings(conf); - if (IS_ERR(timings)) - return -ENOTSUPP; - - /* T1 <=> tCLS */ - if (timings->tCLS_min > min_clk_period) - min_clk_period = timings->tCLS_min; - - /* T2 <=> tCLH */ - if (timings->tCLH_min > min_clk_period) - min_clk_period = timings->tCLH_min; - - /* T3 <=> tCS */ - if (timings->tCS_min > min_clk_period) - min_clk_period = timings->tCS_min; - - /* T4 <=> tCH */ - if (timings->tCH_min > min_clk_period) - min_clk_period = timings->tCH_min; - - /* T5 <=> tWP */ - if (timings->tWP_min > min_clk_period) - min_clk_period = timings->tWP_min; - - /* T6 <=> tWH */ - if (timings->tWH_min > min_clk_period) - min_clk_period = timings->tWH_min; - - /* T7 <=> tALS */ - if (timings->tALS_min > min_clk_period) - min_clk_period = timings->tALS_min; - - /* T8 <=> tDS */ - if (timings->tDS_min > min_clk_period) - min_clk_period = timings->tDS_min; - - /* T9 <=> tDH */ - if (timings->tDH_min > min_clk_period) - min_clk_period = timings->tDH_min; - - /* T10 <=> tRR */ - if (timings->tRR_min > (min_clk_period * 3)) - min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); - - /* T11 <=> tALH */ - if (timings->tALH_min > min_clk_period) - min_clk_period = timings->tALH_min; - - /* T12 <=> tRP */ - if (timings->tRP_min > min_clk_period) - min_clk_period = timings->tRP_min; - - /* T13 <=> tREH */ - if (timings->tREH_min > min_clk_period) - min_clk_period = timings->tREH_min; - - /* T14 <=> tRC */ - if (timings->tRC_min > (min_clk_period * 2)) - min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2); - - /* T15 <=> tWC */ - if (timings->tWC_min > (min_clk_period * 2)) - min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); - - /* T16 - T19 + tCAD */ - if (timings->tWB_max > (min_clk_period * 20)) - min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20); - - if (timings->tADL_min > (min_clk_period * 32)) - min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32); - - if (timings->tWHR_min > (min_clk_period * 32)) - min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32); - - if (timings->tRHW_min > (min_clk_period * 20)) - min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20); - - tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, - min_clk_period); - if (tWB < 0) { - dev_err(nfc->dev, "unsupported tWB\n"); - return tWB; - } - - tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; - if (tADL > 3) { - dev_err(nfc->dev, "unsupported tADL\n"); - return -EINVAL; - } - - tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; - if (tWHR > 3) { - dev_err(nfc->dev, "unsupported tWHR\n"); - return -EINVAL; - } - - tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, - min_clk_period); - if (tRHW < 0) { - dev_err(nfc->dev, "unsupported tRHW\n"); - return tRHW; - } - - if (csline == NAND_DATA_IFACE_CHECK_ONLY) - return 0; - - /* - * TODO: according to ONFI specs this value only applies for DDR NAND, - * but Allwinner seems to set this to 0x7. Mimic them for now. - */ - tCAD = 0x7; - - /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ - chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); - - /* Convert min_clk_period from picoseconds to nanoseconds */ - min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); - - /* - * Unlike what is stated in Allwinner datasheet, the clk_rate should - * be set to (1 / min_clk_period), and not (2 / min_clk_period). - * This new formula was verified with a scope and validated by - * Allwinner engineers. - */ - chip->clk_rate = NSEC_PER_SEC / min_clk_period; - real_clk_rate = clk_round_rate(nfc->mod_clk, chip->clk_rate); - if (real_clk_rate <= 0) { - dev_err(nfc->dev, "Unable to round clk %lu\n", chip->clk_rate); - return -EINVAL; - } - - /* - * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data - * output cycle timings shall be used if the host drives tRC less than - * 30 ns. - */ - min_clk_period = NSEC_PER_SEC / real_clk_rate; - chip->timing_ctl = ((min_clk_period * 2) < 30) ? - NFC_TIMING_CTL_EDO : 0; - - return 0; -} - -static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &nand->ecc; - - if (section >= ecc->steps) - return -ERANGE; - - oobregion->offset = section * (ecc->bytes + 4) + 4; - oobregion->length = ecc->bytes; - - return 0; -} - -static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &nand->ecc; - - if (section > ecc->steps) - return -ERANGE; - - /* - * The first 2 bytes are used for BB markers, hence we - * only have 2 bytes available in the first user data - * section. - */ - if (!section && ecc->mode == NAND_ECC_HW) { - oobregion->offset = 2; - oobregion->length = 2; - - return 0; - } - - oobregion->offset = section * (ecc->bytes + 4); - - if (section < ecc->steps) - oobregion->length = 4; - else - oobregion->offset = mtd->oobsize - oobregion->offset; - - return 0; -} - -static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { - .ecc = sunxi_nand_ooblayout_ecc, - .free = sunxi_nand_ooblayout_free, -}; - -static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc, - struct device_node *np) -{ - static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - struct sunxi_nand_hw_ecc *data; - int nsectors; - int ret; - int i; - - if (ecc->options & NAND_ECC_MAXIMIZE) { - int bytes; - - ecc->size = 1024; - nsectors = mtd->writesize / ecc->size; - - /* Reserve 2 bytes for the BBM */ - bytes = (mtd->oobsize - 2) / nsectors; - - /* 4 non-ECC bytes are added before each ECC bytes section */ - bytes -= 4; - - /* and bytes has to be even. */ - if (bytes % 2) - bytes--; - - ecc->strength = bytes * 8 / fls(8 * ecc->size); - - for (i = 0; i < ARRAY_SIZE(strengths); i++) { - if (strengths[i] > ecc->strength) - break; - } - - if (!i) - ecc->strength = 0; - else - ecc->strength = strengths[i - 1]; - } - - if (ecc->size != 512 && ecc->size != 1024) - return -EINVAL; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - /* Prefer 1k ECC chunk over 512 ones */ - if (ecc->size == 512 && mtd->writesize > 512) { - ecc->size = 1024; - ecc->strength *= 2; - } - - /* Add ECC info retrieval from DT */ - for (i = 0; i < ARRAY_SIZE(strengths); i++) { - if (ecc->strength <= strengths[i]) { - /* - * Update ecc->strength value with the actual strength - * that will be used by the ECC engine. - */ - ecc->strength = strengths[i]; - break; - } - } - - if (i >= ARRAY_SIZE(strengths)) { - dev_err(nfc->dev, "unsupported strength\n"); - ret = -ENOTSUPP; - goto err; - } - - data->mode = i; - - /* HW ECC always request ECC bytes for 1024 bytes blocks */ - ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); - - /* HW ECC always work with even numbers of ECC bytes */ - ecc->bytes = ALIGN(ecc->bytes, 2); - - nsectors = mtd->writesize / ecc->size; - - if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { - ret = -EINVAL; - goto err; - } - - ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob; - ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob; - mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); - ecc->priv = data; - - return 0; - -err: - kfree(data); - - return ret; -} - -static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) -{ - kfree(ecc->priv); -} - -static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc, - struct device_node *np) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - int ret; - - ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); - if (ret) - return ret; - - if (nfc->dmac) { - ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; - ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; - ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma; - nand->options |= NAND_USE_BOUNCE_BUFFER; - } else { - ecc->read_page = sunxi_nfc_hw_ecc_read_page; - ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; - ecc->write_page = sunxi_nfc_hw_ecc_write_page; - } - - /* TODO: support DMA for raw accesses and subpage write */ - ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage; - ecc->read_oob_raw = nand_read_oob_std; - ecc->write_oob_raw = nand_write_oob_std; - - return 0; -} - -static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc, - struct device_node *np) -{ - int ret; - - ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); - if (ret) - return ret; - - ecc->prepad = 4; - ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; - ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; - ecc->read_oob_raw = nand_read_oob_syndrome; - ecc->write_oob_raw = nand_write_oob_syndrome; - - return 0; -} - -static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) -{ - switch (ecc->mode) { - case NAND_ECC_HW: - case NAND_ECC_HW_SYNDROME: - sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc); - break; - case NAND_ECC_NONE: - default: - break; - } -} - -static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, - struct device_node *np) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - int ret; - - if (!ecc->size) { - ecc->size = nand->ecc_step_ds; - ecc->strength = nand->ecc_strength_ds; - } - - if (!ecc->size || !ecc->strength) - return -EINVAL; - - switch (ecc->mode) { - case NAND_ECC_HW: - ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np); - if (ret) - return ret; - break; - case NAND_ECC_HW_SYNDROME: - ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc, np); - if (ret) - return ret; - break; - case NAND_ECC_NONE: - case NAND_ECC_SOFT: - break; - default: - return -EINVAL; - } - - return 0; -} - -static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, - struct device_node *np) -{ - struct sunxi_nand_chip *chip; - struct mtd_info *mtd; - struct nand_chip *nand; - int nsels; - int ret; - int i; - u32 tmp; - - if (!of_get_property(np, "reg", &nsels)) - return -EINVAL; - - nsels /= sizeof(u32); - if (!nsels) { - dev_err(dev, "invalid reg property size\n"); - return -EINVAL; - } - - chip = devm_kzalloc(dev, - sizeof(*chip) + - (nsels * sizeof(struct sunxi_nand_chip_sel)), - GFP_KERNEL); - if (!chip) { - dev_err(dev, "could not allocate chip\n"); - return -ENOMEM; - } - - chip->nsels = nsels; - chip->selected = -1; - - for (i = 0; i < nsels; i++) { - ret = of_property_read_u32_index(np, "reg", i, &tmp); - if (ret) { - dev_err(dev, "could not retrieve reg property: %d\n", - ret); - return ret; - } - - if (tmp > NFC_MAX_CS) { - dev_err(dev, - "invalid reg value: %u (max CS = 7)\n", - tmp); - return -EINVAL; - } - - if (test_and_set_bit(tmp, &nfc->assigned_cs)) { - dev_err(dev, "CS %d already assigned\n", tmp); - return -EINVAL; - } - - chip->sels[i].cs = tmp; - - if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) && - tmp < 2) { - chip->sels[i].rb.type = RB_NATIVE; - chip->sels[i].rb.info.nativeid = tmp; - } else { - ret = of_get_named_gpio(np, "rb-gpios", i); - if (ret >= 0) { - tmp = ret; - chip->sels[i].rb.type = RB_GPIO; - chip->sels[i].rb.info.gpio = tmp; - ret = devm_gpio_request(dev, tmp, "nand-rb"); - if (ret) - return ret; - - ret = gpio_direction_input(tmp); - if (ret) - return ret; - } else { - chip->sels[i].rb.type = RB_NONE; - } - } - } - - nand = &chip->nand; - /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ - nand->chip_delay = 200; - nand->controller = &nfc->controller; - /* - * Set the ECC mode to the default value in case nothing is specified - * in the DT. - */ - nand->ecc.mode = NAND_ECC_HW; - nand_set_flash_node(nand, np); - nand->select_chip = sunxi_nfc_select_chip; - nand->cmd_ctrl = sunxi_nfc_cmd_ctrl; - nand->read_buf = sunxi_nfc_read_buf; - nand->write_buf = sunxi_nfc_write_buf; - nand->read_byte = sunxi_nfc_read_byte; - nand->setup_data_interface = sunxi_nfc_setup_data_interface; - - mtd = nand_to_mtd(nand); - mtd->dev.parent = dev; - - ret = nand_scan_ident(mtd, nsels, NULL); - if (ret) - return ret; - - if (nand->bbt_options & NAND_BBT_USE_FLASH) - nand->bbt_options |= NAND_BBT_NO_OOB; - - if (nand->options & NAND_NEED_SCRAMBLING) - nand->options |= NAND_NO_SUBPAGE_WRITE; - - nand->options |= NAND_SUBPAGE_READ; - - ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np); - if (ret) { - dev_err(dev, "ECC init failed: %d\n", ret); - return ret; - } - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(dev, "nand_scan_tail failed: %d\n", ret); - return ret; - } - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - dev_err(dev, "failed to register mtd device: %d\n", ret); - nand_release(mtd); - return ret; - } - - list_add_tail(&chip->node, &nfc->chips); - - return 0; -} - -static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) -{ - struct device_node *np = dev->of_node; - struct device_node *nand_np; - int nchips = of_get_child_count(np); - int ret; - - if (nchips > 8) { - dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips); - return -EINVAL; - } - - for_each_child_of_node(np, nand_np) { - ret = sunxi_nand_chip_init(dev, nfc, nand_np); - if (ret) { - of_node_put(nand_np); - return ret; - } - } - - return 0; -} - -static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) -{ - struct sunxi_nand_chip *chip; - - while (!list_empty(&nfc->chips)) { - chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip, - node); - nand_release(nand_to_mtd(&chip->nand)); - sunxi_nand_ecc_cleanup(&chip->nand.ecc); - list_del(&chip->node); - } -} - -static int sunxi_nfc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *r; - struct sunxi_nfc *nfc; - int irq; - int ret; - - nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); - if (!nfc) - return -ENOMEM; - - nfc->dev = dev; - nand_hw_control_init(&nfc->controller); - INIT_LIST_HEAD(&nfc->chips); - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nfc->regs = devm_ioremap_resource(dev, r); - if (IS_ERR(nfc->regs)) - return PTR_ERR(nfc->regs); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to retrieve irq\n"); - return irq; - } - - nfc->ahb_clk = devm_clk_get(dev, "ahb"); - if (IS_ERR(nfc->ahb_clk)) { - dev_err(dev, "failed to retrieve ahb clk\n"); - return PTR_ERR(nfc->ahb_clk); - } - - ret = clk_prepare_enable(nfc->ahb_clk); - if (ret) - return ret; - - nfc->mod_clk = devm_clk_get(dev, "mod"); - if (IS_ERR(nfc->mod_clk)) { - dev_err(dev, "failed to retrieve mod clk\n"); - ret = PTR_ERR(nfc->mod_clk); - goto out_ahb_clk_unprepare; - } - - ret = clk_prepare_enable(nfc->mod_clk); - if (ret) - goto out_ahb_clk_unprepare; - - nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); - if (IS_ERR(nfc->reset)) { - ret = PTR_ERR(nfc->reset); - goto out_mod_clk_unprepare; - } - - ret = reset_control_deassert(nfc->reset); - if (ret) { - dev_err(dev, "reset err %d\n", ret); - goto out_mod_clk_unprepare; - } - - ret = sunxi_nfc_rst(nfc); - if (ret) - goto out_ahb_reset_reassert; - - writel(0, nfc->regs + NFC_REG_INT); - ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt, - 0, "sunxi-nand", nfc); - if (ret) - goto out_ahb_reset_reassert; - - nfc->dmac = dma_request_slave_channel(dev, "rxtx"); - if (nfc->dmac) { - struct dma_slave_config dmac_cfg = { }; - - dmac_cfg.src_addr = r->start + NFC_REG_IO_DATA; - dmac_cfg.dst_addr = dmac_cfg.src_addr; - dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width; - dmac_cfg.src_maxburst = 4; - dmac_cfg.dst_maxburst = 4; - dmaengine_slave_config(nfc->dmac, &dmac_cfg); - } else { - dev_warn(dev, "failed to request rxtx DMA channel\n"); - } - - platform_set_drvdata(pdev, nfc); - - ret = sunxi_nand_chips_init(dev, nfc); - if (ret) { - dev_err(dev, "failed to init nand chips\n"); - goto out_release_dmac; - } - - return 0; - -out_release_dmac: - if (nfc->dmac) - dma_release_channel(nfc->dmac); -out_ahb_reset_reassert: - reset_control_assert(nfc->reset); -out_mod_clk_unprepare: - clk_disable_unprepare(nfc->mod_clk); -out_ahb_clk_unprepare: - clk_disable_unprepare(nfc->ahb_clk); - - return ret; -} - -static int sunxi_nfc_remove(struct platform_device *pdev) -{ - struct sunxi_nfc *nfc = platform_get_drvdata(pdev); - - sunxi_nand_chips_cleanup(nfc); - - reset_control_assert(nfc->reset); - - if (nfc->dmac) - dma_release_channel(nfc->dmac); - clk_disable_unprepare(nfc->mod_clk); - clk_disable_unprepare(nfc->ahb_clk); - - return 0; -} - -static const struct of_device_id sunxi_nfc_ids[] = { - { .compatible = "allwinner,sun4i-a10-nand" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, sunxi_nfc_ids); - -static struct platform_driver sunxi_nfc_driver = { - .driver = { - .name = "sunxi_nand", - .of_match_table = sunxi_nfc_ids, - }, - .probe = sunxi_nfc_probe, - .remove = sunxi_nfc_remove, -}; -module_platform_driver(sunxi_nfc_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Boris BREZILLON"); -MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); -MODULE_ALIAS("platform:sunxi_nand"); diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c deleted file mode 100644 index c5bee00b7f5e..000000000000 --- a/drivers/mtd/nand/tango_nand.c +++ /dev/null @@ -1,688 +0,0 @@ -/* - * Copyright (C) 2016 Sigma Designs - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Offsets relative to chip->base */ -#define PBUS_CMD 0 -#define PBUS_ADDR 4 -#define PBUS_DATA 8 - -/* Offsets relative to reg_base */ -#define NFC_STATUS 0x00 -#define NFC_FLASH_CMD 0x04 -#define NFC_DEVICE_CFG 0x08 -#define NFC_TIMING1 0x0c -#define NFC_TIMING2 0x10 -#define NFC_XFER_CFG 0x14 -#define NFC_PKT_0_CFG 0x18 -#define NFC_PKT_N_CFG 0x1c -#define NFC_BB_CFG 0x20 -#define NFC_ADDR_PAGE 0x24 -#define NFC_ADDR_OFFSET 0x28 -#define NFC_XFER_STATUS 0x2c - -/* NFC_STATUS values */ -#define CMD_READY BIT(31) - -/* NFC_FLASH_CMD values */ -#define NFC_READ 1 -#define NFC_WRITE 2 - -/* NFC_XFER_STATUS values */ -#define PAGE_IS_EMPTY BIT(16) - -/* Offsets relative to mem_base */ -#define METADATA 0x000 -#define ERROR_REPORT 0x1c0 - -/* - * Error reports are split in two bytes: - * byte 0 for the first packet in the page (PKT_0) - * byte 1 for other packets in the page (PKT_N, for N > 0) - * ERR_COUNT_PKT_N is the max error count over all but the first packet. - */ -#define ERR_COUNT_PKT_0(v) (((v) >> 0) & 0x3f) -#define ERR_COUNT_PKT_N(v) (((v) >> 8) & 0x3f) -#define DECODE_FAIL_PKT_0(v) (((v) & BIT(7)) == 0) -#define DECODE_FAIL_PKT_N(v) (((v) & BIT(15)) == 0) - -/* Offsets relative to pbus_base */ -#define PBUS_CS_CTRL 0x83c -#define PBUS_PAD_MODE 0x8f0 - -/* PBUS_CS_CTRL values */ -#define PBUS_IORDY BIT(31) - -/* - * PBUS_PAD_MODE values - * In raw mode, the driver communicates directly with the NAND chips. - * In NFC mode, the NAND Flash controller manages the communication. - * We use NFC mode for read and write; raw mode for everything else. - */ -#define MODE_RAW 0 -#define MODE_NFC BIT(31) - -#define METADATA_SIZE 4 -#define BBM_SIZE 6 -#define FIELD_ORDER 15 - -#define MAX_CS 4 - -struct tango_nfc { - struct nand_hw_control hw; - void __iomem *reg_base; - void __iomem *mem_base; - void __iomem *pbus_base; - struct tango_chip *chips[MAX_CS]; - struct dma_chan *chan; - int freq_kHz; -}; - -#define to_tango_nfc(ptr) container_of(ptr, struct tango_nfc, hw) - -struct tango_chip { - struct nand_chip nand_chip; - void __iomem *base; - u32 timing1; - u32 timing2; - u32 xfer_cfg; - u32 pkt_0_cfg; - u32 pkt_n_cfg; - u32 bb_cfg; -}; - -#define to_tango_chip(ptr) container_of(ptr, struct tango_chip, nand_chip) - -#define XFER_CFG(cs, page_count, steps, metadata_size) \ - ((cs) << 24 | (page_count) << 16 | (steps) << 8 | (metadata_size)) - -#define PKT_CFG(size, strength) ((size) << 16 | (strength)) - -#define BB_CFG(bb_offset, bb_size) ((bb_offset) << 16 | (bb_size)) - -#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3)) - -static void tango_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) -{ - struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); - - if (ctrl & NAND_CLE) - writeb_relaxed(dat, tchip->base + PBUS_CMD); - - if (ctrl & NAND_ALE) - writeb_relaxed(dat, tchip->base + PBUS_ADDR); -} - -static int tango_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct tango_nfc *nfc = to_tango_nfc(chip->controller); - - return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY; -} - -static u8 tango_read_byte(struct mtd_info *mtd) -{ - struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); - - return readb_relaxed(tchip->base + PBUS_DATA); -} - -static void tango_read_buf(struct mtd_info *mtd, u8 *buf, int len) -{ - struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); - - ioread8_rep(tchip->base + PBUS_DATA, buf, len); -} - -static void tango_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); - - iowrite8_rep(tchip->base + PBUS_DATA, buf, len); -} - -static void tango_select_chip(struct mtd_info *mtd, int idx) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct tango_nfc *nfc = to_tango_nfc(chip->controller); - struct tango_chip *tchip = to_tango_chip(chip); - - if (idx < 0) - return; /* No "chip unselect" function */ - - writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1); - writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2); - writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG); - writel_relaxed(tchip->pkt_0_cfg, nfc->reg_base + NFC_PKT_0_CFG); - writel_relaxed(tchip->pkt_n_cfg, nfc->reg_base + NFC_PKT_N_CFG); - writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG); -} - -/* - * The controller does not check for bitflips in erased pages, - * therefore software must check instead. - */ -static int check_erased_page(struct nand_chip *chip, u8 *buf) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - u8 *meta = chip->oob_poi + BBM_SIZE; - u8 *ecc = chip->oob_poi + BBM_SIZE + METADATA_SIZE; - const int ecc_size = chip->ecc.bytes; - const int pkt_size = chip->ecc.size; - int i, res, meta_len, bitflips = 0; - - for (i = 0; i < chip->ecc.steps; ++i) { - meta_len = i ? 0 : METADATA_SIZE; - res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, - meta, meta_len, - chip->ecc.strength); - if (res < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += res; - - bitflips = max(res, bitflips); - buf += pkt_size; - ecc += ecc_size; - } - - return bitflips; -} - -static int decode_error_report(struct nand_chip *chip) -{ - u32 status, res; - struct mtd_info *mtd = nand_to_mtd(chip); - struct tango_nfc *nfc = to_tango_nfc(chip->controller); - - status = readl_relaxed(nfc->reg_base + NFC_XFER_STATUS); - if (status & PAGE_IS_EMPTY) - return 0; - - res = readl_relaxed(nfc->mem_base + ERROR_REPORT); - - if (DECODE_FAIL_PKT_0(res) || DECODE_FAIL_PKT_N(res)) - return -EBADMSG; - - /* ERR_COUNT_PKT_N is max, not sum, but that's all we have */ - mtd->ecc_stats.corrected += - ERR_COUNT_PKT_0(res) + ERR_COUNT_PKT_N(res); - - return max(ERR_COUNT_PKT_0(res), ERR_COUNT_PKT_N(res)); -} - -static void tango_dma_callback(void *arg) -{ - complete(arg); -} - -static int do_dma(struct tango_nfc *nfc, enum dma_data_direction dir, int cmd, - const void *buf, int len, int page) -{ - void __iomem *addr = nfc->reg_base + NFC_STATUS; - struct dma_chan *chan = nfc->chan; - struct dma_async_tx_descriptor *desc; - enum dma_transfer_direction tdir; - struct scatterlist sg; - struct completion tx_done; - int err = -EIO; - u32 res, val; - - sg_init_one(&sg, buf, len); - if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1) - return -EIO; - - tdir = dir == DMA_TO_DEVICE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - desc = dmaengine_prep_slave_sg(chan, &sg, 1, tdir, DMA_PREP_INTERRUPT); - if (!desc) - goto dma_unmap; - - desc->callback = tango_dma_callback; - desc->callback_param = &tx_done; - init_completion(&tx_done); - - writel_relaxed(MODE_NFC, nfc->pbus_base + PBUS_PAD_MODE); - - writel_relaxed(page, nfc->reg_base + NFC_ADDR_PAGE); - writel_relaxed(0, nfc->reg_base + NFC_ADDR_OFFSET); - writel_relaxed(cmd, nfc->reg_base + NFC_FLASH_CMD); - - dmaengine_submit(desc); - dma_async_issue_pending(chan); - - res = wait_for_completion_timeout(&tx_done, HZ); - if (res > 0) - err = readl_poll_timeout(addr, val, val & CMD_READY, 0, 1000); - - writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE); - -dma_unmap: - dma_unmap_sg(chan->device->dev, &sg, 1, dir); - - return err; -} - -static int tango_read_page(struct mtd_info *mtd, struct nand_chip *chip, - u8 *buf, int oob_required, int page) -{ - struct tango_nfc *nfc = to_tango_nfc(chip->controller); - int err, res, len = mtd->writesize; - - if (oob_required) - chip->ecc.read_oob(mtd, chip, page); - - err = do_dma(nfc, DMA_FROM_DEVICE, NFC_READ, buf, len, page); - if (err) - return err; - - res = decode_error_report(chip); - if (res < 0) { - chip->ecc.read_oob_raw(mtd, chip, page); - res = check_erased_page(chip, buf); - } - - return res; -} - -static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const u8 *buf, int oob_required, int page) -{ - struct tango_nfc *nfc = to_tango_nfc(chip->controller); - int err, status, len = mtd->writesize; - - /* Calling tango_write_oob() would send PAGEPROG twice */ - if (oob_required) - return -ENOTSUPP; - - writel_relaxed(0xffffffff, nfc->mem_base + METADATA); - err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page); - if (err) - return err; - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - return -EIO; - - return 0; -} - -static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - *pos += len; - - if (!*buf) { - /* skip over "len" bytes */ - nand_change_read_column_op(chip, *pos, NULL, 0, false); - } else { - tango_read_buf(mtd, *buf, len); - *buf += len; - } -} - -static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - - *pos += len; - - if (!*buf) { - /* skip over "len" bytes */ - nand_change_write_column_op(chip, *pos, NULL, 0, false); - } else { - tango_write_buf(mtd, *buf, len); - *buf += len; - } -} - -/* - * Physical page layout (not drawn to scale) - * - * NB: Bad Block Marker area splits PKT_N in two (N1, N2). - * - * +---+-----------------+-------+-----+-----------+-----+----+-------+ - * | M | PKT_0 | ECC_0 | ... | N1 | BBM | N2 | ECC_N | - * +---+-----------------+-------+-----+-----------+-----+----+-------+ - * - * Logical page layout: - * - * +-----+---+-------+-----+-------+ - * oob = | BBM | M | ECC_0 | ... | ECC_N | - * +-----+---+-------+-----+-------+ - * - * +-----------------+-----+-----------------+ - * buf = | PKT_0 | ... | PKT_N | - * +-----------------+-----+-----------------+ - */ -static void raw_read(struct nand_chip *chip, u8 *buf, u8 *oob) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - u8 *oob_orig = oob; - const int page_size = mtd->writesize; - const int ecc_size = chip->ecc.bytes; - const int pkt_size = chip->ecc.size; - int pos = 0; /* position within physical page */ - int rem = page_size; /* bytes remaining until BBM area */ - - if (oob) - oob += BBM_SIZE; - - aux_read(chip, &oob, METADATA_SIZE, &pos); - - while (rem > pkt_size) { - aux_read(chip, &buf, pkt_size, &pos); - aux_read(chip, &oob, ecc_size, &pos); - rem = page_size - pos; - } - - aux_read(chip, &buf, rem, &pos); - aux_read(chip, &oob_orig, BBM_SIZE, &pos); - aux_read(chip, &buf, pkt_size - rem, &pos); - aux_read(chip, &oob, ecc_size, &pos); -} - -static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const u8 *oob_orig = oob; - const int page_size = mtd->writesize; - const int ecc_size = chip->ecc.bytes; - const int pkt_size = chip->ecc.size; - int pos = 0; /* position within physical page */ - int rem = page_size; /* bytes remaining until BBM area */ - - if (oob) - oob += BBM_SIZE; - - aux_write(chip, &oob, METADATA_SIZE, &pos); - - while (rem > pkt_size) { - aux_write(chip, &buf, pkt_size, &pos); - aux_write(chip, &oob, ecc_size, &pos); - rem = page_size - pos; - } - - aux_write(chip, &buf, rem, &pos); - aux_write(chip, &oob_orig, BBM_SIZE, &pos); - aux_write(chip, &buf, pkt_size - rem, &pos); - aux_write(chip, &oob, ecc_size, &pos); -} - -static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - u8 *buf, int oob_required, int page) -{ - nand_read_page_op(chip, page, 0, NULL, 0); - raw_read(chip, buf, chip->oob_poi); - return 0; -} - -static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const u8 *buf, int oob_required, int page) -{ - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - raw_write(chip, buf, chip->oob_poi); - return nand_prog_page_end_op(chip); -} - -static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - nand_read_page_op(chip, page, 0, NULL, 0); - raw_read(chip, NULL, chip->oob_poi); - return 0; -} - -static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - raw_write(chip, NULL, chip->oob_poi); - return nand_prog_page_end_op(chip); -} - -static int oob_ecc(struct mtd_info *mtd, int idx, struct mtd_oob_region *res) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (idx >= ecc->steps) - return -ERANGE; - - res->offset = BBM_SIZE + METADATA_SIZE + ecc->bytes * idx; - res->length = ecc->bytes; - - return 0; -} - -static int oob_free(struct mtd_info *mtd, int idx, struct mtd_oob_region *res) -{ - return -ERANGE; /* no free space in spare area */ -} - -static const struct mtd_ooblayout_ops tango_nand_ooblayout_ops = { - .ecc = oob_ecc, - .free = oob_free, -}; - -static u32 to_ticks(int kHz, int ps) -{ - return DIV_ROUND_UP_ULL((u64)kHz * ps, NSEC_PER_SEC); -} - -static int tango_set_timings(struct mtd_info *mtd, int csline, - const struct nand_data_interface *conf) -{ - const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf); - struct nand_chip *chip = mtd_to_nand(mtd); - struct tango_nfc *nfc = to_tango_nfc(chip->controller); - struct tango_chip *tchip = to_tango_chip(chip); - u32 Trdy, Textw, Twc, Twpw, Tacc, Thold, Trpw, Textr; - int kHz = nfc->freq_kHz; - - if (IS_ERR(sdr)) - return PTR_ERR(sdr); - - if (csline == NAND_DATA_IFACE_CHECK_ONLY) - return 0; - - Trdy = to_ticks(kHz, sdr->tCEA_max - sdr->tREA_max); - Textw = to_ticks(kHz, sdr->tWB_max); - Twc = to_ticks(kHz, sdr->tWC_min); - Twpw = to_ticks(kHz, sdr->tWC_min - sdr->tWP_min); - - Tacc = to_ticks(kHz, sdr->tREA_max); - Thold = to_ticks(kHz, sdr->tREH_min); - Trpw = to_ticks(kHz, sdr->tRC_min - sdr->tREH_min); - Textr = to_ticks(kHz, sdr->tRHZ_max); - - tchip->timing1 = TIMING(Trdy, Textw, Twc, Twpw); - tchip->timing2 = TIMING(Tacc, Thold, Trpw, Textr); - - return 0; -} - -static int chip_init(struct device *dev, struct device_node *np) -{ - u32 cs; - int err, res; - struct mtd_info *mtd; - struct nand_chip *chip; - struct tango_chip *tchip; - struct nand_ecc_ctrl *ecc; - struct tango_nfc *nfc = dev_get_drvdata(dev); - - tchip = devm_kzalloc(dev, sizeof(*tchip), GFP_KERNEL); - if (!tchip) - return -ENOMEM; - - res = of_property_count_u32_elems(np, "reg"); - if (res < 0) - return res; - - if (res != 1) - return -ENOTSUPP; /* Multi-CS chips are not supported */ - - err = of_property_read_u32_index(np, "reg", 0, &cs); - if (err) - return err; - - if (cs >= MAX_CS) - return -EINVAL; - - chip = &tchip->nand_chip; - ecc = &chip->ecc; - mtd = nand_to_mtd(chip); - - chip->read_byte = tango_read_byte; - chip->write_buf = tango_write_buf; - chip->read_buf = tango_read_buf; - chip->select_chip = tango_select_chip; - chip->cmd_ctrl = tango_cmd_ctrl; - chip->dev_ready = tango_dev_ready; - chip->setup_data_interface = tango_set_timings; - chip->options = NAND_USE_BOUNCE_BUFFER | - NAND_NO_SUBPAGE_WRITE | - NAND_WAIT_TCCS; - chip->controller = &nfc->hw; - tchip->base = nfc->pbus_base + (cs * 256); - - nand_set_flash_node(chip, np); - mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops); - mtd->dev.parent = dev; - - err = nand_scan_ident(mtd, 1, NULL); - if (err) - return err; - - ecc->mode = NAND_ECC_HW; - ecc->algo = NAND_ECC_BCH; - ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE); - - ecc->read_page_raw = tango_read_page_raw; - ecc->write_page_raw = tango_write_page_raw; - ecc->read_page = tango_read_page; - ecc->write_page = tango_write_page; - ecc->read_oob = tango_read_oob; - ecc->write_oob = tango_write_oob; - - err = nand_scan_tail(mtd); - if (err) - return err; - - tchip->xfer_cfg = XFER_CFG(cs, 1, ecc->steps, METADATA_SIZE); - tchip->pkt_0_cfg = PKT_CFG(ecc->size + METADATA_SIZE, ecc->strength); - tchip->pkt_n_cfg = PKT_CFG(ecc->size, ecc->strength); - tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE); - - err = mtd_device_register(mtd, NULL, 0); - if (err) - return err; - - nfc->chips[cs] = tchip; - - return 0; -} - -static int tango_nand_remove(struct platform_device *pdev) -{ - int cs; - struct tango_nfc *nfc = platform_get_drvdata(pdev); - - dma_release_channel(nfc->chan); - - for (cs = 0; cs < MAX_CS; ++cs) { - if (nfc->chips[cs]) - nand_release(nand_to_mtd(&nfc->chips[cs]->nand_chip)); - } - - return 0; -} - -static int tango_nand_probe(struct platform_device *pdev) -{ - int err; - struct clk *clk; - struct resource *res; - struct tango_nfc *nfc; - struct device_node *np; - - nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); - if (!nfc) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nfc->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nfc->reg_base)) - return PTR_ERR(nfc->reg_base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - nfc->mem_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nfc->mem_base)) - return PTR_ERR(nfc->mem_base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - nfc->pbus_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nfc->pbus_base)) - return PTR_ERR(nfc->pbus_base); - - writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE); - - clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - nfc->chan = dma_request_chan(&pdev->dev, "rxtx"); - if (IS_ERR(nfc->chan)) - return PTR_ERR(nfc->chan); - - platform_set_drvdata(pdev, nfc); - nand_hw_control_init(&nfc->hw); - nfc->freq_kHz = clk_get_rate(clk) / 1000; - - for_each_child_of_node(pdev->dev.of_node, np) { - err = chip_init(&pdev->dev, np); - if (err) { - tango_nand_remove(pdev); - return err; - } - } - - return 0; -} - -static const struct of_device_id tango_nand_ids[] = { - { .compatible = "sigma,smp8758-nand" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, tango_nand_ids); - -static struct platform_driver tango_nand_driver = { - .probe = tango_nand_probe, - .remove = tango_nand_remove, - .driver = { - .name = "tango-nand", - .of_match_table = tango_nand_ids, - }, -}; - -module_platform_driver(tango_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Sigma Designs"); -MODULE_DESCRIPTION("Tango4 NAND Flash controller driver"); diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c deleted file mode 100644 index dcaa924502de..000000000000 --- a/drivers/mtd/nand/tmio_nand.c +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Toshiba TMIO NAND flash controller driver - * - * Slightly murky pre-git history of the driver: - * - * Copyright (c) Ian Molton 2004, 2005, 2008 - * Original work, independent of sharps code. Included hardware ECC support. - * Hard ECC did not work for writes in the early revisions. - * Copyright (c) Dirk Opfer 2005. - * Modifications developed from sharps code but - * NOT containing any, ported onto Ians base. - * Copyright (c) Chris Humbert 2005 - * Copyright (c) Dmitry Baryshkov 2008 - * Minor fixes - * - * Parts copyright Sebastian Carlier - * - * This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/*--------------------------------------------------------------------------*/ - -/* - * NAND Flash Host Controller Configuration Register - */ -#define CCR_COMMAND 0x04 /* w Command */ -#define CCR_BASE 0x10 /* l NAND Flash Control Reg Base Addr */ -#define CCR_INTP 0x3d /* b Interrupt Pin */ -#define CCR_INTE 0x48 /* b Interrupt Enable */ -#define CCR_EC 0x4a /* b Event Control */ -#define CCR_ICC 0x4c /* b Internal Clock Control */ -#define CCR_ECCC 0x5b /* b ECC Control */ -#define CCR_NFTC 0x60 /* b NAND Flash Transaction Control */ -#define CCR_NFM 0x61 /* b NAND Flash Monitor */ -#define CCR_NFPSC 0x62 /* b NAND Flash Power Supply Control */ -#define CCR_NFDC 0x63 /* b NAND Flash Detect Control */ - -/* - * NAND Flash Control Register - */ -#define FCR_DATA 0x00 /* bwl Data Register */ -#define FCR_MODE 0x04 /* b Mode Register */ -#define FCR_STATUS 0x05 /* b Status Register */ -#define FCR_ISR 0x06 /* b Interrupt Status Register */ -#define FCR_IMR 0x07 /* b Interrupt Mask Register */ - -/* FCR_MODE Register Command List */ -#define FCR_MODE_DATA 0x94 /* Data Data_Mode */ -#define FCR_MODE_COMMAND 0x95 /* Data Command_Mode */ -#define FCR_MODE_ADDRESS 0x96 /* Data Address_Mode */ - -#define FCR_MODE_HWECC_CALC 0xB4 /* HW-ECC Data */ -#define FCR_MODE_HWECC_RESULT 0xD4 /* HW-ECC Calc result Read_Mode */ -#define FCR_MODE_HWECC_RESET 0xF4 /* HW-ECC Reset */ - -#define FCR_MODE_POWER_ON 0x0C /* Power Supply ON to SSFDC card */ -#define FCR_MODE_POWER_OFF 0x08 /* Power Supply OFF to SSFDC card */ - -#define FCR_MODE_LED_OFF 0x00 /* LED OFF */ -#define FCR_MODE_LED_ON 0x04 /* LED ON */ - -#define FCR_MODE_EJECT_ON 0x68 /* Ejection events active */ -#define FCR_MODE_EJECT_OFF 0x08 /* Ejection events ignored */ - -#define FCR_MODE_LOCK 0x6C /* Lock_Mode. Eject Switch Invalid */ -#define FCR_MODE_UNLOCK 0x0C /* UnLock_Mode. Eject Switch is valid */ - -#define FCR_MODE_CONTROLLER_ID 0x40 /* Controller ID Read */ -#define FCR_MODE_STANDBY 0x00 /* SSFDC card Changes Standby State */ - -#define FCR_MODE_WE 0x80 -#define FCR_MODE_ECC1 0x40 -#define FCR_MODE_ECC0 0x20 -#define FCR_MODE_CE 0x10 -#define FCR_MODE_PCNT1 0x08 -#define FCR_MODE_PCNT0 0x04 -#define FCR_MODE_ALE 0x02 -#define FCR_MODE_CLE 0x01 - -#define FCR_STATUS_BUSY 0x80 - -/*--------------------------------------------------------------------------*/ - -struct tmio_nand { - struct nand_chip chip; - - struct platform_device *dev; - - void __iomem *ccr; - void __iomem *fcr; - unsigned long fcr_base; - - unsigned int irq; - - /* for tmio_nand_read_byte */ - u8 read; - unsigned read_good:1; -}; - -static inline struct tmio_nand *mtd_to_tmio(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct tmio_nand, chip); -} - - -/*--------------------------------------------------------------------------*/ - -static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct tmio_nand *tmio = mtd_to_tmio(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - - if (ctrl & NAND_CTRL_CHANGE) { - u8 mode; - - if (ctrl & NAND_NCE) { - mode = FCR_MODE_DATA; - - if (ctrl & NAND_CLE) - mode |= FCR_MODE_CLE; - else - mode &= ~FCR_MODE_CLE; - - if (ctrl & NAND_ALE) - mode |= FCR_MODE_ALE; - else - mode &= ~FCR_MODE_ALE; - } else { - mode = FCR_MODE_STANDBY; - } - - tmio_iowrite8(mode, tmio->fcr + FCR_MODE); - tmio->read_good = 0; - } - - if (cmd != NAND_CMD_NONE) - tmio_iowrite8(cmd, chip->IO_ADDR_W); -} - -static int tmio_nand_dev_ready(struct mtd_info *mtd) -{ - struct tmio_nand *tmio = mtd_to_tmio(mtd); - - return !(tmio_ioread8(tmio->fcr + FCR_STATUS) & FCR_STATUS_BUSY); -} - -static irqreturn_t tmio_irq(int irq, void *__tmio) -{ - struct tmio_nand *tmio = __tmio; - struct nand_chip *nand_chip = &tmio->chip; - - /* disable RDYREQ interrupt */ - tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); - - if (unlikely(!waitqueue_active(&nand_chip->controller->wq))) - dev_warn(&tmio->dev->dev, "spurious interrupt\n"); - - wake_up(&nand_chip->controller->wq); - return IRQ_HANDLED; -} - -/* - *The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. - *This interrupt is normally disabled, but for long operations like - *erase and write, we enable it to wake us up. The irq handler - *disables the interrupt. - */ -static int -tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip) -{ - struct tmio_nand *tmio = mtd_to_tmio(mtd); - long timeout; - u8 status; - - /* enable RDYREQ interrupt */ - tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); - tmio_iowrite8(0x81, tmio->fcr + FCR_IMR); - - timeout = wait_event_timeout(nand_chip->controller->wq, - tmio_nand_dev_ready(mtd), - msecs_to_jiffies(nand_chip->state == FL_ERASING ? 400 : 20)); - - if (unlikely(!tmio_nand_dev_ready(mtd))) { - tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); - dev_warn(&tmio->dev->dev, "still busy with %s after %d ms\n", - nand_chip->state == FL_ERASING ? "erase" : "program", - nand_chip->state == FL_ERASING ? 400 : 20); - - } else if (unlikely(!timeout)) { - tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); - dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n"); - } - - nand_status_op(nand_chip, &status); - return status; -} - -/* - *The TMIO controller combines two 8-bit data bytes into one 16-bit - *word. This function separates them so nand_base.c works as expected, - *especially its NAND_CMD_READID routines. - * - *To prevent stale data from being read, tmio_nand_hwcontrol() clears - *tmio->read_good. - */ -static u_char tmio_nand_read_byte(struct mtd_info *mtd) -{ - struct tmio_nand *tmio = mtd_to_tmio(mtd); - unsigned int data; - - if (tmio->read_good--) - return tmio->read; - - data = tmio_ioread16(tmio->fcr + FCR_DATA); - tmio->read = data >> 8; - return data; -} - -/* - *The TMIO controller converts an 8-bit NAND interface to a 16-bit - *bus interface, so all data reads and writes must be 16-bit wide. - *Thus, we implement 16-bit versions of the read, write, and verify - *buffer functions. - */ -static void -tmio_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct tmio_nand *tmio = mtd_to_tmio(mtd); - - tmio_iowrite16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); -} - -static void tmio_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct tmio_nand *tmio = mtd_to_tmio(mtd); - - tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); -} - -static void tmio_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct tmio_nand *tmio = mtd_to_tmio(mtd); - - tmio_iowrite8(FCR_MODE_HWECC_RESET, tmio->fcr + FCR_MODE); - tmio_ioread8(tmio->fcr + FCR_DATA); /* dummy read */ - tmio_iowrite8(FCR_MODE_HWECC_CALC, tmio->fcr + FCR_MODE); -} - -static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - struct tmio_nand *tmio = mtd_to_tmio(mtd); - unsigned int ecc; - - tmio_iowrite8(FCR_MODE_HWECC_RESULT, tmio->fcr + FCR_MODE); - - ecc = tmio_ioread16(tmio->fcr + FCR_DATA); - ecc_code[1] = ecc; /* 000-255 LP7-0 */ - ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */ - ecc = tmio_ioread16(tmio->fcr + FCR_DATA); - ecc_code[2] = ecc; /* 000-255 CP5-0,11b */ - ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */ - ecc = tmio_ioread16(tmio->fcr + FCR_DATA); - ecc_code[3] = ecc; /* 256-511 LP15-8 */ - ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */ - - tmio_iowrite8(FCR_MODE_DATA, tmio->fcr + FCR_MODE); - return 0; -} - -static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - int r0, r1; - - /* assume ecc.size = 512 and ecc.bytes = 6 */ - r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256); - if (r0 < 0) - return r0; - r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256); - if (r1 < 0) - return r1; - return r0 + r1; -} - -static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - int ret; - - if (cell->enable) { - ret = cell->enable(dev); - if (ret) - return ret; - } - - /* (4Ch) CLKRUN Enable 1st spcrunc */ - tmio_iowrite8(0x81, tmio->ccr + CCR_ICC); - - /* (10h)BaseAddress 0x1000 spba.spba2 */ - tmio_iowrite16(tmio->fcr_base, tmio->ccr + CCR_BASE); - tmio_iowrite16(tmio->fcr_base >> 16, tmio->ccr + CCR_BASE + 2); - - /* (04h)Command Register I/O spcmd */ - tmio_iowrite8(0x02, tmio->ccr + CCR_COMMAND); - - /* (62h) Power Supply Control ssmpwc */ - /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ - tmio_iowrite8(0x02, tmio->ccr + CCR_NFPSC); - - /* (63h) Detect Control ssmdtc */ - tmio_iowrite8(0x02, tmio->ccr + CCR_NFDC); - - /* Interrupt status register clear sintst */ - tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); - - /* After power supply, Media are reset smode */ - tmio_iowrite8(FCR_MODE_POWER_ON, tmio->fcr + FCR_MODE); - tmio_iowrite8(FCR_MODE_COMMAND, tmio->fcr + FCR_MODE); - tmio_iowrite8(NAND_CMD_RESET, tmio->fcr + FCR_DATA); - - /* Standby Mode smode */ - tmio_iowrite8(FCR_MODE_STANDBY, tmio->fcr + FCR_MODE); - - mdelay(5); - - return 0; -} - -static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - - tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); - if (cell->disable) - cell->disable(dev); -} - -static int tmio_probe(struct platform_device *dev) -{ - struct tmio_nand_data *data = dev_get_platdata(&dev->dev); - struct resource *fcr = platform_get_resource(dev, - IORESOURCE_MEM, 0); - struct resource *ccr = platform_get_resource(dev, - IORESOURCE_MEM, 1); - int irq = platform_get_irq(dev, 0); - struct tmio_nand *tmio; - struct mtd_info *mtd; - struct nand_chip *nand_chip; - int retval; - - if (data == NULL) - dev_warn(&dev->dev, "NULL platform data!\n"); - - tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL); - if (!tmio) - return -ENOMEM; - - tmio->dev = dev; - - platform_set_drvdata(dev, tmio); - nand_chip = &tmio->chip; - mtd = nand_to_mtd(nand_chip); - mtd->name = "tmio-nand"; - mtd->dev.parent = &dev->dev; - - tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr)); - if (!tmio->ccr) - return -EIO; - - tmio->fcr_base = fcr->start & 0xfffff; - tmio->fcr = devm_ioremap(&dev->dev, fcr->start, resource_size(fcr)); - if (!tmio->fcr) - return -EIO; - - retval = tmio_hw_init(dev, tmio); - if (retval) - return retval; - - /* Set address of NAND IO lines */ - nand_chip->IO_ADDR_R = tmio->fcr; - nand_chip->IO_ADDR_W = tmio->fcr; - - /* Set address of hardware control function */ - nand_chip->cmd_ctrl = tmio_nand_hwcontrol; - nand_chip->dev_ready = tmio_nand_dev_ready; - nand_chip->read_byte = tmio_nand_read_byte; - nand_chip->write_buf = tmio_nand_write_buf; - nand_chip->read_buf = tmio_nand_read_buf; - - /* set eccmode using hardware ECC */ - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - nand_chip->ecc.bytes = 6; - nand_chip->ecc.strength = 2; - nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; - nand_chip->ecc.calculate = tmio_nand_calculate_ecc; - nand_chip->ecc.correct = tmio_nand_correct_data; - - if (data) - nand_chip->badblock_pattern = data->badblock_pattern; - - /* 15 us command delay time */ - nand_chip->chip_delay = 15; - - retval = devm_request_irq(&dev->dev, irq, &tmio_irq, 0, - dev_name(&dev->dev), tmio); - if (retval) { - dev_err(&dev->dev, "request_irq error %d\n", retval); - goto err_irq; - } - - tmio->irq = irq; - nand_chip->waitfunc = tmio_nand_wait; - - /* Scan to find existence of the device */ - retval = nand_scan(mtd, 1); - if (retval) - goto err_irq; - - /* Register the partitions */ - retval = mtd_device_parse_register(mtd, - data ? data->part_parsers : NULL, - NULL, - data ? data->partition : NULL, - data ? data->num_partitions : 0); - if (!retval) - return retval; - - nand_release(mtd); - -err_irq: - tmio_hw_stop(dev, tmio); - return retval; -} - -static int tmio_remove(struct platform_device *dev) -{ - struct tmio_nand *tmio = platform_get_drvdata(dev); - - nand_release(nand_to_mtd(&tmio->chip)); - tmio_hw_stop(dev, tmio); - return 0; -} - -#ifdef CONFIG_PM -static int tmio_suspend(struct platform_device *dev, pm_message_t state) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - - if (cell->suspend) - cell->suspend(dev); - - tmio_hw_stop(dev, platform_get_drvdata(dev)); - return 0; -} - -static int tmio_resume(struct platform_device *dev) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - - /* FIXME - is this required or merely another attack of the broken - * SHARP platform? Looks suspicious. - */ - tmio_hw_init(dev, platform_get_drvdata(dev)); - - if (cell->resume) - cell->resume(dev); - - return 0; -} -#else -#define tmio_suspend NULL -#define tmio_resume NULL -#endif - -static struct platform_driver tmio_driver = { - .driver.name = "tmio-nand", - .driver.owner = THIS_MODULE, - .probe = tmio_probe, - .remove = tmio_remove, - .suspend = tmio_suspend, - .resume = tmio_resume, -}; - -module_platform_driver(tmio_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Ian Molton, Dirk Opfer, Chris Humbert, Dmitry Baryshkov"); -MODULE_DESCRIPTION("NAND flash driver on Toshiba Mobile IO controller"); -MODULE_ALIAS("platform:tmio-nand"); diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c deleted file mode 100644 index b567d212fe7d..000000000000 --- a/drivers/mtd/nand/txx9ndfmc.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * TXx9 NAND flash memory controller driver - * Based on RBTX49xx patch from CELF patch archive. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * (C) Copyright TOSHIBA CORPORATION 2004-2007 - * All Rights Reserved. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* TXX9 NDFMC Registers */ -#define TXX9_NDFDTR 0x00 -#define TXX9_NDFMCR 0x04 -#define TXX9_NDFSR 0x08 -#define TXX9_NDFISR 0x0c -#define TXX9_NDFIMR 0x10 -#define TXX9_NDFSPR 0x14 -#define TXX9_NDFRSTR 0x18 /* not TX4939 */ - -/* NDFMCR : NDFMC Mode Control */ -#define TXX9_NDFMCR_WE 0x80 -#define TXX9_NDFMCR_ECC_ALL 0x60 -#define TXX9_NDFMCR_ECC_RESET 0x60 -#define TXX9_NDFMCR_ECC_READ 0x40 -#define TXX9_NDFMCR_ECC_ON 0x20 -#define TXX9_NDFMCR_ECC_OFF 0x00 -#define TXX9_NDFMCR_CE 0x10 -#define TXX9_NDFMCR_BSPRT 0x04 /* TX4925/TX4926 only */ -#define TXX9_NDFMCR_ALE 0x02 -#define TXX9_NDFMCR_CLE 0x01 -/* TX4939 only */ -#define TXX9_NDFMCR_X16 0x0400 -#define TXX9_NDFMCR_DMAREQ_MASK 0x0300 -#define TXX9_NDFMCR_DMAREQ_NODMA 0x0000 -#define TXX9_NDFMCR_DMAREQ_128 0x0100 -#define TXX9_NDFMCR_DMAREQ_256 0x0200 -#define TXX9_NDFMCR_DMAREQ_512 0x0300 -#define TXX9_NDFMCR_CS_MASK 0x0c -#define TXX9_NDFMCR_CS(ch) ((ch) << 2) - -/* NDFMCR : NDFMC Status */ -#define TXX9_NDFSR_BUSY 0x80 -/* TX4939 only */ -#define TXX9_NDFSR_DMARUN 0x40 - -/* NDFMCR : NDFMC Reset */ -#define TXX9_NDFRSTR_RST 0x01 - -struct txx9ndfmc_priv { - struct platform_device *dev; - struct nand_chip chip; - int cs; - const char *mtdname; -}; - -#define MAX_TXX9NDFMC_DEV 4 -struct txx9ndfmc_drvdata { - struct mtd_info *mtds[MAX_TXX9NDFMC_DEV]; - void __iomem *base; - unsigned char hold; /* in gbusclock */ - unsigned char spw; /* in gbusclock */ - struct nand_hw_control hw_control; -}; - -static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); - return txx9_priv->dev; -} - -static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) -{ - struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); - struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); - - return drvdata->base + (reg << plat->shift); -} - -static u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg) -{ - return __raw_readl(ndregaddr(dev, reg)); -} - -static void txx9ndfmc_write(struct platform_device *dev, - u32 val, unsigned int reg) -{ - __raw_writel(val, ndregaddr(dev, reg)); -} - -static uint8_t txx9ndfmc_read_byte(struct mtd_info *mtd) -{ - struct platform_device *dev = mtd_to_platdev(mtd); - - return txx9ndfmc_read(dev, TXX9_NDFDTR); -} - -static void txx9ndfmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct platform_device *dev = mtd_to_platdev(mtd); - void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); - u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); - - txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR); - while (len--) - __raw_writel(*buf++, ndfdtr); - txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); -} - -static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct platform_device *dev = mtd_to_platdev(mtd); - void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); - - while (len--) - *buf++ = __raw_readl(ndfdtr); -} - -static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); - struct platform_device *dev = txx9_priv->dev; - struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); - - if (ctrl & NAND_CTRL_CHANGE) { - u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); - - mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE); - mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0; - mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0; - /* TXX9_NDFMCR_CE bit is 0:high 1:low */ - mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0; - if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) { - mcr &= ~TXX9_NDFMCR_CS_MASK; - mcr |= TXX9_NDFMCR_CS(txx9_priv->cs); - } - txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); - } - if (cmd != NAND_CMD_NONE) - txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR); - if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) { - /* dummy write to update external latch */ - if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE) - txx9ndfmc_write(dev, 0, TXX9_NDFDTR); - } - mmiowb(); -} - -static int txx9ndfmc_dev_ready(struct mtd_info *mtd) -{ - struct platform_device *dev = mtd_to_platdev(mtd); - - return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY); -} - -static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, - uint8_t *ecc_code) -{ - struct platform_device *dev = mtd_to_platdev(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - int eccbytes; - u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); - - mcr &= ~TXX9_NDFMCR_ECC_ALL; - txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); - txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); - for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) { - ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); - ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); - ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); - ecc_code += 3; - } - txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); - return 0; -} - -static int txx9ndfmc_correct_data(struct mtd_info *mtd, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int eccsize; - int corrected = 0; - int stat; - - for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) { - stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256); - if (stat < 0) - return stat; - corrected += stat; - buf += 256; - read_ecc += 3; - calc_ecc += 3; - } - return corrected; -} - -static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) -{ - struct platform_device *dev = mtd_to_platdev(mtd); - u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); - - mcr &= ~TXX9_NDFMCR_ECC_ALL; - txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR); - txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); - txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR); -} - -static void txx9ndfmc_initialize(struct platform_device *dev) -{ - struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); - struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); - int tmout = 100; - - if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR) - ; /* no NDFRSTR. Write to NDFSPR resets the NDFMC. */ - else { - /* reset NDFMC */ - txx9ndfmc_write(dev, - txx9ndfmc_read(dev, TXX9_NDFRSTR) | - TXX9_NDFRSTR_RST, - TXX9_NDFRSTR); - while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) { - if (--tmout == 0) { - dev_err(&dev->dev, "reset failed.\n"); - break; - } - udelay(1); - } - } - /* setup Hold Time, Strobe Pulse Width */ - txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR); - txx9ndfmc_write(dev, - (plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ? - TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR); -} - -#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ - DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) - -static int txx9ndfmc_nand_scan(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - ret = nand_scan_ident(mtd, 1, NULL); - if (!ret) { - if (mtd->writesize >= 512) { - /* Hardware ECC 6 byte ECC per 512 Byte data */ - chip->ecc.size = 512; - chip->ecc.bytes = 6; - } - ret = nand_scan_tail(mtd); - } - return ret; -} - -static int __init txx9ndfmc_probe(struct platform_device *dev) -{ - struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); - int hold, spw; - int i; - struct txx9ndfmc_drvdata *drvdata; - unsigned long gbusclk = plat->gbus_clock; - struct resource *res; - - drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); - if (!drvdata) - return -ENOMEM; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - drvdata->base = devm_ioremap_resource(&dev->dev, res); - if (IS_ERR(drvdata->base)) - return PTR_ERR(drvdata->base); - - hold = plat->hold ?: 20; /* tDH */ - spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */ - - hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold); - spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw); - if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD) - hold -= 2; /* actual hold time : (HOLD + 2) BUSCLK */ - spw -= 1; /* actual wait time : (SPW + 1) BUSCLK */ - hold = clamp(hold, 1, 15); - drvdata->hold = hold; - spw = clamp(spw, 1, 15); - drvdata->spw = spw; - dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", - (gbusclk + 500000) / 1000000, hold, spw); - - nand_hw_control_init(&drvdata->hw_control); - - platform_set_drvdata(dev, drvdata); - txx9ndfmc_initialize(dev); - - for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { - struct txx9ndfmc_priv *txx9_priv; - struct nand_chip *chip; - struct mtd_info *mtd; - - if (!(plat->ch_mask & (1 << i))) - continue; - txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv), - GFP_KERNEL); - if (!txx9_priv) - continue; - chip = &txx9_priv->chip; - mtd = nand_to_mtd(chip); - mtd->dev.parent = &dev->dev; - - chip->read_byte = txx9ndfmc_read_byte; - chip->read_buf = txx9ndfmc_read_buf; - chip->write_buf = txx9ndfmc_write_buf; - chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; - chip->dev_ready = txx9ndfmc_dev_ready; - chip->ecc.calculate = txx9ndfmc_calculate_ecc; - chip->ecc.correct = txx9ndfmc_correct_data; - chip->ecc.hwctl = txx9ndfmc_enable_hwecc; - chip->ecc.mode = NAND_ECC_HW; - /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ - chip->ecc.size = 256; - chip->ecc.bytes = 3; - chip->ecc.strength = 1; - chip->chip_delay = 100; - chip->controller = &drvdata->hw_control; - - nand_set_controller_data(chip, txx9_priv); - txx9_priv->dev = dev; - - if (plat->ch_mask != 1) { - txx9_priv->cs = i; - txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u", - dev_name(&dev->dev), i); - } else { - txx9_priv->cs = -1; - txx9_priv->mtdname = kstrdup(dev_name(&dev->dev), - GFP_KERNEL); - } - if (!txx9_priv->mtdname) { - kfree(txx9_priv); - dev_err(&dev->dev, "Unable to allocate MTD name.\n"); - continue; - } - if (plat->wide_mask & (1 << i)) - chip->options |= NAND_BUSWIDTH_16; - - if (txx9ndfmc_nand_scan(mtd)) { - kfree(txx9_priv->mtdname); - kfree(txx9_priv); - continue; - } - mtd->name = txx9_priv->mtdname; - - mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); - drvdata->mtds[i] = mtd; - } - - return 0; -} - -static int __exit txx9ndfmc_remove(struct platform_device *dev) -{ - struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); - int i; - - if (!drvdata) - return 0; - for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { - struct mtd_info *mtd = drvdata->mtds[i]; - struct nand_chip *chip; - struct txx9ndfmc_priv *txx9_priv; - - if (!mtd) - continue; - chip = mtd_to_nand(mtd); - txx9_priv = nand_get_controller_data(chip); - - nand_release(mtd); - kfree(txx9_priv->mtdname); - kfree(txx9_priv); - } - return 0; -} - -#ifdef CONFIG_PM -static int txx9ndfmc_resume(struct platform_device *dev) -{ - if (platform_get_drvdata(dev)) - txx9ndfmc_initialize(dev); - return 0; -} -#else -#define txx9ndfmc_resume NULL -#endif - -static struct platform_driver txx9ndfmc_driver = { - .remove = __exit_p(txx9ndfmc_remove), - .resume = txx9ndfmc_resume, - .driver = { - .name = "txx9ndfmc", - }, -}; - -module_platform_driver_probe(txx9ndfmc_driver, txx9ndfmc_probe); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); -MODULE_ALIAS("platform:txx9ndfmc"); diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c deleted file mode 100644 index 5d7a1f8f580f..000000000000 --- a/drivers/mtd/nand/vf610_nfc.c +++ /dev/null @@ -1,835 +0,0 @@ -/* - * Copyright 2009-2015 Freescale Semiconductor, Inc. and others - * - * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver. - * Jason ported to M54418TWR and MVFA5 (VF610). - * Authors: Stefan Agner - * Bill Pringlemeir - * Shaohui Xie - * Jason Jin - * - * Based on original driver mpc5121_nfc.c. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Limitations: - * - Untested on MPC5125 and M54418. - * - DMA and pipelining not used. - * - 2K pages or less. - * - HW ECC: Only 2K page with 64+ OOB. - * - HW ECC: Only 24 and 32-bit error correction implemented. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "vf610_nfc" - -/* Register Offsets */ -#define NFC_FLASH_CMD1 0x3F00 -#define NFC_FLASH_CMD2 0x3F04 -#define NFC_COL_ADDR 0x3F08 -#define NFC_ROW_ADDR 0x3F0c -#define NFC_ROW_ADDR_INC 0x3F14 -#define NFC_FLASH_STATUS1 0x3F18 -#define NFC_FLASH_STATUS2 0x3F1c -#define NFC_CACHE_SWAP 0x3F28 -#define NFC_SECTOR_SIZE 0x3F2c -#define NFC_FLASH_CONFIG 0x3F30 -#define NFC_IRQ_STATUS 0x3F38 - -/* Addresses for NFC MAIN RAM BUFFER areas */ -#define NFC_MAIN_AREA(n) ((n) * 0x1000) - -#define PAGE_2K 0x0800 -#define OOB_64 0x0040 -#define OOB_MAX 0x0100 - -/* - * NFC_CMD2[CODE] values. See section: - * - 31.4.7 Flash Command Code Description, Vybrid manual - * - 23.8.6 Flash Command Sequencer, MPC5125 manual - * - * Briefly these are bitmasks of controller cycles. - */ -#define READ_PAGE_CMD_CODE 0x7EE0 -#define READ_ONFI_PARAM_CMD_CODE 0x4860 -#define PROGRAM_PAGE_CMD_CODE 0x7FC0 -#define ERASE_CMD_CODE 0x4EC0 -#define READ_ID_CMD_CODE 0x4804 -#define RESET_CMD_CODE 0x4040 -#define STATUS_READ_CMD_CODE 0x4068 - -/* NFC ECC mode define */ -#define ECC_BYPASS 0 -#define ECC_45_BYTE 6 -#define ECC_60_BYTE 7 - -/*** Register Mask and bit definitions */ - -/* NFC_FLASH_CMD1 Field */ -#define CMD_BYTE2_MASK 0xFF000000 -#define CMD_BYTE2_SHIFT 24 - -/* NFC_FLASH_CM2 Field */ -#define CMD_BYTE1_MASK 0xFF000000 -#define CMD_BYTE1_SHIFT 24 -#define CMD_CODE_MASK 0x00FFFF00 -#define CMD_CODE_SHIFT 8 -#define BUFNO_MASK 0x00000006 -#define BUFNO_SHIFT 1 -#define START_BIT BIT(0) - -/* NFC_COL_ADDR Field */ -#define COL_ADDR_MASK 0x0000FFFF -#define COL_ADDR_SHIFT 0 - -/* NFC_ROW_ADDR Field */ -#define ROW_ADDR_MASK 0x00FFFFFF -#define ROW_ADDR_SHIFT 0 -#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000 -#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28 -#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000 -#define ROW_ADDR_CHIP_SEL_SHIFT 24 - -/* NFC_FLASH_STATUS2 Field */ -#define STATUS_BYTE1_MASK 0x000000FF - -/* NFC_FLASH_CONFIG Field */ -#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000 -#define CONFIG_ECC_SRAM_ADDR_SHIFT 22 -#define CONFIG_ECC_SRAM_REQ_BIT BIT(21) -#define CONFIG_DMA_REQ_BIT BIT(20) -#define CONFIG_ECC_MODE_MASK 0x000E0000 -#define CONFIG_ECC_MODE_SHIFT 17 -#define CONFIG_FAST_FLASH_BIT BIT(16) -#define CONFIG_16BIT BIT(7) -#define CONFIG_BOOT_MODE_BIT BIT(6) -#define CONFIG_ADDR_AUTO_INCR_BIT BIT(5) -#define CONFIG_BUFNO_AUTO_INCR_BIT BIT(4) -#define CONFIG_PAGE_CNT_MASK 0xF -#define CONFIG_PAGE_CNT_SHIFT 0 - -/* NFC_IRQ_STATUS Field */ -#define IDLE_IRQ_BIT BIT(29) -#define IDLE_EN_BIT BIT(20) -#define CMD_DONE_CLEAR_BIT BIT(18) -#define IDLE_CLEAR_BIT BIT(17) - -/* - * ECC status - seems to consume 8 bytes (double word). The documented - * status byte is located in the lowest byte of the second word (which is - * the 4th or 7th byte depending on endianness). - * Calculate an offset to store the ECC status at the end of the buffer. - */ -#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8) - -#define ECC_STATUS 0x4 -#define ECC_STATUS_MASK 0x80 -#define ECC_STATUS_ERR_COUNT 0x3F - -enum vf610_nfc_alt_buf { - ALT_BUF_DATA = 0, - ALT_BUF_ID = 1, - ALT_BUF_STAT = 2, - ALT_BUF_ONFI = 3, -}; - -enum vf610_nfc_variant { - NFC_VFC610 = 1, -}; - -struct vf610_nfc { - struct nand_chip chip; - struct device *dev; - void __iomem *regs; - struct completion cmd_done; - uint buf_offset; - int write_sz; - /* Status and ID are in alternate locations. */ - enum vf610_nfc_alt_buf alt_buf; - enum vf610_nfc_variant variant; - struct clk *clk; - bool use_hw_ecc; - u32 ecc_mode; -}; - -static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip); -} - -static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg) -{ - return readl(nfc->regs + reg); -} - -static inline void vf610_nfc_write(struct vf610_nfc *nfc, uint reg, u32 val) -{ - writel(val, nfc->regs + reg); -} - -static inline void vf610_nfc_set(struct vf610_nfc *nfc, uint reg, u32 bits) -{ - vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) | bits); -} - -static inline void vf610_nfc_clear(struct vf610_nfc *nfc, uint reg, u32 bits) -{ - vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) & ~bits); -} - -static inline void vf610_nfc_set_field(struct vf610_nfc *nfc, u32 reg, - u32 mask, u32 shift, u32 val) -{ - vf610_nfc_write(nfc, reg, - (vf610_nfc_read(nfc, reg) & (~mask)) | val << shift); -} - -static inline void vf610_nfc_memcpy(void *dst, const void __iomem *src, - size_t n) -{ - /* - * Use this accessor for the internal SRAM buffers. On the ARM - * Freescale Vybrid SoC it's known that the driver can treat - * the SRAM buffer as if it's memory. Other platform might need - * to treat the buffers differently. - * - * For the time being, use memcpy - */ - memcpy(dst, src, n); -} - -/* Clear flags for upcoming command */ -static inline void vf610_nfc_clear_status(struct vf610_nfc *nfc) -{ - u32 tmp = vf610_nfc_read(nfc, NFC_IRQ_STATUS); - - tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT; - vf610_nfc_write(nfc, NFC_IRQ_STATUS, tmp); -} - -static void vf610_nfc_done(struct vf610_nfc *nfc) -{ - unsigned long timeout = msecs_to_jiffies(100); - - /* - * Barrier is needed after this write. This write need - * to be done before reading the next register the first - * time. - * vf610_nfc_set implicates such a barrier by using writel - * to write to the register. - */ - vf610_nfc_set(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); - vf610_nfc_set(nfc, NFC_FLASH_CMD2, START_BIT); - - if (!wait_for_completion_timeout(&nfc->cmd_done, timeout)) - dev_warn(nfc->dev, "Timeout while waiting for BUSY.\n"); - - vf610_nfc_clear_status(nfc); -} - -static u8 vf610_nfc_get_id(struct vf610_nfc *nfc, int col) -{ - u32 flash_id; - - if (col < 4) { - flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS1); - flash_id >>= (3 - col) * 8; - } else { - flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS2); - flash_id >>= 24; - } - - return flash_id & 0xff; -} - -static u8 vf610_nfc_get_status(struct vf610_nfc *nfc) -{ - return vf610_nfc_read(nfc, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK; -} - -static void vf610_nfc_send_command(struct vf610_nfc *nfc, u32 cmd_byte1, - u32 cmd_code) -{ - u32 tmp; - - vf610_nfc_clear_status(nfc); - - tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD2); - tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK); - tmp |= cmd_byte1 << CMD_BYTE1_SHIFT; - tmp |= cmd_code << CMD_CODE_SHIFT; - vf610_nfc_write(nfc, NFC_FLASH_CMD2, tmp); -} - -static void vf610_nfc_send_commands(struct vf610_nfc *nfc, u32 cmd_byte1, - u32 cmd_byte2, u32 cmd_code) -{ - u32 tmp; - - vf610_nfc_send_command(nfc, cmd_byte1, cmd_code); - - tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD1); - tmp &= ~CMD_BYTE2_MASK; - tmp |= cmd_byte2 << CMD_BYTE2_SHIFT; - vf610_nfc_write(nfc, NFC_FLASH_CMD1, tmp); -} - -static irqreturn_t vf610_nfc_irq(int irq, void *data) -{ - struct mtd_info *mtd = data; - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - - vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); - complete(&nfc->cmd_done); - - return IRQ_HANDLED; -} - -static void vf610_nfc_addr_cycle(struct vf610_nfc *nfc, int column, int page) -{ - if (column != -1) { - if (nfc->chip.options & NAND_BUSWIDTH_16) - column = column / 2; - vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK, - COL_ADDR_SHIFT, column); - } - if (page != -1) - vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK, - ROW_ADDR_SHIFT, page); -} - -static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode) -{ - vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ecc_mode); -} - -static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size) -{ - vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size); -} - -static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, - int column, int page) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0; - - nfc->buf_offset = max(column, 0); - nfc->alt_buf = ALT_BUF_DATA; - - switch (command) { - case NAND_CMD_SEQIN: - /* Use valid column/page from preread... */ - vf610_nfc_addr_cycle(nfc, column, page); - nfc->buf_offset = 0; - - /* - * SEQIN => data => PAGEPROG sequence is done by the controller - * hence we do not need to issue the command here... - */ - return; - case NAND_CMD_PAGEPROG: - trfr_sz += nfc->write_sz; - vf610_nfc_transfer_size(nfc, trfr_sz); - vf610_nfc_send_commands(nfc, NAND_CMD_SEQIN, - command, PROGRAM_PAGE_CMD_CODE); - if (nfc->use_hw_ecc) - vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); - else - vf610_nfc_ecc_mode(nfc, ECC_BYPASS); - break; - - case NAND_CMD_RESET: - vf610_nfc_transfer_size(nfc, 0); - vf610_nfc_send_command(nfc, command, RESET_CMD_CODE); - break; - - case NAND_CMD_READOOB: - trfr_sz += mtd->oobsize; - column = mtd->writesize; - vf610_nfc_transfer_size(nfc, trfr_sz); - vf610_nfc_send_commands(nfc, NAND_CMD_READ0, - NAND_CMD_READSTART, READ_PAGE_CMD_CODE); - vf610_nfc_addr_cycle(nfc, column, page); - vf610_nfc_ecc_mode(nfc, ECC_BYPASS); - break; - - case NAND_CMD_READ0: - trfr_sz += mtd->writesize + mtd->oobsize; - vf610_nfc_transfer_size(nfc, trfr_sz); - vf610_nfc_send_commands(nfc, NAND_CMD_READ0, - NAND_CMD_READSTART, READ_PAGE_CMD_CODE); - vf610_nfc_addr_cycle(nfc, column, page); - vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); - break; - - case NAND_CMD_PARAM: - nfc->alt_buf = ALT_BUF_ONFI; - trfr_sz = 3 * sizeof(struct nand_onfi_params); - vf610_nfc_transfer_size(nfc, trfr_sz); - vf610_nfc_send_command(nfc, command, READ_ONFI_PARAM_CMD_CODE); - vf610_nfc_addr_cycle(nfc, -1, column); - vf610_nfc_ecc_mode(nfc, ECC_BYPASS); - break; - - case NAND_CMD_ERASE1: - vf610_nfc_transfer_size(nfc, 0); - vf610_nfc_send_commands(nfc, command, - NAND_CMD_ERASE2, ERASE_CMD_CODE); - vf610_nfc_addr_cycle(nfc, column, page); - break; - - case NAND_CMD_READID: - nfc->alt_buf = ALT_BUF_ID; - nfc->buf_offset = 0; - vf610_nfc_transfer_size(nfc, 0); - vf610_nfc_send_command(nfc, command, READ_ID_CMD_CODE); - vf610_nfc_addr_cycle(nfc, -1, column); - break; - - case NAND_CMD_STATUS: - nfc->alt_buf = ALT_BUF_STAT; - vf610_nfc_transfer_size(nfc, 0); - vf610_nfc_send_command(nfc, command, STATUS_READ_CMD_CODE); - break; - default: - return; - } - - vf610_nfc_done(nfc); - - nfc->use_hw_ecc = false; - nfc->write_sz = 0; -} - -static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - uint c = nfc->buf_offset; - - /* Alternate buffers are only supported through read_byte */ - WARN_ON(nfc->alt_buf); - - vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len); - - nfc->buf_offset += len; -} - -static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - uint c = nfc->buf_offset; - uint l; - - l = min_t(uint, len, mtd->writesize + mtd->oobsize - c); - vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l); - - nfc->write_sz += l; - nfc->buf_offset += l; -} - -static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - u8 tmp; - uint c = nfc->buf_offset; - - switch (nfc->alt_buf) { - case ALT_BUF_ID: - tmp = vf610_nfc_get_id(nfc, c); - break; - case ALT_BUF_STAT: - tmp = vf610_nfc_get_status(nfc); - break; -#ifdef __LITTLE_ENDIAN - case ALT_BUF_ONFI: - /* Reverse byte since the controller uses big endianness */ - c = nfc->buf_offset ^ 0x3; - /* fall-through */ -#endif - default: - tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c)); - break; - } - nfc->buf_offset++; - return tmp; -} - -static u16 vf610_nfc_read_word(struct mtd_info *mtd) -{ - u16 tmp; - - vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); - return tmp; -} - -/* If not provided, upper layers apply a fixed delay. */ -static int vf610_nfc_dev_ready(struct mtd_info *mtd) -{ - /* NFC handles R/B internally; always ready. */ - return 1; -} - -/* - * This function supports Vybrid only (MPC5125 would have full RB and four CS) - */ -static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - u32 tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR); - - /* Vybrid only (MPC5125 would have full RB and four CS) */ - if (nfc->variant != NFC_VFC610) - return; - - tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK); - - if (chip >= 0) { - tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; - tmp |= BIT(chip) << ROW_ADDR_CHIP_SEL_SHIFT; - } - - vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp); -} - -static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, - uint8_t *oob, int page) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS; - u8 ecc_status; - u8 ecc_count; - int flips_threshold = nfc->chip.ecc.strength / 2; - - ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff; - ecc_count = ecc_status & ECC_STATUS_ERR_COUNT; - - if (!(ecc_status & ECC_STATUS_MASK)) - return ecc_count; - - /* Read OOB without ECC unit enabled */ - vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page); - vf610_nfc_read_buf(mtd, oob, mtd->oobsize); - - /* - * On an erased page, bit count (including OOB) should be zero or - * at least less then half of the ECC strength. - */ - return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob, - mtd->oobsize, NULL, 0, - flips_threshold); -} - -static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int eccsize = chip->ecc.size; - int stat; - - nand_read_page_op(chip, page, 0, buf, eccsize); - if (oob_required) - vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page); - - if (stat < 0) { - mtd->ecc_stats.failed++; - return 0; - } else { - mtd->ecc_stats.corrected += stat; - return stat; - } -} - -static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - if (oob_required) - vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - /* Always write whole page including OOB due to HW ECC */ - nfc->use_hw_ecc = true; - nfc->write_sz = mtd->writesize + mtd->oobsize; - - return nand_prog_page_end_op(chip); -} - -static const struct of_device_id vf610_nfc_dt_ids[] = { - { .compatible = "fsl,vf610-nfc", .data = (void *)NFC_VFC610 }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, vf610_nfc_dt_ids); - -static void vf610_nfc_preinit_controller(struct vf610_nfc *nfc) -{ - vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); - vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); - vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT); - vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT); - vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT); - vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT); - - /* Disable virtual pages, only one elementary transfer unit */ - vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, - CONFIG_PAGE_CNT_SHIFT, 1); -} - -static void vf610_nfc_init_controller(struct vf610_nfc *nfc) -{ - if (nfc->chip.options & NAND_BUSWIDTH_16) - vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); - else - vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); - - if (nfc->chip.ecc.mode == NAND_ECC_HW) { - /* Set ECC status offset in SRAM */ - vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, - CONFIG_ECC_SRAM_ADDR_MASK, - CONFIG_ECC_SRAM_ADDR_SHIFT, - ECC_SRAM_ADDR >> 3); - - /* Enable ECC status in SRAM */ - vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT); - } -} - -static int vf610_nfc_probe(struct platform_device *pdev) -{ - struct vf610_nfc *nfc; - struct resource *res; - struct mtd_info *mtd; - struct nand_chip *chip; - struct device_node *child; - const struct of_device_id *of_id; - int err; - int irq; - - nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); - if (!nfc) - return -ENOMEM; - - nfc->dev = &pdev->dev; - chip = &nfc->chip; - mtd = nand_to_mtd(chip); - - mtd->owner = THIS_MODULE; - mtd->dev.parent = nfc->dev; - mtd->name = DRV_NAME; - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -EINVAL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nfc->regs = devm_ioremap_resource(nfc->dev, res); - if (IS_ERR(nfc->regs)) - return PTR_ERR(nfc->regs); - - nfc->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(nfc->clk)) - return PTR_ERR(nfc->clk); - - err = clk_prepare_enable(nfc->clk); - if (err) { - dev_err(nfc->dev, "Unable to enable clock!\n"); - return err; - } - - of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev); - nfc->variant = (enum vf610_nfc_variant)of_id->data; - - for_each_available_child_of_node(nfc->dev->of_node, child) { - if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) { - - if (nand_get_flash_node(chip)) { - dev_err(nfc->dev, - "Only one NAND chip supported!\n"); - err = -EINVAL; - goto err_disable_clk; - } - - nand_set_flash_node(chip, child); - } - } - - if (!nand_get_flash_node(chip)) { - dev_err(nfc->dev, "NAND chip sub-node missing!\n"); - err = -ENODEV; - goto err_disable_clk; - } - - chip->dev_ready = vf610_nfc_dev_ready; - chip->cmdfunc = vf610_nfc_command; - chip->read_byte = vf610_nfc_read_byte; - chip->read_word = vf610_nfc_read_word; - chip->read_buf = vf610_nfc_read_buf; - chip->write_buf = vf610_nfc_write_buf; - chip->select_chip = vf610_nfc_select_chip; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; - - chip->options |= NAND_NO_SUBPAGE_WRITE; - - init_completion(&nfc->cmd_done); - - err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd); - if (err) { - dev_err(nfc->dev, "Error requesting IRQ!\n"); - goto err_disable_clk; - } - - vf610_nfc_preinit_controller(nfc); - - /* first scan to find the device and get the page size */ - err = nand_scan_ident(mtd, 1, NULL); - if (err) - goto err_disable_clk; - - vf610_nfc_init_controller(nfc); - - /* Bad block options. */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) - chip->bbt_options |= NAND_BBT_NO_OOB; - - /* Single buffer only, max 256 OOB minus ECC status */ - if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { - dev_err(nfc->dev, "Unsupported flash page size\n"); - err = -ENXIO; - goto err_disable_clk; - } - - if (chip->ecc.mode == NAND_ECC_HW) { - if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { - dev_err(nfc->dev, "Unsupported flash with hwecc\n"); - err = -ENXIO; - goto err_disable_clk; - } - - if (chip->ecc.size != mtd->writesize) { - dev_err(nfc->dev, "Step size needs to be page size\n"); - err = -ENXIO; - goto err_disable_clk; - } - - /* Only 64 byte ECC layouts known */ - if (mtd->oobsize > 64) - mtd->oobsize = 64; - - /* - * mtd->ecclayout is not specified here because we're using the - * default large page ECC layout defined in NAND core. - */ - if (chip->ecc.strength == 32) { - nfc->ecc_mode = ECC_60_BYTE; - chip->ecc.bytes = 60; - } else if (chip->ecc.strength == 24) { - nfc->ecc_mode = ECC_45_BYTE; - chip->ecc.bytes = 45; - } else { - dev_err(nfc->dev, "Unsupported ECC strength\n"); - err = -ENXIO; - goto err_disable_clk; - } - - chip->ecc.read_page = vf610_nfc_read_page; - chip->ecc.write_page = vf610_nfc_write_page; - - chip->ecc.size = PAGE_2K; - } - - /* second phase scan */ - err = nand_scan_tail(mtd); - if (err) - goto err_disable_clk; - - platform_set_drvdata(pdev, mtd); - - /* Register device in MTD */ - err = mtd_device_register(mtd, NULL, 0); - if (err) - goto err_cleanup_nand; - return 0; - -err_cleanup_nand: - nand_cleanup(chip); -err_disable_clk: - clk_disable_unprepare(nfc->clk); - return err; -} - -static int vf610_nfc_remove(struct platform_device *pdev) -{ - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - - nand_release(mtd); - clk_disable_unprepare(nfc->clk); - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int vf610_nfc_suspend(struct device *dev) -{ - struct mtd_info *mtd = dev_get_drvdata(dev); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - - clk_disable_unprepare(nfc->clk); - return 0; -} - -static int vf610_nfc_resume(struct device *dev) -{ - int err; - - struct mtd_info *mtd = dev_get_drvdata(dev); - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - - err = clk_prepare_enable(nfc->clk); - if (err) - return err; - - vf610_nfc_preinit_controller(nfc); - vf610_nfc_init_controller(nfc); - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(vf610_nfc_pm_ops, vf610_nfc_suspend, vf610_nfc_resume); - -static struct platform_driver vf610_nfc_driver = { - .driver = { - .name = DRV_NAME, - .of_match_table = vf610_nfc_dt_ids, - .pm = &vf610_nfc_pm_ops, - }, - .probe = vf610_nfc_probe, - .remove = vf610_nfc_remove, -}; - -module_platform_driver(vf610_nfc_driver); - -MODULE_AUTHOR("Stefan Agner "); -MODULE_DESCRIPTION("Freescale VF610/MPC5125 NFC MTD NAND driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c deleted file mode 100644 index 9926b4e3d69d..000000000000 --- a/drivers/mtd/nand/xway_nand.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * Copyright © 2012 John Crispin - * Copyright © 2016 Hauke Mehrtens - */ - -#include -#include -#include - -#include - -/* nand registers */ -#define EBU_ADDSEL1 0x24 -#define EBU_NAND_CON 0xB0 -#define EBU_NAND_WAIT 0xB4 -#define NAND_WAIT_RD BIT(0) /* NAND flash status output */ -#define NAND_WAIT_WR_C BIT(3) /* NAND Write/Read complete */ -#define EBU_NAND_ECC0 0xB8 -#define EBU_NAND_ECC_AC 0xBC - -/* - * nand commands - * The pins of the NAND chip are selected based on the address bits of the - * "register" read and write. There are no special registers, but an - * address range and the lower address bits are used to activate the - * correct line. For example when the bit (1 << 2) is set in the address - * the ALE pin will be activated. - */ -#define NAND_CMD_ALE BIT(2) /* address latch enable */ -#define NAND_CMD_CLE BIT(3) /* command latch enable */ -#define NAND_CMD_CS BIT(4) /* chip select */ -#define NAND_CMD_SE BIT(5) /* spare area access latch */ -#define NAND_CMD_WP BIT(6) /* write protect */ -#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE) -#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE) -#define NAND_WRITE_DATA (NAND_CMD_CS) -#define NAND_READ_DATA (NAND_CMD_CS) - -/* we need to tel the ebu which addr we mapped the nand to */ -#define ADDSEL1_MASK(x) (x << 4) -#define ADDSEL1_REGEN 1 - -/* we need to tell the EBU that we have nand attached and set it up properly */ -#define BUSCON1_SETUP (1 << 22) -#define BUSCON1_BCGEN_RES (0x3 << 12) -#define BUSCON1_WAITWRC2 (2 << 8) -#define BUSCON1_WAITRDC2 (2 << 6) -#define BUSCON1_HOLDC1 (1 << 4) -#define BUSCON1_RECOVC1 (1 << 2) -#define BUSCON1_CMULT4 1 - -#define NAND_CON_CE (1 << 20) -#define NAND_CON_OUT_CS1 (1 << 10) -#define NAND_CON_IN_CS1 (1 << 8) -#define NAND_CON_PRE_P (1 << 7) -#define NAND_CON_WP_P (1 << 6) -#define NAND_CON_SE_P (1 << 5) -#define NAND_CON_CS_P (1 << 4) -#define NAND_CON_CSMUX (1 << 1) -#define NAND_CON_NANDM 1 - -struct xway_nand_data { - struct nand_chip chip; - unsigned long csflags; - void __iomem *nandaddr; -}; - -static u8 xway_readb(struct mtd_info *mtd, int op) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct xway_nand_data *data = nand_get_controller_data(chip); - - return readb(data->nandaddr + op); -} - -static void xway_writeb(struct mtd_info *mtd, int op, u8 value) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct xway_nand_data *data = nand_get_controller_data(chip); - - writeb(value, data->nandaddr + op); -} - -static void xway_select_chip(struct mtd_info *mtd, int select) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct xway_nand_data *data = nand_get_controller_data(chip); - - switch (select) { - case -1: - ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON); - ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON); - spin_unlock_irqrestore(&ebu_lock, data->csflags); - break; - case 0: - spin_lock_irqsave(&ebu_lock, data->csflags); - ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON); - ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON); - break; - default: - BUG(); - } -} - -static void xway_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - xway_writeb(mtd, NAND_WRITE_CMD, cmd); - else if (ctrl & NAND_ALE) - xway_writeb(mtd, NAND_WRITE_ADDR, cmd); - - while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) - ; -} - -static int xway_dev_ready(struct mtd_info *mtd) -{ - return ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_RD; -} - -static unsigned char xway_read_byte(struct mtd_info *mtd) -{ - return xway_readb(mtd, NAND_READ_DATA); -} - -static void xway_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - buf[i] = xway_readb(mtd, NAND_WRITE_DATA); -} - -static void xway_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - xway_writeb(mtd, NAND_WRITE_DATA, buf[i]); -} - -/* - * Probe for the NAND device. - */ -static int xway_nand_probe(struct platform_device *pdev) -{ - struct xway_nand_data *data; - struct mtd_info *mtd; - struct resource *res; - int err; - u32 cs; - u32 cs_flag = 0; - - /* Allocate memory for the device structure (and zero it) */ - data = devm_kzalloc(&pdev->dev, sizeof(struct xway_nand_data), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->nandaddr = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(data->nandaddr)) - return PTR_ERR(data->nandaddr); - - nand_set_flash_node(&data->chip, pdev->dev.of_node); - mtd = nand_to_mtd(&data->chip); - mtd->dev.parent = &pdev->dev; - - data->chip.cmd_ctrl = xway_cmd_ctrl; - data->chip.dev_ready = xway_dev_ready; - data->chip.select_chip = xway_select_chip; - data->chip.write_buf = xway_write_buf; - data->chip.read_buf = xway_read_buf; - data->chip.read_byte = xway_read_byte; - data->chip.chip_delay = 30; - - data->chip.ecc.mode = NAND_ECC_SOFT; - data->chip.ecc.algo = NAND_ECC_HAMMING; - - platform_set_drvdata(pdev, data); - nand_set_controller_data(&data->chip, data); - - /* load our CS from the DT. Either we find a valid 1 or default to 0 */ - err = of_property_read_u32(pdev->dev.of_node, "lantiq,cs", &cs); - if (!err && cs == 1) - cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1; - - /* setup the EBU to run in NAND mode on our base addr */ - ltq_ebu_w32(CPHYSADDR(data->nandaddr) - | ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1); - - ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2 - | BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1 - | BUSCON1_CMULT4, LTQ_EBU_BUSCON1); - - ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P - | NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P - | cs_flag, EBU_NAND_CON); - - /* Scan to find existence of the device */ - err = nand_scan(mtd, 1); - if (err) - return err; - - err = mtd_device_register(mtd, NULL, 0); - if (err) - nand_release(mtd); - - return err; -} - -/* - * Remove a NAND device. - */ -static int xway_nand_remove(struct platform_device *pdev) -{ - struct xway_nand_data *data = platform_get_drvdata(pdev); - - nand_release(nand_to_mtd(&data->chip)); - - return 0; -} - -static const struct of_device_id xway_nand_match[] = { - { .compatible = "lantiq,nand-xway" }, - {}, -}; - -static struct platform_driver xway_nand_driver = { - .probe = xway_nand_probe, - .remove = xway_nand_remove, - .driver = { - .name = "lantiq,nand-xway", - .of_match_table = xway_nand_match, - }, -}; - -builtin_platform_driver(xway_nand_driver); diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 4237c7cebf02..fa176f5937bb 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -17,7 +17,7 @@ #include #include #include -#include "nand/sm_common.h" +#include "nand/raw/sm_common.h" #include "sm_ftl.h" -- cgit v1.3-14-g43fede From 9c3736a3de21d916a6af0594418b85a112f4bef6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 5 Feb 2018 23:02:05 +0100 Subject: mtd: nand: Add core infrastructure to deal with NAND devices Add an intermediate layer to abstract NAND device interface so that some logic can be shared between SPI NANDs, parallel/raw NANDs, OneNANDs, ... Signed-off-by: Boris Brezillon --- drivers/mtd/nand/Kconfig | 3 + drivers/mtd/nand/Makefile | 3 + drivers/mtd/nand/bbt.c | 130 +++++++++ drivers/mtd/nand/core.c | 244 ++++++++++++++++ include/linux/mtd/nand.h | 731 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1111 insertions(+) create mode 100644 drivers/mtd/nand/bbt.c create mode 100644 drivers/mtd/nand/core.c create mode 100644 include/linux/mtd/nand.h diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 6d5373471809..1c1a1f487e20 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1 +1,4 @@ +config MTD_NAND_CORE + tristate + source "drivers/mtd/nand/raw/Kconfig" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 32af7168c5ba..a72d3cb0f325 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,3 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +nandcore-objs := core.o bbt.o +obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o + obj-y += raw/ diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c new file mode 100644 index 000000000000..56cde38b92c0 --- /dev/null +++ b/drivers/mtd/nand/bbt.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#define pr_fmt(fmt) "nand-bbt: " fmt + +#include +#include + +/** + * nanddev_bbt_init() - Initialize the BBT (Bad Block Table) + * @nand: NAND device + * + * Initialize the in-memory BBT. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_bbt_init(struct nand_device *nand) +{ + unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); + unsigned int nblocks = nanddev_neraseblocks(nand); + unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block, + BITS_PER_LONG); + + nand->bbt.cache = kzalloc(nwords, GFP_KERNEL); + if (!nand->bbt.cache) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_bbt_init); + +/** + * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table) + * @nand: NAND device + * + * Undoes what has been done in nanddev_bbt_init() + */ +void nanddev_bbt_cleanup(struct nand_device *nand) +{ + kfree(nand->bbt.cache); +} +EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup); + +/** + * nanddev_bbt_update() - Update a BBT + * @nand: nand device + * + * Update the BBT. Currently a NOP function since on-flash bbt is not yet + * supported. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_bbt_update(struct nand_device *nand) +{ + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_bbt_update); + +/** + * nanddev_bbt_get_block_status() - Return the status of an eraseblock + * @nand: nand device + * @entry: the BBT entry + * + * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry + * is bigger than the BBT size. + */ +int nanddev_bbt_get_block_status(const struct nand_device *nand, + unsigned int entry) +{ + unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); + unsigned long *pos = nand->bbt.cache + + ((entry * bits_per_block) / BITS_PER_LONG); + unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; + unsigned long status; + + if (entry >= nanddev_neraseblocks(nand)) + return -ERANGE; + + status = pos[0] >> offs; + if (bits_per_block + offs > BITS_PER_LONG) + status |= pos[1] << (BITS_PER_LONG - offs); + + return status & GENMASK(bits_per_block - 1, 0); +} +EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status); + +/** + * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the + * in-memory BBT + * @nand: nand device + * @entry: the BBT entry to update + * @status: the new status + * + * Update an entry of the in-memory BBT. If you want to push the updated BBT + * the NAND you should call nanddev_bbt_update(). + * + * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT + * size. + */ +int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, + enum nand_bbt_block_status status) +{ + unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); + unsigned long *pos = nand->bbt.cache + + ((entry * bits_per_block) / BITS_PER_LONG); + unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; + unsigned long val = status & GENMASK(bits_per_block - 1, 0); + + if (entry >= nanddev_neraseblocks(nand)) + return -ERANGE; + + pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs); + pos[0] |= val << offs; + + if (bits_per_block + offs > BITS_PER_LONG) { + unsigned int rbits = bits_per_block + offs - BITS_PER_LONG; + + pos[1] &= ~GENMASK(rbits - 1, 0); + pos[1] |= val >> rbits; + } + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status); diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c new file mode 100644 index 000000000000..f237a688f8e9 --- /dev/null +++ b/drivers/mtd/nand/core.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#define pr_fmt(fmt) "nand: " fmt + +#include +#include + +/** + * nanddev_isbad() - Check if a block is bad + * @nand: NAND device + * @pos: position pointing to the block we want to check + * + * Return: true if the block is bad, false otherwise. + */ +bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos) +{ + if (nanddev_bbt_is_initialized(nand)) { + unsigned int entry; + int status; + + entry = nanddev_bbt_pos_to_entry(nand, pos); + status = nanddev_bbt_get_block_status(nand, entry); + /* Lazy block status retrieval */ + if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) { + if (nand->ops->isbad(nand, pos)) + status = NAND_BBT_BLOCK_FACTORY_BAD; + else + status = NAND_BBT_BLOCK_GOOD; + + nanddev_bbt_set_block_status(nand, entry, status); + } + + if (status == NAND_BBT_BLOCK_WORN || + status == NAND_BBT_BLOCK_FACTORY_BAD) + return true; + + return false; + } + + return nand->ops->isbad(nand, pos); +} +EXPORT_SYMBOL_GPL(nanddev_isbad); + +/** + * nanddev_markbad() - Mark a block as bad + * @nand: NAND device + * @block: block to mark bad + * + * Mark a block bad. This function is updating the BBT if available and + * calls the low-level markbad hook (nand->ops->markbad()). + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + unsigned int entry; + int ret = 0; + + if (nanddev_isbad(nand, pos)) + return 0; + + ret = nand->ops->markbad(nand, pos); + if (ret) + pr_warn("failed to write BBM to block @%llx (err = %d)\n", + nanddev_pos_to_offs(nand, pos), ret); + + if (!nanddev_bbt_is_initialized(nand)) + goto out; + + entry = nanddev_bbt_pos_to_entry(nand, pos); + ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN); + if (ret) + goto out; + + ret = nanddev_bbt_update(nand); + +out: + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} +EXPORT_SYMBOL_GPL(nanddev_markbad); + +/** + * nanddev_isreserved() - Check whether an eraseblock is reserved or not + * @nand: NAND device + * @pos: NAND position to test + * + * Checks whether the eraseblock pointed by @pos is reserved or not. + * + * Return: true if the eraseblock is reserved, false otherwise. + */ +bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos) +{ + unsigned int entry; + int status; + + if (!nanddev_bbt_is_initialized(nand)) + return false; + + /* Return info from the table */ + entry = nanddev_bbt_pos_to_entry(nand, pos); + status = nanddev_bbt_get_block_status(nand, entry); + return status == NAND_BBT_BLOCK_RESERVED; +} +EXPORT_SYMBOL_GPL(nanddev_isreserved); + +/** + * nanddev_erase() - Erase a NAND portion + * @nand: NAND device + * @block: eraseblock to erase + * + * Erases @block if it's not bad. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) +{ + if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) { + pr_warn("attempt to erase a bad/reserved block @%llx\n", + nanddev_pos_to_offs(nand, pos)); + return -EIO; + } + + return nand->ops->erase(nand, pos); +} +EXPORT_SYMBOL_GPL(nanddev_erase); + +/** + * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices + * @mtd: MTD device + * @einfo: erase request + * + * This is a simple mtd->_erase() implementation iterating over all blocks + * concerned by @einfo and calling nand->ops->erase() on each of them. + * + * Note that mtd->_erase should not be directly assigned to this helper, + * because there's no locking here. NAND specialized layers should instead + * implement there own wrapper around nanddev_mtd_erase() taking the + * appropriate lock before calling nanddev_mtd_erase(). + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_pos pos, last; + int ret; + + nanddev_offs_to_pos(nand, einfo->addr, &pos); + nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last); + while (nanddev_pos_cmp(&pos, &last) <= 0) { + ret = nanddev_erase(nand, &pos); + if (ret) { + einfo->fail_addr = nanddev_pos_to_offs(nand, &pos); + einfo->state = MTD_ERASE_FAILED; + + return ret; + } + + nanddev_pos_next_eraseblock(nand, &pos); + } + + einfo->state = MTD_ERASE_DONE; + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_mtd_erase); + +/** + * nanddev_init() - Initialize a NAND device + * @nand: NAND device + * @memorg: NAND memory organization descriptor + * @ops: NAND device operations + * + * Initializes a NAND device object. Consistency checks are done on @memorg and + * @ops. Also takes care of initializing the BBT. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, + struct module *owner) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_memory_organization *memorg = nanddev_get_memorg(nand); + + if (!nand || !ops) + return -EINVAL; + + if (!ops->erase || !ops->markbad || !ops->isbad) + return -EINVAL; + + if (!memorg->bits_per_cell || !memorg->pagesize || + !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun || + !memorg->planes_per_lun || !memorg->luns_per_target || + !memorg->ntargets) + return -EINVAL; + + nand->rowconv.eraseblock_addr_shift = + fls(memorg->pages_per_eraseblock - 1); + nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) + + nand->rowconv.eraseblock_addr_shift; + + nand->ops = ops; + + mtd->type = memorg->bits_per_cell == 1 ? + MTD_NANDFLASH : MTD_MLCNANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock; + mtd->writesize = memorg->pagesize; + mtd->writebufsize = memorg->pagesize; + mtd->oobsize = memorg->oobsize; + mtd->size = nanddev_size(nand); + mtd->owner = owner; + + return nanddev_bbt_init(nand); +} +EXPORT_SYMBOL_GPL(nanddev_init); + +/** + * nanddev_cleanup() - Release resources allocated in nanddev_init() + * @nand: NAND device + * + * Basically undoes what has been done in nanddev_init(). + */ +void nanddev_cleanup(struct nand_device *nand) +{ + if (nanddev_bbt_is_initialized(nand)) + nanddev_bbt_cleanup(nand); +} +EXPORT_SYMBOL_GPL(nanddev_cleanup); + +MODULE_DESCRIPTION("Generic NAND framework"); +MODULE_AUTHOR("Boris Brezillon "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h new file mode 100644 index 000000000000..792ea5c26329 --- /dev/null +++ b/include/linux/mtd/nand.h @@ -0,0 +1,731 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2017 - Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#ifndef __LINUX_MTD_NAND_H +#define __LINUX_MTD_NAND_H + +#include + +/** + * struct nand_memory_organization - Memory organization structure + * @bits_per_cell: number of bits per NAND cell + * @pagesize: page size + * @oobsize: OOB area size + * @pages_per_eraseblock: number of pages per eraseblock + * @eraseblocks_per_lun: number of eraseblocks per LUN (Logical Unit Number) + * @planes_per_lun: number of planes per LUN + * @luns_per_target: number of LUN per target (target is a synonym for die) + * @ntargets: total number of targets exposed by the NAND device + */ +struct nand_memory_organization { + unsigned int bits_per_cell; + unsigned int pagesize; + unsigned int oobsize; + unsigned int pages_per_eraseblock; + unsigned int eraseblocks_per_lun; + unsigned int planes_per_lun; + unsigned int luns_per_target; + unsigned int ntargets; +}; + +#define NAND_MEMORG(bpc, ps, os, ppe, epl, ppl, lpt, nt) \ + { \ + .bits_per_cell = (bpc), \ + .pagesize = (ps), \ + .oobsize = (os), \ + .pages_per_eraseblock = (ppe), \ + .eraseblocks_per_lun = (epl), \ + .planes_per_lun = (ppl), \ + .luns_per_target = (lpt), \ + .ntargets = (nt), \ + } + +/** + * struct nand_row_converter - Information needed to convert an absolute offset + * into a row address + * @lun_addr_shift: position of the LUN identifier in the row address + * @eraseblock_addr_shift: position of the eraseblock identifier in the row + * address + */ +struct nand_row_converter { + unsigned int lun_addr_shift; + unsigned int eraseblock_addr_shift; +}; + +/** + * struct nand_pos - NAND position object + * @target: the NAND target/die + * @lun: the LUN identifier + * @plane: the plane within the LUN + * @eraseblock: the eraseblock within the LUN + * @page: the page within the LUN + * + * These information are usually used by specific sub-layers to select the + * appropriate target/die and generate a row address to pass to the device. + */ +struct nand_pos { + unsigned int target; + unsigned int lun; + unsigned int plane; + unsigned int eraseblock; + unsigned int page; +}; + +/** + * struct nand_page_io_req - NAND I/O request object + * @pos: the position this I/O request is targeting + * @dataoffs: the offset within the page + * @datalen: number of data bytes to read from/write to this page + * @databuf: buffer to store data in or get data from + * @ooboffs: the OOB offset within the page + * @ooblen: the number of OOB bytes to read from/write to this page + * @oobbuf: buffer to store OOB data in or get OOB data from + * + * This object is used to pass per-page I/O requests to NAND sub-layers. This + * way all useful information are already formatted in a useful way and + * specific NAND layers can focus on translating these information into + * specific commands/operations. + */ +struct nand_page_io_req { + struct nand_pos pos; + unsigned int dataoffs; + unsigned int datalen; + union { + const void *out; + void *in; + } databuf; + unsigned int ooboffs; + unsigned int ooblen; + union { + const void *out; + void *in; + } oobbuf; +}; + +/** + * struct nand_ecc_req - NAND ECC requirements + * @strength: ECC strength + * @step_size: ECC step/block size + */ +struct nand_ecc_req { + unsigned int strength; + unsigned int step_size; +}; + +#define NAND_ECCREQ(str, stp) { .strength = (str), .step_size = (stp) } + +/** + * struct nand_bbt - bad block table object + * @cache: in memory BBT cache + */ +struct nand_bbt { + unsigned long *cache; +}; + +struct nand_device; + +/** + * struct nand_ops - NAND operations + * @erase: erase a specific block. No need to check if the block is bad before + * erasing, this has been taken care of by the generic NAND layer + * @markbad: mark a specific block bad. No need to check if the block is + * already marked bad, this has been taken care of by the generic + * NAND layer. This method should just write the BBM (Bad Block + * Marker) so that future call to struct_nand_ops->isbad() return + * true + * @isbad: check whether a block is bad or not. This method should just read + * the BBM and return whether the block is bad or not based on what it + * reads + * + * These are all low level operations that should be implemented by specialized + * NAND layers (SPI NAND, raw NAND, ...). + */ +struct nand_ops { + int (*erase)(struct nand_device *nand, const struct nand_pos *pos); + int (*markbad)(struct nand_device *nand, const struct nand_pos *pos); + bool (*isbad)(struct nand_device *nand, const struct nand_pos *pos); +}; + +/** + * struct nand_device - NAND device + * @mtd: MTD instance attached to the NAND device + * @memorg: memory layout + * @eccreq: ECC requirements + * @rowconv: position to row address converter + * @bbt: bad block table info + * @ops: NAND operations attached to the NAND device + * + * Generic NAND object. Specialized NAND layers (raw NAND, SPI NAND, OneNAND) + * should declare their own NAND object embedding a nand_device struct (that's + * how inheritance is done). + * struct_nand_device->memorg and struct_nand_device->eccreq should be filled + * at device detection time to reflect the NAND device + * capabilities/requirements. Once this is done nanddev_init() can be called. + * It will take care of converting NAND information into MTD ones, which means + * the specialized NAND layers should never manually tweak + * struct_nand_device->mtd except for the ->_read/write() hooks. + */ +struct nand_device { + struct mtd_info mtd; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; + struct nand_row_converter rowconv; + struct nand_bbt bbt; + const struct nand_ops *ops; +}; + +/** + * struct nand_io_iter - NAND I/O iterator + * @req: current I/O request + * @oobbytes_per_page: maximum number of OOB bytes per page + * @dataleft: remaining number of data bytes to read/write + * @oobleft: remaining number of OOB bytes to read/write + * + * Can be used by specialized NAND layers to iterate over all pages covered + * by an MTD I/O request, which should greatly simplifies the boiler-plate + * code needed to read/write data from/to a NAND device. + */ +struct nand_io_iter { + struct nand_page_io_req req; + unsigned int oobbytes_per_page; + unsigned int dataleft; + unsigned int oobleft; +}; + +/** + * mtd_to_nanddev() - Get the NAND device attached to the MTD instance + * @mtd: MTD instance + * + * Return: the NAND device embedding @mtd. + */ +static inline struct nand_device *mtd_to_nanddev(struct mtd_info *mtd) +{ + return container_of(mtd, struct nand_device, mtd); +} + +/** + * nanddev_to_mtd() - Get the MTD device attached to a NAND device + * @nand: NAND device + * + * Return: the MTD device embedded in @nand. + */ +static inline struct mtd_info *nanddev_to_mtd(struct nand_device *nand) +{ + return &nand->mtd; +} + +/* + * nanddev_bits_per_cell() - Get the number of bits per cell + * @nand: NAND device + * + * Return: the number of bits per cell. + */ +static inline unsigned int nanddev_bits_per_cell(const struct nand_device *nand) +{ + return nand->memorg.bits_per_cell; +} + +/** + * nanddev_page_size() - Get NAND page size + * @nand: NAND device + * + * Return: the page size. + */ +static inline size_t nanddev_page_size(const struct nand_device *nand) +{ + return nand->memorg.pagesize; +} + +/** + * nanddev_per_page_oobsize() - Get NAND OOB size + * @nand: NAND device + * + * Return: the OOB size. + */ +static inline unsigned int +nanddev_per_page_oobsize(const struct nand_device *nand) +{ + return nand->memorg.oobsize; +} + +/** + * nanddev_pages_per_eraseblock() - Get the number of pages per eraseblock + * @nand: NAND device + * + * Return: the number of pages per eraseblock. + */ +static inline unsigned int +nanddev_pages_per_eraseblock(const struct nand_device *nand) +{ + return nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_per_page_oobsize() - Get NAND erase block size + * @nand: NAND device + * + * Return: the eraseblock size. + */ +static inline size_t nanddev_eraseblock_size(const struct nand_device *nand) +{ + return nand->memorg.pagesize * nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_eraseblocks_per_lun() - Get the number of eraseblocks per LUN + * @nand: NAND device + * + * Return: the number of eraseblocks per LUN. + */ +static inline unsigned int +nanddev_eraseblocks_per_lun(const struct nand_device *nand) +{ + return nand->memorg.eraseblocks_per_lun; +} + +/** + * nanddev_target_size() - Get the total size provided by a single target/die + * @nand: NAND device + * + * Return: the total size exposed by a single target/die in bytes. + */ +static inline u64 nanddev_target_size(const struct nand_device *nand) +{ + return (u64)nand->memorg.luns_per_target * + nand->memorg.eraseblocks_per_lun * + nand->memorg.pages_per_eraseblock * + nand->memorg.pagesize; +} + +/** + * nanddev_ntarget() - Get the total of targets + * @nand: NAND device + * + * Return: the number of targets/dies exposed by @nand. + */ +static inline unsigned int nanddev_ntargets(const struct nand_device *nand) +{ + return nand->memorg.ntargets; +} + +/** + * nanddev_neraseblocks() - Get the total number of erasablocks + * @nand: NAND device + * + * Return: the total number of eraseblocks exposed by @nand. + */ +static inline unsigned int nanddev_neraseblocks(const struct nand_device *nand) +{ + return (u64)nand->memorg.luns_per_target * + nand->memorg.eraseblocks_per_lun * + nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_size() - Get NAND size + * @nand: NAND device + * + * Return: the total size (in bytes) exposed by @nand. + */ +static inline u64 nanddev_size(const struct nand_device *nand) +{ + return nanddev_target_size(nand) * nanddev_ntargets(nand); +} + +/** + * nanddev_get_memorg() - Extract memory organization info from a NAND device + * @nand: NAND device + * + * This can be used by the upper layer to fill the memorg info before calling + * nanddev_init(). + * + * Return: the memorg object embedded in the NAND device. + */ +static inline struct nand_memory_organization * +nanddev_get_memorg(struct nand_device *nand) +{ + return &nand->memorg; +} + +int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, + struct module *owner); +void nanddev_cleanup(struct nand_device *nand); + +/** + * nanddev_register() - Register a NAND device + * @nand: NAND device + * + * Register a NAND device. + * This function is just a wrapper around mtd_device_register() + * registering the MTD device embedded in @nand. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +static inline int nanddev_register(struct nand_device *nand) +{ + return mtd_device_register(&nand->mtd, NULL, 0); +} + +/** + * nanddev_unregister() - Unregister a NAND device + * @nand: NAND device + * + * Unregister a NAND device. + * This function is just a wrapper around mtd_device_unregister() + * unregistering the MTD device embedded in @nand. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +static inline int nanddev_unregister(struct nand_device *nand) +{ + return mtd_device_unregister(&nand->mtd); +} + +/** + * nanddev_set_of_node() - Attach a DT node to a NAND device + * @nand: NAND device + * @np: DT node + * + * Attach a DT node to a NAND device. + */ +static inline void nanddev_set_of_node(struct nand_device *nand, + struct device_node *np) +{ + mtd_set_of_node(&nand->mtd, np); +} + +/** + * nanddev_get_of_node() - Retrieve the DT node attached to a NAND device + * @nand: NAND device + * + * Return: the DT node attached to @nand. + */ +static inline struct device_node *nanddev_get_of_node(struct nand_device *nand) +{ + return mtd_get_of_node(&nand->mtd); +} + +/** + * nanddev_offs_to_pos() - Convert an absolute NAND offset into a NAND position + * @nand: NAND device + * @offs: absolute NAND offset (usually passed by the MTD layer) + * @pos: a NAND position object to fill in + * + * Converts @offs into a nand_pos representation. + * + * Return: the offset within the NAND page pointed by @pos. + */ +static inline unsigned int nanddev_offs_to_pos(struct nand_device *nand, + loff_t offs, + struct nand_pos *pos) +{ + unsigned int pageoffs; + u64 tmp = offs; + + pageoffs = do_div(tmp, nand->memorg.pagesize); + pos->page = do_div(tmp, nand->memorg.pages_per_eraseblock); + pos->eraseblock = do_div(tmp, nand->memorg.eraseblocks_per_lun); + pos->plane = pos->eraseblock % nand->memorg.planes_per_lun; + pos->lun = do_div(tmp, nand->memorg.luns_per_target); + pos->target = tmp; + + return pageoffs; +} + +/** + * nanddev_pos_cmp() - Compare two NAND positions + * @a: First NAND position + * @b: Second NAND position + * + * Compares two NAND positions. + * + * Return: -1 if @a < @b, 0 if @a == @b and 1 if @a > @b. + */ +static inline int nanddev_pos_cmp(const struct nand_pos *a, + const struct nand_pos *b) +{ + if (a->target != b->target) + return a->target < b->target ? -1 : 1; + + if (a->lun != b->lun) + return a->lun < b->lun ? -1 : 1; + + if (a->eraseblock != b->eraseblock) + return a->eraseblock < b->eraseblock ? -1 : 1; + + if (a->page != b->page) + return a->page < b->page ? -1 : 1; + + return 0; +} + +/** + * nanddev_pos_to_offs() - Convert a NAND position into an absolute offset + * @nand: NAND device + * @pos: the NAND position to convert + * + * Converts @pos NAND position into an absolute offset. + * + * Return: the absolute offset. Note that @pos points to the beginning of a + * page, if one wants to point to a specific offset within this page + * the returned offset has to be adjusted manually. + */ +static inline loff_t nanddev_pos_to_offs(struct nand_device *nand, + const struct nand_pos *pos) +{ + unsigned int npages; + + npages = pos->page + + ((pos->eraseblock + + (pos->lun + + (pos->target * nand->memorg.luns_per_target)) * + nand->memorg.eraseblocks_per_lun) * + nand->memorg.pages_per_eraseblock); + + return (loff_t)npages * nand->memorg.pagesize; +} + +/** + * nanddev_pos_to_row() - Extract a row address from a NAND position + * @nand: NAND device + * @pos: the position to convert + * + * Converts a NAND position into a row address that can then be passed to the + * device. + * + * Return: the row address extracted from @pos. + */ +static inline unsigned int nanddev_pos_to_row(struct nand_device *nand, + const struct nand_pos *pos) +{ + return (pos->lun << nand->rowconv.lun_addr_shift) | + (pos->eraseblock << nand->rowconv.eraseblock_addr_shift) | + pos->page; +} + +/** + * nanddev_pos_next_target() - Move a position to the next target/die + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next target/die. Useful when you + * want to iterate over all targets/dies of a NAND device. + */ +static inline void nanddev_pos_next_target(struct nand_device *nand, + struct nand_pos *pos) +{ + pos->page = 0; + pos->plane = 0; + pos->eraseblock = 0; + pos->lun = 0; + pos->target++; +} + +/** + * nanddev_pos_next_lun() - Move a position to the next LUN + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next LUN. Useful when you want to + * iterate over all LUNs of a NAND device. + */ +static inline void nanddev_pos_next_lun(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->lun >= nand->memorg.luns_per_target - 1) + return nanddev_pos_next_target(nand, pos); + + pos->lun++; + pos->page = 0; + pos->plane = 0; + pos->eraseblock = 0; +} + +/** + * nanddev_pos_next_eraseblock() - Move a position to the next eraseblock + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next eraseblock. Useful when you + * want to iterate over all eraseblocks of a NAND device. + */ +static inline void nanddev_pos_next_eraseblock(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->eraseblock >= nand->memorg.eraseblocks_per_lun - 1) + return nanddev_pos_next_lun(nand, pos); + + pos->eraseblock++; + pos->page = 0; + pos->plane = pos->eraseblock % nand->memorg.planes_per_lun; +} + +/** + * nanddev_pos_next_eraseblock() - Move a position to the next page + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next page. Useful when you want to + * iterate over all pages of a NAND device. + */ +static inline void nanddev_pos_next_page(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->page >= nand->memorg.pages_per_eraseblock - 1) + return nanddev_pos_next_eraseblock(nand, pos); + + pos->page++; +} + +/** + * nand_io_iter_init - Initialize a NAND I/O iterator + * @nand: NAND device + * @offs: absolute offset + * @req: MTD request + * @iter: NAND I/O iterator + * + * Initializes a NAND iterator based on the information passed by the MTD + * layer. + */ +static inline void nanddev_io_iter_init(struct nand_device *nand, + loff_t offs, struct mtd_oob_ops *req, + struct nand_io_iter *iter) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + + iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos); + iter->req.ooboffs = req->ooboffs; + iter->oobbytes_per_page = mtd_oobavail(mtd, req); + iter->dataleft = req->len; + iter->oobleft = req->ooblen; + iter->req.databuf.in = req->datbuf; + iter->req.datalen = min_t(unsigned int, + nand->memorg.pagesize - iter->req.dataoffs, + iter->dataleft); + iter->req.oobbuf.in = req->oobbuf; + iter->req.ooblen = min_t(unsigned int, + iter->oobbytes_per_page - iter->req.ooboffs, + iter->oobleft); +} + +/** + * nand_io_iter_next_page - Move to the next page + * @nand: NAND device + * @iter: NAND I/O iterator + * + * Updates the @iter to point to the next page. + */ +static inline void nanddev_io_iter_next_page(struct nand_device *nand, + struct nand_io_iter *iter) +{ + nanddev_pos_next_page(nand, &iter->req.pos); + iter->dataleft -= iter->req.datalen; + iter->req.databuf.in += iter->req.datalen; + iter->oobleft -= iter->req.ooblen; + iter->req.oobbuf.in += iter->req.ooblen; + iter->req.dataoffs = 0; + iter->req.ooboffs = 0; + iter->req.datalen = min_t(unsigned int, nand->memorg.pagesize, + iter->dataleft); + iter->req.ooblen = min_t(unsigned int, iter->oobbytes_per_page, + iter->oobleft); +} + +/** + * nand_io_iter_end - Should end iteration or not + * @nand: NAND device + * @iter: NAND I/O iterator + * + * Check whether @iter has reached the end of the NAND portion it was asked to + * iterate on or not. + * + * Return: true if @iter has reached the end of the iteration request, false + * otherwise. + */ +static inline bool nanddev_io_iter_end(struct nand_device *nand, + const struct nand_io_iter *iter) +{ + if (iter->dataleft || iter->oobleft) + return false; + + return true; +} + +/** + * nand_io_for_each_page - Iterate over all NAND pages contained in an MTD I/O + * request + * @nand: NAND device + * @start: start address to read/write from + * @req: MTD I/O request + * @iter: NAND I/O iterator + * + * Should be used for iterate over pages that are contained in an MTD request. + */ +#define nanddev_io_for_each_page(nand, start, req, iter) \ + for (nanddev_io_iter_init(nand, start, req, iter); \ + !nanddev_io_iter_end(nand, iter); \ + nanddev_io_iter_next_page(nand, iter)) + +bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos); +bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos); +int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos); +int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos); + +/* BBT related functions */ +enum nand_bbt_block_status { + NAND_BBT_BLOCK_STATUS_UNKNOWN, + NAND_BBT_BLOCK_GOOD, + NAND_BBT_BLOCK_WORN, + NAND_BBT_BLOCK_RESERVED, + NAND_BBT_BLOCK_FACTORY_BAD, + NAND_BBT_BLOCK_NUM_STATUS, +}; + +int nanddev_bbt_init(struct nand_device *nand); +void nanddev_bbt_cleanup(struct nand_device *nand); +int nanddev_bbt_update(struct nand_device *nand); +int nanddev_bbt_get_block_status(const struct nand_device *nand, + unsigned int entry); +int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, + enum nand_bbt_block_status status); +int nanddev_bbt_markbad(struct nand_device *nand, unsigned int block); + +/** + * nanddev_bbt_pos_to_entry() - Convert a NAND position into a BBT entry + * @nand: NAND device + * @pos: the NAND position we want to get BBT entry for + * + * Return the BBT entry used to store information about the eraseblock pointed + * by @pos. + * + * Return: the BBT entry storing information about eraseblock pointed by @pos. + */ +static inline unsigned int nanddev_bbt_pos_to_entry(struct nand_device *nand, + const struct nand_pos *pos) +{ + return pos->eraseblock + + ((pos->lun + (pos->target * nand->memorg.luns_per_target)) * + nand->memorg.eraseblocks_per_lun); +} + +/** + * nanddev_bbt_is_initialized() - Check if the BBT has been initialized + * @nand: NAND device + * + * Return: true if the BBT has been initialized, false otherwise. + */ +static inline bool nanddev_bbt_is_initialized(struct nand_device *nand) +{ + return !!nand->bbt.cache; +} + +/* MTD -> NAND helper functions. */ +int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo); + +#endif /* __LINUX_MTD_NAND_H */ -- cgit v1.3-14-g43fede From 91c9c9ec54c1e88d9ef59a7b12cf31d6ef5d1e58 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 8 Feb 2018 17:39:01 +0100 Subject: udf: Fix off-by-one in volume descriptor sequence length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We pass one block beyond end of volume descriptor sequence into process_sequence() as 'lastblock' instead of the last block of the sequence. When the sequence is not terminated with TD descriptor, this could lead to false errors due to invalid blocks in volume descriptor sequence and thus unmountable volumes. Acked-by: Pali Rohár Signed-off-by: Jan Kara --- fs/udf/super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index f73239a9a97d..5c5d5fd513cc 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1658,7 +1658,7 @@ static noinline int udf_process_sequence( next_e = le32_to_cpu( vdp->nextVolDescSeqExt.extLength); next_e = next_e >> sb->s_blocksize_bits; - next_e += next_s; + next_e += next_s - 1; } break; case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */ @@ -1760,13 +1760,13 @@ static int udf_load_sequence(struct super_block *sb, struct buffer_head *bh, main_s = le32_to_cpu(anchor->mainVolDescSeqExt.extLocation); main_e = le32_to_cpu(anchor->mainVolDescSeqExt.extLength); main_e = main_e >> sb->s_blocksize_bits; - main_e += main_s; + main_e += main_s - 1; /* Locate the reserve sequence */ reserve_s = le32_to_cpu(anchor->reserveVolDescSeqExt.extLocation); reserve_e = le32_to_cpu(anchor->reserveVolDescSeqExt.extLength); reserve_e = reserve_e >> sb->s_blocksize_bits; - reserve_e += reserve_s; + reserve_e += reserve_s - 1; /* Process the main & reserve sequences */ /* responsible for finding the PartitionDesc(s) */ -- cgit v1.3-14-g43fede From 7b568cba4f0af32522f84c1378e71a47a9c61bb0 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 9 Feb 2018 13:28:41 +0100 Subject: udf: Simplify handling of Volume Descriptor Pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to ECMA-167 3/8.4.2 Volume Descriptor Pointer is terminating current extent of Volume Descriptor Sequence. Also according to ECMA-167 3/8.4.3 Volume Descriptor Sequence Number is not significant for Volume Descriptor Pointers. Simplify the handling of Volume Descriptor Pointers to take this into account. Acked-by: Pali Rohár Signed-off-by: Jan Kara --- fs/udf/super.c | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index 5c5d5fd513cc..f80b97173acd 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1615,7 +1615,6 @@ static noinline int udf_process_sequence( bool done = false; uint32_t vdsn; uint16_t ident; - long next_s = 0, next_e = 0; int ret; unsigned int indirections = 0; @@ -1647,19 +1646,22 @@ static noinline int udf_process_sequence( } break; case TAG_IDENT_VDP: /* ISO 13346 3/10.3 */ - curr = &vds[VDS_POS_VOL_DESC_PTR]; - if (vdsn >= curr->volDescSeqNum) { - curr->volDescSeqNum = vdsn; - curr->block = block; - - vdp = (struct volDescPtr *)bh->b_data; - next_s = le32_to_cpu( - vdp->nextVolDescSeqExt.extLocation); - next_e = le32_to_cpu( - vdp->nextVolDescSeqExt.extLength); - next_e = next_e >> sb->s_blocksize_bits; - next_e += next_s - 1; + if (++indirections > UDF_MAX_TD_NESTING) { + udf_err(sb, "too many Volume Descriptor " + "Pointers (max %u supported)\n", + UDF_MAX_TD_NESTING); + brelse(bh); + return -EIO; } + + vdp = (struct volDescPtr *)bh->b_data; + block = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation); + lastblock = le32_to_cpu( + vdp->nextVolDescSeqExt.extLength) >> + sb->s_blocksize_bits; + lastblock += block - 1; + /* For loop is going to increment 'block' again */ + block--; break; case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */ curr = &vds[VDS_POS_IMP_USE_VOL_DESC]; @@ -1688,19 +1690,8 @@ static noinline int udf_process_sequence( } break; case TAG_IDENT_TD: /* ISO 13346 3/10.9 */ - if (++indirections > UDF_MAX_TD_NESTING) { - udf_err(sb, "too many TDs (max %u supported)\n", UDF_MAX_TD_NESTING); - brelse(bh); - return -EIO; - } - vds[VDS_POS_TERMINATING_DESC].block = block; - if (next_e) { - block = next_s; - lastblock = next_e; - next_s = next_e = 0; - } else - done = true; + done = true; break; } brelse(bh); -- cgit v1.3-14-g43fede From 67621675e9636ed272a4f0235215accdfbc99768 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 8 Feb 2018 18:10:54 +0100 Subject: udf: Allow volume descriptor sequence to be terminated by unrecorded block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to ECMA-167 3/8.4.2 a volume descriptor sequence can be terminated also by an unrecorded block within the extent of volume descriptor sequence. Currently we errored out in such case making such volumes unmountable. Handle that case by treating any invalid block as a block terminating the sequence. Reported-by: Pali Rohár Acked-by: Pali Rohár Signed-off-by: Jan Kara --- fs/udf/super.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index f80b97173acd..456d737fc7ca 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1627,12 +1627,8 @@ static noinline int udf_process_sequence( for (; (!done && block <= lastblock); block++) { bh = udf_read_tagged(sb, block, block, &ident); - if (!bh) { - udf_err(sb, - "Block %llu of volume descriptor sequence is corrupted or we could not read it\n", - (unsigned long long)block); - return -EAGAIN; - } + if (!bh) + break; /* Process each descriptor (ISO 13346 3/8.3-8.4) */ gd = (struct generic_desc *)bh->b_data; -- cgit v1.3-14-g43fede From 4b8d425215f9a5f04d6ca51769a1d26cbc084aec Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 9 Feb 2018 12:52:28 +0100 Subject: udf: Convert descriptor index definitions to enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert index definitions from defines to enum. It is a shorter description and easier to modify. Also remove VDS_POS_VOL_DESC_PTR since it is unused. Acked-by: Pali Rohár Signed-off-by: Jan Kara --- fs/udf/super.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index 456d737fc7ca..7712fa4b5a3d 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -64,14 +64,15 @@ #include #include -#define VDS_POS_PRIMARY_VOL_DESC 0 -#define VDS_POS_UNALLOC_SPACE_DESC 1 -#define VDS_POS_LOGICAL_VOL_DESC 2 -#define VDS_POS_PARTITION_DESC 3 -#define VDS_POS_IMP_USE_VOL_DESC 4 -#define VDS_POS_VOL_DESC_PTR 5 -#define VDS_POS_TERMINATING_DESC 6 -#define VDS_POS_LENGTH 7 +enum { + VDS_POS_PRIMARY_VOL_DESC, + VDS_POS_UNALLOC_SPACE_DESC, + VDS_POS_LOGICAL_VOL_DESC, + VDS_POS_PARTITION_DESC, + VDS_POS_IMP_USE_VOL_DESC, + VDS_POS_TERMINATING_DESC, + VDS_POS_LENGTH +}; #define VSD_FIRST_SECTOR_OFFSET 32768 #define VSD_MAX_SECTOR_OFFSET 0x800000 -- cgit v1.3-14-g43fede From f9bc50c7206efecbb91d9b0f199e9ad6c3ec5d65 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Wed, 14 Feb 2018 21:17:31 +0530 Subject: ARM: dts: rockchip: Add eMMC node for rk3288-vyasa RK3288 Vyasa has eMMC, add dts node to support it. Signed-off-by: Jagan Teki Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3288-vyasa.dts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm/boot/dts/rk3288-vyasa.dts b/arch/arm/boot/dts/rk3288-vyasa.dts index 9842a006e823..14c896bfc639 100644 --- a/arch/arm/boot/dts/rk3288-vyasa.dts +++ b/arch/arm/boot/dts/rk3288-vyasa.dts @@ -155,6 +155,17 @@ cpu0-supply = <&vdd_cpu>; }; +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + disable-wp; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_pwr &emmc_bus8>; + vmmc-supply = <&vcc_io>; + status = "okay"; +}; + &gmac { assigned-clocks = <&cru SCLK_MAC>; assigned-clock-parents = <&ext_gmac>; -- cgit v1.3-14-g43fede From d9366032b63bb544fd7d2fd290a922e8484a52c5 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 14 Feb 2018 09:55:06 +0000 Subject: arm64: dts: renesas: r8a7795-es1: Fix register mappings on VSPs The VSPD includes a CLUT on RPF2. Ensure that the register space is mapped correctly to support this. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi index 26769a11a190..f1d5e90503d5 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi @@ -80,7 +80,7 @@ vspd3: vsp@fea38000 { compatible = "renesas,vsp2"; - reg = <0 0xfea38000 0 0x4000>; + reg = <0 0xfea38000 0 0x8000>; interrupts = ; clocks = <&cpg CPG_MOD 620>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; -- cgit v1.3-14-g43fede From c5dcfe6552f418df8db03e92e645339cdf746e34 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 14 Feb 2018 09:55:07 +0000 Subject: arm64: dts: renesas: r8a7795: Fix register mappings on VSPs The VSPD includes a CLUT on RPF2. Ensure that the register space is mapped correctly to support this. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a7795.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi index ce85704976f0..9dc2b43e59f5 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi @@ -2208,7 +2208,7 @@ vspd0: vsp@fea20000 { compatible = "renesas,vsp2"; - reg = <0 0xfea20000 0 0x4000>; + reg = <0 0xfea20000 0 0x8000>; interrupts = ; clocks = <&cpg CPG_MOD 623>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; @@ -2228,7 +2228,7 @@ vspd1: vsp@fea28000 { compatible = "renesas,vsp2"; - reg = <0 0xfea28000 0 0x4000>; + reg = <0 0xfea28000 0 0x8000>; interrupts = ; clocks = <&cpg CPG_MOD 622>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; @@ -2248,7 +2248,7 @@ vspd2: vsp@fea30000 { compatible = "renesas,vsp2"; - reg = <0 0xfea30000 0 0x4000>; + reg = <0 0xfea30000 0 0x8000>; interrupts = ; clocks = <&cpg CPG_MOD 621>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; -- cgit v1.3-14-g43fede From 4361e56b751bbebf6a8660cd41429d8a3eb5e278 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 14 Feb 2018 09:55:08 +0000 Subject: arm64: dts: renesas: r8a7796: Fix register mappings on VSPs The VSPD includes a CLUT on RPF2. Ensure that the register space is mapped correctly to support this. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a7796.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi index f8e9313f9405..157bd28014ed 100644 --- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi @@ -1903,7 +1903,7 @@ vspd0: vsp@fea20000 { compatible = "renesas,vsp2"; - reg = <0 0xfea20000 0 0x4000>; + reg = <0 0xfea20000 0 0x8000>; interrupts = ; clocks = <&cpg CPG_MOD 623>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; @@ -1923,7 +1923,7 @@ vspd1: vsp@fea28000 { compatible = "renesas,vsp2"; - reg = <0 0xfea28000 0 0x4000>; + reg = <0 0xfea28000 0 0x8000>; interrupts = ; clocks = <&cpg CPG_MOD 622>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; @@ -1943,7 +1943,7 @@ vspd2: vsp@fea30000 { compatible = "renesas,vsp2"; - reg = <0 0xfea30000 0 0x4000>; + reg = <0 0xfea30000 0 0x8000>; interrupts = ; clocks = <&cpg CPG_MOD 621>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; -- cgit v1.3-14-g43fede From cfdec2af68acb32c805aaa454f151f8681e07673 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 16 Feb 2018 12:32:35 +0000 Subject: arm64: dts: renesas: draak: Enable DU Enable the DU, providing only the VGA output for now. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm64/boot/dts/renesas/r8a77995-draak.dts | 51 ++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts index 7ea6709d706d..34c7f58417ba 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts +++ b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts @@ -27,6 +27,38 @@ stdout-path = "serial0:115200n8"; }; + vga { + compatible = "vga-connector"; + + port { + vga_in: endpoint { + remote-endpoint = <&adv7123_out>; + }; + }; + }; + + vga-encoder { + compatible = "adi,adv7123"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7123_in: endpoint { + remote-endpoint = <&du_out_rgb>; + }; + }; + port@1 { + reg = <1>; + adv7123_out: endpoint { + remote-endpoint = <&vga_in>; + }; + }; + }; + }; + memory@48000000 { device_type = "memory"; /* first 128MB is reserved for secure area. */ @@ -64,6 +96,11 @@ }; }; + du_pins: du { + groups = "du_rgb888", "du_sync", "du_disp", "du_clk_out_0"; + function = "du"; + }; + i2c0_pins: i2c0 { groups = "i2c0"; function = "i2c0"; @@ -125,6 +162,20 @@ status = "okay"; }; +&du { + pinctrl-0 = <&du_pins>; + pinctrl-names = "default"; + status = "okay"; + + ports { + port@0 { + endpoint { + remote-endpoint = <&adv7123_in>; + }; + }; + }; +}; + &ehci0 { status = "okay"; }; -- cgit v1.3-14-g43fede From 320da785db9b724cc099c79852de477f390b6cab Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 26 Dec 2017 12:50:41 +0100 Subject: reset: meson: enable level reset support on Meson8b Commit a5a10afe04ef ("reset: meson: add level reset support for GX SoC family") only enabled the level resets for the newer GX SoC family. However, the older 32-Meson SoCs (Meson8, Meson8b and Meson8m2) also support level resets using the same offset as the newer GX SoCs. This removes the separation between Meson8b and the GX SoCs from the reset-meson driver to enable the level resets also on Meson8b. Signed-off-by: Martin Blumenstingl Reviewed-by: Neil Armstrong Signed-off-by: Philipp Zabel --- drivers/reset/reset-meson.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c index 93cbee1ae8ef..5242e0679df7 100644 --- a/drivers/reset/reset-meson.c +++ b/drivers/reset/reset-meson.c @@ -124,29 +124,21 @@ static int meson_reset_deassert(struct reset_controller_dev *rcdev, return meson_reset_level(rcdev, id, false); } -static const struct reset_control_ops meson_reset_meson8_ops = { - .reset = meson_reset_reset, -}; - -static const struct reset_control_ops meson_reset_gx_ops = { +static const struct reset_control_ops meson_reset_ops = { .reset = meson_reset_reset, .assert = meson_reset_assert, .deassert = meson_reset_deassert, }; static const struct of_device_id meson_reset_dt_ids[] = { - { .compatible = "amlogic,meson8b-reset", - .data = &meson_reset_meson8_ops, }, - { .compatible = "amlogic,meson-gxbb-reset", - .data = &meson_reset_gx_ops, }, - { .compatible = "amlogic,meson-axg-reset", - .data = &meson_reset_gx_ops, }, + { .compatible = "amlogic,meson8b-reset" }, + { .compatible = "amlogic,meson-gxbb-reset" }, + { .compatible = "amlogic,meson-axg-reset" }, { /* sentinel */ }, }; static int meson_reset_probe(struct platform_device *pdev) { - const struct reset_control_ops *ops; struct meson_reset *data; struct resource *res; @@ -154,10 +146,6 @@ static int meson_reset_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - ops = of_device_get_match_data(&pdev->dev); - if (!ops) - return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); data->reg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(data->reg_base)) @@ -169,7 +157,7 @@ static int meson_reset_probe(struct platform_device *pdev) data->rcdev.owner = THIS_MODULE; data->rcdev.nr_resets = REG_COUNT * BITS_PER_REG; - data->rcdev.ops = ops; + data->rcdev.ops = &meson_reset_ops; data->rcdev.of_node = pdev->dev.of_node; return devm_reset_controller_register(&pdev->dev, &data->rcdev); -- cgit v1.3-14-g43fede From 57b29646bb4804732d654d85ca574ced263d7bc2 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 27 Nov 2017 11:57:46 -0500 Subject: drm/i915: Deprecate pci_get_bus_and_slot() pci_get_bus_and_slot() is restrictive such that it assumes domain=0 as where a PCI device is present. This restricts the device drivers to be reused for other domain numbers. Getting ready to remove pci_get_bus_and_slot() function in favor of pci_get_domain_bus_and_slot(). Extract the domain number from drm_device and pass it into pci_get_domain_bus_and_slot() function. Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas --- drivers/gpu/drm/i915/i915_drv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 173d0095e3b2..ca17508fd28c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -434,7 +434,10 @@ static int i915_getparam(struct drm_device *dev, void *data, static int i915_get_bridge_dev(struct drm_i915_private *dev_priv) { - dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)); + int domain = pci_domain_nr(dev_priv->drm.pdev->bus); + + dev_priv->bridge_dev = + pci_get_domain_bus_and_slot(domain, 0, PCI_DEVFN(0, 0)); if (!dev_priv->bridge_dev) { DRM_ERROR("bridge device not found\n"); return -1; -- cgit v1.3-14-g43fede From 5cf0c37a71da0f3a4802806c597b21d99c33ca60 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 19 Dec 2017 00:38:02 -0500 Subject: PCI: Remove pci_get_bus_and_slot() function pci_get_bus_and_slot() is restrictive such that it assumes domain=0 as where a PCI device is present. This restricts the device drivers to be reused for other domain numbers. Now that all users of pci_get_bus_and_slot() switched to pci_get_domain_bus_and_slot(), it is now safe to remove this function. Signed-off-by: Sinan Kaya Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..25b7a3535d26 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -949,11 +949,6 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device, struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn); struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus, unsigned int devfn); -static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus, - unsigned int devfn) -{ - return pci_get_domain_bus_and_slot(0, bus, devfn); -} struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from); int pci_dev_present(const struct pci_device_id *ids); @@ -1661,9 +1656,6 @@ static inline struct pci_bus *pci_find_next_bus(const struct pci_bus *from) static inline struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn) { return NULL; } -static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus, - unsigned int devfn) -{ return NULL; } static inline struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus, unsigned int devfn) { return NULL; } -- cgit v1.3-14-g43fede From 5b0764cac9f1b70a6704b0e546be67c24cc05517 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 16 Feb 2018 10:55:38 -0600 Subject: PCI: Probe for device reset support during enumeration Previously we called pci_probe_reset_function() in this path: pci_sysfs_init # late_initcall for_each_pci_dev(dev) pci_create_sysfs_dev_files(dev) pci_create_capabilities_sysfs(dev) pci_probe_reset_function pci_dev_specific_reset pcie_has_flr pcie_capability_read_dword pci_sysfs_init() is a late_initcall, and a driver may have already claimed one of these devices and enabled runtime power management for it, so the device could already be in D3 by the time we get to pci_sysfs_init(). The device itself should respond to the config read even while it's in D3hot, but if an upstream bridge is also in D3hot, the read won't even reach the device because the bridge won't forward it downstream to the device. If the bridge is a PCIe port, it should complete the read as an Unsupported Request, which may be reported to the CPU as an exception or as invalid data. Avoid this case by probing for reset support from pci_init_capabilities(), before a driver can claim the device. The device may be in D3hot, but any bridges leading to it should be in D0, so the device's config space should be fully accessible at that point. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci-sysfs.c | 3 +-- drivers/pci/probe.c | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index eb6bee8724cc..4933f0270471 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1542,11 +1542,10 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) /* Active State Power Management */ pcie_aspm_create_sysfs_dev_files(dev); - if (!pci_probe_reset_function(dev)) { + if (dev->reset_fn) { retval = device_create_file(&dev->dev, &reset_attr); if (retval) goto error; - dev->reset_fn = 1; } return 0; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ef5377438a1e..489660d0d384 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2121,6 +2121,9 @@ static void pci_init_capabilities(struct pci_dev *dev) /* Advanced Error Reporting */ pci_aer_init(dev); + + if (pci_probe_reset_function(dev) == 0) + dev->reset_fn = 1; } /* -- cgit v1.3-14-g43fede From 1bdd335fa6a8ce27ee9bfe1e0c7a45fc0588156e Mon Sep 17 00:00:00 2001 From: Alexandre Torgue Date: Fri, 16 Feb 2018 18:05:44 +0100 Subject: dt-bindings: pinctrl: Add st,stm32f769-pinctrl compatible to stm32-pinctrl Add new compatible for stm32f769 MCU. Signed-off-by: Alexandre Torgue Reviewed-by: Rob Herring --- Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt index 2c46f30b62c5..9a06e1fdbc42 100644 --- a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt @@ -11,6 +11,7 @@ Required properies: "st,stm32f429-pinctrl" "st,stm32f469-pinctrl" "st,stm32f746-pinctrl" + "st,stm32f769-pinctrl" "st,stm32h743-pinctrl" "st,stm32mp157-pinctrl" "st,stm32mp157-z-pinctrl" -- cgit v1.3-14-g43fede From a45011956d67f89f9434b6d73e65e41993375e69 Mon Sep 17 00:00:00 2001 From: Alexandre Torgue Date: Fri, 16 Feb 2018 18:11:52 +0100 Subject: ARM: dts: stm32: use dedicated files for pinctrl on stm32f7 family Currently, same stm32f746-pinctrl driver is used for stm32f746 and stm32f769 MCU. As pin map is different between those 2 MCUs, a stm32f769-pinctrl driver has been recently added. This patch -allows to use stm32f769-pinctrl driver for stm32f769 boards -reworks stm32 devicetree files to fit with stm32f746 / stm32f769 Signed-off-by: Alexandre Torgue --- arch/arm/boot/dts/stm32746g-eval.dts | 1 + arch/arm/boot/dts/stm32f7-pinctrl.dtsi | 227 +++++++++++++++++++++++++++++++ arch/arm/boot/dts/stm32f746-disco.dts | 1 + arch/arm/boot/dts/stm32f746-pinctrl.dtsi | 11 ++ arch/arm/boot/dts/stm32f746.dtsi | 217 ----------------------------- arch/arm/boot/dts/stm32f769-disco.dts | 3 +- arch/arm/boot/dts/stm32f769-pinctrl.dtsi | 11 ++ 7 files changed, 253 insertions(+), 218 deletions(-) create mode 100644 arch/arm/boot/dts/stm32f7-pinctrl.dtsi create mode 100644 arch/arm/boot/dts/stm32f746-pinctrl.dtsi create mode 100644 arch/arm/boot/dts/stm32f769-pinctrl.dtsi diff --git a/arch/arm/boot/dts/stm32746g-eval.dts b/arch/arm/boot/dts/stm32746g-eval.dts index 2d4e71717694..b2d4b8c46677 100644 --- a/arch/arm/boot/dts/stm32746g-eval.dts +++ b/arch/arm/boot/dts/stm32746g-eval.dts @@ -42,6 +42,7 @@ /dts-v1/; #include "stm32f746.dtsi" +#include "stm32f746-pinctrl.dtsi" #include / { diff --git a/arch/arm/boot/dts/stm32f7-pinctrl.dtsi b/arch/arm/boot/dts/stm32f7-pinctrl.dtsi new file mode 100644 index 000000000000..f518de184e52 --- /dev/null +++ b/arch/arm/boot/dts/stm32f7-pinctrl.dtsi @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +#include +#include + +/ { + soc { + pinctrl: pin-controller { + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x40020000 0x3000>; + interrupt-parent = <&exti>; + st,syscfg = <&syscfg 0x8>; + pins-are-numbered; + + gpioa: gpio@40020000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x0 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOA)>; + st,bank-name = "GPIOA"; + }; + + gpiob: gpio@40020400 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x400 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOB)>; + st,bank-name = "GPIOB"; + }; + + gpioc: gpio@40020800 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x800 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOC)>; + st,bank-name = "GPIOC"; + }; + + gpiod: gpio@40020c00 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0xc00 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOD)>; + st,bank-name = "GPIOD"; + }; + + gpioe: gpio@40021000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x1000 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOE)>; + st,bank-name = "GPIOE"; + }; + + gpiof: gpio@40021400 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x1400 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOF)>; + st,bank-name = "GPIOF"; + }; + + gpiog: gpio@40021800 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x1800 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOG)>; + st,bank-name = "GPIOG"; + }; + + gpioh: gpio@40021c00 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x1c00 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOH)>; + st,bank-name = "GPIOH"; + }; + + gpioi: gpio@40022000 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x2000 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOI)>; + st,bank-name = "GPIOI"; + }; + + gpioj: gpio@40022400 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x2400 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOJ)>; + st,bank-name = "GPIOJ"; + }; + + gpiok: gpio@40022800 { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x2800 0x400>; + clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOK)>; + st,bank-name = "GPIOK"; + }; + + cec_pins_a: cec@0 { + pins { + pinmux = ; /* HDMI CEC */ + slew-rate = <0>; + drive-open-drain; + bias-disable; + }; + }; + + usart1_pins_a: usart1@0 { + pins1 { + pinmux = ; /* USART1_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* USART1_RX */ + bias-disable; + }; + }; + + usart1_pins_b: usart1@1 { + pins1 { + pinmux = ; /* USART1_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* USART1_RX */ + bias-disable; + }; + }; + + i2c1_pins_b: i2c1@0 { + pins { + pinmux = , /* I2C1 SDA */ + ; /* I2C1 SCL */ + bias-disable; + drive-open-drain; + slew-rate = <0>; + }; + }; + + usbotg_hs_pins_a: usbotg-hs@0 { + pins { + pinmux = , /* OTG_HS_ULPI_NXT */ + , /* OTG_HS_ULPI_DIR */ + , /* OTG_HS_ULPI_STP */ + , /* OTG_HS_ULPI_CK */ + , /* OTG_HS_ULPI_D0 */ + , /* OTG_HS_ULPI_D1 */ + , /* OTG_HS_ULPI_D2 */ + , /* OTG_HS_ULPI_D3 */ + , /* OTG_HS_ULPI_D4 */ + , /* OTG_HS_ULPI_D5 */ + , /* OTG_HS_ULPI_D6 */ + ; /* OTG_HS_ULPI_D7 */ + bias-disable; + drive-push-pull; + slew-rate = <2>; + }; + }; + + usbotg_hs_pins_b: usbotg-hs@1 { + pins { + pinmux = , /* OTG_HS_ULPI_NXT */ + , /* OTG_HS_ULPI_DIR */ + , /* OTG_HS_ULPI_STP */ + , /* OTG_HS_ULPI_CK */ + , /* OTG_HS_ULPI_D0 */ + , /* OTG_HS_ULPI_D1 */ + , /* OTG_HS_ULPI_D2 */ + , /* OTG_HS_ULPI_D3 */ + , /* OTG_HS_ULPI_D4 */ + , /* OTG_HS_ULPI_D5 */ + , /* OTG_HS_ULPI_D6 */ + ; /* OTG_HS_ULPI_D7 */ + bias-disable; + drive-push-pull; + slew-rate = <2>; + }; + }; + + usbotg_fs_pins_a: usbotg-fs@0 { + pins { + pinmux = , /* OTG_FS_ID */ + , /* OTG_FS_DM */ + ; /* OTG_FS_DP */ + bias-disable; + drive-push-pull; + slew-rate = <2>; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/stm32f746-disco.dts b/arch/arm/boot/dts/stm32f746-disco.dts index 4d85dba59e1d..623b6f2bc361 100644 --- a/arch/arm/boot/dts/stm32f746-disco.dts +++ b/arch/arm/boot/dts/stm32f746-disco.dts @@ -42,6 +42,7 @@ /dts-v1/; #include "stm32f746.dtsi" +#include "stm32f746-pinctrl.dtsi" #include / { diff --git a/arch/arm/boot/dts/stm32f746-pinctrl.dtsi b/arch/arm/boot/dts/stm32f746-pinctrl.dtsi new file mode 100644 index 000000000000..fcfd2ac7239b --- /dev/null +++ b/arch/arm/boot/dts/stm32f746-pinctrl.dtsi @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +#include "stm32f7-pinctrl.dtsi" + +&pinctrl{ + compatible = "st,stm32f746-pinctrl"; +}; diff --git a/arch/arm/boot/dts/stm32f746.dtsi b/arch/arm/boot/dts/stm32f746.dtsi index 5f66d151eedb..8fe96d6d0ca2 100644 --- a/arch/arm/boot/dts/stm32f746.dtsi +++ b/arch/arm/boot/dts/stm32f746.dtsi @@ -42,7 +42,6 @@ #include "skeleton.dtsi" #include "armv7-m.dtsi" -#include #include #include @@ -498,222 +497,6 @@ reg = <0x40007000 0x400>; }; - pin-controller { - #address-cells = <1>; - #size-cells = <1>; - compatible = "st,stm32f746-pinctrl"; - ranges = <0 0x40020000 0x3000>; - interrupt-parent = <&exti>; - st,syscfg = <&syscfg 0x8>; - pins-are-numbered; - - gpioa: gpio@40020000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x0 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOA)>; - st,bank-name = "GPIOA"; - }; - - gpiob: gpio@40020400 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x400 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOB)>; - st,bank-name = "GPIOB"; - }; - - gpioc: gpio@40020800 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x800 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOC)>; - st,bank-name = "GPIOC"; - }; - - gpiod: gpio@40020c00 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0xc00 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOD)>; - st,bank-name = "GPIOD"; - }; - - gpioe: gpio@40021000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x1000 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOE)>; - st,bank-name = "GPIOE"; - }; - - gpiof: gpio@40021400 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x1400 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOF)>; - st,bank-name = "GPIOF"; - }; - - gpiog: gpio@40021800 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x1800 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOG)>; - st,bank-name = "GPIOG"; - }; - - gpioh: gpio@40021c00 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x1c00 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOH)>; - st,bank-name = "GPIOH"; - }; - - gpioi: gpio@40022000 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x2000 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOI)>; - st,bank-name = "GPIOI"; - }; - - gpioj: gpio@40022400 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x2400 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOJ)>; - st,bank-name = "GPIOJ"; - }; - - gpiok: gpio@40022800 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - reg = <0x2800 0x400>; - clocks = <&rcc 0 STM32F7_AHB1_CLOCK(GPIOK)>; - st,bank-name = "GPIOK"; - }; - - cec_pins_a: cec@0 { - pins { - pinmux = ; /* HDMI CEC */ - slew-rate = <0>; - drive-open-drain; - bias-disable; - }; - }; - - usart1_pins_a: usart1@0 { - pins1 { - pinmux = ; /* USART1_TX */ - bias-disable; - drive-push-pull; - slew-rate = <0>; - }; - pins2 { - pinmux = ; /* USART1_RX */ - bias-disable; - }; - }; - - usart1_pins_b: usart1@1 { - pins1 { - pinmux = ; /* USART1_TX */ - bias-disable; - drive-push-pull; - slew-rate = <0>; - }; - pins2 { - pinmux = ; /* USART1_RX */ - bias-disable; - }; - }; - - i2c1_pins_b: i2c1@0 { - pins { - pinmux = , /* I2C1 SDA */ - ; /* I2C1 SCL */ - bias-disable; - drive-open-drain; - slew-rate = <0>; - }; - }; - - usbotg_hs_pins_a: usbotg-hs@0 { - pins { - pinmux = , /* OTG_HS_ULPI_NXT */ - , /* OTG_HS_ULPI_DIR */ - , /* OTG_HS_ULPI_STP */ - , /* OTG_HS_ULPI_CK */ - , /* OTG_HS_ULPI_D0 */ - , /* OTG_HS_ULPI_D1 */ - , /* OTG_HS_ULPI_D2 */ - , /* OTG_HS_ULPI_D3 */ - , /* OTG_HS_ULPI_D4 */ - , /* OTG_HS_ULPI_D5 */ - , /* OTG_HS_ULPI_D6 */ - ; /* OTG_HS_ULPI_D7 */ - bias-disable; - drive-push-pull; - slew-rate = <2>; - }; - }; - - usbotg_hs_pins_b: usbotg-hs@1 { - pins { - pinmux = , /* OTG_HS_ULPI_NXT */ - , /* OTG_HS_ULPI_DIR */ - , /* OTG_HS_ULPI_STP */ - , /* OTG_HS_ULPI_CK */ - , /* OTG_HS_ULPI_D0 */ - , /* OTG_HS_ULPI_D1 */ - , /* OTG_HS_ULPI_D2 */ - , /* OTG_HS_ULPI_D3 */ - , /* OTG_HS_ULPI_D4 */ - , /* OTG_HS_ULPI_D5 */ - , /* OTG_HS_ULPI_D6 */ - ; /* OTG_HS_ULPI_D7 */ - bias-disable; - drive-push-pull; - slew-rate = <2>; - }; - }; - - usbotg_fs_pins_a: usbotg-fs@0 { - pins { - pinmux = , /* OTG_FS_ID */ - , /* OTG_FS_DM */ - ; /* OTG_FS_DP */ - bias-disable; - drive-push-pull; - slew-rate = <2>; - }; - }; - }; - crc: crc@40023000 { compatible = "st,stm32f7-crc"; reg = <0x40023000 0x400>; diff --git a/arch/arm/boot/dts/stm32f769-disco.dts b/arch/arm/boot/dts/stm32f769-disco.dts index 4463ca13a740..9dba286c6d2f 100644 --- a/arch/arm/boot/dts/stm32f769-disco.dts +++ b/arch/arm/boot/dts/stm32f769-disco.dts @@ -42,11 +42,12 @@ /dts-v1/; #include "stm32f746.dtsi" +#include "stm32f769-pinctrl.dtsi" #include / { model = "STMicroelectronics STM32F769-DISCO board"; - compatible = "st,stm32f769-disco", "st,stm32f7"; + compatible = "st,stm32f769-disco", "st,stm32f769"; chosen { bootargs = "root=/dev/ram"; diff --git a/arch/arm/boot/dts/stm32f769-pinctrl.dtsi b/arch/arm/boot/dts/stm32f769-pinctrl.dtsi new file mode 100644 index 000000000000..31005dd9929c --- /dev/null +++ b/arch/arm/boot/dts/stm32f769-pinctrl.dtsi @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Alexandre Torgue for STMicroelectronics. + */ + +#include "stm32f7-pinctrl.dtsi" + +&pinctrl{ + compatible = "st,stm32f769-pinctrl"; +}; -- cgit v1.3-14-g43fede From 92230709a9fcc7c0bc3a6db90430a1fd7edaad03 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 16 Feb 2018 11:16:04 -0500 Subject: ARM: dts: sun7i: Enable HDMI support on the Banana Pi Enable the display pipeline and HDMI output Signed-off-by: Stefan Monnier Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun7i-a20-bananapi.dts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi.dts b/arch/arm/boot/dts/sun7i-a20-bananapi.dts index 88a1c2363c6c..45da8a282288 100644 --- a/arch/arm/boot/dts/sun7i-a20-bananapi.dts +++ b/arch/arm/boot/dts/sun7i-a20-bananapi.dts @@ -63,6 +63,17 @@ stdout-path = "serial0:115200n8"; }; + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + leds { compatible = "gpio-leds"; pinctrl-names = "default"; @@ -109,6 +120,10 @@ >; }; +&de { + status = "okay"; +}; + &ehci0 { status = "okay"; }; @@ -130,6 +145,16 @@ }; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &i2c0 { pinctrl-names = "default"; pinctrl-0 = <&i2c0_pins_a>; -- cgit v1.3-14-g43fede From 4df6ed4f0ac9a1bb776eabb5a669321b68296ff2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 16 Feb 2018 15:22:47 +0100 Subject: mtd: nand: fsmc: get rid of IO_ADDR_[R|W] Remove the use of IO_ADDR_[R|W] in the fsmc_nand driver. Instead, use a pointer to the control registers to avoid doing several arithmetic operations (including a multiplication) each time a control register is read or written. All references to IO_ADDR_[R|W] are not entirely removed from the driver as, at this time, these values are needed by the NAND core in the default ->read/write_byte/word() hooks. These references will be entirely removed when switching to ->exec_op(), that does not make use of these hooks anymore. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsmc_nand.c | 100 ++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 58 deletions(-) diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 7e66268f8154..75a825dc5a4c 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -103,10 +103,6 @@ #define ECC3 0x1C #define FSMC_NAND_BANK_SZ 0x20 -#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \ - (FSMC_NAND_BANK_SZ * (bank)) + \ - reg) - #define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ) struct fsmc_nand_timings { @@ -143,7 +139,7 @@ enum access_mode { * @data_va: NAND port for Data. * @cmd_va: NAND port for Command. * @addr_va: NAND port for Address. - * @regs_va: FSMC regs base address. + * @regs_va: Registers base address for a given bank. */ struct fsmc_nand_data { u32 pid; @@ -265,8 +261,6 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *this = mtd_to_nand(mtd); struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - unsigned int bank = host->bank; if (ctrl & NAND_CTRL_CHANGE) { u32 pc; @@ -282,12 +276,12 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) this->IO_ADDR_W = host->data_va; } - pc = readl(FSMC_NAND_REG(regs, bank, PC)); + pc = readl(host->regs_va + PC); if (ctrl & NAND_NCE) pc |= FSMC_ENABLE; else pc &= ~FSMC_ENABLE; - writel_relaxed(pc, FSMC_NAND_REG(regs, bank, PC)); + writel_relaxed(pc, host->regs_va + PC); } mb(); @@ -307,8 +301,6 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host, { uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; uint32_t tclr, tar, thiz, thold, twait, tset; - unsigned int bank = host->bank; - void __iomem *regs = host->regs_va; tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; @@ -318,18 +310,14 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host, tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; if (host->nand.options & NAND_BUSWIDTH_16) - writel_relaxed(value | FSMC_DEVWID_16, - FSMC_NAND_REG(regs, bank, PC)); + writel_relaxed(value | FSMC_DEVWID_16, host->regs_va + PC); else - writel_relaxed(value | FSMC_DEVWID_8, - FSMC_NAND_REG(regs, bank, PC)); - - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar, - FSMC_NAND_REG(regs, bank, PC)); - writel_relaxed(thiz | thold | twait | tset, - FSMC_NAND_REG(regs, bank, COMM)); - writel_relaxed(thiz | thold | twait | tset, - FSMC_NAND_REG(regs, bank, ATTRIB)); + writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + PC); + + writel_relaxed(readl(host->regs_va + PC) | tclr | tar, + host->regs_va + PC); + writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM); + writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB); } static int fsmc_calc_timings(struct fsmc_nand_data *host, @@ -419,15 +407,13 @@ static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline, static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) { struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - uint32_t bank = host->bank; - - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256, - FSMC_NAND_REG(regs, bank, PC)); - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN, - FSMC_NAND_REG(regs, bank, PC)); - writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN, - FSMC_NAND_REG(regs, bank, PC)); + + writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCPLEN_256, + host->regs_va + PC); + writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCEN, + host->regs_va + PC); + writel_relaxed(readl(host->regs_va + PC) | FSMC_ECCEN, + host->regs_va + PC); } /* @@ -439,13 +425,11 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc) { struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - uint32_t bank = host->bank; uint32_t ecc_tmp; unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; do { - if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) + if (readl_relaxed(host->regs_va + STS) & FSMC_CODE_RDY) break; else cond_resched(); @@ -456,25 +440,25 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, return -ETIMEDOUT; } - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); + ecc_tmp = readl_relaxed(host->regs_va + ECC1); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); ecc[2] = (uint8_t) (ecc_tmp >> 16); ecc[3] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); + ecc_tmp = readl_relaxed(host->regs_va + ECC2); ecc[4] = (uint8_t) (ecc_tmp >> 0); ecc[5] = (uint8_t) (ecc_tmp >> 8); ecc[6] = (uint8_t) (ecc_tmp >> 16); ecc[7] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); + ecc_tmp = readl_relaxed(host->regs_va + ECC3); ecc[8] = (uint8_t) (ecc_tmp >> 0); ecc[9] = (uint8_t) (ecc_tmp >> 8); ecc[10] = (uint8_t) (ecc_tmp >> 16); ecc[11] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); + ecc_tmp = readl_relaxed(host->regs_va + STS); ecc[12] = (uint8_t) (ecc_tmp >> 16); return 0; @@ -489,11 +473,9 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc) { struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - uint32_t bank = host->bank; uint32_t ecc_tmp; - ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); + ecc_tmp = readl_relaxed(host->regs_va + ECC1); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); ecc[2] = (uint8_t) (ecc_tmp >> 16); @@ -598,18 +580,18 @@ unmap_dma: */ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); int i; - struct nand_chip *chip = mtd_to_nand(mtd); if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && IS_ALIGNED(len, sizeof(uint32_t))) { uint32_t *p = (uint32_t *)buf; len = len >> 2; for (i = 0; i < len; i++) - writel_relaxed(p[i], chip->IO_ADDR_W); + writel_relaxed(p[i], host->data_va); } else { for (i = 0; i < len; i++) - writeb_relaxed(buf[i], chip->IO_ADDR_W); + writeb_relaxed(buf[i], host->data_va); } } @@ -621,18 +603,18 @@ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) */ static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); int i; - struct nand_chip *chip = mtd_to_nand(mtd); if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && IS_ALIGNED(len, sizeof(uint32_t))) { uint32_t *p = (uint32_t *)buf; len = len >> 2; for (i = 0; i < len; i++) - p[i] = readl_relaxed(chip->IO_ADDR_R); + p[i] = readl_relaxed(host->data_va); } else { for (i = 0; i < len; i++) - buf[i] = readb_relaxed(chip->IO_ADDR_R); + buf[i] = readb_relaxed(host->data_va); } } @@ -754,13 +736,11 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, { struct nand_chip *chip = mtd_to_nand(mtd); struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - void __iomem *regs = host->regs_va; - unsigned int bank = host->bank; uint32_t err_idx[8]; uint32_t num_err, i; uint32_t ecc1, ecc2, ecc3, ecc4; - num_err = (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF; + num_err = (readl_relaxed(host->regs_va + STS) >> 10) & 0xF; /* no bit flipping */ if (likely(num_err == 0)) @@ -803,10 +783,10 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, * uint64_t array and error offset indexes are populated in err_idx * array */ - ecc1 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); - ecc2 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); - ecc3 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); - ecc4 = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); + ecc1 = readl_relaxed(host->regs_va + ECC1); + ecc2 = readl_relaxed(host->regs_va + ECC2); + ecc3 = readl_relaxed(host->regs_va + ECC3); + ecc4 = readl_relaxed(host->regs_va + STS); err_idx[0] = (ecc1 >> 0) & 0x1FFF; err_idx[1] = (ecc1 >> 13) & 0x1FFF; @@ -889,6 +869,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) struct mtd_info *mtd; struct nand_chip *nand; struct resource *res; + void __iomem *base; dma_cap_mask_t mask; int ret = 0; u32 pid; @@ -923,9 +904,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) return PTR_ERR(host->cmd_va); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); - host->regs_va = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->regs_va)) - return PTR_ERR(host->regs_va); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + host->regs_va = base + FSMC_NOR_REG_SIZE + + (host->bank * FSMC_NAND_BANK_SZ); host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) { @@ -942,7 +926,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * AMBA PrimeCell bus. However it is not a PrimeCell. */ for (pid = 0, i = 0; i < 4; i++) - pid |= (readl(host->regs_va + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8); + pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8); host->pid = pid; dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, " "revision %02x, config %02x\n", -- cgit v1.3-14-g43fede From 4da712e702941daa849ccd7fbcaa677dce7855b2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 16 Feb 2018 15:22:48 +0100 Subject: mtd: nand: fsmc: use ->exec_op() Remove the deprecated ->cmd_ctrl() implementation to use ->exec_op() in the fsmc_nand driver. Implement the ->select_chip() hook to avoid having to support the hack from the core that send a NAND_CMD_NONE with NAND_NCE to signal a deassertion of nCE. Also get rid of the last references to ->IO_ADDR_[R|W] that are not used anymore. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsmc_nand.c | 150 ++++++++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 51 deletions(-) diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 75a825dc5a4c..28c48dcc514e 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -253,43 +253,6 @@ static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd) return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand); } -/* - * fsmc_cmd_ctrl - For facilitaing Hardware access - * This routine allows hardware specific access to control-lines(ALE,CLE) - */ -static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - - if (ctrl & NAND_CTRL_CHANGE) { - u32 pc; - - if (ctrl & NAND_CLE) { - this->IO_ADDR_R = host->cmd_va; - this->IO_ADDR_W = host->cmd_va; - } else if (ctrl & NAND_ALE) { - this->IO_ADDR_R = host->addr_va; - this->IO_ADDR_W = host->addr_va; - } else { - this->IO_ADDR_R = host->data_va; - this->IO_ADDR_W = host->data_va; - } - - pc = readl(host->regs_va + PC); - if (ctrl & NAND_NCE) - pc |= FSMC_ENABLE; - else - pc &= ~FSMC_ENABLE; - writel_relaxed(pc, host->regs_va + PC); - } - - mb(); - - if (cmd != NAND_CMD_NONE) - writeb_relaxed(cmd, this->IO_ADDR_W); -} - /* * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine * @@ -645,6 +608,102 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); } +/* fsmc_select_chip - assert or deassert nCE */ +static void fsmc_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + u32 pc; + + /* Support only one CS */ + if (chipnr > 0) + return; + + pc = readl(host->regs_va + PC); + if (chipnr < 0) + writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + PC); + else + writel_relaxed(pc | FSMC_ENABLE, host->regs_va + PC); + + /* nCE line must be asserted before starting any operation */ + mb(); +} + +/* + * fsmc_exec_op - hook called by the core to execute NAND operations + * + * This controller is simple enough and thus does not need to use the parser + * provided by the core, instead, handle every situation here. + */ +static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, + bool check_only) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + const struct nand_op_instr *instr = NULL; + int ret = 0; + unsigned int op_id; + int i; + + pr_debug("Executing operation [%d instructions]:\n", op->ninstrs); + for (op_id = 0; op_id < op->ninstrs; op_id++) { + instr = &op->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + pr_debug(" ->CMD [0x%02x]\n", + instr->ctx.cmd.opcode); + + writeb_relaxed(instr->ctx.cmd.opcode, host->cmd_va); + break; + + case NAND_OP_ADDR_INSTR: + pr_debug(" ->ADDR [%d cyc]", + instr->ctx.addr.naddrs); + + for (i = 0; i < instr->ctx.addr.naddrs; i++) + writeb_relaxed(instr->ctx.addr.addrs[i], + host->addr_va); + break; + + case NAND_OP_DATA_IN_INSTR: + pr_debug(" ->DATA_IN [%d B%s]\n", instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + + if (host->mode == USE_DMA_ACCESS) + fsmc_read_buf_dma(mtd, instr->ctx.data.buf.in, + instr->ctx.data.len); + else + fsmc_read_buf(mtd, instr->ctx.data.buf.in, + instr->ctx.data.len); + break; + + case NAND_OP_DATA_OUT_INSTR: + pr_debug(" ->DATA_OUT [%d B%s]\n", instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + + if (host->mode == USE_DMA_ACCESS) + fsmc_write_buf_dma(mtd, instr->ctx.data.buf.out, + instr->ctx.data.len); + else + fsmc_write_buf(mtd, instr->ctx.data.buf.out, + instr->ctx.data.len); + break; + + case NAND_OP_WAITRDY_INSTR: + pr_debug(" ->WAITRDY [max %d ms]\n", + instr->ctx.waitrdy.timeout_ms); + + ret = nand_soft_waitrdy(chip, + instr->ctx.waitrdy.timeout_ms); + break; + } + } + + return ret; +} + /* * fsmc_read_page_hwecc * @mtd: mtd info structure @@ -944,9 +1003,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand_set_flash_node(nand, pdev->dev.of_node); mtd->dev.parent = &pdev->dev; - nand->IO_ADDR_R = host->data_va; - nand->IO_ADDR_W = host->data_va; - nand->cmd_ctrl = fsmc_cmd_ctrl; + nand->exec_op = fsmc_exec_op; + nand->select_chip = fsmc_select_chip; nand->chip_delay = 30; /* @@ -958,8 +1016,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.size = 512; nand->badblockbits = 7; - switch (host->mode) { - case USE_DMA_ACCESS: + if (host->mode == USE_DMA_ACCESS) { dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); host->read_dma_chan = dma_request_channel(mask, filter, NULL); @@ -972,15 +1029,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to get write dma channel\n"); goto err_req_write_chnl; } - nand->read_buf = fsmc_read_buf_dma; - nand->write_buf = fsmc_write_buf_dma; - break; - - default: - case USE_WORD_ACCESS: - nand->read_buf = fsmc_read_buf; - nand->write_buf = fsmc_write_buf; - break; } if (host->dev_timings) -- cgit v1.3-14-g43fede From e493621390f9be06b51afe99d982cbd2b2eb81b1 Mon Sep 17 00:00:00 2001 From: Klaus Goger Date: Tue, 30 Jan 2018 13:39:28 +0100 Subject: dt-bindings: add RK3368-uQ7 SoM and EVK base board RK3368-uQ7 is a Qseven compatible system-on-module by Theobroma Systems. This adds the module and the EVK baseboard "Haikou" Signed-off-by: Klaus Goger Signed-off-by: Heiko Stuebner --- Documentation/devicetree/bindings/arm/rockchip.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt index 0f76a621015c..9a3e98356b1e 100644 --- a/Documentation/devicetree/bindings/arm/rockchip.txt +++ b/Documentation/devicetree/bindings/arm/rockchip.txt @@ -189,6 +189,10 @@ Rockchip platforms device tree bindings Required root node properties: - compatible = "rockchip,rk3399-sapphire-excavator", "rockchip,rk3399"; +- Theobroma Systems RK3368-uQ7 Haikou Baseboard: + Required root node properties: + - compatible = "tsd,rk3368-uq7-haikou", "rockchip,rk3368"; + - Theobroma Systems RK3399-Q7 Haikou Baseboard: Required root node properties: - compatible = "tsd,rk3399-q7-haikou", "rockchip,rk3399"; -- cgit v1.3-14-g43fede From d99a02bcfa8152dd82f6be4da1816da4f52cf002 Mon Sep 17 00:00:00 2001 From: Klaus Goger Date: Tue, 30 Jan 2018 13:39:29 +0100 Subject: arm64: dts: rockchip: add RK3368-uQ7 (Lion) SoM The RK3368-uQ7 SoM is a uQseven-compatible (40mm x 70mm, MXM-230 connector) system-on-module from Theobroma Systems, featuring the Rockchip RK3368. It provides the following feature set: * up to 4GB DDR3 * on-module SPI-NOR flash * on-module eMMC (with 8-bit 1.8V interface) * SD card (on a baseboad) via edge connector * Gigabit Ethernet with on-module Micrel KSZ9031 GbE PHY * HDMI/eDP/MIPI-DSI/LVDS * MIPI-CSI * USB - 1x USB 2.0 dual-role - 1x USB 2.0 host * on-module STM32 Cortex-M0 companion controller, implementing: - low-power RTC functionality (ISL1208 emulation) - fan controller (AMC6821 emulation) - USB<->CAN bridge controller Signed-off-by: Klaus Goger Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3368-lion.dtsi | 317 ++++++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-lion.dtsi diff --git a/arch/arm64/boot/dts/rockchip/rk3368-lion.dtsi b/arch/arm64/boot/dts/rockchip/rk3368-lion.dtsi new file mode 100644 index 000000000000..1315972412df --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3368-lion.dtsi @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2018 Theobroma Systems Design und Consulting GmbH + */ + +/dts-v1/; +#include "rk3368.dtsi" + +/ { + chosen { + stdout-path = "serial0:115200n8"; + }; + + ext_gmac: gmac-clk { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "ext_gmac"; + #clock-cells = <0>; + }; + + i2cmux1 { + compatible = "i2c-mux-gpio"; + #address-cells = <1>; + #size-cells = <0>; + i2c-parent = <&i2c1>; + mux-gpios = <&gpio1 RK_PA7 GPIO_ACTIVE_HIGH>; + + /* Q7_GPO_I2C */ + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + /* Q7_SMB */ + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + i2cmux2 { + compatible = "i2c-mux-gpio"; + #address-cells = <1>; + #size-cells = <0>; + i2c-parent = <&i2c2>; + mux-gpios = <&gpio1 RK_PB4 GPIO_ACTIVE_HIGH>; + + /* Q7_LVDS_BLC_I2C */ + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + fan: fan@18 { + compatible = "ti,amc6821"; + reg = <0x18>; + cooling-min-state = <0>; + cooling-max-state = <9>; + #cooling-cells = <2>; + }; + + rtc_twi: rtc@6f { + compatible = "isil,isl1208"; + reg = <0x6f>; + }; + }; + + /* Q7_GP2_I2C */ + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&led_pins_module>; + + module_led1 { + label = "module_led1"; + gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + panic-indicator; + }; + + module_led2 { + label = "module_led2"; + gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; + + vcc_sys: vcc-sys-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + }; +}; + +&cpu_l0 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu_l1 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu_l2 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu_l3 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu_b0 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu_b1 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu_b2 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu_b3 { + cpu-supply = <&vdd_cpu>; +}; + +&emmc { + bus-width = <8>; + clock-frequency = <150000000>; + disable-wp; + mmc-hs200-1_8v; + non-removable; + vmmc-supply = <&vcc33_io>; + vqmmc-supply = <&vcc18_io>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk>, <&emmc_cmd>, <&emmc_bus8>; + status = "okay"; +}; + +&gmac { + assigned-clocks = <&cru SCLK_MAC>; + assigned-clock-parents = <&ext_gmac>; + clock_in_out = "input"; + phy-supply = <&vcc33_io>; + phy-mode = "rgmii"; + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_pins>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 50000>; + snps,reset-gpio = <&gpio3 RK_PB3 GPIO_ACTIVE_HIGH>; + tx_delay = <0x10>; + rx_delay = <0x10>; + status = "okay"; +}; + +&i2c0 { + status = "okay"; + + rk808: pmic@1b { + compatible = "rockchip,rk808"; + reg = <0x1b>; + interrupt-parent = <&gpio0>; + interrupts = ; + clock-output-names = "xin32k", "rk808-clkout2"; + #clock-cells = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>, <&pmic_sleep>; + rockchip,system-power-controller; + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc6-supply = <&vcc_sys>; + vcc7-supply = <&vcc_sys>; + vcc8-supply = <&vcc_sys>; + vcc9-supply = <&vcc_sys>; + vcc10-supply = <&vcc_sys>; + vcc11-supply = <&vcc_sys>; + vcc12-supply = <&vcc_sys>; + + regulators { + vdd_cpu: DCDC_REG1 { + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_log: DCDC_REG2 { + regulator-name = "vdd_log"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + }; + + vcc_ddr: DCDC_REG3 { + regulator-name = "vcc_ddr"; + regulator-always-on; + regulator-boot-on; + }; + + vcc33_io: DCDC_REG4 { + regulator-name = "vcc33_io"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + vcc33_video: LDO_REG2 { + regulator-name = "vcc33_video"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd10_pll: LDO_REG3 { + regulator-name = "vdd10_pll"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + }; + + vcc18_io: LDO_REG4 { + regulator-name = "vcc18_io"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + }; + + vdd10_video: LDO_REG6 { + regulator-name = "vdd10_video"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + }; + + vcc18_video: LDO_REG8 { + regulator-name = "vcc18_video"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&pinctrl { + leds { + led_pins_module: led-module-gpio { + rockchip,pins = + , + ; + }; + }; + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = ; + }; + + pmic_sleep: pmic-sleep { + rockchip,pins = ; + }; + }; +}; + +&spi1 { + status = "okay"; + + norflash: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <50000000>; + }; +}; + +&uart1 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&wdt { + status = "okay"; +}; -- cgit v1.3-14-g43fede From 0aaf2359593f69f0ac4b5fc5e14fa2219698a0d5 Mon Sep 17 00:00:00 2001 From: Klaus Goger Date: Tue, 30 Jan 2018 13:39:30 +0100 Subject: arm64: dts: rockchip: add Haikou baseboard with RK3368-uQ7 SoM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Haikou is a Qseven and μQseven baseboard used in Theobroma Systems evaluation kits. This dts adds a version for use with a RK3368-uQ7 SoM called Lion. Signed-off-by: Klaus Goger Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/Makefile | 1 + .../arm64/boot/dts/rockchip/rk3368-lion-haikou.dts | 146 +++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-lion-haikou.dts diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile index cf14b8679eac..1e57f87f30c4 100644 --- a/arch/arm64/boot/dts/rockchip/Makefile +++ b/arch/arm64/boot/dts/rockchip/Makefile @@ -4,6 +4,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-rock64.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3328-roc-cc.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-lion-haikou.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68-meta.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-px5-evb.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb diff --git a/arch/arm64/boot/dts/rockchip/rk3368-lion-haikou.dts b/arch/arm64/boot/dts/rockchip/rk3368-lion-haikou.dts new file mode 100644 index 000000000000..fca8e87d8f52 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3368-lion-haikou.dts @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2018 Theobroma Systems Design und Consulting GmbH + */ + +/dts-v1/; +#include "rk3368-lion.dtsi" + +/ { + model = "Theobroma Systems RK3368-uQ7 Baseboard"; + compatible = "tsd,rk3368-lion-haikou", "rockchip,rk3368"; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + i2cmux2 { + i2c@0 { + eeprom: eeprom@50 { + compatible = "atmel,24c01"; + pagesize = <8>; + reg = <0x50>; + }; + }; + }; + + leds { + pinctrl-0 = <&led_pins_module>, <&led_sd_haikou>; + + sd-card-led { + label = "sd_card_led"; + gpios = <&gpio0 RK_PD2 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + }; + }; + + dc_12v: dc-12v { + compatible = "regulator-fixed"; + regulator-name = "dc_12v"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + }; + + vcc3v3_baseboard: vcc3v3-baseboard { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_baseboard"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&dc_12v>; + }; + + vcc5v0_otg: vcc5v0-otg-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 RK_PD4 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&otg_vbus_drv>; + regulator-name = "vcc5v0_otg"; + regulator-always-on; + }; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + cd-gpios = <&gpio2 RK_PB3 GPIO_ACTIVE_LOW>; + disable-wp; + max-frequency = <25000000>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_bus4>; + rockchip,default-sample-phase = <90>; + vmmc-supply = <&vcc3v3_baseboard>; + status = "okay"; +}; + +&spi2 { + cs-gpios = <0>, <&gpio2 RK_PC3 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; + status = "okay"; +}; + +&usb_otg { + dr_mode = "otg"; + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; + status = "okay"; +}; + +&uart1 { + /* alternate function of GPIO5/6 */ + status = "disabled"; +}; + +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&haikou_pin_hog>; + + hog { + haikou_pin_hog: haikou-pin-hog { + rockchip,pins = + /* LID_BTN */ + , + /* BATLOW# */ + , + /* SLP_BTN# */ + , + /* BIOS_DISABLE# */ + ; + }; + }; + + leds { + led_sd_haikou: led-sd-gpio { + rockchip,pins = + ; + }; + }; + + sdmmc { + sdmmc_cd_gpio: sdmmc-cd-gpio { + rockchip,pins = + ; + }; + }; + + usb_otg { + otg_vbus_drv: otg-vbus-drv { + rockchip,pins = + ; + }; + }; +}; -- cgit v1.3-14-g43fede From 1689dbe7b52f0b7c270ce813711e0e27e6f7329c Mon Sep 17 00:00:00 2001 From: John Keeping Date: Sun, 18 Feb 2018 17:03:34 +0000 Subject: ARM: dts: rockchip: add power key for Rock2 Square Support the power button on Radxa Rock2 Square. Signed-off-by: John Keeping Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3288-rock2-square.dts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/rk3288-rock2-square.dts b/arch/arm/boot/dts/rk3288-rock2-square.dts index 0e084b8a86ac..6adeb331452f 100644 --- a/arch/arm/boot/dts/rk3288-rock2-square.dts +++ b/arch/arm/boot/dts/rk3288-rock2-square.dts @@ -39,6 +39,7 @@ */ /dts-v1/; +#include #include "rk3288-rock2-som.dtsi" / { @@ -49,6 +50,19 @@ stdout-path = "serial2:115200n8"; }; + gpio-keys { + compatible = "gpio-keys"; + + power { + gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>; + label = "GPIO Power"; + linux,code = ; + pinctrl-names = "default"; + pinctrl-0 = <&pwr_key>; + wakeup-source; + }; + }; + gpio-leds { compatible = "gpio-leds"; @@ -220,6 +234,12 @@ }; }; + keys { + pwr_key: pwr-key { + rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + pmic { pmic_int: pmic-int { rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_up>; -- cgit v1.3-14-g43fede From 83454312f72ea310c315117dad27e5f0c0b05c30 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Sun, 18 Feb 2018 17:03:35 +0000 Subject: ARM: dts: rockchip: add recovery button for Rock2 Square The recovery button is connected to ADC1. This is the same setup as the Firefly board, but for Rock2 the power supply is connected on the module and all of the inputs are wired up to the edge connector, so use of the ADC depends on the carrier board. Signed-off-by: John Keeping Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3288-rock2-som.dtsi | 4 ++++ arch/arm/boot/dts/rk3288-rock2-square.dts | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/arch/arm/boot/dts/rk3288-rock2-som.dtsi b/arch/arm/boot/dts/rk3288-rock2-som.dtsi index b9c471fcbd42..51f36a1b698e 100644 --- a/arch/arm/boot/dts/rk3288-rock2-som.dtsi +++ b/arch/arm/boot/dts/rk3288-rock2-som.dtsi @@ -280,6 +280,10 @@ }; }; +&saradc { + vref-supply = <&vcc_18>; +}; + &tsadc { rockchip,hw-tshut-mode = <0>; /* tshut mode 0:CRU 1:GPIO */ rockchip,hw-tshut-polarity = <0>; /* tshut polarity 0:LOW 1:HIGH */ diff --git a/arch/arm/boot/dts/rk3288-rock2-square.dts b/arch/arm/boot/dts/rk3288-rock2-square.dts index 6adeb331452f..0ea2a3a1fa51 100644 --- a/arch/arm/boot/dts/rk3288-rock2-square.dts +++ b/arch/arm/boot/dts/rk3288-rock2-square.dts @@ -50,6 +50,19 @@ stdout-path = "serial2:115200n8"; }; + adc-keys { + compatible = "adc-keys"; + io-channels = <&saradc 1>; + io-channel-names = "buttons"; + keyup-threshold-microvolt = <1800000>; + + button-recovery { + label = "Recovery"; + linux,code = ; + press-threshold-microvolt = <0>; + }; + }; + gpio-keys { compatible = "gpio-keys"; @@ -281,6 +294,10 @@ }; }; +&saradc { + status = "okay"; +}; + &spdif { status = "okay"; }; -- cgit v1.3-14-g43fede From 8dc9b9720380a3c16969932151ba92b558110b2f Mon Sep 17 00:00:00 2001 From: John Keeping Date: Sun, 18 Feb 2018 17:03:36 +0000 Subject: ARM: dts: rockchip: enable USB-OTG port on Radxa Rock2 Square This allows the USB gadget framework to be used on Rock2 Square. Signed-off-by: John Keeping Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3288-rock2-square.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/rk3288-rock2-square.dts b/arch/arm/boot/dts/rk3288-rock2-square.dts index 0ea2a3a1fa51..8ccc89dbdfaf 100644 --- a/arch/arm/boot/dts/rk3288-rock2-square.dts +++ b/arch/arm/boot/dts/rk3288-rock2-square.dts @@ -321,3 +321,7 @@ &usb_host1 { status = "okay"; }; + +&usb_otg { + status = "okay"; +}; -- cgit v1.3-14-g43fede From 51923db733659b9cf0f68f23ab89d5ab3027f761 Mon Sep 17 00:00:00 2001 From: Vicente Bergas Date: Sun, 18 Feb 2018 16:43:59 +0100 Subject: arm64: dts: rockchip: vdd_log on rk3399-sapphire is not an i2c slave The vdd_log power supply is controlled by a PWM pin, not by i2c register access. There is a boot message that reports an error about not being able to bring that supply up. Signed-off-by: Vicente Bergas Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi index 0f873c897d0d..46c95ccfd5cb 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi @@ -143,6 +143,17 @@ regulator-always-on; vin-supply = <&vcc_sys>; }; + + vdd_log: vdd-log { + compatible = "pwm-regulator"; + pwms = <&pwm2 0 25000 1>; + regulator-name = "vdd_log"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1400000>; + vin-supply = <&vcc_sys>; + }; }; &cpu_l0 { @@ -421,17 +432,6 @@ regulator-off-in-suspend; }; }; - - vdd_log: vdd-log { - compatible = "pwm-regulator"; - pwms = <&pwm2 0 25000 1>; - regulator-name = "vdd_log"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <1400000>; - vin-supply = <&vcc_sys>; - }; }; &i2c3 { -- cgit v1.3-14-g43fede From 75b310b770d631bea4bd00f9b8d3a8c902342fe2 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 12 Feb 2018 18:13:20 +1030 Subject: ARM: dts: aspeed: Add IPMI BT node The IPMI BT device part of the LPC interface and is used for communication with the host processor. Acked-by: Andrew Jeffery Signed-off-by: Joel Stanley --- arch/arm/boot/dts/aspeed-g4.dtsi | 7 +++++++ arch/arm/boot/dts/aspeed-g5.dtsi | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi index b0d8431a3700..8b18176dca59 100644 --- a/arch/arm/boot/dts/aspeed-g4.dtsi +++ b/arch/arm/boot/dts/aspeed-g4.dtsi @@ -247,6 +247,13 @@ compatible = "aspeed,ast2400-lhc"; reg = <0x20 0x24 0x48 0x8>; }; + + ibt: ibt@c0 { + compatible = "aspeed,ast2400-ibt-bmc"; + reg = <0xc0 0x18>; + interrupts = <8>; + status = "disabled"; + }; }; }; diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index 40de3b66c33f..18185484a224 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -298,6 +298,13 @@ compatible = "aspeed,ast2500-lhc"; reg = <0x20 0x24 0x48 0x8>; }; + + ibt: ibt@c0 { + compatible = "aspeed,ast2500-ibt-bmc"; + reg = <0xc0 0x18>; + interrupts = <8>; + status = "disabled"; + }; }; }; -- cgit v1.3-14-g43fede From 347328110eabec0b76a9b76d4a43fbae62930bc0 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 12 Feb 2018 18:13:21 +1030 Subject: ARM: dts: aspeed: Enable IPMI BT node on OpenPower machines These BMC systems require this device to communicate with the host. Acked-by: Andrew Jeffery Signed-off-by: Joel Stanley --- arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 4 ++++ arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts | 4 ++++ arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 4 ++++ arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts index 4379d09a261f..5115247e5e59 100644 --- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts +++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts @@ -111,3 +111,7 @@ &vuart { status = "okay"; }; + +&ibt { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts b/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts index 623b6ab42021..d41be52c2393 100644 --- a/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts +++ b/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts @@ -257,3 +257,7 @@ aspeed,fan-tach-ch = /bits/ 8 <0x0e>; }; }; + +&ibt { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts index 5f9049d2c4c3..7056231cbee6 100644 --- a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts +++ b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts @@ -546,3 +546,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_wdtrst1_default>; }; + +&ibt { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts index c881484a85cf..ebe726a0d311 100644 --- a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts +++ b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts @@ -424,3 +424,7 @@ aspeed,fan-tach-ch = /bits/ 8 <0x03>; }; }; + +&ibt { + status = "okay"; +}; -- cgit v1.3-14-g43fede From 542d2f4588ae651c4c1c59a5ca9ac731fe06c881 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 12 Feb 2018 18:13:22 +1030 Subject: ARM: dts: aspeed-g5: Update LPC node This addresses some differences between the G5 and G4 LPC nodes that make them hard to compare. There is no functional change. Reviewed-by: Andrew Jeffery Signed-off-by: Joel Stanley --- arch/arm/boot/dts/aspeed-g5.dtsi | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index 18185484a224..e0b6803f6845 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -264,7 +264,7 @@ #address-cells = <1>; #size-cells = <1>; - ranges = <0 0x1e789000 0x1000>; + ranges = <0x0 0x1e789000 0x1000>; lpc_bmc: lpc-bmc@0 { compatible = "aspeed,ast2500-lpc-bmc"; @@ -274,12 +274,11 @@ lpc_host: lpc-host@80 { compatible = "aspeed,ast2500-lpc-host", "simple-mfd", "syscon"; reg = <0x80 0x1e0>; + reg-io-width = <4>; #address-cells = <1>; #size-cells = <1>; - ranges = <0 0x80 0x1e0>; - - reg-io-width = <4>; + ranges = <0x0 0x80 0x1e0>; lpc_ctrl: lpc-ctrl@0 { compatible = "aspeed,ast2500-lpc-ctrl"; -- cgit v1.3-14-g43fede From 7674bf96b82e22950c00151d4a9f45e0bc988698 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 12 Feb 2018 18:13:23 +1030 Subject: ARM: dts: aspeed: Add LPC clock phandles The LPC device uses LCLK. Tested-by: Lei YU Reviewed-by: Andrew Jeffery Signed-off-by: Joel Stanley --- arch/arm/boot/dts/aspeed-g4.dtsi | 1 + arch/arm/boot/dts/aspeed-g5.dtsi | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi index 8b18176dca59..48c28a71ae7e 100644 --- a/arch/arm/boot/dts/aspeed-g4.dtsi +++ b/arch/arm/boot/dts/aspeed-g4.dtsi @@ -233,6 +233,7 @@ lpc_ctrl: lpc-ctrl@0 { compatible = "aspeed,ast2400-lpc-ctrl"; reg = <0x0 0x80>; + clocks = <&syscon ASPEED_CLK_GATE_LCLK>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index e0b6803f6845..8eac57c33880 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -283,6 +283,7 @@ lpc_ctrl: lpc-ctrl@0 { compatible = "aspeed,ast2500-lpc-ctrl"; reg = <0x0 0x80>; + clocks = <&syscon ASPEED_CLK_GATE_LCLK>; status = "disabled"; }; -- cgit v1.3-14-g43fede From 65a33f05384274de27754b7e230f8b314c231f06 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 12 Feb 2018 18:13:24 +1030 Subject: ARM: dts: palmetto: Add LEDs and GPIO keys These describe the front panel LEDs that are present on a Palmetto chassis, and the checkstop GPIO that comes from the Power8 CPU to indicate a host error. Acked-by: Andrew Jeffery Signed-off-by: Joel Stanley --- arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts index 5115247e5e59..2a4a355a779d 100644 --- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts +++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts @@ -2,6 +2,7 @@ /dts-v1/; #include "aspeed-g4.dtsi" +#include / { model = "Palmetto BMC"; @@ -26,6 +27,32 @@ reg = <0x5f000000 0x01000000>; /* 16M */ }; }; + + leds { + compatible = "gpio-leds"; + + heartbeat { + gpios = <&gpio ASPEED_GPIO(R, 4) GPIO_ACTIVE_LOW>; + }; + + power { + gpios = <&gpio ASPEED_GPIO(R, 5) GPIO_ACTIVE_LOW>; + }; + + identify { + gpios = <&gpio ASPEED_GPIO(A, 2) GPIO_ACTIVE_LOW>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + checkstop { + label = "checkstop"; + gpios = <&gpio ASPEED_GPIO(P, 5) GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; }; &fmc { -- cgit v1.3-14-g43fede From f4c37354565d347575254d3775ae1aaccb05dbbe Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 12 Feb 2018 18:13:25 +1030 Subject: ARM: dts: aspeed: Add Palmetto GPIO hogs These hogs are for parts of the system that need to be in this state, but do not yet have a driver associated with them but they must be configured in order to successfully boot the host. There are also some pinmux hogs, where the default mode of the IP block is configured. Acked-by: Andrew Jeffery Signed-off-by: Joel Stanley --- arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 157 ++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts index 2a4a355a779d..bbddfe1d1cc8 100644 --- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts +++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts @@ -74,6 +74,14 @@ }; }; +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flbusy_default &pinctrl_flwp_default + + &pinctrl_vgahs_default &pinctrl_vgavs_default + &pinctrl_ddcclk_default &pinctrl_ddcdat_default>; +}; + &uart5 { status = "okay"; }; @@ -142,3 +150,152 @@ &ibt { status = "okay"; }; + +&gpio { + pin_func_mode0 { + gpio-hog; + gpios = ; + output-low; + line-name = "func_mode0"; + }; + + pin_func_mode1 { + gpio-hog; + gpios = ; + output-low; + line-name = "func_mode1"; + }; + + pin_func_mode2 { + gpio-hog; + gpios = ; + output-low; + line-name = "func_mode2"; + }; + + pin_gpio_a0 { + gpio-hog; + gpios = ; + input; + line-name = "BMC_FAN_RESERVED_N"; + }; + + pin_gpio_a1 { + gpio-hog; + gpios = ; + output-high; + line-name = "APSS_WDT_N"; + }; + + pin_gpio_b1 { + gpio-hog; + gpios = ; + output-high; + line-name = "APSS_BOOT_MODE"; + }; + + pin_gpio_b2 { + gpio-hog; + gpios = ; + output-high; + line-name = "APSS_RESET_N"; + }; + + pin_gpio_b7 { + gpio-hog; + gpios = ; + output-high; + line-name = "SPIVID_STBY_RESET_N"; + }; + + pin_gpio_d1 { + gpio-hog; + gpios = ; + output-high; + line-name = "BMC_POWER_UP"; + }; + + pin_gpio_f1 { + gpio-hog; + gpios = ; + input; + line-name = "BMC_BATTERY_TEST"; + }; + + pin_gpio_f4 { + gpio-hog; + gpios = ; + input; + line-name = "AST_HW_FAULT_N"; + }; + + pin_gpio_f5 { + gpio-hog; + gpios = ; + input; + line-name = "AST_SYS_FAULT_N"; + }; + + pin_gpio_f7 { + gpio-hog; + gpios = ; + output-high; + line-name = "BMC_FULL_SPEED_N"; + }; + + pin_gpio_g3 { + gpio-hog; + gpios = ; + output-high; + line-name = "BMC_FAN_ERROR_N"; + }; + + pin_gpio_g4 { + gpio-hog; + gpios = ; + input; + line-name = "BMC_WDT_RST1_P"; + }; + + pin_gpio_g5 { + gpio-hog; + gpios = ; + input; + line-name = "BMC_WDT_RST2_P"; + }; + + pin_gpio_h0 { + gpio-hog; + gpios = ; + input; + line-name = "PE_SLOT_TEST_EN_N"; + }; + + pin_gpio_h1 { + gpio-hog; + gpios = ; + input; + line-name = "BMC_RTCRST_N"; + }; + + pin_gpio_h2 { + gpio-hog; + gpios = ; + output-high; + line-name = "SYS_PWROK_BMC"; + }; + + pin_gpio_h6 { + gpio-hog; + gpios = ; + output-high; + line-name = "SCM1_FSI0_DATA_EN"; + }; + + pin_gpio_h7 { + gpio-hog; + gpios = ; + output-high; + line-name = "BMC_TPM_INT_N"; + }; +}; -- cgit v1.3-14-g43fede From e3dd25ae0a910b77d049a8cda586a17d8661689d Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 16 Feb 2018 15:52:09 -0500 Subject: ARM: dts: sun7i: Enable HDMI support on the Orange Pi mini Enable the display pipeline and HDMI output for the Orange Pi mini Signed-off-by: Stefan Monnier Signed-off-by: Maxime Ripard --- arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts b/arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts index 7af4c8fc1865..ce02ab532bc8 100644 --- a/arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts +++ b/arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts @@ -61,6 +61,17 @@ stdout-path = "serial0:115200n8"; }; + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + leds { compatible = "gpio-leds"; pinctrl-names = "default"; @@ -98,6 +109,10 @@ status = "okay"; }; +&de { + status = "okay"; +}; + &ehci0 { status = "okay"; }; @@ -119,6 +134,16 @@ }; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &i2c0 { pinctrl-names = "default"; pinctrl-0 = <&i2c0_pins_a>; -- cgit v1.3-14-g43fede From 139eabece9a66cb6ae694369cfa4c64badbf994a Mon Sep 17 00:00:00 2001 From: Klaus Goger Date: Mon, 19 Feb 2018 09:18:53 +0100 Subject: arm64: dts: rockchip: move i2s0 node from baseboard to SoM on rk3399-puma The I2S definition is part of the SoM and therefore should be in rk3399-puma.dtsi. Also correct the number of channels available. Signed-off-by: Klaus Goger Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts | 8 -------- arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts index 9a7486058455..8fd0d7ba1dc9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts @@ -117,14 +117,6 @@ clock-frequency = <400000>; }; -&i2s0 { - status = "okay"; - rockchip,playback-channels = <8>; - rockchip,capture-channels = <8>; - #sound-dai-cells = <0>; - status = "okay"; -}; - &pcie_phy { status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi index ce15a57a5b85..fc913e2d962f 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi @@ -435,6 +435,14 @@ }; }; +&i2s0 { + pinctrl-0 = <&i2s0_2ch_bus>; + rockchip,playback-channels = <2>; + rockchip,capture-channels = <2>; + #sound-dai-cells = <0>; + status = "okay"; +}; + &io_domains { status = "okay"; bt656-supply = <&vcc_1v8>; -- cgit v1.3-14-g43fede From d95ed4308e2f5709193c612c4c06021afdb58975 Mon Sep 17 00:00:00 2001 From: Klaus Goger Date: Mon, 19 Feb 2018 09:18:54 +0100 Subject: arm64: dts: rockchip: enable I2S codec on rk3399-puma-haikou Enable the NXP SGTL5000 audio codec on the RK3399-Q7 EVK baseboard Haikou. Signed-off-by: Klaus Goger Signed-off-by: Heiko Stuebner --- .../arm64/boot/dts/rockchip/rk3399-puma-haikou.dts | 63 ++++++++++++++++++++++ arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi | 14 +++++ 2 files changed, 77 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts index 8fd0d7ba1dc9..7d3e8bfd51dd 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts @@ -61,6 +61,30 @@ }; }; + i2s0-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "Haikou,I2S-codec"; + simple-audio-card,mclk-fs = <512>; + + simple-audio-card,codec { + clocks = <&sgtl5000_clk>; + sound-dai = <&sgtl5000>; + }; + + simple-audio-card,cpu { + bitclock-master; + frame-master; + sound-dai = <&i2s0>; + }; + }; + + sgtl5000_clk: sgtl5000-oscillator { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + }; + dc_12v: dc-12v { compatible = "regulator-fixed"; regulator-name = "dc_12v"; @@ -80,6 +104,16 @@ vin-supply = <&dc_12v>; }; + vcc5v0_baseboard: vcc5v0-baseboard { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_baseboard"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&dc_12v>; + }; + vcc5v0_otg: vcc5v0-otg-regulator { compatible = "regulator-fixed"; enable-active-high; @@ -89,6 +123,24 @@ regulator-name = "vcc5v0_otg"; regulator-always-on; }; + + vdda_codec: vdda-codec { + compatible = "regulator-fixed"; + regulator-name = "vdda_codec"; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc5v0_baseboard>; + }; + + vddd_codec: vddd-codec { + compatible = "regulator-fixed"; + regulator-name = "vddd_codec"; + regulator-boot-on; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1600000>; + vin-supply = <&vcc5v0_baseboard>; + }; }; &i2c1 { @@ -110,6 +162,17 @@ &i2c4 { status = "okay"; clock-frequency = <400000>; + + sgtl5000: codec@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + clocks = <&sgtl5000_clk>; + #sound-dai-cells = <0>; + VDDA-supply = <&vdda_codec>; + VDDIO-supply = <&vdda_codec>; + VDDD-supply = <&vddd_codec>; + status = "okay"; + }; }; &i2c6 { diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi index fc913e2d962f..4a2d06abe9c1 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi @@ -443,6 +443,20 @@ status = "okay"; }; +/* + * As Q7 does not specify neither a global nor a RX clock for I2S these + * signals are not used. Furthermore I2S0_LRCK_RX is used as GPIO. + * Therefore we have to redefine the i2s0_2ch_bus definition to prevent + * conflicts. + */ +&i2s0_2ch_bus { + rockchip,pins = + , + , + , + ; +}; + &io_domains { status = "okay"; bt656-supply = <&vcc_1v8>; -- cgit v1.3-14-g43fede From 40d5c8e94751f4a4fa4e0e27e3805e201a4e79c0 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 16 Feb 2018 23:09:48 +0300 Subject: soc: renesas: r8a77970-sysc: fix power area parents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the figure 9.2(b) of the R-Car Series, 3rd Generation User’s Manual: Hardware Rev. 0.80 the A2IRn and A2SCn power areas in R8A77970 have the A3IR area as a parent, thus the SYSC driver has those parents wrong... Fixes: bab9b2a74fe9 ("soc: renesas: rcar-sysc: add R8A77970 support") Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/r8a77970-sysc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/soc/renesas/r8a77970-sysc.c b/drivers/soc/renesas/r8a77970-sysc.c index 8c614164718e..caf894f193ed 100644 --- a/drivers/soc/renesas/r8a77970-sysc.c +++ b/drivers/soc/renesas/r8a77970-sysc.c @@ -25,12 +25,12 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = { PD_CPU_NOCR }, { "cr7", 0x240, 0, R8A77970_PD_CR7, R8A77970_PD_ALWAYS_ON }, { "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON }, - { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_ALWAYS_ON }, - { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A2IR0 }, - { "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A2IR0 }, - { "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A2IR0 }, - { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_ALWAYS_ON }, - { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A2SC0 }, + { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_A3IR }, + { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A3IR }, + { "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A3IR }, + { "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A3IR }, + { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_A3IR }, + { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A3IR }, }; const struct rcar_sysc_info r8a77970_sysc_info __initconst = { -- cgit v1.3-14-g43fede From bdec5a6b57896da81bc47262868468717a06bb69 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 17 Feb 2018 21:22:24 -0600 Subject: ARM: da8xx: use platform data for CFGCHIP syscon regmap This converts from using a platform device for the CFGCHIP syscon regmap to using platform data to pass the regmap to consumers. A lazy getter function is used so that the regmap will only be created if it is actually used. This function will also be used in the clock init when we convert to the common clock framework. The USB PHY driver is currently the only consumer. This driver is updated to use platform data to get the CFGCHIP regmap instead of syscon_regmap_lookup_by_pdevname(). Signed-off-by: David Lechner Acked-by: Kishon Vijay Abraham I Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da830-evm.c | 4 --- arch/arm/mach-davinci/board-da850-evm.c | 4 --- arch/arm/mach-davinci/board-mityomapl138.c | 4 --- arch/arm/mach-davinci/board-omapl138-hawk.c | 4 --- arch/arm/mach-davinci/devices-da8xx.c | 45 +++++++++++++++-------------- arch/arm/mach-davinci/include/mach/da8xx.h | 3 +- arch/arm/mach-davinci/usb-da8xx.c | 6 ++++ drivers/phy/ti/phy-da8xx-usb.c | 8 +++-- include/linux/platform_data/phy-da8xx-usb.h | 21 ++++++++++++++ 9 files changed, 58 insertions(+), 41 deletions(-) create mode 100644 include/linux/platform_data/phy-da8xx-usb.h diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index f673cd7a6766..f960cbef6538 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -551,10 +551,6 @@ static __init void da830_evm_init(void) struct davinci_soc_info *soc_info = &davinci_soc_info; int ret; - ret = da8xx_register_cfgchip(); - if (ret) - pr_warn("%s: CFGCHIP registration failed: %d\n", __func__, ret); - ret = da830_register_gpio(); if (ret) pr_warn("%s: GPIO init failed: %d\n", __func__, ret); diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index d898a94f6eae..26bdb10a8927 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1334,10 +1334,6 @@ static __init void da850_evm_init(void) { int ret; - ret = da8xx_register_cfgchip(); - if (ret) - pr_warn("%s: CFGCHIP registration failed: %d\n", __func__, ret); - ret = da850_register_gpio(); if (ret) pr_warn("%s: GPIO init failed: %d\n", __func__, ret); diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index b73ce7bae81f..9e7388ba413c 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -502,10 +502,6 @@ static void __init mityomapl138_init(void) { int ret; - ret = da8xx_register_cfgchip(); - if (ret) - pr_warn("%s: CFGCHIP registration failed: %d\n", __func__, ret); - /* for now, no special EDMA channels are reserved */ ret = da850_register_edma(NULL); if (ret) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index a3e78074be70..baab7eb61632 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -281,10 +281,6 @@ static __init void omapl138_hawk_init(void) { int ret; - ret = da8xx_register_cfgchip(); - if (ret) - pr_warn("%s: CFGCHIP registration failed: %d\n", __func__, ret); - ret = da850_register_gpio(); if (ret) pr_warn("%s: GPIO init failed: %d\n", __func__, ret); diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index e1c40e73d30a..166bf29b1296 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -11,7 +11,6 @@ * (at your option) any later version. */ #include -#include #include #include #include @@ -1118,29 +1117,33 @@ int __init da850_register_sata(unsigned long refclkpn) } #endif -static struct syscon_platform_data da8xx_cfgchip_platform_data = { - .label = "cfgchip", -}; +static struct regmap *da8xx_cfgchip; -static struct resource da8xx_cfgchip_resources[] = { - { - .start = DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP0_REG, - .end = DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP4_REG + 3, - .flags = IORESOURCE_MEM, - }, -}; +/* regmap doesn't make a copy of this, so we need to keep the pointer around */ +static const char da8xx_cfgchip_name[] = "cfgchip"; -static struct platform_device da8xx_cfgchip_device = { - .name = "syscon", - .id = -1, - .dev = { - .platform_data = &da8xx_cfgchip_platform_data, - }, - .num_resources = ARRAY_SIZE(da8xx_cfgchip_resources), - .resource = da8xx_cfgchip_resources, +static const struct regmap_config da8xx_cfgchip_config __initconst = { + .name = da8xx_cfgchip_name, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = DA8XX_CFGCHIP4_REG - DA8XX_CFGCHIP0_REG, }; -int __init da8xx_register_cfgchip(void) +/** + * da8xx_get_cfgchip - Lazy gets CFGCHIP as regmap + * + * This is for use on non-DT boards only. For DT boards, use + * syscon_regmap_lookup_by_compatible("ti,da830-cfgchip") + * + * Returns: Pointer to the CFGCHIP regmap or negative error code. + */ +struct regmap * __init da8xx_get_cfgchip(void) { - return platform_device_register(&da8xx_cfgchip_device); + if (IS_ERR_OR_NULL(da8xx_cfgchip)) + da8xx_cfgchip = regmap_init_mmio(NULL, + DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP0_REG), + &da8xx_cfgchip_config); + + return da8xx_cfgchip; } diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 93ff1569cee5..03f37ef4297f 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -123,7 +124,7 @@ void da8xx_rproc_reserve_cma(void); int da8xx_register_rproc(void); int da850_register_gpio(void); int da830_register_gpio(void); -int da8xx_register_cfgchip(void); +struct regmap *da8xx_get_cfgchip(void); extern struct platform_device da8xx_serial_device[]; extern struct emac_platform_data da8xx_emac_pdata; diff --git a/arch/arm/mach-davinci/usb-da8xx.c b/arch/arm/mach-davinci/usb-da8xx.c index fb31f6eeba96..4d89d86ce7e5 100644 --- a/arch/arm/mach-davinci/usb-da8xx.c +++ b/arch/arm/mach-davinci/usb-da8xx.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,11 @@ static struct platform_device da8xx_usb_phy = { int __init da8xx_register_usb_phy(void) { + struct da8xx_usb_phy_platform_data pdata; + + pdata.cfgchip = da8xx_get_cfgchip(); + da8xx_usb_phy.dev.platform_data = &pdata; + return platform_device_register(&da8xx_usb_phy); } diff --git a/drivers/phy/ti/phy-da8xx-usb.c b/drivers/phy/ti/phy-da8xx-usb.c index 5bd33d06df95..befb886ff121 100644 --- a/drivers/phy/ti/phy-da8xx-usb.c +++ b/drivers/phy/ti/phy-da8xx-usb.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -145,6 +146,7 @@ static struct phy *da8xx_usb_phy_of_xlate(struct device *dev, static int da8xx_usb_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct da8xx_usb_phy_platform_data *pdata = dev->platform_data; struct device_node *node = dev->of_node; struct da8xx_usb_phy *d_phy; @@ -152,11 +154,11 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev) if (!d_phy) return -ENOMEM; - if (node) + if (pdata) + d_phy->regmap = pdata->cfgchip; + else d_phy->regmap = syscon_regmap_lookup_by_compatible( "ti,da830-cfgchip"); - else - d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon"); if (IS_ERR(d_phy->regmap)) { dev_err(dev, "Failed to get syscon\n"); return PTR_ERR(d_phy->regmap); diff --git a/include/linux/platform_data/phy-da8xx-usb.h b/include/linux/platform_data/phy-da8xx-usb.h new file mode 100644 index 000000000000..85c2b99381b2 --- /dev/null +++ b/include/linux/platform_data/phy-da8xx-usb.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver + * + * Copyright (C) 2018 David Lechner + */ + +#ifndef __LINUX_PLATFORM_DATA_PHY_DA8XX_USB_H__ +#define __LINUX_PLATFORM_DATA_PHY_DA8XX_USB_H__ + +#include + +/** + * da8xx_usb_phy_platform_data + * @cfgchip: CFGCHIP syscon regmap + */ +struct da8xx_usb_phy_platform_data { + struct regmap *cfgchip; +}; + +#endif /* __LINUX_PLATFORM_DATA_PHY_DA8XX_USB_H__ */ -- cgit v1.3-14-g43fede From 94f2e94514e5dea6677b4379efd3479105519bdc Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 6 Jan 2018 21:10:15 -0600 Subject: ARM: davinci: remove watchdog reset This removes the watchdog reset code. The reset has been moved to drivers/watchdog/davinci_wdt.c. The watchdog driver registers the reset with the kernel so defining a reset for each machine is no longer needed. Signed-off-by: David Lechner Acked-by: Guenter Roeck Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da830-evm.c | 1 - arch/arm/mach-davinci/board-da850-evm.c | 1 - arch/arm/mach-davinci/board-dm355-evm.c | 1 - arch/arm/mach-davinci/board-dm355-leopard.c | 1 - arch/arm/mach-davinci/board-dm365-evm.c | 1 - arch/arm/mach-davinci/board-dm644x-evm.c | 1 - arch/arm/mach-davinci/board-dm646x-evm.c | 2 - arch/arm/mach-davinci/board-mityomapl138.c | 1 - arch/arm/mach-davinci/board-neuros-osd2.c | 1 - arch/arm/mach-davinci/board-omapl138-hawk.c | 1 - arch/arm/mach-davinci/board-sffsdr.c | 1 - arch/arm/mach-davinci/clock.h | 3 -- arch/arm/mach-davinci/da8xx-dt.c | 1 - arch/arm/mach-davinci/devices-da8xx.c | 13 ------- arch/arm/mach-davinci/devices.c | 7 +--- arch/arm/mach-davinci/include/mach/common.h | 1 - arch/arm/mach-davinci/include/mach/da8xx.h | 1 - arch/arm/mach-davinci/time.c | 57 ----------------------------- 18 files changed, 1 insertion(+), 94 deletions(-) diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index f960cbef6538..d3ee02c12014 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -638,5 +638,4 @@ MACHINE_START(DAVINCI_DA830_EVM, "DaVinci DA830/OMAP-L137/AM17x EVM") .init_machine = da830_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = da8xx_restart, MACHINE_END diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 26bdb10a8927..b3a764feae11 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1481,6 +1481,5 @@ MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") .init_machine = da850_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = da8xx_restart, .reserve = da8xx_rproc_reserve_cma, MACHINE_END diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c index e457f299cd44..29dbfc944e02 100644 --- a/arch/arm/mach-davinci/board-dm355-evm.c +++ b/arch/arm/mach-davinci/board-dm355-evm.c @@ -431,5 +431,4 @@ MACHINE_START(DAVINCI_DM355_EVM, "DaVinci DM355 EVM") .init_machine = dm355_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = davinci_restart, MACHINE_END diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c index be997243447b..1e7e9b8f3bae 100644 --- a/arch/arm/mach-davinci/board-dm355-leopard.c +++ b/arch/arm/mach-davinci/board-dm355-leopard.c @@ -275,5 +275,4 @@ MACHINE_START(DM355_LEOPARD, "DaVinci DM355 leopard") .init_machine = dm355_leopard_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = davinci_restart, MACHINE_END diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index e75741fb2c1d..17b2c29f4053 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -778,6 +778,5 @@ MACHINE_START(DAVINCI_DM365_EVM, "DaVinci DM365 EVM") .init_machine = dm365_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = davinci_restart, MACHINE_END diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 85e6fb33b1ee..35e1274183e1 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -832,5 +832,4 @@ MACHINE_START(DAVINCI_EVM, "DaVinci DM644x EVM") .init_machine = davinci_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = davinci_restart, MACHINE_END diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index cb0a41e83582..003bbe5c24fd 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -801,7 +801,6 @@ MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") .init_machine = evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = davinci_restart, MACHINE_END MACHINE_START(DAVINCI_DM6467TEVM, "DaVinci DM6467T EVM") @@ -812,6 +811,5 @@ MACHINE_START(DAVINCI_DM6467TEVM, "DaVinci DM6467T EVM") .init_machine = evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = davinci_restart, MACHINE_END diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index 9e7388ba413c..72748e5df43c 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -570,5 +570,4 @@ MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") .init_machine = mityomapl138_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = da8xx_restart, MACHINE_END diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c index 4da210a1a110..51a3ae19d765 100644 --- a/arch/arm/mach-davinci/board-neuros-osd2.c +++ b/arch/arm/mach-davinci/board-neuros-osd2.c @@ -231,5 +231,4 @@ MACHINE_START(NEUROS_OSD2, "Neuros OSD2") .init_machine = davinci_ntosd2_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = davinci_restart, MACHINE_END diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index baab7eb61632..ae37bc46bc8c 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -334,6 +334,5 @@ MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") .init_machine = omapl138_hawk_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = da8xx_restart, .reserve = da8xx_rproc_reserve_cma, MACHINE_END diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c index d85accf7f760..1f02d4ec6144 100644 --- a/arch/arm/mach-davinci/board-sffsdr.c +++ b/arch/arm/mach-davinci/board-sffsdr.c @@ -154,5 +154,4 @@ MACHINE_START(SFFSDR, "Lyrtech SFFSDR") .init_machine = davinci_sffsdr_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, - .restart = davinci_restart, MACHINE_END diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h index fa2b83752e03..d7894d5aaa25 100644 --- a/arch/arm/mach-davinci/clock.h +++ b/arch/arm/mach-davinci/clock.h @@ -135,9 +135,6 @@ int davinci_clk_reset(struct clk *clk, bool reset); void davinci_clk_enable(struct clk *clk); void davinci_clk_disable(struct clk *clk); -extern struct platform_device davinci_wdt_device; -extern void davinci_watchdog_reset(struct platform_device *); - #endif #endif diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c index f06db6700ab2..779e8ce887c3 100644 --- a/arch/arm/mach-davinci/da8xx-dt.c +++ b/arch/arm/mach-davinci/da8xx-dt.c @@ -100,7 +100,6 @@ DT_MACHINE_START(DA850_DT, "Generic DA850/OMAP-L138/AM18x") .init_machine = da850_init_machine, .dt_compat = da850_boards_compat, .init_late = davinci_init_late, - .restart = da8xx_restart, MACHINE_END #endif diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 166bf29b1296..24422ba07cc7 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -370,19 +370,6 @@ static struct platform_device da8xx_wdt_device = { .resource = da8xx_watchdog_resources, }; -void da8xx_restart(enum reboot_mode mode, const char *cmd) -{ - struct device *dev; - - dev = bus_find_device_by_name(&platform_bus_type, NULL, "davinci-wdt"); - if (!dev) { - pr_err("%s: failed to find watchdog device\n", __func__); - return; - } - - davinci_watchdog_reset(to_platform_device(dev)); -} - int __init da8xx_register_watchdog(void) { return platform_device_register(&da8xx_wdt_device); diff --git a/arch/arm/mach-davinci/devices.c b/arch/arm/mach-davinci/devices.c index 3ae70f2909b0..0edda4093e47 100644 --- a/arch/arm/mach-davinci/devices.c +++ b/arch/arm/mach-davinci/devices.c @@ -282,18 +282,13 @@ static struct resource wdt_resources[] = { }, }; -struct platform_device davinci_wdt_device = { +static struct platform_device davinci_wdt_device = { .name = "davinci-wdt", .id = -1, .num_resources = ARRAY_SIZE(wdt_resources), .resource = wdt_resources, }; -void davinci_restart(enum reboot_mode mode, const char *cmd) -{ - davinci_watchdog_reset(&davinci_wdt_device); -} - int davinci_init_wdt(void) { return platform_device_register(&davinci_wdt_device); diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index 433a008ff796..19b9346e147e 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -81,7 +81,6 @@ extern struct davinci_soc_info davinci_soc_info; extern void davinci_common_init(const struct davinci_soc_info *soc_info); extern void davinci_init_ide(void); -void davinci_restart(enum reboot_mode mode, const char *cmd); void davinci_init_late(void); #ifdef CONFIG_DAVINCI_RESET_CLOCKS diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 03f37ef4297f..fe2d77654c06 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -119,7 +119,6 @@ int da850_register_vpif_display (struct vpif_display_config *display_config); int da850_register_vpif_capture (struct vpif_capture_config *capture_config); -void da8xx_restart(enum reboot_mode mode, const char *cmd); void da8xx_rproc_reserve_cma(void); int da8xx_register_rproc(void); int da850_register_gpio(void); diff --git a/arch/arm/mach-davinci/time.c b/arch/arm/mach-davinci/time.c index 034f865fe78e..1bb991ad9c1e 100644 --- a/arch/arm/mach-davinci/time.c +++ b/arch/arm/mach-davinci/time.c @@ -80,13 +80,6 @@ enum { #define TGCR_UNRESET 0x1 #define TGCR_RESET_MASK 0x3 -#define WDTCR_WDEN_SHIFT 14 -#define WDTCR_WDEN_DISABLE 0x0 -#define WDTCR_WDEN_ENABLE 0x1 -#define WDTCR_WDKEY_SHIFT 16 -#define WDTCR_WDKEY_SEQ0 0xa5c6 -#define WDTCR_WDKEY_SEQ1 0xda7e - struct timer_s { char *name; unsigned int id; @@ -409,53 +402,3 @@ void __init davinci_timer_init(void) for (i=0; i< ARRAY_SIZE(timers); i++) timer32_config(&timers[i]); } - -/* reset board using watchdog timer */ -void davinci_watchdog_reset(struct platform_device *pdev) -{ - u32 tgcr, wdtcr; - void __iomem *base; - struct clk *wd_clk; - - base = ioremap(pdev->resource[0].start, SZ_4K); - if (WARN_ON(!base)) - return; - - wd_clk = clk_get(&pdev->dev, NULL); - if (WARN_ON(IS_ERR(wd_clk))) - return; - clk_prepare_enable(wd_clk); - - /* disable, internal clock source */ - __raw_writel(0, base + TCR); - - /* reset timer, set mode to 64-bit watchdog, and unreset */ - tgcr = 0; - __raw_writel(tgcr, base + TGCR); - tgcr = TGCR_TIMMODE_64BIT_WDOG << TGCR_TIMMODE_SHIFT; - tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) | - (TGCR_UNRESET << TGCR_TIM34RS_SHIFT); - __raw_writel(tgcr, base + TGCR); - - /* clear counter and period regs */ - __raw_writel(0, base + TIM12); - __raw_writel(0, base + TIM34); - __raw_writel(0, base + PRD12); - __raw_writel(0, base + PRD34); - - /* put watchdog in pre-active state */ - wdtcr = __raw_readl(base + WDTCR); - wdtcr = (WDTCR_WDKEY_SEQ0 << WDTCR_WDKEY_SHIFT) | - (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT); - __raw_writel(wdtcr, base + WDTCR); - - /* put watchdog in active state */ - wdtcr = (WDTCR_WDKEY_SEQ1 << WDTCR_WDKEY_SHIFT) | - (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT); - __raw_writel(wdtcr, base + WDTCR); - - /* write an invalid value to the WDKEY field to trigger - * a watchdog reset */ - wdtcr = 0x00004000; - __raw_writel(wdtcr, base + WDTCR); -} -- cgit v1.3-14-g43fede From 4b785cc55e85e8be065a38ce9df29a2c32c7956a Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Fri, 19 Jan 2018 20:21:29 +0530 Subject: ARM: davinci: board-da830-evm: fix unused const variable warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit da830_evm_emif25_pins[] is used only when CONFIG_DA830_UI_NAND is enabled. It gives the following warning when the config is switched off. Fix it. arch/arm/mach-davinci/board-da830-evm.c:245:20: warning: ‘da830_evm_emif25_pins’ defined but not used [-Wunused-const-variable=] static const short da830_evm_emif25_pins[] = { ^~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da830-evm.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index d3ee02c12014..4a640af80be2 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -239,20 +239,6 @@ static inline void da830_evm_init_mmc(void) } } -/* - * UI board NAND/NOR flashes only use 8-bit data bus. - */ -static const short da830_evm_emif25_pins[] = { - DA830_EMA_D_0, DA830_EMA_D_1, DA830_EMA_D_2, DA830_EMA_D_3, - DA830_EMA_D_4, DA830_EMA_D_5, DA830_EMA_D_6, DA830_EMA_D_7, - DA830_EMA_A_0, DA830_EMA_A_1, DA830_EMA_A_2, DA830_EMA_A_3, - DA830_EMA_A_4, DA830_EMA_A_5, DA830_EMA_A_6, DA830_EMA_A_7, - DA830_EMA_A_8, DA830_EMA_A_9, DA830_EMA_A_10, DA830_EMA_A_11, - DA830_EMA_A_12, DA830_EMA_BA_0, DA830_EMA_BA_1, DA830_NEMA_WE, - DA830_NEMA_CS_2, DA830_NEMA_CS_3, DA830_NEMA_OE, DA830_EMA_WAIT_0, - -1 -}; - #define HAS_MMC IS_ENABLED(CONFIG_MMC_DAVINCI) #ifdef CONFIG_DA830_UI_NAND @@ -357,6 +343,20 @@ static struct platform_device da830_evm_nand_device = { .resource = da830_evm_nand_resources, }; +/* + * UI board NAND/NOR flashes only use 8-bit data bus. + */ +static const short da830_evm_emif25_pins[] = { + DA830_EMA_D_0, DA830_EMA_D_1, DA830_EMA_D_2, DA830_EMA_D_3, + DA830_EMA_D_4, DA830_EMA_D_5, DA830_EMA_D_6, DA830_EMA_D_7, + DA830_EMA_A_0, DA830_EMA_A_1, DA830_EMA_A_2, DA830_EMA_A_3, + DA830_EMA_A_4, DA830_EMA_A_5, DA830_EMA_A_6, DA830_EMA_A_7, + DA830_EMA_A_8, DA830_EMA_A_9, DA830_EMA_A_10, DA830_EMA_A_11, + DA830_EMA_A_12, DA830_EMA_BA_0, DA830_EMA_BA_1, DA830_NEMA_WE, + DA830_NEMA_CS_2, DA830_NEMA_CS_3, DA830_NEMA_OE, DA830_EMA_WAIT_0, + -1 +}; + static inline void da830_evm_init_nand(int mux_mode) { int ret; -- cgit v1.3-14-g43fede From 96c081735d5197d9d4fa0b6ce84cffa3bb197474 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 19 Jan 2018 21:20:22 -0600 Subject: ARM: davinci: move davinci_clk_init() to init_time This moves the call of davinci_clk_init() from map_io to init_time for all boards. This is the proper place to init clocks. This is also done in preparation for moving to the common clock framework. dm646x is a special case because we need to handle different ref_clk rates depending on which board is being used. The clock init in this case is modified to set the rate before registering the clocks instead of using davinci_set_refclk_rate() to recalculate the entire clock tree after all of the clocks are registered. Also, the cpu_clks field is removed from struct davinci_soc_info since it is no longer needed. Signed-off-by: David Lechner Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da830-evm.c | 2 +- arch/arm/mach-davinci/board-da850-evm.c | 2 +- arch/arm/mach-davinci/board-dm355-evm.c | 2 +- arch/arm/mach-davinci/board-dm355-leopard.c | 2 +- arch/arm/mach-davinci/board-dm365-evm.c | 2 +- arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- arch/arm/mach-davinci/board-dm646x-evm.c | 19 +++++++++++++------ arch/arm/mach-davinci/board-mityomapl138.c | 2 +- arch/arm/mach-davinci/board-neuros-osd2.c | 2 +- arch/arm/mach-davinci/board-omapl138-hawk.c | 2 +- arch/arm/mach-davinci/board-sffsdr.c | 2 +- arch/arm/mach-davinci/da830.c | 7 +++++-- arch/arm/mach-davinci/da850.c | 7 +++++-- arch/arm/mach-davinci/da8xx-dt.c | 2 +- arch/arm/mach-davinci/davinci.h | 4 ++++ arch/arm/mach-davinci/dm355.c | 8 ++++++-- arch/arm/mach-davinci/dm365.c | 8 ++++++-- arch/arm/mach-davinci/dm644x.c | 8 ++++++-- arch/arm/mach-davinci/dm646x.c | 22 +++++++++++----------- arch/arm/mach-davinci/include/mach/common.h | 1 - arch/arm/mach-davinci/include/mach/da8xx.h | 3 +++ 21 files changed, 70 insertions(+), 39 deletions(-) diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index 4a640af80be2..004f9c8de032 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -634,7 +634,7 @@ MACHINE_START(DAVINCI_DA830_EVM, "DaVinci DA830/OMAP-L137/AM17x EVM") .atag_offset = 0x100, .map_io = da830_evm_map_io, .init_irq = cp_intc_init, - .init_time = davinci_timer_init, + .init_time = da830_init_time, .init_machine = da830_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index b3a764feae11..3063478bcc36 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1477,7 +1477,7 @@ MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") .atag_offset = 0x100, .map_io = da850_evm_map_io, .init_irq = cp_intc_init, - .init_time = davinci_timer_init, + .init_time = da850_init_time, .init_machine = da850_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c index 29dbfc944e02..9ef24513ade7 100644 --- a/arch/arm/mach-davinci/board-dm355-evm.c +++ b/arch/arm/mach-davinci/board-dm355-evm.c @@ -427,7 +427,7 @@ MACHINE_START(DAVINCI_DM355_EVM, "DaVinci DM355 EVM") .atag_offset = 0x100, .map_io = dm355_evm_map_io, .init_irq = davinci_irq_init, - .init_time = davinci_timer_init, + .init_time = dm355_init_time, .init_machine = dm355_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c index 1e7e9b8f3bae..3ebc89d0bb94 100644 --- a/arch/arm/mach-davinci/board-dm355-leopard.c +++ b/arch/arm/mach-davinci/board-dm355-leopard.c @@ -271,7 +271,7 @@ MACHINE_START(DM355_LEOPARD, "DaVinci DM355 leopard") .atag_offset = 0x100, .map_io = dm355_leopard_map_io, .init_irq = davinci_irq_init, - .init_time = davinci_timer_init, + .init_time = dm355_init_time, .init_machine = dm355_leopard_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index 17b2c29f4053..3daeac747b1e 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -774,7 +774,7 @@ MACHINE_START(DAVINCI_DM365_EVM, "DaVinci DM365 EVM") .atag_offset = 0x100, .map_io = dm365_evm_map_io, .init_irq = davinci_irq_init, - .init_time = davinci_timer_init, + .init_time = dm365_init_time, .init_machine = dm365_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 35e1274183e1..95b55aae1366 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -828,7 +828,7 @@ MACHINE_START(DAVINCI_EVM, "DaVinci DM644x EVM") .atag_offset = 0x100, .map_io = davinci_evm_map_io, .init_irq = davinci_irq_init, - .init_time = davinci_timer_init, + .init_time = dm644x_init_time, .init_machine = davinci_evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index 003bbe5c24fd..2d37f5b0e1f5 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -44,10 +44,8 @@ #include #include #include -#include #include "davinci.h" -#include "clock.h" #define NAND_BLOCK_SIZE SZ_128K @@ -716,14 +714,23 @@ static void __init evm_init_i2c(void) } #endif +#define DM646X_REF_FREQ 27000000 +#define DM646X_AUX_FREQ 24000000 #define DM6467T_EVM_REF_FREQ 33000000 static void __init davinci_map_io(void) { dm646x_init(); +} - if (machine_is_davinci_dm6467tevm()) - davinci_set_refclk_rate(DM6467T_EVM_REF_FREQ); +static void __init dm646x_evm_init_time(void) +{ + dm646x_init_time(DM646X_REF_FREQ, DM646X_AUX_FREQ); +} + +static void __init dm6467t_evm_init_time(void) +{ + dm646x_init_time(DM6467T_EVM_REF_FREQ, DM646X_AUX_FREQ); } #define DM646X_EVM_PHY_ID "davinci_mdio-0:01" @@ -797,7 +804,7 @@ MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") .atag_offset = 0x100, .map_io = davinci_map_io, .init_irq = davinci_irq_init, - .init_time = davinci_timer_init, + .init_time = dm646x_evm_init_time, .init_machine = evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, @@ -807,7 +814,7 @@ MACHINE_START(DAVINCI_DM6467TEVM, "DaVinci DM6467T EVM") .atag_offset = 0x100, .map_io = davinci_map_io, .init_irq = davinci_irq_init, - .init_time = davinci_timer_init, + .init_time = dm6467t_evm_init_time, .init_machine = evm_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index 72748e5df43c..d1c85484c2e2 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -566,7 +566,7 @@ MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") .atag_offset = 0x100, .map_io = mityomapl138_map_io, .init_irq = cp_intc_init, - .init_time = davinci_timer_init, + .init_time = da850_init_time, .init_machine = mityomapl138_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c index 51a3ae19d765..f2875770fbff 100644 --- a/arch/arm/mach-davinci/board-neuros-osd2.c +++ b/arch/arm/mach-davinci/board-neuros-osd2.c @@ -227,7 +227,7 @@ MACHINE_START(NEUROS_OSD2, "Neuros OSD2") .atag_offset = 0x100, .map_io = davinci_ntosd2_map_io, .init_irq = davinci_irq_init, - .init_time = davinci_timer_init, + .init_time = dm644x_init_time, .init_machine = davinci_ntosd2_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index ae37bc46bc8c..e2ba9da6c717 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -330,7 +330,7 @@ MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") .atag_offset = 0x100, .map_io = omapl138_hawk_map_io, .init_irq = cp_intc_init, - .init_time = davinci_timer_init, + .init_time = da850_init_time, .init_machine = omapl138_hawk_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c index 1f02d4ec6144..2922da9d1684 100644 --- a/arch/arm/mach-davinci/board-sffsdr.c +++ b/arch/arm/mach-davinci/board-sffsdr.c @@ -150,7 +150,7 @@ MACHINE_START(SFFSDR, "Lyrtech SFFSDR") .atag_offset = 0x100, .map_io = davinci_sffsdr_map_io, .init_irq = davinci_irq_init, - .init_time = davinci_timer_init, + .init_time = dm644x_init_time, .init_machine = davinci_sffsdr_init, .init_late = davinci_init_late, .dma_zone_size = SZ_128M, diff --git a/arch/arm/mach-davinci/da830.c b/arch/arm/mach-davinci/da830.c index 57ab18cf2a89..350d7673aa4d 100644 --- a/arch/arm/mach-davinci/da830.c +++ b/arch/arm/mach-davinci/da830.c @@ -1200,7 +1200,6 @@ static const struct davinci_soc_info davinci_soc_info_da830 = { .jtag_id_reg = DA8XX_SYSCFG0_BASE + DA8XX_JTAG_ID_REG, .ids = da830_ids, .ids_num = ARRAY_SIZE(da830_ids), - .cpu_clks = da830_clks, .psc_bases = da830_psc_bases, .psc_bases_num = ARRAY_SIZE(da830_psc_bases), .pinmux_base = DA8XX_SYSCFG0_BASE + 0x120, @@ -1220,6 +1219,10 @@ void __init da830_init(void) da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); WARN(!da8xx_syscfg0_base, "Unable to map syscfg0 module"); +} - davinci_clk_init(davinci_soc_info_da830.cpu_clks); +void __init da830_init_time(void) +{ + davinci_clk_init(da830_clks); + davinci_timer_init(); } diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index aa37cbdf7d4d..34117e614e08 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1353,7 +1353,6 @@ static const struct davinci_soc_info davinci_soc_info_da850 = { .jtag_id_reg = DA8XX_SYSCFG0_BASE + DA8XX_JTAG_ID_REG, .ids = da850_ids, .ids_num = ARRAY_SIZE(da850_ids), - .cpu_clks = da850_clks, .psc_bases = da850_psc_bases, .psc_bases_num = ARRAY_SIZE(da850_psc_bases), .pinmux_base = DA8XX_SYSCFG0_BASE + 0x120, @@ -1392,6 +1391,10 @@ void __init da850_init(void) v = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG)); v &= ~CFGCHIP3_PLL1_MASTER_LOCK; __raw_writel(v, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG)); +} - davinci_clk_init(davinci_soc_info_da850.cpu_clks); +void __init da850_init_time(void) +{ + davinci_clk_init(da850_clks); + davinci_timer_init(); } diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c index 779e8ce887c3..ab199f4b9ce4 100644 --- a/arch/arm/mach-davinci/da8xx-dt.c +++ b/arch/arm/mach-davinci/da8xx-dt.c @@ -96,7 +96,7 @@ static const char *const da850_boards_compat[] __initconst = { DT_MACHINE_START(DA850_DT, "Generic DA850/OMAP-L138/AM18x") .map_io = da850_init, - .init_time = davinci_timer_init, + .init_time = da850_init_time, .init_machine = da850_init_machine, .dt_compat = da850_boards_compat, .init_late = davinci_init_late, diff --git a/arch/arm/mach-davinci/davinci.h b/arch/arm/mach-davinci/davinci.h index c62b90c6118a..270cef85750a 100644 --- a/arch/arm/mach-davinci/davinci.h +++ b/arch/arm/mach-davinci/davinci.h @@ -83,6 +83,7 @@ int davinci_init_wdt(void); /* DM355 function declarations */ void dm355_init(void); +void dm355_init_time(void); void dm355_init_spi0(unsigned chipselect_mask, const struct spi_board_info *info, unsigned len); void dm355_init_asp1(u32 evt_enable); @@ -91,6 +92,7 @@ int dm355_gpio_register(void); /* DM365 function declarations */ void dm365_init(void); +void dm365_init_time(void); void dm365_init_asp(void); void dm365_init_vc(void); void dm365_init_ks(struct davinci_ks_platform_data *pdata); @@ -102,12 +104,14 @@ int dm365_gpio_register(void); /* DM644x function declarations */ void dm644x_init(void); +void dm644x_init_time(void); void dm644x_init_asp(void); int dm644x_init_video(struct vpfe_config *, struct vpbe_config *); int dm644x_gpio_register(void); /* DM646x function declarations */ void dm646x_init(void); +void dm646x_init_time(unsigned long ref_clk_rate, unsigned long aux_clkin_rate); void dm646x_init_mcasp0(struct snd_platform_data *pdata); void dm646x_init_mcasp1(struct snd_platform_data *pdata); int dm646x_init_edma(struct edma_rsv_info *rsv); diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 938747f20c22..f29480495c18 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -1012,7 +1012,6 @@ static const struct davinci_soc_info davinci_soc_info_dm355 = { .jtag_id_reg = 0x01c40028, .ids = dm355_ids, .ids_num = ARRAY_SIZE(dm355_ids), - .cpu_clks = dm355_clks, .psc_bases = dm355_psc_bases, .psc_bases_num = ARRAY_SIZE(dm355_psc_bases), .pinmux_base = DAVINCI_SYSTEM_MODULE_BASE, @@ -1043,7 +1042,12 @@ void __init dm355_init(void) { davinci_common_init(&davinci_soc_info_dm355); davinci_map_sysmod(); - davinci_clk_init(davinci_soc_info_dm355.cpu_clks); +} + +void __init dm355_init_time(void) +{ + davinci_clk_init(dm355_clks); + davinci_timer_init(); } int __init dm355_init_video(struct vpfe_config *vpfe_cfg, diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 5d9f96df08e9..1e3df9df1e10 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -1116,7 +1116,6 @@ static const struct davinci_soc_info davinci_soc_info_dm365 = { .jtag_id_reg = 0x01c40028, .ids = dm365_ids, .ids_num = ARRAY_SIZE(dm365_ids), - .cpu_clks = dm365_clks, .psc_bases = dm365_psc_bases, .psc_bases_num = ARRAY_SIZE(dm365_psc_bases), .pinmux_base = DAVINCI_SYSTEM_MODULE_BASE, @@ -1168,7 +1167,12 @@ void __init dm365_init(void) { davinci_common_init(&davinci_soc_info_dm365); davinci_map_sysmod(); - davinci_clk_init(davinci_soc_info_dm365.cpu_clks); +} + +void __init dm365_init_time(void) +{ + davinci_clk_init(dm365_clks); + davinci_timer_init(); } static struct resource dm365_vpss_resources[] = { diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 6b41e1ca511e..b409801649e1 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -905,7 +905,6 @@ static const struct davinci_soc_info davinci_soc_info_dm644x = { .jtag_id_reg = 0x01c40028, .ids = dm644x_ids, .ids_num = ARRAY_SIZE(dm644x_ids), - .cpu_clks = dm644x_clks, .psc_bases = dm644x_psc_bases, .psc_bases_num = ARRAY_SIZE(dm644x_psc_bases), .pinmux_base = DAVINCI_SYSTEM_MODULE_BASE, @@ -931,7 +930,12 @@ void __init dm644x_init(void) { davinci_common_init(&davinci_soc_info_dm644x); davinci_map_sysmod(); - davinci_clk_init(davinci_soc_info_dm644x.cpu_clks); +} + +void __init dm644x_init_time(void) +{ + davinci_clk_init(dm644x_clks); + davinci_timer_init(); } int __init dm644x_init_video(struct vpfe_config *vpfe_cfg, diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 6fc06a6ad4f8..109ab1fa0d2c 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -39,12 +39,6 @@ #define VSCLKDIS_MASK (BIT_MASK(11) | BIT_MASK(10) | BIT_MASK(9) |\ BIT_MASK(8)) -/* - * Device specific clocks - */ -#define DM646X_REF_FREQ 27000000 -#define DM646X_AUX_FREQ 24000000 - #define DM646X_EMAC_BASE 0x01c80000 #define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) #define DM646X_EMAC_CNTRL_OFFSET 0x0000 @@ -64,13 +58,12 @@ static struct pll_data pll2_data = { static struct clk ref_clk = { .name = "ref_clk", - .rate = DM646X_REF_FREQ, - .set_rate = davinci_simple_set_rate, + /* rate is initialized in dm646x_init_time() */ }; static struct clk aux_clkin = { .name = "aux_clkin", - .rate = DM646X_AUX_FREQ, + /* rate is initialized in dm646x_init_time() */ }; static struct clk pll1_clk = { @@ -888,7 +881,6 @@ static const struct davinci_soc_info davinci_soc_info_dm646x = { .jtag_id_reg = 0x01c40028, .ids = dm646x_ids, .ids_num = ARRAY_SIZE(dm646x_ids), - .cpu_clks = dm646x_clks, .psc_bases = dm646x_psc_bases, .psc_bases_num = ARRAY_SIZE(dm646x_psc_bases), .pinmux_base = DAVINCI_SYSTEM_MODULE_BASE, @@ -956,7 +948,15 @@ void __init dm646x_init(void) { davinci_common_init(&davinci_soc_info_dm646x); davinci_map_sysmod(); - davinci_clk_init(davinci_soc_info_dm646x.cpu_clks); +} + +void __init dm646x_init_time(unsigned long ref_clk_rate, + unsigned long aux_clkin_rate) +{ + ref_clk.rate = ref_clk_rate; + aux_clkin.rate = aux_clkin_rate; + davinci_clk_init(dm646x_clks); + davinci_timer_init(); } static int __init dm646x_init_devices(void) diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index 19b9346e147e..f0d5e858f158 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -53,7 +53,6 @@ struct davinci_soc_info { u32 jtag_id_reg; struct davinci_id *ids; unsigned long ids_num; - struct clk_lookup *cpu_clks; u32 *psc_bases; unsigned long psc_bases_num; u32 pinmux_base; diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index fe2d77654c06..9fd6d0125762 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -88,7 +88,10 @@ extern unsigned int da850_max_speed; #define DA8XX_ARM_RAM_BASE 0xffff0000 void da830_init(void); +void da830_init_time(void); + void da850_init(void); +void da850_init_time(void); int da830_register_edma(struct edma_rsv_info *rsv); int da850_register_edma(struct edma_rsv_info *rsv[2]); -- cgit v1.3-14-g43fede From db6775ca6e0353d2618ca7d5e210fc36ad43bbd4 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 5 Dec 2017 23:04:58 -0800 Subject: MIPS: Octeon: Fix logging messages with spurious periods after newlines Using a period after a newline causes bad output. Fixes: 64b139f97c01 ("MIPS: OCTEON: irq: add CIB and other fixes") Signed-off-by: Joe Perches Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/17886/ Signed-off-by: James Hogan --- arch/mips/cavium-octeon/octeon-irq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c index 5b3a3f6a9ad3..b993d9f2c9b9 100644 --- a/arch/mips/cavium-octeon/octeon-irq.c +++ b/arch/mips/cavium-octeon/octeon-irq.c @@ -2271,7 +2271,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, parent_irq = irq_of_parse_and_map(ciu_node, 0); if (!parent_irq) { - pr_err("ERROR: Couldn't acquire parent_irq for %s\n.", + pr_err("ERROR: Couldn't acquire parent_irq for %s\n", ciu_node->name); return -EINVAL; } @@ -2281,7 +2281,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, addr = of_get_address(ciu_node, 0, NULL, NULL); if (!addr) { - pr_err("ERROR: Couldn't acquire reg(0) %s\n.", ciu_node->name); + pr_err("ERROR: Couldn't acquire reg(0) %s\n", ciu_node->name); return -EINVAL; } host_data->raw_reg = (u64)phys_to_virt( @@ -2289,7 +2289,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, addr = of_get_address(ciu_node, 1, NULL, NULL); if (!addr) { - pr_err("ERROR: Couldn't acquire reg(1) %s\n.", ciu_node->name); + pr_err("ERROR: Couldn't acquire reg(1) %s\n", ciu_node->name); return -EINVAL; } host_data->en_reg = (u64)phys_to_virt( @@ -2297,7 +2297,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, r = of_property_read_u32(ciu_node, "cavium,max-bits", &val); if (r) { - pr_err("ERROR: Couldn't read cavium,max-bits from %s\n.", + pr_err("ERROR: Couldn't read cavium,max-bits from %s\n", ciu_node->name); return r; } @@ -2307,7 +2307,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, &octeon_irq_domain_cib_ops, host_data); if (!cib_domain) { - pr_err("ERROR: Couldn't irq_domain_add_linear()\n."); + pr_err("ERROR: Couldn't irq_domain_add_linear()\n"); return -ENOMEM; } -- cgit v1.3-14-g43fede From 8fb71345ae275819424d61b8399443175bb80093 Mon Sep 17 00:00:00 2001 From: Jaedon Shin Date: Wed, 7 Feb 2018 11:36:27 +0900 Subject: MIPS: BMIPS: Enable CONFIG_SOC_BRCMSTB Enable CONFIG_SOC_BRCMSTB in bmips_stb_defconfig. CONFIG_BRCMSTB_PM is also enabled by default option in Kconfig. Signed-off-by: Jaedon Shin Cc: Ralf Baechle Cc: Kevin Cernekee Cc: linux-mips@linux-mips.org Reviewed-by: James Hogan Acked-by: Florian Fainelli Patchwork: https://patchwork.linux-mips.org/patch/18590/ Signed-off-by: James Hogan --- arch/mips/configs/bmips_stb_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/configs/bmips_stb_defconfig b/arch/mips/configs/bmips_stb_defconfig index 3cefa6bc01dd..47aecb8750e6 100644 --- a/arch/mips/configs/bmips_stb_defconfig +++ b/arch/mips/configs/bmips_stb_defconfig @@ -72,6 +72,7 @@ CONFIG_USB_EHCI_HCD_PLATFORM=y CONFIG_USB_OHCI_HCD=y CONFIG_USB_OHCI_HCD_PLATFORM=y CONFIG_USB_STORAGE=y +CONFIG_SOC_BRCMSTB=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y -- cgit v1.3-14-g43fede From 2fe69ede3eaf3d10fce2ae91fc0552d42e1d5d47 Mon Sep 17 00:00:00 2001 From: Pravin Shedge Date: Sun, 10 Dec 2017 23:50:17 +0530 Subject: MIPS: Remove duplicate includes These duplicate includes have been found with scripts/checkincludes.pl but they have been removed manually to avoid removing false positives. Signed-off-by: Pravin Shedge Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/17920/ Signed-off-by: James Hogan --- arch/mips/mm/init.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 84b7b592b834..400676ce03f4 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -46,7 +45,6 @@ #include #include #include -#include /* * We have up to 8 empty zeroed pages so we can map one of the right colour -- cgit v1.3-14-g43fede From 1fd88d9beff2cb25494d17c240b63dfb700ecc21 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 26 Dec 2017 12:03:08 +0530 Subject: MIPS: Alchemy: Constify gpio_led gpio_leds are not supposed to change at runtime. struct gpio_led_platform_data contains a const struct gpio_led pointer since v2.6.39, so mark the gpio_led structures const too. Signed-off-by: Arvind Yadav Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/18006/ [jhogan@kernel.org: improve commit message] Signed-off-by: James Hogan --- arch/mips/alchemy/board-gpr.c | 2 +- arch/mips/alchemy/board-mtx1.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/alchemy/board-gpr.c b/arch/mips/alchemy/board-gpr.c index 328d697e72b4..4e79dbd54a33 100644 --- a/arch/mips/alchemy/board-gpr.c +++ b/arch/mips/alchemy/board-gpr.c @@ -190,7 +190,7 @@ static struct platform_device gpr_mtd_device = { /* * LEDs */ -static struct gpio_led gpr_gpio_leds[] = { +static const struct gpio_led gpr_gpio_leds[] = { { /* green */ .name = "gpr:green", .gpio = 4, diff --git a/arch/mips/alchemy/board-mtx1.c b/arch/mips/alchemy/board-mtx1.c index 85bb75669b0d..aab55aaf3d62 100644 --- a/arch/mips/alchemy/board-mtx1.c +++ b/arch/mips/alchemy/board-mtx1.c @@ -145,7 +145,7 @@ static struct platform_device mtx1_wdt = { .resource = mtx1_wdt_res, }; -static struct gpio_led default_leds[] = { +static const struct gpio_led default_leds[] = { { .name = "mtx1:green", .gpio = 211, -- cgit v1.3-14-g43fede From 041f40f44cd0509a52c66ada27e115429c010d4d Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 26 Dec 2017 12:03:09 +0530 Subject: MIPS: AR7: Constify gpio_led gpio_leds are not supposed to change at runtime. struct gpio_led_platform_data contains a const struct gpio_led pointer since v2.6.39, so mark the gpio_led structures const too. Signed-off-by: Arvind Yadav Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/18007/ [jhogan@kernel.org: improve commit message] Signed-off-by: James Hogan --- arch/mips/ar7/platform.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/mips/ar7/platform.c b/arch/mips/ar7/platform.c index e1675c25d5d4..f09262e0a72f 100644 --- a/arch/mips/ar7/platform.c +++ b/arch/mips/ar7/platform.c @@ -346,7 +346,7 @@ static struct platform_device ar7_udc = { /***************************************************************************** * LEDs ****************************************************************************/ -static struct gpio_led default_leds[] = { +static const struct gpio_led default_leds[] = { { .name = "status", .gpio = 8, @@ -354,12 +354,12 @@ static struct gpio_led default_leds[] = { }, }; -static struct gpio_led titan_leds[] = { +static const struct gpio_led titan_leds[] = { { .name = "status", .gpio = 8, .active_low = 1, }, { .name = "wifi", .gpio = 13, .active_low = 1, }, }; -static struct gpio_led dsl502t_leds[] = { +static const struct gpio_led dsl502t_leds[] = { { .name = "status", .gpio = 9, @@ -377,7 +377,7 @@ static struct gpio_led dsl502t_leds[] = { }, }; -static struct gpio_led dg834g_leds[] = { +static const struct gpio_led dg834g_leds[] = { { .name = "ppp", .gpio = 6, @@ -406,7 +406,7 @@ static struct gpio_led dg834g_leds[] = { }, }; -static struct gpio_led fb_sl_leds[] = { +static const struct gpio_led fb_sl_leds[] = { { .name = "1", .gpio = 7, @@ -433,7 +433,7 @@ static struct gpio_led fb_sl_leds[] = { }, }; -static struct gpio_led fb_fon_leds[] = { +static const struct gpio_led fb_fon_leds[] = { { .name = "1", .gpio = 8, @@ -459,7 +459,7 @@ static struct gpio_led fb_fon_leds[] = { }, }; -static struct gpio_led gt701_leds[] = { +static const struct gpio_led gt701_leds[] = { { .name = "inet:green", .gpio = 13, -- cgit v1.3-14-g43fede From 21cff2d946cd4710abac23d6a7b81ff1965f3fed Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 26 Dec 2017 12:03:10 +0530 Subject: MIPS: TXX9: Constify gpio_led gpio_leds are not supposed to change at runtime. struct gpio_led_platform_data contains a const struct gpio_led pointer since v2.6.39, so mark the gpio_led structures const too. Signed-off-by: Arvind Yadav Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/18008/ [jhogan@kernel.org: improve commit message] Signed-off-by: James Hogan --- arch/mips/txx9/rbtx4927/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/txx9/rbtx4927/setup.c b/arch/mips/txx9/rbtx4927/setup.c index f5b367e20dff..31955c1d5555 100644 --- a/arch/mips/txx9/rbtx4927/setup.c +++ b/arch/mips/txx9/rbtx4927/setup.c @@ -319,7 +319,7 @@ static void __init rbtx4927_mtd_init(void) static void __init rbtx4927_gpioled_init(void) { - static struct gpio_led leds[] = { + static const struct gpio_led leds[] = { { .name = "gpioled:green:0", .gpio = 0, .active_low = 1, }, { .name = "gpioled:green:1", .gpio = 1, .active_low = 1, }, }; -- cgit v1.3-14-g43fede From c768519089785bbbcddd95d70a8db924ac07e065 Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Wed, 24 Jan 2018 12:42:07 +0100 Subject: MIPS: dts: Remove leading 0x and 0s from bindings notation Improve the DTS files by removing all the leading "0x" and zeros to fix the following dtc warnings: Warning (unit_address_format): Node /XXX unit name should not have leading "0x" and Warning (unit_address_format): Node /XXX unit name should not have leading 0s Converted using the following command: find . -type f \( -iname *.dts -o -iname *.dtsi \) -exec sed -E -i -e "s/@0x([0-9a-fA-F\.]+)\s?\{/@\L\1 \{/g" -e "s/@0+([0-9a-fA-F\.]+)\s?\{/@\L\1 \{/g" {} + For simplicity, two sed expressions were used to solve each warnings separately. To make the regex expression more robust a few other issues were resolved, namely setting unit-address to lower case, and adding a whitespace before the the opening curly brace: https://elinux.org/Device_Tree_Linux#Linux_conventions This is a follow up to commit 4c9847b7375a ("dt-bindings: Remove leading 0x from bindings notation") Reported-by: David Daney Suggested-by: Rob Herring Signed-off-by: Mathieu Malaterre Cc: Ralf Baechle Cc: Mark Rutland Cc: Paul Burton Cc: Paul Cercueil Cc: linux-mips@linux-mips.org Cc: devicetree@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/18528/ Signed-off-by: James Hogan --- arch/mips/boot/dts/img/boston.dts | 2 +- arch/mips/boot/dts/ingenic/ci20.dts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/mips/boot/dts/img/boston.dts b/arch/mips/boot/dts/img/boston.dts index 2cd49b60e030..1bd105428f61 100644 --- a/arch/mips/boot/dts/img/boston.dts +++ b/arch/mips/boot/dts/img/boston.dts @@ -157,7 +157,7 @@ #address-cells = <1>; #size-cells = <0>; - rtc@0x68 { + rtc@68 { compatible = "st,m41t81s"; reg = <0x68>; }; diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts index a4cc52214dbd..7d5e49e40b0d 100644 --- a/arch/mips/boot/dts/ingenic/ci20.dts +++ b/arch/mips/boot/dts/ingenic/ci20.dts @@ -110,22 +110,22 @@ reg = <0x0 0x0 0x0 0x800000>; }; - partition@0x800000 { + partition@800000 { label = "u-boot"; reg = <0x0 0x800000 0x0 0x200000>; }; - partition@0xa00000 { + partition@a00000 { label = "u-boot-env"; reg = <0x0 0xa00000 0x0 0x200000>; }; - partition@0xc00000 { + partition@c00000 { label = "boot"; reg = <0x0 0xc00000 0x0 0x4000000>; }; - partition@0x8c00000 { + partition@8c00000 { label = "system"; reg = <0x0 0x4c00000 0x1 0xfb400000>; }; -- cgit v1.3-14-g43fede From 018eab88efc581c977756a8b7b2eb1254cbe1c7c Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Wed, 24 Jan 2018 12:42:08 +0100 Subject: MIPS: dts: Fix a typo in the node unit name The unit name was 8c00000 but since the reg property is declared as: reg = <0x0 0x4c00000 0x1 0xfb400000>; the unit name should have been instead 4c00000. Tested on MIPS Creator CI20 (v1): $ cat /sys/firmware/devicetree/.../partitions/partition@4c00000/label;echo system Reported-by: James Hogan Signed-off-by: Mathieu Malaterre Cc: Ralf Baechle Cc: Rob Herring Cc: Mark Rutland Cc: Paul Burton Cc: Paul Cercueil Cc: linux-mips@linux-mips.org Cc: devicetree@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/18529/ Signed-off-by: James Hogan --- arch/mips/boot/dts/ingenic/ci20.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts index 7d5e49e40b0d..38078594cf97 100644 --- a/arch/mips/boot/dts/ingenic/ci20.dts +++ b/arch/mips/boot/dts/ingenic/ci20.dts @@ -125,7 +125,7 @@ reg = <0x0 0xc00000 0x0 0x4000000>; }; - partition@8c00000 { + partition@4c00000 { label = "system"; reg = <0x0 0x4c00000 0x1 0xfb400000>; }; -- cgit v1.3-14-g43fede From 427aeea072acd7afa6ffe20fc76f887b0f8b232c Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Tue, 2 Jan 2018 19:52:21 +0100 Subject: MIPS: Make declaration for function `memory_region_available` static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix non-fatal warning during compilation using W=1: arch/mips/kernel/setup.c:158:13: warning: no previous prototype for ‘memory_region_available’ [-Wmissing-prototypes] bool __init memory_region_available(phys_addr_t start, phys_addr_t size) ^~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Mathieu Malaterre Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/18175/ [jhogan@kernel.org: tweak whitespace] Signed-off-by: James Hogan --- arch/mips/kernel/setup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 5f8b0a9e30b3..28be8f238e77 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -155,7 +155,8 @@ void __init detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_add add_memory_region(start, size, BOOT_MEM_RAM); } -bool __init memory_region_available(phys_addr_t start, phys_addr_t size) +static bool __init __maybe_unused memory_region_available(phys_addr_t start, + phys_addr_t size) { int i; bool in_ram = false, free = true; -- cgit v1.3-14-g43fede From ab3d8441290bcd648f487b69ea87cfe5b37d9e02 Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Tue, 2 Jan 2018 19:53:15 +0100 Subject: MIPS: Remove a warning when PHYS_OFFSET is 0x0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite the comparison in `else if` statement, case where `min_low_pfn > ARCH_PFN_OFFSET` has already been checked in the first `if` statement: if (min_low_pfn > ARCH_PFN_OFFSET) { Fix non-fatal warning during compilation using W=1: arch/mips/kernel/setup.c: In function ‘bootmem_init’: arch/mips/kernel/setup.c:461:25: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits] } else if (min_low_pfn < ARCH_PFN_OFFSET) { ^ Signed-off-by: Mathieu Malaterre Reviewed-by: James Hogan Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/18176/ Signed-off-by: James Hogan --- arch/mips/kernel/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 28be8f238e77..563188ac6fa2 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -454,7 +454,7 @@ static void __init bootmem_init(void) pr_info("Wasting %lu bytes for tracking %lu unused pages\n", (min_low_pfn - ARCH_PFN_OFFSET) * sizeof(struct page), min_low_pfn - ARCH_PFN_OFFSET); - } else if (min_low_pfn < ARCH_PFN_OFFSET) { + } else if (ARCH_PFN_OFFSET - min_low_pfn > 0UL) { pr_info("%lu free pages won't be used\n", ARCH_PFN_OFFSET - min_low_pfn); } -- cgit v1.3-14-g43fede From 7f166addbebdcaeefe36997d5b8dceb96c5619fa Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 16 Feb 2018 17:40:24 +0200 Subject: platform/x86: wmi: Replace kmalloc + sprintf() with kasprintf() kasprintf() does the job of two: kmalloc() and sprintf(). Replace two calls with one. Reviewed-by: Darren Hart (VMware) Signed-off-by: Andy Shevchenko --- drivers/platform/x86/wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index c0c8945603cb..4e76ffcb5394 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -933,12 +933,11 @@ static int wmi_dev_probe(struct device *dev) goto probe_failure; } - buf = kmalloc(strlen(wdriver->driver.name) + 5, GFP_KERNEL); + buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name); if (!buf) { ret = -ENOMEM; goto probe_string_failure; } - sprintf(buf, "wmi/%s", wdriver->driver.name); wblock->char_dev.minor = MISC_DYNAMIC_MINOR; wblock->char_dev.name = buf; wblock->char_dev.fops = &wmi_fops; -- cgit v1.3-14-g43fede From c769eaf7a85d43d6a57179f3ece31661d1f7caba Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 19 Feb 2018 00:41:22 +1100 Subject: ARM: dts: exynos: Split Trats2 DTS in preparation for Midas boards The Midas boards share a lot with Trats2. Split the common parts out of Trats2 into a common Midas DTSI and a common "Galaxy S3" DTS. Signed-off-by: Simon Shields Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/exynos4412-galaxy-s3.dtsi | 140 +++ arch/arm/boot/dts/exynos4412-midas.dtsi | 1308 +++++++++++++++++++++++++ arch/arm/boot/dts/exynos4412-trats2.dts | 1388 +-------------------------- 3 files changed, 1450 insertions(+), 1386 deletions(-) create mode 100644 arch/arm/boot/dts/exynos4412-galaxy-s3.dtsi create mode 100644 arch/arm/boot/dts/exynos4412-midas.dtsi diff --git a/arch/arm/boot/dts/exynos4412-galaxy-s3.dtsi b/arch/arm/boot/dts/exynos4412-galaxy-s3.dtsi new file mode 100644 index 000000000000..ee8e1f445370 --- /dev/null +++ b/arch/arm/boot/dts/exynos4412-galaxy-s3.dtsi @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Samsung's Exynos4412 based Galaxy S3 board device tree source + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + */ + +/dts-v1/; +#include "exynos4412-midas.dtsi" + +/ { + aliases { + i2c9 = &i2c_ak8975; + i2c10 = &i2c_cm36651; + }; + + regulators { + lcd_vdd3_reg: voltage-regulator-2 { + compatible = "regulator-fixed"; + regulator-name = "LCD_VDD_2.2V"; + regulator-min-microvolt = <2200000>; + regulator-max-microvolt = <2200000>; + gpio = <&gpc0 1 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + ps_als_reg: voltage-regulator-5 { + compatible = "regulator-fixed"; + regulator-name = "LED_A_3.0V"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + gpio = <&gpj0 5 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + }; + + i2c_ak8975: i2c-gpio-0 { + compatible = "i2c-gpio"; + gpios = <&gpy2 4 GPIO_ACTIVE_HIGH>, <&gpy2 5 GPIO_ACTIVE_HIGH>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + ak8975@c { + compatible = "asahi-kasei,ak8975"; + reg = <0x0c>; + gpios = <&gpj0 7 GPIO_ACTIVE_HIGH>; + }; + }; + + i2c_cm36651: i2c-gpio-2 { + compatible = "i2c-gpio"; + gpios = <&gpf0 0 GPIO_ACTIVE_LOW>, <&gpf0 1 GPIO_ACTIVE_LOW>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + + cm36651@18 { + compatible = "capella,cm36651"; + reg = <0x18>; + interrupt-parent = <&gpx0>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + vled-supply = <&ps_als_reg>; + }; + }; +}; + +&buck9_reg { + maxim,ena-gpios = <&gpm0 3 GPIO_ACTIVE_HIGH>; +}; + +&cam_af_reg { + gpio = <&gpm0 4 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&cam_io_reg { + gpio = <&gpm0 2 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&dsi_0 { + status = "okay"; + + panel@0 { + compatible = "samsung,s6e8aa0"; + reg = <0>; + vdd3-supply = <&lcd_vdd3_reg>; + vci-supply = <&ldo25_reg>; + reset-gpios = <&gpf2 1 GPIO_ACTIVE_HIGH>; + power-on-delay= <50>; + reset-delay = <100>; + init-delay = <100>; + flip-horizontal; + flip-vertical; + panel-width-mm = <58>; + panel-height-mm = <103>; + + display-timings { + timing-0 { + clock-frequency = <57153600>; + hactive = <720>; + vactive = <1280>; + hfront-porch = <5>; + hback-porch = <5>; + hsync-len = <5>; + vfront-porch = <13>; + vback-porch = <1>; + vsync-len = <2>; + }; + }; + }; +}; + +&i2c_3 { + mms114-touchscreen@48 { + compatible = "melfas,mms114"; + reg = <0x48>; + interrupt-parent = <&gpm2>; + interrupts = <3 IRQ_TYPE_EDGE_FALLING>; + x-size = <720>; + y-size = <1280>; + avdd-supply = <&ldo23_reg>; + vdd-supply = <&ldo24_reg>; + }; +}; + +&ldo25_reg { + regulator-name = "LCD_VCC_3.3V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; +}; + +&s5c73m3 { + standby-gpios = <&gpm0 1 GPIO_ACTIVE_LOW>; /* ISP_STANDBY */ + vdda-supply = <&ldo17_reg>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/exynos4412-midas.dtsi b/arch/arm/boot/dts/exynos4412-midas.dtsi new file mode 100644 index 000000000000..43266765377a --- /dev/null +++ b/arch/arm/boot/dts/exynos4412-midas.dtsi @@ -0,0 +1,1308 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Samsung's Exynos4412 based Trats 2 board device tree source + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Device tree source file for Samsung's Trats 2 board which is based on + * Samsung's Exynos4412 SoC. + */ + +/dts-v1/; +#include "exynos4412.dtsi" +#include "exynos4412-ppmu-common.dtsi" +#include +#include +#include +#include + +/ { + compatible = "samsung,midas", "samsung,exynos4412", "samsung,exynos4"; + + aliases { + i2c11 = &i2c_max77693; + i2c12 = &i2c_max77693_fuel; + }; + + chosen { + stdout-path = &serial_2; + }; + + firmware@204f000 { + compatible = "samsung,secure-firmware"; + reg = <0x0204F000 0x1000>; + }; + + fixed-rate-clocks { + xxti { + compatible = "samsung,clock-xxti", "fixed-clock"; + clock-frequency = <0>; + }; + + xusbxti { + compatible = "samsung,clock-xusbxti", "fixed-clock"; + clock-frequency = <24000000>; + }; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + cam_io_reg: voltage-regulator-1 { + compatible = "regulator-fixed"; + regulator-name = "CAM_SENSOR_A"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + enable-active-high; + status = "disabled"; + }; + + cam_af_reg: voltage-regulator-3 { + compatible = "regulator-fixed"; + regulator-name = "CAM_AF"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + enable-active-high; + status = "disabled"; + }; + + vsil12: voltage-regulator-6 { + compatible = "regulator-fixed"; + regulator-name = "VSIL_1.2V"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + gpio = <&gpl0 4 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&buck7_reg>; + }; + + vcc33mhl: voltage-regulator-7 { + compatible = "regulator-fixed"; + regulator-name = "VCC_3.3_MHL"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpl0 4 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + vcc18mhl: voltage-regulator-8 { + compatible = "regulator-fixed"; + regulator-name = "VCC_1.8_MHL"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + gpio = <&gpl0 4 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + key-down { + gpios = <&gpx3 3 GPIO_ACTIVE_LOW>; + linux,code = <114>; + label = "volume down"; + debounce-interval = <10>; + }; + + key-up { + gpios = <&gpx2 2 GPIO_ACTIVE_LOW>; + linux,code = <115>; + label = "volume up"; + debounce-interval = <10>; + }; + + key-power { + gpios = <&gpx2 7 GPIO_ACTIVE_LOW>; + linux,code = <116>; + label = "power"; + debounce-interval = <10>; + wakeup-source; + }; + + key-ok { + gpios = <&gpx0 1 GPIO_ACTIVE_LOW>; + linux,code = <139>; + label = "ok"; + debounce-inteval = <10>; + wakeup-source; + }; + }; + + i2c_max77693: i2c-gpio-1 { + compatible = "i2c-gpio"; + gpios = <&gpm2 0 GPIO_ACTIVE_HIGH>, <&gpm2 1 GPIO_ACTIVE_HIGH>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + max77693@66 { + compatible = "maxim,max77693"; + interrupt-parent = <&gpx1>; + interrupts = <5 IRQ_TYPE_EDGE_FALLING>; + reg = <0x66>; + + regulators { + esafeout1_reg: ESAFEOUT1 { + regulator-name = "ESAFEOUT1"; + }; + esafeout2_reg: ESAFEOUT2 { + regulator-name = "ESAFEOUT2"; + }; + charger_reg: CHARGER { + regulator-name = "CHARGER"; + regulator-min-microamp = <60000>; + regulator-max-microamp = <2580000>; + }; + }; + + max77693_haptic { + compatible = "maxim,max77693-haptic"; + haptic-supply = <&ldo26_reg>; + pwms = <&pwm 0 38022 0>; + }; + + charger { + compatible = "maxim,max77693-charger"; + + maxim,constant-microvolt = <4350000>; + maxim,min-system-microvolt = <3600000>; + maxim,thermal-regulation-celsius = <100>; + maxim,battery-overcurrent-microamp = <3500000>; + maxim,charge-input-threshold-microvolt = <4300000>; + }; + }; + }; + + i2c_max77693_fuel: i2c-gpio-3 { + compatible = "i2c-gpio"; + gpios = <&gpf1 5 GPIO_ACTIVE_HIGH>, <&gpf1 4 GPIO_ACTIVE_HIGH>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + max77693-fuel-gauge@36 { + compatible = "maxim,max17047"; + interrupt-parent = <&gpx2>; + interrupts = <3 IRQ_TYPE_EDGE_FALLING>; + reg = <0x36>; + + maxim,over-heat-temp = <700>; + maxim,over-volt = <4500>; + }; + }; + + i2c-mhl { + compatible = "i2c-gpio"; + gpios = <&gpf0 4 GPIO_ACTIVE_HIGH>, <&gpf0 6 GPIO_ACTIVE_HIGH>; + i2c-gpio,delay-us = <100>; + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-0 = <&i2c_mhl_bus>; + pinctrl-names = "default"; + status = "okay"; + + sii9234: hdmi-bridge@39 { + compatible = "sil,sii9234"; + avcc33-supply = <&vcc33mhl>; + iovcc18-supply = <&vcc18mhl>; + avcc12-supply = <&vsil12>; + cvcc12-supply = <&vsil12>; + reset-gpios = <&gpf3 4 GPIO_ACTIVE_LOW>; + interrupt-parent = <&gpf3>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + reg = <0x39>; + + port { + mhl_to_hdmi: endpoint { + remote-endpoint = <&hdmi_to_mhl>; + }; + }; + }; + }; + + wlan_pwrseq: sdhci3-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpj0 0 GPIO_ACTIVE_LOW>; + clocks = <&max77686 MAX77686_CLK_PMIC>; + clock-names = "ext_clock"; + }; + + sound { + compatible = "samsung,trats2-audio"; + samsung,i2s-controller = <&i2s0>; + samsung,model = "Trats2"; + samsung,audio-codec = <&wm1811>; + samsung,audio-routing = + "SPK", "SPKOUTLN", + "SPK", "SPKOUTLP", + "SPK", "SPKOUTRN", + "SPK", "SPKOUTRP"; + }; + + thermistor-ap { + compatible = "murata,ncp15wb473"; + pullup-uv = <1800000>; /* VCC_1.8V_AP */ + pullup-ohm = <100000>; /* 100K */ + pulldown-ohm = <100000>; /* 100K */ + io-channels = <&adc 1>; /* AP temperature */ + }; + + thermistor-battery { + compatible = "murata,ncp15wb473"; + pullup-uv = <1800000>; /* VCC_1.8V_AP */ + pullup-ohm = <100000>; /* 100K */ + pulldown-ohm = <100000>; /* 100K */ + io-channels = <&adc 2>; /* Battery temperature */ + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + cooling-maps { + map0 { + /* Corresponds to 800MHz at freq_table */ + cooling-device = <&cpu0 7 7>; + }; + map1 { + /* Corresponds to 200MHz at freq_table */ + cooling-device = <&cpu0 13 13>; + }; + }; + }; + }; +}; + +&adc { + vdd-supply = <&ldo3_reg>; + status = "okay"; +}; + +&bus_dmc { + devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>; + vdd-supply = <&buck1_reg>; + status = "okay"; +}; + +&bus_acp { + devfreq = <&bus_dmc>; + status = "okay"; +}; + +&bus_c2c { + devfreq = <&bus_dmc>; + status = "okay"; +}; + +&bus_leftbus { + devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>; + vdd-supply = <&buck3_reg>; + status = "okay"; +}; + +&bus_rightbus { + devfreq = <&bus_leftbus>; + status = "okay"; +}; + +&bus_display { + devfreq = <&bus_leftbus>; + status = "okay"; +}; + +&bus_fsys { + devfreq = <&bus_leftbus>; + status = "okay"; +}; + +&bus_peri { + devfreq = <&bus_leftbus>; + status = "okay"; +}; + +&bus_mfc { + devfreq = <&bus_leftbus>; + status = "okay"; +}; + +&camera { + pinctrl-0 = <&cam_port_a_clk_active &cam_port_b_clk_active>; + pinctrl-names = "default"; + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_CAM0>, + <&clock CLK_MOUT_CAM1>; + assigned-clock-parents = <&clock CLK_XUSBXTI>, + <&clock CLK_XUSBXTI>; +}; + +&cpu0 { + cpu0-supply = <&buck2_reg>; +}; + +&csis_0 { + status = "okay"; + vddcore-supply = <&ldo8_reg>; + vddio-supply = <&ldo10_reg>; + assigned-clocks = <&clock CLK_MOUT_CSIS0>, + <&clock CLK_SCLK_CSIS0>; + assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; + assigned-clock-rates = <0>, <176000000>; + + /* Camera C (3) MIPI CSI-2 (CSIS0) */ + port@3 { + reg = <3>; + csis0_ep: endpoint { + remote-endpoint = <&s5c73m3_ep>; + data-lanes = <1 2 3 4>; + samsung,csis-hs-settle = <12>; + }; + }; +}; + +&csis_1 { + status = "okay"; + vddcore-supply = <&ldo8_reg>; + vddio-supply = <&ldo10_reg>; + assigned-clocks = <&clock CLK_MOUT_CSIS1>, + <&clock CLK_SCLK_CSIS1>; + assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; + assigned-clock-rates = <0>, <176000000>; + + /* Camera D (4) MIPI CSI-2 (CSIS1) */ + port@4 { + reg = <4>; + csis1_ep: endpoint { + remote-endpoint = <&is_s5k6a3_ep>; + data-lanes = <1>; + samsung,csis-hs-settle = <18>; + samsung,csis-wclk; + }; + }; +}; + +&dsi_0 { + vddcore-supply = <&ldo8_reg>; + vddio-supply = <&ldo10_reg>; + samsung,burst-clock-frequency = <500000000>; + samsung,esc-clock-frequency = <20000000>; + samsung,pll-clock-frequency = <24000000>; +}; + +&exynos_usbphy { + vbus-supply = <&esafeout1_reg>; + status = "okay"; +}; + +&fimc_0 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC0>, + <&clock CLK_SCLK_FIMC0>; + assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; + assigned-clock-rates = <0>, <176000000>; +}; + +&fimc_1 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC1>, + <&clock CLK_SCLK_FIMC1>; + assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; + assigned-clock-rates = <0>, <176000000>; +}; + +&fimc_2 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC2>, + <&clock CLK_SCLK_FIMC2>; + assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; + assigned-clock-rates = <0>, <176000000>; +}; + +&fimc_3 { + status = "okay"; + assigned-clocks = <&clock CLK_MOUT_FIMC3>, + <&clock CLK_SCLK_FIMC3>; + assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; + assigned-clock-rates = <0>, <176000000>; +}; + +&fimc_is { + pinctrl-0 = <&fimc_is_uart>; + pinctrl-names = "default"; + status = "okay"; + + }; + +&fimc_lite_0 { + status = "okay"; +}; + +&fimc_lite_1 { + status = "okay"; +}; + +&fimd { + status = "okay"; +}; + +&hdmi { + hpd-gpios = <&gpx3 7 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_hpd>; + vdd-supply = <&ldo3_reg>; + vdd_osc-supply = <&ldo4_reg>; + vdd_pll-supply = <&ldo3_reg>; + ddc = <&i2c_5>; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + hdmi_to_mhl: endpoint { + remote-endpoint = <&mhl_to_hdmi>; + }; + }; + }; +}; + +&hsotg { + vusb_d-supply = <&ldo15_reg>; + vusb_a-supply = <&ldo12_reg>; + dr_mode = "peripheral"; + status = "okay"; +}; + +&i2c_0 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-slave-addr = <0x10>; + samsung,i2c-max-bus-freq = <400000>; + pinctrl-0 = <&i2c0_bus>; + pinctrl-names = "default"; + status = "okay"; + + s5c73m3: s5c73m3@3c { + compatible = "samsung,s5c73m3"; + reg = <0x3c>; + xshutdown-gpios = <&gpf1 3 GPIO_ACTIVE_LOW>; /* ISP_RESET */ + vdd-int-supply = <&buck9_reg>; + vddio-cis-supply = <&ldo9_reg>; + vddio-host-supply = <&ldo18_reg>; + vdd-af-supply = <&cam_af_reg>; + vdd-reg-supply = <&cam_io_reg>; + clock-frequency = <24000000>; + /* CAM_A_CLKOUT */ + clocks = <&camera 0>; + clock-names = "cis_extclk"; + status = "disabled"; + port { + s5c73m3_ep: endpoint { + remote-endpoint = <&csis0_ep>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&i2c1_isp { + pinctrl-0 = <&fimc_is_i2c1>; + pinctrl-names = "default"; + + s5k6a3@10 { + compatible = "samsung,s5k6a3"; + reg = <0x10>; + svdda-supply = <&cam_io_reg>; + svddio-supply = <&ldo19_reg>; + afvdd-supply = <&ldo19_reg>; + clock-frequency = <24000000>; + /* CAM_B_CLKOUT */ + clocks = <&camera 1>; + clock-names = "extclk"; + samsung,camclk-out = <1>; + gpios = <&gpm1 6 GPIO_ACTIVE_HIGH>; + + port { + is_s5k6a3_ep: endpoint { + remote-endpoint = <&csis1_ep>; + data-lanes = <1>; + }; + }; + }; +}; + +&i2c_3 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-slave-addr = <0x10>; + samsung,i2c-max-bus-freq = <400000>; + pinctrl-0 = <&i2c3_bus>; + pinctrl-names = "default"; + status = "okay"; +}; + +&i2c_4 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-slave-addr = <0x10>; + samsung,i2c-max-bus-freq = <100000>; + pinctrl-0 = <&i2c4_bus>; + pinctrl-names = "default"; + status = "okay"; + + wm1811: wm1811@1a { + compatible = "wlf,wm1811"; + reg = <0x1a>; + clocks = <&pmu_system_controller 0>; + clock-names = "MCLK1"; + DCVDD-supply = <&ldo3_reg>; + DBVDD1-supply = <&ldo3_reg>; + wlf,ldo1ena = <&gpj0 4 0>; + }; +}; + +&i2c_5 { + status = "okay"; +}; + +&i2c_7 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-slave-addr = <0x10>; + samsung,i2c-max-bus-freq = <100000>; + pinctrl-0 = <&i2c7_bus>; + pinctrl-names = "default"; + status = "okay"; + + max77686: max77686_pmic@9 { + compatible = "maxim,max77686"; + interrupt-parent = <&gpx0>; + interrupts = <7 IRQ_TYPE_NONE>; + reg = <0x09>; + #clock-cells = <1>; + + voltage-regulators { + ldo1_reg: LDO1 { + regulator-name = "VALIVE_1.0V_AP"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + ldo2_reg: LDO2 { + regulator-name = "VM1M2_1.2V_AP"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + ldo3_reg: LDO3 { + regulator-name = "VCC_1.8V_AP"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo4_reg: LDO4 { + regulator-name = "VCC_2.8V_AP"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-always-on; + }; + + ldo5_reg: LDO5 { + regulator-name = "VCC_1.8V_IO"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo6_reg: LDO6 { + regulator-name = "VMPLL_1.0V_AP"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + ldo7_reg: LDO7 { + regulator-name = "VPLL_1.0V_AP"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + ldo8_reg: LDO8 { + regulator-name = "VMIPI_1.0V"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo9_reg: LDO9 { + regulator-name = "CAM_ISP_MIPI_1.2V"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + ldo10_reg: LDO10 { + regulator-name = "VMIPI_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo11_reg: LDO11 { + regulator-name = "VABB1_1.95V"; + regulator-min-microvolt = <1950000>; + regulator-max-microvolt = <1950000>; + regulator-always-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo12_reg: LDO12 { + regulator-name = "VUOTG_3.0V"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo13_reg: LDO13 { + regulator-name = "NFC_AVDD_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo14_reg: LDO14 { + regulator-name = "VABB2_1.95V"; + regulator-min-microvolt = <1950000>; + regulator-max-microvolt = <1950000>; + regulator-always-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo15_reg: LDO15 { + regulator-name = "VHSIC_1.0V"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + ldo16_reg: LDO16 { + regulator-name = "VHSIC_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + ldo17_reg: LDO17 { + regulator-name = "CAM_SENSOR_CORE_1.2V"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + ldo18_reg: LDO18 { + regulator-name = "CAM_ISP_SEN_IO_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo19_reg: LDO19 { + regulator-name = "VT_CAM_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo20_reg: LDO20 { + regulator-name = "VDDQ_PRE_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo21_reg: LDO21 { + regulator-name = "VTF_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + maxim,ena-gpios = <&gpy2 0 GPIO_ACTIVE_HIGH>; + }; + + ldo22_reg: LDO22 { + regulator-name = "VMEM_VDD_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + maxim,ena-gpios = <&gpk0 2 GPIO_ACTIVE_HIGH>; + }; + + ldo23_reg: LDO23 { + regulator-name = "TSP_AVDD_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + ldo24_reg: LDO24 { + regulator-name = "TSP_VDD_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo25_reg: LDO25 { + regulator-name = "LDO25"; + }; + + ldo26_reg: LDO26 { + regulator-name = "MOTOR_VCC_3.0V"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + }; + + buck1_reg: BUCK1 { + regulator-name = "vdd_mif"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + buck2_reg: BUCK2 { + regulator-name = "vdd_arm"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + buck3_reg: BUCK3 { + regulator-name = "vdd_int"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1150000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + buck4_reg: BUCK4 { + regulator-name = "vdd_g3d"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1150000>; + regulator-boot-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + buck5_reg: BUCK5 { + regulator-name = "VMEM_1.2V_AP"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + buck6_reg: BUCK6 { + regulator-name = "VCC_SUB_1.35V"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + }; + + buck7_reg: BUCK7 { + regulator-name = "VCC_SUB_2.0V"; + regulator-min-microvolt = <2000000>; + regulator-max-microvolt = <2000000>; + regulator-always-on; + }; + + buck8_reg: BUCK8 { + regulator-name = "VMEM_VDDF_3.0V"; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + maxim,ena-gpios = <&gpk0 2 GPIO_ACTIVE_HIGH>; + }; + + buck9_reg: BUCK9 { + regulator-name = "CAM_ISP_CORE_1.2V"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1200000>; + }; + }; + }; +}; + +&i2c_8 { + status = "okay"; +}; + +&i2s0 { + pinctrl-0 = <&i2s0_bus>; + pinctrl-names = "default"; + status = "okay"; +}; + +&mixer { + status = "okay"; +}; + +&mshc_0 { + broken-cd; + non-removable; + card-detect-delay = <200>; + vmmc-supply = <&ldo22_reg>; + clock-frequency = <400000000>; + samsung,dw-mshc-ciu-div = <0>; + samsung,dw-mshc-sdr-timing = <2 3>; + samsung,dw-mshc-ddr-timing = <1 2>; + pinctrl-0 = <&sd4_clk &sd4_cmd &sd4_bus4 &sd4_bus8>; + pinctrl-names = "default"; + status = "okay"; + bus-width = <8>; + cap-mmc-highspeed; +}; + +&pmu_system_controller { + assigned-clocks = <&pmu_system_controller 0>; + assigned-clock-parents = <&clock CLK_XUSBXTI>; +}; + +&pinctrl_0 { + pinctrl-names = "default"; + pinctrl-0 = <&sleep0>; + + mhl_int: mhl-int { + samsung,pins = "gpf3-5"; + samsung,pin-pud = ; + }; + + i2c_mhl_bus: i2c-mhl-bus { + samsung,pins = "gpf0-4", "gpf0-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + sleep0: sleep-states { + PIN_SLP(gpa0-0, INPUT, NONE); + PIN_SLP(gpa0-1, OUT0, NONE); + PIN_SLP(gpa0-2, INPUT, NONE); + PIN_SLP(gpa0-3, INPUT, UP); + PIN_SLP(gpa0-4, INPUT, NONE); + PIN_SLP(gpa0-5, INPUT, DOWN); + PIN_SLP(gpa0-6, INPUT, DOWN); + PIN_SLP(gpa0-7, INPUT, UP); + + PIN_SLP(gpa1-0, INPUT, DOWN); + PIN_SLP(gpa1-1, INPUT, DOWN); + PIN_SLP(gpa1-2, INPUT, DOWN); + PIN_SLP(gpa1-3, INPUT, DOWN); + PIN_SLP(gpa1-4, INPUT, DOWN); + PIN_SLP(gpa1-5, INPUT, DOWN); + + PIN_SLP(gpb-0, INPUT, NONE); + PIN_SLP(gpb-1, INPUT, NONE); + PIN_SLP(gpb-2, INPUT, NONE); + PIN_SLP(gpb-3, INPUT, NONE); + PIN_SLP(gpb-4, INPUT, DOWN); + PIN_SLP(gpb-5, INPUT, UP); + PIN_SLP(gpb-6, INPUT, DOWN); + PIN_SLP(gpb-7, INPUT, DOWN); + + PIN_SLP(gpc0-0, INPUT, DOWN); + PIN_SLP(gpc0-1, INPUT, DOWN); + PIN_SLP(gpc0-2, INPUT, DOWN); + PIN_SLP(gpc0-3, INPUT, DOWN); + PIN_SLP(gpc0-4, INPUT, DOWN); + + PIN_SLP(gpc1-0, INPUT, NONE); + PIN_SLP(gpc1-1, PREV, NONE); + PIN_SLP(gpc1-2, INPUT, NONE); + PIN_SLP(gpc1-3, INPUT, NONE); + PIN_SLP(gpc1-4, INPUT, NONE); + + PIN_SLP(gpd0-0, INPUT, DOWN); + PIN_SLP(gpd0-1, INPUT, DOWN); + PIN_SLP(gpd0-2, INPUT, NONE); + PIN_SLP(gpd0-3, INPUT, NONE); + + PIN_SLP(gpd1-0, INPUT, DOWN); + PIN_SLP(gpd1-1, INPUT, DOWN); + PIN_SLP(gpd1-2, INPUT, NONE); + PIN_SLP(gpd1-3, INPUT, NONE); + + PIN_SLP(gpf0-0, INPUT, NONE); + PIN_SLP(gpf0-1, INPUT, NONE); + PIN_SLP(gpf0-2, INPUT, DOWN); + PIN_SLP(gpf0-3, INPUT, DOWN); + PIN_SLP(gpf0-4, INPUT, NONE); + PIN_SLP(gpf0-5, INPUT, DOWN); + PIN_SLP(gpf0-6, INPUT, NONE); + PIN_SLP(gpf0-7, INPUT, DOWN); + + PIN_SLP(gpf1-0, INPUT, DOWN); + PIN_SLP(gpf1-1, INPUT, DOWN); + PIN_SLP(gpf1-2, INPUT, DOWN); + PIN_SLP(gpf1-3, INPUT, DOWN); + PIN_SLP(gpf1-4, INPUT, NONE); + PIN_SLP(gpf1-5, INPUT, NONE); + PIN_SLP(gpf1-6, INPUT, DOWN); + PIN_SLP(gpf1-7, PREV, NONE); + + PIN_SLP(gpf2-0, PREV, NONE); + PIN_SLP(gpf2-1, INPUT, DOWN); + PIN_SLP(gpf2-2, INPUT, DOWN); + PIN_SLP(gpf2-3, INPUT, DOWN); + PIN_SLP(gpf2-4, INPUT, DOWN); + PIN_SLP(gpf2-5, INPUT, DOWN); + PIN_SLP(gpf2-6, INPUT, NONE); + PIN_SLP(gpf2-7, INPUT, NONE); + + PIN_SLP(gpf3-0, INPUT, NONE); + PIN_SLP(gpf3-1, PREV, NONE); + PIN_SLP(gpf3-2, PREV, NONE); + PIN_SLP(gpf3-3, PREV, NONE); + PIN_SLP(gpf3-4, OUT1, NONE); + PIN_SLP(gpf3-5, INPUT, DOWN); + + PIN_SLP(gpj0-0, PREV, NONE); + PIN_SLP(gpj0-1, PREV, NONE); + PIN_SLP(gpj0-2, PREV, NONE); + PIN_SLP(gpj0-3, INPUT, DOWN); + PIN_SLP(gpj0-4, PREV, NONE); + PIN_SLP(gpj0-5, PREV, NONE); + PIN_SLP(gpj0-6, INPUT, DOWN); + PIN_SLP(gpj0-7, INPUT, DOWN); + + PIN_SLP(gpj1-0, INPUT, DOWN); + PIN_SLP(gpj1-1, PREV, NONE); + PIN_SLP(gpj1-2, PREV, NONE); + PIN_SLP(gpj1-3, INPUT, DOWN); + PIN_SLP(gpj1-4, INPUT, DOWN); + }; +}; + +&pinctrl_1 { + pinctrl-names = "default"; + pinctrl-0 = <&sleep1>; + + hdmi_hpd: hdmi-hpd { + samsung,pins = "gpx3-7"; + samsung,pin-pud = ; + }; + + sleep1: sleep-states { + PIN_SLP(gpk0-0, PREV, NONE); + PIN_SLP(gpk0-1, PREV, NONE); + PIN_SLP(gpk0-2, OUT0, NONE); + PIN_SLP(gpk0-3, PREV, NONE); + PIN_SLP(gpk0-4, PREV, NONE); + PIN_SLP(gpk0-5, PREV, NONE); + PIN_SLP(gpk0-6, PREV, NONE); + + PIN_SLP(gpk1-0, INPUT, DOWN); + PIN_SLP(gpk1-1, INPUT, DOWN); + PIN_SLP(gpk1-2, INPUT, DOWN); + PIN_SLP(gpk1-3, PREV, NONE); + PIN_SLP(gpk1-4, PREV, NONE); + PIN_SLP(gpk1-5, PREV, NONE); + PIN_SLP(gpk1-6, PREV, NONE); + + PIN_SLP(gpk2-0, INPUT, DOWN); + PIN_SLP(gpk2-1, INPUT, DOWN); + PIN_SLP(gpk2-2, INPUT, DOWN); + PIN_SLP(gpk2-3, INPUT, DOWN); + PIN_SLP(gpk2-4, INPUT, DOWN); + PIN_SLP(gpk2-5, INPUT, DOWN); + PIN_SLP(gpk2-6, INPUT, DOWN); + + PIN_SLP(gpk3-0, OUT0, NONE); + PIN_SLP(gpk3-1, INPUT, NONE); + PIN_SLP(gpk3-2, INPUT, DOWN); + PIN_SLP(gpk3-3, INPUT, NONE); + PIN_SLP(gpk3-4, INPUT, NONE); + PIN_SLP(gpk3-5, INPUT, NONE); + PIN_SLP(gpk3-6, INPUT, NONE); + + PIN_SLP(gpl0-0, INPUT, DOWN); + PIN_SLP(gpl0-1, INPUT, DOWN); + PIN_SLP(gpl0-2, INPUT, DOWN); + PIN_SLP(gpl0-3, INPUT, DOWN); + PIN_SLP(gpl0-4, PREV, NONE); + PIN_SLP(gpl0-6, PREV, NONE); + + PIN_SLP(gpl1-0, INPUT, DOWN); + PIN_SLP(gpl1-1, INPUT, DOWN); + PIN_SLP(gpl2-0, INPUT, DOWN); + PIN_SLP(gpl2-1, INPUT, DOWN); + PIN_SLP(gpl2-2, INPUT, DOWN); + PIN_SLP(gpl2-3, INPUT, DOWN); + PIN_SLP(gpl2-4, INPUT, DOWN); + PIN_SLP(gpl2-5, INPUT, DOWN); + PIN_SLP(gpl2-6, PREV, NONE); + PIN_SLP(gpl2-7, INPUT, DOWN); + + PIN_SLP(gpm0-0, INPUT, DOWN); + PIN_SLP(gpm0-1, INPUT, DOWN); + PIN_SLP(gpm0-2, INPUT, DOWN); + PIN_SLP(gpm0-3, INPUT, DOWN); + PIN_SLP(gpm0-4, INPUT, DOWN); + PIN_SLP(gpm0-5, INPUT, DOWN); + PIN_SLP(gpm0-6, INPUT, DOWN); + PIN_SLP(gpm0-7, INPUT, DOWN); + + PIN_SLP(gpm1-0, INPUT, DOWN); + PIN_SLP(gpm1-1, INPUT, DOWN); + PIN_SLP(gpm1-2, INPUT, NONE); + PIN_SLP(gpm1-3, INPUT, NONE); + PIN_SLP(gpm1-4, INPUT, NONE); + PIN_SLP(gpm1-5, INPUT, NONE); + PIN_SLP(gpm1-6, INPUT, DOWN); + + PIN_SLP(gpm2-0, INPUT, NONE); + PIN_SLP(gpm2-1, INPUT, NONE); + PIN_SLP(gpm2-2, INPUT, DOWN); + PIN_SLP(gpm2-3, INPUT, DOWN); + PIN_SLP(gpm2-4, INPUT, DOWN); + + PIN_SLP(gpm3-0, PREV, NONE); + PIN_SLP(gpm3-1, PREV, NONE); + PIN_SLP(gpm3-2, PREV, NONE); + PIN_SLP(gpm3-3, OUT1, NONE); + PIN_SLP(gpm3-4, INPUT, DOWN); + PIN_SLP(gpm3-5, INPUT, DOWN); + PIN_SLP(gpm3-6, INPUT, DOWN); + PIN_SLP(gpm3-7, INPUT, DOWN); + + PIN_SLP(gpm4-0, INPUT, DOWN); + PIN_SLP(gpm4-1, INPUT, DOWN); + PIN_SLP(gpm4-2, INPUT, DOWN); + PIN_SLP(gpm4-3, INPUT, DOWN); + PIN_SLP(gpm4-4, INPUT, DOWN); + PIN_SLP(gpm4-5, INPUT, DOWN); + PIN_SLP(gpm4-6, INPUT, DOWN); + PIN_SLP(gpm4-7, INPUT, DOWN); + + PIN_SLP(gpy0-0, INPUT, DOWN); + PIN_SLP(gpy0-1, INPUT, DOWN); + PIN_SLP(gpy0-2, INPUT, DOWN); + PIN_SLP(gpy0-3, INPUT, DOWN); + PIN_SLP(gpy0-4, INPUT, DOWN); + PIN_SLP(gpy0-5, INPUT, DOWN); + + PIN_SLP(gpy1-0, INPUT, DOWN); + PIN_SLP(gpy1-1, INPUT, DOWN); + PIN_SLP(gpy1-2, INPUT, DOWN); + PIN_SLP(gpy1-3, INPUT, DOWN); + + PIN_SLP(gpy2-0, PREV, NONE); + PIN_SLP(gpy2-1, INPUT, DOWN); + PIN_SLP(gpy2-2, INPUT, NONE); + PIN_SLP(gpy2-3, INPUT, NONE); + PIN_SLP(gpy2-4, INPUT, NONE); + PIN_SLP(gpy2-5, INPUT, NONE); + + PIN_SLP(gpy3-0, INPUT, DOWN); + PIN_SLP(gpy3-1, INPUT, DOWN); + PIN_SLP(gpy3-2, INPUT, DOWN); + PIN_SLP(gpy3-3, INPUT, DOWN); + PIN_SLP(gpy3-4, INPUT, DOWN); + PIN_SLP(gpy3-5, INPUT, DOWN); + PIN_SLP(gpy3-6, INPUT, DOWN); + PIN_SLP(gpy3-7, INPUT, DOWN); + + PIN_SLP(gpy4-0, INPUT, DOWN); + PIN_SLP(gpy4-1, INPUT, DOWN); + PIN_SLP(gpy4-2, INPUT, DOWN); + PIN_SLP(gpy4-3, INPUT, DOWN); + PIN_SLP(gpy4-4, INPUT, DOWN); + PIN_SLP(gpy4-5, INPUT, DOWN); + PIN_SLP(gpy4-6, INPUT, DOWN); + PIN_SLP(gpy4-7, INPUT, DOWN); + + PIN_SLP(gpy5-0, INPUT, DOWN); + PIN_SLP(gpy5-1, INPUT, DOWN); + PIN_SLP(gpy5-2, INPUT, DOWN); + PIN_SLP(gpy5-3, INPUT, DOWN); + PIN_SLP(gpy5-4, INPUT, DOWN); + PIN_SLP(gpy5-5, INPUT, DOWN); + PIN_SLP(gpy5-6, INPUT, DOWN); + PIN_SLP(gpy5-7, INPUT, DOWN); + + PIN_SLP(gpy6-0, INPUT, DOWN); + PIN_SLP(gpy6-1, INPUT, DOWN); + PIN_SLP(gpy6-2, INPUT, DOWN); + PIN_SLP(gpy6-3, INPUT, DOWN); + PIN_SLP(gpy6-4, INPUT, DOWN); + PIN_SLP(gpy6-5, INPUT, DOWN); + PIN_SLP(gpy6-6, INPUT, DOWN); + PIN_SLP(gpy6-7, INPUT, DOWN); + }; +}; + +&pinctrl_2 { + pinctrl-names = "default"; + pinctrl-0 = <&sleep2>; + + sleep2: sleep-states { + PIN_SLP(gpz-0, INPUT, DOWN); + PIN_SLP(gpz-1, INPUT, DOWN); + PIN_SLP(gpz-2, INPUT, DOWN); + PIN_SLP(gpz-3, INPUT, DOWN); + PIN_SLP(gpz-4, INPUT, DOWN); + PIN_SLP(gpz-5, INPUT, DOWN); + PIN_SLP(gpz-6, INPUT, DOWN); + }; +}; + +&pinctrl_3 { + pinctrl-names = "default"; + pinctrl-0 = <&sleep3>; + + sleep3: sleep-states { + PIN_SLP(gpv0-0, INPUT, DOWN); + PIN_SLP(gpv0-1, INPUT, DOWN); + PIN_SLP(gpv0-2, INPUT, DOWN); + PIN_SLP(gpv0-3, INPUT, DOWN); + PIN_SLP(gpv0-4, INPUT, DOWN); + PIN_SLP(gpv0-5, INPUT, DOWN); + PIN_SLP(gpv0-6, INPUT, DOWN); + PIN_SLP(gpv0-7, INPUT, DOWN); + + PIN_SLP(gpv1-0, INPUT, DOWN); + PIN_SLP(gpv1-1, INPUT, DOWN); + PIN_SLP(gpv1-2, INPUT, DOWN); + PIN_SLP(gpv1-3, INPUT, DOWN); + PIN_SLP(gpv1-4, INPUT, DOWN); + PIN_SLP(gpv1-5, INPUT, DOWN); + PIN_SLP(gpv1-6, INPUT, DOWN); + PIN_SLP(gpv1-7, INPUT, DOWN); + + PIN_SLP(gpv2-0, INPUT, DOWN); + PIN_SLP(gpv2-1, INPUT, DOWN); + PIN_SLP(gpv2-2, INPUT, DOWN); + PIN_SLP(gpv2-3, INPUT, DOWN); + PIN_SLP(gpv2-4, INPUT, DOWN); + PIN_SLP(gpv2-5, INPUT, DOWN); + PIN_SLP(gpv2-6, INPUT, DOWN); + PIN_SLP(gpv2-7, INPUT, DOWN); + + PIN_SLP(gpv3-0, INPUT, DOWN); + PIN_SLP(gpv3-1, INPUT, DOWN); + PIN_SLP(gpv3-2, INPUT, DOWN); + PIN_SLP(gpv3-3, INPUT, DOWN); + PIN_SLP(gpv3-4, INPUT, DOWN); + PIN_SLP(gpv3-5, INPUT, DOWN); + PIN_SLP(gpv3-6, INPUT, DOWN); + PIN_SLP(gpv3-7, INPUT, DOWN); + + PIN_SLP(gpv4-0, INPUT, DOWN); + }; +}; + +&pwm { + pinctrl-0 = <&pwm0_out>; + pinctrl-names = "default"; + samsung,pwm-outputs = <0>; + status = "okay"; +}; + +&rtc { + status = "okay"; + clocks = <&clock CLK_RTC>, <&max77686 MAX77686_CLK_AP>; + clock-names = "rtc", "rtc_src"; +}; + +&sdhci_2 { + bus-width = <4>; + cd-gpios = <&gpx3 4 GPIO_ACTIVE_HIGH>; + cd-inverted; + pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_bus4>; + pinctrl-names = "default"; + vmmc-supply = <&ldo21_reg>; + status = "okay"; +}; + +&sdhci_3 { + #address-cells = <1>; + #size-cells = <0>; + non-removable; + bus-width = <4>; + + mmc-pwrseq = <&wlan_pwrseq>; + pinctrl-names = "default"; + pinctrl-0 = <&sd3_clk &sd3_cmd &sd3_bus4>; + status = "okay"; + + brcmf: wifi@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + interrupt-parent = <&gpx2>; + interrupts = <5 IRQ_TYPE_NONE>; + interrupt-names = "host-wake"; + }; +}; + +&serial_0 { + status = "okay"; +}; + +&serial_1 { + status = "okay"; +}; + +&serial_2 { + status = "okay"; +}; + +&serial_3 { + status = "okay"; +}; + +&spi_1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_bus>; + cs-gpios = <&gpb 5 GPIO_ACTIVE_HIGH>; + status = "okay"; + + s5c73m3_spi: s5c73m3@0 { + compatible = "samsung,s5c73m3"; + spi-max-frequency = <50000000>; + reg = <0>; + controller-data { + samsung,spi-feedback-delay = <2>; + }; + }; +}; + +&tmu { + vtmu-supply = <&ldo10_reg>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts index 95b638fd281f..327ee980d3a5 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts +++ b/arch/arm/boot/dts/exynos4412-trats2.dts @@ -10,23 +10,11 @@ */ /dts-v1/; -#include "exynos4412.dtsi" -#include "exynos4412-ppmu-common.dtsi" -#include -#include -#include -#include +#include "exynos4412-galaxy-s3.dtsi" / { model = "Samsung Trats 2 based on Exynos4412"; - compatible = "samsung,trats2", "samsung,exynos4412", "samsung,exynos4"; - - aliases { - i2c9 = &i2c_ak8975; - i2c10 = &i2c_cm36651; - i2c11 = &i2c_max77693; - i2c12 = &i2c_max77693_fuel; - }; + compatible = "samsung,trats2", "samsung,midas", "samsung,exynos4412", "samsung,exynos4"; memory@40000000 { device_type = "memory"; @@ -35,1377 +23,5 @@ chosen { bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5"; - stdout-path = &serial_2; - }; - - firmware@204f000 { - compatible = "samsung,secure-firmware"; - reg = <0x0204F000 0x1000>; - }; - - fixed-rate-clocks { - xxti { - compatible = "samsung,clock-xxti", "fixed-clock"; - clock-frequency = <0>; - }; - - xusbxti { - compatible = "samsung,clock-xusbxti", "fixed-clock"; - clock-frequency = <24000000>; - }; - }; - - regulators { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <0>; - - cam_io_reg: voltage-regulator-1 { - compatible = "regulator-fixed"; - regulator-name = "CAM_SENSOR_A"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - gpio = <&gpm0 2 GPIO_ACTIVE_HIGH>; - enable-active-high; - }; - - lcd_vdd3_reg: voltage-regulator-2 { - compatible = "regulator-fixed"; - regulator-name = "LCD_VDD_2.2V"; - regulator-min-microvolt = <2200000>; - regulator-max-microvolt = <2200000>; - gpio = <&gpc0 1 GPIO_ACTIVE_HIGH>; - enable-active-high; - }; - - cam_af_reg: voltage-regulator-3 { - compatible = "regulator-fixed"; - regulator-name = "CAM_AF"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - gpio = <&gpm0 4 GPIO_ACTIVE_HIGH>; - enable-active-high; - }; - - ps_als_reg: voltage-regulator-5 { - compatible = "regulator-fixed"; - regulator-name = "LED_A_3.0V"; - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3000000>; - gpio = <&gpj0 5 GPIO_ACTIVE_HIGH>; - enable-active-high; - }; - - vsil12: voltage-regulator-6 { - compatible = "regulator-fixed"; - regulator-name = "VSIL_1.2V"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - gpio = <&gpl0 4 GPIO_ACTIVE_HIGH>; - enable-active-high; - vin-supply = <&buck7_reg>; - }; - - vcc33mhl: voltage-regulator-7 { - compatible = "regulator-fixed"; - regulator-name = "VCC_3.3_MHL"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpl0 4 GPIO_ACTIVE_HIGH>; - enable-active-high; - }; - - vcc18mhl: voltage-regulator-8 { - compatible = "regulator-fixed"; - regulator-name = "VCC_1.8_MHL"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - gpio = <&gpl0 4 GPIO_ACTIVE_HIGH>; - enable-active-high; - }; - }; - - gpio-keys { - compatible = "gpio-keys"; - - key-down { - gpios = <&gpx3 3 GPIO_ACTIVE_LOW>; - linux,code = <114>; - label = "volume down"; - debounce-interval = <10>; - }; - - key-up { - gpios = <&gpx2 2 GPIO_ACTIVE_LOW>; - linux,code = <115>; - label = "volume up"; - debounce-interval = <10>; - }; - - key-power { - gpios = <&gpx2 7 GPIO_ACTIVE_LOW>; - linux,code = <116>; - label = "power"; - debounce-interval = <10>; - wakeup-source; - }; - - key-ok { - gpios = <&gpx0 1 GPIO_ACTIVE_LOW>; - linux,code = <139>; - label = "ok"; - debounce-inteval = <10>; - wakeup-source; - }; }; - - i2c_max77693: i2c-gpio-1 { - compatible = "i2c-gpio"; - gpios = <&gpm2 0 GPIO_ACTIVE_HIGH>, <&gpm2 1 GPIO_ACTIVE_HIGH>; - i2c-gpio,delay-us = <2>; - #address-cells = <1>; - #size-cells = <0>; - status = "okay"; - - max77693@66 { - compatible = "maxim,max77693"; - interrupt-parent = <&gpx1>; - interrupts = <5 IRQ_TYPE_EDGE_FALLING>; - reg = <0x66>; - - regulators { - esafeout1_reg: ESAFEOUT1 { - regulator-name = "ESAFEOUT1"; - }; - esafeout2_reg: ESAFEOUT2 { - regulator-name = "ESAFEOUT2"; - }; - charger_reg: CHARGER { - regulator-name = "CHARGER"; - regulator-min-microamp = <60000>; - regulator-max-microamp = <2580000>; - }; - }; - - max77693_haptic { - compatible = "maxim,max77693-haptic"; - haptic-supply = <&ldo26_reg>; - pwms = <&pwm 0 38022 0>; - }; - - charger { - compatible = "maxim,max77693-charger"; - - maxim,constant-microvolt = <4350000>; - maxim,min-system-microvolt = <3600000>; - maxim,thermal-regulation-celsius = <100>; - maxim,battery-overcurrent-microamp = <3500000>; - maxim,charge-input-threshold-microvolt = <4300000>; - }; - }; - }; - - i2c_max77693_fuel: i2c-gpio-3 { - compatible = "i2c-gpio"; - gpios = <&gpf1 5 GPIO_ACTIVE_HIGH>, <&gpf1 4 GPIO_ACTIVE_HIGH>; - i2c-gpio,delay-us = <2>; - #address-cells = <1>; - #size-cells = <0>; - status = "okay"; - - max77693-fuel-gauge@36 { - compatible = "maxim,max17047"; - interrupt-parent = <&gpx2>; - interrupts = <3 IRQ_TYPE_EDGE_FALLING>; - reg = <0x36>; - - maxim,over-heat-temp = <700>; - maxim,over-volt = <4500>; - }; - }; - - i2c_ak8975: i2c-gpio-0 { - compatible = "i2c-gpio"; - gpios = <&gpy2 4 GPIO_ACTIVE_HIGH>, <&gpy2 5 GPIO_ACTIVE_HIGH>; - i2c-gpio,delay-us = <2>; - #address-cells = <1>; - #size-cells = <0>; - status = "okay"; - - ak8975@c { - compatible = "asahi-kasei,ak8975"; - reg = <0x0c>; - gpios = <&gpj0 7 GPIO_ACTIVE_HIGH>; - }; - }; - - i2c_cm36651: i2c-gpio-2 { - compatible = "i2c-gpio"; - gpios = <&gpf0 0 GPIO_ACTIVE_LOW>, <&gpf0 1 GPIO_ACTIVE_LOW>; - i2c-gpio,delay-us = <2>; - #address-cells = <1>; - #size-cells = <0>; - - cm36651@18 { - compatible = "capella,cm36651"; - reg = <0x18>; - interrupt-parent = <&gpx0>; - interrupts = <2 IRQ_TYPE_EDGE_FALLING>; - vled-supply = <&ps_als_reg>; - }; - }; - - i2c-mhl { - compatible = "i2c-gpio"; - gpios = <&gpf0 4 GPIO_ACTIVE_HIGH>, <&gpf0 6 GPIO_ACTIVE_HIGH>; - i2c-gpio,delay-us = <100>; - #address-cells = <1>; - #size-cells = <0>; - - pinctrl-0 = <&i2c_mhl_bus>; - pinctrl-names = "default"; - status = "okay"; - - sii9234: hdmi-bridge@39 { - compatible = "sil,sii9234"; - avcc33-supply = <&vcc33mhl>; - iovcc18-supply = <&vcc18mhl>; - avcc12-supply = <&vsil12>; - cvcc12-supply = <&vsil12>; - reset-gpios = <&gpf3 4 GPIO_ACTIVE_LOW>; - interrupt-parent = <&gpf3>; - interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; - reg = <0x39>; - - port { - mhl_to_hdmi: endpoint { - remote-endpoint = <&hdmi_to_mhl>; - }; - }; - }; - }; - - - wlan_pwrseq: sdhci3-pwrseq { - compatible = "mmc-pwrseq-simple"; - reset-gpios = <&gpj0 0 GPIO_ACTIVE_LOW>; - clocks = <&max77686 MAX77686_CLK_PMIC>; - clock-names = "ext_clock"; - }; - - sound { - compatible = "samsung,trats2-audio"; - samsung,i2s-controller = <&i2s0>; - samsung,model = "Trats2"; - samsung,audio-codec = <&wm1811>; - samsung,audio-routing = - "SPK", "SPKOUTLN", - "SPK", "SPKOUTLP", - "SPK", "SPKOUTRN", - "SPK", "SPKOUTRP"; - }; - - thermistor-ap { - compatible = "murata,ncp15wb473"; - pullup-uv = <1800000>; /* VCC_1.8V_AP */ - pullup-ohm = <100000>; /* 100K */ - pulldown-ohm = <100000>; /* 100K */ - io-channels = <&adc 1>; /* AP temperature */ - }; - - thermistor-battery { - compatible = "murata,ncp15wb473"; - pullup-uv = <1800000>; /* VCC_1.8V_AP */ - pullup-ohm = <100000>; /* 100K */ - pulldown-ohm = <100000>; /* 100K */ - io-channels = <&adc 2>; /* Battery temperature */ - }; - - thermal-zones { - cpu_thermal: cpu-thermal { - cooling-maps { - map0 { - /* Corresponds to 800MHz at freq_table */ - cooling-device = <&cpu0 7 7>; - }; - map1 { - /* Corresponds to 200MHz at freq_table */ - cooling-device = <&cpu0 13 13>; - }; - }; - }; - }; -}; - -&adc { - vdd-supply = <&ldo3_reg>; - status = "okay"; -}; - -&bus_dmc { - devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>; - vdd-supply = <&buck1_reg>; - status = "okay"; -}; - -&bus_acp { - devfreq = <&bus_dmc>; - status = "okay"; -}; - -&bus_c2c { - devfreq = <&bus_dmc>; - status = "okay"; -}; - -&bus_leftbus { - devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>; - vdd-supply = <&buck3_reg>; - status = "okay"; -}; - -&bus_rightbus { - devfreq = <&bus_leftbus>; - status = "okay"; -}; - -&bus_display { - devfreq = <&bus_leftbus>; - status = "okay"; -}; - -&bus_fsys { - devfreq = <&bus_leftbus>; - status = "okay"; -}; - -&bus_peri { - devfreq = <&bus_leftbus>; - status = "okay"; -}; - -&bus_mfc { - devfreq = <&bus_leftbus>; - status = "okay"; -}; - -&camera { - pinctrl-0 = <&cam_port_a_clk_active &cam_port_b_clk_active>; - pinctrl-names = "default"; - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_CAM0>, - <&clock CLK_MOUT_CAM1>; - assigned-clock-parents = <&clock CLK_XUSBXTI>, - <&clock CLK_XUSBXTI>; -}; - -&cpu0 { - cpu0-supply = <&buck2_reg>; -}; - -&csis_0 { - status = "okay"; - vddcore-supply = <&ldo8_reg>; - vddio-supply = <&ldo10_reg>; - assigned-clocks = <&clock CLK_MOUT_CSIS0>, - <&clock CLK_SCLK_CSIS0>; - assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; - assigned-clock-rates = <0>, <176000000>; - - /* Camera C (3) MIPI CSI-2 (CSIS0) */ - port@3 { - reg = <3>; - csis0_ep: endpoint { - remote-endpoint = <&s5c73m3_ep>; - data-lanes = <1 2 3 4>; - samsung,csis-hs-settle = <12>; - }; - }; -}; - -&csis_1 { - status = "okay"; - vddcore-supply = <&ldo8_reg>; - vddio-supply = <&ldo10_reg>; - assigned-clocks = <&clock CLK_MOUT_CSIS1>, - <&clock CLK_SCLK_CSIS1>; - assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; - assigned-clock-rates = <0>, <176000000>; - - /* Camera D (4) MIPI CSI-2 (CSIS1) */ - port@4 { - reg = <4>; - csis1_ep: endpoint { - remote-endpoint = <&is_s5k6a3_ep>; - data-lanes = <1>; - samsung,csis-hs-settle = <18>; - samsung,csis-wclk; - }; - }; -}; - -&dsi_0 { - vddcore-supply = <&ldo8_reg>; - vddio-supply = <&ldo10_reg>; - samsung,burst-clock-frequency = <500000000>; - samsung,esc-clock-frequency = <20000000>; - samsung,pll-clock-frequency = <24000000>; - status = "okay"; - - panel@0 { - compatible = "samsung,s6e8aa0"; - reg = <0>; - vdd3-supply = <&lcd_vdd3_reg>; - vci-supply = <&ldo25_reg>; - reset-gpios = <&gpf2 1 GPIO_ACTIVE_HIGH>; - power-on-delay= <50>; - reset-delay = <100>; - init-delay = <100>; - flip-horizontal; - flip-vertical; - panel-width-mm = <58>; - panel-height-mm = <103>; - - display-timings { - timing-0 { - clock-frequency = <57153600>; - hactive = <720>; - vactive = <1280>; - hfront-porch = <5>; - hback-porch = <5>; - hsync-len = <5>; - vfront-porch = <13>; - vback-porch = <1>; - vsync-len = <2>; - }; - }; - }; -}; - -&exynos_usbphy { - vbus-supply = <&esafeout1_reg>; - status = "okay"; -}; - -&fimc_0 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC0>, - <&clock CLK_SCLK_FIMC0>; - assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; - assigned-clock-rates = <0>, <176000000>; -}; - -&fimc_1 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC1>, - <&clock CLK_SCLK_FIMC1>; - assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; - assigned-clock-rates = <0>, <176000000>; -}; - -&fimc_2 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC2>, - <&clock CLK_SCLK_FIMC2>; - assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; - assigned-clock-rates = <0>, <176000000>; -}; - -&fimc_3 { - status = "okay"; - assigned-clocks = <&clock CLK_MOUT_FIMC3>, - <&clock CLK_SCLK_FIMC3>; - assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; - assigned-clock-rates = <0>, <176000000>; -}; - -&fimc_is { - pinctrl-0 = <&fimc_is_uart>; - pinctrl-names = "default"; - status = "okay"; -}; - -&fimc_lite_0 { - status = "okay"; -}; - -&fimc_lite_1 { - status = "okay"; -}; - -&fimd { - status = "okay"; -}; - -&hdmi { - hpd-gpios = <&gpx3 7 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&hdmi_hpd>; - vdd-supply = <&ldo3_reg>; - vdd_osc-supply = <&ldo4_reg>; - vdd_pll-supply = <&ldo3_reg>; - ddc = <&i2c_5>; - status = "okay"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - reg = <1>; - hdmi_to_mhl: endpoint { - remote-endpoint = <&mhl_to_hdmi>; - }; - }; - }; -}; - -&hsotg { - vusb_d-supply = <&ldo15_reg>; - vusb_a-supply = <&ldo12_reg>; - dr_mode = "peripheral"; - status = "okay"; -}; - -&i2c_0 { - samsung,i2c-sda-delay = <100>; - samsung,i2c-slave-addr = <0x10>; - samsung,i2c-max-bus-freq = <400000>; - pinctrl-0 = <&i2c0_bus>; - pinctrl-names = "default"; - status = "okay"; - - s5c73m3@3c { - compatible = "samsung,s5c73m3"; - reg = <0x3c>; - standby-gpios = <&gpm0 1 GPIO_ACTIVE_LOW>; /* ISP_STANDBY */ - xshutdown-gpios = <&gpf1 3 GPIO_ACTIVE_LOW>; /* ISP_RESET */ - vdd-int-supply = <&buck9_reg>; - vddio-cis-supply = <&ldo9_reg>; - vdda-supply = <&ldo17_reg>; - vddio-host-supply = <&ldo18_reg>; - vdd-af-supply = <&cam_af_reg>; - vdd-reg-supply = <&cam_io_reg>; - clock-frequency = <24000000>; - /* CAM_A_CLKOUT */ - clocks = <&camera 0>; - clock-names = "cis_extclk"; - port { - s5c73m3_ep: endpoint { - remote-endpoint = <&csis0_ep>; - data-lanes = <1 2 3 4>; - }; - }; - }; -}; - -&i2c1_isp { - pinctrl-0 = <&fimc_is_i2c1>; - pinctrl-names = "default"; - - s5k6a3@10 { - compatible = "samsung,s5k6a3"; - reg = <0x10>; - svdda-supply = <&cam_io_reg>; - svddio-supply = <&ldo19_reg>; - afvdd-supply = <&ldo19_reg>; - clock-frequency = <24000000>; - /* CAM_B_CLKOUT */ - clocks = <&camera 1>; - clock-names = "extclk"; - samsung,camclk-out = <1>; - gpios = <&gpm1 6 GPIO_ACTIVE_HIGH>; - - port { - is_s5k6a3_ep: endpoint { - remote-endpoint = <&csis1_ep>; - data-lanes = <1>; - }; - }; - }; -}; - -&i2c_3 { - samsung,i2c-sda-delay = <100>; - samsung,i2c-slave-addr = <0x10>; - samsung,i2c-max-bus-freq = <400000>; - pinctrl-0 = <&i2c3_bus>; - pinctrl-names = "default"; - status = "okay"; - - mms114-touchscreen@48 { - compatible = "melfas,mms114"; - reg = <0x48>; - interrupt-parent = <&gpm2>; - interrupts = <3 IRQ_TYPE_EDGE_FALLING>; - x-size = <720>; - y-size = <1280>; - avdd-supply = <&ldo23_reg>; - vdd-supply = <&ldo24_reg>; - }; -}; - -&i2c_4 { - samsung,i2c-sda-delay = <100>; - samsung,i2c-slave-addr = <0x10>; - samsung,i2c-max-bus-freq = <100000>; - pinctrl-0 = <&i2c4_bus>; - pinctrl-names = "default"; - status = "okay"; - - wm1811: wm1811@1a { - compatible = "wlf,wm1811"; - reg = <0x1a>; - clocks = <&pmu_system_controller 0>; - clock-names = "MCLK1"; - DCVDD-supply = <&ldo3_reg>; - DBVDD1-supply = <&ldo3_reg>; - wlf,ldo1ena = <&gpj0 4 0>; - }; -}; - -&i2c_5 { - status = "okay"; -}; - -&i2c_7 { - samsung,i2c-sda-delay = <100>; - samsung,i2c-slave-addr = <0x10>; - samsung,i2c-max-bus-freq = <100000>; - pinctrl-0 = <&i2c7_bus>; - pinctrl-names = "default"; - status = "okay"; - - max77686: max77686_pmic@9 { - compatible = "maxim,max77686"; - interrupt-parent = <&gpx0>; - interrupts = <7 IRQ_TYPE_NONE>; - reg = <0x09>; - #clock-cells = <1>; - - voltage-regulators { - ldo1_reg: LDO1 { - regulator-name = "VALIVE_1.0V_AP"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - regulator-always-on; - }; - - ldo2_reg: LDO2 { - regulator-name = "VM1M2_1.2V_AP"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-always-on; - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - ldo3_reg: LDO3 { - regulator-name = "VCC_1.8V_AP"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-always-on; - }; - - ldo4_reg: LDO4 { - regulator-name = "VCC_2.8V_AP"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - regulator-always-on; - }; - - ldo5_reg: LDO5 { - regulator-name = "VCC_1.8V_IO"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-always-on; - }; - - ldo6_reg: LDO6 { - regulator-name = "VMPLL_1.0V_AP"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - regulator-always-on; - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - ldo7_reg: LDO7 { - regulator-name = "VPLL_1.0V_AP"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - regulator-always-on; - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - ldo8_reg: LDO8 { - regulator-name = "VMIPI_1.0V"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - ldo9_reg: LDO9 { - regulator-name = "CAM_ISP_MIPI_1.2V"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - }; - - ldo10_reg: LDO10 { - regulator-name = "VMIPI_1.8V"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - ldo11_reg: LDO11 { - regulator-name = "VABB1_1.95V"; - regulator-min-microvolt = <1950000>; - regulator-max-microvolt = <1950000>; - regulator-always-on; - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - ldo12_reg: LDO12 { - regulator-name = "VUOTG_3.0V"; - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3000000>; - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - ldo13_reg: LDO13 { - regulator-name = "NFC_AVDD_1.8V"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - ldo14_reg: LDO14 { - regulator-name = "VABB2_1.95V"; - regulator-min-microvolt = <1950000>; - regulator-max-microvolt = <1950000>; - regulator-always-on; - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - ldo15_reg: LDO15 { - regulator-name = "VHSIC_1.0V"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - ldo16_reg: LDO16 { - regulator-name = "VHSIC_1.8V"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - ldo17_reg: LDO17 { - regulator-name = "CAM_SENSOR_CORE_1.2V"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - }; - - ldo18_reg: LDO18 { - regulator-name = "CAM_ISP_SEN_IO_1.8V"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - ldo19_reg: LDO19 { - regulator-name = "VT_CAM_1.8V"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - ldo20_reg: LDO20 { - regulator-name = "VDDQ_PRE_1.8V"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - ldo21_reg: LDO21 { - regulator-name = "VTF_2.8V"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - maxim,ena-gpios = <&gpy2 0 GPIO_ACTIVE_HIGH>; - }; - - ldo22_reg: LDO22 { - regulator-name = "VMEM_VDD_2.8V"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - maxim,ena-gpios = <&gpk0 2 GPIO_ACTIVE_HIGH>; - }; - - ldo23_reg: LDO23 { - regulator-name = "TSP_AVDD_3.3V"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - }; - - ldo24_reg: LDO24 { - regulator-name = "TSP_VDD_1.8V"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - ldo25_reg: LDO25 { - regulator-name = "LCD_VCC_3.3V"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - }; - - ldo26_reg: LDO26 { - regulator-name = "MOTOR_VCC_3.0V"; - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3000000>; - }; - - buck1_reg: BUCK1 { - regulator-name = "vdd_mif"; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <1100000>; - regulator-always-on; - regulator-boot-on; - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - buck2_reg: BUCK2 { - regulator-name = "vdd_arm"; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <1500000>; - regulator-always-on; - regulator-boot-on; - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - buck3_reg: BUCK3 { - regulator-name = "vdd_int"; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <1150000>; - regulator-always-on; - regulator-boot-on; - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - buck4_reg: BUCK4 { - regulator-name = "vdd_g3d"; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <1150000>; - regulator-boot-on; - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - buck5_reg: BUCK5 { - regulator-name = "VMEM_1.2V_AP"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-always-on; - }; - - buck6_reg: BUCK6 { - regulator-name = "VCC_SUB_1.35V"; - regulator-min-microvolt = <1350000>; - regulator-max-microvolt = <1350000>; - regulator-always-on; - }; - - buck7_reg: BUCK7 { - regulator-name = "VCC_SUB_2.0V"; - regulator-min-microvolt = <2000000>; - regulator-max-microvolt = <2000000>; - regulator-always-on; - }; - - buck8_reg: BUCK8 { - regulator-name = "VMEM_VDDF_3.0V"; - regulator-min-microvolt = <2850000>; - regulator-max-microvolt = <2850000>; - maxim,ena-gpios = <&gpk0 2 GPIO_ACTIVE_HIGH>; - }; - - buck9_reg: BUCK9 { - regulator-name = "CAM_ISP_CORE_1.2V"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1200000>; - maxim,ena-gpios = <&gpm0 3 GPIO_ACTIVE_HIGH>; - }; - }; - }; -}; - -&i2c_8 { - status = "okay"; -}; - -&i2s0 { - pinctrl-0 = <&i2s0_bus>; - pinctrl-names = "default"; - status = "okay"; -}; - -&mixer { - status = "okay"; -}; - -&mshc_0 { - broken-cd; - non-removable; - card-detect-delay = <200>; - vmmc-supply = <&ldo22_reg>; - clock-frequency = <400000000>; - samsung,dw-mshc-ciu-div = <0>; - samsung,dw-mshc-sdr-timing = <2 3>; - samsung,dw-mshc-ddr-timing = <1 2>; - pinctrl-0 = <&sd4_clk &sd4_cmd &sd4_bus4 &sd4_bus8>; - pinctrl-names = "default"; - status = "okay"; - bus-width = <8>; - cap-mmc-highspeed; -}; - -&pmu_system_controller { - assigned-clocks = <&pmu_system_controller 0>; - assigned-clock-parents = <&clock CLK_XUSBXTI>; -}; - -&pinctrl_0 { - pinctrl-names = "default"; - pinctrl-0 = <&sleep0>; - - mhl_int: mhl-int { - samsung,pins = "gpf3-5"; - samsung,pin-pud = ; - }; - - i2c_mhl_bus: i2c-mhl-bus { - samsung,pins = "gpf0-4", "gpf0-6"; - samsung,pin-function = ; - samsung,pin-pud = ; - samsung,pin-drv = ; - }; - - sleep0: sleep-states { - PIN_SLP(gpa0-0, INPUT, NONE); - PIN_SLP(gpa0-1, OUT0, NONE); - PIN_SLP(gpa0-2, INPUT, NONE); - PIN_SLP(gpa0-3, INPUT, UP); - PIN_SLP(gpa0-4, INPUT, NONE); - PIN_SLP(gpa0-5, INPUT, DOWN); - PIN_SLP(gpa0-6, INPUT, DOWN); - PIN_SLP(gpa0-7, INPUT, UP); - - PIN_SLP(gpa1-0, INPUT, DOWN); - PIN_SLP(gpa1-1, INPUT, DOWN); - PIN_SLP(gpa1-2, INPUT, DOWN); - PIN_SLP(gpa1-3, INPUT, DOWN); - PIN_SLP(gpa1-4, INPUT, DOWN); - PIN_SLP(gpa1-5, INPUT, DOWN); - - PIN_SLP(gpb-0, INPUT, NONE); - PIN_SLP(gpb-1, INPUT, NONE); - PIN_SLP(gpb-2, INPUT, NONE); - PIN_SLP(gpb-3, INPUT, NONE); - PIN_SLP(gpb-4, INPUT, DOWN); - PIN_SLP(gpb-5, INPUT, UP); - PIN_SLP(gpb-6, INPUT, DOWN); - PIN_SLP(gpb-7, INPUT, DOWN); - - PIN_SLP(gpc0-0, INPUT, DOWN); - PIN_SLP(gpc0-1, INPUT, DOWN); - PIN_SLP(gpc0-2, INPUT, DOWN); - PIN_SLP(gpc0-3, INPUT, DOWN); - PIN_SLP(gpc0-4, INPUT, DOWN); - - PIN_SLP(gpc1-0, INPUT, NONE); - PIN_SLP(gpc1-1, PREV, NONE); - PIN_SLP(gpc1-2, INPUT, NONE); - PIN_SLP(gpc1-3, INPUT, NONE); - PIN_SLP(gpc1-4, INPUT, NONE); - - PIN_SLP(gpd0-0, INPUT, DOWN); - PIN_SLP(gpd0-1, INPUT, DOWN); - PIN_SLP(gpd0-2, INPUT, NONE); - PIN_SLP(gpd0-3, INPUT, NONE); - - PIN_SLP(gpd1-0, INPUT, DOWN); - PIN_SLP(gpd1-1, INPUT, DOWN); - PIN_SLP(gpd1-2, INPUT, NONE); - PIN_SLP(gpd1-3, INPUT, NONE); - - PIN_SLP(gpf0-0, INPUT, NONE); - PIN_SLP(gpf0-1, INPUT, NONE); - PIN_SLP(gpf0-2, INPUT, DOWN); - PIN_SLP(gpf0-3, INPUT, DOWN); - PIN_SLP(gpf0-4, INPUT, NONE); - PIN_SLP(gpf0-5, INPUT, DOWN); - PIN_SLP(gpf0-6, INPUT, NONE); - PIN_SLP(gpf0-7, INPUT, DOWN); - - PIN_SLP(gpf1-0, INPUT, DOWN); - PIN_SLP(gpf1-1, INPUT, DOWN); - PIN_SLP(gpf1-2, INPUT, DOWN); - PIN_SLP(gpf1-3, INPUT, DOWN); - PIN_SLP(gpf1-4, INPUT, NONE); - PIN_SLP(gpf1-5, INPUT, NONE); - PIN_SLP(gpf1-6, INPUT, DOWN); - PIN_SLP(gpf1-7, PREV, NONE); - - PIN_SLP(gpf2-0, PREV, NONE); - PIN_SLP(gpf2-1, INPUT, DOWN); - PIN_SLP(gpf2-2, INPUT, DOWN); - PIN_SLP(gpf2-3, INPUT, DOWN); - PIN_SLP(gpf2-4, INPUT, DOWN); - PIN_SLP(gpf2-5, INPUT, DOWN); - PIN_SLP(gpf2-6, INPUT, NONE); - PIN_SLP(gpf2-7, INPUT, NONE); - - PIN_SLP(gpf3-0, INPUT, NONE); - PIN_SLP(gpf3-1, PREV, NONE); - PIN_SLP(gpf3-2, PREV, NONE); - PIN_SLP(gpf3-3, PREV, NONE); - PIN_SLP(gpf3-4, OUT1, NONE); - PIN_SLP(gpf3-5, INPUT, DOWN); - - PIN_SLP(gpj0-0, PREV, NONE); - PIN_SLP(gpj0-1, PREV, NONE); - PIN_SLP(gpj0-2, PREV, NONE); - PIN_SLP(gpj0-3, INPUT, DOWN); - PIN_SLP(gpj0-4, PREV, NONE); - PIN_SLP(gpj0-5, PREV, NONE); - PIN_SLP(gpj0-6, INPUT, DOWN); - PIN_SLP(gpj0-7, INPUT, DOWN); - - PIN_SLP(gpj1-0, INPUT, DOWN); - PIN_SLP(gpj1-1, PREV, NONE); - PIN_SLP(gpj1-2, PREV, NONE); - PIN_SLP(gpj1-3, INPUT, DOWN); - PIN_SLP(gpj1-4, INPUT, DOWN); - }; -}; - -&pinctrl_1 { - pinctrl-names = "default"; - pinctrl-0 = <&sleep1>; - - hdmi_hpd: hdmi-hpd { - samsung,pins = "gpx3-7"; - samsung,pin-pud = ; - }; - - sleep1: sleep-states { - PIN_SLP(gpk0-0, PREV, NONE); - PIN_SLP(gpk0-1, PREV, NONE); - PIN_SLP(gpk0-2, OUT0, NONE); - PIN_SLP(gpk0-3, PREV, NONE); - PIN_SLP(gpk0-4, PREV, NONE); - PIN_SLP(gpk0-5, PREV, NONE); - PIN_SLP(gpk0-6, PREV, NONE); - - PIN_SLP(gpk1-0, INPUT, DOWN); - PIN_SLP(gpk1-1, INPUT, DOWN); - PIN_SLP(gpk1-2, INPUT, DOWN); - PIN_SLP(gpk1-3, PREV, NONE); - PIN_SLP(gpk1-4, PREV, NONE); - PIN_SLP(gpk1-5, PREV, NONE); - PIN_SLP(gpk1-6, PREV, NONE); - - PIN_SLP(gpk2-0, INPUT, DOWN); - PIN_SLP(gpk2-1, INPUT, DOWN); - PIN_SLP(gpk2-2, INPUT, DOWN); - PIN_SLP(gpk2-3, INPUT, DOWN); - PIN_SLP(gpk2-4, INPUT, DOWN); - PIN_SLP(gpk2-5, INPUT, DOWN); - PIN_SLP(gpk2-6, INPUT, DOWN); - - PIN_SLP(gpk3-0, OUT0, NONE); - PIN_SLP(gpk3-1, INPUT, NONE); - PIN_SLP(gpk3-2, INPUT, DOWN); - PIN_SLP(gpk3-3, INPUT, NONE); - PIN_SLP(gpk3-4, INPUT, NONE); - PIN_SLP(gpk3-5, INPUT, NONE); - PIN_SLP(gpk3-6, INPUT, NONE); - - PIN_SLP(gpl0-0, INPUT, DOWN); - PIN_SLP(gpl0-1, INPUT, DOWN); - PIN_SLP(gpl0-2, INPUT, DOWN); - PIN_SLP(gpl0-3, INPUT, DOWN); - PIN_SLP(gpl0-4, PREV, NONE); - PIN_SLP(gpl0-6, PREV, NONE); - - PIN_SLP(gpl1-0, INPUT, DOWN); - PIN_SLP(gpl1-1, INPUT, DOWN); - PIN_SLP(gpl2-0, INPUT, DOWN); - PIN_SLP(gpl2-1, INPUT, DOWN); - PIN_SLP(gpl2-2, INPUT, DOWN); - PIN_SLP(gpl2-3, INPUT, DOWN); - PIN_SLP(gpl2-4, INPUT, DOWN); - PIN_SLP(gpl2-5, INPUT, DOWN); - PIN_SLP(gpl2-6, PREV, NONE); - PIN_SLP(gpl2-7, INPUT, DOWN); - - PIN_SLP(gpm0-0, INPUT, DOWN); - PIN_SLP(gpm0-1, INPUT, DOWN); - PIN_SLP(gpm0-2, INPUT, DOWN); - PIN_SLP(gpm0-3, INPUT, DOWN); - PIN_SLP(gpm0-4, INPUT, DOWN); - PIN_SLP(gpm0-5, INPUT, DOWN); - PIN_SLP(gpm0-6, INPUT, DOWN); - PIN_SLP(gpm0-7, INPUT, DOWN); - - PIN_SLP(gpm1-0, INPUT, DOWN); - PIN_SLP(gpm1-1, INPUT, DOWN); - PIN_SLP(gpm1-2, INPUT, NONE); - PIN_SLP(gpm1-3, INPUT, NONE); - PIN_SLP(gpm1-4, INPUT, NONE); - PIN_SLP(gpm1-5, INPUT, NONE); - PIN_SLP(gpm1-6, INPUT, DOWN); - - PIN_SLP(gpm2-0, INPUT, NONE); - PIN_SLP(gpm2-1, INPUT, NONE); - PIN_SLP(gpm2-2, INPUT, DOWN); - PIN_SLP(gpm2-3, INPUT, DOWN); - PIN_SLP(gpm2-4, INPUT, DOWN); - - PIN_SLP(gpm3-0, PREV, NONE); - PIN_SLP(gpm3-1, PREV, NONE); - PIN_SLP(gpm3-2, PREV, NONE); - PIN_SLP(gpm3-3, OUT1, NONE); - PIN_SLP(gpm3-4, INPUT, DOWN); - PIN_SLP(gpm3-5, INPUT, DOWN); - PIN_SLP(gpm3-6, INPUT, DOWN); - PIN_SLP(gpm3-7, INPUT, DOWN); - - PIN_SLP(gpm4-0, INPUT, DOWN); - PIN_SLP(gpm4-1, INPUT, DOWN); - PIN_SLP(gpm4-2, INPUT, DOWN); - PIN_SLP(gpm4-3, INPUT, DOWN); - PIN_SLP(gpm4-4, INPUT, DOWN); - PIN_SLP(gpm4-5, INPUT, DOWN); - PIN_SLP(gpm4-6, INPUT, DOWN); - PIN_SLP(gpm4-7, INPUT, DOWN); - - PIN_SLP(gpy0-0, INPUT, DOWN); - PIN_SLP(gpy0-1, INPUT, DOWN); - PIN_SLP(gpy0-2, INPUT, DOWN); - PIN_SLP(gpy0-3, INPUT, DOWN); - PIN_SLP(gpy0-4, INPUT, DOWN); - PIN_SLP(gpy0-5, INPUT, DOWN); - - PIN_SLP(gpy1-0, INPUT, DOWN); - PIN_SLP(gpy1-1, INPUT, DOWN); - PIN_SLP(gpy1-2, INPUT, DOWN); - PIN_SLP(gpy1-3, INPUT, DOWN); - - PIN_SLP(gpy2-0, PREV, NONE); - PIN_SLP(gpy2-1, INPUT, DOWN); - PIN_SLP(gpy2-2, INPUT, NONE); - PIN_SLP(gpy2-3, INPUT, NONE); - PIN_SLP(gpy2-4, INPUT, NONE); - PIN_SLP(gpy2-5, INPUT, NONE); - - PIN_SLP(gpy3-0, INPUT, DOWN); - PIN_SLP(gpy3-1, INPUT, DOWN); - PIN_SLP(gpy3-2, INPUT, DOWN); - PIN_SLP(gpy3-3, INPUT, DOWN); - PIN_SLP(gpy3-4, INPUT, DOWN); - PIN_SLP(gpy3-5, INPUT, DOWN); - PIN_SLP(gpy3-6, INPUT, DOWN); - PIN_SLP(gpy3-7, INPUT, DOWN); - - PIN_SLP(gpy4-0, INPUT, DOWN); - PIN_SLP(gpy4-1, INPUT, DOWN); - PIN_SLP(gpy4-2, INPUT, DOWN); - PIN_SLP(gpy4-3, INPUT, DOWN); - PIN_SLP(gpy4-4, INPUT, DOWN); - PIN_SLP(gpy4-5, INPUT, DOWN); - PIN_SLP(gpy4-6, INPUT, DOWN); - PIN_SLP(gpy4-7, INPUT, DOWN); - - PIN_SLP(gpy5-0, INPUT, DOWN); - PIN_SLP(gpy5-1, INPUT, DOWN); - PIN_SLP(gpy5-2, INPUT, DOWN); - PIN_SLP(gpy5-3, INPUT, DOWN); - PIN_SLP(gpy5-4, INPUT, DOWN); - PIN_SLP(gpy5-5, INPUT, DOWN); - PIN_SLP(gpy5-6, INPUT, DOWN); - PIN_SLP(gpy5-7, INPUT, DOWN); - - PIN_SLP(gpy6-0, INPUT, DOWN); - PIN_SLP(gpy6-1, INPUT, DOWN); - PIN_SLP(gpy6-2, INPUT, DOWN); - PIN_SLP(gpy6-3, INPUT, DOWN); - PIN_SLP(gpy6-4, INPUT, DOWN); - PIN_SLP(gpy6-5, INPUT, DOWN); - PIN_SLP(gpy6-6, INPUT, DOWN); - PIN_SLP(gpy6-7, INPUT, DOWN); - }; -}; - -&pinctrl_2 { - pinctrl-names = "default"; - pinctrl-0 = <&sleep2>; - - sleep2: sleep-states { - PIN_SLP(gpz-0, INPUT, DOWN); - PIN_SLP(gpz-1, INPUT, DOWN); - PIN_SLP(gpz-2, INPUT, DOWN); - PIN_SLP(gpz-3, INPUT, DOWN); - PIN_SLP(gpz-4, INPUT, DOWN); - PIN_SLP(gpz-5, INPUT, DOWN); - PIN_SLP(gpz-6, INPUT, DOWN); - }; -}; - -&pinctrl_3 { - pinctrl-names = "default"; - pinctrl-0 = <&sleep3>; - - sleep3: sleep-states { - PIN_SLP(gpv0-0, INPUT, DOWN); - PIN_SLP(gpv0-1, INPUT, DOWN); - PIN_SLP(gpv0-2, INPUT, DOWN); - PIN_SLP(gpv0-3, INPUT, DOWN); - PIN_SLP(gpv0-4, INPUT, DOWN); - PIN_SLP(gpv0-5, INPUT, DOWN); - PIN_SLP(gpv0-6, INPUT, DOWN); - PIN_SLP(gpv0-7, INPUT, DOWN); - - PIN_SLP(gpv1-0, INPUT, DOWN); - PIN_SLP(gpv1-1, INPUT, DOWN); - PIN_SLP(gpv1-2, INPUT, DOWN); - PIN_SLP(gpv1-3, INPUT, DOWN); - PIN_SLP(gpv1-4, INPUT, DOWN); - PIN_SLP(gpv1-5, INPUT, DOWN); - PIN_SLP(gpv1-6, INPUT, DOWN); - PIN_SLP(gpv1-7, INPUT, DOWN); - - PIN_SLP(gpv2-0, INPUT, DOWN); - PIN_SLP(gpv2-1, INPUT, DOWN); - PIN_SLP(gpv2-2, INPUT, DOWN); - PIN_SLP(gpv2-3, INPUT, DOWN); - PIN_SLP(gpv2-4, INPUT, DOWN); - PIN_SLP(gpv2-5, INPUT, DOWN); - PIN_SLP(gpv2-6, INPUT, DOWN); - PIN_SLP(gpv2-7, INPUT, DOWN); - - PIN_SLP(gpv3-0, INPUT, DOWN); - PIN_SLP(gpv3-1, INPUT, DOWN); - PIN_SLP(gpv3-2, INPUT, DOWN); - PIN_SLP(gpv3-3, INPUT, DOWN); - PIN_SLP(gpv3-4, INPUT, DOWN); - PIN_SLP(gpv3-5, INPUT, DOWN); - PIN_SLP(gpv3-6, INPUT, DOWN); - PIN_SLP(gpv3-7, INPUT, DOWN); - - PIN_SLP(gpv4-0, INPUT, DOWN); - }; -}; - -&pwm { - pinctrl-0 = <&pwm0_out>; - pinctrl-names = "default"; - samsung,pwm-outputs = <0>; - status = "okay"; -}; - -&rtc { - status = "okay"; - clocks = <&clock CLK_RTC>, <&max77686 MAX77686_CLK_AP>; - clock-names = "rtc", "rtc_src"; -}; - -&sdhci_2 { - bus-width = <4>; - cd-gpios = <&gpx3 4 GPIO_ACTIVE_HIGH>; - cd-inverted; - pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_bus4>; - pinctrl-names = "default"; - vmmc-supply = <&ldo21_reg>; - status = "okay"; -}; - -&sdhci_3 { - #address-cells = <1>; - #size-cells = <0>; - non-removable; - bus-width = <4>; - - mmc-pwrseq = <&wlan_pwrseq>; - pinctrl-names = "default"; - pinctrl-0 = <&sd3_clk &sd3_cmd &sd3_bus4>; - status = "okay"; - - brcmf: wifi@1 { - reg = <1>; - compatible = "brcm,bcm4329-fmac"; - interrupt-parent = <&gpx2>; - interrupts = <5 IRQ_TYPE_NONE>; - interrupt-names = "host-wake"; - }; -}; - -&serial_0 { - status = "okay"; -}; - -&serial_1 { - status = "okay"; -}; - -&serial_2 { - status = "okay"; -}; - -&serial_3 { - status = "okay"; -}; - -&spi_1 { - pinctrl-names = "default"; - pinctrl-0 = <&spi1_bus>; - cs-gpios = <&gpb 5 GPIO_ACTIVE_HIGH>; - status = "okay"; - - s5c73m3_spi: s5c73m3@0 { - compatible = "samsung,s5c73m3"; - spi-max-frequency = <50000000>; - reg = <0>; - controller-data { - samsung,spi-feedback-delay = <2>; - }; - }; -}; - -&tmu { - vtmu-supply = <&ldo10_reg>; - status = "okay"; }; -- cgit v1.3-14-g43fede From 8bd65bfbf8fcb6f0affa88904bb8af394275105b Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 19 Feb 2018 00:41:23 +1100 Subject: ARM: dts: exynos: Add Samsung's Exynos4412-based Midas boards "Midas" is the codename for a family of smartphones released by Samsung Mobile. It includes the Galaxy S3 (GT-I9300/I9305) and the Galaxy Note 2 (GT-N7100/N7105). The boards largely have the same peripherals: the main differences are touchscreen, display panel and cellular modem. Signed-off-by: Simon Shields Signed-off-by: Krzysztof Kozlowski --- arch/arm/boot/dts/Makefile | 3 ++ arch/arm/boot/dts/exynos4412-i9300.dts | 22 +++++++++++++ arch/arm/boot/dts/exynos4412-i9305.dts | 20 ++++++++++++ arch/arm/boot/dts/exynos4412-n710x.dts | 57 ++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 arch/arm/boot/dts/exynos4412-i9300.dts create mode 100644 arch/arm/boot/dts/exynos4412-i9305.dts create mode 100644 arch/arm/boot/dts/exynos4412-n710x.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index ade7a38543dc..19e62934c241 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -163,7 +163,10 @@ dtb-$(CONFIG_ARCH_EXYNOS4) += \ exynos4210-smdkv310.dtb \ exynos4210-trats.dtb \ exynos4210-universal_c210.dtb \ + exynos4412-i9300.dtb \ + exynos4412-i9305.dtb \ exynos4412-itop-elite.dtb \ + exynos4412-n710x.dtb \ exynos4412-odroidu3.dtb \ exynos4412-odroidx.dtb \ exynos4412-odroidx2.dtb \ diff --git a/arch/arm/boot/dts/exynos4412-i9300.dts b/arch/arm/boot/dts/exynos4412-i9300.dts new file mode 100644 index 000000000000..f8125a945f8d --- /dev/null +++ b/arch/arm/boot/dts/exynos4412-i9300.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Samsung's Exynos4412 based M0 (GT-I9300) board device tree source + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + */ + +/dts-v1/; +#include "exynos4412-galaxy-s3.dtsi" + +/ { + model = "Samsung Galaxy S3 (GT-I9300) based on Exynos4412"; + compatible = "samsung,i9300", "samsung,midas", "samsung,exynos4412", "samsung,exynos4"; + + /* bootargs are passed in by bootloader */ + + memory@40000000 { + device_type = "memory"; + reg = <0x40000000 0x40000000>; + }; +}; diff --git a/arch/arm/boot/dts/exynos4412-i9305.dts b/arch/arm/boot/dts/exynos4412-i9305.dts new file mode 100644 index 000000000000..54a2a55dbf70 --- /dev/null +++ b/arch/arm/boot/dts/exynos4412-i9305.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +#include "exynos4412-galaxy-s3.dtsi" + +/ { + model = "Samsung Galaxy S3 (GT-I9305) based on Exynos4412"; + compatible = "samsung,i9305", "samsung,midas", "samsung,exynos4412", "samsung,exynos4"; + + /* bootargs are passed in by bootloader */ + + memory@40000000 { + device_type = "memory"; + reg = <0x40000000 0x80000000>; + }; +}; + +&i2c0_bus { + /* SCL and SDA pins are swapped */ + samsung,pins = "gpd1-1", "gpd1-0"; +}; diff --git a/arch/arm/boot/dts/exynos4412-n710x.dts b/arch/arm/boot/dts/exynos4412-n710x.dts new file mode 100644 index 000000000000..7d289c257920 --- /dev/null +++ b/arch/arm/boot/dts/exynos4412-n710x.dts @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +#include "exynos4412-midas.dtsi" + +/ { + compatible = "samsung,n710x", "samsung,midas", "samsung,exynos4412", "samsung,exynos4"; + model = "Samsung Galaxy Note 2 (GT-N7100, GT-N7105) based on Exynos4412"; + + memory@40000000 { + device_type = "memory"; + reg = <0x40000000 0x80000000>; + }; + + /* bootargs are passed in by bootloader */ + + regulators { + cam_vdda_reg: voltage-regulator-9 { + compatible = "regulator-fixed"; + regulator-name = "CAM_SENSOR_CORE_1.2V"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + gpio = <&gpm4 1 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + }; +}; + +&buck9_reg { + maxim,ena-gpios = <&gpm1 0 GPIO_ACTIVE_HIGH>; +}; + +&cam_af_reg { + gpio = <&gpm1 1 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&cam_io_reg { + gpio = <&gpm0 7 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&ldo13_reg { + regulator-name = "VCC_1.8V_LCD"; + regulator-always-on; +}; + +&ldo25_reg { + regulator-name = "VCI_3.0V_LCD"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; +}; + +&s5c73m3 { + standby-gpios = <&gpm0 6 GPIO_ACTIVE_LOW>; /* ISP_STANDBY */ + vdda-supply = <&cam_vdda_reg>; + status = "okay"; +}; -- cgit v1.3-14-g43fede From cc14b2b1f0b98926b6640bdd66861188ded850eb Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 19 Feb 2018 00:41:24 +1100 Subject: ARM: EXYNOS: Extend cpuidle support to Midas boards Exynos4412 can only use cpuidle when running under a compatible bootloader/firmware. The Midas family of boards, including Trats2, have such a firmware, so allow them to use cpuidle. Leave the Trats2 check there in order to preserve the ABI. Signed-off-by: Simon Shields Signed-off-by: Krzysztof Kozlowski --- arch/arm/mach-exynos/exynos.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index fbd108ce8745..8c4f5e342dc1 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -192,7 +192,8 @@ static void __init exynos_dt_machine_init(void) #endif if (of_machine_is_compatible("samsung,exynos4210") || (of_machine_is_compatible("samsung,exynos4412") && - of_machine_is_compatible("samsung,trats2")) || + (of_machine_is_compatible("samsung,trats2") || + of_machine_is_compatible("samsung,midas"))) || of_machine_is_compatible("samsung,exynos3250") || of_machine_is_compatible("samsung,exynos5250")) platform_device_register(&exynos_cpuidle); -- cgit v1.3-14-g43fede From cc70bec070f6985383d5afe3b20b6236f4397ca4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 16 Oct 2017 12:02:32 +0200 Subject: um: Restore symbol versions for __memcpy and memcpy If CONFIG_MODVERSIONS=y: WARNING: EXPORT symbol "__memcpy" [vmlinux] version generation failed, symbol will not be versioned. WARNING: EXPORT symbol "memcpy" [vmlinux] version generation failed, symbol will not be versioned. Add , including the generic version, so that genksyms knows the types of these symbols and can generate CRCs for them. Signed-off-by: Geert Uytterhoeven Signed-off-by: Richard Weinberger --- arch/um/include/asm/asm-prototypes.h | 1 + 1 file changed, 1 insertion(+) create mode 100644 arch/um/include/asm/asm-prototypes.h diff --git a/arch/um/include/asm/asm-prototypes.h b/arch/um/include/asm/asm-prototypes.h new file mode 100644 index 000000000000..5898a26daa0d --- /dev/null +++ b/arch/um/include/asm/asm-prototypes.h @@ -0,0 +1 @@ +#include -- cgit v1.3-14-g43fede From 288fb3d5683f5089fa1ba9258e4351170c1f0401 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 2 Nov 2017 13:07:02 +0100 Subject: um: time: Use timespec64 for persistent clock This read_persistent_clock() implementation is the only remaining caller of set_normalized_timespec(). Using read_persistent_clock64() and set_normalized_timespec64() instead lets us remove the deprecated interface in the future and helps make 32-bit arch/um get closer to working beyond 2038. Signed-off-by: Arnd Bergmann Signed-off-by: Richard Weinberger --- arch/um/kernel/time.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c index 7f69d17de354..052de4c8acb2 100644 --- a/arch/um/kernel/time.c +++ b/arch/um/kernel/time.c @@ -121,12 +121,12 @@ static void __init um_timer_setup(void) clockevents_register_device(&timer_clockevent); } -void read_persistent_clock(struct timespec *ts) +void read_persistent_clock64(struct timespec64 *ts) { long long nsecs = os_persistent_clock_emulation(); - set_normalized_timespec(ts, nsecs / NSEC_PER_SEC, - nsecs % NSEC_PER_SEC); + set_normalized_timespec64(ts, nsecs / NSEC_PER_SEC, + nsecs % NSEC_PER_SEC); } void __init time_init(void) -- cgit v1.3-14-g43fede From 4d1a535b8ec5e74b42dfd9dc809142653b2597f6 Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Wed, 15 Nov 2017 11:12:39 +0100 Subject: um: Use POSIX ucontext_t instead of struct ucontext glibc 2.26 removed the 'struct ucontext' to "improve" POSIX compliance and break programs, including User Mode Linux. Fix User Mode Linux by using POSIX ucontext_t. This fixes: arch/um/os-Linux/signal.c: In function 'hard_handler': arch/um/os-Linux/signal.c:163:22: error: dereferencing pointer to incomplete type 'struct ucontext' mcontext_t *mc = &uc->uc_mcontext; arch/x86/um/stub_segv.c: In function 'stub_segv_handler': arch/x86/um/stub_segv.c:16:13: error: dereferencing pointer to incomplete type 'struct ucontext' &uc->uc_mcontext); Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Mazur Signed-off-by: Richard Weinberger --- arch/um/os-Linux/signal.c | 2 +- arch/x86/um/stub_segv.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index a86d7cc2c2d8..a5c0c909c48b 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -159,7 +159,7 @@ static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = { static void hard_handler(int sig, siginfo_t *si, void *p) { - struct ucontext *uc = p; + ucontext_t *uc = p; mcontext_t *mc = &uc->uc_mcontext; unsigned long pending = 1UL << sig; diff --git a/arch/x86/um/stub_segv.c b/arch/x86/um/stub_segv.c index 1518d2805ae8..fd6825537b97 100644 --- a/arch/x86/um/stub_segv.c +++ b/arch/x86/um/stub_segv.c @@ -10,7 +10,7 @@ void __attribute__ ((__section__ (".__syscall_stub"))) stub_segv_handler(int sig, siginfo_t *info, void *p) { - struct ucontext *uc = p; + ucontext_t *uc = p; GET_FAULTINFO_FROM_MC(*((struct faultinfo *) STUB_DATA), &uc->uc_mcontext); -- cgit v1.3-14-g43fede From ff6a17989c08b0bb0fd490cc500b084581b3a9b9 Mon Sep 17 00:00:00 2001 From: Anton Ivanov Date: Mon, 20 Nov 2017 21:17:58 +0000 Subject: Epoll based IRQ controller 1. Removes the need to walk the IRQ/Device list to determine who triggered the IRQ. 2. Improves scalability (up to several times performance improvement for cases with 10s of devices). 3. Improves UML baseline IO performance for one disk + one NIC use case by up to 10%. 4. Introduces write poll triggered IRQs. 5. Prerequisite for introducing high performance mmesg family of functions in network IO. 6. Fixes RNG shutdown which was leaking a file descriptor Signed-off-by: Anton Ivanov Signed-off-by: Richard Weinberger --- arch/um/drivers/chan_kern.c | 53 +---- arch/um/drivers/line.c | 2 +- arch/um/drivers/random.c | 11 +- arch/um/drivers/ubd_kern.c | 4 +- arch/um/include/shared/irq_user.h | 12 +- arch/um/include/shared/os.h | 17 +- arch/um/kernel/irq.c | 460 ++++++++++++++++++++++++-------------- arch/um/os-Linux/irq.c | 202 +++++++++-------- 8 files changed, 444 insertions(+), 317 deletions(-) diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index acbe6c67afba..05588f9466c7 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c @@ -171,56 +171,19 @@ int enable_chan(struct line *line) return err; } -/* Items are added in IRQ context, when free_irq can't be called, and - * removed in process context, when it can. - * This handles interrupt sources which disappear, and which need to - * be permanently disabled. This is discovered in IRQ context, but - * the freeing of the IRQ must be done later. - */ -static DEFINE_SPINLOCK(irqs_to_free_lock); -static LIST_HEAD(irqs_to_free); - -void free_irqs(void) -{ - struct chan *chan; - LIST_HEAD(list); - struct list_head *ele; - unsigned long flags; - - spin_lock_irqsave(&irqs_to_free_lock, flags); - list_splice_init(&irqs_to_free, &list); - spin_unlock_irqrestore(&irqs_to_free_lock, flags); - - list_for_each(ele, &list) { - chan = list_entry(ele, struct chan, free_list); - - if (chan->input && chan->enabled) - um_free_irq(chan->line->driver->read_irq, chan); - if (chan->output && chan->enabled) - um_free_irq(chan->line->driver->write_irq, chan); - chan->enabled = 0; - } -} - static void close_one_chan(struct chan *chan, int delay_free_irq) { - unsigned long flags; - if (!chan->opened) return; - if (delay_free_irq) { - spin_lock_irqsave(&irqs_to_free_lock, flags); - list_add(&chan->free_list, &irqs_to_free); - spin_unlock_irqrestore(&irqs_to_free_lock, flags); - } - else { - if (chan->input && chan->enabled) - um_free_irq(chan->line->driver->read_irq, chan); - if (chan->output && chan->enabled) - um_free_irq(chan->line->driver->write_irq, chan); - chan->enabled = 0; - } + /* we can safely call free now - it will be marked + * as free and freed once the IRQ stopped processing + */ + if (chan->input && chan->enabled) + um_free_irq(chan->line->driver->read_irq, chan); + if (chan->output && chan->enabled) + um_free_irq(chan->line->driver->write_irq, chan); + chan->enabled = 0; if (chan->ops->close != NULL) (*chan->ops->close)(chan->fd, chan->data); diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index 366e57f5e8d6..8d80b27502e6 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -284,7 +284,7 @@ int line_setup_irq(int fd, int input, int output, struct line *line, void *data) if (err) return err; if (output) - err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, + err = um_request_irq(driver->write_irq, fd, IRQ_NONE, line_write_interrupt, IRQF_SHARED, driver->write_irq_name, data); return err; diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c index 37c51a6be690..778a0e52d5a5 100644 --- a/arch/um/drivers/random.c +++ b/arch/um/drivers/random.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -154,7 +155,14 @@ err_out_cleanup_hw: /* * rng_cleanup - shutdown RNG module */ -static void __exit rng_cleanup (void) + +static void cleanup(void) +{ + free_irq_by_fd(random_fd); + os_close_file(random_fd); +} + +static void __exit rng_cleanup(void) { os_close_file(random_fd); misc_deregister (&rng_miscdev); @@ -162,6 +170,7 @@ static void __exit rng_cleanup (void) module_init (rng_init); module_exit (rng_cleanup); +__uml_exitcall(cleanup); MODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver"); MODULE_LICENSE("GPL"); diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index b55fe9bf5d3e..d4e8c497ae86 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -1587,11 +1587,11 @@ int io_thread(void *arg) do { res = os_write_file(kernel_fd, ((char *) io_req_buffer) + written, n); - if (res > 0) { + if (res >= 0) { written += res; } else { if (res != -EAGAIN) { - printk("io_thread - read failed, fd = %d, " + printk("io_thread - write failed, fd = %d, " "err = %d\n", kernel_fd, -n); } } diff --git a/arch/um/include/shared/irq_user.h b/arch/um/include/shared/irq_user.h index df5633053957..a7a6120f19d5 100644 --- a/arch/um/include/shared/irq_user.h +++ b/arch/um/include/shared/irq_user.h @@ -7,6 +7,7 @@ #define __IRQ_USER_H__ #include +#include struct irq_fd { struct irq_fd *next; @@ -15,10 +16,17 @@ struct irq_fd { int type; int irq; int events; - int current_events; + bool active; + bool pending; + bool purge; }; -enum { IRQ_READ, IRQ_WRITE }; +#define IRQ_READ 0 +#define IRQ_WRITE 1 +#define IRQ_NONE 2 +#define MAX_IRQ_TYPE (IRQ_NONE + 1) + + struct siginfo; extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index d8ddaf9790d2..048ae37eb5aa 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -290,15 +290,16 @@ extern void halt_skas(void); extern void reboot_skas(void); /* irq.c */ -extern int os_waiting_for_events(struct irq_fd *active_fds); -extern int os_create_pollfd(int fd, int events, void *tmp_pfd, int size_tmpfds); -extern void os_free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg, - struct irq_fd *active_fds, struct irq_fd ***last_irq_ptr2); -extern void os_free_irq_later(struct irq_fd *active_fds, - int irq, void *dev_id); -extern int os_get_pollfd(int i); -extern void os_set_pollfd(int i, int fd); +extern int os_waiting_for_events_epoll(void); +extern void *os_epoll_get_data_pointer(int index); +extern int os_epoll_triggered(int index, int events); +extern int os_event_mask(int irq_type); +extern int os_setup_epoll(void); +extern int os_add_epoll_fd(int events, int fd, void *data); +extern int os_mod_epoll_fd(int events, int fd, void *data); +extern int os_del_epoll_fd(int fd); extern void os_set_ioignore(void); +extern void os_close_epoll_fd(void); /* sigio.c */ extern int add_sigio_fd(int fd); diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index 23cb9350d47e..980148d56537 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -1,4 +1,6 @@ /* + * Copyright (C) 2017 - Cambridge Greys Ltd + * Copyright (C) 2011 - 2014 Cisco Systems Inc * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c: @@ -16,243 +18,361 @@ #include #include #include +#include -/* - * This list is accessed under irq_lock, except in sigio_handler, - * where it is safe from being modified. IRQ handlers won't change it - - * if an IRQ source has vanished, it will be freed by free_irqs just - * before returning from sigio_handler. That will process a separate - * list of irqs to free, with its own locking, coming back here to - * remove list elements, taking the irq_lock to do so. + +/* When epoll triggers we do not know why it did so + * we can also have different IRQs for read and write. + * This is why we keep a small irq_fd array for each fd - + * one entry per IRQ type */ -static struct irq_fd *active_fds = NULL; -static struct irq_fd **last_irq_ptr = &active_fds; -extern void free_irqs(void); +struct irq_entry { + struct irq_entry *next; + int fd; + struct irq_fd *irq_array[MAX_IRQ_TYPE + 1]; +}; + +static struct irq_entry *active_fds; + +static DEFINE_SPINLOCK(irq_lock); + +static void irq_io_loop(struct irq_fd *irq, struct uml_pt_regs *regs) +{ +/* + * irq->active guards against reentry + * irq->pending accumulates pending requests + * if pending is raised the irq_handler is re-run + * until pending is cleared + */ + if (irq->active) { + irq->active = false; + do { + irq->pending = false; + do_IRQ(irq->irq, regs); + } while (irq->pending && (!irq->purge)); + if (!irq->purge) + irq->active = true; + } else { + irq->pending = true; + } +} void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) { - struct irq_fd *irq_fd; - int n; + struct irq_entry *irq_entry; + struct irq_fd *irq; + + int n, i, j; while (1) { - n = os_waiting_for_events(active_fds); + /* This is now lockless - epoll keeps back-referencesto the irqs + * which have trigger it so there is no need to walk the irq + * list and lock it every time. We avoid locking by turning off + * IO for a specific fd by executing os_del_epoll_fd(fd) before + * we do any changes to the actual data structures + */ + n = os_waiting_for_events_epoll(); + if (n <= 0) { if (n == -EINTR) continue; - else break; + else + break; } - for (irq_fd = active_fds; irq_fd != NULL; - irq_fd = irq_fd->next) { - if (irq_fd->current_events != 0) { - irq_fd->current_events = 0; - do_IRQ(irq_fd->irq, regs); + for (i = 0; i < n ; i++) { + /* Epoll back reference is the entry with 3 irq_fd + * leaves - one for each irq type. + */ + irq_entry = (struct irq_entry *) + os_epoll_get_data_pointer(i); + for (j = 0; j < MAX_IRQ_TYPE ; j++) { + irq = irq_entry->irq_array[j]; + if (irq == NULL) + continue; + if (os_epoll_triggered(i, irq->events) > 0) + irq_io_loop(irq, regs); + if (irq->purge) { + irq_entry->irq_array[j] = NULL; + kfree(irq); + } } } } +} + +static int assign_epoll_events_to_irq(struct irq_entry *irq_entry) +{ + int i; + int events = 0; + struct irq_fd *irq; - free_irqs(); + for (i = 0; i < MAX_IRQ_TYPE ; i++) { + irq = irq_entry->irq_array[i]; + if (irq != NULL) + events = irq->events | events; + } + if (events > 0) { + /* os_add_epoll will call os_mod_epoll if this already exists */ + return os_add_epoll_fd(events, irq_entry->fd, irq_entry); + } + /* No events - delete */ + return os_del_epoll_fd(irq_entry->fd); } -static DEFINE_SPINLOCK(irq_lock); + static int activate_fd(int irq, int fd, int type, void *dev_id) { - struct pollfd *tmp_pfd; - struct irq_fd *new_fd, *irq_fd; + struct irq_fd *new_fd; + struct irq_entry *irq_entry; + int i, err, events; unsigned long flags; - int events, err, n; err = os_set_fd_async(fd); if (err < 0) goto out; - err = -ENOMEM; - new_fd = kmalloc(sizeof(struct irq_fd), GFP_KERNEL); - if (new_fd == NULL) - goto out; + spin_lock_irqsave(&irq_lock, flags); - if (type == IRQ_READ) - events = UM_POLLIN | UM_POLLPRI; - else events = UM_POLLOUT; - *new_fd = ((struct irq_fd) { .next = NULL, - .id = dev_id, - .fd = fd, - .type = type, - .irq = irq, - .events = events, - .current_events = 0 } ); + /* Check if we have an entry for this fd */ err = -EBUSY; - spin_lock_irqsave(&irq_lock, flags); - for (irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next) { - if ((irq_fd->fd == fd) && (irq_fd->type == type)) { - printk(KERN_ERR "Registering fd %d twice\n", fd); - printk(KERN_ERR "Irqs : %d, %d\n", irq_fd->irq, irq); - printk(KERN_ERR "Ids : 0x%p, 0x%p\n", irq_fd->id, - dev_id); + for (irq_entry = active_fds; + irq_entry != NULL; irq_entry = irq_entry->next) { + if (irq_entry->fd == fd) + break; + } + + if (irq_entry == NULL) { + /* This needs to be atomic as it may be called from an + * IRQ context. + */ + irq_entry = kmalloc(sizeof(struct irq_entry), GFP_ATOMIC); + if (irq_entry == NULL) { + printk(KERN_ERR + "Failed to allocate new IRQ entry\n"); goto out_unlock; } + irq_entry->fd = fd; + for (i = 0; i < MAX_IRQ_TYPE; i++) + irq_entry->irq_array[i] = NULL; + irq_entry->next = active_fds; + active_fds = irq_entry; } - if (type == IRQ_WRITE) - fd = -1; - - tmp_pfd = NULL; - n = 0; + /* Check if we are trying to re-register an interrupt for a + * particular fd + */ - while (1) { - n = os_create_pollfd(fd, events, tmp_pfd, n); - if (n == 0) - break; + if (irq_entry->irq_array[type] != NULL) { + printk(KERN_ERR + "Trying to reregister IRQ %d FD %d TYPE %d ID %p\n", + irq, fd, type, dev_id + ); + goto out_unlock; + } else { + /* New entry for this fd */ + + err = -ENOMEM; + new_fd = kmalloc(sizeof(struct irq_fd), GFP_ATOMIC); + if (new_fd == NULL) + goto out_unlock; - /* - * n > 0 - * It means we couldn't put new pollfd to current pollfds - * and tmp_fds is NULL or too small for new pollfds array. - * Needed size is equal to n as minimum. - * - * Here we have to drop the lock in order to call - * kmalloc, which might sleep. - * If something else came in and changed the pollfds array - * so we will not be able to put new pollfd struct to pollfds - * then we free the buffer tmp_fds and try again. + events = os_event_mask(type); + + *new_fd = ((struct irq_fd) { + .id = dev_id, + .irq = irq, + .type = type, + .events = events, + .active = true, + .pending = false, + .purge = false + }); + /* Turn off any IO on this fd - allows us to + * avoid locking the IRQ loop */ - spin_unlock_irqrestore(&irq_lock, flags); - kfree(tmp_pfd); - - tmp_pfd = kmalloc(n, GFP_KERNEL); - if (tmp_pfd == NULL) - goto out_kfree; - - spin_lock_irqsave(&irq_lock, flags); + os_del_epoll_fd(irq_entry->fd); + irq_entry->irq_array[type] = new_fd; } - *last_irq_ptr = new_fd; - last_irq_ptr = &new_fd->next; - + /* Turn back IO on with the correct (new) IO event mask */ + assign_epoll_events_to_irq(irq_entry); spin_unlock_irqrestore(&irq_lock, flags); - - /* - * This calls activate_fd, so it has to be outside the critical - * section. - */ - maybe_sigio_broken(fd, (type == IRQ_READ)); + maybe_sigio_broken(fd, (type != IRQ_NONE)); return 0; - - out_unlock: +out_unlock: spin_unlock_irqrestore(&irq_lock, flags); - out_kfree: - kfree(new_fd); - out: +out: return err; } -static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) +/* + * Walk the IRQ list and dispose of any unused entries. + * Should be done under irq_lock. + */ + +static void garbage_collect_irq_entries(void) { - unsigned long flags; + int i; + bool reap; + struct irq_entry *walk; + struct irq_entry *previous = NULL; + struct irq_entry *to_free; - spin_lock_irqsave(&irq_lock, flags); - os_free_irq_by_cb(test, arg, active_fds, &last_irq_ptr); - spin_unlock_irqrestore(&irq_lock, flags); + if (active_fds == NULL) + return; + walk = active_fds; + while (walk != NULL) { + reap = true; + for (i = 0; i < MAX_IRQ_TYPE ; i++) { + if (walk->irq_array[i] != NULL) { + reap = false; + break; + } + } + if (reap) { + if (previous == NULL) + active_fds = walk->next; + else + previous->next = walk->next; + to_free = walk; + } else { + to_free = NULL; + } + walk = walk->next; + if (to_free != NULL) + kfree(to_free); + } } -struct irq_and_dev { - int irq; - void *dev; -}; +/* + * Walk the IRQ list and get the descriptor for our FD + */ -static int same_irq_and_dev(struct irq_fd *irq, void *d) +static struct irq_entry *get_irq_entry_by_fd(int fd) { - struct irq_and_dev *data = d; + struct irq_entry *walk = active_fds; - return ((irq->irq == data->irq) && (irq->id == data->dev)); + while (walk != NULL) { + if (walk->fd == fd) + return walk; + walk = walk->next; + } + return NULL; } -static void free_irq_by_irq_and_dev(unsigned int irq, void *dev) -{ - struct irq_and_dev data = ((struct irq_and_dev) { .irq = irq, - .dev = dev }); - free_irq_by_cb(same_irq_and_dev, &data); -} +/* + * Walk the IRQ list and dispose of an entry for a specific + * device, fd and number. Note - if sharing an IRQ for read + * and writefor the same FD it will be disposed in either case. + * If this behaviour is undesirable use different IRQ ids. + */ -static int same_fd(struct irq_fd *irq, void *fd) -{ - return (irq->fd == *((int *)fd)); -} +#define IGNORE_IRQ 1 +#define IGNORE_DEV (1<<1) -void free_irq_by_fd(int fd) +static void do_free_by_irq_and_dev( + struct irq_entry *irq_entry, + unsigned int irq, + void *dev, + int flags +) { - free_irq_by_cb(same_fd, &fd); + int i; + struct irq_fd *to_free; + + for (i = 0; i < MAX_IRQ_TYPE ; i++) { + if (irq_entry->irq_array[i] != NULL) { + if ( + ((flags & IGNORE_IRQ) || + (irq_entry->irq_array[i]->irq == irq)) && + ((flags & IGNORE_DEV) || + (irq_entry->irq_array[i]->id == dev)) + ) { + /* Turn off any IO on this fd - allows us to + * avoid locking the IRQ loop + */ + os_del_epoll_fd(irq_entry->fd); + to_free = irq_entry->irq_array[i]; + irq_entry->irq_array[i] = NULL; + assign_epoll_events_to_irq(irq_entry); + if (to_free->active) + to_free->purge = true; + else + kfree(to_free); + } + } + } } -/* Must be called with irq_lock held */ -static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out) +void free_irq_by_fd(int fd) { - struct irq_fd *irq; - int i = 0; - int fdi; + struct irq_entry *to_free; + unsigned long flags; - for (irq = active_fds; irq != NULL; irq = irq->next) { - if ((irq->fd == fd) && (irq->irq == irqnum)) - break; - i++; - } - if (irq == NULL) { - printk(KERN_ERR "find_irq_by_fd doesn't have descriptor %d\n", - fd); - goto out; - } - fdi = os_get_pollfd(i); - if ((fdi != -1) && (fdi != fd)) { - printk(KERN_ERR "find_irq_by_fd - mismatch between active_fds " - "and pollfds, fd %d vs %d, need %d\n", irq->fd, - fdi, fd); - irq = NULL; - goto out; + spin_lock_irqsave(&irq_lock, flags); + to_free = get_irq_entry_by_fd(fd); + if (to_free != NULL) { + do_free_by_irq_and_dev( + to_free, + -1, + NULL, + IGNORE_IRQ | IGNORE_DEV + ); } - *index_out = i; - out: - return irq; + garbage_collect_irq_entries(); + spin_unlock_irqrestore(&irq_lock, flags); } -void reactivate_fd(int fd, int irqnum) +static void free_irq_by_irq_and_dev(unsigned int irq, void *dev) { - struct irq_fd *irq; + struct irq_entry *to_free; unsigned long flags; - int i; spin_lock_irqsave(&irq_lock, flags); - irq = find_irq_by_fd(fd, irqnum, &i); - if (irq == NULL) { - spin_unlock_irqrestore(&irq_lock, flags); - return; + to_free = active_fds; + while (to_free != NULL) { + do_free_by_irq_and_dev( + to_free, + irq, + dev, + 0 + ); + to_free = to_free->next; } - os_set_pollfd(i, irq->fd); + garbage_collect_irq_entries(); spin_unlock_irqrestore(&irq_lock, flags); +} - add_sigio_fd(fd); + +void reactivate_fd(int fd, int irqnum) +{ + /** NOP - we do auto-EOI now **/ } void deactivate_fd(int fd, int irqnum) { - struct irq_fd *irq; + struct irq_entry *to_free; unsigned long flags; - int i; + os_del_epoll_fd(fd); spin_lock_irqsave(&irq_lock, flags); - irq = find_irq_by_fd(fd, irqnum, &i); - if (irq == NULL) { - spin_unlock_irqrestore(&irq_lock, flags); - return; + to_free = get_irq_entry_by_fd(fd); + if (to_free != NULL) { + do_free_by_irq_and_dev( + to_free, + irqnum, + NULL, + IGNORE_DEV + ); } - - os_set_pollfd(i, -1); + garbage_collect_irq_entries(); spin_unlock_irqrestore(&irq_lock, flags); - ignore_sigio_fd(fd); } EXPORT_SYMBOL(deactivate_fd); @@ -265,17 +385,28 @@ EXPORT_SYMBOL(deactivate_fd); */ int deactivate_all_fds(void) { - struct irq_fd *irq; - int err; + unsigned long flags; + struct irq_entry *to_free; - for (irq = active_fds; irq != NULL; irq = irq->next) { - err = os_clear_fd_async(irq->fd); - if (err) - return err; - } - /* If there is a signal already queued, after unblocking ignore it */ + spin_lock_irqsave(&irq_lock, flags); + /* Stop IO. The IRQ loop has no lock so this is our + * only way of making sure we are safe to dispose + * of all IRQ handlers + */ os_set_ioignore(); - + to_free = active_fds; + while (to_free != NULL) { + do_free_by_irq_and_dev( + to_free, + -1, + NULL, + IGNORE_IRQ | IGNORE_DEV + ); + to_free = to_free->next; + } + garbage_collect_irq_entries(); + spin_unlock_irqrestore(&irq_lock, flags); + os_close_epoll_fd(); return 0; } @@ -353,8 +484,11 @@ void __init init_IRQ(void) irq_set_chip_and_handler(TIMER_IRQ, &SIGVTALRM_irq_type, handle_edge_irq); + for (i = 1; i < NR_IRQS; i++) irq_set_chip_and_handler(i, &normal_irq_type, handle_edge_irq); + /* Initialize EPOLL Loop */ + os_setup_epoll(); } /* diff --git a/arch/um/os-Linux/irq.c b/arch/um/os-Linux/irq.c index b9afb74b79ad..365823010346 100644 --- a/arch/um/os-Linux/irq.c +++ b/arch/um/os-Linux/irq.c @@ -1,135 +1,147 @@ /* + * Copyright (C) 2017 - Cambridge Greys Ltd + * Copyright (C) 2011 - 2014 Cisco Systems Inc * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ #include #include -#include +#include #include #include #include #include #include +/* Epoll support */ + +static int epollfd = -1; + +#define MAX_EPOLL_EVENTS 64 + +static struct epoll_event epoll_events[MAX_EPOLL_EVENTS]; + +/* Helper to return an Epoll data pointer from an epoll event structure. + * We need to keep this one on the userspace side to keep includes separate + */ + +void *os_epoll_get_data_pointer(int index) +{ + return epoll_events[index].data.ptr; +} + +/* Helper to compare events versus the events in the epoll structure. + * Same as above - needs to be on the userspace side + */ + + +int os_epoll_triggered(int index, int events) +{ + return epoll_events[index].events & events; +} +/* Helper to set the event mask. + * The event mask is opaque to the kernel side, because it does not have + * access to the right includes/defines for EPOLL constants. + */ + +int os_event_mask(int irq_type) +{ + if (irq_type == IRQ_READ) + return EPOLLIN | EPOLLPRI; + if (irq_type == IRQ_WRITE) + return EPOLLOUT; + return 0; +} + /* - * Locked by irq_lock in arch/um/kernel/irq.c. Changed by os_create_pollfd - * and os_free_irq_by_cb, which are called under irq_lock. + * Initial Epoll Setup */ -static struct pollfd *pollfds = NULL; -static int pollfds_num = 0; -static int pollfds_size = 0; +int os_setup_epoll(void) +{ + epollfd = epoll_create(MAX_EPOLL_EVENTS); + return epollfd; +} -int os_waiting_for_events(struct irq_fd *active_fds) +/* + * Helper to run the actual epoll_wait + */ +int os_waiting_for_events_epoll(void) { - struct irq_fd *irq_fd; - int i, n, err; + int n, err; - n = poll(pollfds, pollfds_num, 0); + n = epoll_wait(epollfd, + (struct epoll_event *) &epoll_events, MAX_EPOLL_EVENTS, 0); if (n < 0) { err = -errno; if (errno != EINTR) - printk(UM_KERN_ERR "os_waiting_for_events:" - " poll returned %d, errno = %d\n", n, errno); + printk( + UM_KERN_ERR "os_waiting_for_events:" + " epoll returned %d, error = %s\n", n, + strerror(errno) + ); return err; } - - if (n == 0) - return 0; - - irq_fd = active_fds; - - for (i = 0; i < pollfds_num; i++) { - if (pollfds[i].revents != 0) { - irq_fd->current_events = pollfds[i].revents; - pollfds[i].fd = -1; - } - irq_fd = irq_fd->next; - } return n; } -int os_create_pollfd(int fd, int events, void *tmp_pfd, int size_tmpfds) -{ - if (pollfds_num == pollfds_size) { - if (size_tmpfds <= pollfds_size * sizeof(pollfds[0])) { - /* return min size needed for new pollfds area */ - return (pollfds_size + 1) * sizeof(pollfds[0]); - } - - if (pollfds != NULL) { - memcpy(tmp_pfd, pollfds, - sizeof(pollfds[0]) * pollfds_size); - /* remove old pollfds */ - kfree(pollfds); - } - pollfds = tmp_pfd; - pollfds_size++; - } else - kfree(tmp_pfd); /* remove not used tmp_pfd */ - - pollfds[pollfds_num] = ((struct pollfd) { .fd = fd, - .events = events, - .revents = 0 }); - pollfds_num++; - - return 0; -} -void os_free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg, - struct irq_fd *active_fds, struct irq_fd ***last_irq_ptr2) +/* + * Helper to add a fd to epoll + */ +int os_add_epoll_fd(int events, int fd, void *data) { - struct irq_fd **prev; - int i = 0; - - prev = &active_fds; - while (*prev != NULL) { - if ((*test)(*prev, arg)) { - struct irq_fd *old_fd = *prev; - if ((pollfds[i].fd != -1) && - (pollfds[i].fd != (*prev)->fd)) { - printk(UM_KERN_ERR "os_free_irq_by_cb - " - "mismatch between active_fds and " - "pollfds, fd %d vs %d\n", - (*prev)->fd, pollfds[i].fd); - goto out; - } - - pollfds_num--; - - /* - * This moves the *whole* array after pollfds[i] - * (though it doesn't spot as such)! - */ - memmove(&pollfds[i], &pollfds[i + 1], - (pollfds_num - i) * sizeof(pollfds[0])); - if (*last_irq_ptr2 == &old_fd->next) - *last_irq_ptr2 = prev; - - *prev = (*prev)->next; - if (old_fd->type == IRQ_WRITE) - ignore_sigio_fd(old_fd->fd); - kfree(old_fd); - continue; - } - prev = &(*prev)->next; - i++; - } - out: - return; + struct epoll_event event; + int result; + + event.data.ptr = data; + event.events = events | EPOLLET; + result = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event); + if ((result) && (errno == EEXIST)) + result = os_mod_epoll_fd(events, fd, data); + if (result) + printk("epollctl add err fd %d, %s\n", fd, strerror(errno)); + return result; } -int os_get_pollfd(int i) +/* + * Helper to mod the fd event mask and/or data backreference + */ +int os_mod_epoll_fd(int events, int fd, void *data) { - return pollfds[i].fd; + struct epoll_event event; + int result; + + event.data.ptr = data; + event.events = events; + result = epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event); + if (result) + printk(UM_KERN_ERR + "epollctl mod err fd %d, %s\n", fd, strerror(errno)); + return result; } -void os_set_pollfd(int i, int fd) +/* + * Helper to delete the epoll fd + */ +int os_del_epoll_fd(int fd) { - pollfds[i].fd = fd; + struct epoll_event event; + int result; + /* This is quiet as we use this as IO ON/OFF - so it is often + * invoked on a non-existent fd + */ + result = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event); + return result; } void os_set_ioignore(void) { signal(SIGIO, SIG_IGN); } + +void os_close_epoll_fd(void) +{ + /* Needed so we do not leak an fd when rebooting */ + os_close_file(epollfd); +} -- cgit v1.3-14-g43fede From 49da7e64f33e80edffb1a9eeb230fa4c3f42dffb Mon Sep 17 00:00:00 2001 From: Anton Ivanov Date: Mon, 20 Nov 2017 21:17:59 +0000 Subject: High Performance UML Vector Network Driver 1. Provides infrastructure for vector IO using recvmmsg/sendmmsg. 1.1. Multi-message read. 1.2. Multi-message write. 1.3. Optimized queue support for multi-packet enqueue/dequeue. 1.4. BQL/DQL support. 2. Implements transports for several transports as well support for direct wiring of PWEs to NIC. Allows direct connection of VMs to host, other VMs and network devices with no switch in use. 2.1. Raw socket >4 times higher PPS and 10 times higher tcp RX than existing pcap based transport (> 4Gbit) 2.2. New tap transport using socket RX and tap xmit. Similar performance improvements (>4Gbit) 2.3. GRE transport - direct wiring to GRE PWE 2.4. L2TPv3 transport - direct wiring to L2TPv3 PWE 3. Tuning, performance and offload related setting support via ethtool. 4. Initial BPF support - used in tap/raw to avoid software looping 5. Scatter Gather support. 6. VNET and checksum offload support for raw socket transport. 7. TSO/GSO support where applicable or available 8. Migrates all error messages to netdevice_*() and rate limits them where needed. Signed-off-by: Anton Ivanov Signed-off-by: Richard Weinberger --- arch/um/Kconfig.net | 11 + arch/um/drivers/Makefile | 4 +- arch/um/drivers/net_kern.c | 4 +- arch/um/drivers/vector_kern.c | 1630 +++++++++++++++++++++++++++++++++++ arch/um/drivers/vector_kern.h | 129 +++ arch/um/drivers/vector_transports.c | 458 ++++++++++ arch/um/drivers/vector_user.c | 586 +++++++++++++ arch/um/drivers/vector_user.h | 99 +++ arch/um/include/asm/irq.h | 12 + arch/um/include/shared/net_kern.h | 2 + 10 files changed, 2932 insertions(+), 3 deletions(-) create mode 100644 arch/um/drivers/vector_kern.c create mode 100644 arch/um/drivers/vector_kern.h create mode 100644 arch/um/drivers/vector_transports.c create mode 100644 arch/um/drivers/vector_user.c create mode 100644 arch/um/drivers/vector_user.h diff --git a/arch/um/Kconfig.net b/arch/um/Kconfig.net index e871af24d9cd..c390f3deb0dc 100644 --- a/arch/um/Kconfig.net +++ b/arch/um/Kconfig.net @@ -109,6 +109,17 @@ config UML_NET_DAEMON more than one without conflict. If you don't need UML networking, say N. +config UML_NET_VECTOR + bool "Vector I/O high performance network devices" + depends on UML_NET + help + This User-Mode Linux network driver uses multi-message send + and receive functions. The host running the UML guest must have + a linux kernel version above 3.0 and a libc version > 2.13. + This driver provides tap, raw, gre and l2tpv3 network transports + with up to 4 times higher network throughput than the UML network + drivers. + config UML_NET_VDE bool "VDE transport" depends on UML_NET diff --git a/arch/um/drivers/Makefile b/arch/um/drivers/Makefile index e7582e1d248c..16b3cebddafb 100644 --- a/arch/um/drivers/Makefile +++ b/arch/um/drivers/Makefile @@ -9,6 +9,7 @@ slip-objs := slip_kern.o slip_user.o slirp-objs := slirp_kern.o slirp_user.o daemon-objs := daemon_kern.o daemon_user.o +vector-objs := vector_kern.o vector_user.o vector_transports.o umcast-objs := umcast_kern.o umcast_user.o net-objs := net_kern.o net_user.o mconsole-objs := mconsole_kern.o mconsole_user.o @@ -43,6 +44,7 @@ obj-$(CONFIG_STDERR_CONSOLE) += stderr_console.o obj-$(CONFIG_UML_NET_SLIP) += slip.o slip_common.o obj-$(CONFIG_UML_NET_SLIRP) += slirp.o slip_common.o obj-$(CONFIG_UML_NET_DAEMON) += daemon.o +obj-$(CONFIG_UML_NET_VECTOR) += vector.o obj-$(CONFIG_UML_NET_VDE) += vde.o obj-$(CONFIG_UML_NET_MCAST) += umcast.o obj-$(CONFIG_UML_NET_PCAP) += pcap.o @@ -61,7 +63,7 @@ obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o obj-$(CONFIG_UML_RANDOM) += random.o # pcap_user.o must be added explicitly. -USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o pcap_user.o vde_user.o +USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o pcap_user.o vde_user.o vector_user.o CFLAGS_null.o = -DDEV_NULL=$(DEV_NULL_PATH) include arch/um/scripts/Makefile.rules diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index b305f8247909..3ef1b48e064a 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c @@ -288,7 +288,7 @@ static void uml_net_user_timer_expire(struct timer_list *t) #endif } -static void setup_etheraddr(struct net_device *dev, char *str) +void uml_net_setup_etheraddr(struct net_device *dev, char *str) { unsigned char *addr = dev->dev_addr; char *end; @@ -412,7 +412,7 @@ static void eth_configure(int n, void *init, char *mac, */ snprintf(dev->name, sizeof(dev->name), "eth%d", n); - setup_etheraddr(dev, mac); + uml_net_setup_etheraddr(dev, mac); printk(KERN_INFO "Netdevice %d (%pM) : ", n, dev->dev_addr); diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c new file mode 100644 index 000000000000..d1d53015d572 --- /dev/null +++ b/arch/um/drivers/vector_kern.c @@ -0,0 +1,1630 @@ +/* + * Copyright (C) 2017 - Cambridge Greys Limited + * Copyright (C) 2011 - 2014 Cisco Systems Inc + * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mconsole_kern.h" +#include "vector_user.h" +#include "vector_kern.h" + +/* + * Adapted from network devices with the following major changes: + * All transports are static - simplifies the code significantly + * Multiple FDs/IRQs per device + * Vector IO optionally used for read/write, falling back to legacy + * based on configuration and/or availability + * Configuration is no longer positional - L2TPv3 and GRE require up to + * 10 parameters, passing this as positional is not fit for purpose. + * Only socket transports are supported + */ + + +#define DRIVER_NAME "uml-vector" +#define DRIVER_VERSION "01" +struct vector_cmd_line_arg { + struct list_head list; + int unit; + char *arguments; +}; + +struct vector_device { + struct list_head list; + struct net_device *dev; + struct platform_device pdev; + int unit; + int opened; +}; + +static LIST_HEAD(vec_cmd_line); + +static DEFINE_SPINLOCK(vector_devices_lock); +static LIST_HEAD(vector_devices); + +static int driver_registered; + +static void vector_eth_configure(int n, struct arglist *def); + +/* Argument accessors to set variables (and/or set default values) + * mtu, buffer sizing, default headroom, etc + */ + +#define DEFAULT_HEADROOM 2 +#define SAFETY_MARGIN 32 +#define DEFAULT_VECTOR_SIZE 64 +#define TX_SMALL_PACKET 128 +#define MAX_IOV_SIZE (MAX_SKB_FRAGS + 1) + +static const struct { + const char string[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "rx_queue_max" }, + { "rx_queue_running_average" }, + { "tx_queue_max" }, + { "tx_queue_running_average" }, + { "rx_encaps_errors" }, + { "tx_timeout_count" }, + { "tx_restart_queue" }, + { "tx_kicks" }, + { "tx_flow_control_xon" }, + { "tx_flow_control_xoff" }, + { "rx_csum_offload_good" }, + { "rx_csum_offload_errors"}, + { "sg_ok"}, + { "sg_linearized"}, +}; + +#define VECTOR_NUM_STATS ARRAY_SIZE(ethtool_stats_keys) + +static void vector_reset_stats(struct vector_private *vp) +{ + vp->estats.rx_queue_max = 0; + vp->estats.rx_queue_running_average = 0; + vp->estats.tx_queue_max = 0; + vp->estats.tx_queue_running_average = 0; + vp->estats.rx_encaps_errors = 0; + vp->estats.tx_timeout_count = 0; + vp->estats.tx_restart_queue = 0; + vp->estats.tx_kicks = 0; + vp->estats.tx_flow_control_xon = 0; + vp->estats.tx_flow_control_xoff = 0; + vp->estats.sg_ok = 0; + vp->estats.sg_linearized = 0; +} + +static int get_mtu(struct arglist *def) +{ + char *mtu = uml_vector_fetch_arg(def, "mtu"); + long result; + + if (mtu != NULL) { + if (kstrtoul(mtu, 10, &result) == 0) + return result; + } + return ETH_MAX_PACKET; +} + +static int get_depth(struct arglist *def) +{ + char *mtu = uml_vector_fetch_arg(def, "depth"); + long result; + + if (mtu != NULL) { + if (kstrtoul(mtu, 10, &result) == 0) + return result; + } + return DEFAULT_VECTOR_SIZE; +} + +static int get_headroom(struct arglist *def) +{ + char *mtu = uml_vector_fetch_arg(def, "headroom"); + long result; + + if (mtu != NULL) { + if (kstrtoul(mtu, 10, &result) == 0) + return result; + } + return DEFAULT_HEADROOM; +} + +static int get_req_size(struct arglist *def) +{ + char *gro = uml_vector_fetch_arg(def, "gro"); + long result; + + if (gro != NULL) { + if (kstrtoul(gro, 10, &result) == 0) { + if (result > 0) + return 65536; + } + } + return get_mtu(def) + ETH_HEADER_OTHER + + get_headroom(def) + SAFETY_MARGIN; +} + + +static int get_transport_options(struct arglist *def) +{ + char *transport = uml_vector_fetch_arg(def, "transport"); + char *vector = uml_vector_fetch_arg(def, "vec"); + + int vec_rx = VECTOR_RX; + int vec_tx = VECTOR_TX; + long parsed; + + if (vector != NULL) { + if (kstrtoul(vector, 10, &parsed) == 0) { + if (parsed == 0) { + vec_rx = 0; + vec_tx = 0; + } + } + } + + + if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0) + return (vec_rx | VECTOR_BPF); + if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0) + return (vec_rx | vec_tx | VECTOR_BPF); + return (vec_rx | vec_tx); +} + + +/* A mini-buffer for packet drop read + * All of our supported transports are datagram oriented and we always + * read using recvmsg or recvmmsg. If we pass a buffer which is smaller + * than the packet size it still counts as full packet read and will + * clean the incoming stream to keep sigio/epoll happy + */ + +#define DROP_BUFFER_SIZE 32 + +static char *drop_buffer; + +/* Array backed queues optimized for bulk enqueue/dequeue and + * 1:N (small values of N) or 1:1 enqueuer/dequeuer ratios. + * For more details and full design rationale see + * http://foswiki.cambridgegreys.com/Main/EatYourTailAndEnjoyIt + */ + + +/* + * Advance the mmsg queue head by n = advance. Resets the queue to + * maximum enqueue/dequeue-at-once capacity if possible. Called by + * dequeuers. Caller must hold the head_lock! + */ + +static int vector_advancehead(struct vector_queue *qi, int advance) +{ + int queue_depth; + + qi->head = + (qi->head + advance) + % qi->max_depth; + + + spin_lock(&qi->tail_lock); + qi->queue_depth -= advance; + + /* we are at 0, use this to + * reset head and tail so we can use max size vectors + */ + + if (qi->queue_depth == 0) { + qi->head = 0; + qi->tail = 0; + } + queue_depth = qi->queue_depth; + spin_unlock(&qi->tail_lock); + return queue_depth; +} + +/* Advance the queue tail by n = advance. + * This is called by enqueuers which should hold the + * head lock already + */ + +static int vector_advancetail(struct vector_queue *qi, int advance) +{ + int queue_depth; + + qi->tail = + (qi->tail + advance) + % qi->max_depth; + spin_lock(&qi->head_lock); + qi->queue_depth += advance; + queue_depth = qi->queue_depth; + spin_unlock(&qi->head_lock); + return queue_depth; +} + +static int prep_msg(struct vector_private *vp, + struct sk_buff *skb, + struct iovec *iov) +{ + int iov_index = 0; + int nr_frags, frag; + skb_frag_t *skb_frag; + + nr_frags = skb_shinfo(skb)->nr_frags; + if (nr_frags > MAX_IOV_SIZE) { + if (skb_linearize(skb) != 0) + goto drop; + } + if (vp->header_size > 0) { + iov[iov_index].iov_len = vp->header_size; + vp->form_header(iov[iov_index].iov_base, skb, vp); + iov_index++; + } + iov[iov_index].iov_base = skb->data; + if (nr_frags > 0) { + iov[iov_index].iov_len = skb->len - skb->data_len; + vp->estats.sg_ok++; + } else + iov[iov_index].iov_len = skb->len; + iov_index++; + for (frag = 0; frag < nr_frags; frag++) { + skb_frag = &skb_shinfo(skb)->frags[frag]; + iov[iov_index].iov_base = skb_frag_address_safe(skb_frag); + iov[iov_index].iov_len = skb_frag_size(skb_frag); + iov_index++; + } + return iov_index; +drop: + return -1; +} +/* + * Generic vector enqueue with support for forming headers using transport + * specific callback. Allows GRE, L2TPv3, RAW and other transports + * to use a common enqueue procedure in vector mode + */ + +static int vector_enqueue(struct vector_queue *qi, struct sk_buff *skb) +{ + struct vector_private *vp = netdev_priv(qi->dev); + int queue_depth; + int packet_len; + struct mmsghdr *mmsg_vector = qi->mmsg_vector; + int iov_count; + + spin_lock(&qi->tail_lock); + spin_lock(&qi->head_lock); + queue_depth = qi->queue_depth; + spin_unlock(&qi->head_lock); + + if (skb) + packet_len = skb->len; + + if (queue_depth < qi->max_depth) { + + *(qi->skbuff_vector + qi->tail) = skb; + mmsg_vector += qi->tail; + iov_count = prep_msg( + vp, + skb, + mmsg_vector->msg_hdr.msg_iov + ); + if (iov_count < 1) + goto drop; + mmsg_vector->msg_hdr.msg_iovlen = iov_count; + mmsg_vector->msg_hdr.msg_name = vp->fds->remote_addr; + mmsg_vector->msg_hdr.msg_namelen = vp->fds->remote_addr_size; + queue_depth = vector_advancetail(qi, 1); + } else + goto drop; + spin_unlock(&qi->tail_lock); + return queue_depth; +drop: + qi->dev->stats.tx_dropped++; + if (skb != NULL) { + packet_len = skb->len; + dev_consume_skb_any(skb); + netdev_completed_queue(qi->dev, 1, packet_len); + } + spin_unlock(&qi->tail_lock); + return queue_depth; +} + +static int consume_vector_skbs(struct vector_queue *qi, int count) +{ + struct sk_buff *skb; + int skb_index; + int bytes_compl = 0; + + for (skb_index = qi->head; skb_index < qi->head + count; skb_index++) { + skb = *(qi->skbuff_vector + skb_index); + /* mark as empty to ensure correct destruction if + * needed + */ + bytes_compl += skb->len; + *(qi->skbuff_vector + skb_index) = NULL; + dev_consume_skb_any(skb); + } + qi->dev->stats.tx_bytes += bytes_compl; + qi->dev->stats.tx_packets += count; + netdev_completed_queue(qi->dev, count, bytes_compl); + return vector_advancehead(qi, count); +} + +/* + * Generic vector deque via sendmmsg with support for forming headers + * using transport specific callback. Allows GRE, L2TPv3, RAW and + * other transports to use a common dequeue procedure in vector mode + */ + + +static int vector_send(struct vector_queue *qi) +{ + struct vector_private *vp = netdev_priv(qi->dev); + struct mmsghdr *send_from; + int result = 0, send_len, queue_depth = qi->max_depth; + + if (spin_trylock(&qi->head_lock)) { + if (spin_trylock(&qi->tail_lock)) { + /* update queue_depth to current value */ + queue_depth = qi->queue_depth; + spin_unlock(&qi->tail_lock); + while (queue_depth > 0) { + /* Calculate the start of the vector */ + send_len = queue_depth; + send_from = qi->mmsg_vector; + send_from += qi->head; + /* Adjust vector size if wraparound */ + if (send_len + qi->head > qi->max_depth) + send_len = qi->max_depth - qi->head; + /* Try to TX as many packets as possible */ + if (send_len > 0) { + result = uml_vector_sendmmsg( + vp->fds->tx_fd, + send_from, + send_len, + 0 + ); + vp->in_write_poll = + (result != send_len); + } + /* For some of the sendmmsg error scenarios + * we may end being unsure in the TX success + * for all packets. It is safer to declare + * them all TX-ed and blame the network. + */ + if (result < 0) { + if (net_ratelimit()) + netdev_err(vp->dev, "sendmmsg err=%i\n", + result); + result = send_len; + } + if (result > 0) { + queue_depth = + consume_vector_skbs(qi, result); + /* This is equivalent to an TX IRQ. + * Restart the upper layers to feed us + * more packets. + */ + if (result > vp->estats.tx_queue_max) + vp->estats.tx_queue_max = result; + vp->estats.tx_queue_running_average = + (vp->estats.tx_queue_running_average + result) >> 1; + } + netif_trans_update(qi->dev); + netif_wake_queue(qi->dev); + /* if TX is busy, break out of the send loop, + * poll write IRQ will reschedule xmit for us + */ + if (result != send_len) { + vp->estats.tx_restart_queue++; + break; + } + } + } + spin_unlock(&qi->head_lock); + } else { + tasklet_schedule(&vp->tx_poll); + } + return queue_depth; +} + +/* Queue destructor. Deliberately stateless so we can use + * it in queue cleanup if initialization fails. + */ + +static void destroy_queue(struct vector_queue *qi) +{ + int i; + struct iovec *iov; + struct vector_private *vp = netdev_priv(qi->dev); + struct mmsghdr *mmsg_vector; + + if (qi == NULL) + return; + /* deallocate any skbuffs - we rely on any unused to be + * set to NULL. + */ + if (qi->skbuff_vector != NULL) { + for (i = 0; i < qi->max_depth; i++) { + if (*(qi->skbuff_vector + i) != NULL) + dev_kfree_skb_any(*(qi->skbuff_vector + i)); + } + kfree(qi->skbuff_vector); + } + /* deallocate matching IOV structures including header buffs */ + if (qi->mmsg_vector != NULL) { + mmsg_vector = qi->mmsg_vector; + for (i = 0; i < qi->max_depth; i++) { + iov = mmsg_vector->msg_hdr.msg_iov; + if (iov != NULL) { + if ((vp->header_size > 0) && + (iov->iov_base != NULL)) + kfree(iov->iov_base); + kfree(iov); + } + mmsg_vector++; + } + kfree(qi->mmsg_vector); + } + kfree(qi); +} + +/* + * Queue constructor. Create a queue with a given side. + */ +static struct vector_queue *create_queue( + struct vector_private *vp, + int max_size, + int header_size, + int num_extra_frags) +{ + struct vector_queue *result; + int i; + struct iovec *iov; + struct mmsghdr *mmsg_vector; + + result = kmalloc(sizeof(struct vector_queue), GFP_KERNEL); + if (result == NULL) + goto out_fail; + result->max_depth = max_size; + result->dev = vp->dev; + result->mmsg_vector = kmalloc( + (sizeof(struct mmsghdr) * max_size), GFP_KERNEL); + result->skbuff_vector = kmalloc( + (sizeof(void *) * max_size), GFP_KERNEL); + if (result->mmsg_vector == NULL || result->skbuff_vector == NULL) + goto out_fail; + + mmsg_vector = result->mmsg_vector; + for (i = 0; i < max_size; i++) { + /* Clear all pointers - we use non-NULL as marking on + * what to free on destruction + */ + *(result->skbuff_vector + i) = NULL; + mmsg_vector->msg_hdr.msg_iov = NULL; + mmsg_vector++; + } + mmsg_vector = result->mmsg_vector; + result->max_iov_frags = num_extra_frags; + for (i = 0; i < max_size; i++) { + if (vp->header_size > 0) + iov = kmalloc( + sizeof(struct iovec) * (3 + num_extra_frags), + GFP_KERNEL + ); + else + iov = kmalloc( + sizeof(struct iovec) * (2 + num_extra_frags), + GFP_KERNEL + ); + if (iov == NULL) + goto out_fail; + mmsg_vector->msg_hdr.msg_iov = iov; + mmsg_vector->msg_hdr.msg_iovlen = 1; + mmsg_vector->msg_hdr.msg_control = NULL; + mmsg_vector->msg_hdr.msg_controllen = 0; + mmsg_vector->msg_hdr.msg_flags = MSG_DONTWAIT; + mmsg_vector->msg_hdr.msg_name = NULL; + mmsg_vector->msg_hdr.msg_namelen = 0; + if (vp->header_size > 0) { + iov->iov_base = kmalloc(header_size, GFP_KERNEL); + if (iov->iov_base == NULL) + goto out_fail; + iov->iov_len = header_size; + mmsg_vector->msg_hdr.msg_iovlen = 2; + iov++; + } + iov->iov_base = NULL; + iov->iov_len = 0; + mmsg_vector++; + } + spin_lock_init(&result->head_lock); + spin_lock_init(&result->tail_lock); + result->queue_depth = 0; + result->head = 0; + result->tail = 0; + return result; +out_fail: + destroy_queue(result); + return NULL; +} + +/* + * We do not use the RX queue as a proper wraparound queue for now + * This is not necessary because the consumption via netif_rx() + * happens in-line. While we can try using the return code of + * netif_rx() for flow control there are no drivers doing this today. + * For this RX specific use we ignore the tail/head locks and + * just read into a prepared queue filled with skbuffs. + */ + +static struct sk_buff *prep_skb( + struct vector_private *vp, + struct user_msghdr *msg) +{ + int linear = vp->max_packet + vp->headroom + SAFETY_MARGIN; + struct sk_buff *result; + int iov_index = 0, len; + struct iovec *iov = msg->msg_iov; + int err, nr_frags, frag; + skb_frag_t *skb_frag; + + if (vp->req_size <= linear) + len = linear; + else + len = vp->req_size; + result = alloc_skb_with_frags( + linear, + len - vp->max_packet, + 3, + &err, + GFP_ATOMIC + ); + if (vp->header_size > 0) + iov_index++; + if (result == NULL) { + iov[iov_index].iov_base = NULL; + iov[iov_index].iov_len = 0; + goto done; + } + skb_reserve(result, vp->headroom); + result->dev = vp->dev; + skb_put(result, vp->max_packet); + result->data_len = len - vp->max_packet; + result->len += len - vp->max_packet; + skb_reset_mac_header(result); + result->ip_summed = CHECKSUM_NONE; + iov[iov_index].iov_base = result->data; + iov[iov_index].iov_len = vp->max_packet; + iov_index++; + + nr_frags = skb_shinfo(result)->nr_frags; + for (frag = 0; frag < nr_frags; frag++) { + skb_frag = &skb_shinfo(result)->frags[frag]; + iov[iov_index].iov_base = skb_frag_address_safe(skb_frag); + if (iov[iov_index].iov_base != NULL) + iov[iov_index].iov_len = skb_frag_size(skb_frag); + else + iov[iov_index].iov_len = 0; + iov_index++; + } +done: + msg->msg_iovlen = iov_index; + return result; +} + + +/* Prepare queue for recvmmsg one-shot rx - fill with fresh sk_buffs*/ + +static void prep_queue_for_rx(struct vector_queue *qi) +{ + struct vector_private *vp = netdev_priv(qi->dev); + struct mmsghdr *mmsg_vector = qi->mmsg_vector; + void **skbuff_vector = qi->skbuff_vector; + int i; + + if (qi->queue_depth == 0) + return; + for (i = 0; i < qi->queue_depth; i++) { + /* it is OK if allocation fails - recvmmsg with NULL data in + * iov argument still performs an RX, just drops the packet + * This allows us stop faffing around with a "drop buffer" + */ + + *skbuff_vector = prep_skb(vp, &mmsg_vector->msg_hdr); + skbuff_vector++; + mmsg_vector++; + } + qi->queue_depth = 0; +} + +static struct vector_device *find_device(int n) +{ + struct vector_device *device; + struct list_head *ele; + + spin_lock(&vector_devices_lock); + list_for_each(ele, &vector_devices) { + device = list_entry(ele, struct vector_device, list); + if (device->unit == n) + goto out; + } + device = NULL; + out: + spin_unlock(&vector_devices_lock); + return device; +} + +static int vector_parse(char *str, int *index_out, char **str_out, + char **error_out) +{ + int n, len, err = -EINVAL; + char *start = str; + + len = strlen(str); + + while ((*str != ':') && (strlen(str) > 1)) + str++; + if (*str != ':') { + *error_out = "Expected ':' after device number"; + return err; + } + *str = '\0'; + + err = kstrtouint(start, 0, &n); + if (err < 0) { + *error_out = "Bad device number"; + return err; + } + + str++; + if (find_device(n)) { + *error_out = "Device already configured"; + return err; + } + + *index_out = n; + *str_out = str; + return 0; +} + +static int vector_config(char *str, char **error_out) +{ + int err, n; + char *params; + struct arglist *parsed; + + err = vector_parse(str, &n, ¶ms, error_out); + if (err != 0) + return err; + + /* This string is broken up and the pieces used by the underlying + * driver. We should copy it to make sure things do not go wrong + * later. + */ + + params = kstrdup(params, GFP_KERNEL); + if (str == NULL) { + *error_out = "vector_config failed to strdup string"; + return -ENOMEM; + } + + parsed = uml_parse_vector_ifspec(params); + + if (parsed == NULL) { + *error_out = "vector_config failed to parse parameters"; + return -EINVAL; + } + + vector_eth_configure(n, parsed); + return 0; +} + +static int vector_id(char **str, int *start_out, int *end_out) +{ + char *end; + int n; + + n = simple_strtoul(*str, &end, 0); + if ((*end != '\0') || (end == *str)) + return -1; + + *start_out = n; + *end_out = n; + *str = end; + return n; +} + +static int vector_remove(int n, char **error_out) +{ + struct vector_device *vec_d; + struct net_device *dev; + struct vector_private *vp; + + vec_d = find_device(n); + if (vec_d == NULL) + return -ENODEV; + dev = vec_d->dev; + vp = netdev_priv(dev); + if (vp->fds != NULL) + return -EBUSY; + unregister_netdev(dev); + platform_device_unregister(&vec_d->pdev); + return 0; +} + +/* + * There is no shared per-transport initialization code, so + * we will just initialize each interface one by one and + * add them to a list + */ + +static struct platform_driver uml_net_driver = { + .driver = { + .name = DRIVER_NAME, + }, +}; + + +static void vector_device_release(struct device *dev) +{ + struct vector_device *device = dev_get_drvdata(dev); + struct net_device *netdev = device->dev; + + list_del(&device->list); + kfree(device); + free_netdev(netdev); +} + +/* Bog standard recv using recvmsg - not used normally unless the user + * explicitly specifies not to use recvmmsg vector RX. + */ + +static int vector_legacy_rx(struct vector_private *vp) +{ + int pkt_len; + struct user_msghdr hdr; + struct iovec iov[2 + MAX_IOV_SIZE]; /* header + data use case only */ + int iovpos = 0; + struct sk_buff *skb; + int header_check; + + hdr.msg_name = NULL; + hdr.msg_namelen = 0; + hdr.msg_iov = (struct iovec *) &iov; + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + hdr.msg_flags = 0; + + if (vp->header_size > 0) { + iov[0].iov_base = vp->header_rxbuffer; + iov[0].iov_len = vp->header_size; + } + + skb = prep_skb(vp, &hdr); + + if (skb == NULL) { + /* Read a packet into drop_buffer and don't do + * anything with it. + */ + iov[iovpos].iov_base = drop_buffer; + iov[iovpos].iov_len = DROP_BUFFER_SIZE; + hdr.msg_iovlen = 1; + vp->dev->stats.rx_dropped++; + } + + pkt_len = uml_vector_recvmsg(vp->fds->rx_fd, &hdr, 0); + + if (skb != NULL) { + if (pkt_len > vp->header_size) { + if (vp->header_size > 0) { + header_check = vp->verify_header( + vp->header_rxbuffer, skb, vp); + if (header_check < 0) { + dev_kfree_skb_irq(skb); + vp->dev->stats.rx_dropped++; + vp->estats.rx_encaps_errors++; + return 0; + } + if (header_check > 0) { + vp->estats.rx_csum_offload_good++; + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + } + pskb_trim(skb, pkt_len - vp->rx_header_size); + skb->protocol = eth_type_trans(skb, skb->dev); + vp->dev->stats.rx_bytes += skb->len; + vp->dev->stats.rx_packets++; + netif_rx(skb); + } else { + dev_kfree_skb_irq(skb); + } + } + return pkt_len; +} + +/* + * Packet at a time TX which falls back to vector TX if the + * underlying transport is busy. + */ + + + +static int writev_tx(struct vector_private *vp, struct sk_buff *skb) +{ + struct iovec iov[3 + MAX_IOV_SIZE]; + int iov_count, pkt_len = 0; + + iov[0].iov_base = vp->header_txbuffer; + iov_count = prep_msg(vp, skb, (struct iovec *) &iov); + + if (iov_count < 1) + goto drop; + pkt_len = uml_vector_writev( + vp->fds->tx_fd, + (struct iovec *) &iov, + iov_count + ); + + netif_trans_update(vp->dev); + netif_wake_queue(vp->dev); + + if (pkt_len > 0) { + vp->dev->stats.tx_bytes += skb->len; + vp->dev->stats.tx_packets++; + } else { + vp->dev->stats.tx_dropped++; + } + consume_skb(skb); + return pkt_len; +drop: + vp->dev->stats.tx_dropped++; + consume_skb(skb); + return pkt_len; +} + +/* + * Receive as many messages as we can in one call using the special + * mmsg vector matched to an skb vector which we prepared earlier. + */ + +static int vector_mmsg_rx(struct vector_private *vp) +{ + int packet_count, i; + struct vector_queue *qi = vp->rx_queue; + struct sk_buff *skb; + struct mmsghdr *mmsg_vector = qi->mmsg_vector; + void **skbuff_vector = qi->skbuff_vector; + int header_check; + + /* Refresh the vector and make sure it is with new skbs and the + * iovs are updated to point to them. + */ + + prep_queue_for_rx(qi); + + /* Fire the Lazy Gun - get as many packets as we can in one go. */ + + packet_count = uml_vector_recvmmsg( + vp->fds->rx_fd, qi->mmsg_vector, qi->max_depth, 0); + + if (packet_count <= 0) + return packet_count; + + /* We treat packet processing as enqueue, buffer refresh as dequeue + * The queue_depth tells us how many buffers have been used and how + * many do we need to prep the next time prep_queue_for_rx() is called. + */ + + qi->queue_depth = packet_count; + + for (i = 0; i < packet_count; i++) { + skb = (*skbuff_vector); + if (mmsg_vector->msg_len > vp->header_size) { + if (vp->header_size > 0) { + header_check = vp->verify_header( + mmsg_vector->msg_hdr.msg_iov->iov_base, + skb, + vp + ); + if (header_check < 0) { + /* Overlay header failed to verify - discard. + * We can actually keep this skb and reuse it, + * but that will make the prep logic too + * complex. + */ + dev_kfree_skb_irq(skb); + vp->estats.rx_encaps_errors++; + continue; + } + if (header_check > 0) { + vp->estats.rx_csum_offload_good++; + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + } + pskb_trim(skb, + mmsg_vector->msg_len - vp->rx_header_size); + skb->protocol = eth_type_trans(skb, skb->dev); + /* + * We do not need to lock on updating stats here + * The interrupt loop is non-reentrant. + */ + vp->dev->stats.rx_bytes += skb->len; + vp->dev->stats.rx_packets++; + netif_rx(skb); + } else { + /* Overlay header too short to do anything - discard. + * We can actually keep this skb and reuse it, + * but that will make the prep logic too complex. + */ + if (skb != NULL) + dev_kfree_skb_irq(skb); + } + (*skbuff_vector) = NULL; + /* Move to the next buffer element */ + mmsg_vector++; + skbuff_vector++; + } + if (packet_count > 0) { + if (vp->estats.rx_queue_max < packet_count) + vp->estats.rx_queue_max = packet_count; + vp->estats.rx_queue_running_average = + (vp->estats.rx_queue_running_average + packet_count) >> 1; + } + return packet_count; +} + +static void vector_rx(struct vector_private *vp) +{ + int err; + + if ((vp->options & VECTOR_RX) > 0) + while ((err = vector_mmsg_rx(vp)) > 0) + ; + else + while ((err = vector_legacy_rx(vp)) > 0) + ; + if ((err != 0) && net_ratelimit()) + netdev_err(vp->dev, "vector_rx: error(%d)\n", err); +} + +static int vector_net_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct vector_private *vp = netdev_priv(dev); + int queue_depth = 0; + + if ((vp->options & VECTOR_TX) == 0) { + writev_tx(vp, skb); + return NETDEV_TX_OK; + } + + /* We do BQL only in the vector path, no point doing it in + * packet at a time mode as there is no device queue + */ + + netdev_sent_queue(vp->dev, skb->len); + queue_depth = vector_enqueue(vp->tx_queue, skb); + + /* if the device queue is full, stop the upper layers and + * flush it. + */ + + if (queue_depth >= vp->tx_queue->max_depth - 1) { + vp->estats.tx_kicks++; + netif_stop_queue(dev); + vector_send(vp->tx_queue); + return NETDEV_TX_OK; + } + if (skb->xmit_more) { + mod_timer(&vp->tl, vp->coalesce); + return NETDEV_TX_OK; + } + if (skb->len < TX_SMALL_PACKET) { + vp->estats.tx_kicks++; + vector_send(vp->tx_queue); + } else + tasklet_schedule(&vp->tx_poll); + return NETDEV_TX_OK; +} + +static irqreturn_t vector_rx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct vector_private *vp = netdev_priv(dev); + + if (!netif_running(dev)) + return IRQ_NONE; + vector_rx(vp); + return IRQ_HANDLED; + +} + +static irqreturn_t vector_tx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct vector_private *vp = netdev_priv(dev); + + if (!netif_running(dev)) + return IRQ_NONE; + /* We need to pay attention to it only if we got + * -EAGAIN or -ENOBUFFS from sendmmsg. Otherwise + * we ignore it. In the future, it may be worth + * it to improve the IRQ controller a bit to make + * tweaking the IRQ mask less costly + */ + + if (vp->in_write_poll) + tasklet_schedule(&vp->tx_poll); + return IRQ_HANDLED; + +} + +static int irq_rr; + +static int vector_net_close(struct net_device *dev) +{ + struct vector_private *vp = netdev_priv(dev); + unsigned long flags; + + netif_stop_queue(dev); + del_timer(&vp->tl); + + if (vp->fds == NULL) + return 0; + + /* Disable and free all IRQS */ + if (vp->rx_irq > 0) { + um_free_irq(vp->rx_irq, dev); + vp->rx_irq = 0; + } + if (vp->tx_irq > 0) { + um_free_irq(vp->tx_irq, dev); + vp->tx_irq = 0; + } + tasklet_kill(&vp->tx_poll); + if (vp->fds->rx_fd > 0) { + os_close_file(vp->fds->rx_fd); + vp->fds->rx_fd = -1; + } + if (vp->fds->tx_fd > 0) { + os_close_file(vp->fds->tx_fd); + vp->fds->tx_fd = -1; + } + if (vp->bpf != NULL) + kfree(vp->bpf); + if (vp->fds->remote_addr != NULL) + kfree(vp->fds->remote_addr); + if (vp->transport_data != NULL) + kfree(vp->transport_data); + if (vp->header_rxbuffer != NULL) + kfree(vp->header_rxbuffer); + if (vp->header_txbuffer != NULL) + kfree(vp->header_txbuffer); + if (vp->rx_queue != NULL) + destroy_queue(vp->rx_queue); + if (vp->tx_queue != NULL) + destroy_queue(vp->tx_queue); + kfree(vp->fds); + vp->fds = NULL; + spin_lock_irqsave(&vp->lock, flags); + vp->opened = false; + spin_unlock_irqrestore(&vp->lock, flags); + return 0; +} + +/* TX tasklet */ + +static void vector_tx_poll(unsigned long data) +{ + struct vector_private *vp = (struct vector_private *)data; + + vp->estats.tx_kicks++; + vector_send(vp->tx_queue); +} +static void vector_reset_tx(struct work_struct *work) +{ + struct vector_private *vp = + container_of(work, struct vector_private, reset_tx); + netdev_reset_queue(vp->dev); + netif_start_queue(vp->dev); + netif_wake_queue(vp->dev); +} +static int vector_net_open(struct net_device *dev) +{ + struct vector_private *vp = netdev_priv(dev); + unsigned long flags; + int err = -EINVAL; + struct vector_device *vdevice; + + spin_lock_irqsave(&vp->lock, flags); + if (vp->opened) + return -ENXIO; + vp->opened = true; + spin_unlock_irqrestore(&vp->lock, flags); + + vp->fds = uml_vector_user_open(vp->unit, vp->parsed); + + if (vp->fds == NULL) + goto out_close; + + if (build_transport_data(vp) < 0) + goto out_close; + + if ((vp->options & VECTOR_RX) > 0) { + vp->rx_queue = create_queue( + vp, + get_depth(vp->parsed), + vp->rx_header_size, + MAX_IOV_SIZE + ); + vp->rx_queue->queue_depth = get_depth(vp->parsed); + } else { + vp->header_rxbuffer = kmalloc( + vp->rx_header_size, + GFP_KERNEL + ); + if (vp->header_rxbuffer == NULL) + goto out_close; + } + if ((vp->options & VECTOR_TX) > 0) { + vp->tx_queue = create_queue( + vp, + get_depth(vp->parsed), + vp->header_size, + MAX_IOV_SIZE + ); + } else { + vp->header_txbuffer = kmalloc(vp->header_size, GFP_KERNEL); + if (vp->header_txbuffer == NULL) + goto out_close; + } + + /* READ IRQ */ + err = um_request_irq( + irq_rr + VECTOR_BASE_IRQ, vp->fds->rx_fd, + IRQ_READ, vector_rx_interrupt, + IRQF_SHARED, dev->name, dev); + if (err != 0) { + netdev_err(dev, "vector_open: failed to get rx irq(%d)\n", err); + err = -ENETUNREACH; + goto out_close; + } + vp->rx_irq = irq_rr + VECTOR_BASE_IRQ; + dev->irq = irq_rr + VECTOR_BASE_IRQ; + irq_rr = (irq_rr + 1) % VECTOR_IRQ_SPACE; + + /* WRITE IRQ - we need it only if we have vector TX */ + if ((vp->options & VECTOR_TX) > 0) { + err = um_request_irq( + irq_rr + VECTOR_BASE_IRQ, vp->fds->tx_fd, + IRQ_WRITE, vector_tx_interrupt, + IRQF_SHARED, dev->name, dev); + if (err != 0) { + netdev_err(dev, + "vector_open: failed to get tx irq(%d)\n", err); + err = -ENETUNREACH; + goto out_close; + } + vp->tx_irq = irq_rr + VECTOR_BASE_IRQ; + irq_rr = (irq_rr + 1) % VECTOR_IRQ_SPACE; + } + + if ((vp->options & VECTOR_BPF) != 0) + vp->bpf = uml_vector_default_bpf(vp->fds->rx_fd, dev->dev_addr); + + /* Write Timeout Timer */ + + vp->tl.data = (unsigned long) vp; + netif_start_queue(dev); + + /* clear buffer - it can happen that the host side of the interface + * is full when we get here. In this case, new data is never queued, + * SIGIOs never arrive, and the net never works. + */ + + vector_rx(vp); + + vector_reset_stats(vp); + vdevice = find_device(vp->unit); + vdevice->opened = 1; + + if ((vp->options & VECTOR_TX) != 0) + add_timer(&vp->tl); + return 0; +out_close: + vector_net_close(dev); + return err; +} + + +static void vector_net_set_multicast_list(struct net_device *dev) +{ + /* TODO: - we can do some BPF games here */ + return; +} + +static void vector_net_tx_timeout(struct net_device *dev) +{ + struct vector_private *vp = netdev_priv(dev); + + vp->estats.tx_timeout_count++; + netif_trans_update(dev); + schedule_work(&vp->reset_tx); +} + +static netdev_features_t vector_fix_features(struct net_device *dev, + netdev_features_t features) +{ + features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM); + return features; +} + +static int vector_set_features(struct net_device *dev, + netdev_features_t features) +{ + struct vector_private *vp = netdev_priv(dev); + /* Adjust buffer sizes for GSO/GRO. Unfortunately, there is + * no way to negotiate it on raw sockets, so we can change + * only our side. + */ + if (features & NETIF_F_GRO) + /* All new frame buffers will be GRO-sized */ + vp->req_size = 65536; + else + /* All new frame buffers will be normal sized */ + vp->req_size = vp->max_packet + vp->headroom + SAFETY_MARGIN; + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void vector_net_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + vector_rx_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +static void vector_net_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); +} + +static void vector_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct vector_private *vp = netdev_priv(netdev); + + ring->rx_max_pending = vp->rx_queue->max_depth; + ring->tx_max_pending = vp->tx_queue->max_depth; + ring->rx_pending = vp->rx_queue->max_depth; + ring->tx_pending = vp->tx_queue->max_depth; +} + +static void vector_get_strings(struct net_device *dev, u32 stringset, u8 *buf) +{ + switch (stringset) { + case ETH_SS_TEST: + *buf = '\0'; + break; + case ETH_SS_STATS: + memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); + break; + default: + WARN_ON(1); + break; + } +} + +static int vector_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_TEST: + return 0; + case ETH_SS_STATS: + return VECTOR_NUM_STATS; + default: + return -EOPNOTSUPP; + } +} + +static void vector_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *estats, + u64 *tmp_stats) +{ + struct vector_private *vp = netdev_priv(dev); + + memcpy(tmp_stats, &vp->estats, sizeof(struct vector_estats)); +} + +static int vector_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct vector_private *vp = netdev_priv(netdev); + + ec->tx_coalesce_usecs = (vp->coalesce * 1000000) / HZ; + return 0; +} + +static int vector_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct vector_private *vp = netdev_priv(netdev); + + vp->coalesce = (ec->tx_coalesce_usecs * HZ) / 1000000; + if (vp->coalesce == 0) + vp->coalesce = 1; + return 0; +} + +static const struct ethtool_ops vector_net_ethtool_ops = { + .get_drvinfo = vector_net_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ts_info = ethtool_op_get_ts_info, + .get_ringparam = vector_get_ringparam, + .get_strings = vector_get_strings, + .get_sset_count = vector_get_sset_count, + .get_ethtool_stats = vector_get_ethtool_stats, + .get_coalesce = vector_get_coalesce, + .set_coalesce = vector_set_coalesce, +}; + + +static const struct net_device_ops vector_netdev_ops = { + .ndo_open = vector_net_open, + .ndo_stop = vector_net_close, + .ndo_start_xmit = vector_net_start_xmit, + .ndo_set_rx_mode = vector_net_set_multicast_list, + .ndo_tx_timeout = vector_net_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_fix_features = vector_fix_features, + .ndo_set_features = vector_set_features, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = vector_net_poll_controller, +#endif +}; + + +static void vector_timer_expire(unsigned long _conn) +{ + struct vector_private *vp = (struct vector_private *)_conn; + + vp->estats.tx_kicks++; + vector_send(vp->tx_queue); +} + +static void vector_eth_configure( + int n, + struct arglist *def + ) +{ + struct vector_device *device; + struct net_device *dev; + struct vector_private *vp; + int err; + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (device == NULL) { + printk(KERN_ERR "eth_configure failed to allocate struct " + "vector_device\n"); + return; + } + dev = alloc_etherdev(sizeof(struct vector_private)); + if (dev == NULL) { + printk(KERN_ERR "eth_configure: failed to allocate struct " + "net_device for vec%d\n", n); + goto out_free_device; + } + + dev->mtu = get_mtu(def); + + INIT_LIST_HEAD(&device->list); + device->unit = n; + + /* If this name ends up conflicting with an existing registered + * netdevice, that is OK, register_netdev{,ice}() will notice this + * and fail. + */ + snprintf(dev->name, sizeof(dev->name), "vec%d", n); + uml_net_setup_etheraddr(dev, uml_vector_fetch_arg(def, "mac")); + vp = netdev_priv(dev); + + /* sysfs register */ + if (!driver_registered) { + platform_driver_register(¨_net_driver); + driver_registered = 1; + } + device->pdev.id = n; + device->pdev.name = DRIVER_NAME; + device->pdev.dev.release = vector_device_release; + dev_set_drvdata(&device->pdev.dev, device); + if (platform_device_register(&device->pdev)) + goto out_free_netdev; + SET_NETDEV_DEV(dev, &device->pdev.dev); + + device->dev = dev; + + *vp = ((struct vector_private) + { + .list = LIST_HEAD_INIT(vp->list), + .dev = dev, + .unit = n, + .options = get_transport_options(def), + .rx_irq = 0, + .tx_irq = 0, + .parsed = def, + .max_packet = get_mtu(def) + ETH_HEADER_OTHER, + /* TODO - we need to calculate headroom so that ip header + * is 16 byte aligned all the time + */ + .headroom = get_headroom(def), + .form_header = NULL, + .verify_header = NULL, + .header_rxbuffer = NULL, + .header_txbuffer = NULL, + .header_size = 0, + .rx_header_size = 0, + .rexmit_scheduled = false, + .opened = false, + .transport_data = NULL, + .in_write_poll = false, + .coalesce = 2, + .req_size = get_req_size(def) + }); + + dev->features = dev->hw_features = (NETIF_F_SG | NETIF_F_FRAGLIST); + tasklet_init(&vp->tx_poll, vector_tx_poll, (unsigned long)vp); + INIT_WORK(&vp->reset_tx, vector_reset_tx); + + init_timer(&vp->tl); + spin_lock_init(&vp->lock); + vp->tl.function = vector_timer_expire; + + /* FIXME */ + dev->netdev_ops = &vector_netdev_ops; + dev->ethtool_ops = &vector_net_ethtool_ops; + dev->watchdog_timeo = (HZ >> 1); + /* primary IRQ - fixme */ + dev->irq = 0; /* we will adjust this once opened */ + + rtnl_lock(); + err = register_netdevice(dev); + rtnl_unlock(); + if (err) + goto out_undo_user_init; + + spin_lock(&vector_devices_lock); + list_add(&device->list, &vector_devices); + spin_unlock(&vector_devices_lock); + + return; + +out_undo_user_init: + return; +out_free_netdev: + free_netdev(dev); +out_free_device: + kfree(device); +} + + + + +/* + * Invoked late in the init + */ + +static int __init vector_init(void) +{ + struct list_head *ele; + struct vector_cmd_line_arg *def; + struct arglist *parsed; + + list_for_each(ele, &vec_cmd_line) { + def = list_entry(ele, struct vector_cmd_line_arg, list); + parsed = uml_parse_vector_ifspec(def->arguments); + if (parsed != NULL) + vector_eth_configure(def->unit, parsed); + } + return 0; +} + + +/* Invoked at initial argument parsing, only stores + * arguments until a proper vector_init is called + * later + */ + +static int __init vector_setup(char *str) +{ + char *error; + int n, err; + struct vector_cmd_line_arg *new; + + err = vector_parse(str, &n, &str, &error); + if (err) { + printk(KERN_ERR "vector_setup - Couldn't parse '%s' : %s\n", + str, error); + return 1; + } + new = alloc_bootmem(sizeof(*new)); + INIT_LIST_HEAD(&new->list); + new->unit = n; + new->arguments = str; + list_add_tail(&new->list, &vec_cmd_line); + return 1; +} + +__setup("vec", vector_setup); +__uml_help(vector_setup, +"vec[0-9]+: