From a3af8177e5952d6eb4bc9b159f73c95f930f1a74 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Sat, 19 Nov 2016 16:08:40 +0000 Subject: [PATCH 01/17] loader: smbios version check is not correct The version check for sku and family values is not correct, as this data is valid for version 2.4+, that also includes version 3.0 and above. Reported by: Dan McDonald Reviewed by: allanjude Approved by: allanjude (mentor) Differential Revision: https://reviews.freebsd.org/D8578 --- sys/boot/i386/libi386/smbios.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/boot/i386/libi386/smbios.c b/sys/boot/i386/libi386/smbios.c index 4a8f3bc4ef0..2aa62fa85df 100644 --- a/sys/boot/i386/libi386/smbios.c +++ b/sys/boot/i386/libi386/smbios.c @@ -238,7 +238,8 @@ smbios_parse_table(const caddr_t addr) smbios_setenv("smbios.system.serial", addr, 0x07); smbios_setuuid("smbios.system.uuid", addr + 0x08, smbios.ver); #endif - if (smbios.major >= 2 && smbios.minor >= 4) { + if (smbios.major > 2 || + (smbios.major == 2 && smbios.minor >= 4)) { smbios_setenv("smbios.system.sku", addr, 0x19); smbios_setenv("smbios.system.family", addr, 0x1a); } From d509eaf204af38793cbad2b6540cab495d6eaa35 Mon Sep 17 00:00:00 2001 From: Ruslan Bukin Date: Sat, 19 Nov 2016 16:36:38 +0000 Subject: [PATCH 02/17] Account for bigger secondary data cache line size. Secondary data cache line size can be bigger than primary data cache line size, so use biggest value as a minimum alignment. Submitted by: kan Sponsored by: DARPA, AFRL --- sys/mips/include/cache.h | 2 ++ sys/mips/mips/busdma_machdep.c | 14 +++++++------- sys/mips/mips/cache_mipsNN.c | 7 +++++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/sys/mips/include/cache.h b/sys/mips/include/cache.h index 9fc91595a10..a95d5d0a255 100644 --- a/sys/mips/include/cache.h +++ b/sys/mips/include/cache.h @@ -161,6 +161,8 @@ extern struct mips_cache_ops mips_cache_ops; /* PRIMARY CACHE VARIABLES */ extern int mips_picache_linesize; extern int mips_pdcache_linesize; +extern int mips_sdcache_linesize; +extern int mips_dcache_max_linesize; #define __mco_noargs(prefix, x) \ do { \ diff --git a/sys/mips/mips/busdma_machdep.c b/sys/mips/mips/busdma_machdep.c index d9eb9d48106..35d60999f0c 100644 --- a/sys/mips/mips/busdma_machdep.c +++ b/sys/mips/mips/busdma_machdep.c @@ -226,7 +226,7 @@ busdma_init(void *dummy) /* Create a cache of buffers in standard (cacheable) memory. */ standard_allocator = busdma_bufalloc_create("buffer", - mips_pdcache_linesize, /* minimum_alignment */ + mips_dcache_max_linesize, /* minimum_alignment */ NULL, /* uma_alloc func */ NULL, /* uma_free func */ 0); /* uma_zcreate_flags */ @@ -236,7 +236,7 @@ busdma_init(void *dummy) * BUS_DMA_COHERENT flag. */ coherent_allocator = busdma_bufalloc_create("coherent", - mips_pdcache_linesize, /* minimum_alignment */ + mips_dcache_max_linesize, /* minimum_alignment */ busdma_bufalloc_alloc_uncacheable, busdma_bufalloc_free_uncacheable, 0); /* uma_zcreate_flags */ @@ -1061,10 +1061,10 @@ _bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map) static void bus_dmamap_sync_buf(vm_offset_t buf, int len, bus_dmasync_op_t op, int aligned) { - char tmp_cl[mips_pdcache_linesize], tmp_clend[mips_pdcache_linesize]; + char tmp_cl[mips_dcache_max_linesize], tmp_clend[mips_dcache_max_linesize]; vm_offset_t buf_cl, buf_clend; vm_size_t size_cl, size_clend; - int cache_linesize_mask = mips_pdcache_linesize - 1; + int cache_linesize_mask = mips_dcache_max_linesize - 1; /* * dcache invalidation operates on cache line aligned addresses @@ -1091,7 +1091,7 @@ bus_dmamap_sync_buf(vm_offset_t buf, int len, bus_dmasync_op_t op, int aligned) buf_cl = buf & ~cache_linesize_mask; size_cl = buf & cache_linesize_mask; buf_clend = buf + len; - size_clend = (mips_pdcache_linesize - + size_clend = (mips_dcache_max_linesize - (buf_clend & cache_linesize_mask)) & cache_linesize_mask; } @@ -1123,7 +1123,7 @@ bus_dmamap_sync_buf(vm_offset_t buf, int len, bus_dmasync_op_t op, int aligned) if (size_cl) mips_dcache_wbinv_range(buf_cl, size_cl); if (size_clend && (size_cl == 0 || - buf_clend - buf_cl > mips_pdcache_linesize)) + buf_clend - buf_cl > mips_dcache_max_linesize)) mips_dcache_wbinv_range(buf_clend, size_clend); break; @@ -1156,7 +1156,7 @@ bus_dmamap_sync_buf(vm_offset_t buf, int len, bus_dmasync_op_t op, int aligned) if (size_cl) mips_dcache_wbinv_range(buf_cl, size_cl); if (size_clend && (size_cl == 0 || - buf_clend - buf_cl > mips_pdcache_linesize)) + buf_clend - buf_cl > mips_dcache_max_linesize)) mips_dcache_wbinv_range(buf_clend, size_clend); break; diff --git a/sys/mips/mips/cache_mipsNN.c b/sys/mips/mips/cache_mipsNN.c index 50eb76b2fee..7feb60cfd27 100644 --- a/sys/mips/mips/cache_mipsNN.c +++ b/sys/mips/mips/cache_mipsNN.c @@ -97,6 +97,8 @@ xlp_sync(void) */ int mips_picache_linesize; int mips_pdcache_linesize; +int mips_sdcache_linesize; +int mips_dcache_max_linesize; static int picache_size; static int picache_stride; @@ -157,6 +159,10 @@ mipsNN_cache_init(struct mips_cpuinfo * cpuinfo) sdcache_size = cpuinfo->l2.dc_size; sdcache_way_mask = cpuinfo->l2.dc_nways - 1; + mips_sdcache_linesize = cpuinfo->l2.dc_linesize; + mips_dcache_max_linesize = MAX(mips_pdcache_linesize, + mips_sdcache_linesize); + #define CACHE_DEBUG #ifdef CACHE_DEBUG printf("Cache info:\n"); @@ -166,6 +172,7 @@ mipsNN_cache_init(struct mips_cpuinfo * cpuinfo) printf(" picache_loopcount = %d\n", picache_loopcount); printf(" pdcache_stride = %d\n", pdcache_stride); printf(" pdcache_loopcount = %d\n", pdcache_loopcount); + printf(" max line size = %d\n", mips_dcache_max_linesize); #endif } From 91f70e00c4c77d27353a8d9fdaa136677dd7a7a3 Mon Sep 17 00:00:00 2001 From: Ruslan Bukin Date: Sat, 19 Nov 2016 17:01:06 +0000 Subject: [PATCH 03/17] Move intrng includes to the main MIPS includes file. Sponsored by: DARPA, AFRL --- sys/conf/files.mips | 7 +++++++ sys/mips/atheros/ar531x/files.ar5315 | 9 --------- sys/mips/broadcom/files.broadcom | 5 ----- sys/mips/mediatek/files.mediatek | 8 -------- 4 files changed, 7 insertions(+), 22 deletions(-) diff --git a/sys/conf/files.mips b/sys/conf/files.mips index 8d1cf7d5858..443c0d4531e 100644 --- a/sys/conf/files.mips +++ b/sys/conf/files.mips @@ -97,6 +97,13 @@ dev/hwpmc/hwpmc_mips74k.c optional hwpmc_mips74k # ofw support dev/ofw/ofwpci.c optional fdt pci +# INTRNG support code +kern/msi_if.m optional intrng +kern/pic_if.m optional intrng +kern/subr_intr.c optional intrng +# INTRNG compatible MIPS32 interrupt controller +mips/mips/mips_pic.c optional intrng + # DTrace cddl/compat/opensolaris/kern/opensolaris_atomic.c optional zfs | dtrace compile-with "${CDDL_C}" cddl/dev/dtrace/mips/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}" diff --git a/sys/mips/atheros/ar531x/files.ar5315 b/sys/mips/atheros/ar531x/files.ar5315 index f608aa69de1..8079d74a14e 100644 --- a/sys/mips/atheros/ar531x/files.ar5315 +++ b/sys/mips/atheros/ar531x/files.ar5315 @@ -18,14 +18,5 @@ mips/mips/tick.c standard dev/etherswitch/e6000sw/e6060sw.c optional etherswitch -# Hack to reuse ARM intrng code -kern/subr_intr.c optional intrng -kern/msi_if.m optional intrng -kern/pic_if.m optional intrng - -# Intrng compatible MIPS32 interrupt controller -mips/mips/mips_pic.c optional intrng - # Non Intrng mips/mips/intr_machdep.c optional !intrng - diff --git a/sys/mips/broadcom/files.broadcom b/sys/mips/broadcom/files.broadcom index ab534eca160..04070411444 100644 --- a/sys/mips/broadcom/files.broadcom +++ b/sys/mips/broadcom/files.broadcom @@ -9,11 +9,6 @@ mips/broadcom/bcm_bmips.c optional siba_nexus siba mips/broadcom/bcm_mips74k.c optional bcma_nexus bcma mips/broadcom/bcm_pmu.c standard mips/mips/tick.c standard -mips/mips/mips_pic.c standard -kern/subr_intr.c standard -kern/pic_if.m standard - -kern/msi_if.m optional intrng mips/broadcom/uart_cpu_chipc.c optional uart mips/broadcom/uart_bus_chipc.c optional uart diff --git a/sys/mips/mediatek/files.mediatek b/sys/mips/mediatek/files.mediatek index a25c3292f50..755b28e2810 100644 --- a/sys/mips/mediatek/files.mediatek +++ b/sys/mips/mediatek/files.mediatek @@ -26,14 +26,6 @@ mips/mediatek/mtk_gpio_v2.c optional gpio mtk_gpio_v2 # Ralink/Mediatek Ethernet driver dev/rt/if_rt.c optional rt -# Hack to reuse ARM intrng code -kern/subr_intr.c standard -kern/msi_if.m standard -kern/pic_if.m standard - -# Intrng compatible MIPS32 interrupt controller -mips/mips/mips_pic.c standard - # Standard MIPS ticker mips/mips/tick.c standard From 8b560139a2cadefbd62e6147351b2e9922853a6b Mon Sep 17 00:00:00 2001 From: Ruslan Bukin Date: Sat, 19 Nov 2016 17:12:24 +0000 Subject: [PATCH 04/17] Add XBurst CPU option. Submitted by: kan Sponsored by: DARPA, AFRL --- sys/conf/options.mips | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/conf/options.mips b/sys/conf/options.mips index 7c80a6cd446..d97ee1568fa 100644 --- a/sys/conf/options.mips +++ b/sys/conf/options.mips @@ -45,6 +45,7 @@ CPU_CNMIPS opt_global.h CPU_RMI opt_global.h CPU_NLM opt_global.h CPU_BERI opt_global.h +CPU_XBURST opt_global.h CPU_MALTA opt_global.h # which MACHINE_ARCH architecture From fbc3f2995ecc68a88c3927e4e11a5024abaa2eda Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 19 Nov 2016 17:12:28 +0000 Subject: [PATCH 05/17] Remove check for valid log pages. Let the drive tell us which pages are valid or not. While many pages are reserved in the standard, that doesn't make them invalid and future versions of the standard may define then. Sponsored by: Netflix, Inc --- sbin/nvmecontrol/logpage.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index ac443bbe15b..036b3880b6e 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -262,14 +262,6 @@ logpage(int argc, char *argv[]) "\"%s\" not valid log page id.\n", optarg); logpage_usage(); - /* TODO: Define valid log page id ranges in nvme.h? */ - } else if (log_page == 0 || - (log_page >= 0x04 && log_page <= 0x7F) || - (log_page >= 0x80 && log_page <= 0xBF)) { - fprintf(stderr, - "\"%s\" not valid log page id.\n", - optarg); - logpage_usage(); } pageflag = true; break; From cc63e8e6abf68771359feab855d31b008b5bb3db Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 19 Nov 2016 17:12:39 +0000 Subject: [PATCH 06/17] Use a table for pages we know the size of. We have a special case for the error log since it isn't a fixed size. Sponsored by: Netflix, Inc --- sbin/nvmecontrol/logpage.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index 036b3880b6e..f58fef1776e 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -222,12 +222,17 @@ print_log_firmware(void *buf, uint32_t size __unused) static struct logpage_function { uint8_t log_page; - print_fn_t fn; + print_fn_t print_fn; + size_t size; } logfuncs[] = { - {NVME_LOG_ERROR, print_log_error }, - {NVME_LOG_HEALTH_INFORMATION, print_log_health }, - {NVME_LOG_FIRMWARE_SLOT, print_log_firmware }, - {0, NULL }, + {NVME_LOG_ERROR, print_log_error, + 0}, + {NVME_LOG_HEALTH_INFORMATION, print_log_health, + sizeof(struct nvme_health_information_page)}, + {NVME_LOG_FIRMWARE_SLOT, print_log_firmware, + sizeof(struct nvme_firmware_page)}, + {0, NULL, + 0}, }; static void @@ -308,6 +313,7 @@ logpage(int argc, char *argv[]) } print_fn = print_hex; + size = DEFAULT_SIZE; if (!hexflag) { /* * See if there is a pretty print function for the @@ -317,30 +323,20 @@ logpage(int argc, char *argv[]) f = logfuncs; while (f->log_page > 0) { if (log_page == f->log_page) { - print_fn = f->fn; + print_fn = f->print_fn; + size = f->size; break; } f++; } } - /* Read the log page */ - switch (log_page) { - case NVME_LOG_ERROR: + if (log_page == NVME_LOG_ERROR) { size = sizeof(struct nvme_error_information_entry); size *= (cdata.elpe + 1); - break; - case NVME_LOG_HEALTH_INFORMATION: - size = sizeof(struct nvme_health_information_page); - break; - case NVME_LOG_FIRMWARE_SLOT: - size = sizeof(struct nvme_firmware_page); - break; - default: - size = DEFAULT_SIZE; - break; } + /* Read the log page */ buf = get_log_buffer(size); read_logpage(fd, log_page, nsid, buf, size); print_fn(buf, size); From 33a099d2e570e1f2167afcc2602afc76164e7072 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 19 Nov 2016 17:12:44 +0000 Subject: [PATCH 07/17] Print numbers instead of hex values for smart data. The full 128-bit number is printed, even though you'd need like a billion IOPs for a 10 billion seconds to overflow the 64-bit counters (~300 years). Sponsored by: Netflix, Inc --- sbin/nvmecontrol/logpage.c | 92 ++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index f58fef1776e..59b47418340 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -42,6 +42,11 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include + +#if _BYTE_ORDER != _LITTLE_ENDIAN +#error "Code only works on little endian machines" +#endif #include "nvmecontrol.h" @@ -50,6 +55,38 @@ __FBSDID("$FreeBSD$"); typedef void (*print_fn_t)(void *buf, uint32_t size); + +/* + * 128-bit integer augments to standard values + */ +#define UINT128_DIG 39 +typedef __uint128_t uint128_t; + +static inline uint128_t +to128(void *p) +{ + return *(uint128_t *)p; +} + +static char * +uint128_to_str(uint128_t u, char *buf, size_t buflen) +{ + char *end = buf + buflen - 1; + + *end-- = '\0'; + if (u == 0) + *end-- = '0'; + while (u && end >= buf) { + *end-- = u % 10 + '0'; + u /= 10; + } + end++; + if (u != 0) + return NULL; + + return end; +} + static void * get_log_buffer(uint32_t size) { @@ -128,6 +165,7 @@ static void print_log_health(void *buf, uint32_t size __unused) { struct nvme_health_information_page *health = buf; + char cbuf[UINT128_DIG + 1]; printf("SMART/Health Information Log\n"); printf("============================\n"); @@ -155,40 +193,26 @@ print_log_health(void *buf, uint32_t size __unused) printf("Percentage used: %u\n", health->percentage_used); - /* - * TODO: These are pretty ugly in hex. Is there a library that - * will convert 128-bit unsigned values to decimal? - */ - printf("Data units (512 byte) read: 0x%016jx%016jx\n", - health->data_units_read[1], - health->data_units_read[0]); - printf("Data units (512 byte) written: 0x%016jx%016jx\n", - health->data_units_written[1], - health->data_units_written[0]); - printf("Host read commands: 0x%016jx%016jx\n", - health->host_read_commands[1], - health->host_read_commands[0]); - printf("Host write commands: 0x%016jx%016jx\n", - health->host_write_commands[1], - health->host_write_commands[0]); - printf("Controller busy time (minutes): 0x%016jx%016jx\n", - health->controller_busy_time[1], - health->controller_busy_time[0]); - printf("Power cycles: 0x%016jx%016jx\n", - health->power_cycles[1], - health->power_cycles[0]); - printf("Power on hours: 0x%016jx%016jx\n", - health->power_on_hours[1], - health->power_on_hours[0]); - printf("Unsafe shutdowns: 0x%016jx%016jx\n", - health->unsafe_shutdowns[1], - health->unsafe_shutdowns[0]); - printf("Media errors: 0x%016jx%016jx\n", - health->media_errors[1], - health->media_errors[0]); - printf("No. error info log entries: 0x%016jx%016jx\n", - health->num_error_info_log_entries[1], - health->num_error_info_log_entries[0]); + printf("Data units (512,000 byte) read: %s\n", + uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); + printf("Data units (512,000 byte) written: %s\n", + uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); + printf("Host read commands: %s\n", + uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); + printf("Host write commands: %s\n", + uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); + printf("Controller busy time (minutes): %s\n", + uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); + printf("Power cycles: %s\n", + uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); + printf("Power on hours: %s\n", + uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); + printf("Unsafe shutdowns: %s\n", + uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); + printf("Media errors: %s\n", + uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); + printf("No. error info log entries: %s\n", + uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); } static void From dc58cdf95edb2de287ff871cc4f144648e9b805a Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 19 Nov 2016 17:12:49 +0000 Subject: [PATCH 08/17] Expand the SMART / Health Information Log Page (Page 02) printout based on NVM Express 1.2.1 Standard. Sponsored by: Netflix, Inc --- sbin/nvmecontrol/logpage.c | 27 +++++++++++++++++++++------ sys/dev/nvme/nvme.h | 5 ++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index 59b47418340..e086c8bd26b 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -161,11 +161,19 @@ print_log_error(void *buf, uint32_t size) } } +static void +print_temp(uint16_t t) +{ + printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); +} + + static void print_log_health(void *buf, uint32_t size __unused) { struct nvme_health_information_page *health = buf; char cbuf[UINT128_DIG + 1]; + int i; printf("SMART/Health Information Log\n"); printf("============================\n"); @@ -182,10 +190,8 @@ print_log_health(void *buf, uint32_t size __unused) health->critical_warning.bits.read_only); printf(" Volatile memory backup: %d\n", health->critical_warning.bits.volatile_memory_backup); - printf("Temperature: %u K, %2.2f C, %3.2f F\n", - health->temperature, - (float)health->temperature - (float)273.15, - ((float)health->temperature * (float)9/5) - (float)459.67); + printf("Temperature: "); + print_temp(health->temperature); printf("Available spare: %u\n", health->available_spare); printf("Available spare threshold: %u\n", @@ -193,9 +199,9 @@ print_log_health(void *buf, uint32_t size __unused) printf("Percentage used: %u\n", health->percentage_used); - printf("Data units (512,000 byte) read: %s\n", + printf("Data units (512,000 byte) read: %s\n", uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); - printf("Data units (512,000 byte) written: %s\n", + printf("Data units written: %s\n", uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); printf("Host read commands: %s\n", uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); @@ -213,6 +219,15 @@ print_log_health(void *buf, uint32_t size __unused) uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); printf("No. error info log entries: %s\n", uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); + + printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); + printf("Error Temp Composite Time: %d\n", health->error_temp_time); + for (i = 0; i < 7; i++) { + if (health->temp_sensor[i] == 0) + continue; + printf("Temperature Sensor %d: ", i + 1); + print_temp(health->temp_sensor[i]); + } } static void diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h index 721fe30c462..afc299506b9 100644 --- a/sys/dev/nvme/nvme.h +++ b/sys/dev/nvme/nvme.h @@ -724,8 +724,11 @@ struct nvme_health_information_page { uint64_t unsafe_shutdowns[2]; uint64_t media_errors[2]; uint64_t num_error_info_log_entries[2]; + uint32_t warning_temp_time; + uint32_t error_temp_time; + uint16_t temp_sensor[8]; - uint8_t reserved2[320]; + uint8_t reserved2[296]; } __packed __aligned(4); struct nvme_firmware_page { From aea528795a281af50e65944e3f60a1efba5e98ea Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 19 Nov 2016 17:12:53 +0000 Subject: [PATCH 09/17] Add log pages defined through NVM Express 1.2.1. Sponsored by: Netflix, Inc --- sys/dev/nvme/nvme.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h index afc299506b9..5d0536f9c01 100644 --- a/sys/dev/nvme/nvme.h +++ b/sys/dev/nvme/nvme.h @@ -663,8 +663,11 @@ enum nvme_log_page { NVME_LOG_ERROR = 0x01, NVME_LOG_HEALTH_INFORMATION = 0x02, NVME_LOG_FIRMWARE_SLOT = 0x03, - /* 0x04-0x7F - reserved */ + NVME_LOG_CHANGED_NAMESPACE = 0x04, + NVME_LOG_COMMAND_EFFECT = 0x05, + /* 0x06-0x7F - reserved */ /* 0x80-0xBF - I/O command set specific */ + NVME_LOG_RES_NOTIFICATION = 0x80 /* 0xC0-0xFF - vendor specific */ }; From d01f26f590dde51d03f57193a05b2d2d89a3ce55 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 19 Nov 2016 17:12:58 +0000 Subject: [PATCH 10/17] Add log pages that Intel SSDs provide. It turns out that many of these are widely implemented beyond just Intel drives. Sponsored by: Netflix, Inc --- sys/dev/nvme/nvme.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h index 5d0536f9c01..e02e5b92fca 100644 --- a/sys/dev/nvme/nvme.h +++ b/sys/dev/nvme/nvme.h @@ -667,8 +667,18 @@ enum nvme_log_page { NVME_LOG_COMMAND_EFFECT = 0x05, /* 0x06-0x7F - reserved */ /* 0x80-0xBF - I/O command set specific */ - NVME_LOG_RES_NOTIFICATION = 0x80 + NVME_LOG_RES_NOTIFICATION = 0x80, /* 0xC0-0xFF - vendor specific */ +/* + * The following are Intel Specific log pages, but they seem to + * be widely implemented. + */ + INTEL_LOG_READ_LAT_LOG = 0xc1, + INTEL_LOG_WRITE_LAT_LOG = 0xc2, + INTEL_LOG_TEMP_STATS = 0xc5, + INTEL_LOG_ADD_SMART = 0xca, + INTEL_LOG_DRIVE_MKT_NAME = 0xdd, + }; struct nvme_error_information_entry { From ab1dd0917bfd0690a3d5a2674a92e4714555ebce Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 19 Nov 2016 17:13:03 +0000 Subject: [PATCH 11/17] Print Intel's expanded Temperature log page. Sponsored by: Netflix, Inc --- sbin/nvmecontrol/logpage.c | 33 ++++++++++++++++++++++++++++++++- sys/dev/nvme/nvme.h | 15 ++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index e086c8bd26b..41128eba227 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -100,7 +100,7 @@ get_log_buffer(uint32_t size) } void -read_logpage(int fd, uint8_t log_page, int nsid, void *payload, +read_logpage(int fd, uint8_t log_page, int nsid, void *payload, uint32_t payload_size) { struct nvme_pt_command pt; @@ -259,6 +259,35 @@ print_log_firmware(void *buf, uint32_t size __unused) } } +static void +print_intel_temp_stats(void *buf, uint32_t size __unused) +{ + struct intel_log_temp_stats *temp = buf; + + printf("Intel Temperature Log\n"); + printf("=====================\n"); + + printf("Current: "); + print_temp(temp->current); + printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); + printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); + printf("Max Temperature "); + print_temp(temp->max_temp); + printf("Min Temperature "); + print_temp(temp->min_temp); + printf("Max Operating Temperature "); + print_temp(temp->max_oper_temp); + printf("Min Operating Temperature "); + print_temp(temp->min_oper_temp); + printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); +} + +/* + * Table of log page printer / sizing. + * + * This includes Intel specific pages that are widely implemented. Not + * sure how best to switch between different vendors. + */ static struct logpage_function { uint8_t log_page; print_fn_t print_fn; @@ -270,6 +299,8 @@ static struct logpage_function { sizeof(struct nvme_health_information_page)}, {NVME_LOG_FIRMWARE_SLOT, print_log_firmware, sizeof(struct nvme_firmware_page)}, + {INTEL_LOG_TEMP_STATS, print_intel_temp_stats, + sizeof(struct intel_log_temp_stats)}, {0, NULL, 0}, }; diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h index e02e5b92fca..b308a8e27b0 100644 --- a/sys/dev/nvme/nvme.h +++ b/sys/dev/nvme/nvme.h @@ -670,7 +670,7 @@ enum nvme_log_page { NVME_LOG_RES_NOTIFICATION = 0x80, /* 0xC0-0xFF - vendor specific */ /* - * The following are Intel Specific log pages, but they seem to + * The following are Intel Specific log pages, but they seem to * be widely implemented. */ INTEL_LOG_READ_LAT_LOG = 0xc1, @@ -756,6 +756,19 @@ struct nvme_firmware_page { uint8_t reserved2[448]; } __packed __aligned(4); +struct intel_log_temp_stats +{ + uint64_t current; + uint64_t overtemp_flag_last; + uint64_t overtemp_flag_life; + uint64_t max_temp; + uint64_t min_temp; + uint64_t _rsvd[5]; + uint64_t max_oper_temp; + uint64_t min_oper_temp; + uint64_t est_offset; +} __packed __aligned(4); + #define NVME_TEST_MAX_THREADS 128 struct nvme_io_test { From 0cf14228c4a23897e8f7b979a5096b7389c48cf7 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 19 Nov 2016 17:13:08 +0000 Subject: [PATCH 12/17] Implement HGST Log page 0xc1, as documented in the HGST SN100 and SN150 product manuals. Subpage 0x32 is documented, but not implemented. Sponsored by: Netflix, Inc --- sbin/nvmecontrol/logpage.c | 420 +++++++++++++++++++++++++++++++++++++ sys/dev/nvme/nvme.h | 13 +- 2 files changed, 429 insertions(+), 4 deletions(-) diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index 41128eba227..e6a6ec921dd 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -56,6 +56,25 @@ __FBSDID("$FreeBSD$"); typedef void (*print_fn_t)(void *buf, uint32_t size); +struct kv_name +{ + uint32_t key; + const char *name; +}; + +static const char * +kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) +{ + static char bad[32]; + size_t i; + + for (i = 0; i < kv_count; i++, kv++) + if (kv->key == key) + return kv->name; + snprintf(bad, sizeof(bad), "Attribute %#x", key); + return bad; +} + /* * 128-bit integer augments to standard values */ @@ -282,6 +301,405 @@ print_intel_temp_stats(void *buf, uint32_t size __unused) printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); } +/* + * HGST's 0xc1 page. This is a grab bag of additional data. Please see + * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf + * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf + * Appendix A for details + */ + +typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); + +struct subpage_print +{ + uint16_t key; + subprint_fn_t fn; +}; + +static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); +static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); + +static struct subpage_print hgst_subpage[] = { + { 0x02, print_hgst_info_write_errors }, + { 0x03, print_hgst_info_read_errors }, + { 0x05, print_hgst_info_verify_errors }, + { 0x10, print_hgst_info_self_test }, + { 0x15, print_hgst_info_background_scan }, + { 0x30, print_hgst_info_erase_errors }, + { 0x31, print_hgst_info_erase_counts }, + { 0x32, print_hgst_info_temp_history }, + { 0x37, print_hgst_info_ssd_perf }, + { 0x38, print_hgst_info_firmware_load }, +}; + +/* Print a subpage that is basically just key value pairs */ +static void +print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, + const struct kv_name *kv, size_t kv_count) +{ + uint8_t *wsp, *esp; + uint16_t ptype; + uint8_t plen; + uint64_t param; + int i; + + wsp = buf; + esp = wsp + size; + while (wsp < esp) { + ptype = le16dec(wsp); + wsp += 2; + wsp++; /* Flags, just ignore */ + plen = *wsp++; + param = 0; + for (i = 0; i < plen; i++) + param |= (uint64_t)*wsp++ << (i * 8); + printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); + } +} + +static void +print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) +{ + static struct kv_name kv[] = + { + { 0x0000, "Corrected Without Delay" }, + { 0x0001, "Corrected Maybe Delayed" }, + { 0x0002, "Re-Writes" }, + { 0x0003, "Errors Corrected" }, + { 0x0004, "Correct Algorithm Used" }, + { 0x0005, "Bytes Processed" }, + { 0x0006, "Uncorrected Errors" }, + { 0x8000, "Flash Write Commands" }, + { 0x8001, "HGST Special" }, + }; + + printf("Write Errors Subpage:\n"); + print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); +} + +static void +print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) +{ + static struct kv_name kv[] = + { + { 0x0000, "Corrected Without Delay" }, + { 0x0001, "Corrected Maybe Delayed" }, + { 0x0002, "Re-Reads" }, + { 0x0003, "Errors Corrected" }, + { 0x0004, "Correct Algorithm Used" }, + { 0x0005, "Bytes Processed" }, + { 0x0006, "Uncorrected Errors" }, + { 0x8000, "Flash Read Commands" }, + { 0x8001, "XOR Recovered" }, + { 0x8002, "Total Corrected Bits" }, + }; + + printf("Read Errors Subpage:\n"); + print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); +} + +static void +print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) +{ + static struct kv_name kv[] = + { + { 0x0000, "Corrected Without Delay" }, + { 0x0001, "Corrected Maybe Delayed" }, + { 0x0002, "Re-Reads" }, + { 0x0003, "Errors Corrected" }, + { 0x0004, "Correct Algorithm Used" }, + { 0x0005, "Bytes Processed" }, + { 0x0006, "Uncorrected Errors" }, + { 0x8000, "Commands Processed" }, + }; + + printf("Verify Errors Subpage:\n"); + print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); +} + +static void +print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) +{ + size_t i; + uint8_t *walker = buf; + uint16_t code, hrs; + uint32_t lba; + + printf("Self Test Subpage:\n"); + for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ + code = le16dec(walker); + walker += 2; + walker++; /* Ignore fixed flags */ + if (*walker == 0) /* Last entry is zero length */ + break; + if (*walker++ != 0x10) { + printf("Bad length for self test report\n"); + return; + } + printf(" %-30s: %d\n", "Recent Test", code); + printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); + printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); + walker++; + printf(" %-28s: %#x\n", "Self-Test Number", *walker++); + hrs = le16dec(walker); + walker += 2; + lba = le32dec(walker); + walker += 4; + printf(" %-28s: %u\n", "Total Power On Hrs", hrs); + printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); + printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); + printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); + printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); + printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); + } +} + +static void +print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) +{ + uint8_t *walker = buf; + uint8_t status; + uint16_t code, nscan, progress; + uint32_t pom, nand; + + printf("Background Media Scan Subpage:\n"); + /* Decode the header */ + code = le16dec(walker); + walker += 2; + walker++; /* Ignore fixed flags */ + if (*walker++ != 0x10) { + printf("Bad length for background scan header\n"); + return; + } + if (code != 0) { + printf("Expceted code 0, found code %#x\n", code); + return; + } + pom = le32dec(walker); + walker += 4; + walker++; /* Reserved */ + status = *walker++; + nscan = le16dec(walker); + walker += 2; + progress = le16dec(walker); + walker += 2; + walker += 6; /* Reserved */ + printf(" %-30s: %d\n", "Power On Minutes", pom); + printf(" %-30s: %x (%s)\n", "BMS Status", status, + status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); + printf(" %-30s: %d\n", "Number of BMS", nscan); + printf(" %-30s: %d\n", "Progress Current BMS", progress); + /* Report retirements */ + if (walker - (uint8_t *)buf != 20) { + printf("Coding error, offset not 20\n"); + return; + } + size -= 20; + printf(" %-30s: %d\n", "BMS retirements", size / 0x18); + while (size > 0) { + code = le16dec(walker); + walker += 2; + walker++; + if (*walker++ != 0x14) { + printf("Bad length parameter\n"); + return; + } + pom = le32dec(walker); + walker += 4; + /* + * Spec sheet says the following are hard coded, if true, just + * print the NAND retirement. + */ + if (walker[0] == 0x41 && + walker[1] == 0x0b && + walker[2] == 0x01 && + walker[3] == 0x00 && + walker[4] == 0x00 && + walker[5] == 0x00 && + walker[6] == 0x00 && + walker[7] == 0x00) { + walker += 8; + walker += 4; /* Skip reserved */ + nand = le32dec(walker); + walker += 4; + printf(" %-30s: %d\n", "Retirement number", code); + printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); + } else { + printf("Parameter %#x entry corrupt\n", code); + walker += 16; + } + } +} + +static void +print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) +{ + static struct kv_name kv[] = + { + { 0x0000, "Corrected Without Delay" }, + { 0x0001, "Corrected Maybe Delayed" }, + { 0x0002, "Re-Erase" }, + { 0x0003, "Errors Corrected" }, + { 0x0004, "Correct Algorithm Used" }, + { 0x0005, "Bytes Processed" }, + { 0x0006, "Uncorrected Errors" }, + { 0x8000, "Flash Erase Commands" }, + { 0x8001, "Mfg Defect Count" }, + { 0x8002, "Grown Defect Count" }, + { 0x8003, "Erase Count -- User" }, + { 0x8004, "Erase Count -- System" }, + }; + + printf("Erase Errors Subpage:\n"); + print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); +} + +static void +print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) +{ + /* My drive doesn't export this -- so not coding up */ + printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); +} + +static void +print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) +{ + uint8_t *walker = buf; + uint32_t min; + + printf("Temperature History:\n"); + printf(" %-30s: %d C\n", "Current Temperature", *walker++); + printf(" %-30s: %d C\n", "Reference Temperature", *walker++); + printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); + printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); + min = le32dec(walker); + walker += 4; + printf(" %-30s: %d:%02d:00\n", "Max Temperture Time", min / 60, min % 60); + min = le32dec(walker); + walker += 4; + printf(" %-30s: %d:%02d:00\n", "Over Temperture Duration", min / 60, min % 60); + min = le32dec(walker); + walker += 4; + printf(" %-30s: %d:%02d:00\n", "Min Temperture Time", min / 60, min % 60); +} + +static void +print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) +{ + uint8_t *walker = buf; + uint64_t val; + + printf("SSD Performance Subpage Type %d:\n", res); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Read Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Read Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Read Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Read Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Write Commands", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Write Blocks", val); + val = le64dec(walker); + walker += 8; + printf(" %-30s: %ju\n", "NAND Read Before Writes", val); +} + +static void +print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) +{ + uint8_t *walker = buf; + + printf("Firmware Load Subpage:\n"); + printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); +} + +static void +kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) +{ + size_t i; + + for (i = 0; i < nsp; i++, sp++) { + if (sp->key == subtype) { + sp->fn(buf, subtype, res, size); + return; + } + } + printf("No handler for page type %x\n", subtype); +} + +static void +print_hgst_info_log(void *buf, uint32_t size __unused) +{ + uint8_t *walker, *end, *subpage; + int pages; + uint16_t len; + uint8_t subtype, res; + + printf("HGST Extra Info Log\n"); + printf("===================\n"); + + walker = buf; + pages = *walker++; + walker++; + len = le16dec(walker); + walker += 2; + end = walker + len; /* Length is exclusive of this header */ + + while (walker < end) { + subpage = walker + 4; + subtype = *walker++ & 0x3f; /* subtype */ + res = *walker++; /* Reserved */ + len = le16dec(walker); + walker += len + 2; /* Length, not incl header */ + if (walker > end) { + printf("Ooops! Off the end of the list\n"); + break; + } + kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); + } +} + /* * Table of log page printer / sizing. * @@ -301,6 +719,8 @@ static struct logpage_function { sizeof(struct nvme_firmware_page)}, {INTEL_LOG_TEMP_STATS, print_intel_temp_stats, sizeof(struct intel_log_temp_stats)}, + {HGST_INFO_LOG, print_hgst_info_log, + DEFAULT_SIZE}, {0, NULL, 0}, }; diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h index b308a8e27b0..970c5f84353 100644 --- a/sys/dev/nvme/nvme.h +++ b/sys/dev/nvme/nvme.h @@ -669,16 +669,21 @@ enum nvme_log_page { /* 0x80-0xBF - I/O command set specific */ NVME_LOG_RES_NOTIFICATION = 0x80, /* 0xC0-0xFF - vendor specific */ -/* - * The following are Intel Specific log pages, but they seem to - * be widely implemented. - */ + + /* + * The following are Intel Specific log pages, but they seem + * to be widely implemented. + */ INTEL_LOG_READ_LAT_LOG = 0xc1, INTEL_LOG_WRITE_LAT_LOG = 0xc2, INTEL_LOG_TEMP_STATS = 0xc5, INTEL_LOG_ADD_SMART = 0xca, INTEL_LOG_DRIVE_MKT_NAME = 0xdd, + /* + * HGST log page, with lots ofs sub pages. + */ + HGST_INFO_LOG = 0xc1, }; struct nvme_error_information_entry { From 9caeb4305d450a253ccf9ada6c5d604fa3e5e969 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 19 Nov 2016 17:13:12 +0000 Subject: [PATCH 13/17] Decode the Intel-specific Additional SMART data page (0xca) and print it in human readable form. Include a pointer to the public spec that was followed to implement this in the code. Samsung also implements page 0xca on some of their drives, but the format is slighly different, so the code skips printing zero keys. Samsung's log page has additional, unknown data after the end of Intel defined data which isn't displayed. Supported by: Netfix, Inc --- sbin/nvmecontrol/logpage.c | 81 +++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index e6a6ec921dd..28295c01e24 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -55,7 +55,6 @@ __FBSDID("$FreeBSD$"); typedef void (*print_fn_t)(void *buf, uint32_t size); - struct kv_name { uint32_t key; @@ -106,6 +105,15 @@ uint128_to_str(uint128_t u, char *buf, size_t buflen) return end; } +/* "fMissing" from endian.h */ +static __inline uint64_t +le48dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((uint64_t)le16dec(p + 4) << 32) | le32dec(p)); +} + static void * get_log_buffer(uint32_t size) { @@ -278,6 +286,13 @@ print_log_firmware(void *buf, uint32_t size __unused) } } +/* + * Intel specific log pages from + * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf + * + * Though the version as of this date has a typo for the size of log page 0xca, + * offset 147: it is only 1 byte, not 6. + */ static void print_intel_temp_stats(void *buf, uint32_t size __unused) { @@ -301,6 +316,68 @@ print_intel_temp_stats(void *buf, uint32_t size __unused) printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); } +static void +print_intel_add_smart(void *buf, uint32_t size __unused) +{ + uint8_t *walker = buf; + uint8_t *end = walker + 150; + const char *name; + uint64_t raw; + uint8_t normalized; + + static struct kv_name kv[] = + { + { 0xab, "Program Fail Count" }, + { 0xac, "Erase Fail Count" }, + { 0xad, "Wear Leveling Count" }, + { 0xb8, "End to End Error Count" }, + { 0xc7, "CRC Error Count" }, + { 0xe2, "Timed: Media Wear" }, + { 0xe3, "Timed: Host Read %" }, + { 0xe4, "Timed: Elapsed Time" }, + { 0xea, "Thermal Throttle Status" }, + { 0xf0, "Retry Buffer Overflows" }, + { 0xf3, "PLL Lock Loss Count" }, + { 0xf4, "NAND Bytes Written" }, + { 0xf5, "Host Bytes Written" }, + }; + + printf("Additional SMART Data Log\n"); + printf("=========================\n"); + /* + * walker[0] = Key + * walker[1,2] = reserved + * walker[3] = Normalized Value + * walker[4] = reserved + * walker[5..10] = Little Endian Raw value + * (or other represenations) + * walker[11] = reserved + */ + while (walker < end) { + name = kv_lookup(kv, nitems(kv), *walker); + normalized = walker[3]; + raw = le48dec(walker + 5); + switch (*walker){ + case 0: + break; + case 0xad: + printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized, + le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9)); + break; + case 0xe2: + printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0); + break; + case 0xea: + printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6)); + break; + default: + printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw); + break; + } + walker += 12; + } +} + /* * HGST's 0xc1 page. This is a grab bag of additional data. Please see * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf @@ -719,6 +796,8 @@ static struct logpage_function { sizeof(struct nvme_firmware_page)}, {INTEL_LOG_TEMP_STATS, print_intel_temp_stats, sizeof(struct intel_log_temp_stats)}, + {INTEL_LOG_ADD_SMART, print_intel_add_smart, + DEFAULT_SIZE}, {HGST_INFO_LOG, print_hgst_info_log, DEFAULT_SIZE}, {0, NULL, From 9a8f61fb5b23d8d82a8507d1281894ec24cb1a9b Mon Sep 17 00:00:00 2001 From: Ruslan Bukin Date: Sat, 19 Nov 2016 17:46:18 +0000 Subject: [PATCH 14/17] Bring in support for Ingenic XBurst JZ4780 and X1000 systems on chips. Imgtec CI20 and Ingenic CANNA boards supported. Submitted by: Alexander Kabaev Reviewed by: Ruslan Bukin Sponsored by: DARPA, AFRL --- bin/dd/dd.c | 30 - sys/mips/conf/CANNA | 29 + sys/mips/conf/CI20 | 31 + sys/mips/conf/JZ4780 | 92 +++ sys/mips/conf/JZ4780.hints | 2 + sys/mips/conf/X1000 | 89 +++ sys/mips/conf/X1000.hints | 2 + sys/mips/ingenic/files.jz4780 | 26 + sys/mips/ingenic/files.x1000 | 17 + sys/mips/ingenic/jz4780_clk.h | 93 +++ sys/mips/ingenic/jz4780_clk_gen.c | 317 +++++++++ sys/mips/ingenic/jz4780_clk_otg.c | 167 +++++ sys/mips/ingenic/jz4780_clk_pll.c | 234 +++++++ sys/mips/ingenic/jz4780_clock.c | 831 ++++++++++++++++++++++++ sys/mips/ingenic/jz4780_clock.h | 36 ++ sys/mips/ingenic/jz4780_cpuregs.h | 73 +++ sys/mips/ingenic/jz4780_dme.c | 124 ++++ sys/mips/ingenic/jz4780_dwc_fdt.c | 202 ++++++ sys/mips/ingenic/jz4780_efuse.c | 213 ++++++ sys/mips/ingenic/jz4780_ehci.c | 345 ++++++++++ sys/mips/ingenic/jz4780_gpio.c | 830 ++++++++++++++++++++++++ sys/mips/ingenic/jz4780_gpio_if.m | 41 ++ sys/mips/ingenic/jz4780_intr.c | 333 ++++++++++ sys/mips/ingenic/jz4780_machdep.c | 296 +++++++++ sys/mips/ingenic/jz4780_mmc.c | 1002 +++++++++++++++++++++++++++++ sys/mips/ingenic/jz4780_mp.c | 182 ++++++ sys/mips/ingenic/jz4780_mpboot.S | 62 ++ sys/mips/ingenic/jz4780_nand.c | 123 ++++ sys/mips/ingenic/jz4780_nemc.c | 373 +++++++++++ sys/mips/ingenic/jz4780_ohci.c | 318 +++++++++ sys/mips/ingenic/jz4780_pinctrl.c | 260 ++++++++ sys/mips/ingenic/jz4780_pinctrl.h | 33 + sys/mips/ingenic/jz4780_regs.h | 787 ++++++++++++++++++++++ sys/mips/ingenic/jz4780_timer.c | 337 ++++++++++ sys/mips/ingenic/jz4780_uart.c | 220 +++++++ 35 files changed, 8120 insertions(+), 30 deletions(-) create mode 100644 sys/mips/conf/CANNA create mode 100644 sys/mips/conf/CI20 create mode 100644 sys/mips/conf/JZ4780 create mode 100644 sys/mips/conf/JZ4780.hints create mode 100644 sys/mips/conf/X1000 create mode 100644 sys/mips/conf/X1000.hints create mode 100644 sys/mips/ingenic/files.jz4780 create mode 100644 sys/mips/ingenic/files.x1000 create mode 100644 sys/mips/ingenic/jz4780_clk.h create mode 100644 sys/mips/ingenic/jz4780_clk_gen.c create mode 100644 sys/mips/ingenic/jz4780_clk_otg.c create mode 100644 sys/mips/ingenic/jz4780_clk_pll.c create mode 100644 sys/mips/ingenic/jz4780_clock.c create mode 100644 sys/mips/ingenic/jz4780_clock.h create mode 100644 sys/mips/ingenic/jz4780_cpuregs.h create mode 100644 sys/mips/ingenic/jz4780_dme.c create mode 100644 sys/mips/ingenic/jz4780_dwc_fdt.c create mode 100644 sys/mips/ingenic/jz4780_efuse.c create mode 100644 sys/mips/ingenic/jz4780_ehci.c create mode 100644 sys/mips/ingenic/jz4780_gpio.c create mode 100644 sys/mips/ingenic/jz4780_gpio_if.m create mode 100644 sys/mips/ingenic/jz4780_intr.c create mode 100644 sys/mips/ingenic/jz4780_machdep.c create mode 100644 sys/mips/ingenic/jz4780_mmc.c create mode 100644 sys/mips/ingenic/jz4780_mp.c create mode 100644 sys/mips/ingenic/jz4780_mpboot.S create mode 100644 sys/mips/ingenic/jz4780_nand.c create mode 100644 sys/mips/ingenic/jz4780_nemc.c create mode 100644 sys/mips/ingenic/jz4780_ohci.c create mode 100644 sys/mips/ingenic/jz4780_pinctrl.c create mode 100644 sys/mips/ingenic/jz4780_pinctrl.h create mode 100644 sys/mips/ingenic/jz4780_regs.h create mode 100644 sys/mips/ingenic/jz4780_timer.c create mode 100644 sys/mips/ingenic/jz4780_uart.c diff --git a/bin/dd/dd.c b/bin/dd/dd.c index a13213dcc8c..56f8efef7a5 100644 --- a/bin/dd/dd.c +++ b/bin/dd/dd.c @@ -48,13 +48,10 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include #include -#include #include -#include #include #include #include @@ -95,10 +92,6 @@ main(int argc __unused, char *argv[]) jcl(argv); setup(); - caph_cache_catpages(); - if (cap_enter() == -1 && errno != ENOSYS) - err(1, "unable to enter capability mode"); - (void)signal(SIGINFO, siginfo_handler); (void)signal(SIGINT, terminate); @@ -132,8 +125,6 @@ static void setup(void) { u_int cnt; - cap_rights_t rights; - unsigned long cmds[] = { FIODTYPE, MTIOCTOP }; if (in.name == NULL) { in.name = "stdin"; @@ -142,20 +133,13 @@ setup(void) in.fd = open(in.name, O_RDONLY, 0); if (in.fd == -1) err(1, "%s", in.name); - if (caph_limit_stdin() == -1) - err(1, "unable to limit capability rights"); } getfdtype(&in); - cap_rights_init(&rights, CAP_READ, CAP_SEEK); - if (cap_rights_limit(in.fd, &rights) == -1 && errno != ENOSYS) - err(1, "unable to limit capability rights"); - if (files_cnt > 1 && !(in.flags & ISTAPE)) errx(1, "files is not supported for non-tape devices"); - cap_rights_set(&rights, CAP_WRITE, CAP_FTRUNCATE, CAP_IOCTL); if (out.name == NULL) { /* No way to check for read access here. */ out.fd = STDOUT_FILENO; @@ -172,27 +156,13 @@ setup(void) if (out.fd == -1) { out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); out.flags |= NOREAD; - cap_rights_clear(&rights, CAP_READ); } if (out.fd == -1) err(1, "%s", out.name); - if (caph_limit_stdout() == -1) - err(1, "unable to limit capability rights"); } getfdtype(&out); - if (cap_rights_limit(out.fd, &rights) == -1 && errno != ENOSYS) - err(1, "unable to limit capability rights"); - if (cap_ioctls_limit(out.fd, cmds, nitems(cmds)) == -1 && - errno != ENOSYS) - err(1, "unable to limit capability rights"); - - if (in.fd != STDERR_FILENO && out.fd != STDERR_FILENO) { - if (caph_limit_stderr() == -1) - err(1, "unable to limit capability rights"); - } - /* * Allocate space for the input and output buffers. If not doing * record oriented I/O, only need a single buffer. diff --git a/sys/mips/conf/CANNA b/sys/mips/conf/CANNA new file mode 100644 index 00000000000..0d6f81b52c3 --- /dev/null +++ b/sys/mips/conf/CANNA @@ -0,0 +1,29 @@ +# CANNA -- Kernel config for Ingenic CANNA board +# +# $FreeBSD$ + +include "X1000" +ident CANNA + +options FDT +options FDT_DTB_STATIC +makeoptions FDT_DTS_FILE=ingenic/canna.dts + +#options KTR +#options KTR_CPUMASK=0x3 +#options KTR_MASK=(KTR_GEN) +#options KTR_COMPILE=(KTR_GEN) +#options KTR_VERBOSE + +# Uncomment for NFS root +#options BOOTP +#options BOOTP_NFSROOT +#options BOOTP_NFSV3 +#options BOOTP_WIRED_TO=dme0 +#options BOOTP_COMPAT +options ROOTDEVNAME=\"ufs:mmcsd0s3\" + +makeoptions TRAMPLOADADDR=0x88000000 + +#options VERBOSE_SYSINIT +options PRINTF_BUFR_SIZE=256 diff --git a/sys/mips/conf/CI20 b/sys/mips/conf/CI20 new file mode 100644 index 00000000000..9d1940d85e8 --- /dev/null +++ b/sys/mips/conf/CI20 @@ -0,0 +1,31 @@ +# CI20 -- Kernel config for Creator CI20 board +# +# $FreeBSD$ + +include "JZ4780" +ident CI20 + +options FDT +options FDT_DTB_STATIC +makeoptions FDT_DTS_FILE=ingenic/ci20.dts + +#options KTR +#options KTR_CPUMASK=0x3 +#options KTR_MASK=(KTR_GEN) +#options KTR_COMPILE=(KTR_GEN) +#options KTR_VERBOSE + +# Uncomment for NFS root +#options BOOTP +#options BOOTP_NFSROOT +#options BOOTP_NFSV3 +#options BOOTP_WIRED_TO=dme0 +#options BOOTP_COMPAT + +options ROOTDEVNAME=\"ufs:mmcsd0\" + +makeoptions TRAMPLOADADDR=0x88000000 + +#options VERBOSE_SYSINIT +device dme +options PRINTF_BUFR_SIZE=256 diff --git a/sys/mips/conf/JZ4780 b/sys/mips/conf/JZ4780 new file mode 100644 index 00000000000..3df33a1cdb2 --- /dev/null +++ b/sys/mips/conf/JZ4780 @@ -0,0 +1,92 @@ +# JZ4780 -- Kernel config for Ingenic JZ47XX boards +# +# $FreeBSD$ + +ident JZ4780 +machine mips mipsel +cpu CPU_XBURST +cpu CPU_MIPS4KC + +makeoptions KERNLOADADDR=0x80020000 +makeoptions ARCH_FLAGS="-EL -march=mips32r2" + +# Don't build any modules yet. +makeoptions MODULES_OVERRIDE="" + +files "../ingenic/files.jz4780" +hints "JZ4780.hints" #Default places to look for devices. + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + +options INTRNG # Borrow interrupt code from ARM +options MIPS_NIRQ=264 # 8 cpuintc + 64 intc + 6 * 23 gpio + +options DDB +options KDB +options BREAK_TO_DEBUGGER + +options COMPAT_FREEBSD10 + +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options NFSCL #Network Filesystem Client +options NFS_ROOT #NFS usable as /, requires NFSCL +options NFSLOCKD #Network Lock Manager +options PSEUDOFS #Pseudo-filesystem framework +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions + +options FFS #Berkeley Fast Filesystem +options SOFTUPDATES #Enable FFS soft updates support +options UFS_ACL #Support for access control lists +options UFS_DIRHASH #Improve performance on big directories +#options ROOTDEVNAME=\"ufs:ada0\" + +options GEOM_LABEL # Provides labelization +options GEOM_PART_GPT # GUID Partition Tables. +#options GEOM_RAID # Soft RAID functionality. + +# Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver +options INVARIANTS #Enable calls of extra sanity checking +options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS +#options WITNESS #Enable checks to detect deadlocks and cycles +#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed + +# Make an SMP-capable kernel by default +# options SMP # Symmetric MultiProcessor Kernel + +device loop +device ether +#device le +device miibus +device bpf +device md +device uart +device random + +device fdt_pinctrl + +device clk +device regulator +device ext_resources + +device gpio + +device scbus +device da + +device mmc +device mmcsd + +# USB support +options USB_DEBUG # enable debug msgs +options USB_HOST_ALIGN=128 # L2 cache line size +device ohci # OHCI PCI->USB interface +device ehci # EHCI PCI->USB interface (USB 2.0) +device dwcotg # DesignWare HS OTG controller +device usb # USB Bus (required) +#device udbp # USB Double Bulk Pipe devices +device uhid # "Human Interface Devices" +#device ulpt # Printer +device umass # Disks/Mass storage - Requires scbus and da +device ums # Mouse diff --git a/sys/mips/conf/JZ4780.hints b/sys/mips/conf/JZ4780.hints new file mode 100644 index 00000000000..6986b85032d --- /dev/null +++ b/sys/mips/conf/JZ4780.hints @@ -0,0 +1,2 @@ +# $FreeBSD$ +# device.hints diff --git a/sys/mips/conf/X1000 b/sys/mips/conf/X1000 new file mode 100644 index 00000000000..8659f1112e4 --- /dev/null +++ b/sys/mips/conf/X1000 @@ -0,0 +1,89 @@ +# X1000 -- Kernel config for Ingenic X1000 boards +# +# $FreeBSD$ + +ident X1000 +machine mips mipsel +cpu CPU_XBURST +cpu CPU_MIPS4KC + +makeoptions KERNLOADADDR=0x80020000 +makeoptions ARCH_FLAGS="-EL -march=mips32r2" + +# Don't build any modules yet. +makeoptions MODULES_OVERRIDE="" + +files "../ingenic/files.x1000" +hints "X1000.hints" #Default places to look for devices. + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + +options INTRNG # Borrow interrupt code from ARM +options MIPS_NIRQ=264 # 8 cpuintc + 64 intc + 6 * 23 gpio + +options DDB +options KDB +options BREAK_TO_DEBUGGER + +options COMPAT_FREEBSD10 + +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options NFSCL #Network Filesystem Client +options NFS_ROOT #NFS usable as /, requires NFSCL +options NFSLOCKD #Network Lock Manager +options PSEUDOFS #Pseudo-filesystem framework +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions + +options FFS #Berkeley Fast Filesystem +options SOFTUPDATES #Enable FFS soft updates support +options UFS_ACL #Support for access control lists +options UFS_DIRHASH #Improve performance on big directories +#options ROOTDEVNAME=\"ufs:ada0\" + +options GEOM_LABEL # Provides labelization +options GEOM_PART_GPT # GUID Partition Tables. +#options GEOM_RAID # Soft RAID functionality. + +# Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver +options INVARIANTS #Enable calls of extra sanity checking +options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS +#options WITNESS #Enable checks to detect deadlocks and cycles +#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed + +device loop +device ether +#device le +device miibus +device bpf +device md +device uart +device random + +device fdt_pinctrl + +device clk +device regulator +device ext_resources + +device gpio + +device scbus +device da + +device mmc +device mmcsd + +# USB support +#options USB_DEBUG # enable debug msgs +#options USB_HOST_ALIGN=128 # L2 cache line size +#device ohci # OHCI PCI->USB interface +#device ehci # EHCI PCI->USB interface (USB 2.0) +#device dwcotg # DesignWare HS OTG controller +#device usb # USB Bus (required) +#device udbp # USB Double Bulk Pipe devices +#device uhid # "Human Interface Devices" +#device ulpt # Printer +#device umass # Disks/Mass storage - Requires scbus and da +#device ums # Mouse diff --git a/sys/mips/conf/X1000.hints b/sys/mips/conf/X1000.hints new file mode 100644 index 00000000000..6986b85032d --- /dev/null +++ b/sys/mips/conf/X1000.hints @@ -0,0 +1,2 @@ +# $FreeBSD$ +# device.hints diff --git a/sys/mips/ingenic/files.jz4780 b/sys/mips/ingenic/files.jz4780 new file mode 100644 index 00000000000..74f9609b8d2 --- /dev/null +++ b/sys/mips/ingenic/files.jz4780 @@ -0,0 +1,26 @@ +# $FreeBSD$ + +mips/ingenic/jz4780_dwc_fdt.c optional dwcotg +mips/ingenic/jz4780_ehci.c optional ehci +mips/ingenic/jz4780_mmc.c optional mmc +mips/ingenic/jz4780_ohci.c optional ohci +mips/ingenic/jz4780_uart.c optional uart + +mips/ingenic/jz4780_clock.c standard +mips/ingenic/jz4780_clk_gen.c standard +mips/ingenic/jz4780_clk_otg.c standard +mips/ingenic/jz4780_clk_pll.c standard +mips/ingenic/jz4780_efuse.c standard +mips/ingenic/jz4780_intr.c standard +mips/ingenic/jz4780_gpio.c standard +mips/ingenic/jz4780_machdep.c standard +mips/ingenic/jz4780_nemc.c standard +mips/ingenic/jz4780_pinctrl.c standard +mips/ingenic/jz4780_timer.c standard + +# SMP +mips/ingenic/jz4780_mp.c optional smp +mips/ingenic/jz4780_mpboot.S optional smp + +# Custom interface between pinctrl and gpio +mips/ingenic/jz4780_gpio_if.m standard diff --git a/sys/mips/ingenic/files.x1000 b/sys/mips/ingenic/files.x1000 new file mode 100644 index 00000000000..db3ae4604de --- /dev/null +++ b/sys/mips/ingenic/files.x1000 @@ -0,0 +1,17 @@ +# $FreeBSD$ + +mips/ingenic/jz4780_mmc.c optional mmc +mips/ingenic/jz4780_uart.c optional uart + +mips/ingenic/jz4780_clock.c standard +mips/ingenic/jz4780_clk_gen.c standard +mips/ingenic/jz4780_clk_otg.c standard +mips/ingenic/jz4780_clk_pll.c standard +mips/ingenic/jz4780_intr.c standard +mips/ingenic/jz4780_gpio.c standard +mips/ingenic/jz4780_machdep.c standard +mips/ingenic/jz4780_pinctrl.c standard +mips/ingenic/jz4780_timer.c standard + +# Custom interface between pinctrl and gpio +mips/ingenic/jz4780_gpio_if.m standard diff --git a/sys/mips/ingenic/jz4780_clk.h b/sys/mips/ingenic/jz4780_clk.h new file mode 100644 index 00000000000..43ade45dd69 --- /dev/null +++ b/sys/mips/ingenic/jz4780_clk.h @@ -0,0 +1,93 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _MIPS_INGENIC_JZ4780_CLK_H +#define _MIPS_INGENIC_JZ4780_CLK_H + +#include +#include + +/* Convenience bitfiled manipulation macros */ +#define REG_MSK(field) (((1u << field ## _WIDTH) - 1) << field ##_SHIFT) +#define REG_VAL(field, val) ((val) << field ##_SHIFT) +#define REG_CLR(reg, field) ((reg) & ~REG_MSK(field)) +#define REG_GET(reg, field) (((reg) & REG_MSK(field)) >> field ##_SHIFT) +#define REG_SET(reg, field, val) (REG_CLR(reg, field) | REG_VAL(field, val)) + +/* Common clock macros */ +#define CLK_LOCK(_sc) mtx_lock((_sc)->clk_mtx) +#define CLK_UNLOCK(_sc) mtx_unlock((_sc)->clk_mtx) + +#define CLK_WR_4(_sc, off, val) bus_write_4((_sc)->clk_res, (off), (val)) +#define CLK_RD_4(_sc, off) bus_read_4((_sc)->clk_res, (off)) + +struct jz4780_clk_mux_descr { + uint16_t mux_reg; + uint16_t mux_shift: 5; + uint16_t mux_bits: 5; + uint16_t mux_map: 4; /* Map into mux space */ +}; + +struct jz4780_clk_div_descr { + uint16_t div_reg; + uint16_t div_shift: 5; + uint16_t div_bits: 5; + uint16_t div_lg: 5; + int div_ce_bit: 6; /* -1, if CE bit is not present */ + int div_st_bit: 6; /* Can be negative */ + int div_busy_bit: 6; /* Can be negative */ +}; + +struct jz4780_clk_descr { + uint16_t clk_id: 6; + uint16_t clk_type: 3; + int clk_gate_bit: 7; /* Can be negative */ + struct jz4780_clk_mux_descr clk_mux; + struct jz4780_clk_div_descr clk_div; + const char *clk_name; + const char *clk_pnames[4]; +}; + +/* clk_type bits */ +#define CLK_MASK_GATE 0x01 +#define CLK_MASK_DIV 0x02 +#define CLK_MASK_MUX 0x04 + +extern int jz4780_clk_gen_register(struct clkdom *clkdom, + const struct jz4780_clk_descr *descr, struct mtx *dev_mtx, + struct resource *mem_res); + +extern int jz4780_clk_pll_register(struct clkdom *clkdom, + struct clknode_init_def *clkdef, struct mtx *dev_mtx, + struct resource *mem_res, uint32_t mem_reg); + +extern int jz4780_clk_otg_register(struct clkdom *clkdom, + struct clknode_init_def *clkdef, struct mtx *dev_mtx, + struct resource *mem_res); + +#endif /* _MIPS_INGENIC_JZ4780_CLK_PLL_H */ diff --git a/sys/mips/ingenic/jz4780_clk_gen.c b/sys/mips/ingenic/jz4780_clk_gen.c new file mode 100644 index 00000000000..7c65fb824d9 --- /dev/null +++ b/sys/mips/ingenic/jz4780_clk_gen.c @@ -0,0 +1,317 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Ingenic JZ4780 generic CGU clock driver. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* JZ4780 generic mux and div clocks implementation */ +static int jz4780_clk_gen_init(struct clknode *clk, device_t dev); +static int jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq); +static int jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop); +static int jz4780_clk_gen_set_gate(struct clknode *clk, bool enable); +static int jz4780_clk_gen_set_mux(struct clknode *clk, int src); + +struct jz4780_clk_gen_sc { + struct mtx *clk_mtx; + struct resource *clk_res; + int clk_reg; + const struct jz4780_clk_descr *clk_descr; +}; + +/* + * JZ4780 clock PLL clock methods + */ +static clknode_method_t jz4780_clk_gen_methods[] = { + CLKNODEMETHOD(clknode_init, jz4780_clk_gen_init), + CLKNODEMETHOD(clknode_set_gate, jz4780_clk_gen_set_gate), + CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_gen_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, jz4780_clk_gen_set_freq), + CLKNODEMETHOD(clknode_set_mux, jz4780_clk_gen_set_mux), + + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_gen_class, jz4780_clk_gen_methods, + sizeof(struct jz4780_clk_gen_sc), clknode_class); + +static inline unsigned +mux_to_reg(unsigned src, unsigned map) +{ + unsigned ret, bit; + + bit = (1u << 3); + for (ret = 0; bit; ret++, bit >>= 1) { + if (map & bit) { + if (src-- == 0) + return (ret); + } + } + panic("mux_to_reg"); +} + +static inline unsigned +reg_to_mux(unsigned reg, unsigned map) +{ + unsigned ret, bit; + + bit = (1u << 3); + for (ret = 0; reg; reg--, bit >>= 1) + if (map & bit) + ret++; + return (ret); +} + +static int +jz4780_clk_gen_init(struct clknode *clk, device_t dev) +{ + struct jz4780_clk_gen_sc *sc; + uint32_t reg, msk, parent_idx; + + sc = clknode_get_softc(clk); + CLK_LOCK(sc); + /* Figure our parent out */ + if (sc->clk_descr->clk_type & CLK_MASK_MUX) { + msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1; + reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg); + reg = (reg >> sc->clk_descr->clk_mux.mux_shift) & msk; + parent_idx = reg_to_mux(reg, sc->clk_descr->clk_mux.mux_map); + } else + parent_idx = 0; + CLK_UNLOCK(sc); + + clknode_init_parent_idx(clk, parent_idx); + return (0); +} + +static int +jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct jz4780_clk_gen_sc *sc; + uint32_t reg; + + sc = clknode_get_softc(clk); + + /* Calculate divisor frequency */ + if (sc->clk_descr->clk_type & CLK_MASK_DIV) { + uint32_t msk; + + msk = (1u << sc->clk_descr->clk_div.div_bits) - 1; + reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg); + reg = (reg >> sc->clk_descr->clk_div.div_shift) & msk; + reg = (reg + 1) << sc->clk_descr->clk_div.div_lg; + *freq /= reg; + } + return (0); +} + +#define DIV_TIMEOUT 100 + +static int +jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop) +{ + struct jz4780_clk_gen_sc *sc; + uint64_t _fout; + uint32_t divider, div_reg, div_msk, reg; + int rv; + + sc = clknode_get_softc(clk); + + divider = fin / *fout; + + /* Adjust for divider multiplier */ + div_reg = divider >> sc->clk_descr->clk_div.div_lg; + divider = div_reg << sc->clk_descr->clk_div.div_lg; + + _fout = fin / divider; + + /* Rounding */ + if ((flags & CLK_SET_ROUND_UP) && (*fout < _fout)) + div_reg--; + else if ((flags & CLK_SET_ROUND_DOWN) && (*fout > _fout)) + div_reg++; + if (div_reg == 0) + div_reg = 1; + + div_msk = (1u << sc->clk_descr->clk_div.div_bits) - 1; + + *stop = 1; + if (div_reg > div_msk + 1) { + *stop = 0; + div_reg = div_msk; + } + + divider = (div_reg << sc->clk_descr->clk_div.div_lg); + div_reg--; + + if ((flags & CLK_SET_DRYRUN) != 0) { + if (*stop != 0 && *fout != fin / divider && + (flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) + return (ERANGE); + *fout = fin / divider; + return (0); + } + + CLK_LOCK(sc); + /* Apply the new divider value */ + reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg); + reg &= ~(div_msk << sc->clk_descr->clk_div.div_shift); + reg |= (div_reg << sc->clk_descr->clk_div.div_shift); + /* Set the change enable bit, it present */ + if (sc->clk_descr->clk_div.div_ce_bit >= 0) + reg |= (1u << sc->clk_descr->clk_div.div_ce_bit); + /* Clear stop bit, it present */ + if (sc->clk_descr->clk_div.div_st_bit >= 0) + reg &= ~(1u << sc->clk_descr->clk_div.div_st_bit); + /* Initiate the change */ + CLK_WR_4(sc, sc->clk_descr->clk_div.div_reg, reg); + + /* Wait for busy bit to clear indicating the change is complete */ + rv = 0; + if (sc->clk_descr->clk_div.div_busy_bit >= 0) { + int i; + + for (i = 0; i < DIV_TIMEOUT; i++) { + reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg); + if (!(reg & (1u << sc->clk_descr->clk_div.div_busy_bit))) + break; + DELAY(1000); + } + if (i == DIV_TIMEOUT) + rv = ETIMEDOUT; + } + CLK_UNLOCK(sc); + + *fout = fin / divider; + return (rv); +} + +static int +jz4780_clk_gen_set_mux(struct clknode *clk, int src) +{ + struct jz4780_clk_gen_sc *sc; + uint32_t reg, msk; + + sc = clknode_get_softc(clk); + + /* Only mux nodes are capable of being reparented */ + if (!(sc->clk_descr->clk_type & CLK_MASK_MUX)) + return (src ? EINVAL : 0); + + msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1; + src = mux_to_reg(src & msk, sc->clk_descr->clk_mux.mux_map); + + CLK_LOCK(sc); + reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg); + reg &= ~(msk << sc->clk_descr->clk_mux.mux_shift); + reg |= (src << sc->clk_descr->clk_mux.mux_shift); + CLK_WR_4(sc, sc->clk_descr->clk_mux.mux_reg, reg); + CLK_UNLOCK(sc); + + return (0); +} + +static int +jz4780_clk_gen_set_gate(struct clknode *clk, bool enable) +{ + struct jz4780_clk_gen_sc *sc; + uint32_t off, reg, bit; + + sc = clknode_get_softc(clk); + + /* Check is clock can be gated */ + if (sc->clk_descr->clk_gate_bit < 0) + return 0; + + bit = sc->clk_descr->clk_gate_bit; + if (bit < 32) { + off = JZ_CLKGR0; + } else { + off = JZ_CLKGR1; + bit -= 32; + } + + CLK_LOCK(sc); + reg = CLK_RD_4(sc, off); + if (enable) + reg &= ~(1u << bit); + else + reg |= (1u << bit); + CLK_WR_4(sc, off, reg); + CLK_UNLOCK(sc); + + return (0); +} + + +int jz4780_clk_gen_register(struct clkdom *clkdom, + const struct jz4780_clk_descr *descr, struct mtx *dev_mtx, + struct resource *mem_res) +{ + struct clknode_init_def clkdef; + struct clknode *clk; + struct jz4780_clk_gen_sc *sc; + + clkdef.id = descr->clk_id; + clkdef.name = __DECONST(char *, descr->clk_name); + /* Silly const games to work around API deficiency */ + clkdef.parent_names = (const char **)(uintptr_t)&descr->clk_pnames[0]; + clkdef.flags = CLK_NODE_STATIC_STRINGS; + if (descr->clk_type & CLK_MASK_MUX) + clkdef.parent_cnt = __bitcount16(descr->clk_mux.mux_map); + else + clkdef.parent_cnt = 1; + + clk = clknode_create(clkdom, &jz4780_clk_gen_class, &clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->clk_mtx = dev_mtx; + sc->clk_res = mem_res; + sc->clk_descr = descr; + clknode_register(clkdom, clk); + + return (0); +} diff --git a/sys/mips/ingenic/jz4780_clk_otg.c b/sys/mips/ingenic/jz4780_clk_otg.c new file mode 100644 index 00000000000..26f2753a1a5 --- /dev/null +++ b/sys/mips/ingenic/jz4780_clk_otg.c @@ -0,0 +1,167 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Ingenic JZ4780 OTG PHY clock driver. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* JZ4780 OTG PHY clock */ +static int jz4780_clk_otg_init(struct clknode *clk, device_t dev); +static int jz4780_clk_otg_recalc_freq(struct clknode *clk, uint64_t *freq); +static int jz4780_clk_otg_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop); + +struct jz4780_clk_otg_sc { + struct mtx *clk_mtx; + struct resource *clk_res; +}; + +/* + * JZ4780 OTG PHY clock methods + */ +static clknode_method_t jz4780_clk_otg_methods[] = { + CLKNODEMETHOD(clknode_init, jz4780_clk_otg_init), + CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_otg_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, jz4780_clk_otg_set_freq), + + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_otg_class, jz4780_clk_otg_methods, + sizeof(struct jz4780_clk_otg_sc), clknode_class); + +static int +jz4780_clk_otg_init(struct clknode *clk, device_t dev) +{ + struct jz4780_clk_otg_sc *sc; + uint32_t reg; + + sc = clknode_get_softc(clk); + CLK_LOCK(sc); + /* Force the use fo the core clock */ + reg = CLK_RD_4(sc, JZ_USBPCR1); + reg &= ~PCR_REFCLK_M; + reg |= PCR_REFCLK_CORE; + CLK_WR_4(sc, JZ_USBPCR1, reg); + CLK_UNLOCK(sc); + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static const struct { + uint32_t div_val; + uint32_t freq; +} otg_div_table[] = { + { PCR_CLK_12, 12000000 }, + { PCR_CLK_192, 19200000 }, + { PCR_CLK_24, 24000000 }, + { PCR_CLK_48, 48000000 } +}; + +static int +jz4780_clk_otg_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct jz4780_clk_otg_sc *sc; + uint32_t reg; + int i; + + sc = clknode_get_softc(clk); + reg = CLK_RD_4(sc, JZ_USBPCR1); + reg &= PCR_CLK_M; + + for (i = 0; i < nitems(otg_div_table); i++) + if (otg_div_table[i].div_val == reg) + *freq = otg_div_table[i].freq; + return (0); +} + +static int +jz4780_clk_otg_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop) +{ + struct jz4780_clk_otg_sc *sc; + uint32_t reg; + int i; + + sc = clknode_get_softc(clk); + + for (i = 0; i < nitems(otg_div_table) - 1; i++) { + if (*fout < (otg_div_table[i].freq + otg_div_table[i + 1].freq) / 2) + break; + } + + *fout = otg_div_table[i].freq; + + *stop = 1; + if (flags & CLK_SET_DRYRUN) + return (0); + + CLK_LOCK(sc); + reg = CLK_RD_4(sc, JZ_USBPCR1); + /* Set the calculated values */ + reg &= ~PCR_CLK_M; + reg |= otg_div_table[i].div_val; + /* Initiate the change */ + CLK_WR_4(sc, JZ_USBPCR1, reg); + CLK_UNLOCK(sc); + + return (0); +} + +int jz4780_clk_otg_register(struct clkdom *clkdom, + struct clknode_init_def *clkdef, struct mtx *dev_mtx, + struct resource *mem_res) +{ + struct clknode *clk; + struct jz4780_clk_otg_sc *sc; + + clk = clknode_create(clkdom, &jz4780_clk_otg_class, clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->clk_mtx = dev_mtx; + sc->clk_res = mem_res; + clknode_register(clkdom, clk); + return (0); +} diff --git a/sys/mips/ingenic/jz4780_clk_pll.c b/sys/mips/ingenic/jz4780_clk_pll.c new file mode 100644 index 00000000000..23710e54ac7 --- /dev/null +++ b/sys/mips/ingenic/jz4780_clk_pll.c @@ -0,0 +1,234 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Ingenic JZ4780 CGU driver. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/********************************************************************** + * JZ4780 PLL control register bit fields + **********************************************************************/ +#define CGU_PLL_M_SHIFT 19 +#define CGU_PLL_M_WIDTH 13 + +#define CGU_PLL_N_SHIFT 13 +#define CGU_PLL_N_WIDTH 6 + +#define CGU_PLL_OD_SHIFT 9 +#define CGU_PLL_OD_WIDTH 4 + +#define CGU_PLL_LOCK_SHIFT 6 +#define CGU_PLL_LOCK_WIDTH 1 + +#define CGU_PLL_ON_SHIFT 4 +#define CGU_PLL_ON_WIDTH 1 + +#define CGU_PLL_MODE_SHIFT 3 +#define CGU_PLL_MODE_WIDTH 1 + +#define CGU_PLL_BP_SHIFT 1 +#define CGU_PLL_BP_WIDTH 1 + +#define CGU_PLL_EN_SHIFT 0 +#define CGU_PLL_EN_WIDTH 1 + +/* JZ4780 PLL clock */ +static int jz4780_clk_pll_init(struct clknode *clk, device_t dev); +static int jz4780_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq); +static int jz4780_clk_pll_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop); + +struct jz4780_clk_pll_sc { + struct mtx *clk_mtx; + struct resource *clk_res; + uint32_t clk_reg; +}; + +/* + * JZ4780 PLL clock methods + */ +static clknode_method_t jz4780_clk_pll_methods[] = { + CLKNODEMETHOD(clknode_init, jz4780_clk_pll_init), + CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_pll_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, jz4780_clk_pll_set_freq), + + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_pll_class, jz4780_clk_pll_methods, + sizeof(struct jz4780_clk_pll_sc), clknode_class); + +static int +jz4780_clk_pll_init(struct clknode *clk, device_t dev) +{ + struct jz4780_clk_pll_sc *sc; + uint32_t reg; + + sc = clknode_get_softc(clk); + CLK_LOCK(sc); + reg = CLK_RD_4(sc, sc->clk_reg); + CLK_WR_4(sc, sc->clk_reg, reg); + CLK_UNLOCK(sc); + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +jz4780_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct jz4780_clk_pll_sc *sc; + uint32_t reg, m, n, od; + + sc = clknode_get_softc(clk); + reg = CLK_RD_4(sc, sc->clk_reg); + + /* Check for PLL enabled status */ + if (REG_GET(reg, CGU_PLL_EN) == 0) { + *freq = 0; + return 0; + } + + /* Return parent frequency if PPL is being bypassed */ + if (REG_GET(reg, CGU_PLL_BP) != 0) + return 0; + + m = REG_GET(reg, CGU_PLL_M) + 1; + n = REG_GET(reg, CGU_PLL_N) + 1; + od = REG_GET(reg, CGU_PLL_OD) + 1; + + /* Sanity check values */ + if (m == 0 || n == 0 || od == 0) { + *freq = 0; + return (EINVAL); + } + + *freq = ((*freq / n) * m) / od; + return (0); +} + +#define MHZ (1000 * 1000) +#define PLL_TIMEOUT 100 + +static int +jz4780_clk_pll_wait_lock(struct jz4780_clk_pll_sc *sc) +{ + int i; + + for (i = 0; i < PLL_TIMEOUT; i++) { + if (CLK_RD_4(sc, sc->clk_reg) & REG_VAL(CGU_PLL_LOCK, 1)) + return (0); + DELAY(1000); + } + return (ETIMEDOUT); +} + +static int +jz4780_clk_pll_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop) +{ + struct jz4780_clk_pll_sc *sc; + uint32_t reg, m, n, od; + int rv; + + sc = clknode_get_softc(clk); + + /* Should be able to figure all clocks with m & n only */ + od = 1; + + m = MIN((uint32_t)(*fout / MHZ), (1u << CGU_PLL_M_WIDTH)); + m = MIN(m, 1); + + n = MIN((uint32_t)(fin / MHZ), (1u << CGU_PLL_N_WIDTH)); + n = MIN(n, 1); + + if (flags & CLK_SET_DRYRUN) { + if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && + (*fout != (((fin / n) * m) / od))) + return (ERANGE); + + *fout = ((fin / n) * m) / od; + return (0); + } + + CLK_LOCK(sc); + reg = CLK_RD_4(sc, sc->clk_reg); + + /* Set the calculated values */ + reg = REG_SET(reg, CGU_PLL_M, m - 1); + reg = REG_SET(reg, CGU_PLL_N, n - 1); + reg = REG_SET(reg, CGU_PLL_OD, od - 1); + + /* Enable the PLL */ + reg = REG_SET(reg, CGU_PLL_EN, 1); + reg = REG_SET(reg, CGU_PLL_BP, 0); + + /* Initiate the change */ + CLK_WR_4(sc, sc->clk_reg, reg); + + /* Wait for PLL to lock */ + rv = jz4780_clk_pll_wait_lock(sc); + CLK_UNLOCK(sc); + if (rv != 0) + return (rv); + + *fout = ((fin / n) * m) / od; + return (0); +} + +int jz4780_clk_pll_register(struct clkdom *clkdom, + struct clknode_init_def *clkdef, struct mtx *dev_mtx, + struct resource *mem_res, uint32_t mem_reg) +{ + struct clknode *clk; + struct jz4780_clk_pll_sc *sc; + + clk = clknode_create(clkdom, &jz4780_clk_pll_class, clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->clk_mtx = dev_mtx; + sc->clk_res = mem_res; + sc->clk_reg = mem_reg; + clknode_register(clkdom, clk); + return (0); +} diff --git a/sys/mips/ingenic/jz4780_clock.c b/sys/mips/ingenic/jz4780_clock.c new file mode 100644 index 00000000000..7261cb00ee4 --- /dev/null +++ b/sys/mips/ingenic/jz4780_clock.c @@ -0,0 +1,831 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Ingenic JZ4780 CGU driver. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include "clkdev_if.h" + +#include + +/********************************************************************** + * JZ4780 CGU clock domain + **********************************************************************/ +struct jz4780_clock_softc { + device_t dev; + struct resource *res[1]; + struct mtx mtx; + struct clkdom *clkdom; +}; + +static struct resource_spec jz4780_clock_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +struct jz4780_clk_pll_def { + uint16_t clk_id; + uint16_t clk_reg; + const char *clk_name; + const char *clk_pname[1]; +}; + +#define PLL(_id, cname, pname, reg) { \ + .clk_id = _id, \ + .clk_reg = reg, \ + .clk_name = cname, \ + .clk_pname[0] = pname, \ +} + +struct jz4780_clk_gate_def { + uint16_t clk_id; + uint16_t clk_bit; + const char *clk_name; + const char *clk_pname[1]; +}; + +#define GATE(_id, cname, pname, bit) { \ + .clk_id = _id, \ + .clk_bit = bit, \ + .clk_name = cname, \ + .clk_pname[0] = pname, \ +} + +#define MUX(reg, shift, bits, map) \ + .clk_mux.mux_reg = (reg), \ + .clk_mux.mux_shift = (shift), \ + .clk_mux.mux_bits = (bits), \ + .clk_mux.mux_map = (map), +#define NO_MUX + +#define DIV(reg, shift, lg, bits, ce, st, bb) \ + .clk_div.div_reg = (reg), \ + .clk_div.div_shift = (shift), \ + .clk_div.div_bits = (bits), \ + .clk_div.div_lg = (lg), \ + .clk_div.div_ce_bit = (ce), \ + .clk_div.div_st_bit = (st), \ + .clk_div.div_busy_bit = (bb), +#define NO_DIV \ + +#define GATEBIT(bit) \ + .clk_gate_bit = (bit), +#define NO_GATE \ + .clk_gate_bit = (-1), + +#define PLIST(pnames...) \ + .clk_pnames = { pnames }, + +#define GENCLK(id, name, type, parents, mux, div, gt) { \ + .clk_id = id, \ + .clk_type = type, \ + .clk_name = name, \ + parents \ + mux \ + div \ + gt \ +} + +/* PLL definitions */ +static struct jz4780_clk_pll_def pll_clks[] = { + PLL(JZ4780_CLK_APLL, "apll", "ext", JZ_CPAPCR), + PLL(JZ4780_CLK_MPLL, "mpll", "ext", JZ_CPMPCR), + PLL(JZ4780_CLK_EPLL, "epll", "ext", JZ_CPEPCR), + PLL(JZ4780_CLK_VPLL, "vpll", "ext", JZ_CPVPCR), +}; + +/* OTG PHY clock (reuse gate def structure */ +static struct jz4780_clk_gate_def otg_clks[] = { + GATE(JZ4780_CLK_OTGPHY, "otg_phy", "ext", 0), +}; + +static const struct jz4780_clk_descr gen_clks[] = { + GENCLK(JZ4780_CLK_SCLKA, "sclk_a", CLK_MASK_MUX, + PLIST("apll", "ext", "rtc"), + MUX(JZ_CPCCR, 30, 2, 0x7), + NO_DIV, + NO_GATE + ), + + GENCLK(JZ4780_CLK_CPUMUX, "cpumux", CLK_MASK_MUX, + PLIST("sclk_a", "mpll", "epll"), + MUX(JZ_CPCCR, 28, 2, 0x7), + NO_DIV, + NO_GATE + ), + + GENCLK(JZ4780_CLK_CPU, "cpu", CLK_MASK_DIV, + PLIST("cpumux"), + NO_MUX, + DIV(JZ_CPCCR, 0, 0, 4, 22, -1, -1), + NO_GATE + ), + + GENCLK(JZ4780_CLK_L2CACHE, "l2cache", CLK_MASK_DIV, + PLIST("cpumux"), + NO_MUX, + DIV(JZ_CPCCR, 4, 0, 4, -1, -1, -1), + NO_GATE + ), + + GENCLK(JZ4780_CLK_AHB0, "ahb0", CLK_MASK_MUX | CLK_MASK_DIV, + PLIST("sclk_a", "mpll", "epll"), + MUX(JZ_CPCCR, 26, 2, 0x7), + DIV(JZ_CPCCR, 8, 0, 4, 21, -1, -1), + NO_GATE + ), + + GENCLK(JZ4780_CLK_AHB2PMUX, "ahb2_apb_mux", CLK_MASK_MUX, + PLIST("sclk_a", "mpll", "rtc"), + MUX(JZ_CPCCR, 24, 2, 0x7), + NO_DIV, + NO_GATE + ), + + GENCLK(JZ4780_CLK_AHB2, "ahb2", CLK_MASK_DIV, + PLIST("ahb2_apb_mux"), + NO_MUX, + DIV(JZ_CPCCR, 12, 0, 4, 20, -1, -1), + NO_GATE + ), + + GENCLK(JZ4780_CLK_PCLK, "pclk", CLK_MASK_DIV, + PLIST("ahb2_apb_mux"), + NO_MUX, + DIV(JZ_CPCCR, 16, 0, 4, 20, -1, -1), + NO_GATE + ), + + GENCLK(JZ4780_CLK_DDR, "ddr", CLK_MASK_MUX | CLK_MASK_DIV, + PLIST("sclk_a", "mpll"), + MUX(JZ_DDCDR, 30, 2, 0x6), + DIV(JZ_DDCDR, 0, 0, 4, 29, 28, 27), + NO_GATE + ), + + GENCLK(JZ4780_CLK_VPU, "vpu", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, + PLIST("sclk_a", "mpll", "epll"), + MUX(JZ_VPUCDR, 30, 2, 0xe), + DIV(JZ_VPUCDR, 0, 0, 4, 29, 28, 27), + GATEBIT(32 + 2) + ), + + GENCLK(JZ4780_CLK_I2SPLL, "i2s_pll", CLK_MASK_MUX | CLK_MASK_DIV, + PLIST("sclk_a", "epll"), + MUX(JZ_I2SCDR, 30, 1, 0xc), + DIV(JZ_I2SCDR, 0, 0, 8, 29, 28, 27), + NO_GATE + ), + + GENCLK(JZ4780_CLK_I2S, "i2s", CLK_MASK_MUX, + PLIST("ext", "i2s_pll"), + MUX(JZ_I2SCDR, 31, 1, 0xc), + NO_DIV, + NO_GATE + ), + + GENCLK(JZ4780_CLK_LCD0PIXCLK, "lcd0pixclk", CLK_MASK_MUX | CLK_MASK_DIV, + PLIST("sclk_a", "mpll", "vpll"), + MUX(JZ_LP0CDR, 30, 2, 0xe), + DIV(JZ_LP0CDR, 0, 0, 8, 28, 27, 26), + NO_GATE + ), + + GENCLK(JZ4780_CLK_LCD1PIXCLK, "lcd1pixclk", CLK_MASK_MUX | CLK_MASK_DIV, + PLIST("sclk_a", "mpll", "vpll"), + MUX(JZ_LP1CDR, 30, 2, 0xe), + DIV(JZ_LP1CDR, 0, 0, 8, 28, 27, 26), + NO_GATE + ), + + GENCLK(JZ4780_CLK_MSCMUX, "msc_mux", CLK_MASK_MUX, + PLIST("sclk_a", "mpll"), + MUX(JZ_MSC0CDR, 30, 2, 0x6), + NO_DIV, + NO_GATE + ), + + GENCLK(JZ4780_CLK_MSC0, "msc0", CLK_MASK_DIV | CLK_MASK_GATE, + PLIST("msc_mux"), + NO_MUX, + DIV(JZ_MSC0CDR, 0, 1, 8, 29, 28, 27), + GATEBIT(3) + ), + + GENCLK(JZ4780_CLK_MSC1, "msc1", CLK_MASK_DIV | CLK_MASK_GATE, + PLIST("msc_mux"), + NO_MUX, + DIV(JZ_MSC1CDR, 0, 1, 8, 29, 28, 27), + GATEBIT(11) + ), + + GENCLK(JZ4780_CLK_MSC2, "msc2", CLK_MASK_DIV | CLK_MASK_GATE, + PLIST("msc_mux"), + NO_MUX, + DIV(JZ_MSC2CDR, 0, 1, 8, 29, 28, 27), + GATEBIT(12) + ), + + GENCLK(JZ4780_CLK_UHC, "uhc", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, + PLIST("sclk_a", "mpll", "epll", "otg_phy"), + MUX(JZ_UHCCDR, 30, 2, 0xf), + DIV(JZ_UHCCDR, 0, 0, 8, 29, 28, 27), + GATEBIT(24) + ), + + GENCLK(JZ4780_CLK_SSIPLL, "ssi_pll", CLK_MASK_MUX | CLK_MASK_DIV, + PLIST("sclk_a", "mpll"), + MUX(JZ_SSICDR, 30, 1, 0xc), + DIV(JZ_SSICDR, 0, 0, 8, 29, 28, 27), + NO_GATE + ), + + GENCLK(JZ4780_CLK_SSI, "ssi", CLK_MASK_MUX, + PLIST("ext", "ssi_pll"), + MUX(JZ_SSICDR, 31, 1, 0xc), + NO_DIV, + NO_GATE + ), + + GENCLK(JZ4780_CLK_CIMMCLK, "cim_mclk", CLK_MASK_MUX | CLK_MASK_DIV, + PLIST("sclk_a", "mpll"), + MUX(JZ_CIMCDR, 31, 1, 0xc), + DIV(JZ_CIMCDR, 0, 0, 8, 30, 29, 28), + NO_GATE + ), + + GENCLK(JZ4780_CLK_PCMPLL, "pcm_pll", CLK_MASK_MUX | CLK_MASK_DIV, + PLIST("sclk_a", "mpll", "epll", "vpll"), + MUX(JZ_PCMCDR, 29, 2, 0xf), + DIV(JZ_PCMCDR, 0, 0, 8, 28, 27, 26), + NO_GATE + ), + + GENCLK(JZ4780_CLK_PCM, "pcm", CLK_MASK_MUX | CLK_MASK_GATE, + PLIST("ext", "pcm_pll"), + MUX(JZ_PCMCDR, 31, 1, 0xc), + NO_DIV, + GATEBIT(32 + 3) + ), + + GENCLK(JZ4780_CLK_GPU, "gpu", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, + PLIST("sclk_a", "mpll", "epll"), + MUX(JZ_GPUCDR, 30, 2, 0x7), + DIV(JZ_GPUCDR, 0, 0, 4, 29, 28, 27), + GATEBIT(32 + 4) + ), + + GENCLK(JZ4780_CLK_HDMI, "hdmi", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, + PLIST("sclk_a", "mpll", "vpll"), + MUX(JZ_HDMICDR, 30, 2, 0xe), + DIV(JZ_HDMICDR, 0, 0, 8, 29, 28, 26), + GATEBIT(32 + 9) + ), + + GENCLK(JZ4780_CLK_BCH, "bch", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, + PLIST("sclk_a", "mpll", "epll"), + MUX(JZ_BCHCDR, 30, 2, 0x7), + DIV(JZ_BCHCDR, 0, 0, 4, 29, 28, 27), + GATEBIT(1) + ), +}; + +static struct jz4780_clk_gate_def gate_clks[] = { + GATE(JZ4780_CLK_NEMC, "nemc", "ahb2", 0), + GATE(JZ4780_CLK_OTG0, "otg0", "ext", 2), + GATE(JZ4780_CLK_SSI0, "ssi0", "ssi", 4), + GATE(JZ4780_CLK_SMB0, "smb0", "pclk", 5), + GATE(JZ4780_CLK_SMB1, "smb1", "pclk", 6), + GATE(JZ4780_CLK_SCC, "scc", "ext", 7), + GATE(JZ4780_CLK_AIC, "aic", "ext", 8), + GATE(JZ4780_CLK_TSSI0, "tssi0", "ext", 9), + GATE(JZ4780_CLK_OWI, "owi", "ext", 10), + GATE(JZ4780_CLK_KBC, "kbc", "ext", 13), + GATE(JZ4780_CLK_SADC, "sadc", "ext", 14), + GATE(JZ4780_CLK_UART0, "uart0", "ext", 15), + GATE(JZ4780_CLK_UART1, "uart1", "ext", 16), + GATE(JZ4780_CLK_UART2, "uart2", "ext", 17), + GATE(JZ4780_CLK_UART3, "uart3", "ext", 18), + GATE(JZ4780_CLK_SSI1, "ssi1", "ssi", 19), + GATE(JZ4780_CLK_SSI2, "ssi2", "ssi", 20), + GATE(JZ4780_CLK_PDMA, "pdma", "ext", 21), + GATE(JZ4780_CLK_GPS, "gps", "ext", 22), + GATE(JZ4780_CLK_MAC, "mac", "ext", 23), + GATE(JZ4780_CLK_SMB2, "smb2", "pclk", 25), + GATE(JZ4780_CLK_CIM, "cim", "ext", 26), + GATE(JZ4780_CLK_LCD, "lcd", "ext", 28), + GATE(JZ4780_CLK_TVE, "tve", "lcd", 27), + GATE(JZ4780_CLK_IPU, "ipu", "ext", 29), + GATE(JZ4780_CLK_DDR0, "ddr0", "ddr", 30), + GATE(JZ4780_CLK_DDR1, "ddr1", "ddr", 31), + GATE(JZ4780_CLK_SMB3, "smb3", "pclk", 32 + 0), + GATE(JZ4780_CLK_TSSI1, "tssi1", "ext", 32 + 1), + GATE(JZ4780_CLK_COMPRESS, "compress", "ext", 32 + 5), + GATE(JZ4780_CLK_AIC1, "aic1", "ext", 32 + 6), + GATE(JZ4780_CLK_GPVLC, "gpvlc", "ext", 32 + 7), + GATE(JZ4780_CLK_OTG1, "otg1", "ext", 32 + 8), + GATE(JZ4780_CLK_UART4, "uart4", "ext", 32 + 10), + GATE(JZ4780_CLK_AHBMON, "ahb_mon", "ext", 32 + 11), + GATE(JZ4780_CLK_SMB4, "smb4", "pclk", 32 + 12), + GATE(JZ4780_CLK_DES, "des", "ext", 32 + 13), + GATE(JZ4780_CLK_X2D, "x2d", "ext", 32 + 14), + GATE(JZ4780_CLK_CORE1, "core1", "cpu", 32 + 15), +}; + +static int +jz4780_clock_register(struct jz4780_clock_softc *sc) +{ + int i, ret; + + /* Register PLLs */ + for (i = 0; i < nitems(pll_clks); i++) { + struct clknode_init_def clkdef; + + clkdef.id = pll_clks[i].clk_id; + clkdef.name = __DECONST(char *, pll_clks[i].clk_name); + clkdef.parent_names = pll_clks[i].clk_pname; + clkdef.parent_cnt = 1; + clkdef.flags = CLK_NODE_STATIC_STRINGS; + + ret = jz4780_clk_pll_register(sc->clkdom, &clkdef, &sc->mtx, + sc->res[0], pll_clks[i].clk_reg); + if (ret != 0) + return (ret); + } + + /* Register OTG clock */ + for (i = 0; i < nitems(otg_clks); i++) { + struct clknode_init_def clkdef; + + clkdef.id = otg_clks[i].clk_id; + clkdef.name = __DECONST(char *, otg_clks[i].clk_name); + clkdef.parent_names = otg_clks[i].clk_pname; + clkdef.parent_cnt = 1; + clkdef.flags = CLK_NODE_STATIC_STRINGS; + + ret = jz4780_clk_otg_register(sc->clkdom, &clkdef, &sc->mtx, + sc->res[0]); + if (ret != 0) + return (ret); + } + + /* Register muxes and divisors */ + for (i = 0; i < nitems(gen_clks); i++) { + ret = jz4780_clk_gen_register(sc->clkdom, &gen_clks[i], + &sc->mtx, sc->res[0]); + if (ret != 0) + return (ret); + } + + /* Register simple gates */ + for (i = 0; i < nitems(gate_clks); i++) { + struct clk_gate_def gatedef; + + gatedef.clkdef.id = gate_clks[i].clk_id; + gatedef.clkdef.name = __DECONST(char *, gate_clks[i].clk_name); + gatedef.clkdef.parent_names = gate_clks[i].clk_pname; + gatedef.clkdef.parent_cnt = 1; + gatedef.clkdef.flags = CLK_NODE_STATIC_STRINGS; + + if (gate_clks[i].clk_bit < 32) { + gatedef.offset = JZ_CLKGR0; + gatedef.shift = gate_clks[i].clk_bit; + } else { + gatedef.offset = JZ_CLKGR1; + gatedef.shift = gate_clks[i].clk_bit - 32; + } + gatedef.mask = 1; + gatedef.on_value = 0; + gatedef.off_value = 1; + gatedef.gate_flags = 0; + + ret = clknode_gate_register(sc->clkdom, &gatedef); + if (ret != 0) + return (ret); + + } + + return (0); +} + +static int +jz4780_clock_fixup(struct jz4780_clock_softc *sc) +{ + struct clknode *clk_uhc; + int ret; + + /* + * Make UHC mux use MPLL as the source. It defaults to OTG_PHY + * and that somehow just does not work. + */ + clkdom_xlock(sc->clkdom); + + /* Assume the worst */ + ret = ENXIO; + + clk_uhc = clknode_find_by_id(sc->clkdom, JZ4780_CLK_UHC); + if (clk_uhc != NULL) { + ret = clknode_set_parent_by_name(clk_uhc, "mpll"); + if (ret != 0) + device_printf(sc->dev, + "unable to reparent uhc clock\n"); + else + ret = clknode_set_freq(clk_uhc, 48000000, 0, 0); + if (ret != 0) + device_printf(sc->dev, "unable to init uhc clock\n"); + } else + device_printf(sc->dev, "unable to lookup uhc clock\n"); + + clkdom_unlock(sc->clkdom); + return (ret); +} + +#define CGU_LOCK(sc) mtx_lock(&(sc)->mtx) +#define CGU_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define CGU_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "jz4780-cgu", MTX_DEF) +#define CGU_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg)) + +static int +jz4780_clock_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-cgu")) + return (ENXIO); + + device_set_desc(dev, "Ingenic jz4780 CGU"); + + return (BUS_PROBE_DEFAULT); +} + +static int +jz4780_clock_attach(device_t dev) +{ + struct jz4780_clock_softc *sc; + + sc = device_get_softc(dev); + if (bus_alloc_resources(dev, jz4780_clock_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + sc->dev = dev; + CGU_LOCK_INIT(sc); + + sc->clkdom = clkdom_create(dev); + if (sc->clkdom == NULL) + goto fail; + if (jz4780_clock_register(sc) != 0) + goto fail; + if (clkdom_finit(sc->clkdom) != 0) + goto fail; + if (jz4780_clock_fixup(sc) != 0) + goto fail; + if (bootverbose) + clkdom_dump(sc->clkdom); + + return (0); +fail: + bus_release_resources(dev, jz4780_clock_spec, sc->res); + CGU_LOCK_DESTROY(sc); + + return (ENXIO); +} + +static int +jz4780_clock_detach(device_t dev) +{ + struct jz4780_clock_softc *sc; + + sc = device_get_softc(dev); + bus_release_resources(dev, jz4780_clock_spec, sc->res); + CGU_LOCK_DESTROY(sc); + + return (0); +} + +static int +jz4780_clock_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct jz4780_clock_softc *sc; + + sc = device_get_softc(dev); + CSR_WRITE_4(sc, addr, val); + return (0); +} + +static int +jz4780_clock_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct jz4780_clock_softc *sc; + + sc = device_get_softc(dev); + *val = CSR_READ_4(sc, addr); + return (0); +} + +static int +jz4780_clock_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask, + uint32_t set_mask) +{ + struct jz4780_clock_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + val = CSR_READ_4(sc, addr); + val &= clear_mask; + val |= set_mask; + CSR_WRITE_4(sc, addr, val); + return (0); +} + +static void +jz4780_clock_device_lock(device_t dev) +{ + struct jz4780_clock_softc *sc; + + sc = device_get_softc(dev); + CGU_LOCK(sc); +} + +static void +jz4780_clock_device_unlock(device_t dev) +{ + struct jz4780_clock_softc *sc; + + sc = device_get_softc(dev); + CGU_UNLOCK(sc); +} + +static device_method_t jz4780_clock_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_clock_probe), + DEVMETHOD(device_attach, jz4780_clock_attach), + DEVMETHOD(device_detach, jz4780_clock_detach), + + /* Clock device interface */ + DEVMETHOD(clkdev_write_4, jz4780_clock_write_4), + DEVMETHOD(clkdev_read_4, jz4780_clock_read_4), + DEVMETHOD(clkdev_modify_4, jz4780_clock_modify_4), + DEVMETHOD(clkdev_device_lock, jz4780_clock_device_lock), + DEVMETHOD(clkdev_device_unlock, jz4780_clock_device_unlock), + + DEVMETHOD_END +}; + +static driver_t jz4780_clock_driver = { + "cgu", + jz4780_clock_methods, + sizeof(struct jz4780_clock_softc), +}; + +static devclass_t jz4780_clock_devclass; + +EARLY_DRIVER_MODULE(jz4780_clock, simplebus, jz4780_clock_driver, + jz4780_clock_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE); + +static int +jz4780_ehci_clk_config(struct jz4780_clock_softc *sc) +{ + clk_t phy_clk, ext_clk; + uint64_t phy_freq; + int err; + + phy_clk = NULL; + ext_clk = NULL; + err = -1; + + /* Set phy timing by copying it from ext */ + if (clk_get_by_id(sc->dev, sc->clkdom, JZ4780_CLK_OTGPHY, + &phy_clk) != 0) + goto done; + if (clk_get_parent(phy_clk, &ext_clk) != 0) + goto done; + if (clk_get_freq(ext_clk, &phy_freq) != 0) + goto done; + if (clk_set_freq(phy_clk, phy_freq, 0) != 0) + goto done; + err = 0; +done: + clk_release(ext_clk); + clk_release(phy_clk); + + return (err); +} + +int +jz4780_ohci_enable(void) +{ + device_t dev; + struct jz4780_clock_softc *sc; + uint32_t reg; + + dev = devclass_get_device(jz4780_clock_devclass, 0); + if (dev == NULL) + return (-1); + + sc = device_get_softc(dev); + CGU_LOCK(sc); + + /* Do not force port1 to suspend mode */ + reg = CSR_READ_4(sc, JZ_OPCR); + reg |= OPCR_SPENDN1; + CSR_WRITE_4(sc, JZ_OPCR, reg); + + CGU_UNLOCK(sc); + return (0); +} + +int +jz4780_ehci_enable(void) +{ + device_t dev; + struct jz4780_clock_softc *sc; + uint32_t reg; + + dev = devclass_get_device(jz4780_clock_devclass, 0); + if (dev == NULL) + return (-1); + + sc = device_get_softc(dev); + + /* + * EHCI should use MPPL as a parent, but Linux configures OTG + * clock anyway. Follow their lead blindly. + */ + if (jz4780_ehci_clk_config(sc) != 0) + return (-1); + + CGU_LOCK(sc); + + /* Enable OTG, should not be necessary since we use PLL clock */ + reg = CSR_READ_4(sc, JZ_USBPCR); + reg &= ~(PCR_OTG_DISABLE); + CSR_WRITE_4(sc, JZ_USBPCR, reg); + + /* Do not force port1 to suspend mode */ + reg = CSR_READ_4(sc, JZ_OPCR); + reg |= OPCR_SPENDN1; + CSR_WRITE_4(sc, JZ_OPCR, reg); + + /* D- pulldown */ + reg = CSR_READ_4(sc, JZ_USBPCR1); + reg |= PCR_DMPD1; + CSR_WRITE_4(sc, JZ_USBPCR1, reg); + + /* D+ pulldown */ + reg = CSR_READ_4(sc, JZ_USBPCR1); + reg |= PCR_DPPD1; + CSR_WRITE_4(sc, JZ_USBPCR1, reg); + + /* 16 bit bus witdth for port 1*/ + reg = CSR_READ_4(sc, JZ_USBPCR1); + reg |= PCR_WORD_I_F1 | PCR_WORD_I_F0; + CSR_WRITE_4(sc, JZ_USBPCR1, reg); + + /* Reset USB */ + reg = CSR_READ_4(sc, JZ_USBPCR); + reg |= PCR_POR; + CSR_WRITE_4(sc, JZ_USBPCR, reg); + DELAY(1); + reg = CSR_READ_4(sc, JZ_USBPCR); + reg &= ~(PCR_POR); + CSR_WRITE_4(sc, JZ_USBPCR, reg); + + /* Soft-reset USB */ + reg = CSR_READ_4(sc, JZ_SRBC); + reg |= SRBC_UHC_SR; + CSR_WRITE_4(sc, JZ_SRBC, reg); + /* 300ms */ + DELAY(300*hz/1000); + + reg = CSR_READ_4(sc, JZ_SRBC); + reg &= ~(SRBC_UHC_SR); + CSR_WRITE_4(sc, JZ_SRBC, reg); + + /* 300ms */ + DELAY(300*hz/1000); + + CGU_UNLOCK(sc); + return (0); +} + +#define USBRESET_DETECT_TIME 0x96 + +int +jz4780_otg_enable(void) +{ + device_t dev; + struct jz4780_clock_softc *sc; + uint32_t reg; + + dev = devclass_get_device(jz4780_clock_devclass, 0); + if (dev == NULL) + return (-1); + + sc = device_get_softc(dev); + + CGU_LOCK(sc); + + /* Select Synopsys OTG mode */ + reg = CSR_READ_4(sc, JZ_USBPCR1); + reg |= PCR_SYNOPSYS; + + /* Set UTMI bus width to 16 bit */ + reg |= PCR_WORD_I_F0 | PCR_WORD_I_F1; + CSR_WRITE_4(sc, JZ_USBPCR1, reg); + + /* Blah */ + reg = CSR_READ_4(sc, JZ_USBVBFIL); + reg = REG_SET(reg, USBVBFIL_IDDIGFIL, 0); + reg = REG_SET(reg, USBVBFIL_USBVBFIL, 0); + CSR_WRITE_4(sc, JZ_USBVBFIL, reg); + + /* Setup reset detect time */ + reg = CSR_READ_4(sc, JZ_USBRDT); + reg = REG_SET(reg, USBRDT_USBRDT, USBRESET_DETECT_TIME); + reg |= USBRDT_VBFIL_LD_EN; + CSR_WRITE_4(sc, JZ_USBRDT, reg); + + /* Setup USBPCR bits */ + reg = CSR_READ_4(sc, JZ_USBPCR); + reg |= PCR_USB_MODE; + reg |= PCR_COMMONONN; + reg |= PCR_VBUSVLDEXT; + reg |= PCR_VBUSVLDEXTSEL; + reg &= ~(PCR_OTG_DISABLE); + CSR_WRITE_4(sc, JZ_USBPCR, reg); + + /* Reset USB */ + reg = CSR_READ_4(sc, JZ_USBPCR); + reg |= PCR_POR; + CSR_WRITE_4(sc, JZ_USBPCR, reg); + DELAY(1000); + reg = CSR_READ_4(sc, JZ_USBPCR); + reg &= ~(PCR_POR); + CSR_WRITE_4(sc, JZ_USBPCR, reg); + + /* Unsuspend OTG port */ + reg = CSR_READ_4(sc, JZ_OPCR); + reg |= OPCR_SPENDN0; + CSR_WRITE_4(sc, JZ_OPCR, reg); + + CGU_UNLOCK(sc); + return (0); +} diff --git a/sys/mips/ingenic/jz4780_clock.h b/sys/mips/ingenic/jz4780_clock.h new file mode 100644 index 00000000000..226e81bc67a --- /dev/null +++ b/sys/mips/ingenic/jz4780_clock.h @@ -0,0 +1,36 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef JZ4780_CLOCK_H +#define JZ4780_CLOCK_H + +extern int jz4780_ehci_enable(void); +extern int jz4780_ohci_enable(void); +extern int jz4780_otg_enable(void); + +#endif diff --git a/sys/mips/ingenic/jz4780_cpuregs.h b/sys/mips/ingenic/jz4780_cpuregs.h new file mode 100644 index 00000000000..35573073845 --- /dev/null +++ b/sys/mips/ingenic/jz4780_cpuregs.h @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef JZ4780_CPUREGS_H +#define JZ4780_CPUREGS_H + +/* Core control register */ +#define JZ_CORECTL_SLP1M_SHIFT 17 +#define JZ_CORECTL_SLP1M (1u << JZ_CORECTL_SLP1M_SHIFT) +#define JZ_CORECTL_SLP0M_SHIFT 16 +#define JZ_CORECTL_SLP0M (1u << JZ_CORECTL_SLP0M_SHIFT) +#define JZ_CORECTL_RPC1_SHIFT 9 +#define JZ_CORECTL_RPC1 (1u << JZ_CORECTL_RPC1_SHIFT) +#define JZ_CORECTL_RPC0_SHIFT 8 +#define JZ_CORECTL_RPC0 (1u << JZ_CORECTL_RPC0_SHIFT) +#define JZ_CORECTL_SWRST1_SHIFT 1 +#define JZ_CORECTL_SWRST1 (1u << JZ_CORECTL_SWRST1_SHIFT) +#define JZ_CORECTL_SWRST0_SHIFT 0 +#define JZ_CORECTL_SWRST0 (1u << JZ_CORECTL_SWRST0_SHIFT) + +/* Core status register */ +#define JZ_CORESTS_SLP1_SHIFT 17 +#define JZ_CORESTS_SLP1 (1u << JZ_CORESTS_SLP1_SHIFT) +#define JZ_CORESTS_SLP0_SHIFT 16 +#define JZ_CORESTS_SLP0 (1u << JZ_CORESTS_SLP0_SHIFT) +#define JZ_CORESTS_IRQ1P_SHIFT 9 +#define JZ_CORESTS_IRQ1P (1u << JZ_CORESTS_IRQ1P_SHIFT) +#define JZ_CORESTS_IRQ0P_SHIFT 8 +#define JZ_CORESTS_IRQ0P (1u << JZ_CORESTS_IRQ0P_SHIFT) +#define JZ_CORESTS_MIRQ1P_SHIFT 1 +#define JZ_CORESTS_MIRQ1P (1u << JZ_CORESTS_MIRQ1P_SHIFT) +#define JZ_CORESTS_MIRQ0P_SHIFT 0 +#define JZ_CORESTS_MIRQ0P (1u << JZ_CORESTS_MIRQ0P_SHIFT) + +/* Reset entry and IRQ mask */ +#define JZ_REIM_ENTRY_SHIFT 16 +#define JZ_REIM_ENTRY_WIDTH 16 +#define JZ_REIM_ENTRY_MASK (0xFFFFu << JZ_REIM_ENTRY_SHIFT) +#define JZ_REIM_IRQ1M_SHIFT 9 +#define JZ_REIM_IRQ1M (1u << JZ_REIM_IRQ1M_SHIFT) +#define JZ_REIM_IRQ0M_SHIFT 8 +#define JZ_REIM_IRQ0M (1u << JZ_REIM_IRQ0M_SHIFT) +#define JZ_REIM_MIRQ1M_SHIFT 1 +#define JZ_REIM_MIRQ1M (1u << JZ_REIM_MIRQ1M_SHIFT) +#define JZ_REIM_MIRQ0M_SHIFT 0 +#define JZ_REIM_MIRQ0M (1u << JZ_REIM_MIRQ0M_SHIFT) + +#endif /* JZ4780_CPUREGS_H */ diff --git a/sys/mips/ingenic/jz4780_dme.c b/sys/mips/ingenic/jz4780_dme.c new file mode 100644 index 00000000000..a612be6a799 --- /dev/null +++ b/sys/mips/ingenic/jz4780_dme.c @@ -0,0 +1,124 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +struct jz4780_dme_softc { + device_t dev; + struct resource *res[2]; +}; + +static struct resource_spec jz4780_dme_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { -1, 0 } +}; + +static int jz4780_dme_probe(device_t dev); +static int jz4780_dme_attach(device_t dev); +static int jz4780_dme_detach(device_t dev); + +static int +jz4780_dme_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "davicom,dm9000")) + return (ENXIO); + + device_set_desc(dev, "Davicom DM9000C 10/100BaseTX"); + + return (BUS_PROBE_DEFAULT); +} + +static int +jz4780_dme_attach(device_t dev) +{ + struct jz4780_dme_softc *sc = device_get_softc(dev); + + sc->dev = dev; + + if (bus_alloc_resources(dev, jz4780_dme_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + return (0); +} + +static int +jz4780_dme_detach(device_t dev) +{ + struct jz4780_dme_softc *sc = device_get_softc(dev); + + bus_release_resources(dev, jz4780_dme_spec, sc->res); + return (0); +} + +static device_method_t jz4780_dme_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_dme_probe), + DEVMETHOD(device_attach, jz4780_dme_attach), + DEVMETHOD(device_detach, jz4780_dme_detach), + + DEVMETHOD_END +}; + +static driver_t jz4780_dme_driver = { + "dme", + jz4780_dme_methods, + sizeof(struct jz4780_dme_softc), +}; + +static devclass_t jz4780_dme_devclass; + +DRIVER_MODULE(jz4780_dme, simplebus, jz4780_dme_driver, + jz4780_dme_devclass, 0, 0); diff --git a/sys/mips/ingenic/jz4780_dwc_fdt.c b/sys/mips/ingenic/jz4780_dwc_fdt.c new file mode 100644 index 00000000000..c5af46b633e --- /dev/null +++ b/sys/mips/ingenic/jz4780_dwc_fdt.c @@ -0,0 +1,202 @@ +/* + * Copyright 2015 Alexander Kabaev . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +static device_probe_t jz4780_dwc_otg_probe; +static device_attach_t jz4780_dwc_otg_attach; +static device_detach_t jz4780_dwc_otg_detach; + +struct jz4780_dwc_otg_softc { + struct dwc_otg_fdt_softc base; /* storage for DWC OTG code */ + clk_t phy_clk; + clk_t otg_clk; +}; + +static int +jz4780_dwc_otg_clk_enable(device_t dev) +{ + struct jz4780_dwc_otg_softc *sc; + int err; + + sc = device_get_softc(dev); + + /* Configure and enable phy clock */ + err = clk_get_by_ofw_name(dev, 0, "otg_phy", &sc->phy_clk); + if (err != 0) { + device_printf(dev, "unable to lookup %s clock\n", "otg_phy"); + return (err); + } + err = clk_set_freq(sc->phy_clk, 48000000, 0); + if (err != 0) { + device_printf(dev, "unable to set %s clock to 48 kHZ\n", + "otg_phy"); + return (err); + } + err = clk_enable(sc->phy_clk); + if (err != 0) { + device_printf(dev, "unable to enable %s clock\n", "otg_phy"); + return (err); + } + + /* Configure and enable otg1 clock */ + err = clk_get_by_ofw_name(dev, 0, "otg1", &sc->otg_clk); + if (err != 0) { + device_printf(dev, "unable to lookup %s clock\n", "otg1"); + return (err); + } + err = clk_enable(sc->phy_clk); + if (err != 0) { + device_printf(dev, "unable to enable %s clock\n", "otg1"); + return (err); + } + + return (0); +} + +static int +jz4780_dwc_otg_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-otg")) + return (ENXIO); + + device_set_desc(dev, "DWC OTG 2.0 integrated USB controller (jz4780)"); + + return (BUS_PROBE_VENDOR); +} + +static int +jz4780_dwc_otg_attach(device_t dev) +{ + struct jz4780_dwc_otg_softc *sc; + struct resource *res; + int err, rid; + + sc = device_get_softc(dev); + + err = jz4780_dwc_otg_clk_enable(dev); + if (err != 0) + goto fail; + + err = jz4780_otg_enable(); + if (err != 0) { + device_printf(dev, "CGU failed to enable OTG\n"); + goto fail; + } + + /* Voodoo: Switch off VBUS overcurrent detection in OTG PHY */ + res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (res != NULL) { + uint32_t reg; + + reg = bus_read_4(res, JZ_DWC2_GUSBCFG); + reg |= 0xc; + bus_write_4(res, JZ_DWC2_GUSBCFG, reg); + bus_release_resource(dev, SYS_RES_MEMORY, rid, res); + } + + sc->base.sc_otg.sc_phy_type = DWC_OTG_PHY_UTMI; + sc->base.sc_otg.sc_phy_bits = 16; + + err = dwc_otg_attach(dev); + if (err != 0) + goto fail; + + return (0); +fail: + if (sc->otg_clk) + clk_release(sc->otg_clk); + if (sc->phy_clk) + clk_release(sc->phy_clk); + return (err); +} + +static int +jz4780_dwc_otg_detach(device_t dev) +{ + struct jz4780_dwc_otg_softc *sc; + int err; + + err = dwc_otg_detach(dev); + if (err != 0) + return (err); + + sc = device_get_softc(dev); + if (sc->otg_clk) + clk_release(sc->otg_clk); + if (sc->phy_clk) + clk_release(sc->phy_clk); + return (0); +} + +static device_method_t jz4780_dwc_otg_methods[] = { + /* bus interface */ + DEVMETHOD(device_probe, jz4780_dwc_otg_probe), + DEVMETHOD(device_attach, jz4780_dwc_otg_attach), + DEVMETHOD(device_detach, jz4780_dwc_otg_detach), + + DEVMETHOD_END +}; + +static devclass_t jz4780_dwc_otg_devclass; + +DEFINE_CLASS_1(jzotg, jz4780_dwc_otg_driver, jz4780_dwc_otg_methods, + sizeof(struct jz4780_dwc_otg_softc), dwc_otg_driver); +DRIVER_MODULE(jzotg, simplebus, jz4780_dwc_otg_driver, + jz4780_dwc_otg_devclass, 0, 0); +MODULE_DEPEND(jzotg, usb, 1, 1, 1); diff --git a/sys/mips/ingenic/jz4780_efuse.c b/sys/mips/ingenic/jz4780_efuse.c new file mode 100644 index 00000000000..4c4ca7ce02f --- /dev/null +++ b/sys/mips/ingenic/jz4780_efuse.c @@ -0,0 +1,213 @@ +/*- + * Copyright (c) 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +static struct ofw_compat_data compat_data[] = { + {"ingenic,jz4780-efuse", 1}, + {NULL, 0} +}; + +struct jz4780_efuse_data { + uint32_t serial_num; + uint32_t date; + uint8_t nanufacturer[2]; + uint8_t macaddr[6]; +} __packed; + +static struct resource_spec jz4780_efuse_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +struct jz4780_efuse_softc { + device_t dev; + struct resource *res[1]; + struct jz4780_efuse_data data; +}; + +#define CSR_WRITE_4(sc, reg, val) \ + bus_write_4((sc)->res[0], (reg), (val)) +#define CSR_READ_4(sc, reg) \ + bus_read_4((sc)->res[0], (reg)) + +#define JZ_EFUSE_BANK_SIZE (4096 / 8) /* Bank size is 4096 bits */ + +static int +jz4780_efuse_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + return (BUS_PROBE_DEFAULT); +} + +static void +jz4780_efuse_read_chunk(struct jz4780_efuse_softc *sc, int addr, uint8_t *buf, int len) +{ + uint32_t abuf; + int i, count; + + /* Setup to read proper bank */ + CSR_WRITE_4(sc, JZ_EFUCTRL, JZ_EFUSE_READ | + (addr < JZ_EFUSE_BANK_SIZE ? 0: JZ_EFUSE_BANK) | + (addr << JZ_EFUSE_ADDR_SHIFT) | + ((len - 1) << JZ_EFUSE_SIZE_SHIFT)); + /* Wait for read to complete */ + while ((CSR_READ_4(sc, JZ_EFUSTATE) & JZ_EFUSE_RD_DONE) == 0) + DELAY(1000); + + /* Round to 4 bytes for the simple loop below */ + count = len & ~3; + + for (i = 0; i < count; i += 4) { + abuf = CSR_READ_4(sc, JZ_EFUDATA0 + i); + memcpy(buf, &abuf, 4); + buf += 4; + } + + /* Read partial word and assign it byte-by-byte */ + if (i < len) { + abuf = CSR_READ_4(sc, JZ_EFUDATA0 + i); + for (/* none */; i < len; i++) { + buf[i] = abuf & 0xff; + abuf >>= 8; + } + } +} + +static void +jz4780_efuse_read(struct jz4780_efuse_softc *sc, int addr, void *buf, int len) +{ + int chunk; + + while (len > 0) { + chunk = (len > 32) ? 32 : len; + jz4780_efuse_read_chunk(sc, addr, buf, chunk); + len -= chunk; + buf = (void *)((uintptr_t)buf + chunk); + addr += chunk; + } +} + +static void +jz4780_efuse_update_kenv(struct jz4780_efuse_softc *sc) +{ + char macstr[sizeof("xx:xx:xx:xx:xx:xx")]; + + /* + * Update hint in kernel env only if none is available yet. + * It is quite possible one was set by command line already. + */ + if (kern_getenv("hint.dme.0.macaddr") == NULL) { + snprintf(macstr, sizeof(macstr), "%6D", + sc->data.macaddr, ":"); + kern_setenv("hint.dme.0.macaddr", macstr); + } +} + +static int +jz4780_efuse_attach(device_t dev) +{ + struct jz4780_efuse_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, jz4780_efuse_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + /* + * Default RD_STROBE to 4 h2clk cycles, should already be set to 4 by reset + * but configure it anyway. + */ + CSR_WRITE_4(sc, JZ_EFUCFG, 0x00040000); + + /* Read user-id segment */ + jz4780_efuse_read(sc, 0x18, &sc->data, sizeof(sc->data)); + + /* + * Set resource hints for the dme device to discover its + * MAC address, if not set already. + */ + jz4780_efuse_update_kenv(sc); + + /* Resource conflicts with NEMC, release early */ + bus_release_resources(dev, jz4780_efuse_spec, sc->res); + return (0); +} + +static int +jz4780_efuse_detach(device_t dev) +{ + + return (0); +} + +static device_method_t jz4780_efuse_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_efuse_probe), + DEVMETHOD(device_attach, jz4780_efuse_attach), + DEVMETHOD(device_detach, jz4780_efuse_detach), + + DEVMETHOD_END +}; + +static driver_t jz4780_efuse_driver = { + "efuse", + jz4780_efuse_methods, + sizeof(struct jz4780_efuse_softc), +}; + +static devclass_t jz4780_efuse_devclass; +EARLY_DRIVER_MODULE(jz4780_efuse, simplebus, jz4780_efuse_driver, + jz4780_efuse_devclass, 0, 0, BUS_PASS_TIMER); diff --git a/sys/mips/ingenic/jz4780_ehci.c b/sys/mips/ingenic/jz4780_ehci.c new file mode 100644 index 00000000000..c6cd2dc9d7b --- /dev/null +++ b/sys/mips/ingenic/jz4780_ehci.c @@ -0,0 +1,345 @@ +/*- + * Copyright (c) 2015 Oleksandr Tymoshenko . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * JZ4780 attachment driver for the USB Enhanced Host Controller. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#define EHCI_HC_DEVSTR "Ingenic JZ4780 EHCI" + +struct jz4780_ehci_softc { + ehci_softc_t base; /* storage for EHCI code */ + clk_t clk; + struct gpiobus_pin *gpio_vbus; +}; + +static device_probe_t jz4780_ehci_probe; +static device_attach_t jz4780_ehci_attach; +static device_detach_t jz4780_ehci_detach; + +static int +jz4780_ehci_vbus_gpio_enable(device_t dev) +{ + struct gpiobus_pin *gpio_vbus; + struct jz4780_ehci_softc *sc; + int err; + + sc = device_get_softc(dev); + + err = ofw_gpiobus_parse_gpios(dev, "ingenic,vbus-gpio", &gpio_vbus); + /* + * The pin can ne already mapped by other device. Optimistically + * surge ahead. + */ + if (err <= 0) + return (0); + + sc->gpio_vbus = gpio_vbus; + if (err > 1) { + device_printf(dev, "too many vbus gpios\n"); + return (ENXIO); + } + + if (sc->gpio_vbus != NULL) { + err = GPIO_PIN_SETFLAGS(sc->gpio_vbus->dev, sc->gpio_vbus->pin, + GPIO_PIN_OUTPUT); + if (err != 0) { + device_printf(dev, "Cannot configure GPIO pin %d on %s\n", + sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev)); + return (err); + } + + err = GPIO_PIN_SET(sc->gpio_vbus->dev, sc->gpio_vbus->pin, 1); + if (err != 0) { + device_printf(dev, "Cannot configure GPIO pin %d on %s\n", + sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev)); + return (err); + } + } + return (0); +} + +static int +jz4780_ehci_clk_enable(device_t dev) +{ + struct jz4780_ehci_softc *sc; + int err; + + sc = device_get_softc(dev); + + err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); + if (err != 0) { + device_printf(dev, "unable to lookup device clock\n"); + return (err); + } + err = clk_enable(sc->clk); + if (err != 0) { + device_printf(dev, "unable to enable device clock\n"); + return (err); + } + err = clk_set_freq(sc->clk, 48000000, 0); + if (err != 0) { + device_printf(dev, "unable to set device clock to 48 kHZ\n"); + return (err); + } + return (0); +} + +static void +jz4780_ehci_intr(void *arg) +{ + + ehci_interrupt(arg); +} + +static int +jz4780_ehci_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-ehci")) + return (ENXIO); + + device_set_desc(dev, EHCI_HC_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +static int +jz4780_ehci_attach(device_t dev) +{ + struct jz4780_ehci_softc *isc; + ehci_softc_t *sc; + int err; + int rid; + uint32_t reg; + + isc = device_get_softc(dev); + sc = &isc->base; + + /* initialise some bus fields */ + sc->sc_bus.parent = dev; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) { + return (ENOMEM); + } + + sc->sc_bus.usbrev = USB_REV_2_0; + + err = jz4780_ehci_vbus_gpio_enable(dev); + if (err) + goto error; + + rid = 0; + sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (!sc->sc_io_res) { + device_printf(dev, "Could not map memory\n"); + goto error; + } + + /* + * Craft special resource for bus space ops that handle + * byte-alignment of non-word addresses. + */ + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + err = jz4780_ehci_clk_enable(dev); + if (err) + goto error; + + if (jz4780_ehci_enable() != 0) { + device_printf(dev, "CGU failed to enable EHCI\n"); + err = ENXIO; + goto error; + } + + EWRITE4(sc, EHCI_USBINTR, 0); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->sc_irq_res == NULL) { + device_printf(dev, "Could not allocate irq\n"); + goto error; + } + sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(dev, "Could not add USB device\n"); + goto error; + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); + + sprintf(sc->sc_vendor, "Ingenic"); + + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, jz4780_ehci_intr, sc, &sc->sc_intr_hdl); + if (err) { + device_printf(dev, "Could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + + err = ehci_init(sc); + if (!err) { + /* Voodoo: set utmi data bus width on controller to 16 bit */ + reg = EREAD4(sc, JZ_EHCI_REG_UTMI_BUS); + reg |= UTMI_BUS_WIDTH; + EWRITE4(sc, JZ_EHCI_REG_UTMI_BUS, reg); + + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(dev, "USB init failed err=%d\n", err); + goto error; + } + return (0); + +error: + jz4780_ehci_detach(dev); + return (ENXIO); +} + +static int +jz4780_ehci_detach(device_t dev) +{ + struct jz4780_ehci_softc *isc; + ehci_softc_t *sc; + device_t bdev; + int err; + + isc = device_get_softc(dev); + sc = &isc->base; + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_children(dev); + + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ehci_detach() after ehci_init() + */ + ehci_detach(sc); + + err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); + + if (err) + /* XXX or should we panic? */ + device_printf(dev, "Could not tear down irq, %d\n", + err); + sc->sc_intr_hdl = NULL; + } + + if (sc->sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + + if (isc->clk) + clk_release(isc->clk); + + usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + free(isc->gpio_vbus, M_DEVBUF); + return (0); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_ehci_probe), + DEVMETHOD(device_attach, jz4780_ehci_attach), + DEVMETHOD(device_detach, jz4780_ehci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +static driver_t ehci_driver = { + .name = "ehci", + .methods = ehci_methods, + .size = sizeof(struct jz4780_ehci_softc), +}; + +static devclass_t ehci_devclass; + +DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1); +MODULE_DEPEND(ehci, gpio, 1, 1, 1); diff --git a/sys/mips/ingenic/jz4780_gpio.c b/sys/mips/ingenic/jz4780_gpio.c new file mode 100644 index 00000000000..aa45e31c3bb --- /dev/null +++ b/sys/mips/ingenic/jz4780_gpio.c @@ -0,0 +1,830 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "jz4780_gpio_if.h" +#include "gpio_if.h" +#include "pic_if.h" + +#define JZ4780_GPIO_PINS 32 + +enum pin_function { + JZ_FUNC_DEV_0, + JZ_FUNC_DEV_1, + JZ_FUNC_DEV_2, + JZ_FUNC_DEV_3, + JZ_FUNC_GPIO, + JZ_FUNC_INTR, +}; + +struct jz4780_gpio_pin { + struct intr_irqsrc pin_irqsrc; + enum intr_trigger intr_trigger; + enum intr_polarity intr_polarity; + enum pin_function pin_func; + uint32_t pin_caps; + uint32_t pin_flags; + uint32_t pin_num; + char pin_name[GPIOMAXNAME]; +}; + +struct jz4780_gpio_softc { + device_t dev; + device_t busdev; + struct resource *res[2]; + struct mtx mtx; + struct jz4780_gpio_pin pins[JZ4780_GPIO_PINS]; + void *intrhand; +}; + +static struct resource_spec jz4780_gpio_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int jz4780_gpio_probe(device_t dev); +static int jz4780_gpio_attach(device_t dev); +static int jz4780_gpio_detach(device_t dev); +static int jz4780_gpio_intr(void *arg); + +#define JZ4780_GPIO_LOCK(sc) mtx_lock_spin(&(sc)->mtx) +#define JZ4780_GPIO_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx) +#define JZ4780_GPIO_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "jz4780_gpio", MTX_SPIN) +#define JZ4780_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg)) + +static int +jz4780_gpio_probe(device_t dev) +{ + phandle_t node; + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + /* We only like particular parent */ + if (!ofw_bus_is_compatible(device_get_parent(dev), + "ingenic,jz4780-pinctrl")) + return (ENXIO); + + /* ... and only specific children os that parent */ + node = ofw_bus_get_node(dev); + if (!OF_hasprop(node, "gpio-controller")) + return (ENXIO); + + device_set_desc(dev, "Ingenic JZ4780 GPIO Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +jz4780_gpio_pin_set_func(struct jz4780_gpio_softc *sc, uint32_t pin, + uint32_t func) +{ + uint32_t mask = (1u << pin); + + if (func > (uint32_t)JZ_FUNC_DEV_3) + return (EINVAL); + + CSR_WRITE_4(sc, JZ_GPIO_INTC, mask); + CSR_WRITE_4(sc, JZ_GPIO_MASKC, mask); + if (func & 2) + CSR_WRITE_4(sc, JZ_GPIO_PAT1S, mask); + else + CSR_WRITE_4(sc, JZ_GPIO_PAT1C, mask); + if (func & 1) + CSR_WRITE_4(sc, JZ_GPIO_PAT0S, mask); + else + CSR_WRITE_4(sc, JZ_GPIO_PAT0C, mask); + + sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); + sc->pins[pin].pin_func = (enum pin_function)func; + return (0); +} + +static int +jz4780_gpio_pin_set_direction(struct jz4780_gpio_softc *sc, + uint32_t pin, uint32_t dir) +{ + uint32_t mask = (1u << pin); + + switch (dir) { + case GPIO_PIN_OUTPUT: + if (sc->pins[pin].pin_caps & dir) + CSR_WRITE_4(sc, JZ_GPIO_PAT1C, mask); + else + return (EINVAL); + break; + case GPIO_PIN_INPUT: + if (sc->pins[pin].pin_caps & dir) + CSR_WRITE_4(sc, JZ_GPIO_PAT1S, mask); + else + return (EINVAL); + break; + } + + sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); + sc->pins[pin].pin_flags |= dir; + return (0); +} + +static int +jz4780_gpio_pin_set_bias(struct jz4780_gpio_softc *sc, + uint32_t pin, uint32_t bias) +{ + uint32_t mask = (1u << pin); + + switch (bias) { + case GPIO_PIN_PULLUP: + case GPIO_PIN_PULLDOWN: + if (sc->pins[pin].pin_caps & bias) + CSR_WRITE_4(sc, JZ_GPIO_DPULLC, mask); + else + return (EINVAL); + break; + case 0: + CSR_WRITE_4(sc, JZ_GPIO_DPULLS, mask); + break; + default: + return (ENOTSUP); + } + + sc->pins[pin].pin_flags &= ~(GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN); + sc->pins[pin].pin_flags |= bias; + return (0); +} + +/* + * Decode pin configuration using this map + */ +#if 0 +INT MASK PAT1 PAT0 +1 x 0 0 /* intr, level, low */ +1 x 0 1 /* intr, level, high */ +1 x 1 0 /* intr, edge, falling */ +1 x 1 1 /* intr, edge, rising */ +0 0 0 0 /* function, func 0 */ +0 0 0 1 /* function, func 1 */ +0 0 1 0 /* function, func 2 */ +0 0 1 0 /* function, func 3 */ +0 1 0 0 /* gpio, output 0 */ +0 1 0 1 /* gpio, output 1 */ +0 1 1 x /* gpio, input */ +#endif + +static void +jz4780_gpio_pin_probe(struct jz4780_gpio_softc *sc, uint32_t pin) +{ + uint32_t mask = (1u << pin); + uint32_t val; + + /* Clear cached gpio config */ + sc->pins[pin].pin_flags = 0; + + /* First check if pin is in interrupt mode */ + val = CSR_READ_4(sc, JZ_GPIO_INT); + if (val & mask) { + /* Pin is in interrupt mode, decode interrupt triggering mode */ + val = CSR_READ_4(sc, JZ_GPIO_PAT1); + if (val & mask) + sc->pins[pin].intr_trigger = INTR_TRIGGER_EDGE; + else + sc->pins[pin].intr_trigger = INTR_TRIGGER_LEVEL; + /* Decode interrupt polarity */ + val = CSR_READ_4(sc, JZ_GPIO_PAT0); + if (val & mask) + sc->pins[pin].intr_polarity = INTR_POLARITY_HIGH; + else + sc->pins[pin].intr_polarity = INTR_POLARITY_LOW; + + sc->pins[pin].pin_func = JZ_FUNC_INTR; + sc->pins[pin].pin_flags = 0; + return; + } + /* Next check if pin is in gpio mode */ + val = CSR_READ_4(sc, JZ_GPIO_MASK); + if (val & mask) { + /* Pin is in gpio mode, decode direction and bias */ + val = CSR_READ_4(sc, JZ_GPIO_PAT1); + if (val & mask) + sc->pins[pin].pin_flags |= GPIO_PIN_INPUT; + else + sc->pins[pin].pin_flags |= GPIO_PIN_OUTPUT; + /* Check for bias */ + val = CSR_READ_4(sc, JZ_GPIO_DPULL); + if ((val & mask) == 0) + sc->pins[pin].pin_flags |= sc->pins[pin].pin_caps & + (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN); + sc->pins[pin].pin_func = JZ_FUNC_GPIO; + return; + } + /* By exclusion, pin is in alternate function mode */ + val = CSR_READ_4(sc, JZ_GPIO_DPULL); + if ((val & mask) == 0) + sc->pins[pin].pin_flags = sc->pins[pin].pin_caps & + (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN); + val = ((CSR_READ_4(sc, JZ_GPIO_PAT1) & mask) >> pin) << 1; + val = val | ((CSR_READ_4(sc, JZ_GPIO_PAT1) & mask) >> pin); + sc->pins[pin].pin_func = (enum pin_function)val; +} + +static int +jz4780_gpio_register_isrcs(struct jz4780_gpio_softc *sc) +{ + int error; + uint32_t irq, i; + struct intr_irqsrc *isrc; + const char *name; + + name = device_get_nameunit(sc->dev); + for (irq = 0; irq < JZ4780_GPIO_PINS; irq++) { + isrc = &sc->pins[irq].pin_irqsrc; + error = intr_isrc_register(isrc, sc->dev, 0, "%s,%d", + name, irq); + if (error != 0) { + for (i = 0; i < irq; i++) + intr_isrc_deregister(&sc->pins[i].pin_irqsrc); + device_printf(sc->dev, "%s failed", __func__); + return (error); + } + } + + return (0); +} + +static int +jz4780_gpio_attach(device_t dev) +{ + struct jz4780_gpio_softc *sc = device_get_softc(dev); + phandle_t node; + uint32_t i, pd_pins, pu_pins; + + sc->dev = dev; + + if (bus_alloc_resources(dev, jz4780_gpio_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + JZ4780_GPIO_LOCK_INIT(sc); + + node = ofw_bus_get_node(dev); + OF_getencprop(node, "ingenic,pull-ups", &pu_pins, sizeof(pu_pins)); + OF_getencprop(node, "ingenic,pull-downs", &pd_pins, sizeof(pd_pins)); + + for (i = 0; i < JZ4780_GPIO_PINS; i++) { + sc->pins[i].pin_num = i; + sc->pins[i].pin_caps |= GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; + if (pu_pins & (1 << i)) + sc->pins[i].pin_caps |= GPIO_PIN_PULLUP; + if (pd_pins & (1 << i)) + sc->pins[i].pin_caps |= GPIO_PIN_PULLDOWN; + sc->pins[i].intr_polarity = INTR_POLARITY_CONFORM; + sc->pins[i].intr_trigger = INTR_TRIGGER_CONFORM; + + snprintf(sc->pins[i].pin_name, GPIOMAXNAME - 1, "gpio%c%d", + device_get_unit(dev) + 'a', i); + sc->pins[i].pin_name[GPIOMAXNAME - 1] = '\0'; + + jz4780_gpio_pin_probe(sc, i); + } + + if (jz4780_gpio_register_isrcs(sc) != 0) + goto fail; + + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) { + device_printf(dev, "could not register PIC\n"); + goto fail; + } + + if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, + jz4780_gpio_intr, NULL, sc, &sc->intrhand) != 0) + goto fail_pic; + + sc->busdev = gpiobus_attach_bus(dev); + if (sc->busdev == NULL) + goto fail_pic; + + return (0); +fail_pic: + intr_pic_deregister(dev, OF_xref_from_node(node)); +fail: + if (sc->intrhand != NULL) + bus_teardown_intr(dev, sc->res[1], sc->intrhand); + bus_release_resources(dev, jz4780_gpio_spec, sc->res); + JZ4780_GPIO_LOCK_DESTROY(sc); + return (ENXIO); +} + +static int +jz4780_gpio_detach(device_t dev) +{ + struct jz4780_gpio_softc *sc = device_get_softc(dev); + + bus_release_resources(dev, jz4780_gpio_spec, sc->res); + JZ4780_GPIO_LOCK_DESTROY(sc); + return (0); +} + +static int +jz4780_gpio_configure_pin(device_t dev, uint32_t pin, uint32_t func, + uint32_t flags) +{ + struct jz4780_gpio_softc *sc; + int retval; + + if (pin >= JZ4780_GPIO_PINS) + return (EINVAL); + + sc = device_get_softc(dev); + JZ4780_GPIO_LOCK(sc); + retval = jz4780_gpio_pin_set_func(sc, pin, func); + if (retval == 0) + retval = jz4780_gpio_pin_set_bias(sc, pin, flags); + JZ4780_GPIO_UNLOCK(sc); + return (retval); +} + +static device_t +jz4780_gpio_get_bus(device_t dev) +{ + struct jz4780_gpio_softc *sc; + + sc = device_get_softc(dev); + + return (sc->busdev); +} + +static int +jz4780_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = JZ4780_GPIO_PINS - 1; + return (0); +} + +static int +jz4780_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct jz4780_gpio_softc *sc; + + if (pin >= JZ4780_GPIO_PINS) + return (EINVAL); + + sc = device_get_softc(dev); + JZ4780_GPIO_LOCK(sc); + *caps = sc->pins[pin].pin_caps; + JZ4780_GPIO_UNLOCK(sc); + + return (0); +} + +static int +jz4780_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct jz4780_gpio_softc *sc; + + if (pin >= JZ4780_GPIO_PINS) + return (EINVAL); + + sc = device_get_softc(dev); + JZ4780_GPIO_LOCK(sc); + *flags = sc->pins[pin].pin_flags; + JZ4780_GPIO_UNLOCK(sc); + + return (0); +} + +static int +jz4780_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct jz4780_gpio_softc *sc; + + if (pin >= JZ4780_GPIO_PINS) + return (EINVAL); + + sc = device_get_softc(dev); + strncpy(name, sc->pins[pin].pin_name, GPIOMAXNAME - 1); + name[GPIOMAXNAME - 1] = '\0'; + + return (0); +} + +static int +jz4780_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct jz4780_gpio_softc *sc; + int retval; + + if (pin >= JZ4780_GPIO_PINS) + return (EINVAL); + + sc = device_get_softc(dev); + JZ4780_GPIO_LOCK(sc); + retval = jz4780_gpio_pin_set_direction(sc, pin, + flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)); + if (retval == 0) + retval = jz4780_gpio_pin_set_bias(sc, pin, + flags & (GPIO_PIN_PULLDOWN | GPIO_PIN_PULLUP)); + JZ4780_GPIO_UNLOCK(sc); + + return (retval); +} + +static int +jz4780_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct jz4780_gpio_softc *sc; + uint32_t mask; + int retval; + + if (pin >= JZ4780_GPIO_PINS) + return (EINVAL); + + retval = EINVAL; + mask = (1u << pin); + sc = device_get_softc(dev); + JZ4780_GPIO_LOCK(sc); + if (sc->pins[pin].pin_func == JZ_FUNC_GPIO) { + CSR_WRITE_4(sc, value ? JZ_GPIO_PAT0S : JZ_GPIO_PAT0C, mask); + retval = 0; + } + JZ4780_GPIO_UNLOCK(sc); + + return (retval); +} + +static int +jz4780_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct jz4780_gpio_softc *sc; + uint32_t data, mask; + + if (pin >= JZ4780_GPIO_PINS) + return (EINVAL); + + mask = (1u << pin); + sc = device_get_softc(dev); + JZ4780_GPIO_LOCK(sc); + data = CSR_READ_4(sc, JZ_GPIO_PIN); + JZ4780_GPIO_UNLOCK(sc); + *val = (data & mask) ? 1 : 0; + + return (0); +} + +static int +jz4780_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct jz4780_gpio_softc *sc; + uint32_t data, mask; + int retval; + + if (pin >= JZ4780_GPIO_PINS) + return (EINVAL); + + retval = EINVAL; + mask = (1u << pin); + sc = device_get_softc(dev); + JZ4780_GPIO_LOCK(sc); + if (sc->pins[pin].pin_func == JZ_FUNC_GPIO && + sc->pins[pin].pin_flags & GPIO_PIN_OUTPUT) { + data = CSR_READ_4(sc, JZ_GPIO_PIN); + CSR_WRITE_4(sc, (data & mask) ? JZ_GPIO_PAT0C : JZ_GPIO_PAT0S, + mask); + retval = 0; + } + JZ4780_GPIO_UNLOCK(sc); + + return (retval); +} + +#ifdef FDT +static int +jz_gpio_map_intr_fdt(device_t dev, struct intr_map_data *data, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) +{ + struct jz4780_gpio_softc *sc; + struct intr_map_data_fdt *daf; + + sc = device_get_softc(dev); + daf = (struct intr_map_data_fdt *)data; + + if (data == NULL || data->type != INTR_MAP_DATA_FDT || + daf->ncells == 0 || daf->ncells > 2) + return (EINVAL); + + *irqp = daf->cells[0]; + if (daf->ncells == 1) { + *trigp = INTR_TRIGGER_CONFORM; + *polp = INTR_POLARITY_CONFORM; + return (0); + } + + switch (daf->cells[1]) + { + case IRQ_TYPE_EDGE_RISING: + *trigp = INTR_TRIGGER_EDGE; + *polp = INTR_POLARITY_HIGH; + break; + case IRQ_TYPE_EDGE_FALLING: + *trigp = INTR_TRIGGER_EDGE; + *polp = INTR_POLARITY_LOW; + break; + case IRQ_TYPE_LEVEL_HIGH: + *trigp = INTR_TRIGGER_LEVEL; + *polp = INTR_POLARITY_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + *trigp = INTR_TRIGGER_LEVEL; + *polp = INTR_POLARITY_LOW; + break; + default: + device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n", + daf->cells[1]); + return (ENOTSUP); + } + + return (0); +} +#endif + +static int +jz_gpio_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) +{ + struct jz4780_gpio_softc *sc; + enum intr_polarity pol; + enum intr_trigger trig; + u_int irq; + + sc = device_get_softc(dev); + switch (data->type) { +#ifdef FDT + case INTR_MAP_DATA_FDT: + if (jz_gpio_map_intr_fdt(dev, data, &irq, &pol, &trig) != 0) + return (EINVAL); + break; +#endif + default: + return (EINVAL); + } + + if (irq >= nitems(sc->pins)) + return (EINVAL); + + *irqp = irq; + if (polp != NULL) + *polp = pol; + if (trigp != NULL) + *trigp = trig; + return (0); +} + +static int +jz4780_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct jz4780_gpio_softc *sc; + int retval; + u_int irq; + + retval = jz_gpio_map_intr(dev, data, &irq, NULL, NULL); + if (retval == 0) { + sc = device_get_softc(dev); + *isrcp = &sc->pins[irq].pin_irqsrc; + } + return (retval); +} + +static int +jz4780_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct jz4780_gpio_softc *sc; + struct jz4780_gpio_pin *pin; + enum intr_polarity pol; + enum intr_trigger trig; + uint32_t mask, irq; + + if (data == NULL) + return (ENOTSUP); + + /* Get config for resource. */ + if (jz_gpio_map_intr(dev, data, &irq, &pol, &trig)) + return (EINVAL); + + pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc); + if (isrc != &pin->pin_irqsrc) + return (EINVAL); + + /* Compare config if this is not first setup. */ + if (isrc->isrc_handlers != 0) { + if ((pol != INTR_POLARITY_CONFORM && pol != pin->intr_polarity) || + (trig != INTR_TRIGGER_CONFORM && trig != pin->intr_trigger)) + return (EINVAL); + else + return (0); + } + + if (pol == INTR_POLARITY_CONFORM) + pol = INTR_POLARITY_LOW; /* just pick some */ + if (trig == INTR_TRIGGER_CONFORM) + trig = INTR_TRIGGER_EDGE; /* just pick some */ + + sc = device_get_softc(dev); + mask = 1u << pin->pin_num; + + JZ4780_GPIO_LOCK(sc); + CSR_WRITE_4(sc, JZ_GPIO_MASKS, mask); + CSR_WRITE_4(sc, JZ_GPIO_INTS, mask); + + if (trig == INTR_TRIGGER_LEVEL) + CSR_WRITE_4(sc, JZ_GPIO_PAT1C, mask); + else + CSR_WRITE_4(sc, JZ_GPIO_PAT1S, mask); + + if (pol == INTR_POLARITY_LOW) + CSR_WRITE_4(sc, JZ_GPIO_PAT0C, mask); + else + CSR_WRITE_4(sc, JZ_GPIO_PAT0S, mask); + + pin->pin_func = JZ_FUNC_INTR; + pin->intr_trigger = trig; + pin->intr_polarity = pol; + + CSR_WRITE_4(sc, JZ_GPIO_FLAGC, mask); + CSR_WRITE_4(sc, JZ_GPIO_MASKC, mask); + JZ4780_GPIO_UNLOCK(sc); + return (0); +} + +static void +jz4780_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct jz4780_gpio_softc *sc; + struct jz4780_gpio_pin *pin; + + sc = device_get_softc(dev); + pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc); + + CSR_WRITE_4(sc, JZ_GPIO_MASKC, 1u << pin->pin_num); +} + +static void +jz4780_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct jz4780_gpio_softc *sc; + struct jz4780_gpio_pin *pin; + + sc = device_get_softc(dev); + pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc); + + CSR_WRITE_4(sc, JZ_GPIO_MASKS, 1u << pin->pin_num); +} + +static void +jz4780_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + jz4780_gpio_pic_disable_intr(dev, isrc); +} + +static void +jz4780_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + jz4780_gpio_pic_enable_intr(dev, isrc); +} + +static void +jz4780_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct jz4780_gpio_softc *sc; + struct jz4780_gpio_pin *pin; + + sc = device_get_softc(dev); + pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc); + + CSR_WRITE_4(sc, JZ_GPIO_FLAGC, 1u << pin->pin_num); +} + +static int +jz4780_gpio_intr(void *arg) +{ + struct jz4780_gpio_softc *sc; + uint32_t i, interrupts; + + sc = arg; + interrupts = CSR_READ_4(sc, JZ_GPIO_FLAG); + + for (i = 0; interrupts != 0; i++, interrupts >>= 1) { + if ((interrupts & 0x1) == 0) + continue; + if (intr_isrc_dispatch(&sc->pins[i].pin_irqsrc, + curthread->td_intr_frame) != 0) { + device_printf(sc->dev, "spurious interrupt %d\n", i); + PIC_DISABLE_INTR(sc->dev, &sc->pins[i].pin_irqsrc); + } + } + + return (FILTER_HANDLED); +} + +static device_method_t jz4780_gpio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_gpio_probe), + DEVMETHOD(device_attach, jz4780_gpio_attach), + DEVMETHOD(device_detach, jz4780_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, jz4780_gpio_get_bus), + DEVMETHOD(gpio_pin_max, jz4780_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, jz4780_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, jz4780_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, jz4780_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, jz4780_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, jz4780_gpio_pin_get), + DEVMETHOD(gpio_pin_set, jz4780_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, jz4780_gpio_pin_toggle), + + /* Custom interface to set pin function */ + DEVMETHOD(jz4780_gpio_configure_pin, jz4780_gpio_configure_pin), + + /* Interrupt controller interface */ + DEVMETHOD(pic_setup_intr, jz4780_gpio_pic_setup_intr), + DEVMETHOD(pic_enable_intr, jz4780_gpio_pic_enable_intr), + DEVMETHOD(pic_disable_intr, jz4780_gpio_pic_disable_intr), + DEVMETHOD(pic_map_intr, jz4780_gpio_pic_map_intr), + DEVMETHOD(pic_post_filter, jz4780_gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, jz4780_gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, jz4780_gpio_pic_pre_ithread), + + DEVMETHOD_END +}; + +static driver_t jz4780_gpio_driver = { + "gpio", + jz4780_gpio_methods, + sizeof(struct jz4780_gpio_softc), +}; + +static devclass_t jz4780_gpio_devclass; + +EARLY_DRIVER_MODULE(jz4780_gpio, simplebus, jz4780_gpio_driver, + jz4780_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); diff --git a/sys/mips/ingenic/jz4780_gpio_if.m b/sys/mips/ingenic/jz4780_gpio_if.m new file mode 100644 index 00000000000..2bbe7ee0492 --- /dev/null +++ b/sys/mips/ingenic/jz4780_gpio_if.m @@ -0,0 +1,41 @@ +#- +# Copyright (c) 2015 Alexander Kabaev +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include + +INTERFACE jz4780_gpio; + +/** + * Configures pin as specified by FDT pinctrl entry + */ +METHOD int configure_pin { + device_t dev; + uint32_t gpio; + uint32_t func; + uint32_t flags; +}; diff --git a/sys/mips/ingenic/jz4780_intr.c b/sys/mips/ingenic/jz4780_intr.c new file mode 100644 index 00000000000..c28f5f90c16 --- /dev/null +++ b/sys/mips/ingenic/jz4780_intr.c @@ -0,0 +1,333 @@ +/*- + * Copyright (c) 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "opt_platform.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "pic_if.h" + +#define JZ4780_NIRQS 64 + +static int jz4780_pic_intr(void *); + +struct jz4780_pic_isrc { + struct intr_irqsrc isrc; + u_int irq; +}; + +struct jz4780_pic_softc { + device_t pic_dev; + void * pic_intrhand; + struct resource * pic_res[2]; + struct jz4780_pic_isrc pic_irqs[JZ4780_NIRQS]; + uint32_t nirqs; +}; + +#define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc) + +static struct resource_spec jz4780_pic_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Registers */ + { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Parent interrupt */ + { -1, 0 } +}; + +static struct ofw_compat_data compat_data[] = { + {"ingenic,jz4780-intc", true}, + {NULL, false} +}; + +#define READ4(_sc, _reg) bus_read_4((_sc)->pic_res[0], _reg) +#define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->pic_res[0], _reg, _val) + +static int +jz4780_pic_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + device_set_desc(dev, "JZ4780 Interrupt Controller"); + return (BUS_PROBE_DEFAULT); +} + +static inline void +pic_irq_unmask(struct jz4780_pic_softc *sc, u_int irq) +{ + if (irq < 32) + WRITE4(sc, JZ_ICMCR0, (1u << irq)); + else + WRITE4(sc, JZ_ICMCR1, (1u << (irq - 32))); +} + +static inline void +pic_irq_mask(struct jz4780_pic_softc *sc, u_int irq) +{ + if (irq < 32) + WRITE4(sc, JZ_ICMSR0, (1u << irq)); + else + WRITE4(sc, JZ_ICMSR1, (1u << (irq - 32))); +} + +static inline intptr_t +pic_xref(device_t dev) +{ + return (OF_xref_from_node(ofw_bus_get_node(dev))); +} + +static int +jz4780_pic_register_isrcs(struct jz4780_pic_softc *sc) +{ + int error; + uint32_t irq, i; + struct intr_irqsrc *isrc; + const char *name; + + name = device_get_nameunit(sc->pic_dev); + for (irq = 0; irq < sc->nirqs; irq++) { + sc->pic_irqs[irq].irq = irq; + isrc = PIC_INTR_ISRC(sc, irq); + error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s,%d", + name, irq); + if (error != 0) { + for (i = 0; i < irq; i++) + intr_isrc_deregister(PIC_INTR_ISRC(sc, irq)); + device_printf(sc->pic_dev, "%s failed", __func__); + return (error); + } + } + + return (0); +} + +static int +jz4780_pic_attach(device_t dev) +{ + struct jz4780_pic_softc *sc; + intptr_t xref; + + xref = pic_xref(dev); + + sc = device_get_softc(dev); + + if (bus_alloc_resources(dev, jz4780_pic_spec, sc->pic_res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + sc->pic_dev = dev; + + /* Set the number of interrupts */ + sc->nirqs = nitems(sc->pic_irqs); + + /* Mask all interrupts */ + WRITE4(sc, JZ_ICMR0, 0xFFFFFFFF); + WRITE4(sc, JZ_ICMR1, 0xFFFFFFFF); + + /* Register the interrupts */ + if (jz4780_pic_register_isrcs(sc) != 0) { + device_printf(dev, "could not register PIC ISRCs\n"); + goto cleanup; + } + + /* + * Now, when everything is initialized, it's right time to + * register interrupt controller to interrupt framefork. + */ + if (intr_pic_register(dev, xref) == NULL) { + device_printf(dev, "could not register PIC\n"); + goto cleanup; + } + + if (bus_setup_intr(dev, sc->pic_res[1], INTR_TYPE_CLK, + jz4780_pic_intr, NULL, sc, &sc->pic_intrhand)) { + device_printf(dev, "could not setup irq handler\n"); + intr_pic_deregister(dev, xref); + goto cleanup; + } + + return (0); + +cleanup: + bus_release_resources(dev, jz4780_pic_spec, sc->pic_res); + + return(ENXIO); +} + +static int +jz4780_pic_intr(void *arg) +{ + struct jz4780_pic_softc *sc = arg; + struct intr_irqsrc *isrc; + struct thread *td; + uint32_t i, intr; + + td = curthread; + /* Workaround: do not inflate intr nesting level */ + td->td_intr_nesting_level--; + + intr = READ4(sc, JZ_ICPR0); + while ((i = fls(intr)) != 0) { + i--; + intr &= ~(1u << i); + + isrc = PIC_INTR_ISRC(sc, i); + if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) { + device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i); + pic_irq_mask(sc, i); + continue; + } + } + + KASSERT(i == 0, ("all interrupts handled")); + + intr = READ4(sc, JZ_ICPR1); + while ((i = fls(intr)) != 0) { + i--; + intr &= ~(1u << i); + i += 32; + + isrc = PIC_INTR_ISRC(sc, i); + if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) { + device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i); + pic_irq_mask(sc, i); + continue; + } + } + + KASSERT(i == 0, ("all interrupts handled")); + td->td_intr_nesting_level++; + + return (FILTER_HANDLED); +} + +static int +jz4780_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ +#ifdef FDT + struct jz4780_pic_softc *sc; + struct intr_map_data_fdt *daf; + + sc = device_get_softc(dev); + daf = (struct intr_map_data_fdt *)data; + + if (data == NULL || data->type != INTR_MAP_DATA_FDT || + daf->ncells != 1 || daf->cells[0] >= sc->nirqs) + return (EINVAL); + + *isrcp = PIC_INTR_ISRC(sc, daf->cells[0]); + return (0); +#else + return (EINVAL); +#endif +} + +static void +jz4780_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct jz4780_pic_isrc *pic_isrc; + + pic_isrc = (struct jz4780_pic_isrc *)isrc; + pic_irq_unmask(device_get_softc(dev), pic_isrc->irq); +} + +static void +jz4780_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct jz4780_pic_isrc *pic_isrc; + + pic_isrc = (struct jz4780_pic_isrc *)isrc; + pic_irq_mask(device_get_softc(dev), pic_isrc->irq); +} + +static void +jz4780_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + jz4780_pic_disable_intr(dev, isrc); +} + +static void +jz4780_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + + jz4780_pic_enable_intr(dev, isrc); +} + +static device_method_t jz4780_pic_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_pic_probe), + DEVMETHOD(device_attach, jz4780_pic_attach), + /* Interrupt controller interface */ + DEVMETHOD(pic_enable_intr, jz4780_pic_enable_intr), + DEVMETHOD(pic_disable_intr, jz4780_pic_disable_intr), + DEVMETHOD(pic_map_intr, jz4780_pic_map_intr), + DEVMETHOD(pic_post_ithread, jz4780_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, jz4780_pic_pre_ithread), + { 0, 0 } +}; + +static driver_t jz4780_pic_driver = { + "intc", + jz4780_pic_methods, + sizeof(struct jz4780_pic_softc), +}; + +static devclass_t jz4780_pic_devclass; + +EARLY_DRIVER_MODULE(intc, ofwbus, jz4780_pic_driver, jz4780_pic_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/mips/ingenic/jz4780_machdep.c b/sys/mips/ingenic/jz4780_machdep.c new file mode 100644 index 00000000000..b125b53acab --- /dev/null +++ b/sys/mips/ingenic/jz4780_machdep.c @@ -0,0 +1,296 @@ +/*- + * Copyright (c) 2009 Oleksandr Tymoshenko + * Copyright (c) 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_ddb.h" +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef FDT +#include +#include +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +uint32_t * const led = (uint32_t *)0xb0010548; + +extern char edata[], end[]; +static char boot1_env[4096]; + +void +platform_cpu_init(void) +{ + uint32_t reg; + + /* + * Do not expect mbox interrups while writing + * mbox + */ + reg = mips_rd_xburst_reim(); + reg &= ~JZ_REIM_MIRQ0M; + mips_wr_xburst_reim(reg); + + /* Clean mailboxes */ + mips_wr_xburst_mbox0(0); + mips_wr_xburst_mbox1(0); + mips_wr_xburst_core_sts(~JZ_CORESTS_MIRQ0P); + + /* Unmask mbox interrupts */ + reg |= JZ_REIM_MIRQ0M; + mips_wr_xburst_reim(reg); +} + +void +platform_reset(void) +{ + /* + * For now, provoke a watchdog reset in about a second, so UART buffers + * have a fighting chance to flush before we pull the plug + */ + writereg(JZ_TCU_BASE + JZ_WDOG_TCER, 0); /* disable watchdog */ + writereg(JZ_TCU_BASE + JZ_WDOG_TCNT, 0); /* reset counter */ + writereg(JZ_TCU_BASE + JZ_WDOG_TDR, 128); /* wait for ~1s */ + writereg(JZ_TCU_BASE + JZ_WDOG_TCSR, TCSR_RTC_EN | TCSR_DIV_256); + writereg(JZ_TCU_BASE + JZ_WDOG_TCER, TCER_ENABLE); /* fire! */ + + /* Wait for reset */ + while (1) + ; +} + +static void +mips_init(void) +{ + int i; +#ifdef FDT + struct mem_region mr[FDT_MEM_REGIONS]; + uint64_t val; + int mr_cnt; + int j; +#endif + + for (i = 0; i < 10; i++) { + phys_avail[i] = 0; + } + + /* The minimal amount of memory Ingenic SoC can have. */ + dump_avail[0] = phys_avail[0] = MIPS_KSEG0_TO_PHYS(kernel_kseg0_end); + physmem = realmem = btoc(32 * 1024 * 1024); + + /* + * X1000 mips cpu special. + * TODO: do anyone know what is this ? + */ + __asm( + "li $2, 0xa9000000 \n\t" + "mtc0 $2, $5, 4 \n\t" + "nop \n\t" + ::"r"(2)); + +#ifdef FDT + if (fdt_get_mem_regions(mr, &mr_cnt, &val) == 0) { + + physmem = realmem = btoc(val); + + KASSERT((phys_avail[0] >= mr[0].mr_start) && \ + (phys_avail[0] < (mr[0].mr_start + mr[0].mr_size)), + ("First region is not within FDT memory range")); + + /* Limit size of the first region */ + phys_avail[1] = (mr[0].mr_start + MIN(mr[0].mr_size, ctob(realmem))); + dump_avail[1] = phys_avail[1]; + + /* Add the rest of regions */ + for (i = 1, j = 2; i < mr_cnt; i++, j+=2) { + phys_avail[j] = mr[i].mr_start; + phys_avail[j+1] = (mr[i].mr_start + mr[i].mr_size); + dump_avail[j] = phys_avail[j]; + dump_avail[j+1] = phys_avail[j+1]; + } + } +#endif + + init_param1(); + init_param2(physmem); + mips_cpu_init(); + pmap_bootstrap(); + mips_proc0_init(); + mutex_init(); + kdb_init(); + led[0] = 0x8000; +#ifdef KDB + if (boothowto & RB_KDB) + kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger"); +#endif +} + +static void +_parse_bootarg(char *v) +{ + char *n; + + if (*v == '-') { + while (*v != '\0') { + v++; + switch (*v) { + case 'a': boothowto |= RB_ASKNAME; break; + /* Someone should simulate that ;-) */ + case 'C': boothowto |= RB_CDROM; break; + case 'd': boothowto |= RB_KDB; break; + case 'D': boothowto |= RB_MULTIPLE; break; + case 'm': boothowto |= RB_MUTE; break; + case 'g': boothowto |= RB_GDB; break; + case 'h': boothowto |= RB_SERIAL; break; + case 'p': boothowto |= RB_PAUSE; break; + case 'r': boothowto |= RB_DFLTROOT; break; + case 's': boothowto |= RB_SINGLE; break; + case 'v': boothowto |= RB_VERBOSE; break; + } + } + } else { + n = strsep(&v, "="); + if (v == NULL) + kern_setenv(n, "1"); + else + kern_setenv(n, v); + } +} + +static void +_parse_cmdline(int argc, char *argv[]) +{ + int i; + + for (i = 1; i < argc; i++) + _parse_bootarg(argv[i]); +} + +#ifdef FDT +/* Parse cmd line args as env - copied from xlp_machdep. */ +/* XXX-BZ this should really be centrally provided for all (boot) code. */ +static void +_parse_bootargs(char *cmdline) +{ + char *v; + + while ((v = strsep(&cmdline, " \n")) != NULL) { + if (*v == '\0') + continue; + _parse_bootarg(v); + } +} +#endif + +void +platform_start(__register_t a0, __register_t a1, + __register_t a2 __unused, __register_t a3 __unused) +{ + char **argv; + int argc; + vm_offset_t kernend; +#ifdef FDT + vm_offset_t dtbp; + phandle_t chosen; + char buf[2048]; /* early stack supposedly big enough */ +#endif + /* + * clear the BSS and SBSS segments, this should be first call in + * the function + */ + kernend = (vm_offset_t)&end; + memset(&edata, 0, kernend - (vm_offset_t)(&edata)); + + mips_postboot_fixup(); + + /* Initialize pcpu stuff */ + mips_pcpu0_init(); + + /* Something to hold kernel env until kmem is available */ + init_static_kenv(boot1_env, sizeof(boot1_env)); +#ifdef FDT + /* + * Find the dtb passed in by the boot loader (currently fictional). + */ + dtbp = (vm_offset_t)NULL; + +#if defined(FDT_DTB_STATIC) + /* + * In case the device tree blob was not retrieved (from metadata) try + * to use the statically embedded one. + */ + if (dtbp == (vm_offset_t)NULL) + dtbp = (vm_offset_t)&fdt_static_dtb; +#else +#error "Non-static FDT not supported on JZ4780" +#endif + if (OF_install(OFW_FDT, 0) == FALSE) + while (1); + if (OF_init((void *)dtbp) != 0) + while (1); +#endif + + cninit(); +#ifdef FDT + /* + * Get bootargs from FDT if specified. + */ + chosen = OF_finddevice("/chosen"); + if (OF_getprop(chosen, "bootargs", buf, sizeof(buf)) != -1) + _parse_bootargs(buf); +#endif + /* Parse cmdline from U-Boot */ + argc = a0; + argv = (char **)a1; + _parse_cmdline(argc, argv); + + mips_init(); +} diff --git a/sys/mips/ingenic/jz4780_mmc.c b/sys/mips/ingenic/jz4780_mmc.c new file mode 100644 index 00000000000..0cf6be2971e --- /dev/null +++ b/sys/mips/ingenic/jz4780_mmc.c @@ -0,0 +1,1002 @@ +/*- + * Copyright (c) 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#undef JZ_MMC_DEBUG + +#define JZ_MSC_MEMRES 0 +#define JZ_MSC_IRQRES 1 +#define JZ_MSC_RESSZ 2 +#define JZ_MSC_DMA_SEGS 128 +#define JZ_MSC_DMA_MAX_SIZE MAXPHYS + +#define JZ_MSC_INT_ERR_BITS (JZ_INT_CRC_RES_ERR | JZ_INT_CRC_READ_ERR | \ + JZ_INT_CRC_WRITE_ERR | JZ_INT_TIMEOUT_RES | \ + JZ_INT_TIMEOUT_READ) +static int jz4780_mmc_pio_mode = 0; + +TUNABLE_INT("hw.jz.mmc.pio_mode", &jz4780_mmc_pio_mode); + +struct jz4780_mmc_dma_desc { + uint32_t dma_next; + uint32_t dma_phys; + uint32_t dma_len; + uint32_t dma_cmd; +}; + +struct jz4780_mmc_softc { + bus_space_handle_t sc_bsh; + bus_space_tag_t sc_bst; + device_t sc_dev; + clk_t sc_clk; + int sc_bus_busy; + int sc_resid; + int sc_timeout; + struct callout sc_timeoutc; + struct mmc_host sc_host; + struct mmc_request * sc_req; + struct mtx sc_mtx; + struct resource * sc_res[JZ_MSC_RESSZ]; + uint32_t sc_intr_seen; + uint32_t sc_intr_mask; + uint32_t sc_intr_wait; + void * sc_intrhand; + uint32_t sc_cmdat; + + /* Fields required for DMA access. */ + bus_addr_t sc_dma_desc_phys; + bus_dmamap_t sc_dma_map; + bus_dma_tag_t sc_dma_tag; + void * sc_dma_desc; + bus_dmamap_t sc_dma_buf_map; + bus_dma_tag_t sc_dma_buf_tag; + int sc_dma_inuse; + int sc_dma_map_err; + uint32_t sc_dma_ctl; +}; + +static struct resource_spec jz4780_mmc_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0, 0 } +}; + +static int jz4780_mmc_probe(device_t); +static int jz4780_mmc_attach(device_t); +static int jz4780_mmc_detach(device_t); +static int jz4780_mmc_setup_dma(struct jz4780_mmc_softc *); +static int jz4780_mmc_reset(struct jz4780_mmc_softc *); +static void jz4780_mmc_intr(void *); +static int jz4780_mmc_enable_clock(struct jz4780_mmc_softc *); +static int jz4780_mmc_config_clock(struct jz4780_mmc_softc *, uint32_t); + +static int jz4780_mmc_update_ios(device_t, device_t); +static int jz4780_mmc_request(device_t, device_t, struct mmc_request *); +static int jz4780_mmc_get_ro(device_t, device_t); +static int jz4780_mmc_acquire_host(device_t, device_t); +static int jz4780_mmc_release_host(device_t, device_t); + +#define JZ_MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define JZ_MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define JZ_MMC_READ_2(_sc, _reg) \ + bus_space_read_2((_sc)->sc_bst, (_sc)->sc_bsh, _reg) +#define JZ_MMC_WRITE_2(_sc, _reg, _value) \ + bus_space_write_2((_sc)->sc_bst, (_sc)->sc_bsh, _reg, _value) +#define JZ_MMC_READ_4(_sc, _reg) \ + bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _reg) +#define JZ_MMC_WRITE_4(_sc, _reg, _value) \ + bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _reg, _value) + +static int +jz4780_mmc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-mmc")) + return (ENXIO); + if (device_get_unit(dev) > 0) /* XXXKAN */ + return (ENXIO); + device_set_desc(dev, "Ingenic JZ4780 Integrated MMC/SD controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +jz4780_mmc_attach(device_t dev) +{ + struct jz4780_mmc_softc *sc; + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *tree; + device_t child; + ssize_t len; + pcell_t prop; + phandle_t node; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_req = NULL; + if (bus_alloc_resources(dev, jz4780_mmc_res_spec, sc->sc_res) != 0) { + device_printf(dev, "cannot allocate device resources\n"); + return (ENXIO); + } + sc->sc_bst = rman_get_bustag(sc->sc_res[JZ_MSC_MEMRES]); + sc->sc_bsh = rman_get_bushandle(sc->sc_res[JZ_MSC_MEMRES]); + if (bus_setup_intr(dev, sc->sc_res[JZ_MSC_IRQRES], + INTR_TYPE_MISC | INTR_MPSAFE, NULL, jz4780_mmc_intr, sc, + &sc->sc_intrhand)) { + bus_release_resources(dev, jz4780_mmc_res_spec, sc->sc_res); + device_printf(dev, "cannot setup interrupt handler\n"); + return (ENXIO); + } + sc->sc_timeout = 10; + ctx = device_get_sysctl_ctx(dev); + tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW, + &sc->sc_timeout, 0, "Request timeout in seconds"); + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), "jz4780_mmc", + MTX_DEF); + callout_init_mtx(&sc->sc_timeoutc, &sc->sc_mtx, 0); + + /* Reset controller. */ + if (jz4780_mmc_reset(sc) != 0) { + device_printf(dev, "cannot reset the controller\n"); + goto fail; + } + if (jz4780_mmc_pio_mode == 0 && jz4780_mmc_setup_dma(sc) != 0) { + device_printf(sc->sc_dev, "Couldn't setup DMA!\n"); + jz4780_mmc_pio_mode = 1; + } + if (bootverbose) + device_printf(sc->sc_dev, "DMA status: %s\n", + jz4780_mmc_pio_mode ? "disabled" : "enabled"); + + node = ofw_bus_get_node(dev); + /* Determine max operating frequency */ + sc->sc_host.f_max = 24000000; + len = OF_getencprop(node, "max-frequency", &prop, sizeof(prop)); + if (len / sizeof(prop) == 1) + sc->sc_host.f_max = prop; + sc->sc_host.f_min = sc->sc_host.f_max / 128; + + sc->sc_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; + sc->sc_host.caps = MMC_CAP_HSPEED; + sc->sc_host.mode = mode_sd; + /* + * Check for bus-width property, default to both 4 and 8 bit + * if no bus width is specified. + */ + len = OF_getencprop(node, "bus-width", &prop, sizeof(prop)); + if (len / sizeof(prop) != 1) + sc->sc_host.caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; + else if (prop == 8) + sc->sc_host.caps |= MMC_CAP_8_BIT_DATA; + else if (prop == 4) + sc->sc_host.caps |= MMC_CAP_4_BIT_DATA; + /* Activate the module clock. */ + if (jz4780_mmc_enable_clock(sc) != 0) { + device_printf(dev, "cannot activate mmc clock\n"); + goto fail; + } + + child = device_add_child(dev, "mmc", -1); + if (child == NULL) { + device_printf(dev, "attaching MMC bus failed!\n"); + goto fail; + } + if (device_probe_and_attach(child) != 0) { + device_printf(dev, "attaching MMC child failed!\n"); + device_delete_child(dev, child); + goto fail; + } + + return (0); + +fail: + callout_drain(&sc->sc_timeoutc); + mtx_destroy(&sc->sc_mtx); + bus_teardown_intr(dev, sc->sc_res[JZ_MSC_IRQRES], sc->sc_intrhand); + bus_release_resources(dev, jz4780_mmc_res_spec, sc->sc_res); + if (sc->sc_clk != NULL) + clk_release(sc->sc_clk); + return (ENXIO); +} + +static int +jz4780_mmc_detach(device_t dev) +{ + + return (EBUSY); +} + +static int +jz4780_mmc_enable_clock(struct jz4780_mmc_softc *sc) +{ + int err; + + err = clk_get_by_ofw_name(sc->sc_dev, 0, "mmc", &sc->sc_clk); + if (err == 0) + err = clk_enable(sc->sc_clk); + if (err == 0) + err = clk_set_freq(sc->sc_clk, sc->sc_host.f_max, 0); + if (err != 0) + clk_release(sc->sc_clk); + return (err); +} + +static void +jz4780_mmc_dma_desc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) +{ + struct jz4780_mmc_softc *sc; + + sc = (struct jz4780_mmc_softc *)arg; + if (err) { + sc->sc_dma_map_err = err; + return; + } + sc->sc_dma_desc_phys = segs[0].ds_addr; +} + +static int +jz4780_mmc_setup_dma(struct jz4780_mmc_softc *sc) +{ + int dma_desc_size, error; + + /* Allocate the DMA descriptor memory. */ + dma_desc_size = sizeof(struct jz4780_mmc_dma_desc) * JZ_MSC_DMA_SEGS; + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + dma_desc_size, 1, dma_desc_size, 0, NULL, NULL, &sc->sc_dma_tag); + if (error) + return (error); + error = bus_dmamem_alloc(sc->sc_dma_tag, &sc->sc_dma_desc, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->sc_dma_map); + if (error) + return (error); + + error = bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, + sc->sc_dma_desc, dma_desc_size, jz4780_mmc_dma_desc_cb, sc, 0); + if (error) + return (error); + if (sc->sc_dma_map_err) + return (sc->sc_dma_map_err); + + /* Create the DMA map for data transfers. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + JZ_MSC_DMA_MAX_SIZE * JZ_MSC_DMA_SEGS, JZ_MSC_DMA_SEGS, + JZ_MSC_DMA_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, + &sc->sc_dma_buf_tag); + if (error) + return (error); + error = bus_dmamap_create(sc->sc_dma_buf_tag, 0, + &sc->sc_dma_buf_map); + if (error) + return (error); + + return (0); +} + +static void +jz4780_mmc_dma_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err) +{ + struct jz4780_mmc_dma_desc *dma_desc; + struct jz4780_mmc_softc *sc; + uint32_t dma_desc_phys; + int i; + + sc = (struct jz4780_mmc_softc *)arg; + sc->sc_dma_map_err = err; + dma_desc = sc->sc_dma_desc; + dma_desc_phys = sc->sc_dma_desc_phys; + + /* Note nsegs is guaranteed to be zero if err is non-zero. */ + for (i = 0; i < nsegs; i++) { + dma_desc[i].dma_phys = segs[i].ds_addr; + dma_desc[i].dma_len = segs[i].ds_len; + if (i < (nsegs - 1)) { + dma_desc_phys += sizeof(struct jz4780_mmc_dma_desc); + dma_desc[i].dma_next = dma_desc_phys; + dma_desc[i].dma_cmd = (i << 16) | JZ_DMA_LINK; + } else { + dma_desc[i].dma_next = 0; + dma_desc[i].dma_cmd = (i << 16) | JZ_DMA_ENDI; + } +#ifdef JZ_MMC_DEBUG + device_printf(sc->sc_dev, "%d: desc %#x phys %#x len %d next %#x cmd %#x\n", + i, dma_desc_phys - sizeof(struct jz4780_mmc_dma_desc), + dma_desc[i].dma_phys, dma_desc[i].dma_len, + dma_desc[i].dma_next, dma_desc[i].dma_cmd); +#endif + } +} + +static int +jz4780_mmc_prepare_dma(struct jz4780_mmc_softc *sc) +{ + bus_dmasync_op_t sync_op; + int error; + struct mmc_command *cmd; + uint32_t off; + + cmd = sc->sc_req->cmd; + if (cmd->data->len > JZ_MSC_DMA_MAX_SIZE * JZ_MSC_DMA_SEGS) + return (EFBIG); + error = bus_dmamap_load(sc->sc_dma_buf_tag, sc->sc_dma_buf_map, + cmd->data->data, cmd->data->len, jz4780_mmc_dma_cb, sc, + BUS_DMA_NOWAIT); + if (error) + return (error); + if (sc->sc_dma_map_err) + return (sc->sc_dma_map_err); + + sc->sc_dma_inuse = 1; + if (cmd->data->flags & MMC_DATA_WRITE) + sync_op = BUS_DMASYNC_PREWRITE; + else + sync_op = BUS_DMASYNC_PREREAD; + bus_dmamap_sync(sc->sc_dma_buf_tag, sc->sc_dma_buf_map, sync_op); + bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, BUS_DMASYNC_PREWRITE); + + /* Configure default DMA parameters */ + sc->sc_dma_ctl = JZ_MODE_SEL | JZ_INCR_64 | JZ_DMAEN; + + /* Enable unaligned buffer handling */ + off = (uintptr_t)cmd->data->data & 3; + if (off != 0) + sc->sc_dma_ctl |= (off << JZ_AOFST_S) | JZ_ALIGNEN; + return (0); +} + +static void +jz4780_mmc_start_dma(struct jz4780_mmc_softc *sc) +{ + + /* Set the address of the first descriptor */ + JZ_MMC_WRITE_4(sc, JZ_MSC_DMANDA, sc->sc_dma_desc_phys); + /* Enable and start the dma engine */ + JZ_MMC_WRITE_4(sc, JZ_MSC_DMAC, sc->sc_dma_ctl); +} + +static int +jz4780_mmc_reset(struct jz4780_mmc_softc *sc) +{ + int timeout; + int reg; + + /* Stop the clock */ + reg = JZ_MMC_READ_4(sc, JZ_MSC_CTRL); + reg &= ~(JZ_CLOCK_CTRL_M); + reg |= JZ_CLOCK_STOP; + JZ_MMC_WRITE_4(sc, JZ_MSC_CTRL, reg); + + timeout = 1000; + while (--timeout > 0) { + if ((JZ_MMC_READ_4(sc, JZ_MSC_STAT) & JZ_CLK_EN) == 0) + break; + DELAY(100); + } + if (timeout == 0) { + device_printf(sc->sc_dev, "Failed to stop clk.\n"); + return (ETIMEDOUT); + } + + /* Reset */ + reg = JZ_MMC_READ_4(sc, JZ_MSC_CTRL); + reg |= JZ_RESET; + JZ_MMC_WRITE_4(sc, JZ_MSC_CTRL, reg); + + timeout = 10; + while (--timeout > 0) { + if ((JZ_MMC_READ_4(sc, JZ_MSC_STAT) & JZ_IS_RESETTING) == 0) + break; + DELAY(1000); + } + + if (timeout == 0) { + /* + * X1000 never clears reseting bit. + * Ignore for now. + */ + } + + /* Set the timeouts. */ + JZ_MMC_WRITE_4(sc, JZ_MSC_RESTO, 0xffff); + JZ_MMC_WRITE_4(sc, JZ_MSC_RDTO, 0xffffffff); + + /* Mask all interrupt initially */ + JZ_MMC_WRITE_4(sc, JZ_MSC_IMASK, 0xffffffff); + /* Clear pending interrupts. */ + JZ_MMC_WRITE_4(sc, JZ_MSC_IFLG, 0xffffffff); + + /* Remember interrupts we always want */ + sc->sc_intr_mask = JZ_MSC_INT_ERR_BITS; + + return (0); +} + +static void +jz4780_mmc_req_done(struct jz4780_mmc_softc *sc) +{ + struct mmc_command *cmd; + struct mmc_request *req; + bus_dmasync_op_t sync_op; + + cmd = sc->sc_req->cmd; + /* Reset the controller in case of errors */ + if (cmd->error != MMC_ERR_NONE) + jz4780_mmc_reset(sc); + /* Unmap DMA if necessary */ + if (sc->sc_dma_inuse == 1) { + if (cmd->data->flags & MMC_DATA_WRITE) + sync_op = BUS_DMASYNC_POSTWRITE; + else + sync_op = BUS_DMASYNC_POSTREAD; + bus_dmamap_sync(sc->sc_dma_buf_tag, sc->sc_dma_buf_map, + sync_op); + bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dma_buf_tag, sc->sc_dma_buf_map); + } + req = sc->sc_req; + callout_stop(&sc->sc_timeoutc); + sc->sc_req = NULL; + sc->sc_resid = 0; + sc->sc_dma_inuse = 0; + sc->sc_dma_map_err = 0; + sc->sc_intr_wait = 0; + sc->sc_intr_seen = 0; + req->done(req); +} + +static void +jz4780_mmc_read_response(struct jz4780_mmc_softc *sc) +{ + struct mmc_command *cmd; + int i; + + cmd = sc->sc_req->cmd; + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + uint16_t val; + + val = JZ_MMC_READ_2(sc, JZ_MSC_RES); + for (i = 0; i < 4; i++) { + cmd->resp[i] = val << 24; + val = JZ_MMC_READ_2(sc, JZ_MSC_RES); + cmd->resp[i] |= val << 8; + val = JZ_MMC_READ_2(sc, JZ_MSC_RES); + cmd->resp[i] |= val >> 8; + } + } else { + cmd->resp[0] = JZ_MMC_READ_2(sc, JZ_MSC_RES) << 24; + cmd->resp[0] |= JZ_MMC_READ_2(sc, JZ_MSC_RES) << 8; + cmd->resp[0] |= JZ_MMC_READ_2(sc, JZ_MSC_RES) & 0xff; + } + } +} + +static void +jz4780_mmc_req_ok(struct jz4780_mmc_softc *sc) +{ + struct mmc_command *cmd; + + cmd = sc->sc_req->cmd; + /* All data has been transferred ? */ + if (cmd->data != NULL && (sc->sc_resid << 2) < cmd->data->len) + cmd->error = MMC_ERR_FAILED; + jz4780_mmc_req_done(sc); +} + +static void +jz4780_mmc_timeout(void *arg) +{ + struct jz4780_mmc_softc *sc; + + sc = (struct jz4780_mmc_softc *)arg; + if (sc->sc_req != NULL) { + device_printf(sc->sc_dev, "controller timeout, rint %#x stat %#x\n", + JZ_MMC_READ_4(sc, JZ_MSC_IFLG), JZ_MMC_READ_4(sc, JZ_MSC_STAT)); + sc->sc_req->cmd->error = MMC_ERR_TIMEOUT; + jz4780_mmc_req_done(sc); + } else + device_printf(sc->sc_dev, + "Spurious timeout - no active request\n"); +} + +static int +jz4780_mmc_pio_transfer(struct jz4780_mmc_softc *sc, struct mmc_data *data) +{ + uint32_t mask, *buf; + int i, write; + + buf = (uint32_t *)data->data; + write = (data->flags & MMC_DATA_WRITE) ? 1 : 0; + mask = write ? JZ_DATA_FIFO_FULL : JZ_DATA_FIFO_EMPTY; + for (i = sc->sc_resid; i < (data->len >> 2); i++) { + if ((JZ_MMC_READ_4(sc, JZ_MSC_STAT) & mask)) + return (1); + if (write) + JZ_MMC_WRITE_4(sc, JZ_MSC_TXFIFO, buf[i]); + else + buf[i] = JZ_MMC_READ_4(sc, JZ_MSC_RXFIFO); + sc->sc_resid = i + 1; + } + + /* Done with pio transfer, shut FIFO interrupts down */ + mask = JZ_MMC_READ_4(sc, JZ_MSC_IMASK); + mask |= (JZ_INT_TXFIFO_WR_REQ | JZ_INT_RXFIFO_RD_REQ); + JZ_MMC_WRITE_4(sc, JZ_MSC_IMASK, mask); + return (0); +} + +static void +jz4780_mmc_intr(void *arg) +{ + struct jz4780_mmc_softc *sc; + struct mmc_data *data; + uint32_t rint; + + sc = (struct jz4780_mmc_softc *)arg; + JZ_MMC_LOCK(sc); + rint = JZ_MMC_READ_4(sc, JZ_MSC_IFLG); +#if defined(JZ_MMC_DEBUG) + device_printf(sc->sc_dev, "rint: %#x, stat: %#x\n", + rint, JZ_MMC_READ_4(sc, JZ_MSC_STAT)); + if (sc->sc_dma_inuse == 1 && (sc->sc_intr_seen & JZ_INT_DMAEND) == 0) + device_printf(sc->sc_dev, "\tdmada %#x dmanext %#x dmac %#x" + " dmalen %d dmacmd %#x\n", + JZ_MMC_READ_4(sc, JZ_MSC_DMADA), + JZ_MMC_READ_4(sc, JZ_MSC_DMANDA), + JZ_MMC_READ_4(sc, JZ_MSC_DMAC), + JZ_MMC_READ_4(sc, JZ_MSC_DMALEN), + JZ_MMC_READ_4(sc, JZ_MSC_DMACMD)); +#endif + if (sc->sc_req == NULL) { + device_printf(sc->sc_dev, + "Spurious interrupt - no active request, rint: 0x%08X\n", + rint); + goto end; + } + if (rint & JZ_MSC_INT_ERR_BITS) { +#if defined(JZ_MMC_DEBUG) + device_printf(sc->sc_dev, "controller error, rint %#x stat %#x\n", + rint, JZ_MMC_READ_4(sc, JZ_MSC_STAT)); +#endif + if (rint & (JZ_INT_TIMEOUT_RES | JZ_INT_TIMEOUT_READ)) + sc->sc_req->cmd->error = MMC_ERR_TIMEOUT; + else + sc->sc_req->cmd->error = MMC_ERR_FAILED; + jz4780_mmc_req_done(sc); + goto end; + } + data = sc->sc_req->cmd->data; + /* Check for command response */ + if (rint & JZ_INT_END_CMD_RES) { + jz4780_mmc_read_response(sc); + if (sc->sc_dma_inuse == 1) + jz4780_mmc_start_dma(sc); + } + if (data != NULL) { + if (sc->sc_dma_inuse == 1 && (rint & JZ_INT_DMAEND)) + sc->sc_resid = data->len >> 2; + else if (sc->sc_dma_inuse == 0 && + (rint & (JZ_INT_TXFIFO_WR_REQ | JZ_INT_RXFIFO_RD_REQ))) + jz4780_mmc_pio_transfer(sc, data); + } + sc->sc_intr_seen |= rint; + if ((sc->sc_intr_seen & sc->sc_intr_wait) == sc->sc_intr_wait) + jz4780_mmc_req_ok(sc); +end: + JZ_MMC_WRITE_4(sc, JZ_MSC_IFLG, rint); + JZ_MMC_UNLOCK(sc); +} + +static int +jz4780_mmc_request(device_t bus, device_t child, struct mmc_request *req) +{ + struct jz4780_mmc_softc *sc; + struct mmc_command *cmd; + uint32_t cmdat, ctrl, iwait; + int blksz; + + sc = device_get_softc(bus); + JZ_MMC_LOCK(sc); + if (sc->sc_req != NULL) { + JZ_MMC_UNLOCK(sc); + return (EBUSY); + } + /* Start with template value */ + cmdat = sc->sc_cmdat; + iwait = JZ_INT_END_CMD_RES; + + /* Configure response format */ + cmd = req->cmd; + switch (MMC_RSP(cmd->flags)) { + case MMC_RSP_R1: + case MMC_RSP_R1B: + cmdat |= JZ_RES_R1; + break; + case MMC_RSP_R2: + cmdat |= JZ_RES_R2; + break; + case MMC_RSP_R3: + cmdat |= JZ_RES_R3; + break; + }; + if (cmd->opcode == MMC_GO_IDLE_STATE) + cmdat |= JZ_INIT; + if (cmd->flags & MMC_RSP_BUSY) { + cmdat |= JZ_BUSY; + iwait |= JZ_INT_PRG_DONE; + } + + sc->sc_req = req; + sc->sc_resid = 0; + cmd->error = MMC_ERR_NONE; + + if (cmd->data != NULL) { + cmdat |= JZ_DATA_EN; + if (cmd->data->flags & MMC_DATA_MULTI) { + cmdat |= JZ_AUTO_CMD12; + iwait |= JZ_INT_AUTO_CMD12_DONE; + } + if (cmd->data->flags & MMC_DATA_WRITE) { + cmdat |= JZ_WRITE; + iwait |= JZ_INT_PRG_DONE; + } + if (cmd->data->flags & MMC_DATA_STREAM) + cmdat |= JZ_STREAM; + else + iwait |= JZ_INT_DATA_TRAN_DONE; + + blksz = min(cmd->data->len, MMC_SECTOR_SIZE); + JZ_MMC_WRITE_4(sc, JZ_MSC_BLKLEN, blksz); + JZ_MMC_WRITE_4(sc, JZ_MSC_NOB, cmd->data->len / blksz); + + /* Attempt to setup DMA for this transaction */ + if (jz4780_mmc_pio_mode == 0) + jz4780_mmc_prepare_dma(sc); + if (sc->sc_dma_inuse != 0) { + /* Wait for DMA completion interrupt */ + iwait |= JZ_INT_DMAEND; + } else { + iwait |= (cmd->data->flags & MMC_DATA_WRITE) ? + JZ_INT_TXFIFO_WR_REQ : JZ_INT_RXFIFO_RD_REQ; + JZ_MMC_WRITE_4(sc, JZ_MSC_DMAC, 0); + } + } + + sc->sc_intr_seen = 0; + sc->sc_intr_wait = iwait; + JZ_MMC_WRITE_4(sc, JZ_MSC_IMASK, ~(sc->sc_intr_mask | iwait)); + +#if defined(JZ_MMC_DEBUG) + device_printf(sc->sc_dev, + "REQUEST: CMD%u arg %#x flags %#x cmdat %#x sc_intr_wait = %#x\n", + cmd->opcode, cmd->arg, cmd->flags, cmdat, sc->sc_intr_wait); +#endif + + JZ_MMC_WRITE_4(sc, JZ_MSC_ARG, cmd->arg); + JZ_MMC_WRITE_4(sc, JZ_MSC_CMD, cmd->opcode); + JZ_MMC_WRITE_4(sc, JZ_MSC_CMDAT, cmdat); + + ctrl = JZ_MMC_READ_4(sc, JZ_MSC_CTRL); + ctrl |= JZ_START_OP | JZ_CLOCK_START; + JZ_MMC_WRITE_4(sc, JZ_MSC_CTRL, ctrl); + + callout_reset(&sc->sc_timeoutc, sc->sc_timeout * hz, + jz4780_mmc_timeout, sc); + JZ_MMC_UNLOCK(sc); + + return (0); +} + +static int +jz4780_mmc_read_ivar(device_t bus, device_t child, int which, + uintptr_t *result) +{ + struct jz4780_mmc_softc *sc; + + sc = device_get_softc(bus); + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + *(int *)result = sc->sc_host.ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *(int *)result = sc->sc_host.ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *(int *)result = sc->sc_host.ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *(int *)result = sc->sc_host.ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *(int *)result = sc->sc_host.f_min; + break; + case MMCBR_IVAR_F_MAX: + *(int *)result = sc->sc_host.f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *(int *)result = sc->sc_host.host_ocr; + break; + case MMCBR_IVAR_MODE: + *(int *)result = sc->sc_host.mode; + break; + case MMCBR_IVAR_OCR: + *(int *)result = sc->sc_host.ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *(int *)result = sc->sc_host.ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *(int *)result = sc->sc_host.ios.vdd; + break; + case MMCBR_IVAR_CAPS: + *(int *)result = sc->sc_host.caps; + break; + case MMCBR_IVAR_MAX_DATA: + *(int *)result = 65535; + break; + case MMCBR_IVAR_TIMING: + *(int *)result = sc->sc_host.ios.timing; + break; + } + + return (0); +} + +static int +jz4780_mmc_write_ivar(device_t bus, device_t child, int which, + uintptr_t value) +{ + struct jz4780_mmc_softc *sc; + + sc = device_get_softc(bus); + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + sc->sc_host.ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + sc->sc_host.ios.bus_width = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + sc->sc_host.ios.chip_select = value; + break; + case MMCBR_IVAR_CLOCK: + sc->sc_host.ios.clock = value; + break; + case MMCBR_IVAR_MODE: + sc->sc_host.mode = value; + break; + case MMCBR_IVAR_OCR: + sc->sc_host.ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + sc->sc_host.ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + sc->sc_host.ios.vdd = value; + break; + case MMCBR_IVAR_TIMING: + sc->sc_host.ios.timing = value; + break; + /* These are read-only */ + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + return (EINVAL); + } + + return (0); +} + +static int +jz4780_mmc_disable_clock(struct jz4780_mmc_softc *sc) +{ + int timeout; + + JZ_MMC_WRITE_4(sc, JZ_MSC_CTRL, + JZ_MMC_READ_4(sc, JZ_MSC_CTRL) | JZ_CLOCK_STOP); + + for (timeout = 1000; timeout > 0; timeout--) + if ((JZ_MMC_READ_4(sc, JZ_MSC_STAT) & JZ_CLK_EN) == 0) + return (0); + return (ETIMEDOUT); +} + +static int +jz4780_mmc_config_clock(struct jz4780_mmc_softc *sc, uint32_t freq) +{ + uint64_t rate; + uint32_t clk_freq; + int err, div; + + err = jz4780_mmc_disable_clock(sc); + if (err != 0) + return (err); + + clk_get_freq(sc->sc_clk, &rate); + clk_freq = (uint32_t)rate; + + div = 0; + while (clk_freq > freq) { + div++; + clk_freq >>= 1; + } + if (div >= 7) + div = 7; +#if defined(JZ_MMC_DEBUG) + if (div != JZ_MMC_READ_4(sc, JZ_MSC_CLKRT)) + device_printf(sc->sc_dev, + "UPDATE_IOS: clk -> %u\n", clk_freq); +#endif + JZ_MMC_WRITE_4(sc, JZ_MSC_CLKRT, div); + return (0); +} + +static int +jz4780_mmc_update_ios(device_t bus, device_t child) +{ + struct jz4780_mmc_softc *sc; + struct mmc_ios *ios; + int error; + + sc = device_get_softc(bus); + ios = &sc->sc_host.ios; + if (ios->clock) { + /* Set the MMC clock. */ + error = jz4780_mmc_config_clock(sc, ios->clock); + if (error != 0) + return (error); + } + + /* Set the bus width. */ + switch (ios->bus_width) { + case bus_width_1: + sc->sc_cmdat &= ~(JZ_BUS_WIDTH_M); + sc->sc_cmdat |= JZ_BUS_1BIT; + break; + case bus_width_4: + sc->sc_cmdat &= ~(JZ_BUS_WIDTH_M); + sc->sc_cmdat |= JZ_BUS_4BIT; + break; + case bus_width_8: + sc->sc_cmdat &= ~(JZ_BUS_WIDTH_M); + sc->sc_cmdat |= JZ_BUS_8BIT; + break; + } + return (0); +} + +static int +jz4780_mmc_get_ro(device_t bus, device_t child) +{ + + return (0); +} + +static int +jz4780_mmc_acquire_host(device_t bus, device_t child) +{ + struct jz4780_mmc_softc *sc; + int error; + + sc = device_get_softc(bus); + JZ_MMC_LOCK(sc); + while (sc->sc_bus_busy) { + error = msleep(sc, &sc->sc_mtx, PCATCH, "mmchw", 0); + if (error != 0) { + JZ_MMC_UNLOCK(sc); + return (error); + } + } + sc->sc_bus_busy++; + JZ_MMC_UNLOCK(sc); + + return (0); +} + +static int +jz4780_mmc_release_host(device_t bus, device_t child) +{ + struct jz4780_mmc_softc *sc; + + sc = device_get_softc(bus); + JZ_MMC_LOCK(sc); + sc->sc_bus_busy--; + wakeup(sc); + JZ_MMC_UNLOCK(sc); + + return (0); +} + +static device_method_t jz4780_mmc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_mmc_probe), + DEVMETHOD(device_attach, jz4780_mmc_attach), + DEVMETHOD(device_detach, jz4780_mmc_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, jz4780_mmc_read_ivar), + DEVMETHOD(bus_write_ivar, jz4780_mmc_write_ivar), + DEVMETHOD(bus_print_child, bus_generic_print_child), + + /* MMC bridge interface */ + DEVMETHOD(mmcbr_update_ios, jz4780_mmc_update_ios), + DEVMETHOD(mmcbr_request, jz4780_mmc_request), + DEVMETHOD(mmcbr_get_ro, jz4780_mmc_get_ro), + DEVMETHOD(mmcbr_acquire_host, jz4780_mmc_acquire_host), + DEVMETHOD(mmcbr_release_host, jz4780_mmc_release_host), + + DEVMETHOD_END +}; + +static devclass_t jz4780_mmc_devclass; + +static driver_t jz4780_mmc_driver = { + "jzmmc", + jz4780_mmc_methods, + sizeof(struct jz4780_mmc_softc), +}; + +DRIVER_MODULE(jzmmc, simplebus, jz4780_mmc_driver, jz4780_mmc_devclass, 0, 0); +DRIVER_MODULE(mmc, jzmmc, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(jzmmc, mmc, 1, 1, 1); diff --git a/sys/mips/ingenic/jz4780_mp.c b/sys/mips/ingenic/jz4780_mp.c new file mode 100644 index 00000000000..2f6c8f31fe4 --- /dev/null +++ b/sys/mips/ingenic/jz4780_mp.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 2015 Alexander Kabaev + * Copyright (c) 2004-2010 Juli Mallett + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +void jz4780_mpentry(void); + +#define JZ4780_MAXCPU 2 + +void +platform_ipi_send(int cpuid) +{ + + if (cpuid == 0) + mips_wr_xburst_mbox0(1); + else + mips_wr_xburst_mbox1(1); +} + +void +platform_ipi_clear(void) +{ + int cpuid = PCPU_GET(cpuid); + uint32_t action; + + action = (cpuid == 0) ? mips_rd_xburst_mbox0() : mips_rd_xburst_mbox1(); + KASSERT(action == 1, ("CPU %d: unexpected IPIs: %#x", cpuid, action)); + mips_wr_xburst_core_sts(~(JZ_CORESTS_MIRQ0P << cpuid)); +} + +int +platform_processor_id(void) +{ + + return (mips_rd_ebase() & 7); +} + +int +platform_ipi_hardintr_num(void) +{ + + return (1); +} + +int +platform_ipi_softintr_num(void) +{ + + return (-1); +} + +void +platform_init_ap(int cpuid) +{ + unsigned reg; + + /* + * Clear any pending IPIs. + */ + mips_wr_xburst_core_sts(~(JZ_CORESTS_MIRQ0P << cpuid)); + + /* Allow IPI mbox for this core */ + reg = mips_rd_xburst_reim(); + reg |= (JZ_REIM_MIRQ0M << cpuid); + mips_wr_xburst_reim(reg); + + /* + * Unmask the ipi interrupts. + */ + reg = hard_int_mask(platform_ipi_hardintr_num()); + set_intr_mask(reg); +} + +void +platform_cpu_mask(cpuset_t *mask) +{ + uint32_t i, m; + + CPU_ZERO(mask); + for (i = 0, m = 1 ; i < JZ4780_MAXCPU; i++, m <<= 1) + CPU_SET(i, mask); +} + +struct cpu_group * +platform_smp_topo(void) +{ + return (smp_topo_none()); +} + +static void +jz4780_core_powerup(void) +{ + uint32_t reg; + + reg = readreg(JZ_CGU_BASE + JZ_LPCR); + reg &= ~LPCR_PD_SCPU; + writereg(JZ_CGU_BASE + JZ_LPCR, reg); + do { + reg = readreg(JZ_CGU_BASE + JZ_LPCR); + } while ((reg & LPCR_SCPUS) != 0); +} + +/* + * Spin up the second code. The code is roughly modeled after + * similar routine in Linux. + */ +int +platform_start_ap(int cpuid) +{ + uint32_t reg, addr; + + if (cpuid >= JZ4780_MAXCPU) + return (EINVAL); + + /* Figure out address of mpentry in KSEG1 */ + addr = MIPS_PHYS_TO_KSEG1(MIPS_KSEG0_TO_PHYS(jz4780_mpentry)); + KASSERT((addr & ~JZ_REIM_ENTRY_MASK) == 0, + ("Unaligned mpentry")); + + /* Configure core alternative entry point */ + reg = mips_rd_xburst_reim(); + reg &= ~JZ_REIM_ENTRY_MASK; + reg |= addr & JZ_REIM_ENTRY_MASK; + + /* Allow this core to get IPIs from one being started */ + reg |= JZ_REIM_MIRQ0M; + mips_wr_xburst_reim(reg); + + /* Force core into reset and enable use of alternate entry point */ + reg = mips_rd_xburst_core_ctl(); + reg |= (JZ_CORECTL_SWRST0 << cpuid) | (JZ_CORECTL_RPC0 << cpuid); + mips_wr_xburst_core_ctl(reg); + + /* Power the core up */ + jz4780_core_powerup(); + + /* Take the core out of reset */ + reg &= ~(JZ_CORECTL_SWRST0 << cpuid); + mips_wr_xburst_core_ctl(reg); + + return (0); +} diff --git a/sys/mips/ingenic/jz4780_mpboot.S b/sys/mips/ingenic/jz4780_mpboot.S new file mode 100644 index 00000000000..2c1ed5e081a --- /dev/null +++ b/sys/mips/ingenic/jz4780_mpboot.S @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include + +#include "assym.s" + +#define CACHE_SIZE (32 * 1024) +#define CACHE_LINESIZE 32 + + .text + .set noat + .set noreorder + .section .text.mpentry_jz4780 + .balign 0x10000 + +GLOBAL(jz4780_mpentry) + + /* Initialize caches */ + li t0, MIPS_KSEG0_START + ori t1, t0, CACHE_SIZE + mtc0 zero, MIPS_COP_0_TAG_LO + COP0_SYNC +1: cache CACHEOP_R4K_INDEX_STORE_TAG | CACHE_R4K_I, 0(t0) + cache CACHEOP_R4K_INDEX_STORE_TAG | CACHE_R4K_D, 0(t0) + bne t0, t1, 1b + addiu t0, t0, CACHE_LINESIZE + + /* Set TLB page mask */ + mtc0 zero, MIPS_COP_0_TLB_PG_MASK + COP0_SYNC + + j mpentry + nop diff --git a/sys/mips/ingenic/jz4780_nand.c b/sys/mips/ingenic/jz4780_nand.c new file mode 100644 index 00000000000..4a658dbc044 --- /dev/null +++ b/sys/mips/ingenic/jz4780_nand.c @@ -0,0 +1,123 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +struct jz4780_nand_softc { + device_t dev; + struct resource *res[1]; +}; + +static struct resource_spec jz4780_nand_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int jz4780_nand_probe(device_t dev); +static int jz4780_nand_attach(device_t dev); +static int jz4780_nand_detach(device_t dev); + +static int +jz4780_nand_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-nand")) + return (ENXIO); + + device_set_desc(dev, "Ingenic JZ4780 NAND Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +jz4780_nand_attach(device_t dev) +{ + struct jz4780_nand_softc *sc = device_get_softc(dev); + + sc->dev = dev; + + if (bus_alloc_resources(dev, jz4780_nand_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + return (0); +} + +static int +jz4780_nand_detach(device_t dev) +{ + struct jz4780_nand_softc *sc = device_get_softc(dev); + + bus_release_resources(dev, jz4780_nand_spec, sc->res); + return (0); +} + +static device_method_t jz4780_nand_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_nand_probe), + DEVMETHOD(device_attach, jz4780_nand_attach), + DEVMETHOD(device_detach, jz4780_nand_detach), + + DEVMETHOD_END +}; + +static driver_t jz4780_nand_driver = { + "nand", + jz4780_nand_methods, + sizeof(struct jz4780_nand_softc), +}; + +static devclass_t jz4780_nand_devclass; + +DRIVER_MODULE(jz4780_nand, simplebus, jz4780_nand_driver, + jz4780_nand_devclass, 0, 0); diff --git a/sys/mips/ingenic/jz4780_nemc.c b/sys/mips/ingenic/jz4780_nemc.c new file mode 100644 index 00000000000..5c0b1151576 --- /dev/null +++ b/sys/mips/ingenic/jz4780_nemc.c @@ -0,0 +1,373 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +struct jz4780_nemc_devinfo { + struct simplebus_devinfo sinfo; + uint32_t bank; +}; + +struct jz4780_nemc_softc { + struct simplebus_softc simplebus_sc; + device_t dev; + struct resource *res[1]; + uint32_t banks; + uint32_t clock_tick_psecs; + clk_t clk; +}; + +static struct resource_spec jz4780_nemc_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) + +static int jz4780_nemc_probe(device_t dev); +static int jz4780_nemc_attach(device_t dev); +static int jz4780_nemc_detach(device_t dev); + +static int +jz4780_nemc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-nemc")) + return (ENXIO); + + device_set_desc(dev, "Ingenic JZ4780 NEMC"); + + return (BUS_PROBE_DEFAULT); +} + +#define JZ4780_NEMC_NS_TO_TICKS(sc, val) howmany((val) * 1000, (sc)->clock_tick_psecs) + +/* Use table from JZ4780 programmers manual to convert ticks to tBP/tAW register values */ +static const uint8_t ticks_to_tBP_tAW[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* 1:1 mapping */ + 11, 11, /* 12 cycles */ + 12, 12, 12, /* 15 cycles */ + 13, 13, 13, 13, 13, /* 20 cycles */ + 14, 14, 14, 14, 14, /* 25 cycles */ + 15, 15, 15, 15, 15, 15 /* 31 cycles */ +}; + +static int +jz4780_nemc_configure_bank(struct jz4780_nemc_softc *sc, + device_t dev, u_int bank) +{ + uint32_t smcr, cycles; + phandle_t node; + pcell_t val; + + /* Check if bank is configured already */ + if (sc->banks & (1 << bank)) + return 0; + + smcr = CSR_READ_4(sc, JZ_NEMC_SMCR(bank)); + + smcr &= ~JZ_NEMC_SMCR_SMT_MASK; + smcr |= JZ_NEMC_SMCR_SMT_NORMAL << JZ_NEMC_SMCR_SMT_SHIFT; + + node = ofw_bus_get_node(dev); + if (OF_getencprop(node, "ingenic,nemc-tAS", &val, sizeof(val)) > 0) { + cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val); + if (cycles > 15) { + device_printf(sc->dev, + "invalid value of %s %u (%u cycles), maximum %u cycles supported\n", + "ingenic,nemc-tAS", val, cycles, 15); + return -1; + } + smcr &= ~JZ_NEMC_SMCR_TAS_MASK; + smcr |= cycles << JZ_NEMC_SMCR_TAS_SHIFT; + } + + if (OF_getencprop(node, "ingenic,nemc-tAH", &val, sizeof(val)) > 0) { + cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val); + if (cycles > 15) { + device_printf(sc->dev, + "invalid value of %s %u (%u cycles), maximum %u cycles supported\n", + "ingenic,nemc-tAH", val, cycles, 15); + return -1; + } + smcr &= ~JZ_NEMC_SMCR_TAH_MASK; + smcr |= cycles << JZ_NEMC_SMCR_TAH_SHIFT; + } + + if (OF_getencprop(node, "ingenic,nemc-tBP", &val, sizeof(val)) > 0) { + cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val); + if (cycles > 31) { + device_printf(sc->dev, + "invalid value of %s %u (%u cycles), maximum %u cycles supported\n", + "ingenic,nemc-tBP", val, cycles, 15); + return -1; + } + smcr &= ~JZ_NEMC_SMCR_TBP_MASK; + smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TBP_SHIFT; + } + + if (OF_getencprop(node, "ingenic,nemc-tAW", &val, sizeof(val)) > 0) { + cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val); + if (cycles > 31) { + device_printf(sc->dev, + "invalid value of %s %u (%u cycles), maximum %u cycles supported\n", + "ingenic,nemc-tAW", val, cycles, 15); + return -1; + } + smcr &= ~JZ_NEMC_SMCR_TAW_MASK; + smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TAW_SHIFT; + } + + if (OF_getencprop(node, "ingenic,nemc-tSTRV", &val, sizeof(val)) > 0) { + cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val); + if (cycles > 63) { + device_printf(sc->dev, + "invalid value of %s %u (%u cycles), maximum %u cycles supported\n", + "ingenic,nemc-tSTRV", val, cycles, 15); + return -1; + } + smcr &= ~JZ_NEMC_SMCR_STRV_MASK; + smcr |= cycles << JZ_NEMC_SMCR_STRV_SHIFT; + } + CSR_WRITE_4(sc, JZ_NEMC_SMCR(bank), smcr); + sc->banks |= (1 << bank); + return 0; +} + +/* Wholesale copy of simplebus routine */ +static int +jz4780_nemc_fill_ranges(phandle_t node, struct simplebus_softc *sc) +{ + int host_address_cells; + cell_t *base_ranges; + ssize_t nbase_ranges; + int err; + int i, j, k; + + err = OF_searchencprop(OF_parent(node), "#address-cells", + &host_address_cells, sizeof(host_address_cells)); + if (err <= 0) + return (-1); + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges < 0) + return (-1); + sc->nranges = nbase_ranges / sizeof(cell_t) / + (sc->acells + host_address_cells + sc->scells); + if (sc->nranges == 0) + return (0); + + sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), + M_DEVBUF, M_WAITOK); + base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); + OF_getencprop(node, "ranges", base_ranges, nbase_ranges); + + for (i = 0, j = 0; i < sc->nranges; i++) { + sc->ranges[i].bus = 0; + for (k = 0; k < sc->acells; k++) { + sc->ranges[i].bus <<= 32; + sc->ranges[i].bus |= base_ranges[j++]; + } + sc->ranges[i].host = 0; + for (k = 0; k < host_address_cells; k++) { + sc->ranges[i].host <<= 32; + sc->ranges[i].host |= base_ranges[j++]; + } + sc->ranges[i].size = 0; + for (k = 0; k < sc->scells; k++) { + sc->ranges[i].size <<= 32; + sc->ranges[i].size |= base_ranges[j++]; + } + } + + free(base_ranges, M_DEVBUF); + return (sc->nranges); +} + +static int +jz4780_nemc_attach(device_t dev) +{ + struct jz4780_nemc_softc *sc = device_get_softc(dev); + phandle_t node; + uint64_t freq; + + sc->dev = dev; + + if (bus_alloc_resources(dev, jz4780_nemc_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + + /* Initialize simplebus and enumerate resources */ + simplebus_init(dev, node); + + if (jz4780_nemc_fill_ranges(node, &sc->simplebus_sc) < 0) + goto error; + + /* Figure our underlying clock rate. */ + if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) { + device_printf(dev, "could not lookup device clock\n"); + goto error; + } + if (clk_enable(sc->clk) != 0) { + device_printf(dev, "could not enable device clock\n"); + goto error; + } + if (clk_get_freq(sc->clk, &freq) != 0) { + device_printf(dev, "could not determine clock speed\n"); + goto error; + } + + /* Convert clock frequency to picoseconds-per-tick value. */ + sc->clock_tick_psecs = (uint32_t)(1000000000000ULL / freq); + + /* + * Allow devices to identify. + */ + bus_generic_probe(dev); + + /* + * Now walk the tree and attach top level devices + */ + for (node = OF_child(node); node > 0; node = OF_peer(node)) + simplebus_add_device(dev, node, 0, NULL, -1, NULL); + + return (bus_generic_attach(dev)); +error: + jz4780_nemc_detach(dev); + return (ENXIO); +} + +static int +jz4780_nemc_detach(device_t dev) +{ + struct jz4780_nemc_softc *sc = device_get_softc(dev); + + bus_generic_detach(dev); + if (sc->clk != NULL) + clk_release(sc->clk); + bus_release_resources(dev, jz4780_nemc_spec, sc->res); + return (0); +} + +static int +jz4780_nemc_decode_bank(struct simplebus_softc *sc, struct resource *r, + u_int *bank) +{ + rman_res_t start, end; + int i; + + start = rman_get_start(r); + end = rman_get_end(r); + + /* Remap through ranges property */ + for (i = 0; i < sc->nranges; i++) { + if (start >= sc->ranges[i].host && end < + sc->ranges[i].host + sc->ranges[i].size) { + *bank = (sc->ranges[i].bus >> 32); + return (0); + } + } + return (1); +} + +static int +jz4780_nemc_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + struct jz4780_nemc_softc *sc; + u_int bank; + int err; + + if (type == SYS_RES_MEMORY) { + sc = device_get_softc(bus); + + /* Figure out on what bank device is residing */ + err = jz4780_nemc_decode_bank(&sc->simplebus_sc, r, &bank); + if (err == 0) { + /* Attempt to configure the bank if not done already */ + err = jz4780_nemc_configure_bank(sc, child, bank); + if (err != 0) + return (err); + } + } + + /* Call default implementation to finish the work */ + return (bus_generic_activate_resource(bus, child, + type, rid, r)); +} + +static device_method_t jz4780_nemc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_nemc_probe), + DEVMETHOD(device_attach, jz4780_nemc_attach), + DEVMETHOD(device_detach, jz4780_nemc_detach), + + /* Overrides to configure bank on resource activation */ + DEVMETHOD(bus_activate_resource, jz4780_nemc_activate_resource), + + DEVMETHOD_END +}; + +static devclass_t jz4780_nemc_devclass; +DEFINE_CLASS_1(nemc, jz4780_nemc_driver, jz4780_nemc_methods, + sizeof(struct jz4780_nemc_softc), simplebus_driver); +DRIVER_MODULE(jz4780_nemc, simplebus, jz4780_nemc_driver, + jz4780_nemc_devclass, 0, 0); diff --git a/sys/mips/ingenic/jz4780_ohci.c b/sys/mips/ingenic/jz4780_ohci.c new file mode 100644 index 00000000000..3b4ffdbfceb --- /dev/null +++ b/sys/mips/ingenic/jz4780_ohci.c @@ -0,0 +1,318 @@ +/*- + * Copyright (c) 2015, Alexander Kabaev + * Copyright (c) 2009, Oleksandr Tymoshenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +static int jz4780_ohci_attach(device_t dev); +static int jz4780_ohci_detach(device_t dev); +static int jz4780_ohci_probe(device_t dev); + +struct jz4780_ohci_softc +{ + struct ohci_softc sc_ohci; + struct gpiobus_pin *gpio_vbus; + clk_t clk; +}; + +static int +jz4780_ohci_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-ohci")) + return (ENXIO); + + device_set_desc(dev, "Ingenic JZ4780 OHCI"); + + return (BUS_PROBE_DEFAULT); +} + +static int +jz4780_ohci_vbus_gpio_enable(device_t dev, struct jz4780_ohci_softc *sc) +{ + struct gpiobus_pin *gpio_vbus; + int error; + + error = ofw_gpiobus_parse_gpios(dev, "ingenic,vbus-gpio", &gpio_vbus); + /* + * The pin can be mapped already by other device. Assume it also has need + * activated and proceed happily. + */ + if (error <= 0) + return (0); + + sc->gpio_vbus = gpio_vbus; + if (error > 1) { + device_printf(dev, "too many vbus gpios\n"); + return (ENXIO); + } + + if (sc->gpio_vbus != NULL) { + error = GPIO_PIN_SET(sc->gpio_vbus->dev, sc->gpio_vbus->pin, 1); + if (error != 0) { + device_printf(dev, "Cannot configure GPIO pin %d on %s\n", + sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev)); + return (error); + } + + error = GPIO_PIN_SETFLAGS(sc->gpio_vbus->dev, sc->gpio_vbus->pin, + GPIO_PIN_OUTPUT); + if (error != 0) { + device_printf(dev, "Cannot configure GPIO pin %d on %s\n", + sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev)); + return (error); + } + } + return (0); +} + +static int +jz4780_ohci_clk_enable(device_t dev) +{ + struct jz4780_ohci_softc *sc; + int err; + + sc = device_get_softc(dev); + + err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); + if (err != 0) { + device_printf(dev, "unable to lookup device clock\n"); + return (err); + } + err = clk_enable(sc->clk); + if (err != 0) { + device_printf(dev, "unable to enable device clock\n"); + return (err); + } + err = clk_set_freq(sc->clk, 48000000, 0); + if (err != 0) { + device_printf(dev, "unable to set device clock to 48 kHZ\n"); + return (err); + } + return (0); +} + +static int +jz4780_ohci_attach(device_t dev) +{ + struct jz4780_ohci_softc *sc = device_get_softc(dev); + int err; + int rid; + + /* initialize some bus fields */ + sc->sc_ohci.sc_bus.parent = dev; + sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; + sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_ohci.sc_bus.dma_bits = 32; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, + USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { + return (ENOMEM); + } + + sc->sc_ohci.sc_dev = dev; + + /* frob vbus gpio */ + err = jz4780_ohci_vbus_gpio_enable(dev, sc); + if (err) + goto error; + + err = jz4780_ohci_clk_enable(dev); + if (err) + goto error; + + rid = 0; + sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_ohci.sc_io_res == NULL) { + err = ENOMEM; + goto error; + } + sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); + sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); + sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); + + rid = 0; + sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->sc_ohci.sc_irq_res == NULL) { + err = ENOMEM; + goto error; + } + + if (jz4780_ohci_enable() != 0) { + device_printf(dev, "CGU failed to enable OHCI\n"); + err = ENXIO; + goto error; + } + + sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (sc->sc_ohci.sc_bus.bdev == NULL) { + err = ENOMEM; + goto error; + } + device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); + + err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, + INTR_TYPE_BIO | INTR_MPSAFE, NULL, + (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); + if (err) { + err = ENXIO; + goto error; + } + + strlcpy(sc->sc_ohci.sc_vendor, "Ingenic", sizeof(sc->sc_ohci.sc_vendor)); + bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); + + err = ohci_init(&sc->sc_ohci); + if (!err) + err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); + + if (err) + goto error; + return (0); + +error: + if (err) + jz4780_ohci_detach(dev); + return (err); +} + +static int +jz4780_ohci_detach(device_t dev) +{ + struct jz4780_ohci_softc *sc = device_get_softc(dev); + device_t bdev; + + if (sc->sc_ohci.sc_bus.bdev) { + bdev = sc->sc_ohci.sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_children(dev); + + /* + * Put the controller into reset, then disable clocks and do + * the MI tear down. We have to disable the clocks/hardware + * after we do the rest of the teardown. We also disable the + * clocks in the opposite order we acquire them, but that + * doesn't seem to be absolutely necessary. We free up the + * clocks after we disable them, so the system could, in + * theory, reuse them. + */ + if (sc->sc_ohci.sc_io_res != NULL) { + bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, + OHCI_CONTROL, 0); + } + + if (sc->sc_ohci.sc_intr_hdl) { + bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); + sc->sc_ohci.sc_intr_hdl = NULL; + } + + if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { + /* + * only call ohci_detach() after ohci_init() + */ + ohci_detach(&sc->sc_ohci); + + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); + sc->sc_ohci.sc_irq_res = NULL; + } + if (sc->sc_ohci.sc_io_res) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.sc_io_res); + sc->sc_ohci.sc_io_res = NULL; + sc->sc_ohci.sc_io_tag = 0; + sc->sc_ohci.sc_io_hdl = 0; + } + + if (sc->clk != NULL) + clk_release(sc->clk); + + usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); + free(sc->gpio_vbus, M_DEVBUF); + return (0); +} + +static device_method_t ohci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_ohci_probe), + DEVMETHOD(device_attach, jz4780_ohci_attach), + DEVMETHOD(device_detach, jz4780_ohci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +static driver_t ohci_driver = { + .name = "ohci", + .methods = ohci_methods, + .size = sizeof(struct jz4780_ohci_softc), +}; + +static devclass_t ohci_devclass; + +DRIVER_MODULE(ohci, simplebus, ohci_driver, ohci_devclass, 0, 0); diff --git a/sys/mips/ingenic/jz4780_pinctrl.c b/sys/mips/ingenic/jz4780_pinctrl.c new file mode 100644 index 00000000000..adacb169147 --- /dev/null +++ b/sys/mips/ingenic/jz4780_pinctrl.c @@ -0,0 +1,260 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Ingenic JZ4780 pinctrl driver. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "jz4780_gpio_if.h" + +struct jz4780_pinctrl_softc { + struct simplebus_softc ssc; + device_t dev; +}; + +#define CHIP_REG_STRIDE 256 +#define CHIP_REG_OFFSET(base, chip) ((base) + (chip) * CHIP_REG_STRIDE) + +static int +jz4780_pinctrl_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-pinctrl")) + return (ENXIO); + + device_set_desc(dev, "Ingenic JZ4780 GPIO"); + + return (BUS_PROBE_DEFAULT); +} + +static int +jz4780_pinctrl_attach(device_t dev) +{ + struct jz4780_pinctrl_softc *sc; + struct resource_list *rs; + struct resource_list_entry *re; + phandle_t dt_parent, dt_child; + int i, ret; + + sc = device_get_softc(dev); + sc->dev = dev; + + /* + * Fetch our own resource list to dole memory between children + */ + rs = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); + if (rs == NULL) + return (ENXIO); + re = resource_list_find(rs, SYS_RES_MEMORY, 0); + if (re == NULL) + return (ENXIO); + + simplebus_init(dev, 0); + + /* Iterate over this node children, looking for pin controllers */ + dt_parent = ofw_bus_get_node(dev); + i = 0; + for (dt_child = OF_child(dt_parent); dt_child != 0; + dt_child = OF_peer(dt_child)) { + struct simplebus_devinfo *ndi; + device_t child; + bus_addr_t phys; + bus_size_t size; + + /* Add gpio controller child */ + if (!OF_hasprop(dt_child, "gpio-controller")) + continue; + child = simplebus_add_device(dev, dt_child, 0, NULL, -1, NULL); + if (child == NULL) + break; + /* Setup child resources */ + phys = CHIP_REG_OFFSET(re->start, i); + size = CHIP_REG_STRIDE; + if (phys + size - 1 <= re->end) { + ndi = device_get_ivars(child); + resource_list_add(&ndi->rl, SYS_RES_MEMORY, 0, + phys, phys + size - 1, size); + } + i++; + } + + ret = bus_generic_attach(dev); + if (ret == 0) { + fdt_pinctrl_register(dev, "ingenic,pins"); + fdt_pinctrl_configure_tree(dev); + } + return (ret); +} + +static int +jz4780_pinctrl_detach(device_t dev) +{ + + bus_generic_detach(dev); + return (0); +} + +struct jx4780_bias_prop { + const char *name; + uint32_t bias; +}; + +static struct jx4780_bias_prop jx4780_bias_table[] = { + { "bias-disable", 0 }, + { "bias-pull-up", GPIO_PIN_PULLUP }, + { "bias-pull-down", GPIO_PIN_PULLDOWN }, +}; + +static int +jz4780_pinctrl_parse_pincfg(phandle_t pincfgxref, uint32_t *bias_value) +{ + phandle_t pincfg_node; + int i; + + pincfg_node = OF_node_from_xref(pincfgxref); + for (i = 0; i < nitems(jx4780_bias_table); i++) { + if (OF_hasprop(pincfg_node, jx4780_bias_table[i].name)) { + *bias_value = jx4780_bias_table[i].bias; + return 0; + } + } + + return -1; +} + +static device_t +jz4780_pinctrl_chip_lookup(struct jz4780_pinctrl_softc *sc, phandle_t chipxref) +{ + device_t chipdev; + + chipdev = OF_device_from_xref(chipxref); + return chipdev; +} + +static int +jz4780_pinctrl_configure_pins(device_t dev, phandle_t cfgxref) +{ + struct jz4780_pinctrl_softc *sc = device_get_softc(dev); + device_t chip; + phandle_t node; + ssize_t i, len; + uint32_t *value, *pconf; + int result; + + node = OF_node_from_xref(cfgxref); + + len = OF_getencprop_alloc(node, "ingenic,pins", sizeof(uint32_t) * 4, + (void **)&value); + if (len < 0) { + device_printf(dev, + "missing ingenic,pins attribute in FDT\n"); + return (ENXIO); + } + + pconf = value; + result = EINVAL; + for (i = 0; i < len; i++, pconf += 4) { + uint32_t bias; + + /* Lookup the chip that handles this configuration */ + chip = jz4780_pinctrl_chip_lookup(sc, pconf[0]); + if (chip == NULL) { + device_printf(dev, + "invalid gpio controller reference in FDT\n"); + goto done; + } + + if (jz4780_pinctrl_parse_pincfg(pconf[3], &bias) != 0) { + device_printf(dev, + "invalid pin bias for pin %u on %s in FDT\n", + pconf[1], ofw_bus_get_name(chip)); + goto done; + } + + result = JZ4780_GPIO_CONFIGURE_PIN(chip, pconf[1], pconf[2], + bias); + if (result != 0) { + device_printf(dev, + "failed to configure pin %u on %s\n", pconf[1], + ofw_bus_get_name(chip)); + goto done; + } + } + + result = 0; +done: + free(value, M_OFWPROP); + return (result); +} + + +static device_method_t jz4780_pinctrl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_pinctrl_probe), + DEVMETHOD(device_attach, jz4780_pinctrl_attach), + DEVMETHOD(device_detach, jz4780_pinctrl_detach), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure, jz4780_pinctrl_configure_pins), + + DEVMETHOD_END +}; + +static devclass_t jz4780_pinctrl_devclass; +DEFINE_CLASS_1(pinctrl, jz4780_pinctrl_driver, jz4780_pinctrl_methods, + sizeof(struct jz4780_pinctrl_softc), simplebus_driver); +EARLY_DRIVER_MODULE(pinctrl, simplebus, jz4780_pinctrl_driver, + jz4780_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); diff --git a/sys/mips/ingenic/jz4780_pinctrl.h b/sys/mips/ingenic/jz4780_pinctrl.h new file mode 100644 index 00000000000..4aaa96e0001 --- /dev/null +++ b/sys/mips/ingenic/jz4780_pinctrl.h @@ -0,0 +1,33 @@ +/*- + * Copyright 2015 Alexander Kabaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _MIPS_INGENIC_JZ4780_PINCTRL_H +#define _MIPS_INGENIC_JZ4780_PINCTRL_H + + +#endif /* _MIPS_INGENIC_JZ4780_PINCTRL_H */ diff --git a/sys/mips/ingenic/jz4780_regs.h b/sys/mips/ingenic/jz4780_regs.h new file mode 100644 index 00000000000..221c2178492 --- /dev/null +++ b/sys/mips/ingenic/jz4780_regs.h @@ -0,0 +1,787 @@ +/* $NetBSD: ingenic_regs.h,v 1.22 2015/10/08 17:54:30 macallan Exp $ */ + +/*- + * Copyright (c) 2014 Michael Lorenz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef JZ4780_REGS_H +#define JZ4780_REGS_H + +/* for mips_wbflush() */ +#include + +/* UARTs, mostly 16550 compatible with 32bit spaced registers */ +#define JZ_UART0 0x10030000 +#define JZ_UART1 0x10031000 +#define JZ_UART2 0x10032000 +#define JZ_UART3 0x10033000 +#define JZ_UART4 0x10034000 + +/* LCD controller base addresses, registers are in jzfb_regs.h */ +#define JZ_LCDC0_BASE 0x13050000 +#define JZ_LCDC1_BASE 0x130a0000 + +/* TCU unit base address */ +#define JZ_TCU_BASE 0x10002000 + +/* Watchdog */ +#define JZ_WDOG_TDR 0x00000000 /* compare */ +#define JZ_WDOG_TCER 0x00000004 + #define TCER_ENABLE 0x01 /* enable counter */ +#define JZ_WDOG_TCNT 0x00000008 /* 16bit up count */ +#define JZ_WDOG_TCSR 0x0000000c + #define TCSR_PCK_EN 0x01 /* PCLK */ + #define TCSR_RTC_EN 0x02 /* RTCCLK - 32.768kHz */ + #define TCSR_EXT_EN 0x04 /* EXTCLK - 48MHz */ + #define TCSR_PRESCALE_M 0x38 + #define TCSR_DIV_1 0x00 + #define TCSR_DIV_4 0x08 + #define TCSR_DIV_16 0x10 + #define TCSR_DIV_64 0x18 + #define TCSR_DIV_256 0x20 + #define TCSR_DIV_1024 0x28 + +/* timers and PWMs */ +#define JZ_TC_TER 0x00000010 /* TC enable reg, ro */ +#define JZ_TC_TESR 0x00000014 /* TC enable set reg. */ + #define TESR_TCST0 0x0001 /* enable counter 0 */ + #define TESR_TCST1 0x0002 /* enable counter 1 */ + #define TESR_TCST2 0x0004 /* enable counter 2 */ + #define TESR_TCST3 0x0008 /* enable counter 3 */ + #define TESR_TCST4 0x0010 /* enable counter 4 */ + #define TESR_TCST5 0x0020 /* enable counter 5 */ + #define TESR_TCST6 0x0040 /* enable counter 6 */ + #define TESR_TCST7 0x0080 /* enable counter 7 */ + #define TESR_OST 0x8000 /* enable OST */ +#define JZ_TC_TECR 0x00000018 /* TC enable clear reg. */ +#define JZ_TC_TFR 0x00000020 + #define TFR_FFLAG0 0x00000001 /* channel 0 */ + #define TFR_FFLAG1 0x00000002 /* channel 1 */ + #define TFR_FFLAG2 0x00000004 /* channel 2 */ + #define TFR_FFLAG3 0x00000008 /* channel 3 */ + #define TFR_FFLAG4 0x00000010 /* channel 4 */ + #define TFR_FFLAG5 0x00000020 /* channel 5 */ + #define TFR_FFLAG6 0x00000040 /* channel 6 */ + #define TFR_FFLAG7 0x00000080 /* channel 7 */ + #define TFR_OSTFLAG 0x00008000 /* OS timer */ +#define JZ_TC_TFSR 0x00000024 /* timer flag set */ +#define JZ_TC_TFCR 0x00000028 /* timer flag clear */ +#define JZ_TC_TMR 0x00000030 /* timer flag mask */ + #define TMR_FMASK(n) (1 << (n)) + #define TMR_HMASK(n) (1 << ((n) + 16)) +#define JZ_TC_TMSR 0x00000034 /* timer flag mask set */ +#define JZ_TC_TMCR 0x00000038 /* timer flag mask clear*/ + +#define JZ_TC_TDFR(n) (0x00000040 + (n * 0x10)) /* FULL compare */ +#define JZ_TC_TDHR(n) (0x00000044 + (n * 0x10)) /* HALF compare */ +#define JZ_TC_TCNT(n) (0x00000048 + (n * 0x10)) /* count */ + +#define JZ_TC_TCSR(n) (0x0000004c + (n * 0x10)) +/* same bits as in JZ_WDOG_TCSR */ + +/* operating system timer */ +#define JZ_OST_DATA 0x000000e0 /* compare */ +#define JZ_OST_CNT_LO 0x000000e4 +#define JZ_OST_CNT_HI 0x000000e8 +#define JZ_OST_CTRL 0x000000ec + #define OSTC_PCK_EN 0x0001 /* use PCLK */ + #define OSTC_RTC_EN 0x0002 /* use RTCCLK */ + #define OSTC_EXT_EN 0x0004 /* use EXTCLK */ + #define OSTC_PRESCALE_M 0x0038 + #define OSTC_DIV_1 0x0000 + #define OSTC_DIV_4 0x0008 + #define OSTC_DIV_16 0x0010 + #define OSTC_DIV_64 0x0018 + #define OSTC_DIV_256 0x0020 + #define OSTC_DIV_1024 0x0028 + #define OSTC_SHUTDOWN 0x0200 + #define OSTC_MODE 0x8000 /* 0 - reset to 0 when = OST_DATA */ +#define JZ_OST_CNT_U32 0x000000fc /* copy of CNT_HI when reading CNT_LO */ + +static inline void +writereg(uint32_t reg, uint32_t val) +{ + *(volatile int32_t *)MIPS_PHYS_TO_KSEG1(reg) = val; + mips_wbflush(); +} + +static inline uint32_t +readreg(uint32_t reg) +{ + mips_wbflush(); + return *(volatile int32_t *)MIPS_PHYS_TO_KSEG1(reg); +} + +/* Clock management */ +#define JZ_CGU_BASE 0x10000000 + +#define JZ_CPCCR 0x00000000 /* Clock Control Register */ + #define JZ_PDIV_M 0x000f0000 /* PCLK divider mask */ + #define JZ_PDIV_S 16 /* PCLK divider shift */ + #define JZ_CDIV_M 0x0000000f /* CPU clock divider mask */ + #define JZ_CDIV_S 0 /* CPU clock divider shift */ +#define JZ_CPAPCR 0x00000010 /* APLL */ +#define JZ_CPMPCR 0x00000014 /* MPLL */ +#define JZ_CPEPCR 0x00000018 /* EPLL */ +#define JZ_CPVPCR 0x0000001C /* VPLL */ + #define JZ_PLLM_S 19 /* PLL multiplier shift */ + #define JZ_PLLM_M 0xfff80000 /* PLL multiplier mask */ + #define JZ_PLLN_S 13 /* PLL divider shift */ + #define JZ_PLLN_M 0x0007e000 /* PLL divider mask */ + #define JZ_PLLP_S 9 /* PLL postdivider shift */ + #define JZ_PLLP_M 0x00001700 /* PLL postdivider mask */ + #define JZ_PLLON 0x00000010 /* PLL is on and stable */ + #define JZ_PLLBP 0x00000002 /* PLL bypass */ + #define JZ_PLLEN 0x00000001 /* PLL enable */ +#define JZ_CLKGR0 0x00000020 /* Clock Gating Registers */ + #define CLK_NEMC (1 << 0) + #define CLK_BCH (1 << 1) + #define CLK_OTG0 (1 << 2) + #define CLK_MSC0 (1 << 3) + #define CLK_SSI0 (1 << 4) + #define CLK_SMB0 (1 << 5) + #define CLK_SMB1 (1 << 6) + #define CLK_SCC (1 << 7) + #define CLK_AIC (1 << 8) + #define CLK_TSSI0 (1 << 9) + #define CLK_OWI (1 << 10) + #define CLK_MSC1 (1 << 11) + #define CLK_MSC2 (1 << 12) + #define CLK_KBC (1 << 13) + #define CLK_SADC (1 << 14) + #define CLK_UART0 (1 << 15) + #define CLK_UART1 (1 << 16) + #define CLK_UART2 (1 << 17) + #define CLK_UART3 (1 << 18) + #define CLK_SSI1 (1 << 19) + #define CLK_SSI2 (1 << 20) + #define CLK_PDMA (1 << 21) + #define CLK_GPS (1 << 22) + #define CLK_MAC (1 << 23) + #define CLK_UHC (1 << 24) + #define CLK_SMB2 (1 << 25) + #define CLK_CIM (1 << 26) + #define CLK_TVE (1 << 27) + #define CLK_LCD (1 << 28) + #define CLK_IPU (1 << 29) + #define CLK_DDR0 (1 << 30) + #define CLK_DDR1 (1 << 31) +#define JZ_CLKGR1 0x00000028 /* Clock Gating Registers */ + #define CLK_SMB3 (1 << 0) + #define CLK_TSSI1 (1 << 1) + #define CLK_VPU (1 << 2) + #define CLK_PCM (1 << 3) + #define CLK_GPU (1 << 4) + #define CLK_COMPRESS (1 << 5) + #define CLK_AIC1 (1 << 6) + #define CLK_GPVLC (1 << 7) + #define CLK_OTG1 (1 << 8) + #define CLK_HDMI (1 << 9) + #define CLK_UART4 (1 << 10) + #define CLK_AHB_MON (1 << 11) + #define CLK_SMB4 (1 << 12) + #define CLK_DES (1 << 13) + #define CLK_X2D (1 << 14) + #define CLK_P1 (1 << 15) +#define JZ_DDCDR 0x0000002c /* DDR clock divider register */ +#define JZ_VPUCDR 0x00000030 /* VPU clock divider register */ +#define JZ_I2SCDR 0x00000060 /* I2S device clock divider register */ +#define JZ_I2S1CDR 0x000000a0 /* I2S device clock divider register */ +#define JZ_USBCDR 0x00000050 /* OTG PHY clock divider register */ +#define JZ_LP0CDR 0x00000054 /* LCD0 pix clock divider register */ +#define JZ_LP1CDR 0x00000064 /* LCD1 pix clock divider register */ +#define JZ_MSC0CDR 0x00000068 /* MSC0 clock divider register */ +#define JZ_MSC1CDR 0x000000a4 /* MSC1 clock divider register */ +#define JZ_MSC2CDR 0x000000a8 /* MSC2 clock divider register */ + #define MSCCDR_SCLK_A 0x40000000 + #define MSCCDR_MPLL 0x80000000 + #define MSCCDR_CE 0x20000000 + #define MSCCDR_BUSY 0x10000000 + #define MSCCDR_STOP 0x08000000 + #define MSCCDR_PHASE 0x00008000 /* 0 - 90deg phase, 1 - 180 */ + #define MSCCDR_DIV_M 0x000000ff /* src / ((div + 1) * 2) */ + #define UHCCDR_DIV_M 0x000000ff +#define JZ_UHCCDR 0x0000006c /* UHC 48M clock divider register */ + #define UHCCDR_SCLK_A 0x00000000 + #define UHCCDR_MPLL 0x40000000 + #define UHCCDR_EPLL 0x80000000 + #define UHCCDR_OTG_PHY 0xc0000000 + #define UHCCDR_CLK_MASK 0xc0000000 + #define UHCCDR_CE 0x20000000 + #define UHCCDR_BUSY 0x10000000 + #define UHCCDR_STOP 0x08000000 + #define UHCCDR_DIV_M 0x000000ff + #define UHCCDR_DIV(d) (d) +#define JZ_SSICDR 0x00000074 /* SSI clock divider register */ +#define JZ_CIMCDR 0x0000007c /* CIM MCLK clock divider register */ +#define JZ_PCMCDR 0x00000084 /* PCM device clock divider register */ +#define JZ_GPUCDR 0x00000088 /* GPU clock divider register */ +#define JZ_HDMICDR 0x0000008c /* HDMI clock divider register */ +#define JZ_BCHCDR 0x000000ac /* BCH clock divider register */ +#define JZ_CPM_INTR 0x000000b0 /* CPM interrupt register */ +#define JZ_CPM_INTRE 0x000000b4 /* CPM interrupt enable register */ +#define JZ_CPSPR 0x00000034 /* CPM scratch register */ +#define JZ_CPSRPR 0x00000038 /* CPM scratch protected register */ +#define JZ_USBPCR 0x0000003c /* USB parameter control register */ + #define PCR_USB_MODE 0x80000000 /* 1 - otg */ + #define PCR_AVLD_REG 0x40000000 + #define PCR_IDPULLUP_MASK 0x30000000 + #define PCR_INCR_MASK 0x08000000 + #define PCR_TCRISETUNE 0x04000000 + #define PCR_COMMONONN 0x02000000 + #define PCR_VBUSVLDEXT 0x01000000 + #define PCR_VBUSVLDEXTSEL 0x00800000 + #define PCR_POR 0x00400000 + #define PCR_SIDDQ 0x00200000 + #define PCR_OTG_DISABLE 0x00100000 + #define PCR_COMPDISTN_M 0x000e0000 + #define PCR_OTGTUNE 0x0001c000 + #define PCR_SQRXTUNE 0x00003800 + #define PCR_TXFSLSTUNE 0x00000780 + #define PCR_TXPREEMPHTUNE 0x00000040 + #define PCR_TXHSXVTUNE 0x00000030 + #define PCR_TXVREFTUNE 0x0000000f +#define JZ_USBRDT 0x00000040 /* Reset detect timer register */ + #define USBRDT_USBRDT_SHIFT 0 + #define USBRDT_USBRDT_WIDTH 23 + #define USBRDT_VBFIL_LD_EN 0x01000000 +#define JZ_USBVBFIL 0x00000044 /* USB jitter filter register */ + #define USBVBFIL_IDDIGFIL_SHIFT 16 + #define USBVBFIL_IDDIGFIL_WIDTH 16 + #define USBVBFIL_USBVBFIL_SHIFT 0 + #define USBVBFIL_USBVBFIL_WIDTH 16 +#define JZ_USBPCR1 0x00000048 /* USB parameter control register 1 */ + #define PCR_SYNOPSYS 0x10000000 /* Mentor mode otherwise */ + #define PCR_REFCLK_CORE 0x08000000 + #define PCR_REFCLK_XO25 0x04000000 + #define PCR_REFCLK_CO 0x00000000 + #define PCR_REFCLK_M 0x0c000000 + #define PCR_CLK_M 0x03000000 /* clock */ + #define PCR_CLK_192 0x03000000 /* 19.2MHz */ + #define PCR_CLK_48 0x02000000 /* 48MHz */ + #define PCR_CLK_24 0x01000000 /* 24MHz */ + #define PCR_CLK_12 0x00000000 /* 12MHz */ + #define PCR_DMPD1 0x00800000 /* pull down D- on port 1 */ + #define PCR_DPPD1 0x00400000 /* pull down D+ on port 1 */ + #define PCR_PORT0_RST 0x00200000 /* port 0 reset */ + #define PCR_PORT1_RST 0x00100000 /* port 1 reset */ + #define PCR_WORD_I_F0 0x00080000 /* 1: 16bit/30M, 8/60 otherw. */ + #define PCR_WORD_I_F1 0x00040000 /* same for port 1 */ + #define PCR_COMPDISTUNE 0x00038000 /* disconnect threshold */ + #define PCR_SQRXTUNE1 0x00007000 /* squelch threshold */ + #define PCR_TXFSLSTUNE1 0x00000f00 /* FS/LS impedance adj. */ + #define PCR_TXPREEMPH 0x00000080 /* HS transm. pre-emphasis */ + #define PCR_TXHSXVTUNE1 0x00000060 /* dp/dm voltage adj. */ + #define PCR_TXVREFTUNE1 0x00000017 /* HS DC voltage adj. */ + #define PCR_TXRISETUNE1 0x00000001 /* rise/fall wave adj. */ + +/* power manager */ +#define JZ_LPCR 0x00000004 + #define LPCR_PD_SCPU (1u << 31) /* CPU1 power down */ + #define LPCR_PD_VPU (1u << 30) /* VPU power down */ + #define LPCR_PD_GPU (1u << 29) /* GPU power down */ + #define LPCR_PD_GPS (1u << 28) /* GPS power down */ + #define LPCR_SCPUS (1u << 27) /* CPU1 power down status */ + #define LPCR_VPUS (1u << 26) /* VPU power down status */ + #define LPCR_GPUS (1u << 25) /* GPU power down status */ + #define LPCR_GPSS (1u << 24) /* GPS power down status */ + #define LPCR_GPU_IDLE (1u << 20) /* GPU idle status */ + #define LPCR_PST_SHIFT 8 /* Power stability time */ + #define LPCR_PST_MASK (0xFFFu << 8) + #define LPCR_DUTY_SHIFT 3 /* CPU clock duty */ + #define LPCR_DUTY_MASK (0x1Fu << 3) + #define LPCR_DOZE (1u << 2) /* Doze mode */ + #define LPCR_LPM_SHIFT 0 /* Low power mode */ + #define LPCR_LPM_MASK (0x03u << 0) + +#define JZ_OPCR 0x00000024 /* Oscillator Power Control Reg. */ + #define OPCR_IDLE_DIS 0x80000000 /* don't stop CPU clk on idle */ + #define OPCR_GPU_CLK_ST 0x40000000 /* stop GPU clock */ + #define OPCR_L2CM_M 0x0c000000 + #define OPCR_L2CM_ON 0x00000000 /* L2 stays on in sleep */ + #define OPCR_L2CM_RET 0x04000000 /* L2 retention mode in sleep */ + #define OPCR_L2CM_OFF 0x08000000 /* L2 powers down in sleep */ + #define OPCR_SPENDN0 0x00000080 /* 0 - OTG port forced down */ + #define OPCR_SPENDN1 0x00000040 /* 0 - UHC port forced down */ + #define OPCR_BUS_MODE 0x00000020 /* 1 - bursts */ + #define OPCR_O1SE 0x00000010 /* EXTCLK on in sleep */ + #define OPCR_PD 0x00000008 /* P0 down in sleep */ + #define OPCR_ERCS 0x00000004 /* 1 RTCCLK, 0 EXTCLK/512 */ + #define OPCR_CPU_MODE 0x00000002 /* 1 access 'accelerated' */ + #define OPCR_OSE 0x00000001 /* disable EXTCLK */ + +#define JZ_SPCR0 0x000000b8 /* SRAM Power Control Registers */ +#define JZ_SPCR1 0x000000bc +#define JZ_SRBC 0x000000c4 /* Soft Reset & Bus Control */ + #define SRBC_UHC_SR 0x00004000 /* UHC soft reset*/ + +/* + * random number generator + * + * Its function currently isn't documented by Ingenic. + * However, testing suggests that it works as expected. + */ +#define JZ_ERNG 0x000000d8 +#define JZ_RNG 0x000000dc + +/* Interrupt controller */ +#define JZ_ICBASE 0x10001000 /* IC base address */ +#define JZ_ICSR0 0x00000000 /* raw IRQ line status */ +#define JZ_ICMR0 0x00000004 /* IRQ mask, 1 masks IRQ */ +#define JZ_ICMSR0 0x00000008 /* sets bits in mask register */ +#define JZ_ICMCR0 0x0000000c /* clears bits in mask register */ +#define JZ_ICPR0 0x00000010 /* line status after masking */ + +#define JZ_ICSR1 0x00000020 /* raw IRQ line status */ +#define JZ_ICMR1 0x00000024 /* IRQ mask, 1 masks IRQ */ +#define JZ_ICMSR1 0x00000028 /* sets bits in mask register */ +#define JZ_ICMCR1 0x0000002c /* clears bits in maks register */ +#define JZ_ICPR1 0x00000030 /* line status after masking */ + +#define JZ_DSR0 0x00000034 /* source for PDMA */ +#define JZ_DMR0 0x00000038 /* mask for PDMA */ +#define JZ_DPR0 0x0000003c /* pending for PDMA */ + +#define JZ_DSR1 0x00000040 /* source for PDMA */ +#define JZ_DMR1 0x00000044 /* mask for PDMA */ +#define JZ_DPR1 0x00000048 /* pending for PDMA */ + +/* memory controller */ +#define JZ_DMMAP0 0x13010024 +#define JZ_DMMAP1 0x13010028 + #define DMMAP_BASE 0x0000ff00 /* base PADDR of memory chunk */ + #define DMMAP_MASK 0x000000ff /* mask which bits of PADDR are + * constant */ +/* USB controllers */ +#define JZ_EHCI_BASE 0x13490000 +#define JZ_EHCI_REG_UTMI_BUS 0x000000b0 + #define UTMI_BUS_WIDTH 0x00000040 +#define JZ_OHCI_BASE 0x134a0000 + +#define JZ_DWC2_BASE 0x13500000 +#define JZ_DWC2_GUSBCFG 0 + +/* Ethernet */ +#define JZ_DME_BASE 0x16000000 +#define JZ_DME_IO 0 +#define JZ_DME_DATA 2 + +/* GPIO */ +#define JZ_GPIO_A_BASE 0x10010000 +#define JZ_GPIO_B_BASE 0x10010100 +#define JZ_GPIO_C_BASE 0x10010200 +#define JZ_GPIO_D_BASE 0x10010300 +#define JZ_GPIO_E_BASE 0x10010400 +#define JZ_GPIO_F_BASE 0x10010500 + +/* GPIO registers per port */ +#define JZ_GPIO_PIN 0x00000000 /* pin level register */ +/* 0 - normal gpio, 1 - interrupt */ +#define JZ_GPIO_INT 0x00000010 /* interrupt register */ +#define JZ_GPIO_INTS 0x00000014 /* interrupt set register */ +#define JZ_GPIO_INTC 0x00000018 /* interrupt clear register */ +/* + * INT == 1: 1 disables interrupt + * INT == 0: device select, see below + */ +#define JZ_GPIO_MASK 0x00000020 /* port mask register */ +#define JZ_GPIO_MASKS 0x00000024 /* port mask set register */ +#define JZ_GPIO_MASKC 0x00000028 /* port mask clear register */ +/* + * INT == 1: 0 - level triggered, 1 - edge triggered + * INT == 0: 0 - device select, see below + */ +#define JZ_GPIO_PAT1 0x00000030 /* pattern 1 register */ +#define JZ_GPIO_PAT1S 0x00000034 /* pattern 1 set register */ +#define JZ_GPIO_PAT1C 0x00000038 /* pattern 1 clear register */ +/* + * INT == 1: + * PAT1 == 0: 0 - trigger on low, 1 - trigger on high + * PAT0 == 1: 0 - trigger on falling edge, 1 - trigger on rising edge + * INT == 0: + * MASK == 0: + * PAT1 == 0: 0 - device 0, 1 - device 1 + * PAT0 == 1: 0 - device 2, 1 - device 3 + * MASK == 1: + * PAT1 == 0: set gpio output + * PAT1 == 1: pin is input + */ +#define JZ_GPIO_PAT0 0x00000040 /* pattern 0 register */ +#define JZ_GPIO_PAT0S 0x00000044 /* pattern 0 set register */ +#define JZ_GPIO_PAT0C 0x00000048 /* pattern 0 clear register */ +/* 1 - interrupt happened */ +#define JZ_GPIO_FLAG 0x00000050 /* flag register */ +#define JZ_GPIO_FLAGC 0x00000058 /* flag clear register */ +/* 1 - disable pull up/down resistors */ +#define JZ_GPIO_DPULL 0x00000070 /* pull disable register */ +#define JZ_GPIO_DPULLS 0x00000074 /* pull disable set register */ +#define JZ_GPIO_DPULLC 0x00000078 /* pull disable clear register */ +/* the following are uncommented in the manual */ +#define JZ_GPIO_DRVL 0x00000080 /* drive low register */ +#define JZ_GPIO_DRVLS 0x00000084 /* drive low set register */ +#define JZ_GPIO_DRVLC 0x00000088 /* drive low clear register */ +#define JZ_GPIO_DIR 0x00000090 /* direction register */ +#define JZ_GPIO_DIRS 0x00000094 /* direction register */ +#define JZ_GPIO_DIRC 0x00000098 /* direction register */ +#define JZ_GPIO_DRVH 0x000000a0 /* drive high register */ +#define JZ_GPIO_DRVHS 0x000000a4 /* drive high set register */ +#define JZ_GPIO_DRVHC 0x000000a8 /* drive high clear register */ + +/* I2C / SMBus */ +#define JZ_SMB0_BASE 0x10050000 +#define JZ_SMB1_BASE 0x10051000 +#define JZ_SMB2_BASE 0x10052000 +#define JZ_SMB3_BASE 0x10053000 +#define JZ_SMB4_BASE 0x10054000 + +/* SMBus register offsets, per port */ +#define JZ_SMBCON 0x00 /* SMB control */ + #define JZ_STPHLD 0x80 /* Stop Hold Enable bit */ + #define JZ_SLVDIS 0x40 /* 1 - slave disabled */ + #define JZ_REST 0x20 /* 1 - allow RESTART */ + #define JZ_MATP 0x10 /* 1 - enable 10bit addr. for master */ + #define JZ_SATP 0x08 /* 1 - enable 10bit addr. for slave */ + #define JZ_SPD_M 0x06 /* bus speed control */ + #define JZ_SPD_100KB 0x02 /* 100kBit/s mode */ + #define JZ_SPD_400KB 0x04 /* 400kBit/s mode */ + #define JZ_MD 0x01 /* enable master */ +#define JZ_SMBTAR 0x04 /* SMB target address */ + #define JZ_SMATP 0x1000 /* enable 10bit master addr */ + #define JZ_SPECIAL 0x0800 /* 1 - special command */ + #define JZ_START 0x0400 /* 1 - send START */ + #define JZ_SMBTAR_M 0x03ff /* target address */ +#define JZ_SMBSAR 0x08 /* SMB slave address */ +#define JZ_SMBDC 0x10 /* SMB data buffer and command */ + #define JZ_CMD 0x100 /* 1 - read, 0 - write */ + #define JZ_DATA 0x0ff +#define JZ_SMBSHCNT 0x14 /* Standard speed SMB SCL high count */ +#define JZ_SMBSLCNT 0x18 /* Standard speed SMB SCL low count */ +#define JZ_SMBFHCNT 0x1C /* Fast speed SMB SCL high count */ +#define JZ_SMBFLCNT 0x20 /* Fast speed SMB SCL low count */ +#define JZ_SMBINTST 0x2C /* SMB Interrupt Status */ + #define JZ_ISTT 0x400 /* START or RESTART occured */ + #define JZ_ISTP 0x200 /* STOP occured */ + #define JZ_TXABT 0x40 /* ABORT occured */ + #define JZ_TXEMP 0x10 /* TX FIFO is low */ + #define JZ_TXOF 0x08 /* TX FIFO is high */ + #define JZ_RXFL 0x04 /* RX FIFO is at JZ_SMBRXTL*/ + #define JZ_RXOF 0x02 /* RX FIFO is high */ + #define JZ_RXUF 0x01 /* RX FIFO underflow */ +#define JZ_SMBINTM 0x30 /* SMB Interrupt Mask */ +#define JZ_SMBRXTL 0x38 /* SMB RxFIFO Threshold */ +#define JZ_SMBTXTL 0x3C /* SMB TxFIFO Threshold */ +#define JZ_SMBCINT 0x40 /* Clear Interrupts */ + #define JZ_CLEARALL 0x01 +#define JZ_SMBCRXUF 0x44 /* Clear RXUF Interrupt */ +#define JZ_SMBCRXOF 0x48 /* Clear RX_OVER Interrupt */ +#define JZ_SMBCTXOF 0x4C /* Clear TX_OVER Interrupt */ +#define JZ_SMBCRXREQ 0x50 /* Clear RDREQ Interrupt */ +#define JZ_SMBCTXABT 0x54 /* Clear TX_ABRT Interrupt */ +#define JZ_SMBCRXDN 0x58 /* Clear RX_DONE Interrupt */ +#define JZ_SMBCACT 0x5c /* Clear ACTIVITY Interrupt */ +#define JZ_SMBCSTP 0x60 /* Clear STOP Interrupt */ +#define JZ_SMBCSTT 0x64 /* Clear START Interrupt */ +#define JZ_SMBCGC 0x68 /* Clear GEN_CALL Interrupt */ +#define JZ_SMBENB 0x6C /* SMB Enable */ + #define JZ_ENABLE 0x01 +#define JZ_SMBST 0x70 /* SMB Status register */ + #define JZ_SLVACT 0x40 /* slave is active */ + #define JZ_MSTACT 0x20 /* master is active */ + #define JZ_RFF 0x10 /* RX FIFO is full */ + #define JZ_RFNE 0x08 /* RX FIFO not empty */ + #define JZ_TFE 0x04 /* TX FIFO is empty */ + #define JZ_TFNF 0x02 /* TX FIFO is not full */ + #define JZ_ACT 0x01 /* JZ_SLVACT | JZ_MSTACT */ +#define JZ_SMBABTSRC 0x80 /* SMB Transmit Abort Status Register */ +#define JZ_SMBDMACR 0x88 /* DMA Control Register */ +#define JZ_SMBDMATDL 0x8c /* DMA Transmit Data Level */ +#define JZ_SMBDMARDL 0x90 /* DMA Receive Data Level */ +#define JZ_SMBSDASU 0x94 /* SMB SDA Setup Register */ +#define JZ_SMBACKGC 0x98 /* SMB ACK General Call Register */ +#define JZ_SMBENBST 0x9C /* SMB Enable Status Register */ +#define JZ_SMBSDAHD 0xD0 /* SMB SDA HolD time Register */ + #define JZ_HDENB 0x100 /* enable hold time */ + +/* SD/MMC hosts */ +#define JZ_MSC0_BASE 0x13450000 +#define JZ_MSC1_BASE 0x13460000 +#define JZ_MSC2_BASE 0x13470000 + +#define JZ_MSC_CTRL 0x00 + #define JZ_SEND_CCSD 0x8000 + #define JZ_SEND_AS_CCSD 0x4000 + #define JZ_EXIT_MULTIPLE 0x0080 + #define JZ_EXIT_TRANSFER 0x0040 + #define JZ_START_READWAIT 0x0020 + #define JZ_STOP_READWAIT 0x0010 + #define JZ_RESET 0x0008 + #define JZ_START_OP 0x0004 + #define JZ_CLOCK_CTRL_M 0x0003 + #define JZ_CLOCK_START 0x0002 + #define JZ_CLOCK_STOP 0x0001 +#define JZ_MSC_STAT 0x04 + #define JZ_AUTO_CMD12_DONE 0x80000000 + #define JZ_AUTO_CMD23_DONE 0x40000000 + #define JZ_SVS 0x20000000 + #define JZ_PIN_LEVEL_M 0x1f000000 + #define JZ_BCE 0x00100000 /* boot CRC error */ + #define JZ_BDE 0x00080000 /* boot data end */ + #define JZ_BAE 0x00040000 /* boot acknowledge error */ + #define JZ_BAR 0x00020000 /* boot ack. received */ + #define JZ_DMAEND 0x00010000 + #define JZ_IS_RESETTING 0x00008000 + #define JZ_SDIO_INT_ACTIVE 0x00004000 + #define JZ_PRG_DONE 0x00002000 + #define JZ_DATA_TRAN_DONE 0x00001000 + #define JZ_END_CMD_RES 0x00000800 + #define JZ_DATA_FIFO_AFULL 0x00000400 + #define JZ_IS_READWAIT 0x00000200 + #define JZ_CLK_EN 0x00000100 + #define JZ_DATA_FIFO_FULL 0x00000080 + #define JZ_DATA_FIFO_EMPTY 0x00000040 + #define JZ_CRC_RES_ERR 0x00000020 + #define JZ_CRC_READ_ERR 0x00000010 + #define JZ_CRC_WRITE_ERR_M 0x0000000c + #define JZ_CRC_WRITE_OK 0x00000000 + #define JZ_CRC_CARD_ERR 0x00000004 + #define JZ_CRC_NO_STATUS 0x00000008 + #define JZ_TIME_OUT_RES 0x00000002 + #define JZ_TIME_OUT_READ 0x00000001 +#define JZ_MSC_CLKRT 0x08 + #define JZ_DEV_CLK 0x0 + #define JZ_DEV_CLK_2 0x1 /* DEV_CLK / 2 */ + #define JZ_DEV_CLK_4 0x2 /* DEV_CLK / 4 */ + #define JZ_DEV_CLK_8 0x3 /* DEV_CLK / 8 */ + #define JZ_DEV_CLK_16 0x4 /* DEV_CLK / 16 */ + #define JZ_DEV_CLK_32 0x5 /* DEV_CLK / 32 */ + #define JZ_DEV_CLK_64 0x6 /* DEV_CLK / 64 */ + #define JZ_DEV_CLK_128 0x7 /* DEV_CLK / 128 */ +#define JZ_MSC_CMDAT 0x0c + #define JZ_CCS_EXPECTED 0x80000000 + #define JZ_READ_CEATA 0x40000000 + #define JZ_DIS_BOOT 0x08000000 + #define JZ_ENA_BOOT 0x04000000 + #define JZ_EXP_BOOT_ACK 0x02000000 + #define JZ_BOOT_MODE 0x01000000 + #define JZ_AUTO_CMD23 0x00040000 + #define JZ_SDIO_PRDT 0x00020000 + #define JZ_AUTO_CMD12 0x00010000 + #define JZ_RTRG_M 0x0000c000 /* receive FIFO trigger */ + #define JZ_RTRG_16 0x00000000 /* >= 16 */ + #define JZ_RTRG_32 0x00004000 /* >= 32 */ + #define JZ_RTRG_64 0x00008000 /* >= 64 */ + #define JZ_RTRG_96 0x0000c000 /* >= 96 */ + #define JZ_TTRG_M 0x00003000 /* transmit FIFO trigger */ + #define JZ_TTRG_16 0x00000000 /* >= 16 */ + #define JZ_TTRG_32 0x00001000 /* >= 32 */ + #define JZ_TTRG_64 0x00002000 /* >= 64 */ + #define JZ_TTRG_96 0x00003000 /* >= 96 */ + #define JZ_IO_ABORT 0x00000800 + #define JZ_BUS_WIDTH_M 0x00000600 + #define JZ_BUS_1BIT 0x00000000 + #define JZ_BUS_4BIT 0x00000400 + #define JZ_BUS_8BIT 0x00000600 + #define JZ_INIT 0x00000080 /* send 80 clk init before cmd */ + #define JZ_BUSY 0x00000040 + #define JZ_STREAM 0x00000020 + #define JZ_WRITE 0x00000010 /* read otherwise */ + #define JZ_DATA_EN 0x00000008 + #define JZ_RESPONSE_M 0x00000007 /* response format */ + #define JZ_RES_NONE 0x00000000 + #define JZ_RES_R1 0x00000001 /* R1 and R1b */ + #define JZ_RES_R2 0x00000002 + #define JZ_RES_R3 0x00000003 + #define JZ_RES_R4 0x00000004 + #define JZ_RES_R5 0x00000005 + #define JZ_RES_R6 0x00000006 + #define JZ_RES_R7 0x00000007 +#define JZ_MSC_RESTO 0x10 /* 16bit response timeout in MSC_CLK */ +#define JZ_MSC_RDTO 0x14 /* 32bit read timeout in MSC_CLK */ +#define JZ_MSC_BLKLEN 0x18 /* 16bit block length */ +#define JZ_MSC_NOB 0x1c /* 16bit block counter */ +#define JZ_MSC_SNOB 0x20 /* 16bit successful block counter */ +#define JZ_MSC_IMASK 0x24 /* interrupt mask */ + #define JZ_INT_AUTO_CMD23_DONE 0x40000000 + #define JZ_INT_SVS 0x20000000 + #define JZ_INT_PIN_LEVEL_M 0x1f000000 + #define JZ_INT_BCE 0x00100000 + #define JZ_INT_BDE 0x00080000 + #define JZ_INT_BAE 0x00040000 + #define JZ_INT_BAR 0x00020000 + #define JZ_INT_DMAEND 0x00010000 + #define JZ_INT_AUTO_CMD12_DONE 0x00008000 + #define JZ_INT_DATA_FIFO_FULL 0x00004000 + #define JZ_INT_DATA_FIFO_EMPTY 0x00002000 + #define JZ_INT_CRC_RES_ERR 0x00001000 + #define JZ_INT_CRC_READ_ERR 0x00000800 + #define JZ_INT_CRC_WRITE_ERR 0x00000400 + #define JZ_INT_TIMEOUT_RES 0x00000200 + #define JZ_INT_TIMEOUT_READ 0x00000100 + #define JZ_INT_SDIO 0x00000080 + #define JZ_INT_TXFIFO_WR_REQ 0x00000040 + #define JZ_INT_RXFIFO_RD_REQ 0x00000020 + #define JZ_INT_END_CMD_RES 0x00000004 + #define JZ_INT_PRG_DONE 0x00000002 + #define JZ_INT_DATA_TRAN_DONE 0x00000001 +#define JZ_MSC_IFLG 0x28 /* interrupt flags */ +#define JZ_MSC_CMD 0x2c /* 6bit CMD index */ +#define JZ_MSC_ARG 0x30 /* 32bit argument */ +#define JZ_MSC_RES 0x34 /* 8x16bit response data FIFO */ +#define JZ_MSC_RXFIFO 0x38 +#define JZ_MSC_TXFIFO 0x3c +#define JZ_MSC_LPM 0x40 + #define JZ_DRV_SEL_M 0xc0000000 + #define JZ_FALLING_EDGE 0x00000000 + #define JZ_RISING_1NS 0x40000000 /* 1ns delay */ + #define JZ_RISING_4 0x80000000 /* 1/4 MSC_CLK delay */ + #define JZ_SMP_SEL 0x20000000 /* 1 - rising edge */ + #define JZ_LPM 0x00000001 /* low power mode */ +#define JZ_MSC_DMAC 0x44 + #define JZ_MODE_SEL 0x80 /* 1 - specify transfer length */ + #define JZ_AOFST_M 0x60 /* address offset in bytes */ + #define JZ_AOFST_S 6 /* addrress offset shift */ + #define JZ_ALIGNEN 0x10 /* allow non-32bit-aligned transfers */ + #define JZ_INCR_M 0x0c /* burst type */ + #define JZ_INCR_16 0x00 + #define JZ_INCR_32 0x04 + #define JZ_INCR_64 0x08 + #define JZ_DMASEL 0x02 /* 1 - SoC DMAC, 0 - MSC built-in */ + #define JZ_DMAEN 0x01 /* enable DMA */ +#define JZ_MSC_DMANDA 0x48 /* next descriptor paddr */ +#define JZ_MSC_DMADA 0x4c /* current descriptor */ +#define JZ_MSC_DMALEN 0x50 /* transfer tength */ +#define JZ_MSC_DMACMD 0x54 + #define JZ_DMA_IDI_M 0xff000000 + #define JZ_DMA_ID_M 0x00ff0000 + #define JZ_DMA_AOFST_M 0x00000600 + #define JZ_DMA_ALIGN 0x00000100 + #define JZ_DMA_ENDI 0x00000002 + #define JZ_DMA_LINK 0x00000001 +#define JZ_MSC_CTRL2 0x58 + #define JZ_PIP 0x1f000000 /* 1 - intr trigger on high */ + #define JZ_RST_EN 0x00800000 + #define JZ_STPRM 0x00000010 + #define JZ_SVC 0x00000008 + #define JZ_SMS_M 0x00000007 + #define JZ_SMS_DEF 0x00000000 /* default speed */ + #define JZ_SMS_HIGH 0x00000001 /* high speed */ + #define JZ_SMS_SDR12 0x00000002 + #define JZ_SMS_SDR25 0x00000003 + #define JZ_SMS_SDR50 0x00000004 +#define JZ_MSC_RTCNT 0x5c /* RT FIFO count */ + +/* EFUSE Slave Interface */ +#define JZ_EFUSE 0x134100D0 +#define JZ_EFUCTRL 0x00 + #define JZ_EFUSE_BANK 0x40000000 /* select upper 4KBit */ + #define JZ_EFUSE_ADDR_M 0x3fe00000 /* in bytes */ + #define JZ_EFUSE_ADDR_SHIFT 21 + #define JZ_EFUSE_SIZE_M 0x001f0000 /* in bytes */ + #define JZ_EFUSE_SIZE_SHIFT 16 + #define JZ_EFUSE_PROG 0x00008000 /* enable programming */ + #define JZ_EFUSE_WRITE 0x00000002 /* write enable */ + #define JZ_EFUSE_READ 0x00000001 /* read enable */ +#define JZ_EFUCFG 0x04 + #define JZ_EFUSE_INT_E 0x80000000 /* which IRQ? */ + #define JZ_EFUSE_RD_ADJ_M 0x00f00000 + #define JZ_EFUSE_RD_STROBE 0x000f0000 + #define JZ_EFUSE_WR_ADJUST 0x0000f000 + #define JZ_EFUSE_WR_STROBE 0x00000fff +#define JZ_EFUSTATE 0x08 + #define JZ_EFUSE_GLOBAL_P 0x00008000 /* wr protect bits */ + #define JZ_EFUSE_CHIPID_P 0x00004000 + #define JZ_EFUSE_CUSTID_P 0x00002000 + #define JZ_EFUSE_SECWR_EN 0x00001000 + #define JZ_EFUSE_PC_P 0x00000800 + #define JZ_EFUSE_HDMIKEY_P 0x00000400 + #define JZ_EFUSE_SECKEY_P 0x00000200 + #define JZ_EFUSE_SECBOOT_EN 0x00000100 + #define JZ_EFUSE_HDMI_BUSY 0x00000004 + #define JZ_EFUSE_WR_DONE 0x00000002 + #define JZ_EFUSE_RD_DONE 0x00000001 +#define JZ_EFUDATA0 0x0C +#define JZ_EFUDATA1 0x10 +#define JZ_EFUDATA2 0x14 +#define JZ_EFUDATA3 0x18 +#define JZ_EFUDATA4 0x1C +#define JZ_EFUDATA5 0x20 +#define JZ_EFUDATA6 0x24 +#define JZ_EFUDATA7 0x28 + +/* NEMC */ +#define JZ_NEMC_BASE 0x13410000 +#define JZ_NEMC_SMCR(n) (0x10 + (n) * 4) + +# define JZ_NEMC_SMCR_SMT_SHIFT 0 +# define JZ_NEMC_SMCR_SMT_WIDTH 1 +# define JZ_NEMC_SMCR_SMT_MASK (((1 << JZ_NEMC_SMCR_SMT_WIDTH) - 1) << JZ_NEMC_SMCR_SMT_SHIFT) +# define JZ_NEMC_SMCR_SMT_NORMAL (0 << JZ_NEMC_SMCR_SMT_SHIFT) +# define JZ_NEMC_SMCR_SMT_BROM (1 << JZ_NEMC_SMCR_SMT_SHIFT) + +# define JZ_NEMC_SMCR_BL_SHIFT 1 +# define JZ_NEMC_SMCR_BL_WIDTH 2 +# define JZ_NEMC_SMCR_BL_MASK (((1 << JZ_NEMC_SMCR_BL_WIDTH) - 1) << JZ_NEMC_SMCR_BL_SHIFT) +# define JZ_NEMC_SMCR_BL(n) (((n) << JZ_NEMC_SMCR_BL_SHIFT) + +# define JZ_NEMC_SMCR_BW_SHIFT 6 +# define JZ_NEMC_SMCR_BW_WIDTH 2 +# define JZ_NEMC_SMCR_BW_MASK (((1 << JZ_NEMC_SMCR_BW_WIDTH) - 1) << JZ_NEMC_SMCR_BW_SHIFT) +# define JZ_NEMC_SMCR_BW_8 (0 << JZ_NEMC_SMCR_BW_SHIFT) + +# define JZ_NEMC_SMCR_TAS_SHIFT 8 +# define JZ_NEMC_SMCR_TAS_WIDTH 4 +# define JZ_NEMC_SMCR_TAS_MASK (((1 << JZ_NEMC_SMCR_TAS_WIDTH) - 1) << JZ_NEMC_SMCR_TAS_SHIFT) + +# define JZ_NEMC_SMCR_TAH_SHIFT 12 +# define JZ_NEMC_SMCR_TAH_WIDTH 4 +# define JZ_NEMC_SMCR_TAH_MASK (((1 << JZ_NEMC_SMCR_TAH_WIDTH) - 1) << JZ_NEMC_SMCR_TAH_SHIFT) + +# define JZ_NEMC_SMCR_TBP_SHIFT 16 +# define JZ_NEMC_SMCR_TBP_WIDTH 4 +# define JZ_NEMC_SMCR_TBP_MASK (((1 << JZ_NEMC_SMCR_TBP_WIDTH) - 1) << JZ_NEMC_SMCR_TBP_SHIFT) + +# define JZ_NEMC_SMCR_TAW_SHIFT 20 +# define JZ_NEMC_SMCR_TAW_WIDTH 4 +# define JZ_NEMC_SMCR_TAW_MASK (((1 << JZ_NEMC_SMCR_TAW_WIDTH) - 1) << JZ_NEMC_SMCR_TAW_SHIFT) + +# define JZ_NEMC_SMCR_STRV_SHIFT 24 +# define JZ_NEMC_SMCR_STRV_WIDTH 4 +# define JZ_NEMC_SMCR_STRV_MASK (((1 << JZ_NEMC_SMCR_STRV_WIDTH) - 1) << JZ_NEMC_SMCR_STRV_SHIFT) + +#define JZ_NEMC_SACR(n) (0x30 + (n) * 4) + +# define JZ_NEMC_SACR_MASK_SHIFT 0 +# define JZ_NEMC_SACR_MASK_WIDTH 8 +# define JZ_NEMC_SACR_MASK_MASK (((1 << JZ_NEMC_SACR_MASK_WIDTH) - 1) << JZ_NEMC_SACR_MASK_SHIFT) + +# define JZ_NEMC_SACR_ADDR_SHIFT 0 +# define JZ_NEMC_SACR_ADDR_WIDTH 8 +# define JZ_NEMC_SACR_ADDR_MASK (((1 << JZ_NEMC_SACR_ADDR_WIDTH) - 1) << JZ_NEMC_SACR_ADDR_SHIFT) + +#define JC_NEMC_NFSCR 0x50 + +#endif /* JZ4780_REGS_H */ diff --git a/sys/mips/ingenic/jz4780_timer.c b/sys/mips/ingenic/jz4780_timer.c new file mode 100644 index 00000000000..e3cd04f068b --- /dev/null +++ b/sys/mips/ingenic/jz4780_timer.c @@ -0,0 +1,337 @@ +/*- + * Copyright 2013-2015 Alexander Kabaev + * Copyright 2013-2015 John Wehle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +struct jz4780_timer_softc { + device_t dev; + struct resource * res[4]; + void * ih_cookie; + struct eventtimer et; + struct timecounter tc; +}; + +static struct resource_spec jz4780_timer_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, /* OST */ + { SYS_RES_IRQ, 1, RF_ACTIVE }, /* TC5 */ + { SYS_RES_IRQ, 2, RF_ACTIVE }, /* TC0-4,6 */ + { -1, 0 } +}; + +/* + * devclass_get_device / device_get_softc could be used + * to dynamically locate this, however the timers are a + * required device which can't be unloaded so there's + * no need for the overhead. + */ +static struct jz4780_timer_softc *jz4780_timer_sc = NULL; + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) + +static unsigned +jz4780_get_timecount(struct timecounter *tc) +{ + struct jz4780_timer_softc *sc = + (struct jz4780_timer_softc *)tc->tc_priv; + + return CSR_READ_4(sc, JZ_OST_CNT_LO); +} + +static int +jz4780_hardclock(void *arg) +{ + struct jz4780_timer_softc *sc = (struct jz4780_timer_softc *)arg; + + CSR_WRITE_4(sc, JZ_TC_TFCR, TFR_FFLAG5); + CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5); + + if (sc->et.et_active) + sc->et.et_event_cb(&sc->et, sc->et.et_arg); + + return (FILTER_HANDLED); +} + +static int +jz4780_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) +{ + struct jz4780_timer_softc *sc = + (struct jz4780_timer_softc *)et->et_priv; + uint32_t ticks; + + ticks = (first * et->et_frequency) / SBT_1S; + if (ticks == 0) + return (EINVAL); + + CSR_WRITE_4(sc, JZ_TC_TDFR(5), ticks); + CSR_WRITE_4(sc, JZ_TC_TCNT(5), 0); + CSR_WRITE_4(sc, JZ_TC_TESR, TESR_TCST5); + + return (0); +} + +static int +jz4780_timer_stop(struct eventtimer *et) +{ + struct jz4780_timer_softc *sc = + (struct jz4780_timer_softc *)et->et_priv; + + CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5); + return (0); +} + +static int +jz4780_timer_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-tcu")) + return (ENXIO); + + device_set_desc(dev, "Ingenic JZ4780 timer"); + + return (BUS_PROBE_DEFAULT); +} + +static int +jz4780_timer_attach(device_t dev) +{ + struct jz4780_timer_softc *sc = device_get_softc(dev); + pcell_t counter_freq; + clk_t clk; + + /* There should be exactly one instance. */ + if (jz4780_timer_sc != NULL) + return (ENXIO); + + sc->dev = dev; + + if (bus_alloc_resources(dev, jz4780_timer_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + counter_freq = 0; + if (clk_get_by_name(dev, "ext", &clk) == 0) { + uint64_t clk_freq; + + if (clk_get_freq(clk, &clk_freq) == 0) + counter_freq = (uint32_t)clk_freq / 16; + clk_release(clk); + } + if (counter_freq == 0) { + device_printf(dev, "unable to determine ext clock frequency\n"); + /* Hardcode value we 'know' is correct */ + counter_freq = 48000000 / 16; + } + + /* + * Disable the timers, select the input for each timer, + * clear and then start OST. + */ + + /* Stop OST, if it happens to be running */ + CSR_WRITE_4(sc, JZ_TC_TECR, TESR_OST); + /* Stop all other channels as well */ + CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST0 | TESR_TCST1 | TESR_TCST2 | + TESR_TCST3 | TESR_TCST4 | TESR_TCST5 | TESR_TCST6 | TESR_TCST3); + /* Clear detect mask flags */ + CSR_WRITE_4(sc, JZ_TC_TFCR, 0xFFFFFFFF); + /* Mask all interrupts */ + CSR_WRITE_4(sc, JZ_TC_TMSR, 0xFFFFFFFF); + + /* Init counter with known data */ + CSR_WRITE_4(sc, JZ_OST_CTRL, 0); + CSR_WRITE_4(sc, JZ_OST_CNT_LO, 0); + CSR_WRITE_4(sc, JZ_OST_CNT_HI, 0); + CSR_WRITE_4(sc, JZ_OST_DATA, 0xffffffff); + + /* Configure counter for external clock */ + CSR_WRITE_4(sc, JZ_OST_CTRL, OSTC_EXT_EN | OSTC_MODE | OSTC_DIV_16); + + /* Start the counter again */ + CSR_WRITE_4(sc, JZ_TC_TESR, TESR_OST); + + /* Configure TCU channel 5 similarly to OST and leave it disabled */ + CSR_WRITE_4(sc, JZ_TC_TCSR(5), TCSR_EXT_EN | TCSR_DIV_16); + CSR_WRITE_4(sc, JZ_TC_TMCR, TMR_FMASK(5)); + + if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_CLK, + jz4780_hardclock, NULL, sc, &sc->ih_cookie)) { + device_printf(dev, "could not setup interrupt handler\n"); + bus_release_resources(dev, jz4780_timer_spec, sc->res); + return (ENXIO); + } + + sc->et.et_name = "JZ4780 TCU5"; + sc->et.et_flags = ET_FLAGS_ONESHOT; + sc->et.et_frequency = counter_freq; + sc->et.et_quality = 1000; + sc->et.et_min_period = (0x00000002LLU * SBT_1S) / sc->et.et_frequency; + sc->et.et_max_period = (0x0000fffeLLU * SBT_1S) / sc->et.et_frequency; + sc->et.et_start = jz4780_timer_start; + sc->et.et_stop = jz4780_timer_stop; + sc->et.et_priv = sc; + + et_register(&sc->et); + + sc->tc.tc_get_timecount = jz4780_get_timecount; + sc->tc.tc_name = "JZ4780 OST"; + sc->tc.tc_frequency = counter_freq; + sc->tc.tc_counter_mask = ~0u; + sc->tc.tc_quality = 1000; + sc->tc.tc_priv = sc; + + tc_init(&sc->tc); + + /* Now when tc is initialized, allow DELAY to find it */ + jz4780_timer_sc = sc; + + return (0); +} + +static int +jz4780_timer_detach(device_t dev) +{ + + return (EBUSY); +} + +static device_method_t jz4780_timer_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_timer_probe), + DEVMETHOD(device_attach, jz4780_timer_attach), + DEVMETHOD(device_detach, jz4780_timer_detach), + + DEVMETHOD_END +}; + +static driver_t jz4780_timer_driver = { + "timer", + jz4780_timer_methods, + sizeof(struct jz4780_timer_softc), +}; + +static devclass_t jz4780_timer_devclass; + +EARLY_DRIVER_MODULE(timer, simplebus, jz4780_timer_driver, + jz4780_timer_devclass, 0, 0, BUS_PASS_TIMER); + +void +DELAY(int usec) +{ + uint32_t counter; + uint32_t delta, now, previous, remaining; + + /* Timer has not yet been initialized */ + if (jz4780_timer_sc == NULL) { + for (; usec > 0; usec--) + for (counter = 200; counter > 0; counter--) { + /* Prevent gcc from optimizing out the loop */ + mips_rd_cause(); + } + return; + } + + /* + * Some of the other timers in the source tree do this calculation as: + * + * usec * ((sc->tc.tc_frequency / 1000000) + 1) + * + * which gives a fairly pessimistic result when tc_frequency is an exact + * multiple of 1000000. Given the data type and typical values for + * tc_frequency adding 999999 shouldn't overflow. + */ + remaining = usec * ((jz4780_timer_sc->tc.tc_frequency + 999999) / + 1000000); + + /* + * We add one since the first iteration may catch the counter just + * as it is changing. + */ + remaining += 1; + + previous = jz4780_get_timecount(&jz4780_timer_sc->tc); + + for ( ; ; ) { + now = jz4780_get_timecount(&jz4780_timer_sc->tc); + + /* + * If the timer has rolled over, then we have the case: + * + * if (previous > now) { + * delta = (0 - previous) + now + * } + * + * which is really no different then the normal case. + * Both cases are simply: + * + * delta = now - previous. + */ + delta = now - previous; + + if (delta >= remaining) + break; + + previous = now; + remaining -= delta; + } +} + +void +platform_initclocks(void) +{ + +} + diff --git a/sys/mips/ingenic/jz4780_uart.c b/sys/mips/ingenic/jz4780_uart.c new file mode 100644 index 00000000000..cf2e8a73fbc --- /dev/null +++ b/sys/mips/ingenic/jz4780_uart.c @@ -0,0 +1,220 @@ +/*- + * Copyright (c) 2013 Ian Lepore + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "opt_platform.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "uart_if.h" + +/* + * High-level UART interface. + */ +struct jz4780_uart_softc { + struct ns8250_softc ns8250_base; + clk_t clk_mod; + clk_t clk_baud; +}; + +static int +jz4780_bus_attach(struct uart_softc *sc) +{ + struct ns8250_softc *ns8250; + struct uart_bas *bas; + int rv; + + ns8250 = (struct ns8250_softc *)sc; + bas = &sc->sc_bas; + + rv = ns8250_bus_attach(sc); + if (rv != 0) + return (0); + + /* Configure uart to use extra IER_RXTMOUT bit */ + ns8250->ier_rxbits = IER_RXTMOUT | IER_EMSC | IER_ERLS | IER_ERXRDY; + ns8250->ier_mask = ~(ns8250->ier_rxbits); + ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; + ns8250->ier |= ns8250->ier_rxbits; + uart_setreg(bas, REG_IER, ns8250->ier); + uart_barrier(bas); + return (0); +} + +static kobj_method_t jz4780_uart_methods[] = { + KOBJMETHOD(uart_probe, ns8250_bus_probe), + KOBJMETHOD(uart_attach, jz4780_bus_attach), + KOBJMETHOD(uart_detach, ns8250_bus_detach), + KOBJMETHOD(uart_flush, ns8250_bus_flush), + KOBJMETHOD(uart_getsig, ns8250_bus_getsig), + KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), + KOBJMETHOD(uart_ipend, ns8250_bus_ipend), + KOBJMETHOD(uart_param, ns8250_bus_param), + KOBJMETHOD(uart_receive, ns8250_bus_receive), + KOBJMETHOD(uart_setsig, ns8250_bus_setsig), + KOBJMETHOD(uart_transmit, ns8250_bus_transmit), + KOBJMETHOD(uart_grab, ns8250_bus_grab), + KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab), + KOBJMETHOD_END +}; + +static struct uart_class jz4780_uart_class = { + "jz4780_uart_class", + jz4780_uart_methods, + sizeof(struct jz4780_uart_softc), + .uc_ops = &uart_ns8250_ops, + .uc_range = 8, + .uc_rclk = 0, +}; + +/* Compatible devices. */ +static struct ofw_compat_data compat_data[] = { + {"ingenic,jz4780-uart", (uintptr_t)&jz4780_uart_class}, + {NULL, (uintptr_t)NULL}, +}; + +UART_FDT_CLASS(compat_data); + +/* + * UART Driver interface. + */ +static int +jz4780_uart_get_shift(device_t dev) +{ + phandle_t node; + pcell_t shift; + + node = ofw_bus_get_node(dev); + if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) + shift = 2; + return ((int)shift); +} + +static int +jz4780_uart_probe(device_t dev) +{ + struct jz4780_uart_softc *sc; + uint64_t freq; + int shift; + int rv; + const struct ofw_compat_data *cd; + + sc = device_get_softc(dev); + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + cd = ofw_bus_search_compatible(dev, compat_data); + if (cd->ocd_data == 0) + return (ENXIO); + + /* Figure out clock setup */ + rv = clk_get_by_ofw_name(dev, 0, "module", &sc->clk_mod); + if (rv != 0) { + device_printf(dev, "Cannot get UART clock: %d\n", rv); + return (ENXIO); + } + rv = clk_enable(sc->clk_mod); + if (rv != 0) { + device_printf(dev, "Cannot enable UART clock: %d\n", rv); + return (ENXIO); + } + rv = clk_get_by_ofw_name(dev, 0, "baud", &sc->clk_baud); + if (rv != 0) { + device_printf(dev, "Cannot get UART clock: %d\n", rv); + return (ENXIO); + } + rv = clk_enable(sc->clk_baud); + if (rv != 0) { + device_printf(dev, "Cannot enable UART clock: %d\n", rv); + return (ENXIO); + } + rv = clk_get_freq(sc->clk_baud, &freq); + if (rv != 0) { + device_printf(dev, "Cannot determine UART clock frequency: %d\n", rv); + return (ENXIO); + } + + if (bootverbose) + device_printf(dev, "got UART clock: %lld\n", freq); + sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data; + shift = jz4780_uart_get_shift(dev); + return (uart_bus_probe(dev, shift, (int)freq, 0, 0)); +} + +static int +jz4780_uart_detach(device_t dev) +{ + struct jz4780_uart_softc *sc; + int rv; + + rv = uart_bus_detach(dev); + if (rv != 0) + return (rv); + + sc = device_get_softc(dev); + if (sc->clk_mod != NULL) { + clk_release(sc->clk_mod); + } + if (sc->clk_baud != NULL) { + clk_release(sc->clk_baud); + } + return (0); +} + +static device_method_t jz4780_uart_bus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, jz4780_uart_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, jz4780_uart_detach), + { 0, 0 } +}; + +static driver_t jz4780_uart_driver = { + uart_driver_name, + jz4780_uart_bus_methods, + sizeof(struct jz4780_uart_softc), +}; + +DRIVER_MODULE(jz4780_uart, simplebus, jz4780_uart_driver, uart_devclass, + 0, 0); From 6193edf4c552b1c5a3eaeb0a06419adf79ff4f7f Mon Sep 17 00:00:00 2001 From: Ruslan Bukin Date: Sat, 19 Nov 2016 17:51:02 +0000 Subject: [PATCH 15/17] Restore dd changes included accidentally in r308857. --- bin/dd/dd.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/bin/dd/dd.c b/bin/dd/dd.c index 56f8efef7a5..a13213dcc8c 100644 --- a/bin/dd/dd.c +++ b/bin/dd/dd.c @@ -48,10 +48,13 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include +#include #include +#include #include #include #include @@ -92,6 +95,10 @@ main(int argc __unused, char *argv[]) jcl(argv); setup(); + caph_cache_catpages(); + if (cap_enter() == -1 && errno != ENOSYS) + err(1, "unable to enter capability mode"); + (void)signal(SIGINFO, siginfo_handler); (void)signal(SIGINT, terminate); @@ -125,6 +132,8 @@ static void setup(void) { u_int cnt; + cap_rights_t rights; + unsigned long cmds[] = { FIODTYPE, MTIOCTOP }; if (in.name == NULL) { in.name = "stdin"; @@ -133,13 +142,20 @@ setup(void) in.fd = open(in.name, O_RDONLY, 0); if (in.fd == -1) err(1, "%s", in.name); + if (caph_limit_stdin() == -1) + err(1, "unable to limit capability rights"); } getfdtype(&in); + cap_rights_init(&rights, CAP_READ, CAP_SEEK); + if (cap_rights_limit(in.fd, &rights) == -1 && errno != ENOSYS) + err(1, "unable to limit capability rights"); + if (files_cnt > 1 && !(in.flags & ISTAPE)) errx(1, "files is not supported for non-tape devices"); + cap_rights_set(&rights, CAP_WRITE, CAP_FTRUNCATE, CAP_IOCTL); if (out.name == NULL) { /* No way to check for read access here. */ out.fd = STDOUT_FILENO; @@ -156,13 +172,27 @@ setup(void) if (out.fd == -1) { out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); out.flags |= NOREAD; + cap_rights_clear(&rights, CAP_READ); } if (out.fd == -1) err(1, "%s", out.name); + if (caph_limit_stdout() == -1) + err(1, "unable to limit capability rights"); } getfdtype(&out); + if (cap_rights_limit(out.fd, &rights) == -1 && errno != ENOSYS) + err(1, "unable to limit capability rights"); + if (cap_ioctls_limit(out.fd, cmds, nitems(cmds)) == -1 && + errno != ENOSYS) + err(1, "unable to limit capability rights"); + + if (in.fd != STDERR_FILENO && out.fd != STDERR_FILENO) { + if (caph_limit_stderr() == -1) + err(1, "unable to limit capability rights"); + } + /* * Allocate space for the input and output buffers. If not doing * record oriented I/O, only need a single buffer. From 4943459ce322c1f1d2624cb89adaefcd923b6103 Mon Sep 17 00:00:00 2001 From: Ruslan Bukin Date: Sat, 19 Nov 2016 18:03:46 +0000 Subject: [PATCH 16/17] Enable SMP on Ingenic JZ4780. It is required to proceed full cache flush before we can use wait instruction on multicore, so use nop instead for now. Submitted by: kan Sponsored by: DARPA, AFRL --- sys/mips/conf/JZ4780 | 2 +- sys/mips/mips/exception.S | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sys/mips/conf/JZ4780 b/sys/mips/conf/JZ4780 index 3df33a1cdb2..2b7335af226 100644 --- a/sys/mips/conf/JZ4780 +++ b/sys/mips/conf/JZ4780 @@ -53,7 +53,7 @@ options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required #options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed # Make an SMP-capable kernel by default -# options SMP # Symmetric MultiProcessor Kernel +options SMP # Symmetric MultiProcessor Kernel device loop device ether diff --git a/sys/mips/mips/exception.S b/sys/mips/mips/exception.S index 04614737ab9..7262a3665f6 100644 --- a/sys/mips/mips/exception.S +++ b/sys/mips/mips/exception.S @@ -590,7 +590,11 @@ GLOBAL(MipsWaitStart) # this is 16 byte aligned mtc0 t1, MIPS_COP_0_STATUS bnez v0, MipsWaitEnd nop +#if defined(CPU_XBURST) && defined(SMP) + nop +#else wait +#endif GLOBAL(MipsWaitEnd) # MipsWaitStart + 16 jr ra PTR_ADDU sp, sp, CALLFRAME_SIZ From 29aee14890edbe5929d6b3baa3f712dc2bc37d2c Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Sat, 19 Nov 2016 18:19:21 +0000 Subject: [PATCH 17/17] [dd] Revert the capsicum bits for now until it gets fixed. dd is a bootstrap tool and that header isn't installed as part of the bootstrap environment for previous releases (eg freebsd-10.) We'll figure it out in post and then re-commit it. --- bin/dd/dd.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/bin/dd/dd.c b/bin/dd/dd.c index a13213dcc8c..56f8efef7a5 100644 --- a/bin/dd/dd.c +++ b/bin/dd/dd.c @@ -48,13 +48,10 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include #include -#include #include -#include #include #include #include @@ -95,10 +92,6 @@ main(int argc __unused, char *argv[]) jcl(argv); setup(); - caph_cache_catpages(); - if (cap_enter() == -1 && errno != ENOSYS) - err(1, "unable to enter capability mode"); - (void)signal(SIGINFO, siginfo_handler); (void)signal(SIGINT, terminate); @@ -132,8 +125,6 @@ static void setup(void) { u_int cnt; - cap_rights_t rights; - unsigned long cmds[] = { FIODTYPE, MTIOCTOP }; if (in.name == NULL) { in.name = "stdin"; @@ -142,20 +133,13 @@ setup(void) in.fd = open(in.name, O_RDONLY, 0); if (in.fd == -1) err(1, "%s", in.name); - if (caph_limit_stdin() == -1) - err(1, "unable to limit capability rights"); } getfdtype(&in); - cap_rights_init(&rights, CAP_READ, CAP_SEEK); - if (cap_rights_limit(in.fd, &rights) == -1 && errno != ENOSYS) - err(1, "unable to limit capability rights"); - if (files_cnt > 1 && !(in.flags & ISTAPE)) errx(1, "files is not supported for non-tape devices"); - cap_rights_set(&rights, CAP_WRITE, CAP_FTRUNCATE, CAP_IOCTL); if (out.name == NULL) { /* No way to check for read access here. */ out.fd = STDOUT_FILENO; @@ -172,27 +156,13 @@ setup(void) if (out.fd == -1) { out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); out.flags |= NOREAD; - cap_rights_clear(&rights, CAP_READ); } if (out.fd == -1) err(1, "%s", out.name); - if (caph_limit_stdout() == -1) - err(1, "unable to limit capability rights"); } getfdtype(&out); - if (cap_rights_limit(out.fd, &rights) == -1 && errno != ENOSYS) - err(1, "unable to limit capability rights"); - if (cap_ioctls_limit(out.fd, cmds, nitems(cmds)) == -1 && - errno != ENOSYS) - err(1, "unable to limit capability rights"); - - if (in.fd != STDERR_FILENO && out.fd != STDERR_FILENO) { - if (caph_limit_stderr() == -1) - err(1, "unable to limit capability rights"); - } - /* * Allocate space for the input and output buffers. If not doing * record oriented I/O, only need a single buffer.