From 2e28f246643f29d130d205315a17f3b032a7ec62 Mon Sep 17 00:00:00 2001 From: trasz Date: Mon, 14 Mar 2016 18:57:09 +0000 Subject: Restore accidentaly removed comment line. MFC after: 1 month Sponsored by: The FreeBSD Foundation --- etc/autofs/special_media | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/autofs/special_media b/etc/autofs/special_media index 32f9478..65bcf5a 100755 --- a/etc/autofs/special_media +++ b/etc/autofs/special_media @@ -21,6 +21,7 @@ print_available() { _label="${_fstype_and_label#* }" # Replace plus signs and slashes with minuses; # leading plus signs have special meaning in maps, + # and multi-component keys are just not supported. _label="$(echo ${_label} | sed 's,[+/],-,g')" echo "${_label}" continue -- cgit v1.1 From 94a163b617efbd9e7c640cdb9221cbfa037cfada Mon Sep 17 00:00:00 2001 From: glebius Date: Mon, 14 Mar 2016 20:34:30 +0000 Subject: Revert r296868. The cast is useful to protect against passing incorrect argument type to the macro. Submitted by: rstone --- sys/sys/sysctl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 7c1e79c..ec40d29 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -640,11 +640,12 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); #define SYSCTL_ADD_COUNTER_U64(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ + counter_u64_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U64 | CTLFLAG_MPSAFE | (access), \ - ptr, 0, sysctl_handle_counter_u64, "QU", __DESCR(descr)); \ + __ptr, 0, sysctl_handle_counter_u64, "QU", __DESCR(descr)); \ }) /* Oid for an opaque object. Specified by a pointer and a length. */ -- cgit v1.1 From 33eb7d8e7fd01d6b104123f8572accc532773305 Mon Sep 17 00:00:00 2001 From: lidl Date: Mon, 14 Mar 2016 22:20:22 +0000 Subject: Do not truncate office phones in finger's summary listing When finger is invoked as as "finger username", it produces the long listing by default, and phones numbers are pretty-printed by the prphone() function. When invoked as just "finger", the same pretty-printing happens, but is truncated at 9 characters. Given the summary listing is already greater than 80 columns, making it even wider is of no harm. Approved by: rpaulo (mentor) Differential Revision: https://reviews.freebsd.org/D5638 --- usr.bin/finger/sprint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr.bin/finger/sprint.c b/usr.bin/finger/sprint.c index d9a6cef..afbab4e 100644 --- a/usr.bin/finger/sprint.c +++ b/usr.bin/finger/sprint.c @@ -149,7 +149,7 @@ office: else if (pn->officephone) (void)printf(" %-7.7s", " "); if (pn->officephone) - (void)printf(" %-.9s", + (void)printf(" %-.15s", prphone(pn->officephone)); } else (void)printf(" %.*s", MAXHOSTNAME, w->host); -- cgit v1.1 From 0cafb74055cf78382f1955e41fa7fbe836114373 Mon Sep 17 00:00:00 2001 From: glebius Date: Tue, 15 Mar 2016 00:05:00 +0000 Subject: Provide sysctl(9) macro to deal with array of counter(9). --- share/man/man9/counter.9 | 38 +++++++++++++++++++++++++++++++++++++- sys/kern/subr_counter.c | 24 ++++++++++++++++++++++++ sys/sys/sysctl.h | 21 +++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/share/man/man9/counter.9 b/share/man/man9/counter.9 index f703ccf..85bc9fe 100644 --- a/share/man/man9/counter.9 +++ b/share/man/man9/counter.9 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 7, 2014 +.Dd March 14, 2016 .Dt COUNTER 9 .Os .Sh NAME @@ -54,6 +54,8 @@ .In sys/sysctl.h .Fn SYSCTL_COUNTER_U64 parent nbr name access ptr descr .Fn SYSCTL_ADD_COUNTER_U64 ctx parent nbr name access ptr descr +.Fn SYSCTL_COUNTER_U64_ARRAY parent nbr name access ptr len descr +.Fn SYSCTL_ADD_COUNTER_U64_ARRAY ctx parent nbr name access ptr len descr .Sh DESCRIPTION .Nm is a generic facility to create counters @@ -150,6 +152,40 @@ argument should be a pointer to allocated A read of the oid returns value obtained through .Fn counter_u64_fetch . Any write to the oid zeroes it. +.It Fn SYSCTL_COUNTER_U64_ARRAY parent nbr name access ptr len descr +Declare a static +.Xr sysctl +oid that would represent an array of +.Nm . +The +.Fa ptr +argument should be a pointer to allocated array of +.Vt counter_u64_t's . +The +.Fa len +argument should specify number of elements in the array. +A read of the oid returns len-sized array of +.Vt uint64_t +values obtained through +.Fn counter_u64_fetch . +Any write to the oid zeroes all array elements. +.It Fn SYSCTL_ADD_COUNTER_U64_ARRAY ctx parent nbr name access ptr len descr +Create a +.Xr sysctl +oid that would represent an array of +.Nm . +The +.Fa ptr +argument should be a pointer to allocated array of +.Vt counter_u64_t's . +The +.Fa len +argument should specify number of elements in the array. +A read of the oid returns len-sized array of +.Vt uint64_t +values obtained through +.Fn counter_u64_fetch . +Any write to the oid zeroes all array elements. .El .Sh IMPLEMENTATION DETAILS On all architectures diff --git a/sys/kern/subr_counter.c b/sys/kern/subr_counter.c index ea2759c..bade8b8 100644 --- a/sys/kern/subr_counter.c +++ b/sys/kern/subr_counter.c @@ -94,3 +94,27 @@ sysctl_handle_counter_u64(SYSCTL_HANDLER_ARGS) return (0); } + +int +sysctl_handle_counter_u64_array(SYSCTL_HANDLER_ARGS) +{ + uint64_t *out; + int error; + + out = malloc(arg2 * sizeof(uint64_t), M_TEMP, M_WAITOK); + for (int i = 0; i < arg2; i++) + out[i] = counter_u64_fetch(((counter_u64_t *)arg1)[i]); + + error = SYSCTL_OUT(req, out, arg2 * sizeof(uint64_t)); + + if (error || !req->newptr) + return (error); + + /* + * Any write attempt to a counter zeroes it. + */ + for (int i = 0; i < arg2; i++) + counter_u64_zero(((counter_u64_t *)arg1)[i]); + + return (0); +} diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index ec40d29..34ee03c 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -204,6 +204,7 @@ int sysctl_handle_long(SYSCTL_HANDLER_ARGS); int sysctl_handle_string(SYSCTL_HANDLER_ARGS); int sysctl_handle_opaque(SYSCTL_HANDLER_ARGS); int sysctl_handle_counter_u64(SYSCTL_HANDLER_ARGS); +int sysctl_handle_counter_u64_array(SYSCTL_HANDLER_ARGS); int sysctl_handle_uma_zone_max(SYSCTL_HANDLER_ARGS); int sysctl_handle_uma_zone_cur(SYSCTL_HANDLER_ARGS); @@ -648,6 +649,26 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); __ptr, 0, sysctl_handle_counter_u64, "QU", __DESCR(descr)); \ }) +/* Oid for an array of counter(9)s. The pointer and length must be non zero. */ +#define SYSCTL_COUNTER_U64_ARRAY(parent, nbr, name, access, ptr, len, descr) \ + SYSCTL_OID(parent, nbr, name, \ + CTLTYPE_OPAQUE | CTLFLAG_MPSAFE | (access), \ + (ptr), (len), sysctl_handle_counter_u64_array, "S", descr); \ + CTASSERT(((access) & CTLTYPE) == 0 || \ + ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE) + +#define SYSCTL_ADD_COUNTER_U64_ARRAY(ctx, parent, nbr, name, access, \ + ptr, len, descr) \ +({ \ + counter_u64_t *__ptr = (ptr); \ + CTASSERT(((access) & CTLTYPE) == 0 || \ + ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE); \ + sysctl_add_oid(ctx, parent, nbr, name, \ + CTLTYPE_OPAQUE | CTLFLAG_MPSAFE | (access), \ + __ptr, len, sysctl_handle_counter_u64_array, "S", \ + __DESCR(descr)); \ +}) + /* Oid for an opaque object. Specified by a pointer and a length. */ #define SYSCTL_OPAQUE(parent, nbr, name, access, ptr, len, fmt, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ -- cgit v1.1 From 224119b2ed81616e221ae89688ff098941cfaa82 Mon Sep 17 00:00:00 2001 From: glebius Date: Tue, 15 Mar 2016 00:15:10 +0000 Subject: Redo r294869. The array of counters for TCP states doesn't belong to struct tcpstat, because the structure can be zeroed out by netstat(1) -z, and of course running connection counts shouldn't be touched. Place running connection counts into separate array, and provide separate read-only sysctl oid for it. --- contrib/bsnmp/snmp_mibII/mibII_tcp.c | 16 ++++++++++++++-- sys/netinet/tcp_input.c | 29 ++++++++++++++++++++++++++--- sys/netinet/tcp_subr.c | 10 +++++----- sys/netinet/tcp_syncache.c | 6 +++--- sys/netinet/tcp_timewait.c | 2 +- sys/netinet/tcp_usrreq.c | 2 +- sys/netinet/tcp_var.h | 14 ++++++++------ 7 files changed, 58 insertions(+), 21 deletions(-) diff --git a/contrib/bsnmp/snmp_mibII/mibII_tcp.c b/contrib/bsnmp/snmp_mibII/mibII_tcp.c index c798bae..913a537 100644 --- a/contrib/bsnmp/snmp_mibII/mibII_tcp.c +++ b/contrib/bsnmp/snmp_mibII/mibII_tcp.c @@ -47,6 +47,7 @@ struct tcp_index { static uint64_t tcp_tick; static uint64_t tcp_stats_tick; static struct tcpstat tcpstat; +static uint64_t tcps_states[TCP_NSTATES]; static struct xinpgen *xinpgen; static size_t xinpgen_len; static u_int tcp_total; @@ -78,6 +79,17 @@ fetch_tcp_stats(void) return (-1); } + len = sizeof(tcps_states); + if (sysctlbyname("net.inet.tcp.states", &tcps_states, &len, NULL, + 0) == -1) { + syslog(LOG_ERR, "net.inet.tcp.states: %m"); + return (-1); + } + if (len != sizeof(tcps_states)) { + syslog(LOG_ERR, "net.inet.tcp.states: wrong size"); + return (-1); + } + tcp_stats_tick = get_ticks(); return (0); @@ -231,8 +243,8 @@ op_tcp(struct snmp_context *ctx __unused, struct snmp_value *value, break; case LEAF_tcpCurrEstab: - value->v.uint32 = tcpstat.tcps_states[TCPS_ESTABLISHED] + - tcpstat.tcps_states[TCPS_CLOSE_WAIT]; + value->v.uint32 = tcps_states[TCPS_ESTABLISHED] + + tcps_states[TCPS_CLOSE_WAIT]; break; case LEAF_tcpInSegs: diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 102430d..16be9cb 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -235,16 +235,39 @@ VNET_DEFINE(struct inpcbhead, tcb); VNET_DEFINE(struct inpcbinfo, tcbinfo); /* - * TCP statistics are stored in an "array" of counter(9)s. + * TCP statistics are stored in an array of counter(9)s, which size matches + * size of struct tcpstat. TCP running connection count is a regular array. */ VNET_PCPUSTAT_DEFINE(struct tcpstat, tcpstat); -VNET_PCPUSTAT_SYSINIT(tcpstat); SYSCTL_VNET_PCPUSTAT(_net_inet_tcp, TCPCTL_STATS, stats, struct tcpstat, tcpstat, "TCP statistics (struct tcpstat, netinet/tcp_var.h)"); +VNET_DEFINE(counter_u64_t, tcps_states[TCP_NSTATES]); +SYSCTL_COUNTER_U64_ARRAY(_net_inet_tcp, TCPCTL_STATES, states, CTLFLAG_RD | + CTLFLAG_VNET, &VNET_NAME(tcps_states), TCP_NSTATES, + "TCP connection counts by TCP state"); + +static void +tcp_vnet_init(const void *unused) +{ + + COUNTER_ARRAY_ALLOC(VNET(tcps_states), TCP_NSTATES, M_WAITOK); + VNET_PCPUSTAT_ALLOC(tcpstat, M_WAITOK); +} +VNET_SYSINIT(tcp_vnet_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, + tcp_vnet_init, NULL); #ifdef VIMAGE -VNET_PCPUSTAT_SYSUNINIT(tcpstat); +static void +tcp_vnet_uninit(const void *unused) +{ + + COUNTER_ARRAY_FREE(VNET(tcps_states), TCP_NSTATES); + VNET_PCPUSTAT_FREE(tcpstat); +} +VNET_SYSUNINIT(tcp_vnet_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, + tcp_vnet_uninit, NULL); #endif /* VIMAGE */ + /* * Kernel module interface for updating tcpstat. The argument is an index * into tcpstat treated as an array. diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index ff218dd..6635bb7 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -1542,7 +1542,7 @@ tcp_close(struct tcpcb *tp) #endif in_pcbdrop(inp); TCPSTAT_INC(tcps_closed); - TCPSTAT_DEC(tcps_states[tp->t_state]); + TCPSTATES_DEC(tp->t_state); KASSERT(inp->inp_socket != NULL, ("tcp_close: inp_socket NULL")); so = inp->inp_socket; soisdisconnected(so); @@ -1665,7 +1665,7 @@ tcp_pcblist(SYSCTL_HANDLER_ARGS) */ if (req->oldptr == NULL) { n = V_tcbinfo.ipi_count + - TCPSTAT_FETCH(tcps_states[TCPS_SYN_RECEIVED]); + counter_u64_fetch(VNET(tcps_states)[TCPS_SYN_RECEIVED]); n += imax(n / 8, 10); req->oldidx = 2 * (sizeof xig) + n * sizeof(struct xtcpcb); return (0); @@ -1682,7 +1682,7 @@ tcp_pcblist(SYSCTL_HANDLER_ARGS) n = V_tcbinfo.ipi_count; INP_LIST_RUNLOCK(&V_tcbinfo); - m = TCPSTAT_FETCH(tcps_states[TCPS_SYN_RECEIVED]); + m = counter_u64_fetch(VNET(tcps_states)[TCPS_SYN_RECEIVED]); error = sysctl_wire_old_buffer(req, 2 * (sizeof xig) + (n + m) * sizeof(struct xtcpcb)); @@ -2986,8 +2986,8 @@ tcp_state_change(struct tcpcb *tp, int newstate) int pstate = tp->t_state; #endif - TCPSTAT_DEC(tcps_states[tp->t_state]); - TCPSTAT_INC(tcps_states[newstate]); + TCPSTATES_DEC(tp->t_state); + TCPSTATES_INC(newstate); tp->t_state = newstate; TCP_PROBE6(state__change, NULL, tp, NULL, tp, NULL, pstate); } diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c index 0ff7318..b898c49 100644 --- a/sys/netinet/tcp_syncache.c +++ b/sys/netinet/tcp_syncache.c @@ -351,7 +351,7 @@ syncache_insert(struct syncache *sc, struct syncache_head *sch) SCH_UNLOCK(sch); - TCPSTAT_INC(tcps_states[TCPS_SYN_RECEIVED]); + TCPSTATES_INC(TCPS_SYN_RECEIVED); TCPSTAT_INC(tcps_sc_added); } @@ -365,7 +365,7 @@ syncache_drop(struct syncache *sc, struct syncache_head *sch) SCH_LOCK_ASSERT(sch); - TCPSTAT_DEC(tcps_states[TCPS_SYN_RECEIVED]); + TCPSTATES_DEC(TCPS_SYN_RECEIVED); TAILQ_REMOVE(&sch->sch_bucket, sc, sc_hash); sch->sch_length--; @@ -1003,7 +1003,7 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, * sonewconn->tcp_usr_attach in TCPS_CLOSED state, then * syncache_socket() will change it to TCPS_SYN_RECEIVED. */ - TCPSTAT_DEC(tcps_states[TCPS_SYN_RECEIVED]); + TCPSTATES_DEC(TCPS_SYN_RECEIVED); TAILQ_REMOVE(&sch->sch_bucket, sc, sc_hash); sch->sch_length--; #ifdef TCP_OFFLOAD diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c index c98de24..ff36ce7 100644 --- a/sys/netinet/tcp_timewait.c +++ b/sys/netinet/tcp_timewait.c @@ -660,7 +660,7 @@ tcp_tw_2msl_stop(struct tcptw *tw, int reuse) if (!reuse) uma_zfree(V_tcptw_zone, tw); - TCPSTAT_DEC(tcps_states[TCPS_TIME_WAIT]); + TCPSTATES_DEC(TCPS_TIME_WAIT); } struct tcptw * diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 09493f0..6a3cde6 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1883,7 +1883,7 @@ tcp_attach(struct socket *so) tp->t_state = TCPS_CLOSED; INP_WUNLOCK(inp); INP_INFO_RUNLOCK(&V_tcbinfo); - TCPSTAT_INC(tcps_states[TCPS_CLOSED]); + TCPSTATES_INC(TCPS_CLOSED); return (0); } diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index 2f23881..b28ee23 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -588,9 +588,6 @@ struct tcpstat { uint64_t tcps_sig_err_sigopt; /* No signature expected by socket */ uint64_t tcps_sig_err_nosigopt; /* No signature provided by segment */ - /* Running connection count. */ - uint64_t tcps_states[TCP_NSTATES]; - uint64_t _pad[12]; /* 6 UTO, 6 TBD */ }; @@ -609,9 +606,6 @@ VNET_PCPUSTAT_DECLARE(struct tcpstat, tcpstat); /* tcp statistics */ #define TCPSTAT_ADD(name, val) \ VNET_PCPUSTAT_ADD(struct tcpstat, tcpstat, name, (val)) #define TCPSTAT_INC(name) TCPSTAT_ADD(name, 1) -#define TCPSTAT_DEC(name) TCPSTAT_ADD(name, -1) -#define TCPSTAT_FETCH(name) VNET_PCPUSTAT_FETCH(struct tcpstat, tcpstat, \ - name) /* * Kernel module consumers must use this accessor macro. @@ -621,6 +615,13 @@ void kmod_tcpstat_inc(int statnum); kmod_tcpstat_inc(offsetof(struct tcpstat, name) / sizeof(uint64_t)) /* + * Running TCP connection count by state. + */ +VNET_DECLARE(counter_u64_t, tcps_states[TCP_NSTATES]); +#define TCPSTATES_INC(state) counter_u64_add(VNET(tcps_states)[state], 1) +#define TCPSTATES_DEC(state) counter_u64_add(VNET(tcps_states)[state], -1) + +/* * TCP specific helper hook point identifiers. */ #define HHOOK_TCP_EST_IN 0 @@ -678,6 +679,7 @@ struct xtcpcb { #define TCPCTL_V6MSSDFLT 13 /* MSS default for IPv6 */ #define TCPCTL_SACK 14 /* Selective Acknowledgement,rfc 2018 */ #define TCPCTL_DROP 15 /* drop tcp connection */ +#define TCPCTL_STATES 16 /* connection counts by TCP state */ #ifdef _KERNEL #ifdef SYSCTL_DECL -- cgit v1.1 From c39b2fd5d1f987bd7aa2e9893a928d17dbafecd4 Mon Sep 17 00:00:00 2001 From: glebius Date: Tue, 15 Mar 2016 00:19:30 +0000 Subject: Print running TCP connection counts with TCP statistics. --- usr.bin/netstat/inet.c | 28 ++++++++++++++++++++++++++++ usr.bin/netstat/main.c | 25 +++++++++++++++++++++---- usr.bin/netstat/netstat.h | 6 ++++-- usr.bin/netstat/nlist_symbols | 1 + 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c index b0a0a7f..4793229 100644 --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -82,6 +82,7 @@ __FBSDID("$FreeBSD$"); #include #include #include "netstat.h" +#include "nl_defs.h" char *inetname(struct in_addr *); void inetprint(const char *, struct in_addr *, int, const char *, int, @@ -638,6 +639,7 @@ void tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct tcpstat tcpstat; + uint64_t tcps_states[TCP_NSTATES]; #ifdef INET6 if (tcp_done != 0) @@ -650,6 +652,10 @@ tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) sizeof(tcpstat), kread_counters) != 0) return; + if (fetch_stats_ro("net.inet.tcp.states", nl[N_TCPS_STATES].n_value, + &tcps_states, sizeof(tcps_states), kread_counters) != 0) + return; + xo_open_container("tcp"); xo_emit("{T:/%s}:\n", name); @@ -853,6 +859,28 @@ tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) #undef p2a #undef p3 xo_close_container("ecn"); + + xo_open_container("TCP connection count by state"); + xo_emit("{T:/TCP connection count by state}:\n"); + for (int i = 0; i < TCP_NSTATES; i++) { + /* + * XXXGL: is there a way in libxo to use %s + * in the "content string" of a format + * string? I failed to do that, that's why + * a temporary buffer is used to construct + * format string for xo_emit(). + */ + char fmtbuf[80]; + + if (sflag > 1 && tcps_states[i] == 0) + continue; + snprintf(fmtbuf, sizeof(fmtbuf), "\t{:%s/%%ju} " + "{Np:/connection ,connections} in %s state\n", + tcpstates[i], tcpstates[i]); + xo_emit(fmtbuf, (uintmax_t )tcps_states[i]); + } + xo_close_container("TCP connection count by state"); + xo_close_container("tcp"); } diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c index e8cf914..4e4428d 100644 --- a/usr.bin/netstat/main.c +++ b/usr.bin/netstat/main.c @@ -551,15 +551,15 @@ main(int argc, char *argv[]) exit(0); } -int -fetch_stats(const char *sysctlname, u_long off, void *stats, size_t len, - int (*kreadfn)(u_long, void *, size_t)) +static int +fetch_stats_internal(const char *sysctlname, u_long off, void *stats, + size_t len, kreadfn_t kreadfn, int zero) { int error; if (live) { memset(stats, 0, len); - if (zflag) + if (zero) error = sysctlbyname(sysctlname, NULL, NULL, stats, len); else @@ -574,6 +574,23 @@ fetch_stats(const char *sysctlname, u_long off, void *stats, size_t len, return (error); } +int +fetch_stats(const char *sysctlname, u_long off, void *stats, + size_t len, kreadfn_t kreadfn) +{ + + return (fetch_stats_internal(sysctlname, off, stats, len, kreadfn, + zflag)); +} + +int +fetch_stats_ro(const char *sysctlname, u_long off, void *stats, + size_t len, kreadfn_t kreadfn) +{ + + return (fetch_stats_internal(sysctlname, off, stats, len, kreadfn, 0)); +} + /* * Print out protocol statistics or control blocks (per sflag). * If the interface was not specifically requested, and the symbol diff --git a/usr.bin/netstat/netstat.h b/usr.bin/netstat/netstat.h index 776c4d4..4db2844 100644 --- a/usr.bin/netstat/netstat.h +++ b/usr.bin/netstat/netstat.h @@ -63,8 +63,10 @@ extern int unit; /* unit number for above */ extern int live; /* true if we are examining a live system */ -int fetch_stats(const char *sysctlname, u_long addr, void *stats, - size_t len, int (*kreadfn)(u_long, void *, size_t)); +typedef int kreadfn_t(u_long, void *, size_t); +int fetch_stats(const char *, u_long, void *, size_t, kreadfn_t); +int fetch_stats_ro(const char *, u_long, void *, size_t, kreadfn_t); + int kread(u_long addr, void *buf, size_t size); uint64_t kread_counter(u_long addr); int kread_counters(u_long addr, void *buf, size_t size); diff --git a/usr.bin/netstat/nlist_symbols b/usr.bin/netstat/nlist_symbols index e2b7e2f..afad45d 100644 --- a/usr.bin/netstat/nlist_symbols +++ b/usr.bin/netstat/nlist_symbols @@ -44,6 +44,7 @@ all _sctpstat all _sfstat all _tcbinfo all _tcpstat +all _tcps_states all _udbinfo all _udpstat all _unp_count -- cgit v1.1 From 743cf42b4f88722974a8792a65157a92ef1e4c3a Mon Sep 17 00:00:00 2001 From: glebius Date: Tue, 15 Mar 2016 00:21:32 +0000 Subject: Free the temporary buffer in sysctl_handle_counter_u64_array(). Submitted by: mjg --- sys/kern/subr_counter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/kern/subr_counter.c b/sys/kern/subr_counter.c index bade8b8..5149f2d 100644 --- a/sys/kern/subr_counter.c +++ b/sys/kern/subr_counter.c @@ -106,6 +106,7 @@ sysctl_handle_counter_u64_array(SYSCTL_HANDLER_ARGS) out[i] = counter_u64_fetch(((counter_u64_t *)arg1)[i]); error = SYSCTL_OUT(req, out, arg2 * sizeof(uint64_t)); + free(out, M_TEMP); if (error || !req->newptr) return (error); -- cgit v1.1 From 01b2fd37c124a411a530ceedf51f21dc3466012a Mon Sep 17 00:00:00 2001 From: sobomax Date: Tue, 15 Mar 2016 01:17:38 +0000 Subject: Fix build with HWPMC_HOOKS enabled. --- sys/mips/mips/mips_pic.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sys/mips/mips/mips_pic.c b/sys/mips/mips/mips_pic.c index 250f8cf..4e97c41 100644 --- a/sys/mips/mips/mips_pic.c +++ b/sys/mips/mips/mips_pic.c @@ -48,6 +48,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include @@ -217,8 +219,11 @@ mips_pic_intr(void *arg) KASSERT(i == 0, ("all interrupts handled")); #ifdef HWPMC_HOOKS - if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) + if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN)) { + struct trapframe *tf = PCPU_GET(curthread)->td_intr_frame; + pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf); + } #endif return (FILTER_HANDLED); } -- cgit v1.1 From 96cf53da35daa68adf3599131426d8f33dc33b15 Mon Sep 17 00:00:00 2001 From: imp Date: Tue, 15 Mar 2016 04:42:37 +0000 Subject: Just install ar5523.bin into /usr/share/firmware and stop compiling it in. Differential Review: https://reviews.freebsd.org/D5639 --- etc/mtree/BSD.usr.dist | 2 ++ share/man/man7/hier.7 | 4 +++- tools/build/mk/OptionalObsoleteFiles.inc | 1 + usr.sbin/Makefile | 2 -- usr.sbin/uathload/Makefile | 22 ++++------------------ usr.sbin/uathload/uathload.c | 28 ++++++++++++---------------- 6 files changed, 22 insertions(+), 37 deletions(-) diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist index 12a4ef6..44e9c3e 100644 --- a/etc/mtree/BSD.usr.dist +++ b/etc/mtree/BSD.usr.dist @@ -428,6 +428,8 @@ uefisign .. .. + firmware + .. games fortune .. diff --git a/share/man/man7/hier.7 b/share/man/man7/hier.7 index 9def562..21a2236 100644 --- a/share/man/man7/hier.7 +++ b/share/man/man7/hier.7 @@ -28,7 +28,7 @@ .\" @(#)hier.7 8.1 (Berkeley) 6/5/93 .\" $FreeBSD$ .\" -.Dd October 2, 2015 +.Dd March 15, 2016 .Dt HIER 7 .Os .Sh NAME @@ -545,6 +545,8 @@ Chinese translations of documents in /usr/share/doc .Pp .It Pa examples/ various examples for users and programmers +.It Pa firmware/ +Firmware images loaded by user land programs .It Pa games/ ASCII text files used by various games .It Pa groff_font/ diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc index eb9060d..326e543 100644 --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -8024,6 +8024,7 @@ OLD_FILES+=usr/share/examples/libusb20/control.c OLD_FILES+=usr/share/examples/libusb20/util.c OLD_FILES+=usr/share/examples/libusb20/util.h OLD_DIRS+=usr/share/examples/libusb20 +OLD_FILES+=usr/share/firmware/ar5523.bin OLD_FILES+=usr/share/man/man1/uhsoctl.1.gz OLD_FILES+=usr/share/man/man1/usbhidaction.1.gz OLD_FILES+=usr/share/man/man1/usbhidctl.1.gz diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 86b5247..16feb68 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -194,9 +194,7 @@ SUBDIR.${MK_TIMED}+= timed SUBDIR.${MK_TOOLCHAIN}+= config SUBDIR.${MK_TOOLCHAIN}+= crunch SUBDIR.${MK_UNBOUND}+= unbound -.if !(${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 50200) SUBDIR.${MK_USB}+= uathload -.endif SUBDIR.${MK_USB}+= uhsoctl SUBDIR.${MK_USB}+= usbconfig SUBDIR.${MK_USB}+= usbdump diff --git a/usr.sbin/uathload/Makefile b/usr.sbin/uathload/Makefile index 949f800..f8b26a4 100644 --- a/usr.sbin/uathload/Makefile +++ b/usr.sbin/uathload/Makefile @@ -3,28 +3,14 @@ PROG= uathload MAN= uathload.8 -SRCS= uathload.c ar5523.bin +SRCS= uathload.c +FILES= ar5523.bin +FILESDIR= ${SHAREDIR}/firmware +FILESMODE= 444 CLEANFILES= ar5523.bin -# It's hard to tag ar5523.o with the proper gnu note saying that it has a -# non-executable stack, so ld doesn't properly mark his executable as -# not having an executable stack. Mark it explicitly, but only for those -# platforms that support his feature (otherwise signals don't work). -# Note: Newer versions of ld than is in the tree ignore -z. -.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "powerpc" || ${MACHINE_ARCH} == "powerpc64" -LDFLAGS+= -Wl,-z,noexecstack -.endif - -# The conversion from .bin to .o doesn't always produce a pedantically correct -# .o's. And it doesn't matter, so turn off the mismatch warnings since it is -# pure data. On mips64 here's no easy way to produce a proper .o. -LDFLAGS+= -Wl,--no-warn-mismatch - ar5523.bin: ${.CURDIR}/../../sys/contrib/dev/uath/ar5523.bin.uu uudecode -p ${.CURDIR}/../../sys/contrib/dev/uath/ar5523.bin.uu > ${.TARGET} -ar5523.o: ar5523.bin - ${LD} -b binary -d -warn-common -r -d -o ${.TARGET} ar5523.bin - .include diff --git a/usr.sbin/uathload/uathload.c b/usr.sbin/uathload/uathload.c index 64ae661..ba2258f 100644 --- a/usr.sbin/uathload/uathload.c +++ b/usr.sbin/uathload/uathload.c @@ -140,23 +140,19 @@ main(int argc, char *argv[]) if (argc > 1) usage(); - if (argc == 1) { + if (argc == 1) fwname = argv[0]; - fw = open(fwname, O_RDONLY, 0); - if (fw < 0) - err(-1, "open(%s)", fwname); - if (fstat(fw, &sb) < 0) - err(-1, "fstat(%s)", fwname); - txdata = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fw, 0); - if (txdata == MAP_FAILED) - err(-1, "mmap(%s)", fwname); - len = sb.st_size; - } else { - fwname = "ar5523.bin (builtin)"; - fw = -1; - txdata = &_binary_ar5523_bin_start; - len = &_binary_ar5523_bin_end - &_binary_ar5523_bin_start; - } + else + fwname = "/usr/share/firmware/ar5523.bin"; + fw = open(fwname, O_RDONLY, 0); + if (fw < 0) + err(-1, "open(%s)", fwname); + if (fstat(fw, &sb) < 0) + err(-1, "fstat(%s)", fwname); + txdata = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fw, 0); + if (txdata == MAP_FAILED) + err(-1, "mmap(%s)", fwname); + len = sb.st_size; /* XXX verify device is an AR5005 part */ if (getdevname(devname, msgdev, datadev)) err(-1, "getdevname error"); -- cgit v1.1 From 6695f06328167998684f8edec76d2371afc9a86e Mon Sep 17 00:00:00 2001 From: imp Date: Tue, 15 Mar 2016 04:56:39 +0000 Subject: Remove bare & 0x3; it encodes the values of BIO_READ and BIO_WRITE. It's also unnecessary since the only cases in this switch are those two. --- sys/dev/mfi/mfi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/dev/mfi/mfi.c b/sys/dev/mfi/mfi.c index ddcc853..c224186 100644 --- a/sys/dev/mfi/mfi.c +++ b/sys/dev/mfi/mfi.c @@ -2141,7 +2141,7 @@ mfi_build_syspdio(struct mfi_softc *sc, struct bio *bio) pass = &cm->cm_frame->pass; bzero(pass->cdb, 16); pass->header.cmd = MFI_CMD_PD_SCSI_IO; - switch (bio->bio_cmd & 0x03) { + switch (bio->bio_cmd) { case BIO_READ: flags = MFI_CMD_DATAIN | MFI_CMD_BIO; readop = 1; @@ -2200,7 +2200,7 @@ mfi_build_ldio(struct mfi_softc *sc, struct bio *bio) bzero(cm->cm_frame, sizeof(union mfi_frame)); cm->cm_frame->header.context = context; io = &cm->cm_frame->io; - switch (bio->bio_cmd & 0x03) { + switch (bio->bio_cmd) { case BIO_READ: io->header.cmd = MFI_CMD_LD_READ; flags = MFI_CMD_DATAIN | MFI_CMD_BIO; -- cgit v1.1 From e9a62530aa2a12318895252d7de4b3e6de326263 Mon Sep 17 00:00:00 2001 From: imp Date: Tue, 15 Mar 2016 05:17:29 +0000 Subject: Make sure we check for CAM_CDB_POINTER for all drivers. Also, for the drivers I've touched, filter out CAM_CDB_PHYS. Differential Revision: https://reviews.freebsd.org/D5585 --- sys/cam/cam_ccb.h | 7 +++++++ sys/dev/arcmsr/arcmsr.c | 23 +++++++++++++++-------- sys/dev/iir/iir.c | 6 +++--- sys/dev/isci/isci_controller.c | 10 ++++++++-- sys/dev/isci/isci_io_request.c | 18 +++++++++--------- sys/dev/ncr/ncr.c | 14 +++++++++++--- sys/dev/ppbus/vpo.c | 16 +++++++++++----- 7 files changed, 64 insertions(+), 30 deletions(-) diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h index 4e1e6a2..28415ed 100644 --- a/sys/cam/cam_ccb.h +++ b/sys/cam/cam_ccb.h @@ -725,6 +725,13 @@ struct ccb_scsiio { u_int init_id; /* initiator id of who selected */ }; +static __inline uint8_t * +scsiio_cdb_ptr(struct ccb_scsiio *ccb) +{ + return ((ccb->ccb_h.flags & CAM_CDB_POINTER) ? + ccb->cdb_io.cdb_ptr : ccb->cdb_io.cdb_bytes); +} + /* * ATA I/O Request CCB used for the XPT_ATA_IO function code. */ diff --git a/sys/dev/arcmsr/arcmsr.c b/sys/dev/arcmsr/arcmsr.c index 3cf7fce..cabb99a 100644 --- a/sys/dev/arcmsr/arcmsr.c +++ b/sys/dev/arcmsr/arcmsr.c @@ -872,7 +872,7 @@ static void arcmsr_srb_timeout(void *arg) ARCMSR_LOCK_ACQUIRE(&acb->isr_lock); if(srb->srb_state == ARCMSR_SRB_START) { - cmd = srb->pccb->csio.cdb_io.cdb_bytes[0]; + cmd = scsiio_cdb_ptr(&srb->pccb->csio)[0]; srb->srb_state = ARCMSR_SRB_TIMEOUT; srb->pccb->ccb_h.status |= CAM_CMD_TIMEOUT; arcmsr_srb_complete(srb, 1); @@ -997,7 +997,7 @@ static void arcmsr_build_srb(struct CommandControlBlock *srb, arcmsr_cdb->LUN = pccb->ccb_h.target_lun; arcmsr_cdb->Function = 1; arcmsr_cdb->CdbLength = (u_int8_t)pcsio->cdb_len; - bcopy(pcsio->cdb_io.cdb_bytes, arcmsr_cdb->Cdb, pcsio->cdb_len); + bcopy(scsiio_cdb_ptr(pcsio), arcmsr_cdb->Cdb, pcsio->cdb_len); if(nseg != 0) { struct AdapterControlBlock *acb = srb->acb; bus_dmasync_op_t op; @@ -2453,10 +2453,11 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, union ccb *p struct CMD_MESSAGE_FIELD *pcmdmessagefld; int retvalue = 0, transfer_len = 0; char *buffer; - u_int32_t controlcode = (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[5] << 24 | - (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[6] << 16 | - (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[7] << 8 | - (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[8]; + uint8_t *ptr = scsiio_cdb_ptr(&pccb->csio); + u_int32_t controlcode = (u_int32_t ) ptr[5] << 24 | + (u_int32_t ) ptr[6] << 16 | + (u_int32_t ) ptr[7] << 8 | + (u_int32_t ) ptr[8]; /* 4 bytes: Areca io control code */ if ((pccb->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) { buffer = pccb->csio.data_ptr; @@ -2683,7 +2684,7 @@ static void arcmsr_execute_srb(void *arg, bus_dma_segment_t *dm_segs, int nseg, if(acb->devstate[target][lun] == ARECA_RAID_GONE) { u_int8_t block_cmd, cmd; - cmd = pccb->csio.cdb_io.cdb_bytes[0]; + cmd = scsiio_cdb_ptr(&pccb->csio)[0]; block_cmd = cmd & 0x0f; if(block_cmd == 0x08 || block_cmd == 0x0a) { printf("arcmsr%d:block 'read/write' command " @@ -2800,7 +2801,7 @@ static void arcmsr_handle_virtual_command(struct AdapterControlBlock *acb, return; } pccb->ccb_h.status |= CAM_REQ_CMP; - switch (pccb->csio.cdb_io.cdb_bytes[0]) { + switch (scsiio_cdb_ptr(&pccb->csio)[0]) { case INQUIRY: { unsigned char inqdata[36]; char *buffer = pccb->csio.data_ptr; @@ -2853,6 +2854,12 @@ static void arcmsr_action(struct cam_sim *psim, union ccb *pccb) int target = pccb->ccb_h.target_id; int error; + if (pccb->ccb_h.flags & CAM_CDB_PHYS) { + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + return; + } + if(target == 16) { /* virtual device for iop message transfer */ arcmsr_handle_virtual_command(acb, pccb); diff --git a/sys/dev/iir/iir.c b/sys/dev/iir/iir.c index bf5cec5..e74698e 100644 --- a/sys/dev/iir/iir.c +++ b/sys/dev/iir/iir.c @@ -744,9 +744,9 @@ gdt_next(struct gdt_softc *gdt) ccb->ccb_h.flags)); csio = &ccb->csio; ccbh = &ccb->ccb_h; - cmd = csio->cdb_io.cdb_bytes[0]; - /* Max CDB length is 12 bytes */ - if (csio->cdb_len > 12) { + cmd = scsiio_cdb_ptr(csio)[0]; + /* Max CDB length is 12 bytes, can't be phys addr */ + if (csio->cdb_len > 12 || (ccbh->flags & CAM_CDB_PHYS)) { ccbh->status = CAM_REQ_INVALID; --gdt_stat.io_count_act; xpt_done(ccb); diff --git a/sys/dev/isci/isci_controller.c b/sys/dev/isci/isci_controller.c index b0f4285..d3ec045 100644 --- a/sys/dev/isci/isci_controller.c +++ b/sys/dev/isci/isci_controller.c @@ -740,6 +740,11 @@ void isci_action(struct cam_sim *sim, union ccb *ccb) } break; case XPT_SCSI_IO: + if (ccb->ccb_h.flags & CAM_CDB_PHYS) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } isci_io_request_execute_scsi_io(ccb, controller); break; #if __FreeBSD_version >= 900026 @@ -802,6 +807,7 @@ isci_controller_release_queued_ccbs(struct ISCI_CONTROLLER *controller) { struct ISCI_REMOTE_DEVICE *dev; struct ccb_hdr *ccb_h; + uint8_t *ptr; int dev_idx; KASSERT(mtx_owned(&controller->lock), ("controller lock not owned")); @@ -821,8 +827,8 @@ isci_controller_release_queued_ccbs(struct ISCI_CONTROLLER *controller) if (ccb_h == NULL) continue; - isci_log_message(1, "ISCI", "release %p %x\n", ccb_h, - ((union ccb *)ccb_h)->csio.cdb_io.cdb_bytes[0]); + ptr = scsiio_cdb_ptr(&((union ccb *)ccb_h)->csio); + isci_log_message(1, "ISCI", "release %p %x\n", ccb_h, *ptr); dev->queued_ccb_in_progress = (union ccb *)ccb_h; isci_io_request_execute_scsi_io( diff --git a/sys/dev/isci/isci_io_request.c b/sys/dev/isci/isci_io_request.c index e6b68fd..f86c126 100644 --- a/sys/dev/isci/isci_io_request.c +++ b/sys/dev/isci/isci_io_request.c @@ -86,6 +86,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, struct ISCI_REMOTE_DEVICE *isci_remote_device; union ccb *ccb; BOOL complete_ccb; + struct ccb_scsiio *csio; complete_ccb = TRUE; isci_controller = (struct ISCI_CONTROLLER *) sci_object_get_association(scif_controller); @@ -93,7 +94,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, (struct ISCI_REMOTE_DEVICE *) sci_object_get_association(remote_device); ccb = isci_request->ccb; - + csio = &ccb->csio; ccb->ccb_h.status &= ~CAM_STATUS_MASK; switch (completion_status) { @@ -124,7 +125,6 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, SCI_SSP_RESPONSE_IU_T * response_buffer; uint32_t sense_length; int error_code, sense_key, asc, ascq; - struct ccb_scsiio *csio = &ccb->csio; response_buffer = (SCI_SSP_RESPONSE_IU_T *) scif_io_request_get_response_iu_address( @@ -146,7 +146,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, isci_log_message(1, "ISCI", "isci: bus=%x target=%x lun=%x cdb[0]=%x status=%x key=%x asc=%x ascq=%x\n", ccb->ccb_h.path_id, ccb->ccb_h.target_id, - ccb->ccb_h.target_lun, csio->cdb_io.cdb_bytes[0], + ccb->ccb_h.target_lun, scsiio_cdb_ptr(csio), csio->scsi_status, sense_key, asc, ascq); break; } @@ -157,7 +157,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, isci_log_message(0, "ISCI", "isci: bus=%x target=%x lun=%x cdb[0]=%x remote device reset required\n", ccb->ccb_h.path_id, ccb->ccb_h.target_id, - ccb->ccb_h.target_lun, ccb->csio.cdb_io.cdb_bytes[0]); + ccb->ccb_h.target_lun, scsiio_cdb_ptr(csio)); break; case SCI_IO_FAILURE_TERMINATED: @@ -165,7 +165,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, isci_log_message(0, "ISCI", "isci: bus=%x target=%x lun=%x cdb[0]=%x terminated\n", ccb->ccb_h.path_id, ccb->ccb_h.target_id, - ccb->ccb_h.target_lun, ccb->csio.cdb_io.cdb_bytes[0]); + ccb->ccb_h.target_lun, scsiio_cdb_ptr(csio)); break; case SCI_IO_FAILURE_INVALID_STATE: @@ -208,7 +208,7 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, isci_log_message(1, "ISCI", "isci: bus=%x target=%x lun=%x cdb[0]=%x completion status=%x\n", ccb->ccb_h.path_id, ccb->ccb_h.target_id, - ccb->ccb_h.target_lun, ccb->csio.cdb_io.cdb_bytes[0], + ccb->ccb_h.target_lun, scsiio_cdb_ptr(csio), completion_status); ccb->ccb_h.status |= CAM_REQ_CMP_ERR; break; @@ -285,13 +285,13 @@ isci_io_request_complete(SCI_CONTROLLER_HANDLE_T scif_controller, * get a ready notification for this device. */ isci_log_message(1, "ISCI", "already queued %p %x\n", - ccb, ccb->csio.cdb_io.cdb_bytes[0]); + ccb, scsiio_cdb_ptr(csio)); isci_remote_device->queued_ccb_in_progress = NULL; } else { isci_log_message(1, "ISCI", "queue %p %x\n", ccb, - ccb->csio.cdb_io.cdb_bytes[0]); + scsiio_cdb_ptr(csio)); ccb->ccb_h.status |= CAM_SIM_QUEUED; TAILQ_INSERT_TAIL(&isci_remote_device->queued_ccbs, @@ -373,7 +373,7 @@ scif_cb_io_request_get_cdb_address(void * scif_user_io_request) struct ISCI_IO_REQUEST *isci_request = (struct ISCI_IO_REQUEST *)scif_user_io_request; - return (isci_request->ccb->csio.cdb_io.cdb_bytes); + return (scsiio_cdb_ptr(&isci_request->ccb->csio)); } /** diff --git a/sys/dev/ncr/ncr.c b/sys/dev/ncr/ncr.c index 6c941e1..111cd69 100644 --- a/sys/dev/ncr/ncr.c +++ b/sys/dev/ncr/ncr.c @@ -3860,6 +3860,16 @@ ncr_action (struct cam_sim *sim, union ccb *ccb) csio = &ccb->csio; /* + * Make sure we support this request. We can't do + * PHYS pointers. + */ + if (ccb->ccb_h.flags & CAM_CDB_PHYS) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + return; + } + + /* * Last time we need to check if this CCB needs to * be aborted. */ @@ -4070,8 +4080,7 @@ ncr_action (struct cam_sim *sim, union ccb *ccb) /* ** command */ - /* XXX JGibbs - Support other command types */ - cp->phys.cmd.addr = vtophys (csio->cdb_io.cdb_bytes); + cp->phys.cmd.addr = vtophys (scsiio_cdb_ptr(csio)); cp->phys.cmd.size = csio->cdb_len; /* ** sense command @@ -4083,7 +4092,6 @@ ncr_action (struct cam_sim *sim, union ccb *ccb) */ cp->sensecmd[0] = 0x03; cp->sensecmd[1] = ccb->ccb_h.target_lun << 5; - cp->sensecmd[4] = sizeof(struct scsi_sense_data); cp->sensecmd[4] = csio->sense_len; /* ** sense data diff --git a/sys/dev/ppbus/vpo.c b/sys/dev/ppbus/vpo.c index 9c9054f..a4f5226 100644 --- a/sys/dev/ppbus/vpo.c +++ b/sys/dev/ppbus/vpo.c @@ -187,17 +187,19 @@ vpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio) #ifdef VP0_DEBUG int i; #endif + uint8_t *ptr; + ptr = scsiio_cdb_ptr(csio); if (vpo->vpo_isplus) { errno = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, csio->ccb_h.target_id, - (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, + ptr, csio->cdb_len, (char *)csio->data_ptr, csio->dxfer_len, &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); } else { errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, csio->ccb_h.target_id, - (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, + ptr, csio->cdb_len, (char *)csio->data_ptr, csio->dxfer_len, &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); } @@ -208,7 +210,7 @@ vpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio) /* dump of command */ for (i=0; icdb_len; i++) - printf("%x ", ((char *)&csio->cdb_io.cdb_bytes)[i]); + printf("%x ", ((char *)ptr)[i]); printf("\n"); #endif @@ -307,11 +309,15 @@ vpo_action(struct cam_sim *sim, union ccb *ccb) csio = &ccb->csio; + if (ccb->ccb_h.flags & CAM_CDB_PHYS) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } #ifdef VP0_DEBUG device_printf(vpo->vpo_dev, "XPT_SCSI_IO (0x%x) request\n", - csio->cdb_io.cdb_bytes[0]); + scsiio_cdb_ptr(csio)); #endif - vpo_intr(vpo, csio); xpt_done(ccb); -- cgit v1.1 From c39a40d212a075104c87318e68b3263b92bc2033 Mon Sep 17 00:00:00 2001 From: imp Date: Tue, 15 Mar 2016 05:17:31 +0000 Subject: ms_abi is supported with gcc 4.5 or newer, so build it with gcc if it is 4.5 or newer. --- sys/boot/efi/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/boot/efi/Makefile b/sys/boot/efi/Makefile index 94a975a..21da86f 100644 --- a/sys/boot/efi/Makefile +++ b/sys/boot/efi/Makefile @@ -2,8 +2,9 @@ .include -# In-tree GCC does not support __attribute__((ms_abi)). -.if ${COMPILER_TYPE} != "gcc" +# In-tree GCC does not support __attribute__((ms_abi)), but gcc newer +# than 4.5 supports it. +.if ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 404500 .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm" .if ${MK_FDT} != "no" @@ -17,7 +18,6 @@ SUBDIR+= fdt SUBDIR+= libefi loader boot1 .endif -.endif # ${COMPILER_TYPE} != "gcc" +.endif # ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 404500 .include - -- cgit v1.1 From ebabdead988098632c93046267c173d32e213baf Mon Sep 17 00:00:00 2001 From: wma Date: Tue, 15 Mar 2016 06:06:09 +0000 Subject: Remove redefinitions in ARMADA code * Rename IRQ_MASK to MPIC_IRQ_MASK not to overlap with register name from mvreg.h * Remove ARM_INTRNG from ARMADA38X, apparently was already included. --- sys/arm/conf/ARMADA38X | 1 - sys/arm/mv/mpic.c | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sys/arm/conf/ARMADA38X b/sys/arm/conf/ARMADA38X index 7cc7702..9a31f0c 100644 --- a/sys/arm/conf/ARMADA38X +++ b/sys/arm/conf/ARMADA38X @@ -23,7 +23,6 @@ options SCHED_ULE # ULE scheduler #options SCHED_4BSD # 4BSD scheduler options SMP -options ARM_INTRNG # Debugging #options DEBUG diff --git a/sys/arm/mv/mpic.c b/sys/arm/mv/mpic.c index e81820c..7d53afe 100644 --- a/sys/arm/mv/mpic.c +++ b/sys/arm/mv/mpic.c @@ -74,7 +74,7 @@ __FBSDID("$FreeBSD$"); #define MPIC_INT_ERR 4 #define MPIC_INT_MSI 96 -#define IRQ_MASK 0x3ff +#define MPIC_IRQ_MASK 0x3ff #define MPIC_CTRL 0x0 #define MPIC_SOFT_INT 0x4 @@ -409,10 +409,10 @@ arm_get_next_irq(int last) { u_int irq, next = -1; - irq = mv_mpic_get_cause() & IRQ_MASK; + irq = mv_mpic_get_cause() & MPIC_IRQ_MASK; CTR2(KTR_INTR, "%s: irq:%#x", __func__, irq); - if (irq != IRQ_MASK) { + if (irq != MPIC_IRQ_MASK) { if (irq == MPIC_INT_ERR) irq = mv_mpic_get_cause_err(); if (irq == MPIC_INT_MSI) -- cgit v1.1 From d59afb0080ef4e4c627fe9239305124e719048a5 Mon Sep 17 00:00:00 2001 From: trasz Date: Tue, 15 Mar 2016 11:03:45 +0000 Subject: Use the log_errno value passed to the function instead of the errno global variable. Suggested by: Daniel Braniss MFC after: 1 month Sponsored by: The FreeBSD Foundation --- usr.sbin/autofs/log.c | 15 +++++++++------ usr.sbin/ctld/log.c | 15 +++++++++------ usr.sbin/iscsid/log.c | 15 +++++++++------ 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/usr.sbin/autofs/log.c b/usr.sbin/autofs/log.c index d5682cc..b3935dc 100644 --- a/usr.sbin/autofs/log.c +++ b/usr.sbin/autofs/log.c @@ -88,6 +88,7 @@ log_common(int priority, int log_errno, const char *fmt, va_list ap) { static char msgbuf[MSGBUF_LEN]; static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; + char *errstr; int ret; ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); @@ -121,21 +122,23 @@ log_common(int priority, int log_errno, const char *fmt, va_list ap) } } else { + errstr = strerror(log_errno); + if (peer_name != NULL) { fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), - peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + peer_addr, peer_name, msgbuf_strvised, errstr); syslog(priority, "%s (%s): %s: %s", - peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + peer_addr, peer_name, msgbuf_strvised, errstr); } else if (peer_addr != NULL) { fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), - peer_addr, msgbuf_strvised, strerror(errno)); + peer_addr, msgbuf_strvised, errstr); syslog(priority, "%s: %s: %s", - peer_addr, msgbuf_strvised, strerror(errno)); + peer_addr, msgbuf_strvised, errstr); } else { fprintf(stderr, "%s: %s: %s\n", getprogname(), - msgbuf_strvised, strerror(errno)); + msgbuf_strvised, errstr); syslog(priority, "%s: %s", - msgbuf_strvised, strerror(errno)); + msgbuf_strvised, errstr); } } } diff --git a/usr.sbin/ctld/log.c b/usr.sbin/ctld/log.c index ac838f3..ed66c31 100644 --- a/usr.sbin/ctld/log.c +++ b/usr.sbin/ctld/log.c @@ -88,6 +88,7 @@ log_common(int priority, int log_errno, const char *fmt, va_list ap) { static char msgbuf[MSGBUF_LEN]; static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; + char *errstr; int ret; ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); @@ -121,21 +122,23 @@ log_common(int priority, int log_errno, const char *fmt, va_list ap) } } else { + errstr = strerror(log_errno); + if (peer_name != NULL) { fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), - peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + peer_addr, peer_name, msgbuf_strvised, errstr); syslog(priority, "%s (%s): %s: %s", - peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + peer_addr, peer_name, msgbuf_strvised, errstr); } else if (peer_addr != NULL) { fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), - peer_addr, msgbuf_strvised, strerror(errno)); + peer_addr, msgbuf_strvised, errstr); syslog(priority, "%s: %s: %s", - peer_addr, msgbuf_strvised, strerror(errno)); + peer_addr, msgbuf_strvised, errstr); } else { fprintf(stderr, "%s: %s: %s\n", getprogname(), - msgbuf_strvised, strerror(errno)); + msgbuf_strvised, errstr); syslog(priority, "%s: %s", - msgbuf_strvised, strerror(errno)); + msgbuf_strvised, errstr); } } } diff --git a/usr.sbin/iscsid/log.c b/usr.sbin/iscsid/log.c index ea7755f..920c081 100644 --- a/usr.sbin/iscsid/log.c +++ b/usr.sbin/iscsid/log.c @@ -88,6 +88,7 @@ log_common(int priority, int log_errno, const char *fmt, va_list ap) { static char msgbuf[MSGBUF_LEN]; static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; + char *errstr; int ret; ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); @@ -121,21 +122,23 @@ log_common(int priority, int log_errno, const char *fmt, va_list ap) } } else { + errstr = strerror(log_errno); + if (peer_name != NULL) { fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), - peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + peer_addr, peer_name, msgbuf_strvised, errstr); syslog(priority, "%s (%s): %s: %s", - peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + peer_addr, peer_name, msgbuf_strvised, errstr); } else if (peer_addr != NULL) { fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), - peer_addr, msgbuf_strvised, strerror(errno)); + peer_addr, msgbuf_strvised, errstr); syslog(priority, "%s: %s: %s", - peer_addr, msgbuf_strvised, strerror(errno)); + peer_addr, msgbuf_strvised, errstr); } else { fprintf(stderr, "%s: %s: %s\n", getprogname(), - msgbuf_strvised, strerror(errno)); + msgbuf_strvised, errstr); syslog(priority, "%s: %s", - msgbuf_strvised, strerror(errno)); + msgbuf_strvised, errstr); } } } -- cgit v1.1 From 291073f1f074d2ead3a697d6a2f470d595c9b026 Mon Sep 17 00:00:00 2001 From: trasz Date: Tue, 15 Mar 2016 11:10:08 +0000 Subject: Make fail() preserve the errno. MFC after: 1 month Sponsored by: The FreeBSD Foundation --- usr.sbin/iscsid/iscsid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/usr.sbin/iscsid/iscsid.c b/usr.sbin/iscsid/iscsid.c index 72828ef..5be7a44 100644 --- a/usr.sbin/iscsid/iscsid.c +++ b/usr.sbin/iscsid/iscsid.c @@ -290,7 +290,9 @@ void fail(const struct connection *conn, const char *reason) { struct iscsi_daemon_fail idf; - int error; + int error, saved_errno; + + saved_errno = errno; memset(&idf, 0, sizeof(idf)); idf.idf_session_id = conn->conn_session_id; @@ -299,6 +301,8 @@ fail(const struct connection *conn, const char *reason) error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, &idf); if (error != 0) log_err(1, "ISCSIDFAIL"); + + errno = saved_errno; } /* -- cgit v1.1 From cae3377f73c00e29dd1d5b621be9765dd9de67c4 Mon Sep 17 00:00:00 2001 From: emaste Date: Tue, 15 Mar 2016 13:38:15 +0000 Subject: Fix atkbdmap.h generation for sc/vt consoles Keymap header files have historically been generated using the build host's /usr/sbin/kbdcontrol and using the host's keymap files. However, that introduces an issue when building a kernel to use vt(4) on a system using sc(4), or vice versa: kbdcontrol searches for keymap files in the /usr/share subdirectory appropriate for the host, not the target. With this change the build searches both the and sc keymap directories from the source tree. PR: 193865 Submitted by: Harald Schmalzbauer --- sys/conf/files.amd64 | 4 ++-- sys/conf/files.i386 | 4 ++-- sys/conf/files.pc98 | 2 +- sys/conf/files.sparc64 | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index 97f2e37..4d02e03 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -50,12 +50,12 @@ font.h optional sc_dflt_font \ clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8" # atkbdmap.h optional atkbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ + compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${ATKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h; else echo Error: atkbd_dflt_keymap not found; exit 1; fi" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ + compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${UKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h; else echo Error: ukbd_dflt_keymap not found; exit 1; fi" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index b065165..40b910d 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -49,12 +49,12 @@ font.h optional sc_dflt_font \ clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8" # atkbdmap.h optional atkbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ + compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${ATKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h; else echo Error: atkbd_dflt_keymap not found; exit 1; fi" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ + compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${UKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h; else echo Error: ukbd_dflt_keymap not found; exit 1; fi" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98 index 75269b4..938490c 100644 --- a/sys/conf/files.pc98 +++ b/sys/conf/files.pc98 @@ -46,7 +46,7 @@ svr4_assym.h optional compat_svr4 \ clean "svr4_assym.h" # ukbdmap.h optional ukbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ + compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${ATKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h; else echo Error: ukbd_dflt_keymap not found; exit 1; fi" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # diff --git a/sys/conf/files.sparc64 b/sys/conf/files.sparc64 index 84f23ff..5fdba69 100644 --- a/sys/conf/files.sparc64 +++ b/sys/conf/files.sparc64 @@ -8,17 +8,17 @@ # dependency lines other than the first are silently ignored. # atkbdmap.h optional atkbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ + compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${ATKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h; else echo Error: atkbd_dflt_keymap not found; exit 1; fi" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # sunkbdmap.h optional sunkbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${SUNKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > sunkbdmap.h" \ + compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${SUNKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${SUNKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > sunkbdmap.h; else echo Error: sunkbd_dflt_keymap not found; exit 1; fi" \ no-obj no-implicit-rule before-depend \ clean "sunkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ - compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ + compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${UKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h; else echo Error: ukbd_dflt_keymap not found; exit 1; fi" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # -- cgit v1.1 From 35e1f10d38e3f8453d7d72399af85aa8b9237233 Mon Sep 17 00:00:00 2001 From: mmel Date: Tue, 15 Mar 2016 15:24:18 +0000 Subject: Use EARLY_DRIVER_MODULE() with BUS_PASS_BUS priority for ofw_gpiobus and ofw_iicbus. This causes enumeration of gpiobus/iicbus at the base driver attach time. Due to this, childern drivers can be also attached early. --- sys/dev/gpio/ofw_gpiobus.c | 3 ++- sys/dev/ofw/ofw_iicbus.c | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c index be5a747..f6d2115 100644 --- a/sys/dev/gpio/ofw_gpiobus.c +++ b/sys/dev/gpio/ofw_gpiobus.c @@ -575,6 +575,7 @@ static devclass_t ofwgpiobus_devclass; DEFINE_CLASS_1(gpiobus, ofw_gpiobus_driver, ofw_gpiobus_methods, sizeof(struct gpiobus_softc), gpiobus_driver); -DRIVER_MODULE(ofw_gpiobus, gpio, ofw_gpiobus_driver, ofwgpiobus_devclass, 0, 0); +EARLY_DRIVER_MODULE(ofw_gpiobus, gpio, ofw_gpiobus_driver, ofwgpiobus_devclass, + 0, 0, BUS_PASS_BUS); MODULE_VERSION(ofw_gpiobus, 1); MODULE_DEPEND(ofw_gpiobus, gpiobus, 1, 1, 1); diff --git a/sys/dev/ofw/ofw_iicbus.c b/sys/dev/ofw/ofw_iicbus.c index c0fa054..73cad7c 100644 --- a/sys/dev/ofw/ofw_iicbus.c +++ b/sys/dev/ofw/ofw_iicbus.c @@ -80,8 +80,10 @@ static devclass_t ofwiicbus_devclass; DEFINE_CLASS_1(iicbus, ofw_iicbus_driver, ofw_iicbus_methods, sizeof(struct iicbus_softc), iicbus_driver); -DRIVER_MODULE(ofw_iicbus, iicbb, ofw_iicbus_driver, ofwiicbus_devclass, 0, 0); -DRIVER_MODULE(ofw_iicbus, iichb, ofw_iicbus_driver, ofwiicbus_devclass, 0, 0); +EARLY_DRIVER_MODULE(ofw_iicbus, iicbb, ofw_iicbus_driver, ofwiicbus_devclass, + 0, 0, BUS_PASS_BUS); +EARLY_DRIVER_MODULE(ofw_iicbus, iichb, ofw_iicbus_driver, ofwiicbus_devclass, + 0, 0, BUS_PASS_BUS); MODULE_VERSION(ofw_iicbus, 1); MODULE_DEPEND(ofw_iicbus, iicbus, 1, 1, 1); -- cgit v1.1 From 6c29569229222107dbd33d774b06411ea5e41d5e Mon Sep 17 00:00:00 2001 From: hselasky Date: Tue, 15 Mar 2016 15:24:55 +0000 Subject: Improve detection of extended QSFP diagnostics. The standards in the QSFP diagnostics area are not clear when the additional measurements are present or not. Use a valid temperature reading as an indicator for the presence of voltage and TX/RX power measurements. MFC after: 1 week Sponsored by: Mellanox Technologies Tested by: Netflix Differential Revision: https://reviews.freebsd.org/D5391 Reviewed by: gallatin --- sbin/ifconfig/sfp.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/sbin/ifconfig/sfp.c b/sbin/ifconfig/sfp.c index 306140a..d92ee2c 100644 --- a/sbin/ifconfig/sfp.c +++ b/sbin/ifconfig/sfp.c @@ -625,14 +625,17 @@ get_sfp_voltage(struct i2c_info *ii, char *buf, size_t size) convert_sff_voltage(buf, size, xbuf); } -static void +static int get_qsfp_temp(struct i2c_info *ii, char *buf, size_t size) { uint8_t xbuf[2]; memset(xbuf, 0, sizeof(xbuf)); read_i2c(ii, SFF_8436_BASE, SFF_8436_TEMP, 2, xbuf); + if ((xbuf[0] == 0xFF && xbuf[1] == 0xFF) || (xbuf[0] == 0 && xbuf[1] == 0)) + return (-1); convert_sff_temp(buf, size, xbuf); + return (0); } static void @@ -779,22 +782,9 @@ static void print_qsfp_status(struct i2c_info *ii, int verbose) { char buf[80], buf2[40], buf3[40]; - uint8_t diag_type; uint32_t bitrate; int i; - /* Read diagnostic monitoring type */ - read_i2c(ii, SFF_8436_BASE, SFF_8436_DIAG_TYPE, 1, (caddr_t)&diag_type); - if (ii->error != 0) - return; - - /* - * Read monitoring data it is supplied. - * XXX: It is not exactly clear from standard - * how one can specify lack of measurements (passive cables case). - */ - if (diag_type != 0) - ii->do_diag = 1; ii->qsfp = 1; /* Transceiver type */ @@ -817,9 +807,13 @@ print_qsfp_status(struct i2c_info *ii, int verbose) printf("\tnominal bitrate: %u Mbps\n", bitrate); } - /* Request current measurements if they are provided: */ - if (ii->do_diag != 0) { - get_qsfp_temp(ii, buf, sizeof(buf)); + /* + * The standards in this area are not clear when the + * additional measurements are present or not. Use a valid + * temperature reading as an indicator for the presence of + * voltage and TX/RX power measurements. + */ + if (get_qsfp_temp(ii, buf, sizeof(buf)) == 0) { get_qsfp_voltage(ii, buf2, sizeof(buf2)); printf("\tmodule temperature: %s voltage: %s\n", buf, buf2); for (i = 1; i <= 4; i++) { -- cgit v1.1 From 8ed3c0b9a412f9eb8d3c6c0533d12a2ec29b0145 Mon Sep 17 00:00:00 2001 From: mmel Date: Tue, 15 Mar 2016 15:25:26 +0000 Subject: CLK: Remove forgotten mutex from clk_fixed class. --- sys/dev/extres/clk/clk_fixed.c | 9 ++------- sys/dev/extres/clk/clk_fixed.h | 3 +-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/sys/dev/extres/clk/clk_fixed.c b/sys/dev/extres/clk/clk_fixed.c index 4ddceae..de0d9bb 100644 --- a/sys/dev/extres/clk/clk_fixed.c +++ b/sys/dev/extres/clk/clk_fixed.c @@ -42,13 +42,9 @@ __FBSDID("$FreeBSD$"); #include -#define DEVICE_LOCK(_sc) mtx_lock((_sc)->mtx) -#define DEVICE_UNLOCK(_sc) mtx_unlock((_sc)->mtx) - static int clknode_fixed_init(struct clknode *clk, device_t dev); static int clknode_fixed_recalc(struct clknode *clk, uint64_t *freq); struct clknode_fixed_sc { - struct mtx *mtx; int fixed_flags; uint64_t freq; uint32_t mult; @@ -74,6 +70,7 @@ clknode_fixed_init(struct clknode *clk, device_t dev) clknode_init_parent_idx(clk, 0); return(0); } + static int clknode_fixed_recalc(struct clknode *clk, uint64_t *freq) { @@ -90,8 +87,7 @@ clknode_fixed_recalc(struct clknode *clk, uint64_t *freq) } int -clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef, - struct mtx *dev_mtx) +clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef) { struct clknode *clk; struct clknode_fixed_sc *sc; @@ -103,7 +99,6 @@ clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef, return (1); sc = clknode_get_softc(clk); - sc->mtx = dev_mtx; sc->fixed_flags = clkdef->fixed_flags; sc->freq = clkdef->freq; sc->mult = clkdef->mult; diff --git a/sys/dev/extres/clk/clk_fixed.h b/sys/dev/extres/clk/clk_fixed.h index f57ee96..e2643ff 100644 --- a/sys/dev/extres/clk/clk_fixed.h +++ b/sys/dev/extres/clk/clk_fixed.h @@ -47,7 +47,6 @@ struct clk_fixed_def { int fixed_flags; }; -int clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef, - struct mtx *dev_mtx); +int clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef); #endif /*_DEV_EXTRES_CLK_FIXED_H_*/ -- cgit v1.1 From a42e49208d479f9b51659a4a669479cb0425ea83 Mon Sep 17 00:00:00 2001 From: mmel Date: Tue, 15 Mar 2016 15:27:15 +0000 Subject: CLK: Add enumerator for 'clocks' OFW node. Add bus device bindings for clk_fixed class. --- sys/conf/files | 1 + sys/dev/extres/clk/clk.c | 71 ++++++++++++++++ sys/dev/extres/clk/clk.h | 3 + sys/dev/extres/clk/clk_bus.c | 93 +++++++++++++++++++++ sys/dev/extres/clk/clk_fixed.c | 180 +++++++++++++++++++++++++++++++++++++++-- 5 files changed, 341 insertions(+), 7 deletions(-) create mode 100644 sys/dev/extres/clk/clk_bus.c diff --git a/sys/conf/files b/sys/conf/files index 47b68b9..af6be6e 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1413,6 +1413,7 @@ dev/exca/exca.c optional cbb dev/extres/clk/clk.c optional ext_resources clk dev/extres/clk/clkdev_if.m optional ext_resources clk dev/extres/clk/clknode_if.m optional ext_resources clk +dev/extres/clk/clk_bus.c optional ext_resources clk fdt dev/extres/clk/clk_div.c optional ext_resources clk dev/extres/clk/clk_fixed.c optional ext_resources clk dev/extres/clk/clk_gate.c optional ext_resources clk diff --git a/sys/dev/extres/clk/clk.c b/sys/dev/extres/clk/clk.c index f1ed098..761fb0a 100644 --- a/sys/dev/extres/clk/clk.c +++ b/sys/dev/extres/clk/clk.c @@ -1258,4 +1258,75 @@ clk_get_by_ofw_name(device_t dev, const char *name, clk_t *clk) return (rv); return (clk_get_by_ofw_index(dev, idx, clk)); } + +/* -------------------------------------------------------------------------- + * + * Support functions for parsing various clock related OFW things. + */ + +/* + * Get "clock-output-names" and (optional) "clock-indices" lists. + * Both lists are alocated using M_OFWPROP specifier. + * + * Returns number of items or 0. + */ +int +clk_parse_ofw_out_names(device_t dev, phandle_t node, const char ***out_names, + uint32_t *indices) +{ + int name_items, rv; + + *out_names = NULL; + indices = NULL; + if (!OF_hasprop(node, "clock-output-names")) + return (0); + rv = ofw_bus_string_list_to_array(node, "clock-output-names", + out_names); + if (rv <= 0) + return (0); + name_items = rv; + + if (!OF_hasprop(node, "clock-indices")) + return (name_items); + rv = OF_getencprop_alloc(node, "clock-indices", sizeof (uint32_t), + (void **)indices); + if (rv != name_items) { + device_printf(dev, " Size of 'clock-output-names' and " + "'clock-indices' differs\n"); + free(*out_names, M_OFWPROP); + free(indices, M_OFWPROP); + return (0); + } + return (name_items); +} + +/* + * Get output clock name for single output clock node. + */ +int +clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name) +{ + const char **out_names; + const char *tmp_name; + int rv; + + *name = NULL; + if (!OF_hasprop(node, "clock-output-names")) { + tmp_name = ofw_bus_get_name(dev); + if (tmp_name == NULL) + return (ENXIO); + *name = strdup(tmp_name, M_OFWPROP); + return (0); + } + rv = ofw_bus_string_list_to_array(node, "clock-output-names", + &out_names); + if (rv != 1) { + free(out_names, M_OFWPROP); + device_printf(dev, "Malformed 'clock-output-names' property\n"); + return (ENXIO); + } + *name = strdup(out_names[0], M_OFWPROP); + free(out_names, M_OFWPROP); + return (0); +} #endif diff --git a/sys/dev/extres/clk/clk.h b/sys/dev/extres/clk/clk.h index 8547bae..f474252 100644 --- a/sys/dev/extres/clk/clk.h +++ b/sys/dev/extres/clk/clk.h @@ -131,6 +131,9 @@ const char *clk_get_name(clk_t clk); #ifdef FDT int clk_get_by_ofw_index(device_t dev, int idx, clk_t *clk); int clk_get_by_ofw_name(device_t dev, const char *name, clk_t *clk); +int clk_parse_ofw_out_names(device_t dev, phandle_t node, + const char ***out_names, uint32_t *indices); +int clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name); #endif #endif /* _DEV_EXTRES_CLK_H_ */ diff --git a/sys/dev/extres/clk/clk_bus.c b/sys/dev/extres/clk/clk_bus.c new file mode 100644 index 0000000..d875c24 --- /dev/null +++ b/sys/dev/extres/clk/clk_bus.c @@ -0,0 +1,93 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 + +struct ofw_clkbus_softc { + struct simplebus_softc simplebus_sc; +}; + +static int +ofw_clkbus_probe(device_t dev) +{ + const char *name; + + name = ofw_bus_get_name(dev); + + if (name == NULL || strcmp(name, "clocks") != 0) + return (ENXIO); + + device_set_desc(dev, "OFW clocks bus"); + + return (0); +} + +static int +ofw_clkbus_attach(device_t dev) +{ + struct ofw_clkbus_softc *sc; + phandle_t node, child; + device_t cdev; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + simplebus_init(dev, node); + + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); + if (cdev != NULL) + device_probe_and_attach(cdev); + } + + return (bus_generic_attach(dev)); +} + +static device_method_t ofw_clkbus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ofw_clkbus_probe), + DEVMETHOD(device_attach, ofw_clkbus_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(ofw_clkbus, ofw_clkbus_driver, ofw_clkbus_methods, + sizeof(struct ofw_clkbus_softc), simplebus_driver); +static devclass_t ofw_clkbus_devclass; +EARLY_DRIVER_MODULE(ofw_clkbus, simplebus, ofw_clkbus_driver, + ofw_clkbus_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ofw_clkbus, 1); diff --git a/sys/dev/extres/clk/clk_fixed.c b/sys/dev/extres/clk/clk_fixed.c index de0d9bb..8402cbc 100644 --- a/sys/dev/extres/clk/clk_fixed.c +++ b/sys/dev/extres/clk/clk_fixed.c @@ -27,7 +27,6 @@ #include __FBSDID("$FreeBSD$"); - #include #include #include @@ -35,15 +34,24 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include +#include +#include #include +#define CLK_TYPE_FIXED 1 +#define CLK_TYPE_FIXED_FACTOR 2 + static int clknode_fixed_init(struct clknode *clk, device_t dev); static int clknode_fixed_recalc(struct clknode *clk, uint64_t *freq); +static int clknode_fixed_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop); + struct clknode_fixed_sc { int fixed_flags; uint64_t freq; @@ -55,6 +63,7 @@ static clknode_method_t clknode_fixed_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, clknode_fixed_init), CLKNODEMETHOD(clknode_recalc_freq, clknode_fixed_recalc), + CLKNODEMETHOD(clknode_set_freq, clknode_fixed_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(clknode_fixed, clknode_fixed_class, clknode_fixed_methods, @@ -77,12 +86,31 @@ clknode_fixed_recalc(struct clknode *clk, uint64_t *freq) struct clknode_fixed_sc *sc; sc = clknode_get_softc(clk); - if (sc->freq != 0) - *freq = sc->freq; - else if ((sc->mult != 0) && (sc->div != 0)) + + if ((sc->mult != 0) && (sc->div != 0)) *freq = (*freq / sc->div) * sc->mult; else - *freq = 0; + *freq = sc->freq; + return (0); +} + +static int +clknode_fixed_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct clknode_fixed_sc *sc; + + sc = clknode_get_softc(clk); + if (sc->mult == 0 || sc->div == 0) { + /* Fixed frequency clock. */ + *stop = 1; + if (*fout != sc->freq) + return (ERANGE); + return (0); + } + /* Fixed factor clock. */ + *stop = 0; + *fout = (*fout / sc->mult) * sc->div; return (0); } @@ -92,8 +120,6 @@ clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef) struct clknode *clk; struct clknode_fixed_sc *sc; - if ((clkdef->freq == 0) && (clkdef->clkdef.parent_cnt == 0)) - panic("fixed clk: Frequency is not defined for clock source"); clk = clknode_create(clkdom, &clknode_fixed_class, &clkdef->clkdef); if (clk == NULL) return (1); @@ -107,3 +133,143 @@ clknode_fixed_register(struct clkdom *clkdom, struct clk_fixed_def *clkdef) clknode_register(clkdom, clk); return (0); } + +#ifdef FDT + +static struct ofw_compat_data compat_data[] = { + {"fixed-clock", CLK_TYPE_FIXED}, + {"fixed-factor-clock", CLK_TYPE_FIXED_FACTOR}, + {NULL, 0}, +}; + +struct clk_fixed_softc { + device_t dev; + struct clkdom *clkdom; +}; + +static int +clk_fixed_probe(device_t dev) +{ + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Fixed clock"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +clk_fixed_init_fixed(struct clk_fixed_softc *sc, phandle_t node, + struct clk_fixed_def *def) +{ + uint32_t freq; + int rv; + + def->clkdef.id = 1; + rv = OF_getencprop(node, "clock-frequency", &freq, sizeof(freq)); + if (rv <= 0) + return (ENXIO); + def->freq = freq; + return (0); +} + +static int +clk_fixed_init_fixed_factor(struct clk_fixed_softc *sc, phandle_t node, + struct clk_fixed_def *def) +{ + int rv; + clk_t parent; + + def->clkdef.id = 1; + rv = OF_getencprop(node, "clock-mult", &def->mult, sizeof(def->mult)); + if (rv <= 0) + return (ENXIO); + rv = OF_getencprop(node, "clock-div", &def->mult, sizeof(def->div)); + if (rv <= 0) + return (ENXIO); + /* Get name of parent clock */ + rv = clk_get_by_ofw_name(sc->dev, "clocks", &parent); + if (rv != 0) + return (ENXIO); + def->clkdef.parent_names = malloc(sizeof(char *), M_OFWPROP, M_WAITOK); + def->clkdef.parent_names[0] = clk_get_name(parent); + def->clkdef.parent_cnt = 1; + clk_release(parent); + return (0); +} + +static int +clk_fixed_attach(device_t dev) +{ + struct clk_fixed_softc *sc; + intptr_t clk_type; + phandle_t node; + struct clk_fixed_def def; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + clk_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + bzero(&def, sizeof(def)); + if (clk_type == CLK_TYPE_FIXED) + rv = clk_fixed_init_fixed(sc, node, &def); + else if (clk_type == CLK_TYPE_FIXED_FACTOR) + rv = clk_fixed_init_fixed_factor(sc, node, &def); + else + rv = ENXIO; + if (rv != 0) { + device_printf(sc->dev, "Cannot FDT parameters.\n"); + goto fail; + } + rv = clk_parse_ofw_clk_name(dev, node, &def.clkdef.name); + if (rv != 0) { + device_printf(sc->dev, "Cannot parse clock name.\n"); + goto fail; + } + sc->clkdom = clkdom_create(dev); + KASSERT(sc->clkdom != NULL, ("Clock domain is NULL")); + + rv = clknode_fixed_register(sc->clkdom, &def); + if (rv != 0) { + device_printf(sc->dev, "Cannot register fixed clock.\n"); + rv = ENXIO; + goto fail; + } + + rv = clkdom_finit(sc->clkdom); + if (rv != 0) { + device_printf(sc->dev, "Clk domain finit fails.\n"); + rv = ENXIO; + goto fail; + } +#ifdef CLK_DEBUG + clkdom_dump(sc->clkdom); +#endif + free(__DECONST(char *, def.clkdef.name), M_OFWPROP); + free(def.clkdef.parent_names, M_OFWPROP); + return (bus_generic_attach(dev)); + +fail: + free(__DECONST(char *, def.clkdef.name), M_OFWPROP); + free(def.clkdef.parent_names, M_OFWPROP); + return (rv); +} + +static device_method_t clk_fixed_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, clk_fixed_probe), + DEVMETHOD(device_attach, clk_fixed_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(clk_fixed, clk_fixed_driver, clk_fixed_methods, + sizeof(struct clk_fixed_softc)); +static devclass_t clk_fixed_devclass; +EARLY_DRIVER_MODULE(clk_fixed, simplebus, clk_fixed_driver, + clk_fixed_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(clk_fixed, 1); + +#endif -- cgit v1.1 From a2b0006e89c7c71c8d1e5613f1ba6336a419ea33 Mon Sep 17 00:00:00 2001 From: mmel Date: Tue, 15 Mar 2016 15:28:24 +0000 Subject: CLK: Add and use explicit locking for access to clock device registers. Implicit locking (for read/write/modify) is not sufficient for complex cases. --- sys/dev/extres/clk/clk_div.c | 13 ++++++++++++- sys/dev/extres/clk/clk_gate.c | 13 +++++++++++-- sys/dev/extres/clk/clk_mux.c | 16 ++++++++++++++-- sys/dev/extres/clk/clkdev_if.m | 31 +++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/sys/dev/extres/clk/clk_div.c b/sys/dev/extres/clk/clk_div.c index fcb0a57..51cc838 100644 --- a/sys/dev/extres/clk/clk_div.c +++ b/sys/dev/extres/clk/clk_div.c @@ -46,6 +46,10 @@ __FBSDID("$FreeBSD$"); CLKDEV_READ_4(clknode_get_device(_clk), off, val) #define MD4(_clk, off, clr, set ) \ CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) static int clknode_div_init(struct clknode *clk, device_t dev); static int clknode_div_recalc(struct clknode *clk, uint64_t *req); @@ -86,7 +90,9 @@ clknode_div_init(struct clknode *clk, device_t dev) sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); rv = RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); if (rv != 0) return (rv); @@ -171,12 +177,17 @@ clknode_div_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, (*fout != (_fin / divider))) return (ERANGE); + DEVICE_LOCK(clk); rv = MD4(clk, sc->offset, (sc->i_mask << sc->i_shift) | (sc->f_mask << sc->f_shift), (i_div << sc->i_shift) | (f_div << sc->f_shift)); - if (rv != 0) + if (rv != 0) { + DEVICE_UNLOCK(clk); return (rv); + } RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + sc->divider = divider; } diff --git a/sys/dev/extres/clk/clk_gate.c b/sys/dev/extres/clk/clk_gate.c index 2107450..e0673fd 100644 --- a/sys/dev/extres/clk/clk_gate.c +++ b/sys/dev/extres/clk/clk_gate.c @@ -46,7 +46,10 @@ __FBSDID("$FreeBSD$"); CLKDEV_READ_4(clknode_get_device(_clk), off, val) #define MD4(_clk, off, clr, set ) \ CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) - +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) static int clknode_gate_init(struct clknode *clk, device_t dev); static int clknode_gate_set_gate(struct clknode *clk, bool enable); @@ -77,7 +80,9 @@ clknode_gate_init(struct clknode *clk, device_t dev) int rv; sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); rv = RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); if (rv != 0) return (rv); reg = (reg >> sc->shift) & sc->mask; @@ -95,11 +100,15 @@ clknode_gate_set_gate(struct clknode *clk, bool enable) sc = clknode_get_softc(clk); sc->ungated = enable; + DEVICE_LOCK(clk); rv = MD4(clk, sc->offset, sc->mask << sc->shift, (sc->ungated ? sc->on_value : sc->off_value) << sc->shift); - if (rv != 0) + if (rv != 0) { + DEVICE_UNLOCK(clk); return (rv); + } RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); return(0); } diff --git a/sys/dev/extres/clk/clk_mux.c b/sys/dev/extres/clk/clk_mux.c index 54e0653..624f587 100644 --- a/sys/dev/extres/clk/clk_mux.c +++ b/sys/dev/extres/clk/clk_mux.c @@ -46,6 +46,10 @@ __FBSDID("$FreeBSD$"); CLKDEV_READ_4(clknode_get_device(_clk), off, val) #define MD4(_clk, off, clr, set ) \ CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) static int clknode_mux_init(struct clknode *clk, device_t dev); static int clknode_mux_set_mux(struct clknode *clk, int idx); @@ -76,9 +80,12 @@ clknode_mux_init(struct clknode *clk, device_t dev) sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); rv = RD4(clk, sc->offset, ®); - if (rv != 0) + DEVICE_UNLOCK(clk); + if (rv != 0) { return (rv); + } reg = (reg >> sc->shift) & sc->mask; clknode_init_parent_idx(clk, reg); return(0); @@ -93,11 +100,16 @@ clknode_mux_set_mux(struct clknode *clk, int idx) sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); rv = MD4(clk, sc->offset, sc->mask << sc->shift, (idx & sc->mask) << sc->shift); - if (rv != 0) + if (rv != 0) { + DEVICE_UNLOCK(clk); return (rv); + } RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + return(0); } diff --git a/sys/dev/extres/clk/clkdev_if.m b/sys/dev/extres/clk/clkdev_if.m index b43d205..710ee46 100644 --- a/sys/dev/extres/clk/clkdev_if.m +++ b/sys/dev/extres/clk/clkdev_if.m @@ -30,6 +30,23 @@ INTERFACE clkdev; +CODE { + #include + static void + clkdev_default_device_lock(device_t dev) + { + + panic("clkdev_device_lock() is not implemented"); + } + + static void + clkdev_default_device_unlock(device_t dev) + { + + panic("clkdev_device_unlock() is not implemented"); + } +} + # # Write single register # @@ -57,3 +74,17 @@ METHOD int modify_4 { uint32_t clear_mask; uint32_t set_mask; }; + +# +# Get exclusive access to underlying device +# +METHOD void device_lock { + device_t dev; +} DEFAULT clkdev_default_device_lock; + +# +# Release exclusive access to underlying device +# +METHOD void device_unlock { + device_t dev; +} DEFAULT clkdev_default_device_unlock; -- cgit v1.1 From c3bb1016b803b5cf66ac3e7c86b585061a944b72 Mon Sep 17 00:00:00 2001 From: mmel Date: Tue, 15 Mar 2016 15:30:17 +0000 Subject: Add regulator framework, a next part of new 'extended resources' family of support frameworks(i.e. clk/reset/phy/tsensors/fuses...). The framework is still far from perfect and probably doesn't have stable interface yet, but we want to start testing it on more real boards and different architectures. --- sys/conf/files | 11 +- sys/dev/extres/regulator/regdev_if.m | 56 ++ sys/dev/extres/regulator/regnode_if.m | 82 +++ sys/dev/extres/regulator/regulator.c | 984 +++++++++++++++++++++++++++++ sys/dev/extres/regulator/regulator.h | 127 ++++ sys/dev/extres/regulator/regulator_bus.c | 89 +++ sys/dev/extres/regulator/regulator_fixed.c | 456 +++++++++++++ sys/dev/extres/regulator/regulator_fixed.h | 44 ++ 8 files changed, 1846 insertions(+), 3 deletions(-) create mode 100644 sys/dev/extres/regulator/regdev_if.m create mode 100644 sys/dev/extres/regulator/regnode_if.m create mode 100644 sys/dev/extres/regulator/regulator.c create mode 100644 sys/dev/extres/regulator/regulator.h create mode 100644 sys/dev/extres/regulator/regulator_bus.c create mode 100644 sys/dev/extres/regulator/regulator_fixed.c create mode 100644 sys/dev/extres/regulator/regulator_fixed.h diff --git a/sys/conf/files b/sys/conf/files index af6be6e..83c7bcf 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1420,6 +1420,11 @@ dev/extres/clk/clk_gate.c optional ext_resources clk dev/extres/clk/clk_mux.c optional ext_resources clk dev/extres/hwreset/hwreset.c optional ext_resources hwreset dev/extres/hwreset/hwreset_if.m optional ext_resources hwreset +dev/extres/regulator/regdev_if.m optional ext_resources regulator +dev/extres/regulator/regnode_if.m optional ext_resources regulator +dev/extres/regulator/regulator.c optional ext_resources regulator +dev/extres/regulator/regulator_bus.c optional ext_resources regulator fdt +dev/extres/regulator/regulator_fixed.c optional ext_resources regulator dev/fatm/if_fatm.c optional fatm pci dev/fb/fbd.c optional fbd | vt dev/fb/fb_if.m standard @@ -1561,10 +1566,10 @@ ipw_monitor.fw optional ipwmonitorfw | ipwfw \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "ipw_monitor.fw" -dev/iscsi/icl.c optional iscsi | ctl -dev/iscsi/icl_conn_if.m optional iscsi | ctl +dev/iscsi/icl.c optional iscsi | ctl +dev/iscsi/icl_conn_if.m optional iscsi | ctl dev/iscsi/icl_proxy.c optional iscsi | ctl -dev/iscsi/icl_soft.c optional iscsi | ctl +dev/iscsi/icl_soft.c optional iscsi | ctl dev/iscsi/iscsi.c optional iscsi scbus dev/iscsi_initiator/iscsi.c optional iscsi_initiator scbus dev/iscsi_initiator/iscsi_subr.c optional iscsi_initiator scbus diff --git a/sys/dev/extres/regulator/regdev_if.m b/sys/dev/extres/regulator/regdev_if.m new file mode 100644 index 0000000..eecbf8b --- /dev/null +++ b/sys/dev/extres/regulator/regdev_if.m @@ -0,0 +1,56 @@ +#- +# Copyright 2016 Michal Meloun +# 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$ +# + +#ifdef FDT +#include +#include +#endif + +#include + +INTERFACE regdev; + +#ifdef FDT + +HEADER { +int regdev_default_ofw_map(device_t , phandle_t, int, pcell_t *, intptr_t *); +} + +# +# map fdt property cells to regulator number +# Returns 0 on success or a standard errno value. +# +METHOD int map { + device_t provider_dev; + phandle_t xref; + int ncells; + pcell_t *cells; + intptr_t *id; +} DEFAULT regdev_default_ofw_map; + +#endif diff --git a/sys/dev/extres/regulator/regnode_if.m b/sys/dev/extres/regulator/regnode_if.m new file mode 100644 index 0000000..d92f26f --- /dev/null +++ b/sys/dev/extres/regulator/regnode_if.m @@ -0,0 +1,82 @@ +#- +# Copyright (c) 2016 Michal Meloun +# 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$ +# + +INTERFACE regnode; +HEADER { + struct regnode; +} + +# +# Initialize regulator +# Returns 0 on success or a standard errno value. +# +METHOD int init { + struct regnode *regnode; +}; + +# +# Enable/disable regulator +# Returns 0 on success or a standard errno value. +# - enable - input. +# - delay - output, delay needed to stabilize voltage (in us) +# +METHOD int enable { + struct regnode *regnode; + bool enable; + int *udelay; +}; + +# +# Get regulator status +# Returns 0 on success or a standard errno value. +# +METHOD int status { + struct regnode *regnode; + int *status; /* REGULATOR_STATUS_* */ +}; + +# +# Set regulator voltage +# Returns 0 on success or a standard errno value. +# - min_uvolt, max_uvolt - input, requested voltage range (in uV) +# - delay - output, delay needed to stabilize voltage (in us) +METHOD int set_voltage { + struct regnode *regnode; + int min_uvolt; + int max_uvolt; + int *udelay; +}; + +# +# Get regulator voltage +# Returns 0 on success or a standard errno value. +# +METHOD int get_voltage { + struct regnode *regnode; + int *uvolt; +}; diff --git a/sys/dev/extres/regulator/regulator.c b/sys/dev/extres/regulator/regulator.c new file mode 100644 index 0000000..b941aa1 --- /dev/null +++ b/sys/dev/extres/regulator/regulator.c @@ -0,0 +1,984 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 + +#ifdef FDT +#include +#include +#include +#endif +#include + +#include "regdev_if.h" + +MALLOC_DEFINE(M_REGULATOR, "regulator", "Regulator framework"); + +/* Forward declarations. */ +struct regulator; +struct regnode; + +typedef TAILQ_HEAD(regnode_list, regnode) regnode_list_t; +typedef TAILQ_HEAD(regulator_list, regulator) regulator_list_t; + +/* Default regulator methods. */ +static int regnode_method_enable(struct regnode *regnode, bool enable, + int *udelay); +static int regnode_method_status(struct regnode *regnode, int *status); +static int regnode_method_set_voltage(struct regnode *regnode, int min_uvolt, + int max_uvolt, int *udelay); +static int regnode_method_get_voltage(struct regnode *regnode, int *uvolt); + +/* + * Regulator controller methods. + */ +static regnode_method_t regnode_methods[] = { + REGNODEMETHOD(regnode_enable, regnode_method_enable), + REGNODEMETHOD(regnode_status, regnode_method_status), + REGNODEMETHOD(regnode_set_voltage, regnode_method_set_voltage), + REGNODEMETHOD(regnode_get_voltage, regnode_method_get_voltage), + + REGNODEMETHOD_END +}; +DEFINE_CLASS_0(regnode, regnode_class, regnode_methods, 0); + +/* + * Regulator node - basic element for modelling SOC and bard power supply + * chains. Its contains producer data. + */ +struct regnode { + KOBJ_FIELDS; + + TAILQ_ENTRY(regnode) reglist_link; /* Global list entry */ + regulator_list_t consumers_list; /* Consumers list */ + + /* Cache for already resolved names */ + struct regnode *parent; /* Resolved parent */ + + /* Details of this device. */ + const char *name; /* Globally unique name */ + const char *parent_name; /* Parent name */ + + device_t pdev; /* Producer device_t */ + void *softc; /* Producer softc */ + intptr_t id; /* Per producer unique id */ +#ifdef FDT + phandle_t ofw_node; /* OFW node of regulator */ +#endif + int flags; /* REGULATOR_FLAGS_ */ + struct sx lock; /* Lock for this regulator */ + int ref_cnt; /* Reference counter */ + int enable_cnt; /* Enabled counter */ + + struct regnode_std_param std_param; /* Standard parameters */ +}; + +/* + * Per consumer data, information about how a consumer is using a regulator + * node. + * A pointer to this structure is used as a handle in the consumer interface. + */ +struct regulator { + device_t cdev; /* Consumer device */ + struct regnode *regnode; + TAILQ_ENTRY(regulator) link; /* Consumers list entry */ + + int enable_cnt; + int min_uvolt; /* Requested uvolt range */ + int max_uvolt; +}; + +/* + * Regulator names must be system wide unique. + */ +static regnode_list_t regnode_list = TAILQ_HEAD_INITIALIZER(regnode_list); + +static struct sx regnode_topo_lock; +SX_SYSINIT(regulator_topology, ®node_topo_lock, "Regulator topology lock"); + +#define REG_TOPO_SLOCK() sx_slock(®node_topo_lock) +#define REG_TOPO_XLOCK() sx_xlock(®node_topo_lock) +#define REG_TOPO_UNLOCK() sx_unlock(®node_topo_lock) +#define REG_TOPO_ASSERT() sx_assert(®node_topo_lock, SA_LOCKED) +#define REG_TOPO_XASSERT() sx_assert(®node_topo_lock, SA_XLOCKED) + +#define REGNODE_SLOCK(_sc) sx_slock(&((_sc)->lock)) +#define REGNODE_XLOCK(_sc) sx_xlock(&((_sc)->lock)) +#define REGNODE_UNLOCK(_sc) sx_unlock(&((_sc)->lock)) + +/* ---------------------------------------------------------------------------- + * + * Default regulator methods for base class. + * + */ +static int +regnode_method_enable(struct regnode *regnode, bool enable, int *udelay) +{ + + if (!enable) + return (ENXIO); + + *udelay = 0; + return (0); +} + +static int +regnode_method_status(struct regnode *regnode, int *status) +{ + *status = REGULATOR_STATUS_ENABLED; + return (0); +} + +static int +regnode_method_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, + int *udelay) +{ + + if ((min_uvolt > regnode->std_param.max_uvolt) || + (max_uvolt < regnode->std_param.min_uvolt)) + return (ERANGE); + *udelay = 0; + return (0); +} + +static int +regnode_method_get_voltage(struct regnode *regnode, int *uvolt) +{ + + return (regnode->std_param.min_uvolt + + (regnode->std_param.max_uvolt - regnode->std_param.min_uvolt) / 2); +} + +/* ---------------------------------------------------------------------------- + * + * Internal functions. + * + */ + +static struct regnode * +regnode_find_by_name(const char *name) +{ + struct regnode *entry; + + REG_TOPO_ASSERT(); + + TAILQ_FOREACH(entry, ®node_list, reglist_link) { + if (strcmp(entry->name, name) == 0) + return (entry); + } + return (NULL); +} + +static struct regnode * +regnode_find_by_id(device_t dev, intptr_t id) +{ + struct regnode *entry; + + REG_TOPO_ASSERT(); + + TAILQ_FOREACH(entry, ®node_list, reglist_link) { + if ((entry->pdev == dev) && (entry->id == id)) + return (entry); + } + + return (NULL); +} + +/* + * Create and initialize regulator object, but do not register it. + */ +struct regnode * +regnode_create(device_t pdev, regnode_class_t regnode_class, + struct regnode_init_def *def) +{ + struct regnode *regnode; + + KASSERT(def->name != NULL, ("regulator name is NULL")); + KASSERT(def->name[0] != '\0', ("regulator name is empty")); + + REG_TOPO_SLOCK(); + if (regnode_find_by_name(def->name) != NULL) + panic("Duplicated regulator registration: %s\n", def->name); + REG_TOPO_UNLOCK(); + + /* Create object and initialize it. */ + regnode = malloc(sizeof(struct regnode), M_REGULATOR, + M_WAITOK | M_ZERO); + kobj_init((kobj_t)regnode, (kobj_class_t)regnode_class); + sx_init(®node->lock, "Regulator node lock"); + + /* Allocate softc if required. */ + if (regnode_class->size > 0) { + regnode->softc = malloc(regnode_class->size, M_REGULATOR, + M_WAITOK | M_ZERO); + } + + + /* Copy all strings unless they're flagged as static. */ + if (def->flags & REGULATOR_FLAGS_STATIC) { + regnode->name = def->name; + regnode->parent_name = def->parent_name; + } else { + regnode->name = strdup(def->name, M_REGULATOR); + if (def->parent_name != NULL) + regnode->parent_name = strdup(def->parent_name, + M_REGULATOR); + } + + /* Rest of init. */ + TAILQ_INIT(®node->consumers_list); + regnode->id = def->id; + regnode->pdev = pdev; + regnode->flags = def->flags; + regnode->parent = NULL; + regnode->std_param = def->std_param; +#ifdef FDT + regnode->ofw_node = def->ofw_node; +#endif + + return (regnode); +} + +/* Register regulator object. */ +struct regnode * +regnode_register(struct regnode *regnode) +{ + int rv; + +#ifdef FDT + if (regnode->ofw_node <= 0) + regnode->ofw_node = ofw_bus_get_node(regnode->pdev); + if (regnode->ofw_node <= 0) + return (NULL); +#endif + + rv = REGNODE_INIT(regnode); + if (rv != 0) { + printf("REGNODE_INIT failed: %d\n", rv); + return (NULL); + } + + REG_TOPO_XLOCK(); + TAILQ_INSERT_TAIL(®node_list, regnode, reglist_link); + REG_TOPO_UNLOCK(); +#ifdef FDT + OF_device_register_xref(OF_xref_from_node(regnode->ofw_node), + regnode->pdev); +#endif + return (regnode); +} + +static int +regnode_resolve_parent(struct regnode *regnode) +{ + + /* All ready resolved or no parent? */ + if ((regnode->parent != NULL) || + (regnode->parent_name == NULL)) + return (0); + + regnode->parent = regnode_find_by_name(regnode->parent_name); + if (regnode->parent == NULL) + return (ENODEV); + return (0); +} + +static void +regnode_delay(int usec) +{ + int ticks; + + if (usec == 0) + return; + ticks = (usec * hz + 999999) / 1000000; + + if (cold || ticks < 2) + DELAY(usec); + else + pause("REGULATOR", ticks); +} + +/* -------------------------------------------------------------------------- + * + * Regulator providers interface + * + */ + +const char * +regnode_get_name(struct regnode *regnode) +{ + + return (regnode->name); +} + +const char * +regnode_get_parent_name(struct regnode *regnode) +{ + + return (regnode->parent_name); +} + +int +regnode_get_flags(struct regnode *regnode) +{ + + return (regnode->flags); +} + +void * +regnode_get_softc(struct regnode *regnode) +{ + + return (regnode->softc); +} + +device_t +regnode_get_device(struct regnode *regnode) +{ + + return (regnode->pdev); +} + +struct regnode_std_param *regnode_get_stdparam(struct regnode *regnode) +{ + + return (®node->std_param); +} + +void regnode_topo_unlock(void) +{ + + REG_TOPO_UNLOCK(); +} + +void regnode_topo_xlock(void) +{ + + REG_TOPO_XLOCK(); +} + +void regnode_topo_slock(void) +{ + + REG_TOPO_SLOCK(); +} + + +/* -------------------------------------------------------------------------- + * + * Real consumers executive + * + */ +struct regnode * +regnode_get_parent(struct regnode *regnode) +{ + int rv; + + REG_TOPO_ASSERT(); + + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (NULL); + + return (regnode->parent); +} + +/* + * Enable regulator. + */ +int +regnode_enable(struct regnode *regnode) +{ + int udelay; + int rv; + + REG_TOPO_ASSERT(); + + /* Enable regulator for each node in chain, starting from source. */ + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (rv); + if (regnode->parent != NULL) { + rv = regnode_enable(regnode->parent); + if (rv != 0) + return (rv); + } + + /* Handle this node. */ + REGNODE_XLOCK(regnode); + if (regnode->enable_cnt == 0) { + rv = REGNODE_ENABLE(regnode, true, &udelay); + if (rv != 0) { + REGNODE_UNLOCK(regnode); + return (rv); + } + regnode_delay(udelay); + } + regnode->enable_cnt++; + REGNODE_UNLOCK(regnode); + return (0); +} + +/* + * Disable regulator. + */ +int +regnode_disable(struct regnode *regnode) +{ + int udelay; + int rv; + + REG_TOPO_ASSERT(); + rv = 0; + + REGNODE_XLOCK(regnode); + /* Disable regulator for each node in chain, starting from consumer. */ + if ((regnode->enable_cnt == 1) && + ((regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0)) { + rv = REGNODE_ENABLE(regnode, false, &udelay); + if (rv != 0) { + REGNODE_UNLOCK(regnode); + return (rv); + } + regnode_delay(udelay); + } + regnode->enable_cnt--; + REGNODE_UNLOCK(regnode); + + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (rv); + if (regnode->parent != NULL) + rv = regnode_disable(regnode->parent); + return (rv); +} + +/* + * Stop regulator. + */ +int +regnode_stop(struct regnode *regnode, int depth) +{ + int udelay; + int rv; + + REG_TOPO_ASSERT(); + rv = 0; + + REGNODE_XLOCK(regnode); + /* The first node must not be enabled. */ + if ((regnode->enable_cnt != 0) && (depth == 0)) { + REGNODE_UNLOCK(regnode); + return (EBUSY); + } + /* Disable regulator for each node in chain, starting from consumer */ + if ((regnode->enable_cnt == 0) && + ((regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0)) { + rv = REGNODE_ENABLE(regnode, false, &udelay); + if (rv != 0) { + REGNODE_UNLOCK(regnode); + return (rv); + } + regnode_delay(udelay); + } + REGNODE_UNLOCK(regnode); + + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (rv); + if (regnode->parent != NULL) + rv = regnode_stop(regnode->parent, depth + 1); + return (rv); +} + +/* + * Get regulator status. (REGULATOR_STATUS_*) + */ +int +regnode_status(struct regnode *regnode, int *status) +{ + int rv; + + REG_TOPO_ASSERT(); + + REGNODE_XLOCK(regnode); + rv = REGNODE_STATUS(regnode, status); + REGNODE_UNLOCK(regnode); + return (rv); +} + +/* + * Get actual regulator voltage. + */ +int +regnode_get_voltage(struct regnode *regnode, int *uvolt) +{ + int rv; + + REG_TOPO_ASSERT(); + + REGNODE_XLOCK(regnode); + rv = REGNODE_GET_VOLTAGE(regnode, uvolt); + REGNODE_UNLOCK(regnode); + + /* Pass call into parent, if regulator is in bypass mode. */ + if (rv == ENOENT) { + rv = regnode_resolve_parent(regnode); + if (rv != 0) + return (rv); + if (regnode->parent != NULL) + rv = regnode_get_voltage(regnode->parent, uvolt); + + } + return (rv); +} + +/* + * Set regulator voltage. + */ +int +regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt) +{ + int udelay; + int rv; + + REG_TOPO_ASSERT(); + + REGNODE_XLOCK(regnode); + + rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, &udelay); + if (rv == 0) + regnode_delay(udelay); + REGNODE_UNLOCK(regnode); + return (rv); +} + +/* + * Consumer variant of regnode_set_voltage(). + */ +static int +regnode_set_voltage_checked(struct regnode *regnode, struct regulator *reg, + int min_uvolt, int max_uvolt) +{ + int udelay; + int all_max_uvolt; + int all_min_uvolt; + struct regulator *tmp; + int rv; + + REG_TOPO_ASSERT(); + + REGNODE_XLOCK(regnode); + /* Return error if requested range is outside of regulator range. */ + if ((min_uvolt > regnode->std_param.max_uvolt) || + (max_uvolt < regnode->std_param.min_uvolt)) { + REGNODE_UNLOCK(regnode); + return (ERANGE); + } + + /* Get actual voltage range for all consumers. */ + all_min_uvolt = regnode->std_param.min_uvolt; + all_max_uvolt = regnode->std_param.max_uvolt; + TAILQ_FOREACH(tmp, ®node->consumers_list, link) { + /* Don't take requestor in account. */ + if (tmp == reg) + continue; + if (all_min_uvolt < tmp->min_uvolt) + all_min_uvolt = tmp->min_uvolt; + if (all_max_uvolt > tmp->max_uvolt) + all_max_uvolt = tmp->max_uvolt; + } + + /* Test if request fits to actual contract. */ + if ((min_uvolt > all_max_uvolt) || + (max_uvolt < all_min_uvolt)) { + REGNODE_UNLOCK(regnode); + return (ERANGE); + } + + /* Adjust new range.*/ + if (min_uvolt < all_min_uvolt) + min_uvolt = all_min_uvolt; + if (max_uvolt > all_max_uvolt) + max_uvolt = all_max_uvolt; + + rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, &udelay); + regnode_delay(udelay); + REGNODE_UNLOCK(regnode); + return (rv); +} + +#ifdef FDT +phandle_t +regnode_get_ofw_node(struct regnode *regnode) +{ + + return (regnode->ofw_node); +} +#endif + +/* -------------------------------------------------------------------------- + * + * Regulator consumers interface. + * + */ +/* Helper function for regulator_get*() */ +static regulator_t +regulator_create(struct regnode *regnode, device_t cdev) +{ + struct regulator *reg; + + REG_TOPO_ASSERT(); + + reg = malloc(sizeof(struct regulator), M_REGULATOR, + M_WAITOK | M_ZERO); + reg->cdev = cdev; + reg->regnode = regnode; + reg->enable_cnt = 0; + + REGNODE_XLOCK(regnode); + regnode->ref_cnt++; + TAILQ_INSERT_TAIL(®node->consumers_list, reg, link); + reg ->min_uvolt = regnode->std_param.min_uvolt; + reg ->max_uvolt = regnode->std_param.max_uvolt; + REGNODE_UNLOCK(regnode); + + return (reg); +} + +int +regulator_enable(regulator_t reg) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + REG_TOPO_SLOCK(); + rv = regnode_enable(regnode); + if (rv == 0) + reg->enable_cnt++; + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_disable(regulator_t reg) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + KASSERT(reg->enable_cnt > 0, + ("Attempt to disable already disabled regulator: %s\n", + regnode->name)); + REG_TOPO_SLOCK(); + rv = regnode_disable(regnode); + if (rv == 0) + reg->enable_cnt--; + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_stop(regulator_t reg) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + KASSERT(reg->enable_cnt == 0, + ("Attempt to stop already enabled regulator: %s\n", regnode->name)); + + REG_TOPO_SLOCK(); + rv = regnode_stop(regnode, 0); + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_status(regulator_t reg, int *status) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + + REG_TOPO_SLOCK(); + rv = regnode_status(regnode, status); + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_get_voltage(regulator_t reg, int *uvolt) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + + REG_TOPO_SLOCK(); + rv = regnode_get_voltage(regnode, uvolt); + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_set_voltage(regulator_t reg, int min_uvolt, int max_uvolt) +{ + struct regnode *regnode; + int rv; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + + REG_TOPO_SLOCK(); + + rv = regnode_set_voltage_checked(regnode, reg, min_uvolt, max_uvolt); + if (rv == 0) { + reg->min_uvolt = min_uvolt; + reg->max_uvolt = max_uvolt; + } + REG_TOPO_UNLOCK(); + return (rv); +} + +const char * +regulator_get_name(regulator_t reg) +{ + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + return (regnode->name); +} + +int +regulator_get_by_name(device_t cdev, const char *name, regulator_t *reg) +{ + struct regnode *regnode; + + REG_TOPO_SLOCK(); + regnode = regnode_find_by_name(name); + if (regnode == NULL) { + REG_TOPO_UNLOCK(); + return (ENODEV); + } + *reg = regulator_create(regnode, cdev); + REG_TOPO_UNLOCK(); + return (0); +} + +int +regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, regulator_t *reg) +{ + struct regnode *regnode; + + REG_TOPO_SLOCK(); + + regnode = regnode_find_by_id(pdev, id); + if (regnode == NULL) { + REG_TOPO_UNLOCK(); + return (ENODEV); + } + *reg = regulator_create(regnode, cdev); + REG_TOPO_UNLOCK(); + + return (0); +} + +int +regulator_release(regulator_t reg) +{ + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + REG_TOPO_SLOCK(); + while (reg->enable_cnt > 0) { + regnode_disable(regnode); + reg->enable_cnt--; + } + REGNODE_XLOCK(regnode); + TAILQ_REMOVE(®node->consumers_list, reg, link); + regnode->ref_cnt--; + REGNODE_UNLOCK(regnode); + REG_TOPO_UNLOCK(); + + free(reg, M_REGULATOR); + return (0); +} + +#ifdef FDT +/* Default DT mapper. */ +int +regdev_default_ofw_map(device_t dev, phandle_t xref, int ncells, + pcell_t *cells, intptr_t *id) +{ + if (ncells == 0) + *id = 1; + else if (ncells == 1) + *id = cells[0]; + else + return (ERANGE); + + return (0); +} + +int +regulator_parse_ofw_stdparam(device_t pdev, phandle_t node, + struct regnode_init_def *def) +{ + phandle_t supply_xref; + struct regnode_std_param *par; + int rv; + + par = &def->std_param; + rv = OF_getprop_alloc(node, "regulator-name", 1, + (void **)&def->name); + if (rv <= 0) { + device_printf(pdev, "%s: Missing regulator name\n", + __func__); + return (ENXIO); + } + + rv = OF_getencprop(node, "regulator-min-microvolt", &par->min_uvolt, + sizeof(par->min_uvolt)); + if (rv <= 0) + par->min_uvolt = 0; + + rv = OF_getencprop(node, "regulator-max-microvolt", &par->max_uvolt, + sizeof(par->max_uvolt)); + if (rv <= 0) + par->max_uvolt = 0; + + rv = OF_getencprop(node, "regulator-min-microamp", &par->min_uamp, + sizeof(par->min_uamp)); + if (rv <= 0) + par->min_uamp = 0; + + rv = OF_getencprop(node, "regulator-max-microamp", &par->max_uamp, + sizeof(par->max_uamp)); + if (rv <= 0) + par->max_uamp = 0; + + rv = OF_getencprop(node, "regulator-ramp-delay", &par->ramp_delay, + sizeof(par->ramp_delay)); + if (rv <= 0) + par->ramp_delay = 0; + + rv = OF_getencprop(node, "regulator-enable-ramp-delay", + &par->enable_delay, sizeof(par->enable_delay)); + if (rv <= 0) + par->enable_delay = 0; + + if (OF_hasprop(node, "regulator-boot-on")) + par->boot_on = 1; + + if (OF_hasprop(node, "regulator-always-on")) + par->always_on = 1; + + if (OF_hasprop(node, "enable-active-high")) + par->enable_active_high = 1; + + rv = OF_getencprop(node, "vin-supply", &supply_xref, + sizeof(supply_xref)); + if (rv >= 0) { + rv = OF_getprop_alloc(supply_xref, "regulator-name", 1, + (void **)&def->parent_name); + if (rv <= 0) + def->parent_name = NULL; + } + return (0); +} + +int +regulator_get_by_ofw_property(device_t cdev, char *name, regulator_t *reg) +{ + phandle_t cnode, *cells; + device_t regdev; + int ncells, rv; + intptr_t id; + + *reg = NULL; + + cnode = ofw_bus_get_node(cdev); + if (cnode <= 0) { + device_printf(cdev, "%s called on not ofw based device\n", + __func__); + return (ENXIO); + } + + cells = NULL; + ncells = OF_getencprop_alloc(cnode, name, sizeof(*cells), + (void **)&cells); + if (ncells <= 0) + return (ENXIO); + + /* Translate xref to device */ + regdev = OF_device_from_xref(cells[0]); + if (regdev == NULL) { + free(cells, M_OFWPROP); + return (ENODEV); + } + + /* Map regulator to number */ + rv = REGDEV_MAP(regdev, cells[0], ncells - 1, cells + 1, &id); + free(cells, M_OFWPROP); + if (rv != 0) + return (rv); + return (regulator_get_by_id(cdev, regdev, id, reg)); +} +#endif diff --git a/sys/dev/extres/regulator/regulator.h b/sys/dev/extres/regulator/regulator.h new file mode 100644 index 0000000..380bad6 --- /dev/null +++ b/sys/dev/extres/regulator/regulator.h @@ -0,0 +1,127 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 _DEV_EXTRES_REGULATOR_H_ +#define _DEV_EXTRES_REGULATOR_H_ +#include "opt_platform.h" + +#include +#ifdef FDT +#include +#endif +#include "regnode_if.h" + +#define REGULATOR_FLAGS_STATIC 0x00000001 /* Static strings */ +#define REGULATOR_FLAGS_NOT_DISABLE 0x00000002 /* Cannot be disabled */ + +#define REGULATOR_STATUS_ENABLED 0x00000001 +#define REGULATOR_STATUS_OVERCURRENT 0x00000002 + +typedef struct regulator *regulator_t; + +/* Standard regulator parameters. */ +struct regnode_std_param { + int min_uvolt; /* In uV */ + int max_uvolt; /* In uV */ + int min_uamp; /* In uA */ + int max_uamp; /* In uA */ + int ramp_delay; /* In uV/usec */ + int enable_delay; /* In usec */ + bool boot_on; /* Is enabled on boot */ + bool always_on; /* Must be enabled */ + int enable_active_high; +}; + +/* Initialization parameters. */ +struct regnode_init_def { + char *name; /* Regulator name */ + char *parent_name; /* Name of parent regulator */ + struct regnode_std_param std_param; /* Standard parameters */ + intptr_t id; /* Regulator ID */ + int flags; /* Flags */ +#ifdef FDT + phandle_t ofw_node; /* OFW node of regulator */ +#endif + +}; + +/* + * Shorthands for constructing method tables. + */ +#define REGNODEMETHOD KOBJMETHOD +#define REGNODEMETHOD_END KOBJMETHOD_END +#define regnode_method_t kobj_method_t +#define regnode_class_t kobj_class_t +DECLARE_CLASS(regnode_class); + +/* Providers interface. */ +struct regnode *regnode_create(device_t pdev, regnode_class_t regnode_class, + struct regnode_init_def *def); +struct regnode *regnode_register(struct regnode *regnode); +const char *regnode_get_name(struct regnode *regnode); +const char *regnode_get_parent_name(struct regnode *regnode); +struct regnode *regnode_get_parent(struct regnode *regnode); +int regnode_get_flags(struct regnode *regnode); +void *regnode_get_softc(struct regnode *regnode); +device_t regnode_get_device(struct regnode *regnode); +struct regnode_std_param *regnode_get_stdparam(struct regnode *regnode); +void regnode_topo_unlock(void); +void regnode_topo_xlock(void); +void regnode_topo_slock(void); + +int regnode_enable(struct regnode *regnode); +int regnode_disable(struct regnode *regnode); +int regnode_stop(struct regnode *regnode, int depth); +int regnode_status(struct regnode *regnode, int *status); +int regnode_get_voltage(struct regnode *regnode, int *uvolt); +int regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt); +#ifdef FDT +phandle_t regnode_get_ofw_node(struct regnode *regnode); +#endif + +/* Consumers interface. */ +int regulator_get_by_name(device_t cdev, const char *name, + regulator_t *regulator); +int regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, + regulator_t *regulator); +int regulator_release(regulator_t regulator); +const char *regulator_get_name(regulator_t regulator); +int regulator_enable(regulator_t reg); +int regulator_disable(regulator_t reg); +int regulator_stop(regulator_t reg); +int regulator_status(regulator_t reg, int *status); +int regulator_get_voltage(regulator_t reg, int *uvolt); +int regulator_set_voltage(regulator_t reg, int min_uvolt, int max_uvolt); + +#ifdef FDT +int regulator_get_by_ofw_property(device_t dev, char *name, regulator_t *reg); +int regulator_parse_ofw_stdparam(device_t dev, phandle_t node, + struct regnode_init_def *def); +#endif + +#endif /* _DEV_EXTRES_REGULATOR_H_ */ diff --git a/sys/dev/extres/regulator/regulator_bus.c b/sys/dev/extres/regulator/regulator_bus.c new file mode 100644 index 0000000..5e7f4c9 --- /dev/null +++ b/sys/dev/extres/regulator/regulator_bus.c @@ -0,0 +1,89 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 + +struct ofw_regulator_bus_softc { + struct simplebus_softc simplebus_sc; +}; + +static int +ofw_regulator_bus_probe(device_t dev) +{ + const char *name; + + name = ofw_bus_get_name(dev); + if (name == NULL || strcmp(name, "regulators") != 0) + return (ENXIO); + device_set_desc(dev, "OFW regulators bus"); + + return (0); +} + +static int +ofw_regulator_bus_attach(device_t dev) +{ + struct ofw_regulator_bus_softc *sc; + phandle_t node, child; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + simplebus_init(dev, node); + + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + simplebus_add_device(dev, child, 0, NULL, -1, NULL); + } + + return (bus_generic_attach(dev)); +} + +static device_method_t ofw_regulator_bus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ofw_regulator_bus_probe), + DEVMETHOD(device_attach, ofw_regulator_bus_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(ofw_regulator_bus, ofw_regulator_bus_driver, + ofw_regulator_bus_methods, sizeof(struct ofw_regulator_bus_softc), + simplebus_driver); +static devclass_t ofw_regulator_bus_devclass; +EARLY_DRIVER_MODULE(ofw_regulator_bus, simplebus, ofw_regulator_bus_driver, + ofw_regulator_bus_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ofw_regulator_bus, 1); diff --git a/sys/dev/extres/regulator/regulator_fixed.c b/sys/dev/extres/regulator/regulator_fixed.c new file mode 100644 index 0000000..5a44a72 --- /dev/null +++ b/sys/dev/extres/regulator/regulator_fixed.c @@ -0,0 +1,456 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 + +#ifdef FDT +#include +#include +#include +#endif +#include +#include + +#include "regdev_if.h" + +MALLOC_DEFINE(M_FIXEDREGULATOR, "fixedregulator", "Fixed regulator"); + +/* GPIO list for shared pins. */ +typedef TAILQ_HEAD(gpio_list, gpio_entry) gpio_list_t; +struct gpio_entry { + TAILQ_ENTRY(gpio_entry) link; + struct gpiobus_pin gpio_pin; + int use_cnt; + int enable_cnt; +}; +static gpio_list_t gpio_list = TAILQ_HEAD_INITIALIZER(gpio_list); +static struct mtx gpio_list_mtx; +MTX_SYSINIT(gpio_list_lock, &gpio_list_mtx, "Regulator GPIO lock", MTX_DEF); + +struct regnode_fixed_sc { + struct regnode_std_param *param; + bool gpio_open_drain; + struct gpio_entry *gpio_entry; +}; + +static int regnode_fixed_init(struct regnode *regnode); +static int regnode_fixed_enable(struct regnode *regnode, bool enable, + int *udelay); +static int regnode_fixed_status(struct regnode *regnode, int *status); + +static regnode_method_t regnode_fixed_methods[] = { + /* Regulator interface */ + REGNODEMETHOD(regnode_init, regnode_fixed_init), + REGNODEMETHOD(regnode_enable, regnode_fixed_enable), + REGNODEMETHOD(regnode_status, regnode_fixed_status), + REGNODEMETHOD_END +}; +DEFINE_CLASS_1(regnode_fixed, regnode_fixed_class, regnode_fixed_methods, + sizeof(struct regnode_fixed_sc), regnode_class); + +/* + * GPIO list functions. + * Two or more regulators can share single GPIO pins, so we must track all + * GPIOs in gpio_list. + * The GPIO pin is registerd and reseved for first consumer, all others share + * gpio_entry with it. + */ +static struct gpio_entry * +regnode_get_gpio_entry(struct gpiobus_pin *gpio_pin) +{ + struct gpio_entry *entry, *tmp; + device_t busdev; + int rv; + + busdev = GPIO_GET_BUS(gpio_pin->dev); + if (busdev == NULL) + return (NULL); + entry = malloc(sizeof(struct gpio_entry), M_FIXEDREGULATOR, + M_WAITOK | M_ZERO); + + mtx_lock(&gpio_list_mtx); + + TAILQ_FOREACH(tmp, &gpio_list, link) { + if (tmp->gpio_pin.dev == gpio_pin->dev && + tmp->gpio_pin.pin == gpio_pin->pin) { + tmp->use_cnt++; + mtx_unlock(&gpio_list_mtx); + free(entry, M_FIXEDREGULATOR); + return (tmp); + } + } + + /* Reserve pin. */ + /* XXX Can we call gpiobus_map_pin() with gpio_list_mtx mutex held? */ + rv = gpiobus_map_pin(busdev, gpio_pin->pin); + if (rv != 0) { + mtx_unlock(&gpio_list_mtx); + free(entry, M_FIXEDREGULATOR); + return (NULL); + } + /* Everything is OK, build new entry and insert it to list. */ + entry->gpio_pin = *gpio_pin; + entry->use_cnt = 1; + TAILQ_INSERT_TAIL(&gpio_list, entry, link); + + mtx_unlock(&gpio_list_mtx); + return (entry); +} + + +/* + * Regulator class implementation. + */ +static int +regnode_fixed_init(struct regnode *regnode) +{ + device_t dev; + struct regnode_fixed_sc *sc; + struct gpiobus_pin *pin; + uint32_t flags; + bool enable; + int rv; + + sc = regnode_get_softc(regnode); + dev = regnode_get_device(regnode); + sc->param = regnode_get_stdparam(regnode); + if (sc->gpio_entry == NULL) + return (0); + pin = &sc->gpio_entry->gpio_pin; + + flags = GPIO_PIN_OUTPUT; + if (sc->gpio_open_drain) + flags |= GPIO_PIN_OPENDRAIN; + enable = sc->param->boot_on || sc->param->always_on; + if (!sc->param->enable_active_high) + enable = !enable; + rv = GPIO_PIN_SET(pin->dev, pin->pin, enable); + if (rv != 0) { + device_printf(dev, "Cannot set GPIO pin: %d\n", pin->pin); + return (rv); + } + rv = GPIO_PIN_SETFLAGS(pin->dev, pin->pin, flags); + if (rv != 0) { + device_printf(dev, "Cannot configure GPIO pin: %d\n", pin->pin); + return (rv); + } + + return (0); +} + +/* + * Enable/disable regulator. + * Take shared GPIO pins in account + */ +static int +regnode_fixed_enable(struct regnode *regnode, bool enable, int *udelay) +{ + device_t dev; + struct regnode_fixed_sc *sc; + struct gpiobus_pin *pin; + int rv; + + sc = regnode_get_softc(regnode); + dev = regnode_get_device(regnode); + + *udelay = 0; + if (sc->param->always_on && !enable) + return (0); + if (sc->gpio_entry == NULL) + return (0); + pin = &sc->gpio_entry->gpio_pin; + if (enable) { + sc->gpio_entry->enable_cnt++; + if (sc->gpio_entry->enable_cnt > 1) + return (0); + } else { + KASSERT(sc->gpio_entry->enable_cnt > 0, + ("Invalid enable count")); + sc->gpio_entry->enable_cnt--; + if (sc->gpio_entry->enable_cnt >= 1) + return (0); + } + if (!sc->param->enable_active_high) + enable = !enable; + rv = GPIO_PIN_SET(pin->dev, pin->pin, enable); + if (rv != 0) { + device_printf(dev, "Cannot set GPIO pin: %d\n", pin->pin); + return (rv); + } + *udelay = sc->param->enable_delay; + return (0); +} + +static int +regnode_fixed_status(struct regnode *regnode, int *status) +{ + struct regnode_fixed_sc *sc; + struct gpiobus_pin *pin; + uint32_t val; + int rv; + + sc = regnode_get_softc(regnode); + + *status = 0; + if (sc->gpio_entry == NULL) { + *status = REGULATOR_STATUS_ENABLED; + return (0); + } + pin = &sc->gpio_entry->gpio_pin; + + rv = GPIO_PIN_GET(pin->dev, pin->pin, &val); + if (rv == 0) { + if (!sc->param->enable_active_high ^ (val != 0)) + *status = REGULATOR_STATUS_ENABLED; + } + return (rv); +} + +int +regnode_fixed_register(device_t dev, struct regnode_fixed_init_def *init_def) +{ + struct regnode *regnode; + struct regnode_fixed_sc *sc; + + regnode = regnode_create(dev, ®node_fixed_class, + &init_def->reg_init_def); + if (regnode == NULL) { + device_printf(dev, "Cannot create regulator.\n"); + return(ENXIO); + } + sc = regnode_get_softc(regnode); + sc->gpio_open_drain = init_def->gpio_open_drain; + if (init_def->gpio_pin != NULL) { + sc->gpio_entry = regnode_get_gpio_entry(init_def->gpio_pin); + if (sc->gpio_entry == NULL) + return(ENXIO); + } + regnode = regnode_register(regnode); + if (regnode == NULL) { + device_printf(dev, "Cannot register regulator.\n"); + return(ENXIO); + } + return (0); +} + +/* + * OFW Driver implementation. + */ +#ifdef FDT + +struct regfix_softc +{ + device_t dev; + bool attach_done; + struct regnode_fixed_init_def init_def; + phandle_t gpio_prodxref; + pcell_t *gpio_cells; + int gpio_ncells; + struct gpiobus_pin gpio_pin; +}; + +static struct ofw_compat_data compat_data[] = { + {"regulator-fixed", 1}, + {NULL, 0}, +}; + +static int +regfix_get_gpio(struct regfix_softc * sc) +{ + device_t busdev; + phandle_t node; + + int rv; + + if (sc->gpio_prodxref == 0) + return (0); + + node = ofw_bus_get_node(sc->dev); + + /* Test if controller exist. */ + sc->gpio_pin.dev = OF_device_from_xref(sc->gpio_prodxref); + if (sc->gpio_pin.dev == NULL) + return (ENODEV); + + /* Test if GPIO bus already exist. */ + busdev = GPIO_GET_BUS(sc->gpio_pin.dev); + if (busdev == NULL) + return (ENODEV); + + rv = gpio_map_gpios(sc->gpio_pin.dev, node, + OF_node_from_xref(sc->gpio_prodxref), sc->gpio_ncells, + sc->gpio_cells, &(sc->gpio_pin.pin), &(sc->gpio_pin.flags)); + if (rv != 0) { + device_printf(sc->dev, "Cannot map the gpio property.\n"); + return (ENXIO); + } + sc->init_def.gpio_pin = &sc->gpio_pin; + return (0); +} + +static int +regfix_parse_fdt(struct regfix_softc * sc) +{ + phandle_t node; + int rv; + struct regnode_init_def *init_def; + + node = ofw_bus_get_node(sc->dev); + init_def = &sc->init_def.reg_init_def; + + rv = regulator_parse_ofw_stdparam(sc->dev, node, init_def); + if (rv != 0) { + device_printf(sc->dev, "Cannot parse standard parameters.\n"); + return(rv); + } + + /* Fixed regulator uses 'startup-delay-us' property for enable_delay */ + rv = OF_getencprop(node, "startup-delay-us", + &init_def->std_param.enable_delay, + sizeof(init_def->std_param.enable_delay)); + if (rv <= 0) + init_def->std_param.enable_delay = 0; + /* GPIO pin */ + if (OF_hasprop(node, "gpio-open-drain")) + sc->init_def.gpio_open_drain = true; + + if (!OF_hasprop(node, "gpio")) + return (0); + rv = ofw_bus_parse_xref_list_alloc(node, "gpio", "#gpio-cells", 0, + &sc->gpio_prodxref, &sc->gpio_ncells, &sc->gpio_cells); + if (rv != 0) { + sc->gpio_prodxref = 0; + device_printf(sc->dev, "Malformed gpio property\n"); + return (ENXIO); + } + return (0); +} + +static void +regfix_new_pass(device_t dev) +{ + struct regfix_softc * sc; + int rv; + + sc = device_get_softc(dev); + bus_generic_new_pass(dev); + + if (sc->attach_done) + return; + + /* Try to get and configure GPIO. */ + rv = regfix_get_gpio(sc); + if (rv != 0) + return; + + /* Register regulator. */ + regnode_fixed_register(sc->dev, &sc->init_def); + sc->attach_done = true; +} + +static int +regfix_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, "Fixed Regulator"); + return (BUS_PROBE_DEFAULT); +} + +static int +regfix_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static int +regfix_attach(device_t dev) +{ + struct regfix_softc * sc; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + + /* Parse FDT data. */ + rv = regfix_parse_fdt(sc); + if (rv != 0) + return(ENXIO); + + /* Fill reset of init. */ + sc->init_def.reg_init_def.id = 1; + sc->init_def.reg_init_def.flags = REGULATOR_FLAGS_STATIC; + + /* Try to get and configure GPIO. */ + rv = regfix_get_gpio(sc); + if (rv != 0) + return (bus_generic_attach(dev)); + + /* Register regulator. */ + regnode_fixed_register(sc->dev, &sc->init_def); + sc->attach_done = true; + + return (bus_generic_attach(dev)); +} + +static device_method_t regfix_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, regfix_probe), + DEVMETHOD(device_attach, regfix_attach), + DEVMETHOD(device_detach, regfix_detach), + /* Bus interface */ + DEVMETHOD(bus_new_pass, regfix_new_pass), + /* Regdev interface */ + DEVMETHOD(regdev_map, regdev_default_ofw_map), + + DEVMETHOD_END +}; + +static devclass_t regfix_devclass; +DEFINE_CLASS_0(regfix, regfix_driver, regfix_methods, + sizeof(struct regfix_softc)); +EARLY_DRIVER_MODULE(regfix, simplebus, regfix_driver, + regfix_devclass, 0, 0, BUS_PASS_BUS); + +#endif /* FDT */ diff --git a/sys/dev/extres/regulator/regulator_fixed.h b/sys/dev/extres/regulator/regulator_fixed.h new file mode 100644 index 0000000..5cc0751 --- /dev/null +++ b/sys/dev/extres/regulator/regulator_fixed.h @@ -0,0 +1,44 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 _DEV_EXTRES_REGULATOR_FIXED_H_ +#define _DEV_EXTRES_REGULATOR_FIXED_H_ + +#include +#include + +struct regnode_fixed_init_def { + struct regnode_init_def reg_init_def; + bool gpio_open_drain; + struct gpiobus_pin *gpio_pin; +}; + +int regnode_fixed_register(device_t dev, + struct regnode_fixed_init_def *init_def); + +#endif /*_DEV_EXTRES_REGULATOR_FIXED_H_*/ -- cgit v1.1 From 41527ba958fb9ed0eaba4608fbedd17108780626 Mon Sep 17 00:00:00 2001 From: mmel Date: Tue, 15 Mar 2016 15:31:17 +0000 Subject: Add phy framework, a next part of new 'extended resources' family of support frameworks (i.e. clk/regulators/tsensors/fuses...). It provides simple unified consumers interface for manipulations with phy (USB/SATA/PCIe) resources. --- sys/conf/files | 2 + sys/dev/extres/phy/phy.c | 235 ++++++++++++++++++++++++++++++++++++++++++++ sys/dev/extres/phy/phy.h | 63 ++++++++++++ sys/dev/extres/phy/phy_if.m | 84 ++++++++++++++++ 4 files changed, 384 insertions(+) create mode 100644 sys/dev/extres/phy/phy.c create mode 100644 sys/dev/extres/phy/phy.h create mode 100644 sys/dev/extres/phy/phy_if.m diff --git a/sys/conf/files b/sys/conf/files index 83c7bcf..1dd46a5 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1418,6 +1418,8 @@ dev/extres/clk/clk_div.c optional ext_resources clk dev/extres/clk/clk_fixed.c optional ext_resources clk dev/extres/clk/clk_gate.c optional ext_resources clk dev/extres/clk/clk_mux.c optional ext_resources clk +dev/extres/phy/phy.c optional ext_resources phy +dev/extres/phy/phy_if.m optional ext_resources phy dev/extres/hwreset/hwreset.c optional ext_resources hwreset dev/extres/hwreset/hwreset_if.m optional ext_resources hwreset dev/extres/regulator/regdev_if.m optional ext_resources regulator diff --git a/sys/dev/extres/phy/phy.c b/sys/dev/extres/phy/phy.c new file mode 100644 index 0000000..0e2c7e1 --- /dev/null +++ b/sys/dev/extres/phy/phy.c @@ -0,0 +1,235 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 "opt_platform.h" +#include +#include +#include +#include +#include + +#ifdef FDT +#include +#include +#endif + +#include + +#include "phy_if.h" + +struct phy { + device_t consumer_dev; /* consumer device*/ + device_t provider_dev; /* provider device*/ + uintptr_t phy_id; /* phy id */ +}; + +MALLOC_DEFINE(M_PHY, "phy", "Phy framework"); + +int +phy_init(device_t consumer, phy_t phy) +{ + + return (PHY_INIT(phy->provider_dev, phy->phy_id, true)); +} + +int +phy_deinit(device_t consumer, phy_t phy) +{ + + return (PHY_INIT(phy->provider_dev, phy->phy_id, false)); +} + + +int +phy_enable(device_t consumer, phy_t phy) +{ + + return (PHY_ENABLE(phy->provider_dev, phy->phy_id, true)); +} + +int +phy_disable(device_t consumer, phy_t phy) +{ + + return (PHY_ENABLE(phy->provider_dev, phy->phy_id, false)); +} + +int +phy_status(device_t consumer, phy_t phy, int *value) +{ + + return (PHY_STATUS(phy->provider_dev, phy->phy_id, value)); +} + +int +phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id, + phy_t *phy_out) +{ + phy_t phy; + + /* Create handle */ + phy = malloc(sizeof(struct phy), M_PHY, + M_WAITOK | M_ZERO); + phy->consumer_dev = consumer_dev; + phy->provider_dev = provider_dev; + phy->phy_id = id; + *phy_out = phy; + return (0); +} + +void +phy_release(phy_t phy) +{ + free(phy, M_PHY); +} + + +#ifdef FDT +int phy_default_map(device_t provider, phandle_t xref, int ncells, + pcell_t *cells, intptr_t *id) +{ + + if (ncells == 0) + *id = 1; + else if (ncells == 1) + *id = cells[0]; + else + return (ERANGE); + + return (0); +} + +int +phy_get_by_ofw_idx(device_t consumer_dev, int idx, phy_t *phy) +{ + phandle_t cnode, xnode; + pcell_t *cells; + device_t phydev; + int ncells, rv; + intptr_t id; + + cnode = ofw_bus_get_node(consumer_dev); + if (cnode <= 0) { + device_printf(consumer_dev, + "%s called on not ofw based device\n", __func__); + return (ENXIO); + } + rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx, + &xnode, &ncells, &cells); + if (rv != 0) + return (rv); + + /* Tranlate provider to device. */ + phydev = OF_device_from_xref(xnode); + if (phydev == NULL) { + free(cells, M_OFWPROP); + return (ENODEV); + } + /* Map phy to number. */ + rv = PHY_MAP(phydev, xnode, ncells, cells, &id); + free(cells, M_OFWPROP); + if (rv != 0) + return (rv); + + return (phy_get_by_id(consumer_dev, phydev, id, phy)); +} + +int +phy_get_by_ofw_name(device_t consumer_dev, char *name, phy_t *phy) +{ + int rv, idx; + phandle_t cnode; + + cnode = ofw_bus_get_node(consumer_dev); + if (cnode <= 0) { + device_printf(consumer_dev, + "%s called on not ofw based device\n", __func__); + return (ENXIO); + } + rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx); + if (rv != 0) + return (rv); + return (phy_get_by_ofw_idx(consumer_dev, idx, phy)); +} + +int +phy_get_by_ofw_property(device_t consumer_dev, char *name, phy_t *phy) +{ + phandle_t cnode; + pcell_t *cells; + device_t phydev; + int ncells, rv; + intptr_t id; + + cnode = ofw_bus_get_node(consumer_dev); + if (cnode <= 0) { + device_printf(consumer_dev, + "%s called on not ofw based device\n", __func__); + return (ENXIO); + } + ncells = OF_getencprop_alloc(cnode, name, sizeof(pcell_t), + (void **)&cells); + if (ncells < 1) + return (ENXIO); + + /* Tranlate provider to device. */ + phydev = OF_device_from_xref(cells[0]); + if (phydev == NULL) { + free(cells, M_OFWPROP); + return (ENODEV); + } + /* Map phy to number. */ + rv = PHY_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id); + free(cells, M_OFWPROP); + if (rv != 0) + return (rv); + + return (phy_get_by_id(consumer_dev, phydev, id, phy)); +} + +void +phy_register_provider(device_t provider_dev) +{ + phandle_t xref, node; + + node = ofw_bus_get_node(provider_dev); + if (node <= 0) + panic("%s called on not ofw based device.\n", __func__); + + xref = OF_xref_from_node(node); + OF_device_register_xref(xref, provider_dev); +} + +void +phy_unregister_provider(device_t provider_dev) +{ + phandle_t xref; + + xref = OF_xref_from_device(provider_dev); + OF_device_register_xref(xref, NULL); +} +#endif diff --git a/sys/dev/extres/phy/phy.h b/sys/dev/extres/phy/phy.h new file mode 100644 index 0000000..93b4fe4 --- /dev/null +++ b/sys/dev/extres/phy/phy.h @@ -0,0 +1,63 @@ +/*- + * Copyright 2016 Michal Meloun + * 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. + * + * $FreeBSD$ + */ + +#ifndef DEV_EXTRES_PHY_H +#define DEV_EXTRES_PHY_H + +#include "opt_platform.h" +#include +#ifdef FDT +#include +#endif + +typedef struct phy *phy_t; + +/* + * Provider interface + */ +#ifdef FDT +void phy_register_provider(device_t provider); +void phy_unregister_provider(device_t provider); +#endif + +/* + * Consumer interface + */ +int phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id, + phy_t *phy); +void phy_release(phy_t phy); +int phy_get_by_ofw_name(device_t consumer, char *name, phy_t *phy); +int phy_get_by_ofw_idx(device_t consumer, int idx, phy_t *phy); +int phy_get_by_ofw_property(device_t consumer, char *name, phy_t *phy); + +int phy_init(device_t consumer, phy_t phy); +int phy_deinit(device_t consumer, phy_t phy); +int phy_enable(device_t consumer, phy_t phy); +int phy_disable(device_t consumer, phy_t phy); +int phy_status(device_t consumer, phy_t phy, int *value); + +#endif /* DEV_EXTRES_PHY_H */ diff --git a/sys/dev/extres/phy/phy_if.m b/sys/dev/extres/phy/phy_if.m new file mode 100644 index 0000000..e3b4e33 --- /dev/null +++ b/sys/dev/extres/phy/phy_if.m @@ -0,0 +1,84 @@ +#- +# Copyright 2016 Michal Meloun +# 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$ +# + +#ifdef FDT +#include +#include +#endif + +INTERFACE phy; + +#ifdef FDT +HEADER { +int phy_default_map(device_t , phandle_t, int, pcell_t *, intptr_t *); +} + +# +# map fdt property cells to phy number +# Returns 0 on success or a standard errno value. +# +METHOD int map { + device_t provider_dev; + phandle_t xref; + int ncells; + pcell_t *cells; + intptr_t *id; +} DEFAULT phy_default_map; +#endif + +# +# Init/deinit phy +# Returns 0 on success or a standard errno value. +# +METHOD int init { + device_t provider_dev; + intptr_t id; + bool inti; +}; + +# +# Enable/disable phy +# Returns 0 on success or a standard errno value. +# +METHOD int enable { + device_t provider_dev; + intptr_t id; + bool enable; +}; + +# +# Get phy status +# Returns 0 on success or a standard errno value. +# +METHOD int status { + device_t provider_dev; + intptr_t id; + int *status; /* PHY_STATUS_* */ +}; + + -- cgit v1.1 From 3d336a82bd544f54ac0afe5755b36affb62c7865 Mon Sep 17 00:00:00 2001 From: kib Date: Tue, 15 Mar 2016 15:42:53 +0000 Subject: The PKRU state size is 4 bytes, its support makes the XSAVE area size non-multiple of 64 bytes. Thereafter, the user state save area is misaligned, which triggers assertion in the debugging kernels, or segmentation violation on accesses for non-debugging configs. Force the desired alignment of the user save area as the fix (workaround is to disable bit 9 in the hw.xsave_mask loader tunable). This correction is required for booting on the upcoming Intel' Purley platform. Reported and tested by: "Pieper, Jeffrey E" , jimharris Sponsored by: The FreeBSD Foundation MFC after: 3 days --- sys/amd64/amd64/vm_machdep.c | 7 ++++--- sys/i386/i386/vm_machdep.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c index 13241ac..ee6dccd 100644 --- a/sys/amd64/amd64/vm_machdep.c +++ b/sys/amd64/amd64/vm_machdep.c @@ -102,8 +102,8 @@ get_pcb_user_save_td(struct thread *td) vm_offset_t p; p = td->td_kstack + td->td_kstack_pages * PAGE_SIZE - - cpu_max_ext_state_size; - KASSERT((p % 64) == 0, ("Unaligned pcb_user_save area")); + roundup2(cpu_max_ext_state_size, XSAVE_AREA_ALIGN); + KASSERT((p % XSAVE_AREA_ALIGN) == 0, ("Unaligned pcb_user_save area")); return ((struct savefpu *)p); } @@ -122,7 +122,8 @@ get_pcb_td(struct thread *td) vm_offset_t p; p = td->td_kstack + td->td_kstack_pages * PAGE_SIZE - - cpu_max_ext_state_size - sizeof(struct pcb); + roundup2(cpu_max_ext_state_size, XSAVE_AREA_ALIGN) - + sizeof(struct pcb); return ((struct pcb *)p); } diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c index 62e99aa..fe6297a 100644 --- a/sys/i386/i386/vm_machdep.c +++ b/sys/i386/i386/vm_machdep.c @@ -127,8 +127,8 @@ get_pcb_user_save_td(struct thread *td) vm_offset_t p; p = td->td_kstack + td->td_kstack_pages * PAGE_SIZE - - cpu_max_ext_state_size; - KASSERT((p % 64) == 0, ("Unaligned pcb_user_save area")); + roundup2(cpu_max_ext_state_size, XSAVE_AREA_ALIGN); + KASSERT((p % XSAVE_AREA_ALIGN) == 0, ("Unaligned pcb_user_save area")); return ((union savefpu *)p); } @@ -147,7 +147,8 @@ get_pcb_td(struct thread *td) vm_offset_t p; p = td->td_kstack + td->td_kstack_pages * PAGE_SIZE - - cpu_max_ext_state_size - sizeof(struct pcb); + roundup2(cpu_max_ext_state_size, XSAVE_AREA_ALIGN) - + sizeof(struct pcb); return ((struct pcb *)p); } -- cgit v1.1 From 659fbd390709e65917737a323a96434f6a8cb02b Mon Sep 17 00:00:00 2001 From: hselasky Date: Tue, 15 Mar 2016 15:47:26 +0000 Subject: Fix witness panic in the ipoib_ioctl() function when unloading the ipoib module. The bpfdetach() function is trying to turn off promiscious mode on the network interface it is attached to while holding a mutex. The fix consists of ignoring any further calls to the ipoib_ioctl() function when the network interface is going to be detached. The ipoib_ioctl() function might sleep. Sponsored by: Mellanox Technologies MFC after: 1 week --- sys/ofed/drivers/infiniband/ulp/ipoib/ipoib.h | 2 ++ sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib.h b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib.h index eb269a4..acf3d54 100644 --- a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib.h @@ -322,6 +322,8 @@ struct ipoib_dev_priv { unsigned long flags; + int gone; + struct mutex vlan_mutex; struct rb_root path_tree; diff --git a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c index 5e69a52..9a68b23 100644 --- a/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -258,6 +258,10 @@ ipoib_ioctl(struct ifnet *ifp, u_long command, caddr_t data) struct ifreq *ifr = (struct ifreq *) data; int error = 0; + /* check if detaching */ + if (priv == NULL || priv->gone != 0) + return (ENXIO); + switch (command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { @@ -794,6 +798,7 @@ ipoib_detach(struct ipoib_dev_priv *priv) dev = priv->dev; if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) { + priv->gone = 1; bpfdetach(dev); if_detach(dev); if_free(dev); -- cgit v1.1 From 64d6d868e84974e6e2f19b11623e9fbca43e2d47 Mon Sep 17 00:00:00 2001 From: hselasky Date: Tue, 15 Mar 2016 15:53:37 +0000 Subject: Use hardware computed Toeplitz hash for incoming flowids Use the Toeplitz hash value as source for the flowid. This makes the hash value more suitable for so-called hash bucket algorithms which are used in the FreeBSD's TCP/IP stack when RSS is enabled. Sponsored by: Mellanox Technologies MFC after: 1 week --- sys/ofed/drivers/net/mlx4/en_rx.c | 3 ++- sys/ofed/drivers/net/mlx4/en_tx.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sys/ofed/drivers/net/mlx4/en_rx.c b/sys/ofed/drivers/net/mlx4/en_rx.c index 7c5b759..16bf597 100644 --- a/sys/ofed/drivers/net/mlx4/en_rx.c +++ b/sys/ofed/drivers/net/mlx4/en_rx.c @@ -616,7 +616,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud goto next; } - mb->m_pkthdr.flowid = cq->ring; + /* forward Toeplitz compatible hash value */ + mb->m_pkthdr.flowid = be32_to_cpu(cqe->immed_rss_invalid); M_HASHTYPE_SET(mb, M_HASHTYPE_OPAQUE); mb->m_pkthdr.rcvif = dev; if (be32_to_cpu(cqe->vlan_my_qpn) & diff --git a/sys/ofed/drivers/net/mlx4/en_tx.c b/sys/ofed/drivers/net/mlx4/en_tx.c index 56bb5b2..4358aa6 100644 --- a/sys/ofed/drivers/net/mlx4/en_tx.c +++ b/sys/ofed/drivers/net/mlx4/en_tx.c @@ -1060,7 +1060,7 @@ mlx4_en_transmit(struct ifnet *dev, struct mbuf *m) /* Compute which queue to use */ if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) { - i = m->m_pkthdr.flowid % priv->tx_ring_num; + i = (m->m_pkthdr.flowid % 128) % priv->tx_ring_num; } else { i = mlx4_en_select_queue(dev, m); -- cgit v1.1 From 0f68f72eaa236e19061b1cd06bdf4ca95cbd9346 Mon Sep 17 00:00:00 2001 From: emaste Date: Tue, 15 Mar 2016 17:32:29 +0000 Subject: Fix copy-n-pasteo in r296899 ukbdmap.h header generation for pc98 The ukbd default is specified by UKBD_DFLT_KEYMAP not ATKBD_DFLT_KEYMAP. PR: 193865 Submitted by: Harald Schmalzbauer --- sys/conf/files.pc98 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98 index 938490c..9dddbdc 100644 --- a/sys/conf/files.pc98 +++ b/sys/conf/files.pc98 @@ -46,7 +46,7 @@ svr4_assym.h optional compat_svr4 \ clean "svr4_assym.h" # ukbdmap.h optional ukbd_dflt_keymap \ - compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${ATKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h; else echo Error: ukbd_dflt_keymap not found; exit 1; fi" \ + compile-with "unset KEYMAP_PATH; for kmpath in ${S:S/sys$/share/}/vt/keymaps ${S:S/sys$/share/}/syscons/keymaps; do kmapfile=$${kmpath}/${UKBD_DFLT_KEYMAP:C/\.kbd$$//}.kbd; if [ -r $${kmapfile} ]; then KEYMAP_PATH=$${kmpath}; fi; done; if [ X$${KEYMAP_PATH} != X ]; then env KEYMAP_PATH=$${KEYMAP_PATH} /usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h; else echo Error: ukbd_dflt_keymap not found; exit 1; fi" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # -- cgit v1.1 From 354e38690890f748b60e89763c7b8c48182447b5 Mon Sep 17 00:00:00 2001 From: emaste Date: Tue, 15 Mar 2016 19:26:32 +0000 Subject: libc: don't build compat functions if building WITHOUT_SYMVER WITHOUT_SYMVER necessarily implies building a system without symver backwards compatability. Sponsored by: The FreeBSD Foundation --- lib/libc/db/mpool/Makefile.inc | 5 ++++- lib/libc/gen/Makefile.inc | 6 ++++-- lib/libc/iconv/Makefile.inc | 6 +++++- lib/libc/posix1e/Makefile.inc | 4 +++- lib/libc/secure/Makefile.inc | 7 ++++--- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/libc/db/mpool/Makefile.inc b/lib/libc/db/mpool/Makefile.inc index bab7e6d..76ec0eb 100644 --- a/lib/libc/db/mpool/Makefile.inc +++ b/lib/libc/db/mpool/Makefile.inc @@ -3,4 +3,7 @@ .PATH: ${LIBC_SRCTOP}/db/mpool -SRCS+= mpool.c mpool-compat.c +SRCS+= mpool.c +.if ${MK_SYMVER} == yes +SRCS+= mpool-compat.c +.endif diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 7de8ce3..a68f5c7 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -49,7 +49,6 @@ SRCS+= __getosreldate.c \ fstab.c \ ftok.c \ fts.c \ - fts-compat.c \ ftw.c \ getbootfile.c \ getbsize.c \ @@ -137,7 +136,6 @@ SRCS+= __getosreldate.c \ ualarm.c \ ulimit.c \ uname.c \ - unvis-compat.c \ usleep.c \ utime.c \ utxdb.c \ @@ -147,6 +145,10 @@ SRCS+= __getosreldate.c \ waitpid.c \ waitid.c \ wordexp.c +.if ${MK_SYMVER} == yes +SRCS+= fts-compat.c \ + unvis-compat.c +.endif .PATH: ${LIBC_SRCTOP}/../../contrib/libc-pwcache SRCS+= pwcache.c pwcache.h diff --git a/lib/libc/iconv/Makefile.inc b/lib/libc/iconv/Makefile.inc index 5442f79..3b24737 100644 --- a/lib/libc/iconv/Makefile.inc +++ b/lib/libc/iconv/Makefile.inc @@ -14,7 +14,11 @@ SRCS+= citrus_bcs.c citrus_bcs_strtol.c citrus_bcs_strtoul.c \ citrus_esdb.c citrus_hash.c citrus_iconv.c citrus_lookup.c \ citrus_lookup_factory.c citrus_mapper.c citrus_memstream.c \ citrus_mmap.c citrus_module.c citrus_none.c citrus_pivot_factory.c \ - citrus_prop.c citrus_stdenc.c bsd_iconv.c iconv_compat.c + citrus_prop.c citrus_stdenc.c bsd_iconv.c +.if ${MK_SYMVER} == yes +SRCS+= iconv_compat.c +.endif + SYM_MAPS+= ${LIBC_SRCTOP}/iconv/Symbol.map .if ${MK_ICONV} == yes diff --git a/lib/libc/posix1e/Makefile.inc b/lib/libc/posix1e/Makefile.inc index 2cc4158..85fed32 100644 --- a/lib/libc/posix1e/Makefile.inc +++ b/lib/libc/posix1e/Makefile.inc @@ -11,7 +11,6 @@ subr_acl_nfs4.c: ${LIBC_SRCTOP}/../../sys/kern/subr_acl_nfs4.c SRCS+= acl_branding.c \ acl_calc_mask.c \ acl_copy.c \ - acl_compat.c \ acl_delete.c \ acl_delete_entry.c \ acl_entry.c \ @@ -36,6 +35,9 @@ SRCS+= acl_branding.c \ mac_get.c \ mac_set.c \ subr_acl_nfs4.c +.if ${MK_SYMVER} == yes +SRCS+= acl_compat.c +.endif SYM_MAPS+=${LIBC_SRCTOP}/posix1e/Symbol.map diff --git a/lib/libc/secure/Makefile.inc b/lib/libc/secure/Makefile.inc index 6f18bde..e2e75c7 100644 --- a/lib/libc/secure/Makefile.inc +++ b/lib/libc/secure/Makefile.inc @@ -5,8 +5,9 @@ .PATH: ${LIBC_SRCTOP}/secure # Sources common to both syscall interfaces: -SRCS+= \ - stack_protector.c \ - stack_protector_compat.c +SRCS+= stack_protector.c +.if ${MK_SYMVER} == yes +SRCS+= stack_protector_compat.c +.endif SYM_MAPS+= ${LIBC_SRCTOP}/secure/Symbol.map -- cgit v1.1 From 533cbdc4b8b6586b3a8879572d47ba3b92d23ff3 Mon Sep 17 00:00:00 2001 From: trasz Date: Tue, 15 Mar 2016 20:42:36 +0000 Subject: Pacify Coverity. MFC after: 1 month Sponsored by: The FreeBSD Foundation --- sys/fs/autofs/autofs_vnops.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sys/fs/autofs/autofs_vnops.c b/sys/fs/autofs/autofs_vnops.c index 515d228..5fe7de9 100644 --- a/sys/fs/autofs/autofs_vnops.c +++ b/sys/fs/autofs/autofs_vnops.c @@ -370,8 +370,10 @@ static size_t autofs_dirent_reclen(const char *name) { size_t reclen; + int error; - autofs_readdir_one(NULL, name, -1, &reclen); + error = autofs_readdir_one(NULL, name, -1, &reclen); + KASSERT(error == 0, ("autofs_readdir_one() failed")); return (reclen); } -- cgit v1.1 From 7521c72f7766b3f7ae1a37b4fb13268d36fa4c12 Mon Sep 17 00:00:00 2001 From: mav Date: Tue, 15 Mar 2016 21:21:28 +0000 Subject: Fix small memory leak on attempt to access deleted snapshot. MFC after: 3 days --- sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c index a2532f8..49becbc 100644 --- a/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c +++ b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c @@ -196,6 +196,7 @@ mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath, VI_UNLOCK(vp); vrele(vp); vfs_unbusy(mp); + vfs_freeopts(mp->mnt_optnew); vfs_mount_destroy(mp); *vpp = NULL; return (error); -- cgit v1.1 From 9bc25213eaf35499ff521d2eac72813f3806aa52 Mon Sep 17 00:00:00 2001 From: emaste Date: Tue, 15 Mar 2016 21:32:46 +0000 Subject: vtfontcvt: support .hex fonts with characters beyond the Unicode BMP This is already supported by the vt(4) vfnt format mapping from code points to glyphs. Update the .hex font parser to accept up to six hex digits. --- usr.bin/vtfontcvt/vtfontcvt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/usr.bin/vtfontcvt/vtfontcvt.c b/usr.bin/vtfontcvt/vtfontcvt.c index 4dadee7..ffe43b5 100644 --- a/usr.bin/vtfontcvt/vtfontcvt.c +++ b/usr.bin/vtfontcvt/vtfontcvt.c @@ -315,12 +315,13 @@ parse_hex(FILE *fp, unsigned int map_idx) if (bytes != NULL) errx(1, "malformed input: Width tag after font data"); set_width(atoi(ln + 9)); - } else if (sscanf(ln, "%4x:", &curchar)) { + } else if (sscanf(ln, "%6x:", &curchar)) { if (bytes == NULL) { bytes = xmalloc(wbytes * height); bytes_r = xmalloc(wbytes * height); } - p = ln + 5; + /* ln is guaranteed to have a colon here. */ + p = strchr(ln, ':') + 1; chars_per_row = strlen(p) / height; dwidth = width; if (chars_per_row / 2 > (width + 7) / 8) -- cgit v1.1 From 9240f72512c0357636b2e19554d6141ee21edc5f Mon Sep 17 00:00:00 2001 From: bdrewery Date: Tue, 15 Mar 2016 22:27:29 +0000 Subject: Partially revert r266227 and stop stripping paths in ldscripts. Specifically this fixes /usr/lib/libc.so stripping the paths to the libraries. The reason for this in r266227 was both likely because ld(1) did not fully respect --sysroot until r291226 and because of the lib32 build. The lib32 build does not use --sysroot into the /usr/lib32 path, rather it only uses -L and -B into the /usr/lib32 path and --sysroot into the normal (64bit) /usr/lib. The _LDSCRIPTROOT was added with the ldscript support in bsd.lib.mk so that it builds a 32-bit-sysrooted pathed ldscript in the object directory and then installs a normal unprefixed version in installworld. This commit also fixes the rebuild during install which was broken in r266227. This commit would break DIRDEPS_BUILD build of lib32 but it does not currently have a way to build it anyhow. For example, before this change we had in /usr/lib/libc.so: GROUP ( libc.so.7 libc_nonshared.a libssp_nonshared.a ) Now it is restored to pre-r266227: GROUP ( /lib/libc.so.7 /usr/lib/libc_nonshared.a /usr/lib/libssp_nonshared.a ) The motivation for this is in testing of lld. From emaste: lld does not have built-in search paths (e.g. /lib, /usr/lib) and relies on -L arguments passed by the caller. As the linker is nearly always invoked from the clang driver this is fine other than the fact that /usr/lib/libc.so is an ldscript that refers to libc.so.7 which is in /lib, not /usr/lib. PR: 207980 Reported by: emaste Submitted by: emaste (based on) Differential Revision: https://reviews.freebsd.org/D5637 --- share/mk/bsd.lib.mk | 5 ++++- share/mk/bsd.sys.mk | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/share/mk/bsd.lib.mk b/share/mk/bsd.lib.mk index 51fb828..4a8fe89 100644 --- a/share/mk/bsd.lib.mk +++ b/share/mk/bsd.lib.mk @@ -235,7 +235,6 @@ ${SHLIB_NAME_FULL}: beforelinking ${SHLIB_LINK:R}.ld: ${.CURDIR}/${SHLIB_LDSCRIPT} sed -e 's,@@SHLIB@@,${_LDSCRIPTROOT}${_SHLIBDIR}/${SHLIB_NAME},g' \ -e 's,@@LIBDIR@@,${_LDSCRIPTROOT}${_LIBDIR},g' \ - -e 's,/[^ ]*/,,g' \ ${.ALLSRC} > ${.TARGET} ${SHLIB_NAME_FULL}: ${SHLIB_LINK:R}.ld @@ -339,6 +338,9 @@ _SHLINSTALLFLAGS:= ${_SHLINSTALLFLAGS${ie}} .if !defined(INTERNALLIB) realinstall: _libinstall .ORDER: beforeinstall _libinstall +.if target(${SHLIB_LINK:R}.ld) +_libinstall: ${SHLIB_LINK:R}.ld +.endif _libinstall: .if defined(LIB) && !empty(LIB) && ${MK_INSTALLLIB} != "no" ${INSTALL} -C -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ @@ -365,6 +367,7 @@ _libinstall: ${INSTALL} -S -C -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ ${_INSTALLFLAGS} ${SHLIB_LINK:R}.ld \ ${DESTDIR}${_LIBDIR}/${SHLIB_LINK} + rm -f ${SHLIB_LINK:R}.ld .for _SHLIB_LINK_LINK in ${SHLIB_LDSCRIPT_LINKS} ${INSTALL_SYMLINK} ${SHLIB_LINK} ${DESTDIR}${_LIBDIR}/${_SHLIB_LINK_LINK} .endfor diff --git a/share/mk/bsd.sys.mk b/share/mk/bsd.sys.mk index cf53d86..0e6a07ee 100644 --- a/share/mk/bsd.sys.mk +++ b/share/mk/bsd.sys.mk @@ -231,7 +231,7 @@ stage_files.shlib: ${_LIBS:M*.so.*} .endif .if defined(SHLIB_LINK) && commands(${SHLIB_LINK:R}.ld) -_LDSCRIPTROOT?= ${STAGE_OBJTOP} +#_LDSCRIPTROOT?= ${STAGE_OBJTOP} STAGE_AS_SETS+= ldscript STAGE_AS.ldscript+= ${SHLIB_LINK:R}.ld stage_as.ldscript: ${SHLIB_LINK:R}.ld -- cgit v1.1 From 383272b18c81b538fa3a9eb5bd3e79ac2b50efe1 Mon Sep 17 00:00:00 2001 From: smh Date: Wed, 16 Mar 2016 01:41:55 +0000 Subject: Prevent invalid ixgbe advertise setting warning Prevent ixgbe outputting "Invalid advertised speed" warning on boot with no customisations by moving test from sysctl handler to set handler. PR: 208022 MFC after: 3 days Sponsored by: Multiplay --- sys/dev/ixgbe/if_ix.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c index f0e218f..5e16fbd 100644 --- a/sys/dev/ixgbe/if_ix.c +++ b/sys/dev/ixgbe/if_ix.c @@ -4749,10 +4749,6 @@ ixgbe_sysctl_advertise(SYSCTL_HANDLER_ARGS) if ((error) || (req->newptr == NULL)) return (error); - /* Checks to validate new value */ - if (adapter->advertise == advertise) /* no change */ - return (0); - return ixgbe_set_advertise(adapter, advertise); } @@ -4763,6 +4759,10 @@ ixgbe_set_advertise(struct adapter *adapter, int advertise) struct ixgbe_hw *hw; ixgbe_link_speed speed; + /* Checks to validate new value */ + if (adapter->advertise == advertise) /* no change */ + return (0); + hw = &adapter->hw; dev = adapter->dev; -- cgit v1.1 From 565ee84920b1a1b26a0ef22571abe4c42fd9c133 Mon Sep 17 00:00:00 2001 From: adrian Date: Wed, 16 Mar 2016 02:07:04 +0000 Subject: [net80211] Begin implementing rate control module stats. * Implement a new ratectl method, which defaults to returning nothing; * Add a top level sysctl (net.wlan.X.rate_stats) to extract it; * Add ratectl info for the 'amrr' module. Tested: * urtwn(4), STA mode Differential Revision: https://reviews.freebsd.org/D5630 --- sys/net80211/ieee80211_amrr.c | 31 ++++++++++++++++++++++++++++++ sys/net80211/ieee80211_ratectl.c | 41 ++++++++++++++++++++++++++++++++++++++++ sys/net80211/ieee80211_ratectl.h | 11 +++++++++++ 3 files changed, 83 insertions(+) diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c index f50334e..7460223 100644 --- a/sys/net80211/ieee80211_amrr.c +++ b/sys/net80211/ieee80211_amrr.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -75,6 +76,7 @@ static void amrr_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *, void *, void *, void *); static void amrr_sysctlattach(struct ieee80211vap *, struct sysctl_ctx_list *, struct sysctl_oid *); +static void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s); /* number of references from net80211 layer */ static int nrefs = 0; @@ -91,6 +93,7 @@ static const struct ieee80211_ratectl amrr = { .ir_tx_complete = amrr_tx_complete, .ir_tx_update = amrr_tx_update, .ir_setinterval = amrr_setinterval, + .ir_node_stats = amrr_node_stats, }; IEEE80211_RATECTL_MODULE(amrr, 1); IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr); @@ -410,3 +413,31 @@ amrr_sysctlattach(struct ieee80211vap *vap, "amrr_min_sucess_threshold", CTLFLAG_RW, &amrr->amrr_min_success_threshold, 0, ""); } + +static void +amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) +{ + int rate; + struct ieee80211_amrr_node *amn = ni->ni_rctls; + struct ieee80211_rateset *rs; + + /* XXX TODO: check locking? */ + + /* XXX TODO: this should be a method */ + if (amrr_node_is_11n(ni)) { + rs = (struct ieee80211_rateset *) &ni->ni_htrates; + rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; + sbuf_printf(s, "rate: MCS %d\n", rate); + } else { + rs = &ni->ni_rates; + rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; + sbuf_printf(s, "rate: %d Mbit\n", rate / 2); + } + + sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); + sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); + sbuf_printf(s, "success: %u\n", amn->amn_success); + sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); + sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); + sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); +} diff --git a/sys/net80211/ieee80211_ratectl.c b/sys/net80211/ieee80211_ratectl.c index 3eff898..e9a0e2f 100644 --- a/sys/net80211/ieee80211_ratectl.c +++ b/sys/net80211/ieee80211_ratectl.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -68,12 +69,52 @@ ieee80211_ratectl_unregister(int type) ratectls[type] = NULL; } +static void +ieee80211_ratectl_sysctl_stats_node_iter(void *arg, struct ieee80211_node *ni) +{ + + struct sbuf *sb = (struct sbuf *) arg; + sbuf_printf(sb, "MAC: %6D\n", ni->ni_macaddr, ":"); + ieee80211_ratectl_node_stats(ni, sb); + sbuf_printf(sb, "\n"); +} + +static int +ieee80211_ratectl_sysctl_stats(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211vap *vap = arg1; + struct ieee80211com *ic = vap->iv_ic; + struct sbuf sb; + int error; + + error = sysctl_wire_old_buffer(req, 0); + if (error) + return (error); + sbuf_new_for_sysctl(&sb, NULL, 8, req); + sbuf_clear_flags(&sb, SBUF_INCLUDENUL); + + IEEE80211_LOCK(ic); + ieee80211_iterate_nodes(&ic->ic_sta, + ieee80211_ratectl_sysctl_stats_node_iter, + &sb); + IEEE80211_UNLOCK(ic); + + error = sbuf_finish(&sb); + sbuf_delete(&sb); + return (error); +} + void ieee80211_ratectl_init(struct ieee80211vap *vap) { if (vap->iv_rate == ratectls[IEEE80211_RATECTL_NONE]) ieee80211_ratectl_set(vap, IEEE80211_RATECTL_AMRR); vap->iv_rate->ir_init(vap); + + /* Attach generic stats sysctl */ + SYSCTL_ADD_PROC(vap->iv_sysctl, SYSCTL_CHILDREN(vap->iv_oid), OID_AUTO, + "rate_stats", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, vap, + 0, ieee80211_ratectl_sysctl_stats, "A", "ratectl node stats"); } void diff --git a/sys/net80211/ieee80211_ratectl.h b/sys/net80211/ieee80211_ratectl.h index 5603509..9765fe7 100644 --- a/sys/net80211/ieee80211_ratectl.h +++ b/sys/net80211/ieee80211_ratectl.h @@ -53,6 +53,7 @@ struct ieee80211_ratectl { const struct ieee80211_node *, void *, void *, void *); void (*ir_setinterval)(const struct ieee80211vap *, int); + void (*ir_node_stats)(struct ieee80211_node *ni, struct sbuf *s); }; void ieee80211_ratectl_register(int, const struct ieee80211_ratectl *); @@ -115,3 +116,13 @@ ieee80211_ratectl_setinterval(const struct ieee80211vap *vap, int msecs) return; vap->iv_rate->ir_setinterval(vap, msecs); } + +static __inline void +ieee80211_ratectl_node_stats(struct ieee80211_node *ni, struct sbuf *s) +{ + const struct ieee80211vap *vap = ni->ni_vap; + + if (vap->iv_rate->ir_node_stats == NULL) + return; + vap->iv_rate->ir_node_stats(ni, s); +} -- cgit v1.1 From d0976574d3998776fb6647b0d464ce2e9bcc4351 Mon Sep 17 00:00:00 2001 From: emaste Date: Wed, 16 Mar 2016 04:05:02 +0000 Subject: kbdcontrol: add -P path option to add keymap search paths PR: 193865 Reviewed by: cem Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D5645 --- usr.sbin/kbdcontrol/kbdcontrol.1 | 9 ++++- usr.sbin/kbdcontrol/kbdcontrol.c | 87 ++++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/usr.sbin/kbdcontrol/kbdcontrol.1 b/usr.sbin/kbdcontrol/kbdcontrol.1 index ea76b53..17e9d11 100644 --- a/usr.sbin/kbdcontrol/kbdcontrol.1 +++ b/usr.sbin/kbdcontrol/kbdcontrol.1 @@ -13,7 +13,7 @@ .\" @(#)kbdcontrol.1 .\" $FreeBSD$ .\" -.Dd January 29, 2008 +.Dd March 16, 2016 .Dt KBDCONTROL 1 .Os .Sh NAME @@ -36,6 +36,7 @@ .Op Fl f Ar # Ar string .Op Fl k Ar keyboard_device .Op Fl L Ar keymap_file +.Op Fl P Ar path .Sh DESCRIPTION The .Nm @@ -171,6 +172,12 @@ and write the compiled from it to stdout. This option is primarily intended for programmers and is probably of little use under normal circumstances. +.It Fl P Ar path +Search for the keymap file in +.Ar path . +The +.Fl P +option may be specified multiple times. .El .Sh ENVIRONMENT The environment variable diff --git a/usr.sbin/kbdcontrol/kbdcontrol.c b/usr.sbin/kbdcontrol/kbdcontrol.c index 0f927ef..f381bd7 100644 --- a/usr.sbin/kbdcontrol/kbdcontrol.c +++ b/usr.sbin/kbdcontrol/kbdcontrol.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include "path.h" #include "lex.h" @@ -112,11 +113,13 @@ static const int repeats[] = { 34, 38, 42, 46, 50, 55, 59, 63, static const int ndelays = (sizeof(delays) / sizeof(int)); static const int nrepeats = (sizeof(repeats) / sizeof(int)); static int hex = 0; +static int paths_configured = 0; static int token; int number; char letter; +static void add_keymap_path(const char *path); static void dump_accent_definition(char *name, accentmap_t *accentmap); static void dump_entry(int value); static void dump_key_definition(char *name, keymap_t *keymap); @@ -142,6 +145,12 @@ static void set_keyrates(char *opt); static void show_kbd_info(void); static void usage(void) __dead2; +struct pathent { + STAILQ_ENTRY(pathent) next; + char *path; +}; +static STAILQ_HEAD(, pathent) pathlist = STAILQ_HEAD_INITIALIZER(pathlist); + /* Detect presence of vt(4). */ static int is_vt4(void) @@ -279,11 +288,11 @@ get_entry(void) } static int -get_definition_line(FILE *fd, keymap_t *keymap, accentmap_t *accentmap) +get_definition_line(FILE *file, keymap_t *keymap, accentmap_t *accentmap) { int c; - yyin = fd; + yyin = file; if (token < 0) token = yylex(); @@ -791,32 +800,57 @@ dump_accent_definition(char *name, accentmap_t *accentmap) } static void +add_keymap_path(const char *path) +{ + struct pathent* pe; + size_t len; + + len = strlen(path); + if ((pe = malloc(sizeof(*pe))) == NULL || + (pe->path = malloc(len + 2)) == NULL) + err(1, "malloc"); + memcpy(pe->path, path, len); + if (len > 0 && path[len - 1] != '/') + pe->path[len++] = '/'; + pe->path[len] = '\0'; + STAILQ_INSERT_TAIL(&pathlist, pe, next); +} + +static void load_keymap(char *opt, int dumponly) { keymap_t keymap; accentmap_t accentmap; - FILE *fd; - int i, j; + struct pathent *pe; + FILE *file; + int j; char *name, *cp; char blank[] = "", keymap_path[] = KEYMAP_PATH; char vt_keymap_path[] = VT_KEYMAP_PATH, dotkbd[] = ".kbd"; - char *prefix[] = {blank, blank, keymap_path, NULL}; char *postfix[] = {blank, dotkbd, NULL}; - if (is_vt4()) - prefix[2] = vt_keymap_path; - cp = getenv("KEYMAP_PATH"); - if (cp != NULL) - asprintf(&(prefix[0]), "%s/", cp); - - fd = NULL; - for (i=0; prefix[i] && fd == NULL; i++) { - for (j=0; postfix[j] && fd == NULL; j++) { - name = mkfullname(prefix[i], opt, postfix[j]); - fd = fopen(name, "r"); + if (!paths_configured) { + cp = getenv("KEYMAP_PATH"); + if (cp != NULL) + add_keymap_path(cp); + add_keymap_path(""); + if (is_vt4()) + add_keymap_path(vt_keymap_path); + else + add_keymap_path(keymap_path); + paths_configured = 1; + } + + file = NULL; + STAILQ_FOREACH(pe, &pathlist, next) { + for (j=0; postfix[j] && file == NULL; j++) { + name = mkfullname(pe->path, opt, postfix[j]); + file = fopen(name, "r"); + if (file != NULL) + break; } } - if (fd == NULL) { + if (file == NULL) { warn("keymap file \"%s\" not found", opt); return; } @@ -824,7 +858,7 @@ load_keymap(char *opt, int dumponly) memset(&accentmap, 0, sizeof(accentmap)); token = -1; while (1) { - if (get_definition_line(fd, &keymap, &accentmap) < 0) + if (get_definition_line(file, &keymap, &accentmap) < 0) break; } if (dumponly) { @@ -841,13 +875,13 @@ load_keymap(char *opt, int dumponly) } if ((keymap.n_keys > 0) && (ioctl(0, PIO_KEYMAP, &keymap) < 0)) { warn("setting keymap"); - fclose(fd); + fclose(file); return; } if ((accentmap.n_accs > 0) && (ioctl(0, PIO_DEADKEYMAP, &accentmap) < 0)) { warn("setting accentmap"); - fclose(fd); + fclose(file); return; } } @@ -1170,7 +1204,7 @@ usage(void) fprintf(stderr, "%s\n%s\n%s\n", "usage: kbdcontrol [-dFKix] [-A name] [-a name] [-b duration.pitch | [quiet.]belltype]", " [-r delay.repeat | speed] [-l mapfile] [-f # string]", -" [-k device] [-L mapfile]"); +" [-k device] [-L mapfile] [-P path]"); exit(1); } @@ -1178,9 +1212,16 @@ usage(void) int main(int argc, char **argv) { + const char *optstring = "A:a:b:df:iKk:Fl:L:P:r:x"; int opt; - while((opt = getopt(argc, argv, "A:a:b:df:iKk:Fl:L:r:x")) != -1) + /* Collect any -P arguments, regardless of where they appear. */ + while ((opt = getopt(argc, argv, optstring)) != -1) + if (opt == 'P') + add_keymap_path(optarg); + + optind = optreset = 1; + while ((opt = getopt(argc, argv, optstring)) != -1) switch(opt) { case 'A': case 'a': @@ -1198,6 +1239,8 @@ main(int argc, char **argv) case 'L': load_keymap(optarg, 1); break; + case 'P': + break; case 'f': set_functionkey(optarg, nextarg(argc, argv, &optind, 'f')); -- cgit v1.1 From 1cab282ecb65bc4e743d463a25435dd44ea8723b Mon Sep 17 00:00:00 2001 From: cem Date: Wed, 16 Mar 2016 04:22:32 +0000 Subject: fail(9): Upstreaming some fail point enhancements This is several year's worth of fail point upgrades done at EMC Isilon. They are interdependent enough that it makes sense to put a single diff up for them. Primarily, we added: - Changing all mainline execution paths to be lockless, which lets us use fail points in more sleep-sensitive areas, and allows more parallel execution - A number of additional commands, including 'pause' that lets us do some interesting deterministic repros of race conditions - The ability to dump the stacks of all threads sleeping on a fail point - A number of other API changes to allow marking up the fail point's context in the code, and firing callbacks before and after execution - A man page update Submitted by: Matthew Bryan Reviewed by: cem (earlier version), jhb, kib, pho With feedback from: bdrewery Sponsored by: EMC / Isilon Storage Division Differential Revision: https://reviews.freebsd.org/D5427 --- share/man/man9/fail.9 | 91 ++++-- sys/kern/kern_fail.c | 776 +++++++++++++++++++++++++++++++++++++-------- sys/kern/subr_sleepqueue.c | 116 +++++++ sys/sys/fail.h | 246 ++++++++++---- sys/sys/sleepqueue.h | 4 + 5 files changed, 1025 insertions(+), 208 deletions(-) diff --git a/share/man/man9/fail.9 b/share/man/man9/fail.9 index 8dbba1b..b5f0882 100644 --- a/share/man/man9/fail.9 +++ b/share/man/man9/fail.9 @@ -26,25 +26,31 @@ .\" .\" $FreeBSD$ .\" -.Dd May 10, 2009 +.Dd February 02, 2016 .Dt FAIL 9 .Os .Sh NAME .Nm KFAIL_POINT_CODE , +.Nm KFAIL_POINT_CODE_FLAGS , +.Nm KFAIL_POINT_CODE_COND , .Nm KFAIL_POINT_RETURN , .Nm KFAIL_POINT_RETURN_VOID , .Nm KFAIL_POINT_ERROR , .Nm KFAIL_POINT_GOTO , +.Nm KFAIL_POINT_SLEEP_CALLBACKS , .Nm fail_point , .Nm DEBUG_FP .Nd fail points .Sh SYNOPSIS .In sys/fail.h .Fn KFAIL_POINT_CODE "parent" "name" "code" +.Fn KFAIL_POINT_CODE_FLAGS "parent" "name" "flags" "code" +.Fn KFAIL_POINT_CODE_COND "parent" "name" "cond" "flags" "code" .Fn KFAIL_POINT_RETURN "parent" "name" .Fn KFAIL_POINT_RETURN_VOID "parent" "name" .Fn KFAIL_POINT_ERROR "parent" "name" "error_var" .Fn KFAIL_POINT_GOTO "parent" "name" "error_var" "label" +.Fn KFAIL_POINT_SLEEP_CALLBACKS "parent" "name" "pre_func" "pre_arg" "post_func" "post_arg" "code" .Sh DESCRIPTION Fail points are used to add code points where errors may be injected in a user controlled fashion. @@ -77,6 +83,42 @@ argument, the evaluation of is derived from the .Fn return value set in the sysctl MIB. +.Pp +Additionally, +.Fn KFAIL_POINT_CODE_FLAGS +provides a +.Fa flags +argument which controls the fail point's behaviour. +This can be used to e.g., mark the fail point's context as non-sleepable, +which causes the +.Sy sleep +action to be coerced to a busy wait. +The supported flags are: +.Bl -ohang -offset indent +.It FAIL_POINT_USE_TIMEOUT_PATH +Rather than sleeping on a +.Fn sleep +call, just fire the post-sleep function after a timeout fires. +.It FAIL_POINT_NONSLEEPABLE +Mark the fail point as being in a non-sleepable context, which coerces +.Fn sleep +calls to +.Fn delay +calls. +.El +.Pp +Likewise, +.Fn KFAIL_POINT_CODE_COND +supplies a +.Fa cond +argument, which allows you to set the condition under which the fail point's +code may fire. +This is equivalent to: +.Bd -literal + if (cond) + KFAIL_POINT_CODE_FLAGS(...); + +.Ed See .Sx SYSCTL VARIABLES below. @@ -107,26 +149,12 @@ Many base kernel MIBs can be found in the tree (referenced in code by .Sy DEBUG_FP ) . .Pp -The sysctl variable may be set using the following grammar: +The sysctl variable may be set in a number of ways: .Bd -literal - :: - ( "->" )* - - :: - ( ( "%") | ( "*" ) )* - - [ "(" ")" ] - [ "[pid " "]" ] - - :: - [ "." ] | - "." - - :: - "off" | "return" | "sleep" | "panic" | "break" | "print" + [%][*][(args...)][->] .Ed .Pp -The argument specifies which action to take: +The argument specifies which action to take; it can be one of: .Bl -tag -width ".Dv return" .It Sy off Take no action (does not trigger fail point code) @@ -140,13 +168,23 @@ Panic Break into the debugger, or trap if there is no debugger support .It Sy print Print that the fail point executed +.It Sy pause +Threads sleep at the fail point until the fail point is set to +.Sy off +.It Sy yield +Thread yields the cpu when the fail point is evaluated +.It Sy delay +Similar to sleep, but busy waits the cpu. +(Useful in non-sleepable contexts.) .El .Pp -The % and * modifiers prior to control when +The % and * modifiers prior to control when is executed. -The % form (e.g. "1.2%") can be used to specify a +The % form (e.g. "1.2%") can be used to specify a probability that will execute. -The * form (e.g. "5*") can be used to specify the number of +This is a decimal in the range (0, 100] which can specify up to +1/10,000% precision. +The * form (e.g. "5*") can be used to specify the number of times should be executed before this is disabled. Only the last probability and the last count are used if multiple are specified, i.e. "1.2%2%" is the same as "2%". @@ -191,6 +229,10 @@ Return 5 once, when pid 1234 executes the fail point. .Sh AUTHORS .An -nosplit This manual page was written by +.Pp +.An Matthew Bryan Aq Mt matthew.bryan@isilon.com +and +.Pp .An Zach Loafman Aq Mt zml@FreeBSD.org . .Sh CAVEATS It is easy to shoot yourself in the foot by setting fail points too @@ -206,3 +248,10 @@ Currently, .Fn fail_point_eval does not verify whether the context is appropriate for calling .Fn msleep . +You can force it to evaluate a +.Sy sleep +action as a +.Sy delay +action by specifying the +.Sy FAIL_POINT_NONSLEEPABLE +flag at the point the fail point is declared. diff --git a/sys/kern/kern_fail.c b/sys/kern/kern_fail.c index 3737aa3..b0a166d 100644 --- a/sys/kern/kern_fail.c +++ b/sys/kern/kern_fail.c @@ -57,12 +57,18 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include +#include #include #ifdef ILOG_DEFINE_FOR_FILE @@ -72,11 +78,45 @@ ILOG_DEFINE_FOR_FILE(L_ISI_FAIL_POINT, L_ILOG, fail_point); static MALLOC_DEFINE(M_FAIL_POINT, "Fail Points", "fail points system"); #define fp_free(ptr) free(ptr, M_FAIL_POINT) #define fp_malloc(size, flags) malloc((size), M_FAIL_POINT, (flags)) +#define fs_free(ptr) fp_free(ptr) +#define fs_malloc() fp_malloc(sizeof(struct fail_point_setting), \ + M_WAITOK | M_ZERO) + + /** + * These define the wchans that are used for sleeping, pausing respectively. + * They are chosen arbitrarily but need to be distinct to the failpoint and + * the sleep/pause distinction. + */ +#define FP_SLEEP_CHANNEL(fp) (void*)(fp) +#define FP_PAUSE_CHANNEL(fp) __DEVOLATILE(void*, &fp->fp_setting) -static struct mtx g_fp_mtx; -MTX_SYSINIT(g_fp_mtx, &g_fp_mtx, "fail point mtx", MTX_DEF); -#define FP_LOCK() mtx_lock(&g_fp_mtx) -#define FP_UNLOCK() mtx_unlock(&g_fp_mtx) +/** + * Don't allow more than this many entries in a fail point set by sysctl. + * The 99.99...% case is to have 1 entry. I can't imagine having this many + * entries, so it should not limit us. Saves on re-mallocs while holding + * a non-sleepable lock. + */ +#define FP_MAX_ENTRY_COUNT 20 + +/* Used to drain sbufs to the sysctl output */ +int fail_sysctl_drain_func(void *, const char *, int); + +/* Head of tailq of struct fail_point_entry */ +TAILQ_HEAD(fail_point_entry_queue, fail_point_entry); + +/** + * fp entries garbage list; outstanding entries are cleaned up in the + * garbage collector + */ +STAILQ_HEAD(fail_point_setting_garbage, fail_point_setting); +static struct fail_point_setting_garbage fp_setting_garbage = + STAILQ_HEAD_INITIALIZER(fp_setting_garbage); +static struct mtx mtx_garbage_list; +MTX_SYSINIT(mtx_garbage_list, &mtx_garbage_list, "fail point garbage mtx", + MTX_SPIN); + +static struct sx sx_fp_set; +SX_SYSINIT(sx_fp_set, &sx_fp_set, "fail point set sx"); /** * Failpoint types. @@ -90,7 +130,11 @@ enum fail_point_t { FAIL_POINT_BREAK, /**< break into the debugger */ FAIL_POINT_PRINT, /**< print a message */ FAIL_POINT_SLEEP, /**< sleep for some msecs */ - FAIL_POINT_NUMTYPES + FAIL_POINT_PAUSE, /**< sleep until failpoint is set to off */ + FAIL_POINT_YIELD, /**< yield the cpu */ + FAIL_POINT_DELAY, /**< busy wait the cpu */ + FAIL_POINT_NUMTYPES, + FAIL_POINT_INVALID = -1 }; static struct { @@ -104,53 +148,307 @@ static struct { [FAIL_POINT_BREAK] = FP_TYPE_NM_LEN("break"), [FAIL_POINT_PRINT] = FP_TYPE_NM_LEN("print"), [FAIL_POINT_SLEEP] = FP_TYPE_NM_LEN("sleep"), + [FAIL_POINT_PAUSE] = FP_TYPE_NM_LEN("pause"), + [FAIL_POINT_YIELD] = FP_TYPE_NM_LEN("yield"), + [FAIL_POINT_DELAY] = FP_TYPE_NM_LEN("delay"), }; +#define FE_COUNT_UNTRACKED (INT_MIN) + /** * Internal structure tracking a single term of a complete failpoint. * @ingroup failpoint_private */ struct fail_point_entry { - enum fail_point_t fe_type; /**< type of entry */ + volatile bool fe_stale; + enum fail_point_t fe_type; /**< type of entry */ int fe_arg; /**< argument to type (e.g. return value) */ int fe_prob; /**< likelihood of firing in millionths */ - int fe_count; /**< number of times to fire, 0 means always */ + int fe_count; /**< number of times to fire, -1 means infinite */ pid_t fe_pid; /**< only fail for this process */ - TAILQ_ENTRY(fail_point_entry) fe_entries; /**< next entry in fail point */ + struct fail_point *fe_parent; /**< backpointer to fp */ + TAILQ_ENTRY(fail_point_entry) fe_entries; /**< next entry ptr */ +}; + +struct fail_point_setting { + STAILQ_ENTRY(fail_point_setting) fs_garbage_link; + struct fail_point_entry_queue fp_entry_queue; + struct fail_point * fs_parent; + struct mtx feq_mtx; /* Gives fail_point_pause something to do. */ }; +/** + * Defines stating the equivalent of probablilty one (100%) + */ +enum { + PROB_MAX = 1000000, /* probability between zero and this number */ + PROB_DIGITS = 6 /* number of zero's in above number */ +}; + +/* Get a ref on an fp's fp_setting */ +static inline struct fail_point_setting *fail_point_setting_get_ref( + struct fail_point *fp); +/* Release a ref on an fp_setting */ +static inline void fail_point_setting_release_ref(struct fail_point *fp); +/* Allocate and initialize a struct fail_point_setting */ +static struct fail_point_setting *fail_point_setting_new(struct + fail_point *); +/* Free a struct fail_point_setting */ +static void fail_point_setting_destroy(struct fail_point_setting *fp_setting); +/* Allocate and initialize a struct fail_point_entry */ +static struct fail_point_entry *fail_point_entry_new(struct + fail_point_setting *); +/* Free a struct fail_point_entry */ +static void fail_point_entry_destroy(struct fail_point_entry *fp_entry); +/* Append fp setting to garbage list */ +static inline void fail_point_setting_garbage_append( + struct fail_point_setting *fp_setting); +/* Swap fp's setting with fp_setting_new */ +static inline struct fail_point_setting * + fail_point_swap_settings(struct fail_point *fp, + struct fail_point_setting *fp_setting_new); +/* Free up any zero-ref setting in the garbage queue */ +static void fail_point_garbage_collect(void); +/* If this fail point's setting are empty, then swap it out to NULL. */ +static inline void fail_point_eval_swap_out(struct fail_point *fp, + struct fail_point_setting *fp_setting); + +bool +fail_point_is_off(struct fail_point *fp) +{ + bool return_val; + struct fail_point_setting *fp_setting; + struct fail_point_entry *ent; + + return_val = true; + + fp_setting = fail_point_setting_get_ref(fp); + if (fp_setting != NULL) { + TAILQ_FOREACH(ent, &fp_setting->fp_entry_queue, + fe_entries) { + if (!ent->fe_stale) { + return_val = false; + break; + } + } + } + fail_point_setting_release_ref(fp); + + return (return_val); +} + +/* Allocate and initialize a struct fail_point_setting */ +static struct fail_point_setting * +fail_point_setting_new(struct fail_point *fp) +{ + struct fail_point_setting *fs_new; + + fs_new = fs_malloc(); + fs_new->fs_parent = fp; + TAILQ_INIT(&fs_new->fp_entry_queue); + mtx_init(&fs_new->feq_mtx, "fail point entries", NULL, MTX_SPIN); + + fail_point_setting_garbage_append(fs_new); + + return (fs_new); +} + +/* Free a struct fail_point_setting */ +static void +fail_point_setting_destroy(struct fail_point_setting *fp_setting) +{ + struct fail_point_entry *ent; + + while (!TAILQ_EMPTY(&fp_setting->fp_entry_queue)) { + ent = TAILQ_FIRST(&fp_setting->fp_entry_queue); + TAILQ_REMOVE(&fp_setting->fp_entry_queue, ent, fe_entries); + fail_point_entry_destroy(ent); + } + + fs_free(fp_setting); +} + +/* Allocate and initialize a struct fail_point_entry */ +static struct fail_point_entry * +fail_point_entry_new(struct fail_point_setting *fp_setting) +{ + struct fail_point_entry *fp_entry; + + fp_entry = fp_malloc(sizeof(struct fail_point_entry), + M_WAITOK | M_ZERO); + fp_entry->fe_parent = fp_setting->fs_parent; + fp_entry->fe_prob = PROB_MAX; + fp_entry->fe_pid = NO_PID; + fp_entry->fe_count = FE_COUNT_UNTRACKED; + TAILQ_INSERT_TAIL(&fp_setting->fp_entry_queue, fp_entry, + fe_entries); + + return (fp_entry); +} + +/* Free a struct fail_point_entry */ +static void +fail_point_entry_destroy(struct fail_point_entry *fp_entry) +{ + + fp_free(fp_entry); +} + +/* Get a ref on an fp's fp_setting */ +static inline struct fail_point_setting * +fail_point_setting_get_ref(struct fail_point *fp) +{ + struct fail_point_setting *fp_setting; + + /* Invariant: if we have a ref, our pointer to fp_setting is safe */ + atomic_add_acq_32(&fp->fp_ref_cnt, 1); + fp_setting = fp->fp_setting; + + return (fp_setting); +} + +/* Release a ref on an fp_setting */ +static inline void +fail_point_setting_release_ref(struct fail_point *fp) +{ + + KASSERT(&fp->fp_ref_cnt > 0, ("Attempting to deref w/no refs")); + atomic_subtract_rel_32(&fp->fp_ref_cnt, 1); +} + +/* Append fp entries to fp garbage list */ +static inline void +fail_point_setting_garbage_append(struct fail_point_setting *fp_setting) +{ + + mtx_lock_spin(&mtx_garbage_list); + STAILQ_INSERT_TAIL(&fp_setting_garbage, fp_setting, + fs_garbage_link); + mtx_unlock_spin(&mtx_garbage_list); +} + +/* Swap fp's entries with fp_setting_new */ +static struct fail_point_setting * +fail_point_swap_settings(struct fail_point *fp, + struct fail_point_setting *fp_setting_new) +{ + struct fail_point_setting *fp_setting_old; + + fp_setting_old = fp->fp_setting; + fp->fp_setting = fp_setting_new; + + return (fp_setting_old); +} + +static inline void +fail_point_eval_swap_out(struct fail_point *fp, + struct fail_point_setting *fp_setting) +{ + + /* We may have already been swapped out and replaced; ignore. */ + if (fp->fp_setting == fp_setting) + fail_point_swap_settings(fp, NULL); +} + +/* Free up any zero-ref entries in the garbage queue */ +static void +fail_point_garbage_collect() +{ + struct fail_point_setting *fs_current, *fs_next; + struct fail_point_setting_garbage fp_ents_free_list; + + /** + * We will transfer the entries to free to fp_ents_free_list while holding + * the spin mutex, then free it after we drop the lock. This avoids + * triggering witness due to sleepable mutexes in the memory + * allocator. + */ + STAILQ_INIT(&fp_ents_free_list); + + mtx_lock_spin(&mtx_garbage_list); + STAILQ_FOREACH_SAFE(fs_current, &fp_setting_garbage, fs_garbage_link, + fs_next) { + if (fs_current->fs_parent->fp_setting != fs_current && + fs_current->fs_parent->fp_ref_cnt == 0) { + STAILQ_REMOVE(&fp_setting_garbage, fs_current, + fail_point_setting, fs_garbage_link); + STAILQ_INSERT_HEAD(&fp_ents_free_list, fs_current, + fs_garbage_link); + } + } + mtx_unlock_spin(&mtx_garbage_list); + + STAILQ_FOREACH_SAFE(fs_current, &fp_ents_free_list, fs_garbage_link, + fs_next) + fail_point_setting_destroy(fs_current); +} + +/* Drain out all refs from this fail point */ +static inline void +fail_point_drain(struct fail_point *fp, int expected_ref) +{ + struct fail_point_setting *entries; + + entries = fail_point_swap_settings(fp, NULL); + /** + * We have unpaused all threads; so we will wait no longer + * than the time taken for the longest remaining sleep, or + * the length of time of a long-running code block. + */ + while (fp->fp_ref_cnt > expected_ref) { + wakeup(FP_PAUSE_CHANNEL(fp)); + tsleep(&fp, PWAIT, "fail_point_drain", hz / 100); + } + fail_point_swap_settings(fp, entries); +} + +static inline void +fail_point_pause(struct fail_point *fp, enum fail_point_return_code *pret, + struct mtx *mtx_sleep) +{ + + if (fp->fp_pre_sleep_fn) + fp->fp_pre_sleep_fn(fp->fp_pre_sleep_arg); + + msleep_spin(FP_PAUSE_CHANNEL(fp), mtx_sleep, "failpt", 0); + + if (fp->fp_post_sleep_fn) + fp->fp_post_sleep_fn(fp->fp_post_sleep_arg); +} + static inline void -fail_point_sleep(struct fail_point *fp, struct fail_point_entry *ent, - int msecs, enum fail_point_return_code *pret) +fail_point_sleep(struct fail_point *fp, int msecs, + enum fail_point_return_code *pret) { - /* convert from millisecs to ticks, rounding up */ - int timo = ((msecs * hz) + 999) / 1000; + int timo; + + /* Convert from millisecs to ticks, rounding up */ + timo = howmany(msecs * hz, 1000); if (timo > 0) { - if (fp->fp_sleep_fn == NULL) { - msleep(fp, &g_fp_mtx, PWAIT, "failpt", timo); + if (!(fp->fp_flags & FAIL_POINT_USE_TIMEOUT_PATH)) { + if (fp->fp_pre_sleep_fn) + fp->fp_pre_sleep_fn(fp->fp_pre_sleep_arg); + + tsleep(FP_SLEEP_CHANNEL(fp), PWAIT, "failpt", timo); + + if (fp->fp_post_sleep_fn) + fp->fp_post_sleep_fn(fp->fp_post_sleep_arg); } else { - timeout(fp->fp_sleep_fn, fp->fp_sleep_arg, timo); + if (fp->fp_pre_sleep_fn) + fp->fp_pre_sleep_fn(fp->fp_pre_sleep_arg); + + timeout(fp->fp_post_sleep_fn, fp->fp_post_sleep_arg, + timo); *pret = FAIL_POINT_RC_QUEUED; } } } - -/** - * Defines stating the equivalent of probablilty one (100%) - */ -enum { - PROB_MAX = 1000000, /* probability between zero and this number */ - PROB_DIGITS = 6, /* number of zero's in above number */ -}; - -static char *parse_fail_point(struct fail_point_entries *, char *); -static char *parse_term(struct fail_point_entries *, char *); +static char *parse_fail_point(struct fail_point_setting *, char *); +static char *parse_term(struct fail_point_setting *, char *); static char *parse_number(int *out_units, int *out_decimal, char *); static char *parse_type(struct fail_point_entry *, char *); -static void free_entry(struct fail_point_entries *, struct fail_point_entry *); -static void clear_entries(struct fail_point_entries *); /** * Initialize a fail_point. The name is formed in a printf-like fashion @@ -167,7 +465,7 @@ fail_point_init(struct fail_point *fp, const char *fmt, ...) char *name; int n; - TAILQ_INIT(&fp->fp_entries); + fp->fp_setting = NULL; fp->fp_flags = 0; /* Figure out the size of the name. */ @@ -185,25 +483,33 @@ fail_point_init(struct fail_point *fp, const char *fmt, ...) fp->fp_name = name; fp->fp_location = ""; fp->fp_flags |= FAIL_POINT_DYNAMIC_NAME; - fp->fp_sleep_fn = NULL; - fp->fp_sleep_arg = NULL; + fp->fp_pre_sleep_fn = NULL; + fp->fp_pre_sleep_arg = NULL; + fp->fp_post_sleep_fn = NULL; + fp->fp_post_sleep_arg = NULL; } /** - * Free the resources held by a fail_point. - * + * Free the resources held by a fail_point, and wake any paused threads. + * Thou shalt not allow threads to hit this fail point after you enter this + * function, nor shall you call this multiple times for a given fp. * @ingroup failpoint */ void fail_point_destroy(struct fail_point *fp) { + fail_point_drain(fp, 0); + if ((fp->fp_flags & FAIL_POINT_DYNAMIC_NAME) != 0) { fp_free(__DECONST(void *, fp->fp_name)); fp->fp_name = NULL; } fp->fp_flags = 0; - clear_entries(&fp->fp_entries); + + sx_xlock(&sx_fp_set); + fail_point_garbage_collect(); + sx_xunlock(&sx_fp_set); } /** @@ -216,21 +522,51 @@ fail_point_destroy(struct fail_point *fp) enum fail_point_return_code fail_point_eval_nontrivial(struct fail_point *fp, int *return_value) { - enum fail_point_return_code ret = FAIL_POINT_RC_CONTINUE; - struct fail_point_entry *ent, *next; + bool execute = false; + struct fail_point_entry *ent; + struct fail_point_setting *fp_setting; + enum fail_point_return_code ret; + int cont; + int count; int msecs; + int usecs; - FP_LOCK(); + ret = FAIL_POINT_RC_CONTINUE; + cont = 0; /* don't continue by default */ - TAILQ_FOREACH_SAFE(ent, &fp->fp_entries, fe_entries, next) { - int cont = 0; /* don't continue by default */ + fp_setting = fail_point_setting_get_ref(fp); + if (fp_setting == NULL) + goto abort; + + TAILQ_FOREACH(ent, &fp_setting->fp_entry_queue, fe_entries) { + + if (ent->fe_stale) + continue; if (ent->fe_prob < PROB_MAX && ent->fe_prob < random() % PROB_MAX) continue; + if (ent->fe_pid != NO_PID && ent->fe_pid != curproc->p_pid) continue; + if (ent->fe_count != FE_COUNT_UNTRACKED) { + count = ent->fe_count; + while (count > 0) { + if (atomic_cmpset_32(&ent->fe_count, count, count - 1)) { + count--; + execute = true; + break; + } + count = ent->fe_count; + } + if (execute == false) + /* We lost the race; consider the entry stale and bail now */ + continue; + if (count == 0) + ent->fe_stale = true; + } + switch (ent->fe_type) { case FAIL_POINT_PANIC: panic("fail point %s panicking", fp->fp_name); @@ -244,7 +580,7 @@ fail_point_eval_nontrivial(struct fail_point *fp, int *return_value) case FAIL_POINT_BREAK: printf("fail point %s breaking to debugger\n", - fp->fp_name); + fp->fp_name); breakpoint(); break; @@ -254,51 +590,95 @@ fail_point_eval_nontrivial(struct fail_point *fp, int *return_value) break; case FAIL_POINT_SLEEP: - /* - * Free the entry now if necessary, since - * we're about to drop the mutex and sleep. - */ msecs = ent->fe_arg; - if (ent->fe_count > 0 && --ent->fe_count == 0) { - free_entry(&fp->fp_entries, ent); - ent = NULL; - } - if (msecs) - fail_point_sleep(fp, ent, msecs, &ret); + fail_point_sleep(fp, msecs, &ret); + break; + + case FAIL_POINT_PAUSE: + /** + * Pausing is inherently strange with multiple + * entries given our design. That is because some + * entries could be unreachable, for instance in cases like: + * pause->return. We can never reach the return entry. + * The sysctl layer actually truncates all entries after + * a pause for this reason. + */ + mtx_lock_spin(&fp_setting->feq_mtx); + fail_point_pause(fp, &ret, &fp_setting->feq_mtx); + mtx_unlock_spin(&fp_setting->feq_mtx); + break; + + case FAIL_POINT_YIELD: + kern_yield(-1); + break; + + case FAIL_POINT_DELAY: + usecs = ent->fe_arg; + DELAY(usecs); break; default: break; } - if (ent != NULL && ent->fe_count > 0 && --ent->fe_count == 0) - free_entry(&fp->fp_entries, ent); if (cont == 0) break; } - /* Get rid of "off"s at the end. */ - while ((ent = TAILQ_LAST(&fp->fp_entries, fail_point_entries)) && - ent->fe_type == FAIL_POINT_OFF) - free_entry(&fp->fp_entries, ent); + if (fail_point_is_off(fp)) + fail_point_eval_swap_out(fp, fp_setting); - FP_UNLOCK(); +abort: + fail_point_setting_release_ref(fp); return (ret); + } /** * Translate internal fail_point structure into human-readable text. */ static void -fail_point_get(struct fail_point *fp, struct sbuf *sb) +fail_point_get(struct fail_point *fp, struct sbuf *sb, + bool verbose) { struct fail_point_entry *ent; + struct fail_point_setting *fp_setting; + struct fail_point_entry *fp_entry_cpy; + int cnt_sleeping; + int idx; + int printed_entry_count; - FP_LOCK(); + cnt_sleeping = 0; + idx = 0; + printed_entry_count = 0; - TAILQ_FOREACH(ent, &fp->fp_entries, fe_entries) { + fp_entry_cpy = fp_malloc(sizeof(struct fail_point_entry) * + (FP_MAX_ENTRY_COUNT + 1), M_WAITOK); + + fp_setting = fail_point_setting_get_ref(fp); + + if (fp_setting != NULL) { + TAILQ_FOREACH(ent, &fp_setting->fp_entry_queue, fe_entries) { + if (ent->fe_stale) + continue; + + KASSERT(printed_entry_count < FP_MAX_ENTRY_COUNT, + ("FP entry list larger than allowed")); + + fp_entry_cpy[printed_entry_count] = *ent; + ++printed_entry_count; + } + } + fail_point_setting_release_ref(fp); + + /* This is our equivalent of a NULL terminator */ + fp_entry_cpy[printed_entry_count].fe_type = FAIL_POINT_INVALID; + + while (idx < printed_entry_count) { + ent = &fp_entry_cpy[idx]; + ++idx; if (ent->fe_prob < PROB_MAX) { int decimal = ent->fe_prob % (PROB_MAX / 100); int units = ent->fe_prob / (PROB_MAX / 100); @@ -313,7 +693,7 @@ fail_point_get(struct fail_point *fp, struct sbuf *sb) } sbuf_printf(sb, "%%"); } - if (ent->fe_count > 0) + if (ent->fe_count >= 0) sbuf_printf(sb, "%d*", ent->fe_count); sbuf_printf(sb, "%s", fail_type_strings[ent->fe_type].name); if (ent->fe_arg) @@ -323,10 +703,29 @@ fail_point_get(struct fail_point *fp, struct sbuf *sb) if (TAILQ_NEXT(ent, fe_entries)) sbuf_printf(sb, "->"); } - if (TAILQ_EMPTY(&fp->fp_entries)) + if (!printed_entry_count) sbuf_printf(sb, "off"); - FP_UNLOCK(); + fp_free(fp_entry_cpy); + if (verbose) { + /* Print number of sleeping threads. queue=0 is the argument + * used by msleep when sending our threads to sleep. */ + sbuf_printf(sb, "\nsleeping_thread_stacks = {\n"); + sleepq_sbuf_print_stacks(sb, FP_SLEEP_CHANNEL(fp), 0, + &cnt_sleeping); + + sbuf_printf(sb, "},\n"); + sbuf_printf(sb, "sleeping_thread_count = %d,\n", + cnt_sleeping); + + sbuf_printf(sb, "paused_thread_stacks = {\n"); + sleepq_sbuf_print_stacks(sb, FP_PAUSE_CHANNEL(fp), 0, + &cnt_sleeping); + + sbuf_printf(sb, "},\n"); + sbuf_printf(sb, "paused_thread_count = %d\n", + cnt_sleeping); + } } /** @@ -336,38 +735,91 @@ fail_point_get(struct fail_point *fp, struct sbuf *sb) static int fail_point_set(struct fail_point *fp, char *buf) { - int error = 0; struct fail_point_entry *ent, *ent_next; - struct fail_point_entries new_entries; + struct fail_point_setting *entries; + bool should_wake_paused; + bool should_truncate; + int error; + + error = 0; + should_wake_paused = false; + should_truncate = false; /* Parse new entries. */ - TAILQ_INIT(&new_entries); - if (!parse_fail_point(&new_entries, buf)) { - clear_entries(&new_entries); + /** + * ref protects our new malloc'd stuff from being garbage collected + * before we link it. + */ + fail_point_setting_get_ref(fp); + entries = fail_point_setting_new(fp); + if (parse_fail_point(entries, buf) == NULL) { + STAILQ_REMOVE(&fp_setting_garbage, entries, + fail_point_setting, fs_garbage_link); + fail_point_setting_destroy(entries); error = EINVAL; goto end; } - FP_LOCK(); - - /* Move new entries in. */ - TAILQ_SWAP(&fp->fp_entries, &new_entries, fail_point_entry, fe_entries); - clear_entries(&new_entries); + /** + * Transfer the entries we are going to keep to a new list. + * Get rid of useless zero probability entries, and entries with hit + * count 0. + * If 'off' is present, and it has no hit count set, then all entries + * after it are discarded since they are unreachable. + */ + TAILQ_FOREACH_SAFE(ent, &entries->fp_entry_queue, fe_entries, ent_next) { + if (ent->fe_prob == 0 || ent->fe_count == 0) { + printf("Discarding entry which cannot execute %s\n", + fail_type_strings[ent->fe_type].name); + TAILQ_REMOVE(&entries->fp_entry_queue, ent, + fe_entries); + fp_free(ent); + continue; + } else if (should_truncate) { + printf("Discarding unreachable entry %s\n", + fail_type_strings[ent->fe_type].name); + TAILQ_REMOVE(&entries->fp_entry_queue, ent, + fe_entries); + fp_free(ent); + continue; + } - /* Get rid of useless zero probability entries. */ - TAILQ_FOREACH_SAFE(ent, &fp->fp_entries, fe_entries, ent_next) { - if (ent->fe_prob == 0) - free_entry(&fp->fp_entries, ent); + if (ent->fe_type == FAIL_POINT_OFF) { + should_wake_paused = true; + if (ent->fe_count == FE_COUNT_UNTRACKED) { + should_truncate = true; + TAILQ_REMOVE(&entries->fp_entry_queue, ent, + fe_entries); + fp_free(ent); + } + } else if (ent->fe_type == FAIL_POINT_PAUSE) { + should_truncate = true; + } else if (ent->fe_type == FAIL_POINT_SLEEP && (fp->fp_flags & + FAIL_POINT_NONSLEEPABLE)) { + /** + * If this fail point is annotated as being in a + * non-sleepable ctx, convert sleep to delay and + * convert the msec argument to usecs. + */ + printf("Sleep call request on fail point in " + "non-sleepable context; using delay instead " + "of sleep\n"); + ent->fe_type = FAIL_POINT_DELAY; + ent->fe_arg *= 1000; + } } - /* Get rid of "off"s at the end. */ - while ((ent = TAILQ_LAST(&fp->fp_entries, fail_point_entries)) && - ent->fe_type == FAIL_POINT_OFF) - free_entry(&fp->fp_entries, ent); - - FP_UNLOCK(); + if (TAILQ_EMPTY(&entries->fp_entry_queue)) { + entries = fail_point_swap_settings(fp, NULL); + if (entries != NULL) + wakeup(FP_PAUSE_CHANNEL(fp)); + } else { + if (should_wake_paused) + wakeup(FP_PAUSE_CHANNEL(fp)); + fail_point_swap_settings(fp, entries); + } - end: +end: #ifdef IWARNING if (error) IWARNING("Failed to set %s %s to %s", @@ -377,6 +829,7 @@ fail_point_set(struct fail_point *fp, char *buf) fp->fp_name, fp->fp_location, buf); #endif /* IWARNING */ + fail_point_setting_release_ref(fp); return (error); } @@ -385,25 +838,33 @@ fail_point_set(struct fail_point *fp, char *buf) /** * Handle kernel failpoint set/get. */ + int fail_point_sysctl(SYSCTL_HANDLER_ARGS) { - struct fail_point *fp = arg1; - char *buf = NULL; + struct fail_point *fp; + char *buf; + struct sbuf *sb_check; struct sbuf sb; int error; - /* Retrieving */ - sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND | SBUF_INCLUDENUL); - fail_point_get(fp, &sb); - sbuf_trim(&sb); - error = sbuf_finish(&sb); - if (error == 0) - error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb)); - sbuf_delete(&sb); + error = 0; + fp = arg1; + buf = NULL; + + sb_check = sbuf_new(&sb, NULL, 1024, SBUF_AUTOEXTEND); + if (sb_check != &sb) + return (ENOMEM); + + sbuf_set_drain(&sb, (sbuf_drain_func *)fail_sysctl_drain_func, req); /* Setting */ - if (!error && req->newptr) { + /** + * Lock protects any new entries from being garbage collected before we + * can link them to the fail point. + */ + sx_xlock(&sx_fp_set); + if (req->newptr) { if (req->newlen > MAX_FAIL_POINT_BUF) { error = EINVAL; goto out; @@ -417,31 +878,95 @@ fail_point_sysctl(SYSCTL_HANDLER_ARGS) buf[req->newlen] = '\0'; error = fail_point_set(fp, buf); - } + } + + fail_point_garbage_collect(); + sx_xunlock(&sx_fp_set); + + /* Retrieving. */ + fail_point_get(fp, &sb, false); out: - fp_free(buf); + sbuf_finish(&sb); + sbuf_delete(&sb); + + if (buf) + fp_free(buf); + return (error); } +int +fail_point_sysctl_status(SYSCTL_HANDLER_ARGS) +{ + struct fail_point *fp; + struct sbuf sb, *sb_check; + + fp = arg1; + + sb_check = sbuf_new(&sb, NULL, 1024, SBUF_AUTOEXTEND); + if (sb_check != &sb) + return (ENOMEM); + + sbuf_set_drain(&sb, (sbuf_drain_func *)fail_sysctl_drain_func, req); + + /* Retrieving. */ + fail_point_get(fp, &sb, true); + + sbuf_finish(&sb); + sbuf_delete(&sb); + + /** + * Lock protects any new entries from being garbage collected before we + * can link them to the fail point. + */ + sx_xlock(&sx_fp_set); + fail_point_garbage_collect(); + sx_xunlock(&sx_fp_set); + + return (0); +} + +int +fail_sysctl_drain_func(void *sysctl_args, const char *buf, int len) +{ + struct sysctl_req *sa; + int error; + + sa = sysctl_args; + + error = SYSCTL_OUT(sa, buf, len); + + if (error == ENOMEM) + return (-1); + else + return (len); +} + + /** * Internal helper function to translate a human-readable failpoint string * into a internally-parsable fail_point structure. */ static char * -parse_fail_point(struct fail_point_entries *ents, char *p) +parse_fail_point(struct fail_point_setting *ents, char *p) { /* :: * ( "->" )* */ + uint8_t term_count; + + term_count = 1; + p = parse_term(ents, p); if (p == NULL) return (NULL); + while (*p != '\0') { - if (p[0] != '-' || p[1] != '>') - return (NULL); - p = parse_term(ents, p + 2); - if (p == NULL) + term_count++; + if (p[0] != '-' || p[1] != '>' || + (p = parse_term(ents, p+2)) == NULL || + term_count > FP_MAX_ENTRY_COUNT) return (NULL); } return (p); @@ -451,14 +976,11 @@ parse_fail_point(struct fail_point_entries *ents, char *p) * Internal helper function to parse an individual term from a failpoint. */ static char * -parse_term(struct fail_point_entries *ents, char *p) +parse_term(struct fail_point_setting *ents, char *p) { struct fail_point_entry *ent; - ent = fp_malloc(sizeof *ent, M_WAITOK | M_ZERO); - ent->fe_prob = PROB_MAX; - ent->fe_pid = NO_PID; - TAILQ_INSERT_TAIL(ents, ent, fe_entries); + ent = fail_point_entry_new(ents); /* * :: @@ -483,7 +1005,7 @@ parse_term(struct fail_point_entries *ents, char *p) if (ent->fe_prob > PROB_MAX) ent->fe_prob = PROB_MAX; } else if (*p == '*') { - if (!units || decimal) + if (!units || units < 0 || decimal) return (NULL); ent->fe_count = units; } else @@ -500,7 +1022,7 @@ parse_term(struct fail_point_entries *ents, char *p) /* [ "(" ")" ] */ if (*p != '(') - return p; + return (p); p++; if (!isdigit(*p) && *p != '-') return (NULL); @@ -509,7 +1031,7 @@ parse_term(struct fail_point_entries *ents, char *p) return (NULL); /* [ "[pid " "]" ] */ -#define PID_STRING "[pid " +#define PID_STRING "[pid " if (strncmp(p, PID_STRING, sizeof(PID_STRING) - 1) != 0) return (p); p += sizeof(PID_STRING) - 1; @@ -530,7 +1052,7 @@ parse_number(int *out_units, int *out_decimal, char *p) { char *old_p; - /* + /** * :: * [ "." ] | * "." @@ -584,29 +1106,17 @@ parse_type(struct fail_point_entry *ent, char *beg) return (NULL); } -/** - * Internal helper function to free an individual failpoint term. - */ -static void -free_entry(struct fail_point_entries *ents, struct fail_point_entry *ent) -{ - TAILQ_REMOVE(ents, ent, fe_entries); - fp_free(ent); -} +/* The fail point sysctl tree. */ +SYSCTL_NODE(_debug, OID_AUTO, fail_point, CTLFLAG_RW, 0, "fail points"); -/** - * Internal helper function to clear out all failpoint terms for a single - * failpoint. - */ -static void -clear_entries(struct fail_point_entries *ents) +/* Debugging/testing stuff for fail point */ +static int +sysctl_test_fail_point(SYSCTL_HANDLER_ARGS) { - struct fail_point_entry *ent, *ent_next; - TAILQ_FOREACH_SAFE(ent, ents, fe_entries, ent_next) - fp_free(ent); - TAILQ_INIT(ents); + KFAIL_POINT_RETURN(DEBUG_FP, test_fail_point); + return (0); } - -/* The fail point sysctl tree. */ -SYSCTL_NODE(_debug, OID_AUTO, fail_point, CTLFLAG_RW, 0, "fail points"); +SYSCTL_OID(_debug_fail_point, OID_AUTO, test_trigger_fail_point, + CTLTYPE_STRING | CTLFLAG_RD, NULL, 0, sysctl_test_fail_point, "A", + "Trigger test fail points"); diff --git a/sys/kern/subr_sleepqueue.c b/sys/kern/subr_sleepqueue.c index 12908f6..2c68454 100644 --- a/sys/kern/subr_sleepqueue.c +++ b/sys/kern/subr_sleepqueue.c @@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -83,6 +84,7 @@ __FBSDID("$FreeBSD$"); #include #endif + /* * Constants for the hash table of sleep queue chains. * SC_TABLESIZE must be a power of two for SC_MASK to work properly. @@ -1034,6 +1036,120 @@ sleepq_abort(struct thread *td, int intrval) return (sleepq_resume_thread(sq, td, 0)); } +/* + * Prints the stacks of all threads presently sleeping on wchan/queue to + * the sbuf sb. Sets count_stacks_printed to the number of stacks actually + * printed. Typically, this will equal the number of threads sleeping on the + * queue, but may be less if sb overflowed before all stacks were printed. + */ +int +sleepq_sbuf_print_stacks(struct sbuf *sb, void *wchan, int queue, + int *count_stacks_printed) +{ + struct thread *td, *td_next; + struct sleepqueue *sq; + struct stack **st; + struct sbuf **td_infos; + int i, stack_idx, error, stacks_to_allocate; + bool finished, partial_print; + + error = 0; + finished = false; + partial_print = false; + + KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); + MPASS((queue >= 0) && (queue < NR_SLEEPQS)); + + stacks_to_allocate = 10; + for (i = 0; i < 3 && !finished ; i++) { + /* We cannot malloc while holding the queue's spinlock, so + * we do our mallocs now, and hope it is enough. If it + * isn't, we will free these, drop the lock, malloc more, + * and try again, up to a point. After that point we will + * give up and report ENOMEM. We also cannot write to sb + * during this time since the client may have set the + * SBUF_AUTOEXTEND flag on their sbuf, which could cause a + * malloc as we print to it. So we defer actually printing + * to sb until after we drop the spinlock. + */ + + /* Where we will store the stacks. */ + st = malloc(sizeof(struct stack *) * stacks_to_allocate, + M_TEMP, M_WAITOK); + for (stack_idx = 0; stack_idx < stacks_to_allocate; + stack_idx++) + st[stack_idx] = stack_create(); + + /* Where we will store the td name, tid, etc. */ + td_infos = malloc(sizeof(struct sbuf *) * stacks_to_allocate, + M_TEMP, M_WAITOK); + for (stack_idx = 0; stack_idx < stacks_to_allocate; + stack_idx++) + td_infos[stack_idx] = sbuf_new(NULL, NULL, + MAXCOMLEN + sizeof(struct thread *) * 2 + 40, + SBUF_FIXEDLEN); + + sleepq_lock(wchan); + sq = sleepq_lookup(wchan); + if (sq == NULL) { + /* This sleepq does not exist; exit and return ENOENT. */ + error = ENOENT; + finished = true; + sleepq_release(wchan); + goto loop_end; + } + + stack_idx = 0; + /* Save thread info */ + TAILQ_FOREACH_SAFE(td, &sq->sq_blocked[queue], td_slpq, + td_next) { + if (stack_idx >= stacks_to_allocate) + goto loop_end; + + /* Note the td_lock is equal to the sleepq_lock here. */ + stack_save_td(st[stack_idx], td); + + sbuf_printf(td_infos[stack_idx], "%d: %s %p", + td->td_tid, td->td_name, td); + + ++stack_idx; + } + + finished = true; + sleepq_release(wchan); + + /* Print the stacks */ + for (i = 0; i < stack_idx; i++) { + sbuf_finish(td_infos[i]); + sbuf_printf(sb, "--- thread %s: ---\n", sbuf_data(td_infos[i])); + stack_sbuf_print(sb, st[i]); + sbuf_printf(sb, "\n"); + + error = sbuf_error(sb); + if (error == 0) + *count_stacks_printed = stack_idx; + } + +loop_end: + if (!finished) + sleepq_release(wchan); + for (stack_idx = 0; stack_idx < stacks_to_allocate; + stack_idx++) + stack_destroy(st[stack_idx]); + for (stack_idx = 0; stack_idx < stacks_to_allocate; + stack_idx++) + sbuf_delete(td_infos[stack_idx]); + free(st, M_TEMP); + free(td_infos, M_TEMP); + stacks_to_allocate *= 10; + } + + if (!finished && error == 0) + error = ENOMEM; + + return (error); +} + #ifdef SLEEPQUEUE_PROFILING #define SLEEPQ_PROF_LOCATIONS 1024 #define SLEEPQ_SBUFSIZE 512 diff --git a/sys/sys/fail.h b/sys/sys/fail.h index e011459..bd2eab1 100644 --- a/sys/sys/fail.h +++ b/sys/sys/fail.h @@ -37,6 +37,11 @@ #include #include #include +#include +#include +#include +#include +#include /** * Failpoint return codes, used internally. @@ -49,7 +54,8 @@ enum fail_point_return_code { }; struct fail_point_entry; -TAILQ_HEAD(fail_point_entries, fail_point_entry); +struct fail_point_setting; + /** * Internal failpoint structure, tracking all the current details of the * failpoint. This structure is the core component shared between the @@ -57,22 +63,42 @@ TAILQ_HEAD(fail_point_entries, fail_point_entry); * @ingroup failpoint_private */ struct fail_point { - const char *fp_name; /**< name of fail point */ - const char *fp_location; /**< file:line of fail point */ - struct fail_point_entries fp_entries; /**< list of entries */ + const char *fp_name; /* name of fail point */ + const char *fp_location; /* file:line of fail point */ + volatile int fp_ref_cnt; /** + * protects fp_setting: while holding + * a ref, fp_setting points to an + * unfreed fail_point_setting + */ + struct fail_point_setting * volatile fp_setting; int fp_flags; - void (*fp_sleep_fn)(void *); /**< Function to call at end of - * sleep for sleep failpoints */ - void *fp_sleep_arg; /**< Arg for sleep_fn */ + + /**< Function to call before sleep or pause */ + void (*fp_pre_sleep_fn)(void *); + /**< Arg for fp_pre_sleep_fn */ + void *fp_pre_sleep_arg; + + /**< Function to call after waking from sleep or pause */ + void (*fp_post_sleep_fn)(void *); + /**< Arg for fp_post_sleep_fn */ + void *fp_post_sleep_arg; }; #define FAIL_POINT_DYNAMIC_NAME 0x01 /**< Must free name on destroy */ +/**< Use timeout path for sleep instead of msleep */ +#define FAIL_POINT_USE_TIMEOUT_PATH 0x02 +/**< If fail point is set to sleep, replace the sleep call with delay */ +#define FAIL_POINT_NONSLEEPABLE 0x04 + +#define FAIL_POINT_CV_DESC "fp cv no iterators" +#define FAIL_POINT_IS_OFF(fp) (__predict_true((fp)->fp_setting == NULL) || \ + __predict_true(fail_point_is_off(fp))) __BEGIN_DECLS /* Private failpoint eval function -- use fail_point_eval() instead. */ enum fail_point_return_code fail_point_eval_nontrivial(struct fail_point *, - int *ret); + int *ret); /** * @addtogroup failpoint @@ -86,26 +112,62 @@ enum fail_point_return_code fail_point_eval_nontrivial(struct fail_point *, void fail_point_init(struct fail_point *, const char *fmt, ...) __printflike(2, 3); +/* Return true iff this fail point is set to off, false otherwise */ +bool fail_point_is_off(struct fail_point *fp); + +/** + * Set the pre-sleep function for a fail point + * If fp_post_sleep_fn is specified, then FAIL_POINT_SLEEP will result in a + * (*fp->fp_pre_sleep_fn)(fp->fp_pre_sleep_arg) call by the thread. + */ +static inline void +fail_point_sleep_set_pre_func(struct fail_point *fp, void (*sleep_fn)(void *)) +{ + fp->fp_pre_sleep_fn = sleep_fn; +} + +static inline void +fail_point_sleep_set_pre_arg(struct fail_point *fp, void *sleep_arg) +{ + fp->fp_pre_sleep_arg = sleep_arg; +} + /** - * Set the sleep function for a fail point - * If sleep_fn is specified, then FAIL_POINT_SLEEP will result in a - * (*fp->sleep_fn)(fp->sleep_arg) call by the timer thread. Otherwise, - * if sleep_fn is NULL (default), then FAIL_POINT_SLEEP will result in the - * fail_point_eval() call sleeping. + * Set the post-sleep function. This will be passed to timeout if we take + * the timeout path. This must be set if you sleep using the timeout path. */ -static __inline void -fail_point_sleep_set_func(struct fail_point *fp, void (*sleep_fn)(void *)) +static inline void +fail_point_sleep_set_post_func(struct fail_point *fp, void (*sleep_fn)(void *)) { - fp->fp_sleep_fn = sleep_fn; + fp->fp_post_sleep_fn = sleep_fn; } +static inline void +fail_point_sleep_set_post_arg(struct fail_point *fp, void *sleep_arg) +{ + fp->fp_post_sleep_arg = sleep_arg; +} /** - * Set the argument for the sleep function for a fail point + * If the FAIL_POINT_USE_TIMEOUT flag is set on a failpoint, then + * FAIL_POINT_SLEEP will result in a call to timeout instead of + * msleep. Note that if you sleep while this flag is set, you must + * set fp_post_sleep_fn or an error will occur upon waking. */ -static __inline void -fail_point_sleep_set_arg(struct fail_point *fp, void *sleep_arg) +static inline void +fail_point_use_timeout_path(struct fail_point *fp, bool use_timeout, + void (*post_sleep_fn)(void *)) { - fp->fp_sleep_arg = sleep_arg; + KASSERT(!use_timeout || post_sleep_fn != NULL || + (post_sleep_fn == NULL && fp->fp_post_sleep_fn != NULL), + ("Setting fp to use timeout, but not setting post_sleep_fn\n")); + + if (use_timeout) + fp->fp_flags |= FAIL_POINT_USE_TIMEOUT_PATH; + else + fp->fp_flags &= ~FAIL_POINT_USE_TIMEOUT_PATH; + + if (post_sleep_fn != NULL) + fp->fp_post_sleep_fn = post_sleep_fn; } /** @@ -116,33 +178,64 @@ void fail_point_destroy(struct fail_point *); /** * Evaluate a failpoint. */ -static __inline enum fail_point_return_code +static inline enum fail_point_return_code fail_point_eval(struct fail_point *fp, int *ret) { - if (TAILQ_EMPTY(&fp->fp_entries)) { + if (__predict_true(fp->fp_setting == NULL)) return (FAIL_POINT_RC_CONTINUE); - } return (fail_point_eval_nontrivial(fp, ret)); } __END_DECLS /* Declare a fail_point and its sysctl in a function. */ -#define _FAIL_POINT_NAME(name) _fail_point_##name -#define _FAIL_POINT_LOCATION() "(" __FILE__ ":" __XSTRING(__LINE__) ")" +#define _FAIL_POINT_NAME(name) _fail_point_##name +#define _FAIL_POINT_LOCATION() "(" __FILE__ ":" __XSTRING(__LINE__) ")" +#define _FAIL_POINT_INIT(parent, name, flags) \ + static struct fail_point _FAIL_POINT_NAME(name) = { \ + .fp_name = #name, \ + .fp_location = _FAIL_POINT_LOCATION(), \ + .fp_ref_cnt = 0, \ + .fp_setting = NULL, \ + .fp_flags = (flags), \ + .fp_pre_sleep_fn = NULL, \ + .fp_pre_sleep_arg = NULL, \ + .fp_post_sleep_fn = NULL, \ + .fp_post_sleep_arg = NULL, \ + }; \ + SYSCTL_OID(parent, OID_AUTO, name, \ + CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, \ + &_FAIL_POINT_NAME(name), 0, fail_point_sysctl, \ + "A", ""); \ + SYSCTL_OID(parent, OID_AUTO, status_##name, \ + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, \ + &_FAIL_POINT_NAME(name), 0, \ + fail_point_sysctl_status, "A", ""); +#define _FAIL_POINT_EVAL(name, cond, code...) \ + int RETURN_VALUE; \ + \ + if (__predict_false(cond && \ + fail_point_eval(&_FAIL_POINT_NAME(name), &RETURN_VALUE))) { \ + \ + code; \ + \ + } + /** - * Instantiate a failpoint which returns "value" from the function when triggered. - * @param parent The parent sysctl under which to locate the sysctl + * Instantiate a failpoint which returns "RETURN_VALUE" from the function + * when triggered. + * @param parent The parent sysctl under which to locate the fp's sysctl * @param name The name of the failpoint in the sysctl tree (and printouts) - * @return Instantly returns the return("value") specified in the + * @return Instantly returns the RETURN_VALUE specified in the * failpoint, if triggered. */ #define KFAIL_POINT_RETURN(parent, name) \ KFAIL_POINT_CODE(parent, name, return RETURN_VALUE) /** - * Instantiate a failpoint which returns (void) from the function when triggered. + * Instantiate a failpoint which returns (void) from the function when + * triggered. * @param parent The parent sysctl under which to locate the sysctl * @param name The name of the failpoint in the sysctl tree (and printouts) * @return Instantly returns void, if triggered in the failpoint. @@ -153,7 +246,8 @@ __END_DECLS /** * Instantiate a failpoint which sets an error when triggered. * @param parent The parent sysctl under which to locate the sysctl - * @param name The name of the failpoint in the sysctl tree (and printouts) + * @param name The name of the failpoint in the sysctl tree (and + * printouts) * @param error_var A variable to set to the failpoint's specified * return-value when triggered */ @@ -164,7 +258,8 @@ __END_DECLS * Instantiate a failpoint which sets an error and then goes to a * specified label in the function when triggered. * @param parent The parent sysctl under which to locate the sysctl - * @param name The name of the failpoint in the sysctl tree (and printouts) + * @param name The name of the failpoint in the sysctl tree (and + * printouts) * @param error_var A variable to set to the failpoint's specified * return-value when triggered * @param label The location to goto when triggered. @@ -173,39 +268,81 @@ __END_DECLS KFAIL_POINT_CODE(parent, name, (error_var) = RETURN_VALUE; goto label) /** + * Instantiate a failpoint which sets its pre- and post-sleep callback + * mechanisms. + * @param parent The parent sysctl under which to locate the sysctl + * @param name The name of the failpoint in the sysctl tree (and + * printouts) + * @param pre_func Function pointer to the pre-sleep function, which will be + * called directly before going to sleep. + * @param pre_arg Argument to the pre-sleep function + * @param post_func Function pointer to the pot-sleep function, which will be + * called directly before going to sleep. + * @param post_arg Argument to the post-sleep function + */ +#define KFAIL_POINT_SLEEP_CALLBACKS(parent, name, pre_func, pre_arg, \ + post_func, post_arg) \ + KFAIL_POINT_CODE_SLEEP_CALLBACKS(parent, name, pre_func, \ + pre_arg, post_func, post_arg, return RETURN_VALUE) + +/** + * Instantiate a failpoint which runs arbitrary code when triggered, and sets + * its pre- and post-sleep callback mechanisms + * @param parent The parent sysctl under which to locate the sysctl + * @param name The name of the failpoint in the sysctl tree (and + * printouts) + * @param pre_func Function pointer to the pre-sleep function, which will be + * called directly before going to sleep. + * @param pre_arg Argument to the pre-sleep function + * @param post_func Function pointer to the pot-sleep function, which will be + * called directly before going to sleep. + * @param post_arg Argument to the post-sleep function + * @param code The arbitrary code to run when triggered. Can reference + * "RETURN_VALUE" if desired to extract the specified + * user return-value when triggered. Note that this is + * implemented with a do-while loop so be careful of + * break and continue statements. + */ +#define KFAIL_POINT_CODE_SLEEP_CALLBACKS(parent, name, pre_func, pre_arg, \ + post_func, post_arg, code...) \ + do { \ + _FAIL_POINT_INIT(parent, name) \ + _FAIL_POINT_NAME(name).fp_pre_sleep_fn = pre_func; \ + _FAIL_POINT_NAME(name).fp_pre_sleep_arg = pre_arg; \ + _FAIL_POINT_NAME(name).fp_post_sleep_fn = post_func; \ + _FAIL_POINT_NAME(name).fp_post_sleep_arg = post_arg; \ + _FAIL_POINT_EVAL(name, true, code) \ + } while (0) + + +/** * Instantiate a failpoint which runs arbitrary code when triggered. * @param parent The parent sysctl under which to locate the sysctl * @param name The name of the failpoint in the sysctl tree - * (and printouts) + * (and printouts) * @param code The arbitrary code to run when triggered. Can reference * "RETURN_VALUE" if desired to extract the specified * user return-value when triggered. Note that this is * implemented with a do-while loop so be careful of * break and continue statements. */ -#define KFAIL_POINT_CODE(parent, name, code) \ -do { \ - int RETURN_VALUE; \ - static struct fail_point _FAIL_POINT_NAME(name) = { \ - #name, \ - _FAIL_POINT_LOCATION(), \ - TAILQ_HEAD_INITIALIZER(_FAIL_POINT_NAME(name).fp_entries), \ - 0, \ - NULL, NULL, \ - }; \ - SYSCTL_OID(parent, OID_AUTO, name, \ - CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, \ - &_FAIL_POINT_NAME(name), 0, fail_point_sysctl, \ - "A", ""); \ - \ - if (__predict_false( \ - fail_point_eval(&_FAIL_POINT_NAME(name), &RETURN_VALUE))) { \ - \ - code; \ - \ - } \ -} while (0) +#define KFAIL_POINT_CODE(parent, name, code...) \ + do { \ + _FAIL_POINT_INIT(parent, name, 0) \ + _FAIL_POINT_EVAL(name, true, code) \ + } while (0) + +#define KFAIL_POINT_CODE_FLAGS(parent, name, flags, code...) \ + do { \ + _FAIL_POINT_INIT(parent, name, flags) \ + _FAIL_POINT_EVAL(name, true, code) \ + } while (0) +#define KFAIL_POINT_CODE_COND(parent, name, cond, flags, code...) \ + do { \ + _FAIL_POINT_INIT(parent, name, flags) \ + _FAIL_POINT_EVAL(name, cond, code) \ + } while (0) /** * @} @@ -214,6 +351,7 @@ do { \ #ifdef _KERNEL int fail_point_sysctl(SYSCTL_HANDLER_ARGS); +int fail_point_sysctl_status(SYSCTL_HANDLER_ARGS); /* The fail point sysctl tree. */ SYSCTL_DECL(_debug_fail_point); diff --git a/sys/sys/sleepqueue.h b/sys/sys/sleepqueue.h index cdb7a39..b607a53 100644 --- a/sys/sys/sleepqueue.h +++ b/sys/sys/sleepqueue.h @@ -107,5 +107,9 @@ int sleepq_type(void *wchan); void sleepq_wait(void *wchan, int pri); int sleepq_wait_sig(void *wchan, int pri); +#include +int sleepq_sbuf_print_stacks(struct sbuf *sb, void *wchan, int queue, + int *count_stacks_printed); + #endif /* _KERNEL */ #endif /* !_SYS_SLEEPQUEUE_H_ */ -- cgit v1.1 From 92a7708ba6309521948898336f7365cc2cd76aa0 Mon Sep 17 00:00:00 2001 From: cem Date: Wed, 16 Mar 2016 05:05:54 +0000 Subject: fail.9: Bump Dd --- share/man/man9/fail.9 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/man/man9/fail.9 b/share/man/man9/fail.9 index b5f0882..cf62365 100644 --- a/share/man/man9/fail.9 +++ b/share/man/man9/fail.9 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 02, 2016 +.Dd March 15, 2016 .Dt FAIL 9 .Os .Sh NAME -- cgit v1.1 From 95d15635d50cea1b5bac38ce9743c0df5b10a48a Mon Sep 17 00:00:00 2001 From: adrian Date: Wed, 16 Mar 2016 06:26:50 +0000 Subject: Add initial VHT IE's and action codes. Yes, there's more to 802.11ac than this. --- sys/net80211/ieee80211.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h index 0a25392..15e79bf 100644 --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -346,6 +346,7 @@ struct ieee80211_action { #define IEEE80211_ACTION_CAT_MESH 13 /* Mesh */ #define IEEE80211_ACTION_CAT_SELF_PROT 15 /* Self-protected */ /* 16 - 125 reserved */ +#define IEEE80211_ACTION_VHT 21 #define IEEE80211_ACTION_CAT_VENDOR 127 /* Vendor Specific */ #define IEEE80211_ACTION_HT_TXCHWIDTH 0 /* recommended xmit chan width*/ @@ -760,6 +761,11 @@ enum { IEEE80211_ELEMID_MESHPXU = 137, IEEE80211_ELEMID_MESHPXUC = 138, IEEE80211_ELEMID_MESHAH = 60, /* XXX: remove */ + + /* 802.11ac */ + IEEE80211_ELEMID_VHT_CAP = 191, + IEEE80211_ELEMID_VHT_OPMODE = 192, + IEEE80211_ELEMID_VHT_PWR_ENV = 195, }; struct ieee80211_tim_ie { -- cgit v1.1 From 7c300189de77703235cdc36c616821af022cc3bd Mon Sep 17 00:00:00 2001 From: adrian Date: Wed, 16 Mar 2016 06:27:57 +0000 Subject: Display the VHT IE names. This doesn't decode the IEs just yet. Tested: * Archer c2 AP: TP-LINK_D579_5G 60:e3:27:e1:d5:78 44 54M 26:0 100 EP SSID RATES DSPARMS<44> COUNTRY TIM<050400010000> HTCAP HTINFO VHTCAP VHTOPMODE RSN WME BSSLOAD<0b05000001127a> VEN --- sbin/ifconfig/ifieee80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c index 5ababf3..cb86a25 100644 --- a/sbin/ifconfig/ifieee80211.c +++ b/sbin/ifconfig/ifieee80211.c @@ -3087,6 +3087,9 @@ iename(int elemid) case IEEE80211_ELEMID_APCHANREP:return " APCHANREP"; case IEEE80211_ELEMID_TPC: return " TPC"; case IEEE80211_ELEMID_CCKM: return " CCKM"; + case IEEE80211_ELEMID_VHT_CAP: return " VHTCAP"; + case IEEE80211_ELEMID_VHT_OPMODE: return " VHTOPMODE"; + case IEEE80211_ELEMID_VHT_PWR_ENV: return " VHTPWRENV"; } return " ???"; } -- cgit v1.1 From f0149ff4ac175b42adabe6e41da09574d6e552e5 Mon Sep 17 00:00:00 2001 From: kp Date: Wed, 16 Mar 2016 06:42:15 +0000 Subject: pf: Improve forwarding detection When we guess the nature of the outbound packet (output vs. forwarding) we need to take bridges into account. When bridging the input interface does not match the output interface, but we're not forwarding. Similarly, it's possible for the interface to actually be the bridge interface itself (and not a member interface). PR: 202351 MFC after: 2 weeks --- sys/netpfil/pf/pf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 283dddc9..0220286 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -6192,11 +6192,13 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) * We do need to be careful about bridges. If the * net.link.bridge.pfil_bridge sysctl is set we can be filtering on a * bridge, so if the input interface is a bridge member and the output - * interface is its bridge we're not actually forwarding but bridging. + * interface is its bridge or a member of the same bridge we're not + * actually forwarding but bridging. */ - if (dir == PF_OUT && m->m_pkthdr.rcvif && ifp != m->m_pkthdr.rcvif - && (m->m_pkthdr.rcvif->if_bridge == NULL - || m->m_pkthdr.rcvif->if_bridge != ifp->if_softc)) + if (dir == PF_OUT && m->m_pkthdr.rcvif && ifp != m->m_pkthdr.rcvif && + (m->m_pkthdr.rcvif->if_bridge == NULL || + (m->m_pkthdr.rcvif->if_bridge != ifp->if_softc && + m->m_pkthdr.rcvif->if_bridge != ifp->if_bridge))) fwdir = PF_FWD; if (!V_pf_status.running) -- cgit v1.1 From f1fdbd94a37f930ff87f0e0001ea6933e8029343 Mon Sep 17 00:00:00 2001 From: hselasky Date: Wed, 16 Mar 2016 08:37:52 +0000 Subject: Improve the implementation and documentation of the SYSCTL_COUNTER_U64_ARRAY() macro. - Add proper asserts to the SYSCTL_COUNTER_U64_ARRAY() macro that checks the size of the first element of the array. - Add an example to the counter(9) manual page how to use the SYSCTL_COUNTER_U64_ARRAY() macro. - Add some missing symbolic links for counter(9) while at it. --- share/man/man9/Makefile | 6 +++++- share/man/man9/counter.9 | 10 ++++++++++ sys/sys/sysctl.h | 6 ++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 66c16d7..26f230c 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -639,7 +639,11 @@ MLINKS+=counter.9 counter_u64_alloc.9 \ counter.9 counter_exit.9 \ counter.9 counter_u64_add_protected.9 \ counter.9 counter_u64_fetch.9 \ - counter.9 counter_u64_zero.9 + counter.9 counter_u64_zero.9 \ + counter.9 SYSCTL_COUNTER_U64.9 \ + counter.9 SYSCTL_ADD_COUNTER_U64.9 \ + counter.9 SYSCTL_COUNTER_U64_ARRAY.9 \ + counter.9 SYSCTL_ADD_COUNTER_U64_ARRAY.9 MLINKS+=cpuset.9 CPUSET_T_INITIALIZER.9 \ cpuset.9 CPUSET_FSET.9 \ cpuset.9 CPU_CLR.9 \ diff --git a/share/man/man9/counter.9 b/share/man/man9/counter.9 index 85bc9fe..0fcf2ad 100644 --- a/share/man/man9/counter.9 +++ b/share/man/man9/counter.9 @@ -215,6 +215,16 @@ amd64 single-instruction implementation. On some architectures updating a counter require a .Xr critical 9 section. +.Sh EXAMPLES +The following example creates a static counter array exported to +userspace through a sysctl: +.Bd -literal -offset indent +#define MY_SIZE 8 +static counter_u64_t array[MY_SIZE]; +SYSCTL_COUNTER_U64_ARRAY(_debug, OID_AUTO, counter_array, CTLFLAG_RW, + &array[0], MY_SIZE, "Test counter array"); +.Ed +.Pp .Sh SEE ALSO .Xr atomic 9 , .Xr critical 9 , diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 34ee03c..eb5eef0 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -654,8 +654,10 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); SYSCTL_OID(parent, nbr, name, \ CTLTYPE_OPAQUE | CTLFLAG_MPSAFE | (access), \ (ptr), (len), sysctl_handle_counter_u64_array, "S", descr); \ - CTASSERT(((access) & CTLTYPE) == 0 || \ - ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE) + CTASSERT((((access) & CTLTYPE) == 0 || \ + ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE) && \ + sizeof(counter_u64_t) == sizeof(*(ptr)) && \ + sizeof(uint64_t) == sizeof(**(ptr))) #define SYSCTL_ADD_COUNTER_U64_ARRAY(ctx, parent, nbr, name, access, \ ptr, len, descr) \ -- cgit v1.1 From 5b52683cd8329f800d8a0f00de9c63a4d4fbeeca Mon Sep 17 00:00:00 2001 From: hselasky Date: Wed, 16 Mar 2016 08:49:38 +0000 Subject: Fix crash in krping when run as a client due to NULL pointer access. Initialize pointer in question which is used only when fast registers mode is selected. Sponsored by: Mellanox Technologies MFC after: 1 week --- sys/contrib/rdma/krping/krping.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/contrib/rdma/krping/krping.c b/sys/contrib/rdma/krping/krping.c index 667af40..e99ff7f 100644 --- a/sys/contrib/rdma/krping/krping.c +++ b/sys/contrib/rdma/krping/krping.c @@ -261,6 +261,7 @@ static int krping_cma_event_handler(struct rdma_cm_id *cma_id, case RDMA_CM_EVENT_ROUTE_RESOLVED: cb->state = ROUTE_RESOLVED; + cb->child_cm_id = cma_id; wake_up_interruptible(&cb->sem); break; -- cgit v1.1 From a2e6e2bfcdb04765bcf7b9abfee6533a52ce685f Mon Sep 17 00:00:00 2001 From: hselasky Date: Wed, 16 Mar 2016 10:42:24 +0000 Subject: Fix kernel build after adding new sysctl asserts in r296933. --- sys/netinet/tcp_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 16be9cb..7134ce4 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -243,7 +243,7 @@ SYSCTL_VNET_PCPUSTAT(_net_inet_tcp, TCPCTL_STATS, stats, struct tcpstat, tcpstat, "TCP statistics (struct tcpstat, netinet/tcp_var.h)"); VNET_DEFINE(counter_u64_t, tcps_states[TCP_NSTATES]); SYSCTL_COUNTER_U64_ARRAY(_net_inet_tcp, TCPCTL_STATES, states, CTLFLAG_RD | - CTLFLAG_VNET, &VNET_NAME(tcps_states), TCP_NSTATES, + CTLFLAG_VNET, &VNET_NAME(tcps_states)[0], TCP_NSTATES, "TCP connection counts by TCP state"); static void -- cgit v1.1 From 60696189344ef659be9079fa3b833d2b898a9b24 Mon Sep 17 00:00:00 2001 From: mmel Date: Wed, 16 Mar 2016 13:01:48 +0000 Subject: Import basic support for Nvidia Jetson TK1 board and tegra124 SoC. The following pheripherals are supported: UART, MMC, AHCI, EHCI, PCIe, I2C, PMIC, GPIO, CPU temperature and clock. Note: The PCIe driver is pure mash at this moment. It will be reworked immediately when both D5237 and D2579 enter the current tree. --- sys/arm/conf/JETSON-TK1 | 37 + sys/arm/conf/TEGRA124.common | 154 ++ sys/arm/nvidia/as3722.c | 411 +++++ sys/arm/nvidia/as3722.h | 323 ++++ sys/arm/nvidia/as3722_gpio.c | 577 +++++++ sys/arm/nvidia/as3722_regulators.c | 811 ++++++++++ sys/arm/nvidia/as3722_rtc.c | 115 ++ sys/arm/nvidia/tegra124/files.tegra124 | 57 + sys/arm/nvidia/tegra124/std.tegra124 | 14 + sys/arm/nvidia/tegra124/tegra124_car.c | 613 ++++++++ sys/arm/nvidia/tegra124/tegra124_car.h | 337 ++++ sys/arm/nvidia/tegra124/tegra124_clk_per.c | 810 ++++++++++ sys/arm/nvidia/tegra124/tegra124_clk_pll.c | 1066 +++++++++++++ sys/arm/nvidia/tegra124/tegra124_clk_super.c | 265 ++++ sys/arm/nvidia/tegra124/tegra124_coretemp.c | 273 ++++ sys/arm/nvidia/tegra124/tegra124_cpufreq.c | 583 +++++++ sys/arm/nvidia/tegra124/tegra124_machdep.c | 173 +++ sys/arm/nvidia/tegra124/tegra124_mp.c | 127 ++ sys/arm/nvidia/tegra124/tegra124_mp.h | 35 + sys/arm/nvidia/tegra124/tegra124_pmc.c | 566 +++++++ sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c | 603 ++++++++ sys/arm/nvidia/tegra_abpmisc.c | 194 +++ sys/arm/nvidia/tegra_ahci.c | 627 ++++++++ sys/arm/nvidia/tegra_efuse.c | 368 +++++ sys/arm/nvidia/tegra_efuse.h | 61 + sys/arm/nvidia/tegra_ehci.c | 322 ++++ sys/arm/nvidia/tegra_gpio.c | 480 ++++++ sys/arm/nvidia/tegra_i2c.c | 804 ++++++++++ sys/arm/nvidia/tegra_lic.c | 265 ++++ sys/arm/nvidia/tegra_pcie.c | 1692 +++++++++++++++++++++ sys/arm/nvidia/tegra_pinmux.c | 804 ++++++++++ sys/arm/nvidia/tegra_pmc.h | 115 ++ sys/arm/nvidia/tegra_rtc.c | 303 ++++ sys/arm/nvidia/tegra_sdhci.c | 465 ++++++ sys/arm/nvidia/tegra_soctherm.c | 696 +++++++++ sys/arm/nvidia/tegra_soctherm_if.m | 42 + sys/arm/nvidia/tegra_uart.c | 252 +++ sys/arm/nvidia/tegra_usbphy.c | 839 ++++++++++ sys/boot/fdt/dts/arm/tegra124-jetson-tk1-fbsd.dts | 46 + 39 files changed, 16325 insertions(+) create mode 100644 sys/arm/conf/JETSON-TK1 create mode 100644 sys/arm/conf/TEGRA124.common create mode 100644 sys/arm/nvidia/as3722.c create mode 100644 sys/arm/nvidia/as3722.h create mode 100644 sys/arm/nvidia/as3722_gpio.c create mode 100644 sys/arm/nvidia/as3722_regulators.c create mode 100644 sys/arm/nvidia/as3722_rtc.c create mode 100644 sys/arm/nvidia/tegra124/files.tegra124 create mode 100644 sys/arm/nvidia/tegra124/std.tegra124 create mode 100644 sys/arm/nvidia/tegra124/tegra124_car.c create mode 100644 sys/arm/nvidia/tegra124/tegra124_car.h create mode 100644 sys/arm/nvidia/tegra124/tegra124_clk_per.c create mode 100644 sys/arm/nvidia/tegra124/tegra124_clk_pll.c create mode 100644 sys/arm/nvidia/tegra124/tegra124_clk_super.c create mode 100644 sys/arm/nvidia/tegra124/tegra124_coretemp.c create mode 100644 sys/arm/nvidia/tegra124/tegra124_cpufreq.c create mode 100644 sys/arm/nvidia/tegra124/tegra124_machdep.c create mode 100644 sys/arm/nvidia/tegra124/tegra124_mp.c create mode 100644 sys/arm/nvidia/tegra124/tegra124_mp.h create mode 100644 sys/arm/nvidia/tegra124/tegra124_pmc.c create mode 100644 sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c create mode 100644 sys/arm/nvidia/tegra_abpmisc.c create mode 100644 sys/arm/nvidia/tegra_ahci.c create mode 100644 sys/arm/nvidia/tegra_efuse.c create mode 100644 sys/arm/nvidia/tegra_efuse.h create mode 100644 sys/arm/nvidia/tegra_ehci.c create mode 100644 sys/arm/nvidia/tegra_gpio.c create mode 100644 sys/arm/nvidia/tegra_i2c.c create mode 100644 sys/arm/nvidia/tegra_lic.c create mode 100644 sys/arm/nvidia/tegra_pcie.c create mode 100644 sys/arm/nvidia/tegra_pinmux.c create mode 100644 sys/arm/nvidia/tegra_pmc.h create mode 100644 sys/arm/nvidia/tegra_rtc.c create mode 100644 sys/arm/nvidia/tegra_sdhci.c create mode 100644 sys/arm/nvidia/tegra_soctherm.c create mode 100644 sys/arm/nvidia/tegra_soctherm_if.m create mode 100644 sys/arm/nvidia/tegra_uart.c create mode 100644 sys/arm/nvidia/tegra_usbphy.c create mode 100644 sys/boot/fdt/dts/arm/tegra124-jetson-tk1-fbsd.dts diff --git a/sys/arm/conf/JETSON-TK1 b/sys/arm/conf/JETSON-TK1 new file mode 100644 index 0000000..6ff9e71 --- /dev/null +++ b/sys/arm/conf/JETSON-TK1 @@ -0,0 +1,37 @@ +# Kernel configuration for Jetson TK1 board +# +# For more information on this file, please read the config(5) manual page, +# and/or the handbook section on Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +#NO_UNIVERSE + +include "TEGRA124.common" +ident JETSON-TK1 + +# Flattened Device Tree +options FDT_DTB_STATIC +makeoptions FDT_DTS_FILE=tegra124-jetson-tk1-fbsd.dts + +makeoptions MODULES_OVERRIDE="" +#options BOOTVERBOSE +#options BOOTHOWTO=RB_SINGLE + +#options ROOTDEVNAME=\"ufs:mmcsd0s2a\" +options ROOTDEVNAME=\"ufs:ada0s1a\" + +# CTF doesn't works yet +makeoptions WITHOUT_CTF=1 diff --git a/sys/arm/conf/TEGRA124.common b/sys/arm/conf/TEGRA124.common new file mode 100644 index 0000000..87e6a8a --- /dev/null +++ b/sys/arm/conf/TEGRA124.common @@ -0,0 +1,154 @@ +# +# Kernel configuration for NVIDIA Tegra124 based boards. +# +# For more information on this file, please read the config(5) manual page, +# and/or the handbook section on Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +include "std.armv6" +include "../nvidia/tegra124/std.tegra124" + +options HZ=100 # Scheduling quantum is 10 milliseconds. +options SCHED_ULE # ULE scheduler +options PLATFORM # Platform based SoC +options PLATFORM_SMP +options SMP # Enable multiple cores + +# Debugging for use in -current +makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols +options BREAK_TO_DEBUGGER +options ALT_BREAK_TO_DEBUGGER +#options VERBOSE_SYSINIT # Enable verbose sysinit messages +options KDB # Enable kernel debugger support +# For minimum debugger support (stable branch) use: +#options KDB_TRACE # Print a stack trace for a panic +# For full debugger support use this instead: +options DDB # Enable the kernel debugger +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 + +# Interrupt controller +device gic + +# ARM Generic Timer +device generic_timer + +# EXT_RESOURCES pseudo devices +options EXT_RESOURCES +device clk +device phy +device hwreset +device regulator + +# Pseudo devices. +device loop # Network loopback +device random # Entropy device +device vlan # 802.1Q VLAN support +#device tun # Packet tunnel. +device md # Memory "disks" +#device gif # IPv6 and IPv4 tunneling +#device firmware # firmware assist module +device ether # Ethernet support +device miibus # Required for ethernet +device bpf # Berkeley packet filter (required for DHCP) + + +# General-purpose input/output +device gpio +#device gpioled + +# I2C support +device iic +device iicbus +device icee + +# Serial (COM) ports +device uart # Multi-uart driver +device uart_ns8250 + +# MMC/SD/SDIO Card slot support +device sdhci # SD controller +device mmc # SD/MMC protocol +device mmcsd # SDCard disk device + +# ATA controllers +device ahci # AHCI-compatible SATA controllers + +# SCSI peripherals +device scbus # SCSI bus (required for ATA/SCSI) +device da # Direct Access (disks) +device cd # CD +device pass # Passthrough device (direct ATA/SCSI access) + +# USB support +options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. +options USB_DEBUG # enable debug msgs +device ehci # EHCI USB interface +device usb # USB Bus (required) +device umass # Disks/Mass storage - Requires scbus and da +device uhid # "Human Interface Devices" +#device u3g # USB modems +device ukbd # Allow keyboard like HIDs to control console +device ums # USB mouse + +# USB Ethernet, requires miibus +#device aue # ADMtek USB Ethernet +#device axe # ASIX Electronics USB Ethernet +#device cdce # Generic USB over Ethernet +#device cue # CATC USB Ethernet +#device kue # Kawasaki LSI USB Ethernet +#device rue # RealTek RTL8150 USB Ethernet +#device udav # Davicom DM9601E USB + +# USB Wireless +#device rum # Ralink Technology RT2501USB wireless NICs + +# Wireless NIC cards +#device wlan # 802.11 support +#device wlan_wep # 802.11 WEP support +#device wlan_ccmp # 802.11 CCMP support +#device wlan_tkip # 802.11 TKIP support +#device wlan_amrr # AMRR transmit rate control algorithm + +# PCI +options NEW_PCIB +device pci + +# PCI Ethernet NICs that use the common MII bus controller code. +# NOTE: Be sure to keep the 'device miibus' line in order to use these NICs! +device re # RealTek 8139C+/8169/8169S/8110S + +# DRM2 +#device fbd +#device vt +#device splash +#device kbdmux +#device drm2 + +# Sound +#device sound +#device snd_hda + +# Flattened Device Tree +options FDT # Configure using FDT/DTB data +device fdt_pinctrl + +# SoC-specific devices + +#device hwpmc +#options HWPMC_HOOKS diff --git a/sys/arm/nvidia/as3722.c b/sys/arm/nvidia/as3722.c new file mode 100644 index 0000000..3a03322 --- /dev/null +++ b/sys/arm/nvidia/as3722.c @@ -0,0 +1,411 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * AS3722 PMIC driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clock_if.h" +#include "regdev_if.h" + +#include "as3722.h" + +static struct ofw_compat_data compat_data[] = { + {"ams,as3722", 1}, + {NULL, 0}, +}; + +#define LOCK(_sc) sx_xlock(&(_sc)->lock) +#define UNLOCK(_sc) sx_xunlock(&(_sc)->lock) +#define LOCK_INIT(_sc) sx_init(&(_sc)->lock, "as3722") +#define LOCK_DESTROY(_sc) sx_destroy(&(_sc)->lock); +#define ASSERT_LOCKED(_sc) sx_assert(&(_sc)->lock, SA_XLOCKED); +#define ASSERT_UNLOCKED(_sc) sx_assert(&(_sc)->lock, SA_UNLOCKED); + +#define AS3722_DEVICE_ID 0x0C + +/* + * Raw register access function. + */ +int +as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val) +{ + uint8_t addr; + int rv; + struct iic_msg msgs[2] = { + {0, IIC_M_WR, 1, &addr}, + {0, IIC_M_RD, 1, val}, + }; + + msgs[0].slave = sc->bus_addr; + msgs[1].slave = sc->bus_addr; + addr = reg; + + rv = iicbus_transfer(sc->dev, msgs, 2); + if (rv != 0) { + device_printf(sc->dev, + "Error when reading reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + + return (0); +} + +int as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, + size_t size) +{ + uint8_t addr; + int rv; + struct iic_msg msgs[2] = { + {0, IIC_M_WR, 1, &addr}, + {0, IIC_M_RD, size, buf}, + }; + + msgs[0].slave = sc->bus_addr; + msgs[1].slave = sc->bus_addr; + addr = reg; + + rv = iicbus_transfer(sc->dev, msgs, 2); + if (rv != 0) { + device_printf(sc->dev, + "Error when reading reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + + return (0); +} + +int +as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val) +{ + uint8_t data[2]; + int rv; + + struct iic_msg msgs[1] = { + {0, IIC_M_WR, 2, data}, + }; + + msgs[0].slave = sc->bus_addr; + data[0] = reg; + data[1] = val; + + rv = iicbus_transfer(sc->dev, msgs, 1); + if (rv != 0) { + device_printf(sc->dev, + "Error when writing reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + return (0); +} + +int as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, + size_t size) +{ + uint8_t data[1]; + int rv; + struct iic_msg msgs[2] = { + {0, IIC_M_WR, 1, data}, + {0, IIC_M_WR | IIC_M_NOSTART, size, buf}, + }; + + msgs[0].slave = sc->bus_addr; + msgs[1].slave = sc->bus_addr; + data[0] = reg; + + rv = iicbus_transfer(sc->dev, msgs, 2); + if (rv != 0) { + device_printf(sc->dev, + "Error when writing reg 0x%02X, rv: %d\n", reg, rv); + return (EIO); + } + return (0); +} + +int +as3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear, uint8_t set) +{ + uint8_t val; + int rv; + + rv = as3722_read(sc, reg, &val); + if (rv != 0) + return (rv); + + val &= ~clear; + val |= set; + + rv = as3722_write(sc, reg, val); + if (rv != 0) + return (rv); + + return (0); +} + +static int +as3722_get_version(struct as3722_softc *sc) +{ + uint8_t reg; + int rv; + + /* Verify AS3722 ID and version. */ + rv = RD1(sc, AS3722_ASIC_ID1, ®); + if (rv != 0) + return (ENXIO); + + if (reg != AS3722_DEVICE_ID) { + device_printf(sc->dev, "Invalid chip ID is 0x%x\n", reg); + return (ENXIO); + } + + rv = RD1(sc, AS3722_ASIC_ID2, &sc->chip_rev); + if (rv != 0) + return (ENXIO); + + if (bootverbose) + device_printf(sc->dev, "AS3722 rev: 0x%x\n", sc->chip_rev); + return (0); +} + +static int +as3722_init(struct as3722_softc *sc) +{ + uint32_t reg; + int rv; + + reg = 0; + if (sc->int_pullup) + reg |= AS3722_INT_PULL_UP; + if (sc->i2c_pullup) + reg |= AS3722_I2C_PULL_UP; + + rv = RM1(sc, AS3722_IO_VOLTAGE, + AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, reg); + if (rv != 0) + return (ENXIO); + + /* mask interrupts */ + rv = WR1(sc, AS3722_INTERRUPT_MASK1, 0); + if (rv != 0) + return (ENXIO); + rv = WR1(sc, AS3722_INTERRUPT_MASK2, 0); + if (rv != 0) + return (ENXIO); + rv = WR1(sc, AS3722_INTERRUPT_MASK3, 0); + if (rv != 0) + return (ENXIO); + rv = WR1(sc, AS3722_INTERRUPT_MASK4, 0); + if (rv != 0) + return (ENXIO); + return (0); +} + +static int +as3722_parse_fdt(struct as3722_softc *sc, phandle_t node) +{ + + sc->int_pullup = + OF_hasprop(node, "ams,enable-internal-int-pullup") ? 1 : 0; + sc->i2c_pullup = + OF_hasprop(node, "ams,enable-internal-i2c-pullup") ? 1 : 0; + return 0; +} + +static void +as3722_intr(void *arg) +{ + struct as3722_softc *sc; + + sc = (struct as3722_softc *)arg; + /* XXX Finish temperature alarms. */ +} + +static int +as3722_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, "AS3722 PMIC"); + return (BUS_PROBE_DEFAULT); +} + +static int +as3722_attach(device_t dev) +{ + struct as3722_softc *sc; + const char *dname; + int dunit, rv, rid; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->bus_addr = iicbus_get_addr(dev); + node = ofw_bus_get_node(sc->dev); + dname = device_get_name(dev); + dunit = device_get_unit(dev); + rv = 0; + LOCK_INIT(sc); + + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate interrupt.\n"); + rv = ENXIO; + goto fail; + } + + rv = as3722_parse_fdt(sc, node); + if (rv != 0) + goto fail; + rv = as3722_get_version(sc); + if (rv != 0) + goto fail; + rv = as3722_init(sc); + if (rv != 0) + goto fail; + rv = as3722_regulator_attach(sc, node); + if (rv != 0) + goto fail; + rv = as3722_gpio_attach(sc, node); + if (rv != 0) + goto fail; + rv = as3722_rtc_attach(sc, node); + if (rv != 0) + goto fail; + + fdt_pinctrl_register(dev, NULL); + fdt_pinctrl_configure_by_name(dev, "default"); + + /* Setup interrupt. */ + rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, as3722_intr, sc, &sc->irq_h); + if (rv) { + device_printf(dev, "Cannot setup interrupt.\n"); + goto fail; + } + return (bus_generic_attach(dev)); + +fail: + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + LOCK_DESTROY(sc); + return (rv); +} + +static int +as3722_detach(device_t dev) +{ + struct as3722_softc *sc; + + sc = device_get_softc(dev); + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + LOCK_DESTROY(sc); + + return (bus_generic_detach(dev)); +} + +static phandle_t +as3722_gpio_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t as3722_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, as3722_probe), + DEVMETHOD(device_attach, as3722_attach), + DEVMETHOD(device_detach, as3722_detach), + + /* Regdev interface */ + DEVMETHOD(regdev_map, as3722_regulator_map), + + /* RTC interface */ + DEVMETHOD(clock_gettime, as3722_rtc_gettime), + DEVMETHOD(clock_settime, as3722_rtc_settime), + + /* GPIO protocol interface */ + DEVMETHOD(gpio_get_bus, as3722_gpio_get_bus), + DEVMETHOD(gpio_pin_max, as3722_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, as3722_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, as3722_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, as3722_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, as3722_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, as3722_gpio_pin_get), + DEVMETHOD(gpio_pin_set, as3722_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, as3722_gpio_pin_toggle), + DEVMETHOD(gpio_map_gpios, as3722_gpio_map_gpios), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure, as3722_pinmux_configure), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, as3722_gpio_get_node), + + DEVMETHOD_END +}; + +static devclass_t as3722_devclass; +DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods, + sizeof(struct as3722_softc)); +EARLY_DRIVER_MODULE(as3722, iicbus, as3722_driver, as3722_devclass, + 0, 0, 74); diff --git a/sys/arm/nvidia/as3722.h b/sys/arm/nvidia/as3722.h new file mode 100644 index 0000000..c559a8a --- /dev/null +++ b/sys/arm/nvidia/as3722.h @@ -0,0 +1,323 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 _AS3722_H_ + +#include + +#define AS3722_SD0_VOLTAGE 0x00 +#define AS3722_SD_VSEL_MASK 0x7F /* For all SD */ +#define AS3722_SD0_VSEL_MIN 0x01 +#define AS3722_SD0_VSEL_MAX 0x5A +#define AS3722_SD0_VSEL_LOW_VOL_MAX 0x6E + +#define AS3722_SD1_VOLTAGE 0x01 +#define AS3722_SD2_VOLTAGE 0x02 +#define AS3722_SD2_VSEL_MIN 0x01 +#define AS3722_SD2_VSEL_MAX 0x7F +#define AS3722_SD3_VOLTAGE 0x03 +#define AS3722_SD4_VOLTAGE 0x04 +#define AS3722_SD5_VOLTAGE 0x05 +#define AS3722_SD6_VOLTAGE 0x06 +#define AS3722_GPIO0_CONTROL 0x08 +#define AS3722_GPIO_INVERT 0x80 +#define AS3722_GPIO_IOSF_MASK 0x0F +#define AS3722_GPIO_IOSF_SHIFT 3 +#define AS3722_GPIO_MODE_MASK 0x07 +#define AS3722_GPIO_MODE_SHIFT 0 + +#define AS3722_GPIO1_CONTROL 0x09 +#define AS3722_GPIO2_CONTROL 0x0A +#define AS3722_GPIO3_CONTROL 0x0B +#define AS3722_GPIO4_CONTROL 0x0C +#define AS3722_GPIO5_CONTROL 0x0D +#define AS3722_GPIO6_CONTROL 0x0E +#define AS3722_GPIO7_CONTROL 0x0F +#define AS3722_LDO0_VOLTAGE 0x10 +#define AS3722_LDO0_VSEL_MASK 0x1F +#define AS3722_LDO0_VSEL_MIN 0x01 +#define AS3722_LDO0_VSEL_MAX 0x12 +#define AS3722_LDO0_NUM_VOLT 0x12 + +#define AS3722_LDO1_VOLTAGE 0x11 +#define AS3722_LDO_VSEL_MASK 0x7F +#define AS3722_LDO_VSEL_MIN 0x01 +#define AS3722_LDO_VSEL_MAX 0x7F +#define AS3722_LDO_VSEL_DNU_MIN 0x25 +#define AS3722_LDO_VSEL_DNU_MAX 0x3F +#define AS3722_LDO_NUM_VOLT 0x80 + +#define AS3722_LDO2_VOLTAGE 0x12 +#define AS3722_LDO3_VOLTAGE 0x13 +#define AS3722_LDO3_VSEL_MASK 0x3F +#define AS3722_LDO3_VSEL_MIN 0x01 +#define AS3722_LDO3_VSEL_MAX 0x2D +#define AS3722_LDO3_NUM_VOLT 0x2D +#define AS3722_LDO3_MODE_MASK (0x3 << 6) +#define AS3722_LDO3_MODE_GET(x) (((x) >> 6) & 0x3) +#define AS3722_LDO3_MODE(x) (((x) & 0x3) << 6) +#define AS3722_LDO3_MODE_PMOS AS3722_LDO3_MODE(0) +#define AS3722_LDO3_MODE_PMOS_TRACKING AS3722_LDO3_MODE(1) +#define AS3722_LDO3_MODE_NMOS AS3722_LDO3_MODE(2) +#define AS3722_LDO3_MODE_SWITCH AS3722_LDO3_MODE(3) + +#define AS3722_LDO4_VOLTAGE 0x14 +#define AS3722_LDO5_VOLTAGE 0x15 +#define AS3722_LDO6_VOLTAGE 0x16 +#define AS3722_LDO6_SEL_BYPASS 0x3F +#define AS3722_LDO7_VOLTAGE 0x17 +#define AS3722_LDO9_VOLTAGE 0x19 +#define AS3722_LDO10_VOLTAGE 0x1A +#define AS3722_LDO11_VOLTAGE 0x1B +#define AS3722_LDO3_SETTINGS 0x1D +#define AS3722_GPIO_DEB1 0x1E +#define AS3722_GPIO_DEB2 0x1F +#define AS3722_GPIO_SIGNAL_OUT 0x20 +#define AS3722_GPIO_SIGNAL_IN 0x21 +#define AS3722_REG_SEQU_MOD1 0x22 +#define AS3722_REG_SEQU_MOD2 0x23 +#define AS3722_REG_SEQU_MOD3 0x24 +#define AS3722_SD_PHSW_CTRL 0x27 +#define AS3722_SD_PHSW_STATUS 0x28 + +#define AS3722_SD0_CONTROL 0x29 +#define AS3722_SD0_MODE_FAST (1 << 4) + +#define AS3722_SD1_CONTROL 0x2A +#define AS3722_SD1_MODE_FAST (1 << 4) + +#define AS3722_SDMPH_CONTROL 0x2B +#define AS3722_SD23_CONTROL 0x2C +#define AS3722_SD3_MODE_FAST (1 << 6) +#define AS3722_SD2_MODE_FAST (1 << 2) + +#define AS3722_SD4_CONTROL 0x2D +#define AS3722_SD4_MODE_FAST (1 << 2) + +#define AS3722_SD5_CONTROL 0x2E +#define AS3722_SD5_MODE_FAST (1 << 2) + +#define AS3722_SD6_CONTROL 0x2F +#define AS3722_SD6_MODE_FAST (1 << 4) + +#define AS3722_SD_DVM 0x30 +#define AS3722_RESET_REASON 0x31 +#define AS3722_BATTERY_VOLTAGE_MONITOR 0x32 +#define AS3722_STARTUP_CONTROL 0x33 +#define AS3722_RESET_TIMER 0x34 +#define AS3722_REFERENCE_CONTROL 0x35 +#define AS3722_RESET_CONTROL 0x36 +#define AS3722_OVERTEMPERATURE_CONTROL 0x37 +#define AS3722_WATCHDOG_CONTROL 0x38 +#define AS3722_REG_STANDBY_MOD1 0x39 +#define AS3722_REG_STANDBY_MOD2 0x3A +#define AS3722_REG_STANDBY_MOD3 0x3B +#define AS3722_ENABLE_CTRL1 0x3C +#define AS3722_SD3_EXT_ENABLE_MASK 0xC0 +#define AS3722_SD2_EXT_ENABLE_MASK 0x30 +#define AS3722_SD1_EXT_ENABLE_MASK 0x0C +#define AS3722_SD0_EXT_ENABLE_MASK 0x03 + +#define AS3722_ENABLE_CTRL2 0x3D +#define AS3722_SD6_EXT_ENABLE_MASK 0x30 +#define AS3722_SD5_EXT_ENABLE_MASK 0x0C +#define AS3722_SD4_EXT_ENABLE_MASK 0x03 + +#define AS3722_ENABLE_CTRL3 0x3E +#define AS3722_LDO3_EXT_ENABLE_MASK 0xC0 +#define AS3722_LDO2_EXT_ENABLE_MASK 0x30 +#define AS3722_LDO1_EXT_ENABLE_MASK 0x0C +#define AS3722_LDO0_EXT_ENABLE_MASK 0x03 + +#define AS3722_ENABLE_CTRL4 0x3F +#define AS3722_LDO7_EXT_ENABLE_MASK 0xC0 +#define AS3722_LDO6_EXT_ENABLE_MASK 0x30 +#define AS3722_LDO5_EXT_ENABLE_MASK 0x0C +#define AS3722_LDO4_EXT_ENABLE_MASK 0x03 + +#define AS3722_ENABLE_CTRL5 0x40 +#define AS3722_LDO11_EXT_ENABLE_MASK 0xC0 +#define AS3722_LDO10_EXT_ENABLE_MASK 0x30 +#define AS3722_LDO9_EXT_ENABLE_MASK 0x0C + +#define AS3722_PWM_CONTROL_L 0x41 +#define AS3722_PWM_CONTROL_H 0x42 +#define AS3722_WATCHDOG_TIMER 0x46 +#define AS3722_WATCHDOG_SOFTWARE_SIGNAL 0x48 +#define AS3722_IO_VOLTAGE 0x49 +#define AS3722_I2C_PULL_UP (1 << 4) +#define AS3722_INT_PULL_UP (1 << 5) + +#define AS3722_BATTERY_VOLTAGE_MONITOR2 0x4A +#define AS3722_SD_CONTROL 0x4D +#define AS3722_SDN_CTRL(x) (1 << (x)) + +#define AS3722_LDO_CONTROL0 0x4E +#define AS3722_LDO7_CTRL (1 << 7) +#define AS3722_LDO6_CTRL (1 << 6) +#define AS3722_LDO5_CTRL (1 << 5) +#define AS3722_LDO4_CTRL (1 << 4) +#define AS3722_LDO3_CTRL (1 << 3) +#define AS3722_LDO2_CTRL (1 << 2) +#define AS3722_LDO1_CTRL (1 << 1) +#define AS3722_LDO0_CTRL (1 << 0) + +#define AS3722_LDO_CONTROL1 0x4F +#define AS3722_LDO11_CTRL (1 << 3) +#define AS3722_LDO10_CTRL (1 << 2) +#define AS3722_LDO9_CTRL (1 << 1) + +#define AS3722_SD0_PROTECT 0x50 +#define AS3722_SD6_PROTECT 0x51 +#define AS3722_PWM_VCONTROL1 0x52 +#define AS3722_PWM_VCONTROL2 0x53 +#define AS3722_PWM_VCONTROL3 0x54 +#define AS3722_PWM_VCONTROL4 0x55 +#define AS3722_BB_CHARGER 0x57 +#define AS3722_CTRL_SEQU1 0x58 +#define AS3722_CTRL_SEQU2 0x59 +#define AS3722_OV_CURRENT 0x5A +#define AS3722_OV_CURRENT_DEB 0x5B +#define AS3722_SDLV_DEB 0x5C +#define AS3722_OC_PG_CTRL 0x5D +#define AS3722_OC_PG_CTRL2 0x5E +#define AS3722_CTRL_STATUS 0x5F +#define AS3722_RTC_CONTROL 0x60 +#define AS3722_RTC_AM_PM_MODE (1 << 7) +#define AS3722_RTC_CLK32K_OUT_EN (1 << 5) +#define AS3722_RTC_IRQ_MODE (1 << 3) +#define AS3722_RTC_ON (1 << 2) +#define AS3722_RTC_ALARM_WAKEUP_EN (1 << 1) +#define AS3722_RTC_REP_WAKEUP_EN (1 << 0) + +#define AS3722_RTC_SECOND 0x61 +#define AS3722_RTC_MINUTE 0x62 +#define AS3722_RTC_HOUR 0x63 +#define AS3722_RTC_DAY 0x64 +#define AS3722_RTC_MONTH 0x65 +#define AS3722_RTC_YEAR 0x66 +#define AS3722_RTC_ALARM_SECOND 0x67 +#define AS3722_RTC_ALARM_MINUTE 0x68 +#define AS3722_RTC_ALARM_HOUR 0x69 +#define AS3722_RTC_ALARM_DAY 0x6A +#define AS3722_RTC_ALARM_MONTH 0x6B +#define AS3722_RTC_ALARM_YEAR 0x6C +#define AS3722_SRAM 0x6D +#define AS3722_RTC_ACCESS 0x6F +#define AS3722_REG_STATUS 0x73 +#define AS3722_INTERRUPT_MASK1 0x74 +#define AS3722_INTERRUPT_MASK2 0x75 +#define AS3722_INTERRUPT_MASK3 0x76 +#define AS3722_INTERRUPT_MASK4 0x77 +#define AS3722_INTERRUPT_STATUS1 0x78 +#define AS3722_INTERRUPT_STATUS2 0x79 +#define AS3722_INTERRUPT_STATUS3 0x7A +#define AS3722_INTERRUPT_STATUS4 0x7B +#define AS3722_TEMP_STATUS 0x7D +#define AS3722_ADC0_CONTROL 0x80 +#define AS3722_ADC1_CONTROL 0x81 +#define AS3722_ADC0_MSB_RESULT 0x82 +#define AS3722_ADC0_LSB_RESULT 0x83 +#define AS3722_ADC1_MSB_RESULT 0x84 +#define AS3722_ADC1_LSB_RESULT 0x85 +#define AS3722_ADC1_THRESHOLD_HI_MSB 0x86 +#define AS3722_ADC1_THRESHOLD_HI_LSB 0x87 +#define AS3722_ADC1_THRESHOLD_LO_MSB 0x88 +#define AS3722_ADC1_THRESHOLD_LO_LSB 0x89 +#define AS3722_ADC_CONFIGURATION 0x8A +#define AS3722_ASIC_ID1 0x90 +#define AS3722_ASIC_ID2 0x91 +#define AS3722_LOCK 0x9E +#define AS3722_FUSE7 0x9E +#define AS3722_FUSE7_SD0_LOW_VOLTAGE (1 << 4) + +struct as3722_reg_sc; +struct as3722_gpio_pin; + +struct as3722_softc { + device_t dev; + struct sx lock; + int bus_addr; + struct resource *irq_res; + void *irq_h; + + uint8_t chip_rev; + int int_pullup; + int i2c_pullup; + + /* Regulators. */ + struct as3722_reg_sc **regs; + int nregs; + + /* GPIO */ + device_t gpio_busdev; + struct as3722_gpio_pin **gpio_pins; + int gpio_npins; + struct sx gpio_lock; + +}; + +#define RD1(sc, reg, val) as3722_read(sc, reg, val) +#define WR1(sc, reg, val) as3722_write(sc, reg, val) +#define RM1(sc, reg, clr, set) as3722_modify(sc, reg, clr, set) + +int as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val); +int as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val); +int as3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear, + uint8_t set); +int as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, + size_t size); +int as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf, + size_t size); + +/* Regulators */ +int as3722_regulator_attach(struct as3722_softc *sc, phandle_t node); +int as3722_regulator_map(device_t dev, phandle_t xref, int ncells, + pcell_t *cells, int *num); + +/* RTC */ +int as3722_rtc_attach(struct as3722_softc *sc, phandle_t node); +int as3722_rtc_gettime(device_t dev, struct timespec *ts); +int as3722_rtc_settime(device_t dev, struct timespec *ts); + +/* GPIO */ +device_t as3722_gpio_get_bus(device_t dev); +int as3722_gpio_pin_max(device_t dev, int *maxpin); +int as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name); +int as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags); +int as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); +int as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); +int as3722_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); +int as3722_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); +int as3722_gpio_pin_toggle(device_t dev, uint32_t pin); +int as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent, + int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags); +int as3722_gpio_attach(struct as3722_softc *sc, phandle_t node); +int as3722_pinmux_configure(device_t dev, phandle_t cfgxref); + +#endif /* _AS3722_H_ */ diff --git a/sys/arm/nvidia/as3722_gpio.c b/sys/arm/nvidia/as3722_gpio.c new file mode 100644 index 0000000..8e53bce --- /dev/null +++ b/sys/arm/nvidia/as3722_gpio.c @@ -0,0 +1,577 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 "as3722.h" + +MALLOC_DEFINE(M_AS3722_GPIO, "AS3722 gpio", "AS3722 GPIO"); + +/* AS3722_GPIOx_CONTROL MODE and IOSF definition. */ +#define AS3722_IOSF_GPIO 0x00 +#define AS3722_IOSF_INTERRUPT_OUT 0x01 +#define AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT 0x02 +#define AS3722_IOSF_GPIO_IN_INTERRUPT 0x03 +#define AS3722_IOSF_PWM_IN 0x04 +#define AS3722_IOSF_VOLTAGE_IN_STANDBY 0x05 +#define AS3722_IOSF_OC_PG_SD0 0x06 +#define AS3722_IOSF_POWERGOOD_OUT 0x07 +#define AS3722_IOSF_CLK32K_OUT 0x08 +#define AS3722_IOSF_WATCHDOG_IN 0x09 +#define AS3722_IOSF_SOFT_RESET_IN 0x0b +#define AS3722_IOSF_PWM_OUT 0x0c +#define AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT 0x0d +#define AS3722_IOSF_OC_PG_SD6 0x0e + +#define AS3722_MODE_INPUT 0 +#define AS3722_MODE_PUSH_PULL 1 +#define AS3722_MODE_OPEN_DRAIN 2 +#define AS3722_MODE_TRISTATE 3 +#define AS3722_MODE_INPUT_PULL_UP_LV 4 +#define AS3722_MODE_INPUT_PULL_DOWN 5 +#define AS3722_MODE_OPEN_DRAIN_LV 6 +#define AS3722_MODE_PUSH_PULL_LV 7 + +#define NGPIO 8 + +#define GPIO_LOCK(_sc) sx_slock(&(_sc)->gpio_lock) +#define GPIO_UNLOCK(_sc) sx_unlock(&(_sc)->gpio_lock) +#define GPIO_ASSERT(_sc) sx_assert(&(_sc)->gpio_lock, SA_LOCKED) + +#define AS3722_CFG_BIAS_DISABLE 0x0001 +#define AS3722_CFG_BIAS_PULL_UP 0x0002 +#define AS3722_CFG_BIAS_PULL_DOWN 0x0004 +#define AS3722_CFG_BIAS_HIGH_IMPEDANCE 0x0008 +#define AS3722_CFG_OPEN_DRAIN 0x0010 + +static const struct { + const char *name; + int config; /* AS3722_CFG_ */ +} as3722_cfg_names[] = { + {"bias-disable", AS3722_CFG_BIAS_DISABLE}, + {"bias-pull-up", AS3722_CFG_BIAS_PULL_UP}, + {"bias-pull-down", AS3722_CFG_BIAS_PULL_DOWN}, + {"bias-high-impedance", AS3722_CFG_BIAS_HIGH_IMPEDANCE}, + {"drive-open-drain", AS3722_CFG_OPEN_DRAIN}, +}; + +static struct { + const char *name; + int fnc_val; +} as3722_fnc_table[] = { + {"gpio", AS3722_IOSF_GPIO}, + {"interrupt-out", AS3722_IOSF_INTERRUPT_OUT}, + {"vsup-vbat-low-undebounce-out", AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT}, + {"gpio-in-interrupt", AS3722_IOSF_GPIO_IN_INTERRUPT}, + {"pwm-in", AS3722_IOSF_PWM_IN}, + {"voltage-in-standby", AS3722_IOSF_VOLTAGE_IN_STANDBY}, + {"oc-pg-sd0", AS3722_IOSF_OC_PG_SD0}, + {"powergood-out", AS3722_IOSF_POWERGOOD_OUT}, + {"clk32k-out", AS3722_IOSF_CLK32K_OUT}, + {"watchdog-in", AS3722_IOSF_WATCHDOG_IN}, + {"soft-reset-in", AS3722_IOSF_SOFT_RESET_IN}, + {"pwm-out", AS3722_IOSF_PWM_OUT}, + {"vsup-vbat-low-debounce-out", AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT}, + {"oc-pg-sd6", AS3722_IOSF_OC_PG_SD6}, +}; + +struct as3722_pincfg { + char *function; + int flags; +}; + +struct as3722_gpio_pin { + int pin_caps; + uint8_t pin_ctrl_reg; + char pin_name[GPIOMAXNAME]; + int pin_cfg_flags; +}; + + +/* -------------------------------------------------------------------------- + * + * Pinmux functions. + */ +static int +as3722_pinmux_get_function(struct as3722_softc *sc, char *name) +{ + int i; + + for (i = 0; i < nitems(as3722_fnc_table); i++) { + if (strcmp(as3722_fnc_table[i].name, name) == 0) + return (as3722_fnc_table[i].fnc_val); + } + return (-1); +} + + + +static int +as3722_pinmux_config_node(struct as3722_softc *sc, char *pin_name, + struct as3722_pincfg *cfg) +{ + uint8_t ctrl; + int rv, fnc, pin; + + for (pin = 0; pin < sc->gpio_npins; pin++) { + if (strcmp(sc->gpio_pins[pin]->pin_name, pin_name) == 0) + break; + } + if (pin >= sc->gpio_npins) { + device_printf(sc->dev, "Unknown pin: %s\n", pin_name); + return (ENXIO); + } + + ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; + sc->gpio_pins[pin]->pin_cfg_flags = cfg->flags; + if (cfg->function != NULL) { + fnc = as3722_pinmux_get_function(sc, cfg->function); + if (fnc == -1) { + device_printf(sc->dev, + "Unknown function %s for pin %s\n", cfg->function, + sc->gpio_pins[pin]->pin_name); + return (ENXIO); + } + switch (fnc) { + case AS3722_IOSF_INTERRUPT_OUT: + case AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT: + case AS3722_IOSF_OC_PG_SD0: + case AS3722_IOSF_POWERGOOD_OUT: + case AS3722_IOSF_CLK32K_OUT: + case AS3722_IOSF_PWM_OUT: + case AS3722_IOSF_OC_PG_SD6: + ctrl &= ~(AS3722_GPIO_MODE_MASK << + AS3722_GPIO_MODE_SHIFT); + ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT; + /* XXX Handle flags (OC + pullup) */ + break; + case AS3722_IOSF_GPIO_IN_INTERRUPT: + case AS3722_IOSF_PWM_IN: + case AS3722_IOSF_VOLTAGE_IN_STANDBY: + case AS3722_IOSF_WATCHDOG_IN: + case AS3722_IOSF_SOFT_RESET_IN: + ctrl &= ~(AS3722_GPIO_MODE_MASK << + AS3722_GPIO_MODE_SHIFT); + ctrl |= AS3722_MODE_INPUT << AS3722_GPIO_MODE_SHIFT; + /* XXX Handle flags (pulldown + pullup) */ + + default: + break; + } + ctrl &= ~(AS3722_GPIO_IOSF_MASK << AS3722_GPIO_IOSF_SHIFT); + ctrl |= fnc << AS3722_GPIO_IOSF_SHIFT; + } + rv = 0; + if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) { + rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl); + sc->gpio_pins[pin]->pin_ctrl_reg = ctrl; + } + return (rv); +} + +static int +as3722_pinmux_read_node(struct as3722_softc *sc, phandle_t node, + struct as3722_pincfg *cfg, char **pins, int *lpins) +{ + int rv, i; + + *lpins = OF_getprop_alloc(node, "pins", 1, (void **)pins); + if (*lpins <= 0) + return (ENOENT); + + /* Read function (mux) settings. */ + rv = OF_getprop_alloc(node, "function", 1, (void **)&cfg->function); + if (rv <= 0) + cfg->function = NULL; + + /* Read boolean properties. */ + for (i = 0; i < nitems(as3722_cfg_names); i++) { + if (OF_hasprop(node, as3722_cfg_names[i].name)) + cfg->flags |= as3722_cfg_names[i].config; + } + return (0); +} + +static int +as3722_pinmux_process_node(struct as3722_softc *sc, phandle_t node) +{ + struct as3722_pincfg cfg; + char *pins, *pname; + int i, len, lpins, rv; + + rv = as3722_pinmux_read_node(sc, node, &cfg, &pins, &lpins); + if (rv != 0) + return (rv); + + len = 0; + pname = pins; + do { + i = strlen(pname) + 1; + rv = as3722_pinmux_config_node(sc, pname, &cfg); + if (rv != 0) { + device_printf(sc->dev, + "Cannot configure pin: %s: %d\n", pname, rv); + } + len += i; + pname += i; + } while (len < lpins); + + if (pins != NULL) + free(pins, M_OFWPROP); + if (cfg.function != NULL) + free(cfg.function, M_OFWPROP); + + return (rv); +} + +int as3722_pinmux_configure(device_t dev, phandle_t cfgxref) +{ + struct as3722_softc *sc; + phandle_t node, cfgnode; + int rv; + + sc = device_get_softc(dev); + cfgnode = OF_node_from_xref(cfgxref); + + for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + rv = as3722_pinmux_process_node(sc, node); + if (rv != 0) + device_printf(dev, "Failed to process pinmux"); + + } + return (0); +} + +/* -------------------------------------------------------------------------- + * + * GPIO + */ +device_t +as3722_gpio_get_bus(device_t dev) +{ + struct as3722_softc *sc; + + sc = device_get_softc(dev); + return (sc->gpio_busdev); +} + +int +as3722_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = NGPIO - 1; + return (0); +} + +int +as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct as3722_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + GPIO_LOCK(sc); + *caps = sc->gpio_pins[pin]->pin_caps; + GPIO_UNLOCK(sc); + return (0); +} + +int +as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct as3722_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + GPIO_LOCK(sc); + memcpy(name, sc->gpio_pins[pin]->pin_name, GPIOMAXNAME); + GPIO_UNLOCK(sc); + return (0); +} + +int +as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *out_flags) +{ + struct as3722_softc *sc; + uint8_t tmp, mode, iosf; + uint32_t flags; + bool inverted; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + tmp = sc->gpio_pins[pin]->pin_ctrl_reg; + GPIO_UNLOCK(sc); + iosf = (tmp >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK; + mode = (tmp >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK; + inverted = (tmp & AS3722_GPIO_INVERT) != 0; + /* Is pin in GPIO mode ? */ + if (iosf != AS3722_IOSF_GPIO) + return (ENXIO); + + flags = 0; + switch (mode) { + case AS3722_MODE_INPUT: + flags = GPIO_PIN_INPUT; + break; + case AS3722_MODE_PUSH_PULL: + case AS3722_MODE_PUSH_PULL_LV: + flags = GPIO_PIN_OUTPUT; + break; + case AS3722_MODE_OPEN_DRAIN: + case AS3722_MODE_OPEN_DRAIN_LV: + flags = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN; + break; + case AS3722_MODE_TRISTATE: + flags = GPIO_PIN_TRISTATE; + break; + case AS3722_MODE_INPUT_PULL_UP_LV: + flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP; + break; + + case AS3722_MODE_INPUT_PULL_DOWN: + flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLDOWN; + break; + } + if (inverted) + flags |= GPIO_PIN_INVIN | GPIO_PIN_INVOUT; + *out_flags = flags; + return (0); +} + +static int +as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags) +{ + uint8_t ctrl; + int flags; + + ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; + flags = sc->gpio_pins[pin]->pin_cfg_flags; + + /* Tristate mode. */ + if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE || + gpio_flags & GPIO_PIN_TRISTATE) + return (AS3722_MODE_TRISTATE); + + /* Open drain modes. */ + if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) { + /* Only pull up have effect */ + if (flags & AS3722_CFG_BIAS_PULL_UP || + gpio_flags & GPIO_PIN_PULLUP) + return (AS3722_MODE_OPEN_DRAIN_LV); + return (AS3722_MODE_OPEN_DRAIN); + } + /* Input modes. */ + if (gpio_flags & GPIO_PIN_INPUT) { + /* Accept pull up or pull down. */ + if (flags & AS3722_CFG_BIAS_PULL_UP || + gpio_flags & GPIO_PIN_PULLUP) + return (AS3722_MODE_INPUT_PULL_UP_LV); + + if (flags & AS3722_CFG_BIAS_PULL_DOWN || + gpio_flags & GPIO_PIN_PULLDOWN) + return (AS3722_MODE_INPUT_PULL_DOWN); + return (AS3722_MODE_INPUT); + } + /* + * Output modes. + * Pull down is used as indicator of low voltage output. + */ + if (flags & AS3722_CFG_BIAS_PULL_DOWN || + gpio_flags & GPIO_PIN_PULLDOWN) + return (AS3722_MODE_PUSH_PULL_LV); + return (AS3722_MODE_PUSH_PULL); +} + +int +as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct as3722_softc *sc; + uint8_t ctrl, mode, iosf; + int rv; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; + iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK; + /* Is pin in GPIO mode ? */ + if (iosf != AS3722_IOSF_GPIO) { + GPIO_UNLOCK(sc); + return (ENXIO); + } + mode = as3722_gpio_get_mode(sc, pin, flags); + ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT); + ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT; + rv = 0; + if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) { + rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl); + sc->gpio_pins[pin]->pin_ctrl_reg = ctrl; + } + GPIO_UNLOCK(sc); + return (rv); +} + +int +as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val) +{ + struct as3722_softc *sc; + uint8_t tmp; + int rv; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + tmp = (val != 0) ? 1 : 0; + if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT) + tmp ^= 1; + + GPIO_LOCK(sc); + rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin)); + GPIO_UNLOCK(sc); + return (rv); +} + +int +as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val) +{ + struct as3722_softc *sc; + uint8_t tmp, mode, ctrl; + int rv; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; + mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK; + if ((mode == AS3722_MODE_PUSH_PULL) || + (mode == AS3722_MODE_PUSH_PULL_LV)) + rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp); + else + rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp); + GPIO_UNLOCK(sc); + if (rv != 0) + return (rv); + + *val = tmp & (1 << pin) ? 1 : 0; + if (ctrl & AS3722_GPIO_INVERT) + *val ^= 1; + return (0); +} + +int +as3722_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct as3722_softc *sc; + uint8_t tmp; + int rv; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp); + if (rv != 0) { + GPIO_UNLOCK(sc); + return (rv); + } + tmp ^= (1 <gpio_lock, "AS3722 GPIO lock"); + sc->gpio_npins = NGPIO; + sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) * + sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO); + + + sc->gpio_busdev = gpiobus_attach_bus(sc->dev); + if (sc->gpio_busdev == NULL) + return (ENXIO); + for (i = 0; i < sc->gpio_npins; i++) { + sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin), + M_AS3722_GPIO, M_WAITOK | M_ZERO); + pin = sc->gpio_pins[i]; + sprintf(pin->pin_name, "gpio%d", i); + pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | + GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE | + GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN | + GPIO_PIN_INVOUT; + rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg); + if (rv != 0) { + device_printf(sc->dev, + "Cannot read configuration for pin %s\n", + sc->gpio_pins[i]->pin_name); + } + } + return (0); +} diff --git a/sys/arm/nvidia/as3722_regulators.c b/sys/arm/nvidia/as3722_regulators.c new file mode 100644 index 0000000..0bca8e1 --- /dev/null +++ b/sys/arm/nvidia/as3722_regulators.c @@ -0,0 +1,811 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 "as3722.h" + +MALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator"); + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +enum as3722_reg_id { + AS3722_REG_ID_SD0, + AS3722_REG_ID_SD1, + AS3722_REG_ID_SD2, + AS3722_REG_ID_SD3, + AS3722_REG_ID_SD4, + AS3722_REG_ID_SD5, + AS3722_REG_ID_SD6, + AS3722_REG_ID_LDO0, + AS3722_REG_ID_LDO1, + AS3722_REG_ID_LDO2, + AS3722_REG_ID_LDO3, + AS3722_REG_ID_LDO4, + AS3722_REG_ID_LDO5, + AS3722_REG_ID_LDO6, + AS3722_REG_ID_LDO7, + AS3722_REG_ID_LDO9, + AS3722_REG_ID_LDO10, + AS3722_REG_ID_LDO11, +}; + +struct regulator_range { + u_int min_uvolt; + u_int step_uvolt; + u_int min_sel; + u_int max_sel; +}; + + +/* Regulator HW definition. */ +struct reg_def { + intptr_t id; /* ID */ + char *name; /* Regulator name */ + char *supply_name; /* Source property name */ + uint8_t volt_reg; + uint8_t volt_vsel_mask; + uint8_t enable_reg; + uint8_t enable_mask; + uint8_t ext_enable_reg; + uint8_t ext_enable_mask; + struct regulator_range *ranges; + int nranges; +}; + +struct as3722_reg_sc { + struct regnode *regnode; + struct as3722_softc *base_sc; + struct reg_def *def; + phandle_t xref; + + struct regnode_std_param *param; + int ext_control; + int enable_tracking; + + int enable_usec; +}; + +#define RANGE_INIT(_min_sel, _max_sel, _min_uvolt, _step_uvolt) \ +{ \ + .min_sel = _min_sel, \ + .max_sel = _max_sel, \ + .min_uvolt = _min_uvolt, \ + .step_uvolt = _step_uvolt, \ +} + +static struct regulator_range as3722_sd016_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x5A, 610000, 10000), +}; + +static struct regulator_range as3722_sd0_lv_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x6E, 410000, 10000), +}; + +static struct regulator_range as3722_sd_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x40, 612500, 12500), + RANGE_INIT(0x41, 0x70, 1425000, 25000), + RANGE_INIT(0x71, 0x7F, 2650000, 50000), +}; + +static struct regulator_range as3722_ldo3_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x2D, 620000, 20000), +}; + +static struct regulator_range as3722_ldo_ranges[] = { + RANGE_INIT(0x00, 0x00, 0, 0), + RANGE_INIT(0x01, 0x24, 825000, 25000), + RANGE_INIT(0x40, 0x7F, 1725000, 25000), +}; + +static struct reg_def as3722s_def[] = { + { + .id = AS3722_REG_ID_SD0, + .name = "sd0", + .volt_reg = AS3722_SD0_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(0), + .ext_enable_reg = AS3722_ENABLE_CTRL1, + .ext_enable_mask = AS3722_SD0_EXT_ENABLE_MASK, + .ranges = as3722_sd016_ranges, + .nranges = nitems(as3722_sd016_ranges), + }, + { + .id = AS3722_REG_ID_SD1, + .name = "sd1", + .volt_reg = AS3722_SD1_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(1), + .ext_enable_reg = AS3722_ENABLE_CTRL1, + .ext_enable_mask = AS3722_SD1_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD2, + .name = "sd2", + .supply_name = "vsup-sd2", + .volt_reg = AS3722_SD2_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(2), + .ext_enable_reg = AS3722_ENABLE_CTRL1, + .ext_enable_mask = AS3722_SD2_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD3, + .name = "sd3", + .supply_name = "vsup-sd3", + .volt_reg = AS3722_SD3_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(3), + .ext_enable_reg = AS3722_ENABLE_CTRL1, + .ext_enable_mask = AS3722_SD3_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD4, + .name = "sd4", + .supply_name = "vsup-sd4", + .volt_reg = AS3722_SD4_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(4), + .ext_enable_reg = AS3722_ENABLE_CTRL2, + .ext_enable_mask = AS3722_SD4_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD5, + .name = "sd5", + .supply_name = "vsup-sd5", + .volt_reg = AS3722_SD5_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(5), + .ext_enable_reg = AS3722_ENABLE_CTRL2, + .ext_enable_mask = AS3722_SD5_EXT_ENABLE_MASK, + .ranges = as3722_sd_ranges, + .nranges = nitems(as3722_sd_ranges), + }, + { + .id = AS3722_REG_ID_SD6, + .name = "sd6", + .volt_reg = AS3722_SD6_VOLTAGE, + .volt_vsel_mask = AS3722_SD_VSEL_MASK, + .enable_reg = AS3722_SD_CONTROL, + .enable_mask = AS3722_SDN_CTRL(6), + .ext_enable_reg = AS3722_ENABLE_CTRL2, + .ext_enable_mask = AS3722_SD6_EXT_ENABLE_MASK, + .ranges = as3722_sd016_ranges, + .nranges = nitems(as3722_sd016_ranges), + }, + { + .id = AS3722_REG_ID_LDO0, + .name = "ldo0", + .supply_name = "vin-ldo0", + .volt_reg = AS3722_LDO0_VOLTAGE, + .volt_vsel_mask = AS3722_LDO0_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO0_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL3, + .ext_enable_mask = AS3722_LDO0_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO1, + .name = "ldo1", + .supply_name = "vin-ldo1-6", + .volt_reg = AS3722_LDO1_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO1_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL3, + .ext_enable_mask = AS3722_LDO1_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO2, + .name = "ldo2", + .supply_name = "vin-ldo2-5-7", + .volt_reg = AS3722_LDO2_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO2_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL3, + .ext_enable_mask = AS3722_LDO2_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO3, + .name = "ldo3", + .supply_name = "vin-ldo3-4", + .volt_reg = AS3722_LDO3_VOLTAGE, + .volt_vsel_mask = AS3722_LDO3_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO3_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL3, + .ext_enable_mask = AS3722_LDO3_EXT_ENABLE_MASK, + .ranges = as3722_ldo3_ranges, + .nranges = nitems(as3722_ldo3_ranges), + }, + { + .id = AS3722_REG_ID_LDO4, + .name = "ldo4", + .supply_name = "vin-ldo3-4", + .volt_reg = AS3722_LDO4_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO4_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL4, + .ext_enable_mask = AS3722_LDO4_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO5, + .name = "ldo5", + .supply_name = "vin-ldo2-5-7", + .volt_reg = AS3722_LDO5_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO5_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL4, + .ext_enable_mask = AS3722_LDO5_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO6, + .name = "ldo6", + .supply_name = "vin-ldo1-6", + .volt_reg = AS3722_LDO6_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO6_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL4, + .ext_enable_mask = AS3722_LDO6_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO7, + .name = "ldo7", + .supply_name = "vin-ldo2-5-7", + .volt_reg = AS3722_LDO7_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL0, + .enable_mask = AS3722_LDO7_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL4, + .ext_enable_mask = AS3722_LDO7_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO9, + .name = "ldo9", + .supply_name = "vin-ldo9-10", + .volt_reg = AS3722_LDO9_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL1, + .enable_mask = AS3722_LDO9_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL5, + .ext_enable_mask = AS3722_LDO9_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO10, + .name = "ldo10", + .supply_name = "vin-ldo9-10", + .volt_reg = AS3722_LDO10_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL1, + .enable_mask = AS3722_LDO10_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL5, + .ext_enable_mask = AS3722_LDO10_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, + { + .id = AS3722_REG_ID_LDO11, + .name = "ldo11", + .supply_name = "vin-ldo11", + .volt_reg = AS3722_LDO11_VOLTAGE, + .volt_vsel_mask = AS3722_LDO_VSEL_MASK, + .enable_reg = AS3722_LDO_CONTROL1, + .enable_mask = AS3722_LDO11_CTRL, + .ext_enable_reg = AS3722_ENABLE_CTRL5, + .ext_enable_mask = AS3722_LDO11_EXT_ENABLE_MASK, + .ranges = as3722_ldo_ranges, + .nranges = nitems(as3722_ldo_ranges), + }, +}; + + +struct as3722_regnode_init_def { + struct regnode_init_def reg_init_def; + int ext_control; + int enable_tracking; +}; + +static int as3722_regnode_init(struct regnode *regnode); +static int as3722_regnode_enable(struct regnode *regnode, bool enable, + int *udelay); +static int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, + int max_uvolt, int *udelay); +static int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt); +static regnode_method_t as3722_regnode_methods[] = { + /* Regulator interface */ + REGNODEMETHOD(regnode_init, as3722_regnode_init), + REGNODEMETHOD(regnode_enable, as3722_regnode_enable), + REGNODEMETHOD(regnode_set_voltage, as3722_regnode_set_volt), + REGNODEMETHOD(regnode_get_voltage, as3722_regnode_get_volt), + REGNODEMETHOD_END +}; +DEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods, + sizeof(struct as3722_reg_sc), regnode_class); + +static int +regulator_range_sel_to_volt(struct as3722_reg_sc *sc, uint8_t sel, int *volt) +{ + struct regulator_range *range; + struct reg_def *def; + int i; + + def = sc->def; + if (def->nranges == 0) + panic("Voltage regulator have zero ranges\n"); + + for (i = 0; i < def->nranges ; i++) { + range = def->ranges + i; + + if (!(sel >= range->min_sel && + sel <= range->max_sel)) + continue; + + sel -= range->min_sel; + + *volt = range->min_uvolt + sel * range->step_uvolt; + return (0); + } + + return (ERANGE); +} + +static int +regulator_range_volt_to_sel(struct as3722_reg_sc *sc, int min_uvolt, + int max_uvolt, uint8_t *out_sel) +{ + struct regulator_range *range; + struct reg_def *def; + uint8_t sel; + int uvolt; + int rv, i; + + def = sc->def; + if (def->nranges == 0) + panic("Voltage regulator have zero ranges\n"); + + for (i = 0; i < def->nranges; i++) { + range = def->ranges + i; + uvolt = range->min_uvolt + + (range->max_sel - range->min_sel) * range->step_uvolt; + + if ((min_uvolt > uvolt) || + (max_uvolt < range->min_uvolt)) + continue; + + if (min_uvolt <= range->min_uvolt) + min_uvolt = range->min_uvolt; + + /* If step is zero then range is fixed voltage range. */ + if (range->step_uvolt == 0) + sel = 0; + else + sel = DIV_ROUND_UP(min_uvolt - range->min_uvolt, + range->step_uvolt); + + + sel += range->min_sel; + + break; + } + + if (i >= def->nranges) + return (ERANGE); + + /* Verify new settings. */ + rv = regulator_range_sel_to_volt(sc, sel, &uvolt); + if (rv != 0) + return (rv); + if ((uvolt < min_uvolt) || (uvolt > max_uvolt)) + return (ERANGE); + + *out_sel = sel; + return (0); +} + + +static int +as3722_read_sel(struct as3722_reg_sc *sc, uint8_t *sel) +{ + int rv; + + rv = RD1(sc->base_sc, sc->def->volt_reg, sel); + if (rv != 0) + return (rv); + *sel &= sc->def->volt_vsel_mask; + *sel >>= ffs(sc->def->volt_vsel_mask) - 1; + return (0); +} + +static int +as3722_write_sel(struct as3722_reg_sc *sc, uint8_t sel) +{ + int rv; + + sel <<= ffs(sc->def->volt_vsel_mask) - 1; + sel &= sc->def->volt_vsel_mask; + + rv = RM1(sc->base_sc, sc->def->volt_reg, + sc->def->volt_vsel_mask, sel); + if (rv != 0) + return (rv); + return (rv); +} + +static bool +as3722_sd0_is_low_voltage(struct as3722_reg_sc *sc) +{ + uint8_t val; + int rv; + + rv = RD1(sc->base_sc, AS3722_FUSE7, &val); + if (rv != 0) + return (rv); + return (val & AS3722_FUSE7_SD0_LOW_VOLTAGE ? true : false); +} + +static int +as3722_reg_extreg_setup(struct as3722_reg_sc *sc, int ext_pwr_ctrl) +{ + uint8_t val; + int rv; + + val = ext_pwr_ctrl << (ffs(sc->def->ext_enable_mask) - 1); + rv = RM1(sc->base_sc, sc->def->ext_enable_reg, + sc->def->ext_enable_mask, val); + return (rv); +} + +static int +as3722_reg_enable(struct as3722_reg_sc *sc) +{ + int rv; + + rv = RM1(sc->base_sc, sc->def->enable_reg, + sc->def->enable_mask, sc->def->enable_mask); + return (rv); +} + +static int +as3722_reg_disable(struct as3722_reg_sc *sc) +{ + int rv; + + rv = RM1(sc->base_sc, sc->def->enable_reg, + sc->def->enable_mask, 0); + return (rv); +} + +static int +as3722_regnode_init(struct regnode *regnode) +{ + struct as3722_reg_sc *sc; + int rv; + + sc = regnode_get_softc(regnode); + + sc->enable_usec = 500; + if (sc->def->id == AS3722_REG_ID_SD0) { + if (as3722_sd0_is_low_voltage(sc)) { + sc->def->ranges = as3722_sd0_lv_ranges; + sc->def->nranges = nitems(as3722_sd0_lv_ranges); + } + sc->enable_usec = 600; + } else if (sc->def->id == AS3722_REG_ID_LDO3) { + if (sc->enable_tracking) { + rv = RM1(sc->base_sc, sc->def->volt_reg, + AS3722_LDO3_MODE_MASK, + AS3722_LDO3_MODE_PMOS_TRACKING); + if (rv < 0) { + device_printf(sc->base_sc->dev, + "LDO3 tracking failed: %d\n", rv); + return (rv); + } + } + } + + if (sc->ext_control) { + + rv = as3722_reg_enable(sc); + if (rv < 0) { + device_printf(sc->base_sc->dev, + "Failed to enable %s regulator: %d\n", + sc->def->name, rv); + return (rv); + } + rv = as3722_reg_extreg_setup(sc, sc->ext_control); + if (rv < 0) { + device_printf(sc->base_sc->dev, + "%s ext control failed: %d", sc->def->name, rv); + return (rv); + } + } + return (0); +} + +static void +as3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def, +struct as3722_regnode_init_def *init_def) +{ + int rv; + phandle_t parent, supply_node; + char prop_name[64]; /* Maximum OFW property name length. */ + + rv = regulator_parse_ofw_stdparam(sc->dev, node, + &init_def->reg_init_def); + + rv = OF_getencprop(node, "ams,ext-control", &init_def->ext_control, + sizeof(init_def->ext_control)); + if (rv <= 0) + init_def->ext_control = 0; + if (init_def->ext_control > 3) { + device_printf(sc->dev, + "Invalid value for ams,ext-control property: %d\n", + init_def->ext_control); + init_def->ext_control = 0; + } + if (OF_hasprop(node, "ams,enable-tracking")) + init_def->enable_tracking = 1; + + + /* Get parent supply. */ + if (def->supply_name == NULL) + return; + + parent = OF_parent(node); + snprintf(prop_name, sizeof(prop_name), "%s-supply", + def->supply_name); + rv = OF_getencprop(parent, prop_name, &supply_node, + sizeof(supply_node)); + if (rv <= 0) + return; + supply_node = OF_node_from_xref(supply_node); + rv = OF_getprop_alloc(supply_node, "regulator-name", 1, + (void **)&init_def->reg_init_def.parent_name); + if (rv <= 0) + init_def->reg_init_def.parent_name = NULL; +} + +static struct as3722_reg_sc * +as3722_attach(struct as3722_softc *sc, phandle_t node, struct reg_def *def) +{ + struct as3722_reg_sc *reg_sc; + struct as3722_regnode_init_def init_def; + struct regnode *regnode; + + bzero(&init_def, sizeof(init_def)); + + as3722_fdt_parse(sc, node, def, &init_def); + init_def.reg_init_def.id = def->id; + init_def.reg_init_def.ofw_node = node; + regnode = regnode_create(sc->dev, &as3722_regnode_class, + &init_def.reg_init_def); + if (regnode == NULL) { + device_printf(sc->dev, "Cannot create regulator.\n"); + return (NULL); + } + reg_sc = regnode_get_softc(regnode); + + /* Init regulator softc. */ + reg_sc->regnode = regnode; + reg_sc->base_sc = sc; + reg_sc->def = def; + reg_sc->xref = OF_xref_from_node(node); + + reg_sc->param = regnode_get_stdparam(regnode); + reg_sc->ext_control = init_def.ext_control; + reg_sc->enable_tracking = init_def.enable_tracking; + + regnode_register(regnode); + if (bootverbose) { + int volt, rv; + regnode_topo_slock(); + rv = regnode_get_voltage(regnode, &volt); + if (rv == ENODEV) { + device_printf(sc->dev, + " Regulator %s: parent doesn't exist yet.\n", + regnode_get_name(regnode)); + } else if (rv != 0) { + device_printf(sc->dev, + " Regulator %s: voltage: INVALID!!!\n", + regnode_get_name(regnode)); + } else { + device_printf(sc->dev, + " Regulator %s: voltage: %d uV\n", + regnode_get_name(regnode), volt); + } + regnode_topo_unlock(); + } + + return (reg_sc); +} + +int +as3722_regulator_attach(struct as3722_softc *sc, phandle_t node) +{ + struct as3722_reg_sc *reg; + phandle_t child, rnode; + int i; + + rnode = ofw_bus_find_child(node, "regulators"); + if (rnode <= 0) { + device_printf(sc->dev, " Cannot find regulators subnode\n"); + return (ENXIO); + } + + sc->nregs = nitems(as3722s_def); + sc->regs = malloc(sizeof(struct as3722_reg_sc *) * sc->nregs, + M_AS3722_REG, M_WAITOK | M_ZERO); + + + /* Attach all known regulators if exist in DT. */ + for (i = 0; i < sc->nregs; i++) { + child = ofw_bus_find_child(rnode, as3722s_def[i].name); + if (child == 0) { + if (bootverbose) + device_printf(sc->dev, + "Regulator %s missing in DT\n", + as3722s_def[i].name); + continue; + } + reg = as3722_attach(sc, child, as3722s_def + i); + if (reg == NULL) { + device_printf(sc->dev, "Cannot attach regulator: %s\n", + as3722s_def[i].name); + return (ENXIO); + } + sc->regs[i] = reg; + } + return (0); +} + +int +as3722_regulator_map(device_t dev, phandle_t xref, int ncells, + pcell_t *cells, int *num) +{ + struct as3722_softc *sc; + int i; + + sc = device_get_softc(dev); + for (i = 0; i < sc->nregs; i++) { + if (sc->regs[i] == NULL) + continue; + if (sc->regs[i]->xref == xref) { + *num = sc->regs[i]->def->id; + return (0); + } + } + return (ENXIO); +} + +static int +as3722_regnode_enable(struct regnode *regnode, bool val, int *udelay) +{ + struct as3722_reg_sc *sc; + int rv; + + sc = regnode_get_softc(regnode); + + if (val) + rv = as3722_reg_enable(sc); + else + rv = as3722_reg_disable(sc); + *udelay = sc->enable_usec; + return (rv); +} + +static int +as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt, + int *udelay) +{ + struct as3722_reg_sc *sc; + uint8_t sel; + int rv; + + sc = regnode_get_softc(regnode); + + *udelay = 0; + rv = regulator_range_volt_to_sel(sc, min_uvolt, max_uvolt, &sel); + if (rv != 0) + return (rv); + rv = as3722_write_sel(sc, sel); + return (rv); + +} + +static int +as3722_regnode_get_volt(struct regnode *regnode, int *uvolt) +{ + struct as3722_reg_sc *sc; + uint8_t sel; + int rv; + + sc = regnode_get_softc(regnode); + rv = as3722_read_sel(sc, &sel); + if (rv != 0) + return (rv); + + /* LDO6 have bypass. */ + if (sc->def->id == AS3722_REG_ID_LDO6 && sel == AS3722_LDO6_SEL_BYPASS) + return (ENOENT); + rv = regulator_range_sel_to_volt(sc, sel, uvolt); + return (rv); +} diff --git a/sys/arm/nvidia/as3722_rtc.c b/sys/arm/nvidia/as3722_rtc.c new file mode 100644 index 0000000..8f13104 --- /dev/null +++ b/sys/arm/nvidia/as3722_rtc.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 "clock_if.h" +#include "as3722.h" + +#define AS3722_RTC_START_YEAR 2000 + +int +as3722_rtc_gettime(device_t dev, struct timespec *ts) +{ + struct as3722_softc *sc; + struct clocktime ct; + uint8_t buf[6]; + int rv; + + sc = device_get_softc(dev); + + rv = as3722_read_buf(sc, AS3722_RTC_SECOND, buf, 6); + if (rv != 0) { + device_printf(sc->dev, "Failed to read RTC data\n"); + return (rv); + } + ct.nsec = 0; + ct.sec = bcd2bin(buf[0] & 0x7F); + ct.min = bcd2bin(buf[1] & 0x7F); + ct.hour = bcd2bin(buf[2] & 0x3F); + ct.day = bcd2bin(buf[3] & 0x3F); + ct.mon = bcd2bin(buf[4] & 0x1F); + ct.year = bcd2bin(buf[5] & 0x7F) + AS3722_RTC_START_YEAR; + ct.dow = -1; + + return clock_ct_to_ts(&ct, ts); +} + +int +as3722_rtc_settime(device_t dev, struct timespec *ts) +{ + struct as3722_softc *sc; + struct clocktime ct; + uint8_t buf[6]; + int rv; + + sc = device_get_softc(dev); + clock_ts_to_ct(ts, &ct); + + if (ct.year < AS3722_RTC_START_YEAR) + return (EINVAL); + + buf[0] = bin2bcd(ct.sec); + buf[1] = bin2bcd(ct.min); + buf[2] = bin2bcd(ct.hour); + buf[3] = bin2bcd(ct.day); + buf[4] = bin2bcd(ct.mon); + buf[5] = bin2bcd(ct.year - AS3722_RTC_START_YEAR); + + rv = as3722_write_buf(sc, AS3722_RTC_SECOND, buf, 6); + if (rv != 0) { + device_printf(sc->dev, "Failed to write RTC data\n"); + return (rv); + } + return (0); +} + +int +as3722_rtc_attach(struct as3722_softc *sc, phandle_t node) +{ + int rv; + + /* Enable RTC, set 24 hours mode and alarms */ + rv = RM1(sc, AS3722_RTC_CONTROL, + AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN | AS3722_RTC_AM_PM_MODE, + AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN); + if (rv < 0) { + device_printf(sc->dev, "Failed to initialize RTC controller\n"); + return (ENXIO); + } + clock_register(sc->dev, 1000000); + + return (0); +} diff --git a/sys/arm/nvidia/tegra124/files.tegra124 b/sys/arm/nvidia/tegra124/files.tegra124 new file mode 100644 index 0000000..d4d38f7 --- /dev/null +++ b/sys/arm/nvidia/tegra124/files.tegra124 @@ -0,0 +1,57 @@ +# $FreeBSD$ + +# +# Standard ARM support. +# +kern/kern_clocksource.c standard +dev/ofw/ofw_cpu.c optional fdt + +# +# Standard tegra124 devices and support. +# +arm/nvidia/tegra124/tegra124_machdep.c standard +arm/nvidia/tegra124/tegra124_mp.c optional smp +arm/nvidia/tegra124/tegra124_car.c standard +arm/nvidia/tegra124/tegra124_clk_pll.c standard +arm/nvidia/tegra124/tegra124_clk_per.c standard +arm/nvidia/tegra124/tegra124_clk_super.c standard +arm/nvidia/tegra124/tegra124_xusbpadctl.c standard +arm/nvidia/tegra124/tegra124_pmc.c standard +arm/nvidia/tegra124/tegra124_cpufreq.c standard +arm/nvidia/tegra124/tegra124_coretemp.c standard +arm/nvidia/tegra_usbphy.c standard +arm/nvidia/tegra_pinmux.c standard +arm/nvidia/tegra_uart.c optional uart +arm/nvidia/tegra_sdhci.c optional sdhci +arm/nvidia/tegra_gpio.c optional gpio +arm/nvidia/tegra_ehci.c optional ehci +arm/nvidia/tegra_ahci.c optional ahci +arm/nvidia/tegra_pcie.c optional pci +arm/nvidia/tegra_i2c.c optional iic +arm/nvidia/tegra_rtc.c standard +arm/nvidia/tegra_abpmisc.c standard +arm/nvidia/tegra_efuse.c standard +arm/nvidia/tegra_soctherm_if.m standard +arm/nvidia/tegra_soctherm.c standard +arm/nvidia/tegra_lic.c standard +#arm/nvidia/tegra_hda.c optional snd_hda +#arm/nvidia/drm2/hdmi.c optional drm2 +#arm/nvidia/drm2/tegra_drm_if.m optional drm2 +#arm/nvidia/drm2/tegra_drm_subr.c optional drm2 +#arm/nvidia/drm2/tegra_host1x.c optional drm2 +#arm/nvidia/drm2/tegra_hdmi.c optional drm2 +#arm/nvidia/drm2/tegra_dc_if.m optional drm2 +#arm/nvidia/drm2/tegra_dc.c optional drm2 +#arm/nvidia/drm2/tegra_fb.c optional drm2 +#arm/nvidia/drm2/tegra_bo.c optional drm2 +# +# Optional devices. +# + +# +# Temporary/ to be moved stuff +# +arm/nvidia/as3722.c optional iic +arm/nvidia/as3722_regulators.c optional iic +arm/nvidia/as3722_rtc.c optional iic +arm/nvidia/as3722_gpio.c optional iic diff --git a/sys/arm/nvidia/tegra124/std.tegra124 b/sys/arm/nvidia/tegra124/std.tegra124 new file mode 100644 index 0000000..127b001 --- /dev/null +++ b/sys/arm/nvidia/tegra124/std.tegra124 @@ -0,0 +1,14 @@ +# $FreeBSD$ +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +options KERNVIRTADDR = 0xc0200000 +makeoptions KERNVIRTADDR = 0xc0200000 + +options ARM_INTRNG + +options IPI_IRQ_START=0 +options IPI_IRQ_END=15 + +files "../nvidia/tegra124/files.tegra124" diff --git a/sys/arm/nvidia/tegra124/tegra124_car.c b/sys/arm/nvidia/tegra124/tegra124_car.c new file mode 100644 index 0000000..c0e93c7 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_car.c @@ -0,0 +1,613 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 +#include + +#include + +#include "clkdev_if.h" +#include "hwreset_if.h" +#include "tegra124_car.h" + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-car", 1}, + {NULL, 0}, +}; + +#define PLIST(x) static const char *x[] + +/* Pure multiplexer. */ +#define MUX(_id, cname, plists, o, s, w) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = plists, \ + .clkdef.parent_cnt = nitems(plists), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .shift = s, \ + .width = w, \ +} + +/* Fractional divider (7.1). */ +#define DIV7_1(_id, cname, plist, o, s) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .i_shift = (s) + 1, \ + .i_width = 7, \ + .f_shift = s, \ + .f_width = 1, \ +} + +/* Integer divider. */ +#define DIV(_id, cname, plist, o, s, w, f) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .i_shift = s, \ + .i_width = w, \ + .div_flags = f, \ +} + +/* Gate in PLL block. */ +#define GATE_PLL(_id, cname, plist, o, s) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .shift = s, \ + .mask = 3, \ + .on_value = 3, \ + .off_value = 0, \ +} + +/* Standard gate. */ +#define GATE(_id, cname, plist, o, s) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .shift = s, \ + .mask = 1, \ + .on_value = 1, \ + .off_value = 0, \ +} + +/* Inverted gate. */ +#define GATE_INV(_id, cname, plist, o, s) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = o, \ + .shift = s, \ + .mask = 1, \ + .on_value = 0, \ + .off_value = 1, \ +} + +/* Fixed rate clock. */ +#define FRATE(_id, cname, _freq) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = NULL, \ + .clkdef.parent_cnt = 0, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .freq = _freq, \ +} + +/* Fixed rate multipier/divider. */ +#define FACT(_id, cname, pname, _mult, _div) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .mult = _mult, \ + .div = _div, \ +} + +static uint32_t osc_freqs[16] = { + [0] = 13000000, + [1] = 16800000, + [4] = 19200000, + [5] = 38400000, + [8] = 12000000, + [9] = 48000000, + [12] = 260000000, +}; + + +/* Parent lists. */ +PLIST(mux_pll_srcs) = {"osc_div_clk", NULL, "pllP_out0", NULL}; /* FIXME */ +PLIST(mux_plle_src1) = {"osc_div_clk", "pllP_out0"}; +PLIST(mux_plle_src) = {"pllE_src1", "pllREFE_out"}; +PLIST(mux_plld_out0_plld2_out0) = {"pllD_out0", "pllD2_out0"}; +PLIST(mux_pllmcp_clkm) = {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m", + "pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"}; +PLIST(mux_xusb_hs) = {"pc_xusb_ss", "pllU_60"}; +PLIST(mux_xusb_ss) = {"pc_xusb_ss", "osc_div_clk"}; + + +/* Clocks ajusted online. */ +static struct clk_fixed_def fixed_clk_m = + FRATE(0, "clk_m", 12000000); +static struct clk_fixed_def fixed_osc_div_clk = + FACT(0, "osc_div_clk", "clk_m", 1, 1); + +static struct clk_fixed_def tegra124_fixed_clks[] = { + /* Core clocks. */ + FRATE(0, "clk_s", 32768), + FACT(0, "clk_m_div2", "clk_m", 1, 2), + FACT(0, "clk_m_div4", "clk_m", 1, 3), + FACT(0, "pllU_60", "pllU_out", 1, 8), + FACT(0, "pllU_48", "pllU_out", 1, 10), + FACT(0, "pllU_12", "pllU_out", 1, 40), + FACT(TEGRA124_CLK_PLL_D_OUT0, "pllD_out0", "pllD_out", 1, 2), + FACT(TEGRA124_CLK_PLL_D2_OUT0, "pllD2_out0", "pllD2_out", 1, 1), + FACT(0, "pllX_out0", "pllX_out", 1, 2), + FACT(0, "pllC_UD", "pllC_out0", 1, 1), + FACT(0, "pllM_UD", "pllM_out0", 1, 1), + + /* Audio clocks. */ + FRATE(0, "audio0", 10000000), + FRATE(0, "audio1", 10000000), + FRATE(0, "audio2", 10000000), + FRATE(0, "audio3", 10000000), + FRATE(0, "audio4", 10000000), + FRATE(0, "ext_vimclk", 10000000), +}; + + +static struct clk_mux_def tegra124_mux_clks[] = { + /* Core clocks. */ + MUX(0, "pllD2_src", mux_pll_srcs, PLLD2_BASE, 25, 2), + MUX(0, "pllDP_src", mux_pll_srcs, PLLDP_BASE, 25, 2), + MUX(0, "pllC4_src", mux_pll_srcs, PLLC4_BASE, 25, 2), + MUX(0, "pllE_src1", mux_plle_src1, PLLE_AUX, 2, 1), + MUX(0, "pllE_src", mux_plle_src, PLLE_AUX, 28, 1), + + /* Base peripheral clocks. */ + MUX(0, "dsia_mux", mux_plld_out0_plld2_out0, PLLD_BASE, 25, 1), + MUX(0, "dsib_mux", mux_plld_out0_plld2_out0, PLLD2_BASE, 25, 1), + MUX(0, "emc_mux", mux_pllmcp_clkm, CLK_SOURCE_EMC, 29, 3), + + /* USB. */ + MUX(0, "xusb_hs", mux_xusb_hs, CLK_SOURCE_XUSB_SS, 25, 1), + MUX(0, "xusb_ss_mux", mux_xusb_ss, CLK_SOURCE_XUSB_SS, 24, 1), + +}; + + +static struct clk_gate_def tegra124_gate_clks[] = { + /* Core clocks. */ + GATE_PLL(0, "pllC_out1", "pllC_out1_div", PLLC_OUT, 0), + GATE_PLL(0, "pllM_out1", "pllM_out1_div", PLLM_OUT, 0), + GATE_PLL(0, "pllU_480", "pllU_out", PLLU_BASE, 22), + GATE_PLL(0, "pllP_outX0", "pllP_outX0_div", PLLP_RESHIFT, 0), + GATE_PLL(0, "pllP_out1", "pllP_out1_div", PLLP_OUTA, 0), + GATE_PLL(0, "pllP_out2", "pllP_out2_div", PLLP_OUTA, 16), + GATE_PLL(0, "pllP_out3", "pllP_out3_div", PLLP_OUTB, 0), + GATE_PLL(0, "pllP_out4", "pllP_out4_div", PLLP_OUTB, 16), + GATE_PLL(0, "pllP_out5", "pllP_out5_div", PLLP_OUTC, 16), + GATE_PLL(0, "pllA_out0", "pllA_out1_div", PLLA_OUT, 0), + + /* Base peripheral clocks. */ + GATE(TEGRA124_CLK_CML0, "cml0", "pllE_out0", PLLE_AUX, 0), + GATE(TEGRA124_CLK_CML1, "cml1", "pllE_out0", PLLE_AUX, 1), + GATE_INV(TEGRA124_CLK_HCLK, "hclk", "hclk_div", CLK_SYSTEM_RATE, 7), + GATE_INV(TEGRA124_CLK_PCLK, "pclk", "pclk_div", CLK_SYSTEM_RATE, 3), +}; + +static struct clk_div_def tegra124_div_clks[] = { + /* Core clocks. */ + DIV7_1(0, "pllC_out1_div", "pllC_out0", PLLC_OUT, 2), + DIV7_1(0, "pllM_out1_div", "pllM_out0", PLLM_OUT, 8), + DIV7_1(0, "pllP_outX0_div", "pllP_out0", PLLP_RESHIFT, 2), + DIV7_1(0, "pllP_out1_div", "pllP_out0", PLLP_OUTA, 8), + DIV7_1(0, "pllP_out2_div", "pllP_out0", PLLP_OUTA, 24), + DIV7_1(0, "pllP_out3_div", "pllP_out0", PLLP_OUTB, 8), + DIV7_1(0, "pllP_out4_div", "pllP_out0", PLLP_OUTB, 24), + DIV7_1(0, "pllP_out5_div", "pllP_out0", PLLP_OUTC, 24), + DIV7_1(0, "pllA_out1_div", "pllA_out", PLLA_OUT, 8), + + /* Base peripheral clocks. */ + DIV(0, "hclk_div", "sclk", CLK_SYSTEM_RATE, 4, 2, 0), + DIV(0, "pclk_div", "hclk", CLK_SYSTEM_RATE, 0, 2, 0), +}; + +/* Initial setup table. */ +static struct tegra124_init_item clk_init_table[] = { + /* clock, partent, frequency, enable */ + {"uarta", "pllP_out0", 408000000, 0}, + {"uartb", "pllP_out0", 408000000, 0}, + {"uartc", "pllP_out0", 408000000, 0}, + {"uartd", "pllP_out0", 408000000, 0}, + {"pllA_out", NULL, 282240000, 1}, + {"pllA_out0", NULL, 11289600, 1}, + {"extperiph1", "pllA_out0", 0, 1}, + {"i2s0", "pllA_out0", 11289600, 0}, + {"i2s1", "pllA_out0", 11289600, 0}, + {"i2s2", "pllA_out0", 11289600, 0}, + {"i2s3", "pllA_out0", 11289600, 0}, + {"i2s4", "pllA_out0", 11289600, 0}, + {"vde", "pllP_out0", 0, 0}, + {"host1x", "pllP_out0", 136000000, 1}, + {"sclk", "pllP_out2", 102000000, 1}, + {"dvfs_soc", "pllP_out0", 51000000, 1}, + {"dvfs_ref", "pllP_out0", 51000000, 1}, + {"pllC_out0", NULL, 600000000, 0}, + {"pllC_out1", NULL, 100000000, 0}, + {"spi4", "pllP_out0", 12000000, 1}, + {"tsec", "pllC3_out0", 0, 0}, + {"msenc", "pllC3_out0", 0, 0}, + {"pllREFE_out", NULL, 672000000, 0}, + {"pc_xusb_ss", "pllU_480", 120000000, 0}, + {"xusb_ss", "pc_xusb_ss", 120000000, 0}, + {"pc_xusb_fs", "pllU_48", 48000000, 0}, + {"xusb_hs", "pllU_60", 60000000, 0}, + {"pc_xusb_falcon", "pllREFE_out", 224000000, 0}, + {"xusb_core_host", "pllREFE_out", 112000000, 0}, + {"sata", "pllP_out0", 102000000, 0}, + {"sata_oob", "pllP_out0", 204000000, 0}, + {"sata_cold", NULL, 0, 1}, + {"emc", NULL, 0, 1}, + {"mselect", NULL, 0, 1}, + {"csite", NULL, 0, 1}, + {"tsensor", "clk_m", 400000, 0}, + + /* tegra124 only*/ + {"soc_therm", "pllP_out0", 51000000, 0}, + {"cclk_g", NULL, 0, 1}, + {"hda", "pllP_out0", 102000000, 0}, + {"hda2codec_2x", "pllP_out0", 48000000, 0}, +}; + +static void +init_divs(struct tegra124_car_softc *sc, struct clk_div_def *clks, int nclks) +{ + int i, rv; + + for (i = 0; i < nclks; i++) { + rv = clknode_div_register(sc->clkdom, clks + i); + if (rv != 0) + panic("clk_div_register failed"); + } +} + +static void +init_gates(struct tegra124_car_softc *sc, struct clk_gate_def *clks, int nclks) +{ + int i, rv; + + + for (i = 0; i < nclks; i++) { + rv = clknode_gate_register(sc->clkdom, clks + i); + if (rv != 0) + panic("clk_gate_register failed"); + } +} + +static void +init_muxes(struct tegra124_car_softc *sc, struct clk_mux_def *clks, int nclks) +{ + int i, rv; + + + for (i = 0; i < nclks; i++) { + rv = clknode_mux_register(sc->clkdom, clks + i); + if (rv != 0) + panic("clk_mux_register failed"); + } +} + +static void +init_fixeds(struct tegra124_car_softc *sc, struct clk_fixed_def *clks, + int nclks) +{ + int i, rv; + uint32_t val; + int osc_idx; + + CLKDEV_READ_4(sc->dev, OSC_CTRL, &val); + osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT; + fixed_clk_m.freq = osc_freqs[osc_idx]; + if (fixed_clk_m.freq == 0) + panic("Undefined input frequency"); + rv = clknode_fixed_register(sc->clkdom, &fixed_clk_m); + if (rv != 0) panic("clk_fixed_register failed"); + + val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; + fixed_osc_div_clk.div = 1 << val; + rv = clknode_fixed_register(sc->clkdom, &fixed_osc_div_clk); + if (rv != 0) panic("clk_fixed_register failed"); + + for (i = 0; i < nclks; i++) { + rv = clknode_fixed_register(sc->clkdom, clks + i); + if (rv != 0) + panic("clk_fixed_register failed"); + } +} + +static void +postinit_clock(struct tegra124_car_softc *sc) +{ + int i; + struct tegra124_init_item *tbl; + struct clknode *clknode; + int rv; + + for (i = 0; i < nitems(clk_init_table); i++) { + tbl = &clk_init_table[i]; + + clknode = clknode_find_by_name(tbl->name); + if (clknode == NULL) { + device_printf(sc->dev, "Cannot find clock %s\n", + tbl->name); + continue; + } + if (tbl->parent != NULL) { + rv = clknode_set_parent_by_name(clknode, tbl->parent); + if (rv != 0) { + device_printf(sc->dev, + "Cannot set parent for %s (to %s): %d\n", + tbl->name, tbl->parent, rv); + continue; + } + } + if (tbl->frequency != 0) { + rv = clknode_set_freq(clknode, tbl->frequency, 0 , 9999); + if (rv != 0) { + device_printf(sc->dev, + "Cannot set frequency for %s: %d\n", + tbl->name, rv); + continue; + } + } + if (tbl->enable!= 0) { + rv = clknode_enable(clknode); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable %s: %d\n", tbl->name, rv); + continue; + } + } + } +} + +static void +register_clocks(device_t dev) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + sc->clkdom = clkdom_create(dev); + if (sc->clkdom == NULL) + panic("clkdom == NULL"); + + tegra124_init_plls(sc); + init_fixeds(sc, tegra124_fixed_clks, nitems(tegra124_fixed_clks)); + init_muxes(sc, tegra124_mux_clks, nitems(tegra124_mux_clks)); + init_divs(sc, tegra124_div_clks, nitems(tegra124_div_clks)); + init_gates(sc, tegra124_gate_clks, nitems(tegra124_gate_clks)); + tegra124_periph_clock(sc); + tegra124_super_mux_clock(sc); + clkdom_finit(sc->clkdom); + clkdom_xlock(sc->clkdom); + postinit_clock(sc); + clkdom_unlock(sc->clkdom); + if (bootverbose) + clkdom_dump(sc->clkdom); +} + +static int +tegra124_car_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + *val = bus_read_4(sc->mem_res, addr); + return (0); +} + +static int +tegra124_car_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + bus_write_4(sc->mem_res, addr, val); + return (0); +} + +static int +tegra124_car_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask, + uint32_t set_mask) +{ + struct tegra124_car_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + reg = bus_read_4(sc->mem_res, addr); + reg &= ~clear_mask; + reg |= set_mask; + bus_write_4(sc->mem_res, addr, reg); + return (0); +} + +static void +tegra124_car_clkdev_device_lock(device_t dev) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); +} + +static void +tegra124_car_clkdev_device_unlock(device_t dev) +{ + struct tegra124_car_softc *sc; + + sc = device_get_softc(dev); + mtx_unlock(&sc->mtx); +} + +static int +tegra124_car_detach(device_t dev) +{ + + device_printf(dev, "Error: Clock driver cannot be detached\n"); + return (EBUSY); +} + +static int +tegra124_car_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Tegra Clock Driver"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +tegra124_car_attach(device_t dev) +{ + struct tegra124_car_softc *sc = device_get_softc(dev); + int rid, rv; + + sc->dev = dev; + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + /* Resource setup. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->mem_res) { + device_printf(dev, "cannot allocate memory resource\n"); + rv = ENXIO; + goto fail; + } + + register_clocks(dev); + hwreset_register_ofw_provider(dev); + return (0); + +fail: + if (sc->mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (rv); +} + +static int +tegra124_car_hwreset_assert(device_t dev, intptr_t id, bool value) +{ + struct tegra124_car_softc *sc = device_get_softc(dev); + + return (tegra124_hwreset_by_idx(sc, id, value)); +} + +static device_method_t tegra124_car_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra124_car_probe), + DEVMETHOD(device_attach, tegra124_car_attach), + DEVMETHOD(device_detach, tegra124_car_detach), + + /* Clkdev interface*/ + DEVMETHOD(clkdev_read_4, tegra124_car_clkdev_read_4), + DEVMETHOD(clkdev_write_4, tegra124_car_clkdev_write_4), + DEVMETHOD(clkdev_modify_4, tegra124_car_clkdev_modify_4), + DEVMETHOD(clkdev_device_lock, tegra124_car_clkdev_device_lock), + DEVMETHOD(clkdev_device_unlock, tegra124_car_clkdev_device_unlock), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, tegra124_car_hwreset_assert), + + DEVMETHOD_END +}; + +static devclass_t tegra124_car_devclass; + +static driver_t tegra124_car_driver = { + "tegra124_car", + tegra124_car_methods, + sizeof(struct tegra124_car_softc), +}; + +EARLY_DRIVER_MODULE(tegra124_car, simplebus, tegra124_car_driver, + tegra124_car_devclass, 0, 0, BUS_PASS_TIMER); diff --git a/sys/arm/nvidia/tegra124/tegra124_car.h b/sys/arm/nvidia/tegra124/tegra124_car.h new file mode 100644 index 0000000..b11742c --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_car.h @@ -0,0 +1,337 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 _TEGRA124_CAR_ +#define _TEGRA124_CAR_ + +#include "clkdev_if.h" + +#define RD4(sc, reg, val) CLKDEV_READ_4((sc)->clkdev, reg, val) +#define WR4(sc, reg, val) CLKDEV_WRITE_4((sc)->clkdev, reg, val) +#define MD4(sc, reg, mask, set) CLKDEV_MODIFY_4((sc)->clkdev, reg, mask, set) +#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) +#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) + +#define RST_DEVICES_L 0x004 +#define RST_DEVICES_H 0x008 +#define RST_DEVICES_U 0x00C +#define CLK_OUT_ENB_L 0x010 +#define CLK_OUT_ENB_H 0x014 +#define CLK_OUT_ENB_U 0x018 +#define CCLK_BURST_POLICY 0x020 +#define SUPER_CCLK_DIVIDER 0x024 +#define SCLK_BURST_POLICY 0x028 +#define SUPER_SCLK_DIVIDER 0x02c +#define CLK_SYSTEM_RATE 0x030 + +#define OSC_CTRL 0x050 + #define OSC_CTRL_OSC_FREQ_SHIFT 28 + #define OSC_CTRL_PLL_REF_DIV_SHIFT 26 + +#define PLLE_SS_CNTL 0x068 +#define PLLE_SS_CNTL_SSCINCINTRV_MASK (0x3f << 24) +#define PLLE_SS_CNTL_SSCINCINTRV_VAL (0x20 << 24) +#define PLLE_SS_CNTL_SSCINC_MASK (0xff << 16) +#define PLLE_SS_CNTL_SSCINC_VAL (0x1 << 16) +#define PLLE_SS_CNTL_SSCINVERT (1 << 15) +#define PLLE_SS_CNTL_SSCCENTER (1 << 14) +#define PLLE_SS_CNTL_SSCBYP (1 << 12) +#define PLLE_SS_CNTL_INTERP_RESET (1 << 11) +#define PLLE_SS_CNTL_BYPASS_SS (1 << 10) +#define PLLE_SS_CNTL_SSCMAX_MASK 0x1ff +#define PLLE_SS_CNTL_SSCMAX_VAL 0x25 +#define PLLE_SS_CNTL_DISABLE (PLLE_SS_CNTL_BYPASS_SS | \ + PLLE_SS_CNTL_INTERP_RESET | \ + PLLE_SS_CNTL_SSCBYP) +#define PLLE_SS_CNTL_COEFFICIENTS_MASK (PLLE_SS_CNTL_SSCMAX_MASK | \ + PLLE_SS_CNTL_SSCINC_MASK | \ + PLLE_SS_CNTL_SSCINCINTRV_MASK) +#define PLLE_SS_CNTL_COEFFICIENTS_VAL (PLLE_SS_CNTL_SSCMAX_VAL | \ + PLLE_SS_CNTL_SSCINC_VAL | \ + PLLE_SS_CNTL_SSCINCINTRV_VAL) + +#define PLLC_BASE 0x080 +#define PLLC_OUT 0x084 +#define PLLC_MISC2 0x088 +#define PLLC_MISC 0x08c +#define PLLM_BASE 0x090 +#define PLLM_OUT 0x094 +#define PLLM_MISC 0x09c +#define PLLP_BASE 0x0a0 +#define PLLP_MISC 0x0ac +#define PLLP_OUTA 0x0a4 +#define PLLP_OUTB 0x0a8 +#define PLLA_BASE 0x0b0 +#define PLLA_OUT 0x0b4 +#define PLLA_MISC 0x0bc +#define PLLU_BASE 0x0c0 +#define PLLU_MISC 0x0cc +#define PLLD_BASE 0x0d0 +#define PLLD_MISC 0x0dc +#define PLLX_BASE 0x0e0 +#define PLLX_MISC 0x0e4 +#define PLLE_BASE 0x0e8 +#define PLLE_BASE_LOCK_OVERRIDE (1 << 29) +#define PLLE_BASE_DIVCML_SHIFT 24 +#define PLLE_BASE_DIVCML_MASK 0xf + +#define PLLE_MISC 0x0ec +#define PLLE_MISC_SETUP_BASE_SHIFT 16 +#define PLLE_MISC_SETUP_BASE_MASK (0xffff << PLLE_MISC_SETUP_BASE_SHIFT) +#define PLLE_MISC_READY (1 << 15) +#define PLLE_MISC_IDDQ_SWCTL (1 << 14) +#define PLLE_MISC_IDDQ_OVERRIDE_VALUE (1 << 13) +#define PLLE_MISC_LOCK (1 << 11) +#define PLLE_MISC_REF_ENABLE (1 << 10) +#define PLLE_MISC_LOCK_ENABLE (1 << 9) +#define PLLE_MISC_PTS (1 << 8) +#define PLLE_MISC_VREG_BG_CTRL_SHIFT 4 +#define PLLE_MISC_VREG_BG_CTRL_MASK (3 << PLLE_MISC_VREG_BG_CTRL_SHIFT) +#define PLLE_MISC_VREG_CTRL_SHIFT 2 +#define PLLE_MISC_VREG_CTRL_MASK (2 << PLLE_MISC_VREG_CTRL_SHIFT) + +#define CLK_SOURCE_I2S1 0x100 +#define CLK_SOURCE_I2S2 0x104 +#define CLK_SOURCE_SPDIF_OUT 0x108 +#define CLK_SOURCE_SPDIF_IN 0x10c +#define CLK_SOURCE_PWM 0x110 +#define CLK_SOURCE_SPI2 0x118 +#define CLK_SOURCE_SPI3 0x11c +#define CLK_SOURCE_I2C1 0x124 +#define CLK_SOURCE_I2C5 0x128 +#define CLK_SOURCE_SPI1 0x134 +#define CLK_SOURCE_DISP1 0x138 +#define CLK_SOURCE_DISP2 0x13c +#define CLK_SOURCE_ISP 0x144 +#define CLK_SOURCE_VI 0x148 +#define CLK_SOURCE_SDMMC1 0x150 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC4 0x164 +#define CLK_SOURCE_VFIR 0x168 +#define CLK_SOURCE_HSI 0x174 +#define CLK_SOURCE_UARTA 0x178 +#define CLK_SOURCE_UARTB 0x17c +#define CLK_SOURCE_HOST1X 0x180 +#define CLK_SOURCE_HDMI 0x18c +#define CLK_SOURCE_I2C2 0x198 +#define CLK_SOURCE_EMC 0x19c +#define CLK_SOURCE_UARTC 0x1a0 +#define CLK_SOURCE_VI_SENSOR 0x1a8 +#define CLK_SOURCE_SPI4 0x1b4 +#define CLK_SOURCE_I2C3 0x1b8 +#define CLK_SOURCE_SDMMC3 0x1bc +#define CLK_SOURCE_UARTD 0x1c0 +#define CLK_SOURCE_VDE 0x1c8 +#define CLK_SOURCE_OWR 0x1cc +#define CLK_SOURCE_NOR 0x1d0 +#define CLK_SOURCE_CSITE 0x1d4 +#define CLK_SOURCE_I2S0 0x1d8 +#define CLK_SOURCE_DTV 0x1dc +#define CLK_SOURCE_MSENC 0x1f0 +#define CLK_SOURCE_TSEC 0x1f4 +#define CLK_SOURCE_SPARE2 0x1f8 + +#define CLK_OUT_ENB_X 0x280 +#define RST_DEVICES_X 0x28C + +#define RST_DEVICES_V 0x358 +#define RST_DEVICES_W 0x35C +#define CLK_OUT_ENB_V 0x360 +#define CLK_OUT_ENB_W 0x364 +#define CCLKG_BURST_POLICY 0x368 +#define SUPER_CCLKG_DIVIDER 0x36C +#define CCLKLP_BURST_POLICY 0x370 +#define SUPER_CCLKLP_DIVIDER 0x374 + +#define CLK_SOURCE_MSELECT 0x3b4 +#define CLK_SOURCE_TSENSOR 0x3b8 +#define CLK_SOURCE_I2S3 0x3bc +#define CLK_SOURCE_I2S4 0x3c0 +#define CLK_SOURCE_I2C4 0x3c4 +#define CLK_SOURCE_SPI5 0x3c8 +#define CLK_SOURCE_SPI6 0x3cc +#define CLK_SOURCE_AUDIO 0x3d0 +#define CLK_SOURCE_DAM0 0x3d8 +#define CLK_SOURCE_DAM1 0x3dc +#define CLK_SOURCE_DAM2 0x3e0 +#define CLK_SOURCE_HDA2CODEC_2X 0x3e4 +#define CLK_SOURCE_ACTMON 0x3e8 +#define CLK_SOURCE_EXTPERIPH1 0x3ec +#define CLK_SOURCE_EXTPERIPH2 0x3f0 +#define CLK_SOURCE_EXTPERIPH3 0x3f4 +#define CLK_SOURCE_I2C_SLOW 0x3fc + +#define CLK_SOURCE_SYS 0x400 +#define CLK_SOURCE_SOR0 0x414 +#define CLK_SOURCE_SATA_OOB 0x420 +#define CLK_SOURCE_SATA 0x424 +#define CLK_SOURCE_HDA 0x428 +#define UTMIP_PLL_CFG0 0x480 +#define UTMIP_PLL_CFG1 0x484 +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP (1 << 17) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN (1 << 16) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP (1 << 15) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN (1 << 14) +#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN (1 << 12) +#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6) +#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) + +#define UTMIP_PLL_CFG2 0x488 +#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18) +#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN (1 << 4) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN (1 << 2) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN (1 << 0) + +#define PLLE_AUX 0x48c +#define PLLE_AUX_PLLRE_SEL (1 << 28) +#define PLLE_AUX_SEQ_START_STATE (1 << 25) +#define PLLE_AUX_SEQ_ENABLE (1 << 24) +#define PLLE_AUX_SS_SWCTL (1 << 6) +#define PLLE_AUX_ENABLE_SWCTL (1 << 4) +#define PLLE_AUX_USE_LOCKDET (1 << 3) +#define PLLE_AUX_PLLP_SEL (1 << 2) + +#define SATA_PLL_CFG0 0x490 +#define SATA_PLL_CFG0_SEQ_START_STATE (1 << 25) +#define SATA_PLL_CFG0_SEQ_ENABLE (1 << 24) +#define SATA_PLL_CFG0_SEQ_PADPLL_PD_INPUT_VALUE (1 << 7) +#define SATA_PLL_CFG0_SEQ_LANE_PD_INPUT_VALUE (1 << 6) +#define SATA_PLL_CFG0_SEQ_RESET_INPUT_VALUE (1 << 5) +#define SATA_PLL_CFG0_SEQ_IN_SWCTL (1 << 4) +#define SATA_PLL_CFG0_PADPLL_USE_LOCKDET (1 << 2) +#define SATA_PLL_CFG0_PADPLL_RESET_OVERRIDE_VALUE (1 << 1) +#define SATA_PLL_CFG0_PADPLL_RESET_SWCTL (1 << 0) + +#define SATA_PLL_CFG1 0x494 +#define PCIE_PLL_CFG0 0x498 +#define PCIE_PLL_CFG0_SEQ_START_STATE (1 << 25) +#define PCIE_PLL_CFG0_SEQ_ENABLE (1 << 24) + +#define PLLD2_BASE 0x4b8 +#define PLLD2_MISC 0x4bc +#define UTMIP_PLL_CFG3 0x4c0 +#define PLLRE_BASE 0x4c4 +#define PLLRE_MISC 0x4c8 +#define PLLC2_BASE 0x4e8 +#define PLLC2_MISC 0x4ec +#define PLLC3_BASE 0x4fc + +#define PLLC3_MISC 0x500 +#define PLLX_MISC2 0x514 +#define PLLX_MISC2 0x514 +#define PLLX_MISC3 0x518 +#define PLLX_MISC3_DYNRAMP_STEPB_MASK 0xFF +#define PLLX_MISC3_DYNRAMP_STEPB_SHIFT 24 +#define PLLX_MISC3_DYNRAMP_STEPA_MASK 0xFF +#define PLLX_MISC3_DYNRAMP_STEPA_SHIFT 16 +#define PLLX_MISC3_NDIV_NEW_MASK 0xFF +#define PLLX_MISC3_NDIV_NEW_SHIFT 8 +#define PLLX_MISC3_EN_FSTLCK (1 << 5) +#define PLLX_MISC3_LOCK_OVERRIDE (1 << 4) +#define PLLX_MISC3_PLL_FREQLOCK (1 << 3) +#define PLLX_MISC3_DYNRAMP_DONE (1 << 2) +#define PLLX_MISC3_CLAMP_NDIV (1 << 1) +#define PLLX_MISC3_EN_DYNRAMP (1 << 0) +#define XUSBIO_PLL_CFG0 0x51c +#define XUSBIO_PLL_CFG0_SEQ_START_STATE (1 << 25) +#define XUSBIO_PLL_CFG0_SEQ_ENABLE (1 << 24) +#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET (1 << 6) +#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL (1 << 2) +#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL (1 << 0) + +#define PLLP_RESHIFT 0x528 +#define UTMIPLL_HW_PWRDN_CFG0 0x52c +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE (1 << 25) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE (1 << 24) +#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET (1 << 6) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE (1 << 5) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL (1 << 4) +#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL (1 << 2) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE (1 << 1) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL (1 << 0) + +#define PLLDP_BASE 0x590 +#define PLLDP_MISC 0x594 +#define PLLC4_BASE 0x5a4 +#define PLLC4_MISC 0x5a8 + +#define CLK_SOURCE_XUSB_CORE_HOST 0x600 +#define CLK_SOURCE_XUSB_FALCON 0x604 +#define CLK_SOURCE_XUSB_FS 0x608 +#define CLK_SOURCE_XUSB_CORE_DEV 0x60c +#define CLK_SOURCE_XUSB_SS 0x610 +#define CLK_SOURCE_CILAB 0x614 +#define CLK_SOURCE_CILCD 0x618 +#define CLK_SOURCE_CILE 0x61c +#define CLK_SOURCE_DSIA_LP 0x620 +#define CLK_SOURCE_DSIB_LP 0x624 +#define CLK_SOURCE_ENTROPY 0x628 +#define CLK_SOURCE_DVFS_REF 0x62c +#define CLK_SOURCE_DVFS_SOC 0x630 +#define CLK_SOURCE_TRACECLKIN 0x634 +#define CLK_SOURCE_ADX 0x638 +#define CLK_SOURCE_AMX 0x63c +#define CLK_SOURCE_EMC_LATENCY 0x640 +#define CLK_SOURCE_SOC_THERM 0x644 +#define CLK_SOURCE_VI_SENSOR2 0x658 +#define CLK_SOURCE_I2C6 0x65c +#define CLK_SOURCE_EMC_DLL 0x664 +#define CLK_SOURCE_HDMI_AUDIO 0x668 +#define CLK_SOURCE_CLK72MHZ 0x66c +#define CLK_SOURCE_ADX1 0x670 +#define CLK_SOURCE_AMX1 0x674 +#define CLK_SOURCE_VIC 0x678 +#define PLLP_OUTC 0x67c +#define PLLP_MISC1 0x680 + + +struct tegra124_car_softc { + device_t dev; + struct resource * mem_res; + struct mtx mtx; + struct clkdom *clkdom; + int type; +}; + +struct tegra124_init_item { + char *name; + char *parent; + uint64_t frequency; + int enable; +}; + +void tegra124_init_plls(struct tegra124_car_softc *sc); + +void tegra124_periph_clock(struct tegra124_car_softc *sc); +void tegra124_super_mux_clock(struct tegra124_car_softc *sc); + +int tegra124_hwreset_by_idx(struct tegra124_car_softc *sc, intptr_t idx, + bool reset); + +#endif /*_TEGRA124_CAR_*/ \ No newline at end of file diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_per.c b/sys/arm/nvidia/tegra124/tegra124_clk_per.c new file mode 100644 index 0000000..65ae5d9 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_clk_per.c @@ -0,0 +1,810 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 "tegra124_car.h" + +/* Bits in base register. */ +#define PERLCK_AMUX_MASK 0x0F +#define PERLCK_AMUX_SHIFT 16 +#define PERLCK_AMUX_DIS (1 << 20) +#define PERLCK_UDIV_DIS (1 << 24) +#define PERLCK_ENA_MASK (1 << 28) +#define PERLCK_MUX_SHIFT 29 +#define PERLCK_MUX_MASK 0x07 + + +struct periph_def { + struct clknode_init_def clkdef; + uint32_t base_reg; + uint32_t div_width; + uint32_t div_mask; + uint32_t div_f_width; + uint32_t div_f_mask; + uint32_t flags; +}; + +struct pgate_def { + struct clknode_init_def clkdef; + uint32_t idx; + uint32_t flags; +}; +#define PLIST(x) static const char *x[] + +#define GATE(_id, cname, plist, _idx) \ +{ \ + .clkdef.id = TEGRA124_CLK_##_id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){plist}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .idx = _idx, \ + .flags = 0, \ +} + +/* Sources for multiplexors. */ +PLIST(mux_a_N_audio_N_p_N_clkm) = + {"pllA_out0", NULL, "audio", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio0_N_p_N_clkm) = + {"pllA_out0", NULL, "audio0", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio1_N_p_N_clkm) = + {"pllA_out0", NULL, "audio1", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio2_N_p_N_clkm) = + {"pllA_out0", NULL, "audio2", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio3_N_p_N_clkm) = + {"pllA_out0", NULL, "audio3", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_N_audio4_N_p_N_clkm) = + {"pllA_out0", NULL, "audio4", NULL, + "pllP_out0", NULL, "clk_m"}; +PLIST(mux_a_clks_p_clkm_e) = + {"pllA_out0", "clk_s", "pllP_out0", + "clk_m", "pllE_out0"}; +PLIST(mux_a_c2_c_c3_p_N_clkm) = + {"pllA_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllP_out0", NULL, "clk_m"}; + +PLIST(mux_m_c_p_a_c2_c3) = + {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0", + "pllC2_out0", "pllC3_out0"}; +PLIST(mux_m_c_p_a_c2_c3_clkm) = + {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0", + "pllC2_out0", "pllC3_out0", "clk_m"}; +PLIST(mux_m_c_p_a_c2_c3_clkm_c4) = + {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0", + "pllC2_out0", "pllC3_out0", "clk_m", "pllC4_out0"}; +PLIST(mux_m_c_p_clkm_mud_c2_c3) = + {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m", + "pllM_UD", "pllC2_out0", "pllC3_out0"}; +PLIST(mux_m_c2_c_c3_p_N_a) = + {"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllP_out0", NULL, "pllA_out0"}; +PLIST(mux_m_c2_c_c3_p_N_a_c4) = + {"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + NULL, "pllA_out0", "pllC4_out0"}; + +PLIST(mux_p_N_c_N_N_N_clkm) = + {"pllP_out0", NULL, "pllC_out0", NULL, + NULL, NULL, "clk_m"}; +PLIST(mux_p_N_c_N_m_N_clkm) = + {"pllP_out0", NULL, "pllC_out0", NULL, + "pllM_out0", NULL, "clk_m"}; +PLIST(mux_p_c_c2_clkm) = + {"pllP_out0", "pllC_out0", "pllC2_out0", "clk_m"}; +PLIST(mux_p_c2_c_c3_m) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0"}; +PLIST(mux_p_c2_c_c3_m_N_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0", NULL, "clk_m"}; +PLIST(mux_p_c2_c_c3_m_e_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0", "pllE_out0", "clk_m"}; +PLIST(mux_p_c2_c_c3_m_a_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0", "pllA_out0", "clk_m"}; +PLIST(mux_p_c2_c_c3_m_clks_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllM_out0", "clk_s", "clk_m"}; +PLIST(mux_p_c2_c_c3_clks_N_clkm) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "clk_s", NULL, "clk_m"}; +PLIST(mux_p_c2_c_c3_clkm_N_clks) = + {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "clk_m", NULL, "clk_s"}; +PLIST(mux_p_clkm_clks_E) = + {"pllP_out0", "clk_m", "clk_s", "pllE_out0"}; +PLIST(mux_p_m_d_a_c_d2_clkm) = + {"pllP_out0", "pllM_out0", "pllD_out0", "pllA_out0", + "pllC_out0", "pllD2_out0", "clk_m"}; + +PLIST(mux_clkm_N_u48_N_p_N_u480) = + {"clk_m", NULL, "pllU_48", NULL, + "pllP_out0", NULL, "pllU_480"}; +PLIST(mux_clkm_p_c2_c_c3_refre) = + {"clk_m", "pllP_out0", "pllC2_out0", "pllC_out0", + "pllC3_out0", "pllREFE_out"}; +PLIST(mux_clkm_refe_clks_u480_c_c2_c3_oscdiv) = + {"clk_m", "pllREFE_out", "clk_s", "pllU_480", + "pllC_out0", "pllC2_out0", "pllC3_out0", "osc_div_clk"}; + +PLIST(mux_sep_audio) = + {"pllA_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", + "pllP_out0", NULL, "clk_m", NULL, + "spdif_in", "i2s0", "i2s1", "i2s2", + "i2s4", "pllA_out0", "ext_vimclk"}; + +static uint32_t clk_enabale_reg[] = { + CLK_OUT_ENB_L, + CLK_OUT_ENB_H, + CLK_OUT_ENB_U, + CLK_OUT_ENB_V, + CLK_OUT_ENB_W, + CLK_OUT_ENB_X, +}; + +static uint32_t clk_reset_reg[] = { + RST_DEVICES_L, + RST_DEVICES_H, + RST_DEVICES_U, + RST_DEVICES_V, + RST_DEVICES_W, + RST_DEVICES_X, +}; + +#define L(n) ((0 * 32) + (n)) +#define H(n) ((1 * 32) + (n)) +#define U(n) ((2 * 32) + (n)) +#define V(n) ((3 * 32) + (n)) +#define W(n) ((4 * 32) + (n)) +#define X(n) ((5 * 32) + (n)) + +static struct pgate_def pgate_def[] = { + /* bank L -> 0-31 */ + /* GATE(CPU, "cpu", "clk_m", L(0)), */ + GATE(ISPB, "ispb", "clk_m", L(3)), + GATE(RTC, "rtc", "clk_s", L(4)), + GATE(TIMER, "timer", "clk_m", L(5)), + GATE(UARTA, "uarta", "pc_uarta" , L(6)), + GATE(UARTB, "uartb", "pc_uartb", L(7)), + GATE(VFIR, "vfir", "pc_vfir", L(7)), + /* GATE(GPIO, "gpio", "clk_m", L(8)), */ + GATE(SDMMC2, "sdmmc2", "pc_sdmmc2", L(9)), + GATE(SPDIF_OUT, "spdif_out", "pc_spdif_out", L(10)), + GATE(SPDIF_IN, "spdif_in", "pc_spdif_in", L(10)), + GATE(I2S1, "i2s1", "pc_i2s1", L(11)), + GATE(I2C1, "i2c1", "pc_i2c1", L(12)), + GATE(SDMMC1, "sdmmc1", "pc_sdmmc1", L(14)), + GATE(SDMMC4, "sdmmc4", "pc_sdmmc4", L(15)), + GATE(PWM, "pwm", "pc_pwm", L(17)), + GATE(I2S2, "i2s2", "pc_i2s2", L(18)), + GATE(VI, "vi", "pc_vi", L(20)), + GATE(USBD, "usbd", "clk_m", L(22)), + GATE(ISP, "isp", "pc_isp", L(23)), + GATE(DISP2, "disp2", "pc_disp2", L(26)), + GATE(DISP1, "disp1", "pc_disp1", L(27)), + GATE(HOST1X, "host1x", "pc_host1x", L(28)), + GATE(VCP, "vcp", "clk_m", L(29)), + GATE(I2S0, "i2s0", "pc_i2s0", L(30)), + /* GATE(CACHE2, "ccache2", "clk_m", L(31)), */ + + /* bank H -> 32-63 */ + GATE(MC, "mem", "clk_m", H(0)), + /* GATE(AHBDMA, "ahbdma", "clk_m", H(1)), */ + GATE(APBDMA, "apbdma", "clk_m", H(2)), + GATE(KBC, "kbc", "clk_s", H(4)), + /* GATE(STAT_MON, "stat_mon", "clk_s", H(5)), */ + /* GATE(PMC, "pmc", "clk_s", H(6)), */ + GATE(FUSE, "fuse", "clk_m", H(7)), + GATE(KFUSE, "kfuse", "clk_m", H(8)), + GATE(SBC1, "spi1", "pc_spi1", H(9)), + GATE(NOR, "snor", "pc_snor", H(10)), + /* GATE(JTAG2TBC, "jtag2tbc", "clk_m", H(11)), */ + GATE(SBC2, "spi2", "pc_spi2", H(12)), + GATE(SBC3, "spi3", "pc_spi3", H(14)), + GATE(I2C5, "i2c5", "pc_i2c5", H(15)), + GATE(DSIA, "dsia", "dsia_mux", H(16)), + GATE(MIPI, "hsi", "pc_hsi", H(18)), + GATE(HDMI, "hdmi", "pc_hdmi", H(19)), + GATE(CSI, "csi", "pllP_out3", H(20)), + GATE(I2C2, "i2c2", "pc_i2c2", H(22)), + GATE(UARTC, "uartc", "pc_uartc", H(23)), + GATE(MIPI_CAL, "mipi_cal", "clk_m", H(24)), + GATE(EMC, "emc", "emc_mux", H(25)), + GATE(USB2, "usb2", "clk_m", H(26)), + GATE(USB3, "usb3", "clk_m", H(27)), + GATE(VDE, "vde", "pc_vde", H(29)), + GATE(BSEA, "bsea", "clk_m", H(30)), + GATE(BSEV, "bsev", "clk_m", H(31)), + + /* bank U -> 64-95 */ + GATE(UARTD, "uartd", "pc_uartd", U(1)), + GATE(I2C3, "i2c3", "pc_i2c3", U(3)), + GATE(SBC4, "spi4", "pc_spi4", U(4)), + GATE(SDMMC3, "sdmmc3", "pc_sdmmc3", U(5)), + GATE(PCIE, "pcie", "clk_m", U(6)), + GATE(OWR, "owr", "pc_owr", U(7)), + GATE(AFI, "afi", "clk_m", U(8)), + GATE(CSITE, "csite", "pc_csite", U(9)), + /* GATE(AVPUCQ, "avpucq", clk_m, U(11)), */ + GATE(TRACE, "traceclkin", "pc_traceclkin", U(13)), + GATE(SOC_THERM, "soc_therm", "pc_soc_therm", U(14)), + GATE(DTV, "dtv", "clk_m", U(15)), + GATE(I2CSLOW, "i2c_slow", "pc_i2c_slow", U(17)), + GATE(DSIB, "dsib", "dsib_mux", U(18)), + GATE(TSEC, "tsec", "pc_tsec", U(19)), + /* GATE(IRAMA, "irama", "clk_m", U(20)), */ + /* GATE(IRAMB, "iramb", "clk_m", U(21)), */ + /* GATE(IRAMC, "iramc", "clk_m", U(22)), */ + /* GATE(IRAMD, "iramd", "clk_m", U(23)), */ + /* GATE(CRAM2, "cram2", "clk_m", U(24)), */ + GATE(XUSB_HOST, "xusb_core_host", "pc_xusb_core_host", U(25)), + /* GATE(M_DOUBLER, "m_doubler", "clk_m", U(26)), */ + GATE(MSENC, "msenc", "pc_msenc", U(27)), + GATE(CSUS, "sus_out", "clk_m", U(28)), + /* GATE(DEVD2_OUT, "devd2_out", "clk_m", U(29)), */ + /* GATE(DEVD1_OUT, "devd1_out", "clk_m", U(30)), */ + GATE(XUSB_DEV_SRC, "xusb_core_dev", "pc_xusb_core_dev", U(31)), + + /* bank V -> 96-127 */ + /* GATE(CPUG, "cpug", "clk_m", V(0)), */ + /* GATE(CPULP, "cpuLP", "clk_m", V(1)), */ + GATE(MSELECT, "mselect", "pc_mselect", V(3)), + GATE(TSENSOR, "tsensor", "pc_tsensor", V(4)), + GATE(I2S3, "i2s3", "pc_i2s3", V(5)), + GATE(I2S4, "i2s4", "pc_i2s4", V(6)), + GATE(I2C4, "i2c4", "pc_i2c4", V(7)), + GATE(SBC5, "spi5", "pc_spi5", V(8)), + GATE(SBC6, "spi6", "pc_spi6", V(9)), + GATE(D_AUDIO, "audio", "pc_audio", V(10)), + GATE(APBIF, "apbif", "clk_m", V(11)), + GATE(DAM0, "dam0", "pc_dam0", V(12)), + GATE(DAM1, "dam1", "pc_dam1", V(13)), + GATE(DAM2, "dam2", "pc_dam2", V(14)), + GATE(HDA2CODEC_2X, "hda2codec_2x", "pc_hda2codec_2x", V(15)), + /* GATE(ATOMICS, "atomics", "clk_m", V(16)), */ + /* GATE(SPDIF_DOUBLER, "spdif_doubler", "clk_m", V(22)), */ + GATE(ACTMON, "actmon", "pc_actmon", V(23)), + GATE(EXTERN1, "extperiph1", "pc_extperiph1", V(24)), + GATE(EXTERN2, "extperiph2", "pc_extperiph2", V(25)), + GATE(EXTERN3, "extperiph3", "pc_extperiph3", V(26)), + GATE(SATA_OOB, "sata_oob", "pc_sata_oob", V(27)), + GATE(SATA, "sata", "pc_sata", V(28)), + GATE(HDA, "hda", "pc_hda", V(29)), + + /* bank W -> 128-159*/ + GATE(HDA2HDMI, "hda2hdmi", "clk_m", W(0)), + GATE(SATA_COLD, "sata_cold", "clk_m", W(1)), /* Reset only */ + /* GATE(PCIERX0, "pcierx0", "clk_m", W(2)), */ + /* GATE(PCIERX1, "pcierx1", "clk_m", W(3)), */ + /* GATE(PCIERX2, "pcierx2", "clk_m", W(4)), */ + /* GATE(PCIERX3, "pcierx3", "clk_m", W(5)), */ + /* GATE(PCIERX4, "pcierx4", "clk_m", W(6)), */ + /* GATE(PCIERX5, "pcierx5", "clk_m", W(7)), */ + /* GATE(CEC, "cec", "clk_m", W(8)), */ + /* GATE(PCIE2_IOBIST, "pcie2_iobist", "clk_m", W(9)), */ + /* GATE(EMC_IOBIST, "emc_iobist", "clk_m", W(10)), */ + /* GATE(HDMI_IOBIST, "hdmi_iobist", "clk_m", W(11)), */ + /* GATE(SATA_IOBIST, "sata_iobist", "clk_m", W(12)), */ + /* GATE(MIPI_IOBIST, "mipi_iobist", "clk_m", W(13)), */ + /* GATE(XUSB_IOBIST, "xusb_iobist", "clk_m", W(15)), */ + GATE(CILAB, "cilab", "pc_cilab", W(16)), + GATE(CILCD, "cilcd", "pc_cilcd", W(17)), + GATE(CILE, "cile", "pc_cile", W(18)), + GATE(DSIALP, "dsia_lp", "pc_dsia_lp", W(19)), + GATE(DSIBLP, "dsib_lp", "pc_dsib_lp", W(20)), + GATE(ENTROPY, "entropy", "pc_entropy", W(21)), + GATE(AMX, "amx", "pc_amx", W(25)), + GATE(ADX, "adx", "pc_adx", W(26)), + GATE(DFLL_REF, "dvfs_ref", "pc_dvfs_ref", X(27)), + GATE(DFLL_SOC, "dvfs_soc", "pc_dvfs_soc", X(27)), + GATE(XUSB_SS_SRC, "xusb_ss", "xusb_ss_mux", X(28)), + /* GATE(EMC_LATENCY, "emc_latency", "pc_emc_latency", X(29)), */ + + /* bank X -> 160-191*/ + /* GATE(SPARE, "spare", "clk_m", X(0)), */ + /* GATE(CAM_MCLK, "CAM_MCLK", "clk_m", X(4)), */ + /* GATE(CAM_MCLK2, "CAM_MCLK2", "clk_m", X(5)), */ + GATE(I2C6, "i2c6", "pc_i2c6", X(6)), + /* GATE(VIM2_CLK, "vim2_clk", clk_m, X(11)), */ + /* GATE(EMC_DLL, "emc_dll", "pc_emc_dll", X(14)), */ + GATE(HDMI_AUDIO, "hdmi_audio", "pc_hdmi_audio", X(16)), + GATE(CLK72MHZ, "clk72mhz", "pc_clk72mhz", X(17)), + GATE(VIC03, "vic", "pc_vic", X(18)), + GATE(ADX1, "adx1", "pc_adx1", X(20)), + GATE(DPAUX, "dpaux", "clk_m", X(21)), + GATE(SOR0_LVDS, "sor0", "pc_sor0", X(22)), + GATE(GPU, "gpu", "osc_div_clk", X(24)), + GATE(AMX1, "amx1", "pc_amx1", X(26)), +}; + +/* Peripheral clock clock */ +#define DCF_HAVE_MUX 0x0100 /* Block with multipexor */ +#define DCF_HAVE_ENA 0x0200 /* Block with enable bit */ +#define DCF_HAVE_DIV 0x0400 /* Block with divider */ + +/* Mark block with additional bis / functionality. */ +#define DCF_IS_MASK 0x00FF +#define DCF_IS_UART 0x0001 +#define DCF_IS_VI 0x0002 +#define DCF_IS_HOST1X 0x0003 +#define DCF_IS_XUSB_SS 0x0004 +#define DCF_IS_EMC_DLL 0x0005 +#define FDS_IS_SATA 0x0006 +#define DCF_IS_VIC 0x0007 +#define DCF_IS_AUDIO 0x0008 +#define DCF_IS_SOR0 0x0009 + +/* Basic pheripheral clock */ +#define PER_CLK(_id, cn, pl, r, diw, fiw, f) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cn, \ + .clkdef.parent_names = pl, \ + .clkdef.parent_cnt = nitems(pl), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .base_reg = r, \ + .div_width = diw, \ + .div_f_width = fiw, \ + .flags = f, \ +} + +/* Mux with fractional 8.1 divider. */ +#define CLK_8_1(cn, pl, r, f) \ + PER_CLK(0, cn, pl, r, 8, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) +/* Mux with fractional 16.1 divider. */ +#define CLK16_1(cn, pl, r, f) \ + PER_CLK(0, cn, pl, r, 16, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) +/* Mux with integer 16bits divider. */ +#define CLK16_0(cn, pl, r, f) \ + PER_CLK(0, cn, pl, r, 16, 0, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) +/* Mux wihout divider. */ +#define CLK_0_0(cn, pl, r, f) \ + PER_CLK(0, cn, pl, r, 0, 0, (f) | DCF_HAVE_MUX) + +static struct periph_def periph_def[] = { + CLK_8_1("pc_i2s1", mux_a_N_audio1_N_p_N_clkm, CLK_SOURCE_I2S1, DCF_HAVE_ENA), + CLK_8_1("pc_i2s2", mux_a_N_audio2_N_p_N_clkm, CLK_SOURCE_I2S2, DCF_HAVE_ENA), + CLK_8_1("pc_spdif_out", mux_a_N_audio_N_p_N_clkm, CLK_SOURCE_SPDIF_OUT, 0), + CLK_8_1("pc_spdif_in", mux_p_c2_c_c3_m, CLK_SOURCE_SPDIF_IN, 0), + CLK_8_1("pc_pwm", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_PWM, 0), + CLK_8_1("pc_spi2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI2, 0), + CLK_8_1("pc_spi3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI3, 0), + CLK16_0("pc_i2c5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C5, 0), + CLK16_0("pc_i2c1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C1, 0), + CLK_8_1("pc_spi1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI1, 0), + CLK_0_0("pc_disp1", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP1, 0), + CLK_0_0("pc_disp2", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP2, 0), + CLK_8_1("pc_isp", mux_m_c_p_a_c2_c3_clkm_c4, CLK_SOURCE_ISP, 0), + CLK_8_1("pc_vi", mux_m_c2_c_c3_p_N_a_c4, CLK_SOURCE_VI, DCF_IS_VI), + CLK_8_1("pc_sdmmc1", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC1, 0), + CLK_8_1("pc_sdmmc2", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC2, 0), + CLK_8_1("pc_sdmmc4", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC4, 0), + CLK_8_1("pc_vfir", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VFIR, 0), + CLK_8_1("pc_hsi", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HSI, 0), + CLK16_1("pc_uarta", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTA, DCF_IS_UART), + CLK16_1("pc_uartb", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTB, DCF_IS_UART), + CLK_8_1("pc_host1x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HOST1X, DCF_IS_HOST1X), + CLK_8_1("pc_hdmi", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_HDMI, 0), + CLK16_0("pc_i2c2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C2, 0), +/* EMC 8 */ + CLK16_1("pc_uartc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTC, DCF_IS_UART), + CLK_8_1("pc_vi_sensor", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR, 0), + CLK_8_1("pc_spi4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI4, 0), + CLK16_0("pc_i2c3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C3, 0), + CLK_8_1("pc_sdmmc3", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC3, 0), + CLK16_1("pc_uartd", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTD, DCF_IS_UART), + CLK_8_1("pc_vde", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VDE, 0), + CLK_8_1("pc_owr", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_OWR, 0), + CLK_8_1("pc_snor", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_NOR, 0), + CLK_8_1("pc_csite", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_CSITE, 0), + CLK_8_1("pc_i2s0", mux_a_N_audio0_N_p_N_clkm, CLK_SOURCE_I2S0, 0), +/* DTV xxx */ + CLK_8_1("pc_msenc", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_MSENC, 0), + CLK_8_1("pc_tsec", mux_p_c2_c_c3_m_a_clkm, CLK_SOURCE_TSEC, 0), +/* SPARE2 */ + + + CLK_8_1("pc_mselect", mux_p_c2_c_c3_m_clks_clkm, CLK_SOURCE_MSELECT, 0), + CLK_8_1("pc_tsensor", mux_p_c2_c_c3_clkm_N_clks, CLK_SOURCE_TSENSOR, 0), + CLK_8_1("pc_i2s3", mux_a_N_audio3_N_p_N_clkm, CLK_SOURCE_I2S3, DCF_HAVE_ENA), + CLK_8_1("pc_i2s4", mux_a_N_audio4_N_p_N_clkm, CLK_SOURCE_I2S4, DCF_HAVE_ENA), + CLK16_0("pc_i2c4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C4, 0), + CLK_8_1("pc_spi5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI5, 0), + CLK_8_1("pc_spi6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI6, 0), + CLK_8_1("pc_audio", mux_sep_audio, CLK_SOURCE_AUDIO, DCF_IS_AUDIO), + CLK_8_1("pc_dam0", mux_sep_audio, CLK_SOURCE_DAM0, DCF_IS_AUDIO), + CLK_8_1("pc_dam1", mux_sep_audio, CLK_SOURCE_DAM1, DCF_IS_AUDIO), + CLK_8_1("pc_dam2", mux_sep_audio, CLK_SOURCE_DAM2, DCF_IS_AUDIO), + CLK_8_1("pc_hda2codec_2x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA2CODEC_2X, 0), + CLK_8_1("pc_actmon", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_ACTMON, 0), + CLK_8_1("pc_extperiph1", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH1, 0), + CLK_8_1("pc_extperiph2", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH2, 0), + CLK_8_1("pc_extperiph3", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH3, 0), + CLK_8_1("pc_i2c_slow", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_I2C_SLOW, 0), +/* SYS */ + CLK_8_1("pc_sor0", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_SOR0, DCF_IS_SOR0), + CLK_8_1("pc_sata_oob", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA_OOB, 0), + CLK_8_1("pc_sata", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA, FDS_IS_SATA), + CLK_8_1("pc_hda", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA, 0), + + + CLK_8_1("pc_xusb_core_host", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_HOST, 0), + CLK_8_1("pc_xusb_falcon", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_FALCON, 0), + CLK_8_1("pc_xusb_fs", mux_clkm_N_u48_N_p_N_u480, CLK_SOURCE_XUSB_FS, 0), + CLK_8_1("pc_xusb_core_dev", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_DEV, 0), + CLK_8_1("pc_xusb_ss", mux_clkm_refe_clks_u480_c_c2_c3_oscdiv, CLK_SOURCE_XUSB_SS, DCF_IS_XUSB_SS), + CLK_8_1("pc_cilab", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILAB, 0), + CLK_8_1("pc_cilcd", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILCD, 0), + CLK_8_1("pc_cile", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILE, 0), + CLK_8_1("pc_dsia_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIA_LP, 0), + CLK_8_1("pc_dsib_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIB_LP, 0), + CLK_8_1("pc_entropy", mux_p_clkm_clks_E, CLK_SOURCE_ENTROPY, 0), + CLK_8_1("pc_dvfs_ref", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_REF, DCF_HAVE_ENA), + CLK_8_1("pc_dvfs_soc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_SOC, DCF_HAVE_ENA), + CLK_8_1("pc_traceclkin", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_TRACECLKIN, 0), + CLK_8_1("pc_adx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX, DCF_HAVE_ENA), + CLK_8_1("pc_amx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX, DCF_HAVE_ENA), + CLK_8_1("pc_emc_latency", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_LATENCY, 0), + CLK_8_1("pc_soc_therm", mux_m_c_p_a_c2_c3, CLK_SOURCE_SOC_THERM, 0), + CLK_8_1("pc_vi_sensor2", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR2, 0), + CLK16_0("pc_i2c6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C6, 0), + CLK_8_1("pc_emc_dll", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_DLL, DCF_IS_EMC_DLL), + CLK_8_1("pc_hdmi_audio", mux_p_c_c2_clkm, CLK_SOURCE_HDMI_AUDIO, 0), + CLK_8_1("pc_clk72mhz", mux_p_c_c2_clkm, CLK_SOURCE_CLK72MHZ, 0), + CLK_8_1("pc_adx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX1, DCF_HAVE_ENA), + CLK_8_1("pc_amx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX1, DCF_HAVE_ENA), + CLK_8_1("pc_vic", mux_m_c_p_a_c2_c3_clkm, CLK_SOURCE_VIC, DCF_IS_VIC), +}; + +static int periph_init(struct clknode *clk, device_t dev); +static int periph_recalc(struct clknode *clk, uint64_t *freq); +static int periph_set_freq(struct clknode *clk, uint64_t fin, + uint64_t *fout, int flags, int *stop); +static int periph_set_mux(struct clknode *clk, int idx); + +struct periph_sc { + device_t clkdev; + uint32_t base_reg; + uint32_t div_shift; + uint32_t div_width; + uint32_t div_mask; + uint32_t div_f_width; + uint32_t div_f_mask; + uint32_t flags; + + uint32_t divider; + int mux; +}; + +static clknode_method_t periph_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, periph_init), + CLKNODEMETHOD(clknode_recalc_freq, periph_recalc), + CLKNODEMETHOD(clknode_set_freq, periph_set_freq), + CLKNODEMETHOD(clknode_set_mux, periph_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(tegra124_periph, tegra124_periph_class, periph_methods, + sizeof(struct periph_sc), clknode_class); +static int +periph_init(struct clknode *clk, device_t dev) +{ + struct periph_sc *sc; + uint32_t reg; + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + if (sc->flags & DCF_HAVE_ENA) + MD4(sc, sc->base_reg, PERLCK_ENA_MASK, PERLCK_ENA_MASK); + + RD4(sc, sc->base_reg, ®); + DEVICE_UNLOCK(sc); + + /* Stnadard mux. */ + if (sc->flags & DCF_HAVE_MUX) + sc->mux = (reg >> PERLCK_MUX_SHIFT) & PERLCK_MUX_MASK; + else + sc->mux = 0; + if (sc->flags & DCF_HAVE_DIV) + sc->divider = (reg & sc->div_mask) + 2; + else + sc->divider = 1; + if ((sc->flags & DCF_IS_MASK) == DCF_IS_UART) { + if (!(reg & PERLCK_UDIV_DIS)) + sc->divider = 2; + } + + /* AUDIO MUX */ + if ((sc->flags & DCF_IS_MASK) == DCF_IS_AUDIO) { + if (!(reg & PERLCK_AMUX_DIS) && (sc->mux == 7)) { + sc->mux = 8 + + ((reg >> PERLCK_AMUX_SHIFT) & PERLCK_MUX_MASK); + } + } + clknode_init_parent_idx(clk, sc->mux); + return(0); +} + +static int +periph_set_mux(struct clknode *clk, int idx) +{ + struct periph_sc *sc; + uint32_t reg; + + + sc = clknode_get_softc(clk); + if (!(sc->flags & DCF_HAVE_MUX)) + return (ENXIO); + + sc->mux = idx; + DEVICE_LOCK(sc); + RD4(sc, sc->base_reg, ®); + reg &= ~(PERLCK_MUX_MASK << PERLCK_MUX_SHIFT); + if ((sc->flags & DCF_IS_MASK) == DCF_IS_AUDIO) { + reg &= ~PERLCK_AMUX_DIS; + reg &= ~(PERLCK_MUX_MASK << PERLCK_AMUX_SHIFT); + + if (idx <= 7) { + reg |= idx << PERLCK_MUX_SHIFT; + } else { + reg |= 7 << PERLCK_MUX_SHIFT; + reg |= (idx - 8) << PERLCK_AMUX_SHIFT; + } + } else { + reg |= idx << PERLCK_MUX_SHIFT; + } + WR4(sc, sc->base_reg, reg); + DEVICE_UNLOCK(sc); + + return(0); +} + +static int +periph_recalc(struct clknode *clk, uint64_t *freq) +{ + struct periph_sc *sc; + uint32_t reg; + + sc = clknode_get_softc(clk); + + if (sc->flags & DCF_HAVE_DIV) { + DEVICE_LOCK(sc); + RD4(sc, sc->base_reg, ®); + DEVICE_UNLOCK(sc); + *freq = (*freq << sc->div_f_width) / sc->divider; + } + return (0); +} + +static int +periph_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct periph_sc *sc; + uint64_t tmp, divider; + + sc = clknode_get_softc(clk); + if (!(sc->flags & DCF_HAVE_DIV)) { + *stop = 0; + return (0); + } + + tmp = fin << sc->div_f_width; + divider = tmp / *fout; + if ((tmp % *fout) != 0) + divider++; + + if (divider < (1 << sc->div_f_width)) + divider = 1 << sc->div_f_width; + + if ((*stop != 0) && + ((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && + (*fout != (tmp / divider))) + return (ERANGE); + + if ((flags & CLK_SET_DRYRUN) == 0) { + DEVICE_LOCK(sc); + MD4(sc, sc->base_reg, sc->div_mask, + (divider - (1 << sc->div_f_width))); + DEVICE_UNLOCK(sc); + sc->divider = divider; + } + *fout = tmp / divider; + *stop = 1; + return (0); +} + +static int +periph_register(struct clkdom *clkdom, struct periph_def *clkdef) +{ + struct clknode *clk; + struct periph_sc *sc; + + clk = clknode_create(clkdom, &tegra124_periph_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->clkdev = clknode_get_device(clk); + sc->base_reg = clkdef->base_reg; + sc->div_width = clkdef->div_width; + sc->div_mask = (1 <div_width) - 1; + sc->div_f_width = clkdef->div_f_width; + sc->div_f_mask = (1 <div_f_width) - 1; + sc->flags = clkdef->flags; + + clknode_register(clkdom, clk); + return (0); +} + +/* -------------------------------------------------------------------------- */ +static int pgate_init(struct clknode *clk, device_t dev); +static int pgate_set_gate(struct clknode *clk, bool enable); + +struct pgate_sc { + device_t clkdev; + uint32_t idx; + uint32_t flags; + uint32_t enabled; + +}; + +static clknode_method_t pgate_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, pgate_init), + CLKNODEMETHOD(clknode_set_gate, pgate_set_gate), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(tegra124_pgate, tegra124_pgate_class, pgate_methods, + sizeof(struct pgate_sc), clknode_class); + +static uint32_t +get_enable_reg(int idx) +{ + KASSERT(idx / 32 < nitems(clk_enabale_reg), + ("Invalid clock index for enable: %d", idx)); + return (clk_enabale_reg[idx / 32]); +} + +static uint32_t +get_reset_reg(int idx) +{ + KASSERT(idx / 32 < nitems(clk_reset_reg), + ("Invalid clock index for reset: %d", idx)); + return (clk_reset_reg[idx / 32]); +} + +static int +pgate_init(struct clknode *clk, device_t dev) +{ + struct pgate_sc *sc; + uint32_t ena_reg, rst_reg, mask; + + sc = clknode_get_softc(clk); + mask = 1 << (sc->idx % 32); + + DEVICE_LOCK(sc); + RD4(sc, get_enable_reg(sc->idx), &ena_reg); + RD4(sc, get_reset_reg(sc->idx), &rst_reg); + DEVICE_UNLOCK(sc); + + sc->enabled = ena_reg & mask ? 1 : 0; + clknode_init_parent_idx(clk, 0); + + return(0); +} + +static int +pgate_set_gate(struct clknode *clk, bool enable) +{ + struct pgate_sc *sc; + uint32_t reg, mask, base_reg; + + sc = clknode_get_softc(clk); + mask = 1 << (sc->idx % 32); + sc->enabled = enable; + base_reg = get_enable_reg(sc->idx); + + DEVICE_LOCK(sc); + MD4(sc, base_reg, mask, enable ? mask : 0); + RD4(sc, base_reg, ®); + DEVICE_UNLOCK(sc); + + DELAY(2); + return(0); +} + +int +tegra124_hwreset_by_idx(struct tegra124_car_softc *sc, intptr_t idx, bool reset) +{ + uint32_t reg, mask, reset_reg; + + mask = 1 << (idx % 32); + reset_reg = get_reset_reg(idx); + + CLKDEV_DEVICE_LOCK(sc->dev); + CLKDEV_MODIFY_4(sc->dev, reset_reg, mask, reset ? mask : 0); + CLKDEV_READ_4(sc->dev, reset_reg, ®); + CLKDEV_DEVICE_UNLOCK(sc->dev); + + return(0); +} + +static int +pgate_register(struct clkdom *clkdom, struct pgate_def *clkdef) +{ + struct clknode *clk; + struct pgate_sc *sc; + + clk = clknode_create(clkdom, &tegra124_pgate_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->clkdev = clknode_get_device(clk); + sc->idx = clkdef->idx; + sc->flags = clkdef->flags; + + clknode_register(clkdom, clk); + return (0); +} + +void +tegra124_periph_clock(struct tegra124_car_softc *sc) +{ + int i, rv; + + for (i = 0; i < nitems(periph_def); i++) { + rv = periph_register(sc->clkdom, &periph_def[i]); + if (rv != 0) + panic("tegra124_periph_register failed"); + } + for (i = 0; i < nitems(pgate_def); i++) { + rv = pgate_register(sc->clkdom, &pgate_def[i]); + if (rv != 0) + panic("tegra124_pgate_register failed"); + } + +} diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_pll.c b/sys/arm/nvidia/tegra124/tegra124_clk_pll.c new file mode 100644 index 0000000..6adfd32 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_clk_pll.c @@ -0,0 +1,1066 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 "tegra124_car.h" + +/* #define TEGRA_PLL_DEBUG */ +#ifdef TEGRA_PLL_DEBUG +#define dprintf(...) printf(__VA_ARGS__) +#else +#define dprintf(...) +#endif + +/* All PLLs. */ +enum pll_type { + PLL_M, + PLL_X, + PLL_C, + PLL_C2, + PLL_C3, + PLL_C4, + PLL_P, + PLL_A, + PLL_U, + PLL_D, + PLL_D2, + PLL_DP, + PLL_E, + PLL_REFE}; + +/* Common base register bits. */ +#define PLL_BASE_BYPASS (1U << 31) +#define PLL_BASE_ENABLE (1 << 30) +#define PLL_BASE_REFDISABLE (1 << 29) +#define PLL_BASE_LOCK (1 << 27) +#define PLL_BASE_DIVM_SHIFT 0 +#define PLL_BASE_DIVN_SHIFT 8 + +#define PLLRE_MISC_LOCK (1 << 24) + +#define PLL_MISC_LOCK_ENABLE (1 << 18) +#define PLLC_MISC_LOCK_ENABLE (1 << 24) +#define PLLDU_MISC_LOCK_ENABLE (1 << 22) +#define PLLRE_MISC_LOCK_ENABLE (1 << 30) +#define PLLSS_MISC_LOCK_ENABLE (1 << 30) + +#define PLLC_IDDQ_BIT 26 +#define PLLX_IDDQ_BIT 3 +#define PLLRE_IDDQ_BIT 16 +#define PLLSS_IDDQ_BIT 19 + +#define PLL_LOCK_TIMEOUT 1000 + +/* Post divider <-> register value mapping. */ +struct pdiv_table { + uint32_t divider; /* real divider */ + uint32_t value; /* register value */ +}; + +/* Bits definition of M, N and P fields. */ +struct mnp_bits { + uint32_t m_width; + uint32_t n_width; + uint32_t p_width; + uint32_t p_shift; +}; + +struct clk_pll_def { + struct clknode_init_def clkdef; + enum pll_type type; + uint32_t base_reg; + uint32_t misc_reg; + uint32_t lock_mask; + uint32_t lock_enable; + uint32_t iddq_reg; + uint32_t iddq_mask; + uint32_t flags; + struct pdiv_table *pdiv_table; + struct mnp_bits mnp_bits; +}; + +#define PLL(_id, cname, pname) \ + .clkdef.id = _id, \ + .clkdef.name = cname, \ + .clkdef.parent_names = (const char *[]){pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS + +/* Tegra K1 PLLs + PLLM: Clock source for EMC 2x clock + PLLX: Clock source for the fast CPU cluster and the shadow CPU + PLLC: Clock source for general use + PLLC2: Clock source for engine scaling + PLLC3: Clock source for engine scaling + PLLC4: Clock source for ISP/VI units + PLLP: Clock source for most peripherals + PLLA: Audio clock sources: (11.2896 MHz, 12.288 MHz, 24.576 MHz) + PLLU: Clock source for USB PHY, provides 12/60/480 MHz + PLLD: Clock sources for the DSI and display subsystem + PLLD2: Clock sources for the DSI and display subsystem + refPLLe: + PLLE: generate the 100 MHz reference clock for USB 3.0 (spread spectrum) + PLLDP: Clock source for eDP/LVDS (spread spectrum) + + DFLLCPU: DFLL clock source for the fast CPU cluster + GPCPLL: Clock source for the GPU +*/ + +static struct pdiv_table pllm_map[] = { + {1, 0}, + {2, 1}, + {0, 0} +}; + +static struct pdiv_table pllxc_map[] = { + { 1, 0}, + { 2, 1}, + { 3, 2}, + { 4, 3}, + { 5, 4}, + { 6, 5}, + { 8, 6}, + {10, 7}, + {12, 8}, + {16, 9}, + {12, 10}, + {16, 11}, + {20, 12}, + {24, 13}, + {32, 14}, + { 0, 0} +}; + +static struct pdiv_table pllc_map[] = { + { 1, 0}, + { 2, 1}, + { 3, 2}, + { 4, 3}, + { 6, 4}, + { 8, 5}, + {12, 6}, + {16, 7}, + { 0, 0} +}; + +static struct pdiv_table pll12g_ssd_esd_map[] = { + { 1, 0}, + { 2, 1}, + { 3, 2}, + { 4, 3}, + { 5, 4}, + { 6, 5}, + { 8, 6}, + {10, 7}, + {12, 8}, + {16, 9}, + {12, 10}, + {16, 11}, + {20, 12}, + {24, 13}, + {32, 14}, + { 0, 0} +}; + +static struct pdiv_table pllu_map[] = { + {1, 1}, + {2, 0}, + {0, 0} +}; + +static struct clk_pll_def pll_clks[] = { +/* PLLM: 880 MHz Clock source for EMC 2x clock */ + { + PLL(TEGRA124_CLK_PLL_M, "pllM_out0", "osc_div_clk"), + .type = PLL_M, + .base_reg = PLLM_BASE, + .misc_reg = PLLM_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .pdiv_table = pllm_map, + .mnp_bits = {8, 8, 1, 20}, + }, +/* PLLX: 1GHz Clock source for the fast CPU cluster and the shadow CPU */ + { + PLL(TEGRA124_CLK_PLL_X, "pllX_out", "osc_div_clk"), + .type = PLL_X, + .base_reg = PLLX_BASE, + .misc_reg = PLLX_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .iddq_reg = PLLX_MISC3, + .iddq_mask = 1 << PLLX_IDDQ_BIT, + .pdiv_table = pllxc_map, + .mnp_bits = {8, 8, 4, 20}, + }, +/* PLLC: 600 MHz Clock source for general use */ + { + PLL(TEGRA124_CLK_PLL_C, "pllC_out0", "osc_div_clk"), + .type = PLL_C, + .base_reg = PLLC_BASE, + .misc_reg = PLLC_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLC_MISC_LOCK_ENABLE, + .iddq_reg = PLLC_MISC, + .iddq_mask = 1 << PLLC_IDDQ_BIT, + .pdiv_table = pllc_map, + .mnp_bits = {8, 8, 4, 20}, + }, +/* PLLC2: 600 MHz Clock source for engine scaling */ + { + PLL(TEGRA124_CLK_PLL_C2, "pllC2_out0", "osc_div_clk"), + .type = PLL_C2, + .base_reg = PLLC2_BASE, + .misc_reg = PLLC2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .pdiv_table = pllc_map, + .mnp_bits = {2, 8, 3, 20}, + }, +/* PLLC3: 600 MHz Clock source for engine scaling */ + { + PLL(TEGRA124_CLK_PLL_C3, "pllC3_out0", "osc_div_clk"), + .type = PLL_C3, + .base_reg = PLLC3_BASE, + .misc_reg = PLLC3_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .pdiv_table = pllc_map, + .mnp_bits = {2, 8, 3, 20}, + }, +/* PLLC4: 600 MHz Clock source for ISP/VI units */ + { + PLL(TEGRA124_CLK_PLL_C4, "pllC4_out0", "pllC4_src"), + .type = PLL_C4, + .base_reg = PLLC4_BASE, + .misc_reg = PLLC4_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLSS_MISC_LOCK_ENABLE, + .iddq_reg = PLLC4_BASE, + .iddq_mask = 1 << PLLSS_IDDQ_BIT, + .pdiv_table = pll12g_ssd_esd_map, + .mnp_bits = {8, 8, 4, 20}, + }, +/* PLLP: 408 MHz Clock source for most peripherals */ + { + PLL(TEGRA124_CLK_PLL_P, "pllP_out0", "osc_div_clk"), + .type = PLL_P, + .base_reg = PLLP_BASE, + .misc_reg = PLLP_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .mnp_bits = {5, 10, 3, 20}, + }, +/* PLLA: Audio clock sources: (11.2896 MHz, 12.288 MHz, 24.576 MHz) */ + { + PLL(TEGRA124_CLK_PLL_A, "pllA_out", "pllP_out1"), + .type = PLL_A, + .base_reg = PLLA_BASE, + .misc_reg = PLLA_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .mnp_bits = {5, 10, 3, 20}, + }, +/* PLLU: 480 MHz Clock source for USB PHY, provides 12/60/480 MHz */ + { + PLL(TEGRA124_CLK_PLL_U, "pllU_out", "osc_div_clk"), + .type = PLL_U, + .base_reg = PLLU_BASE, + .misc_reg = PLLU_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLDU_MISC_LOCK_ENABLE, + .pdiv_table = pllu_map, + .mnp_bits = {5, 10, 1, 20}, + }, +/* PLLD: 600 MHz Clock sources for the DSI and display subsystem */ + { + PLL(TEGRA124_CLK_PLL_D, "pllD_out", "osc_div_clk"), + .type = PLL_D, + .base_reg = PLLD_BASE, + .misc_reg = PLLD_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLL_MISC_LOCK_ENABLE, + .mnp_bits = {5, 11, 3, 20}, + }, +/* PLLD2: 600 MHz Clock sources for the DSI and display subsystem */ + { + PLL(TEGRA124_CLK_PLL_D2, "pllD2_out", "pllD2_src"), + .type = PLL_D2, + .base_reg = PLLD2_BASE, + .misc_reg = PLLD2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLSS_MISC_LOCK_ENABLE, + .iddq_reg = PLLD2_BASE, + .iddq_mask = 1 << PLLSS_IDDQ_BIT, + .pdiv_table = pll12g_ssd_esd_map, + .mnp_bits = {8, 8, 4, 20}, + }, +/* refPLLe: */ + { + PLL(0, "pllREFE_out", "osc_div_clk"), + .type = PLL_REFE, + .base_reg = PLLRE_BASE, + .misc_reg = PLLRE_MISC, + .lock_mask = PLLRE_MISC_LOCK, + .lock_enable = PLLRE_MISC_LOCK_ENABLE, + .iddq_reg = PLLRE_MISC, + .iddq_mask = 1 << PLLRE_IDDQ_BIT, + .mnp_bits = {8, 8, 4, 16}, + }, +/* PLLE: generate the 100 MHz reference clock for USB 3.0 (spread spectrum) */ + { + PLL(TEGRA124_CLK_PLL_E, "pllE_out0", "pllE_src"), + .type = PLL_E, + .base_reg = PLLE_BASE, + .misc_reg = PLLE_MISC, + .lock_mask = PLLE_MISC_LOCK, + .lock_enable = PLLE_MISC_LOCK_ENABLE, + .mnp_bits = {8, 8, 4, 24}, + }, +/* PLLDP: 600 MHz Clock source for eDP/LVDS (spread spectrum) */ + { + PLL(0, "pllDP_out0", "pllDP_src"), + .type = PLL_DP, + .base_reg = PLLDP_BASE, + .misc_reg = PLLDP_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable = PLLSS_MISC_LOCK_ENABLE, + .iddq_reg = PLLDP_BASE, + .iddq_mask = 1 << PLLSS_IDDQ_BIT, + .pdiv_table = pll12g_ssd_esd_map, + .mnp_bits = {8, 8, 4, 20}, + }, +}; + +static int tegra124_pll_init(struct clknode *clk, device_t dev); +static int tegra124_pll_set_gate(struct clknode *clk, bool enable); +static int tegra124_pll_recalc(struct clknode *clk, uint64_t *freq); +static int tegra124_pll_set_freq(struct clknode *clknode, uint64_t fin, + uint64_t *fout, int flags, int *stop); +struct pll_sc { + device_t clkdev; + enum pll_type type; + uint32_t base_reg; + uint32_t misc_reg; + uint32_t lock_mask; + uint32_t lock_enable; + uint32_t iddq_reg; + uint32_t iddq_mask; + uint32_t flags; + struct pdiv_table *pdiv_table; + struct mnp_bits mnp_bits; +}; + +static clknode_method_t tegra124_pll_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, tegra124_pll_init), + CLKNODEMETHOD(clknode_set_gate, tegra124_pll_set_gate), + CLKNODEMETHOD(clknode_recalc_freq, tegra124_pll_recalc), + CLKNODEMETHOD(clknode_set_freq, tegra124_pll_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(tegra124_pll, tegra124_pll_class, tegra124_pll_methods, + sizeof(struct pll_sc), clknode_class); + +static int +pll_enable(struct pll_sc *sc) +{ + uint32_t reg; + + + RD4(sc, sc->base_reg, ®); + if (sc->type != PLL_E) + reg &= ~PLL_BASE_BYPASS; + reg |= PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + return (0); +} + +static int +pll_disable(struct pll_sc *sc) +{ + uint32_t reg; + + RD4(sc, sc->base_reg, ®); + if (sc->type != PLL_E) + reg |= PLL_BASE_BYPASS; + reg &= ~PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + return (0); +} + +static uint32_t +pdiv_to_reg(struct pll_sc *sc, uint32_t p_div) +{ + struct pdiv_table *tbl; + + tbl = sc->pdiv_table; + if (tbl == NULL) + return (ffs(p_div)); + + while (tbl->divider != 0) { + if (p_div <= tbl->divider) + return (tbl->value); + tbl++; + } + return ~0; +} + +static uint32_t +reg_to_pdiv(struct pll_sc *sc, uint32_t reg) +{ + struct pdiv_table *tbl; + + tbl = sc->pdiv_table; + if (tbl != NULL) { + while (tbl->divider) { + if (reg == tbl->value) + return (tbl->divider); + tbl++; + } + return (0); + } + return (1 << reg); +} + +static uint32_t +get_masked(uint32_t val, uint32_t shift, uint32_t width) +{ + + return ((val >> shift) & ((1 << width) - 1)); +} + +static uint32_t +set_masked(uint32_t val, uint32_t v, uint32_t shift, uint32_t width) +{ + + val &= ~(((1 << width) - 1) << shift); + val |= (v & ((1 << width) - 1)) << shift; + return (val); +} + +static void +get_divisors(struct pll_sc *sc, uint32_t *m, uint32_t *n, uint32_t *p) +{ + uint32_t val; + struct mnp_bits *mnp_bits; + + mnp_bits = &sc->mnp_bits; + RD4(sc, sc->base_reg, &val); + *m = get_masked(val, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width); + *n = get_masked(val, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width); + *p = get_masked(val, mnp_bits->p_shift, mnp_bits->p_width); +} + +static uint32_t +set_divisors(struct pll_sc *sc, uint32_t val, uint32_t m, uint32_t n, + uint32_t p) +{ + struct mnp_bits *mnp_bits; + + mnp_bits = &sc->mnp_bits; + val = set_masked(val, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width); + val = set_masked(val, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width); + val = set_masked(val, p, mnp_bits->p_shift, mnp_bits->p_width); + return (val); +} + +static bool +is_locked(struct pll_sc *sc) +{ + uint32_t reg; + + switch (sc->type) { + case PLL_REFE: + RD4(sc, sc->misc_reg, ®); + reg &= PLLRE_MISC_LOCK; + break; + + case PLL_E: + RD4(sc, sc->misc_reg, ®); + reg &= PLLE_MISC_LOCK; + break; + + default: + RD4(sc, sc->base_reg, ®); + reg &= PLL_BASE_LOCK; + break; + } + return (reg != 0); +} + +static int +wait_for_lock(struct pll_sc *sc) +{ + int i; + + for (i = PLL_LOCK_TIMEOUT / 10; i > 0; i--) { + if (is_locked(sc)) + break; + DELAY(10); + } + if (i <= 0) { + printf("PLL lock timeout\n"); + return (ETIMEDOUT); + } + return (0); +} + +static int +plle_enable(struct pll_sc *sc) +{ + uint32_t reg; + int rv; + struct mnp_bits *mnp_bits; + uint32_t pll_m = 1; + uint32_t pll_n = 200; + uint32_t pll_p = 13; + uint32_t pll_cml = 13; + + mnp_bits = &sc->mnp_bits; + + + /* Disable lock override. */ + RD4(sc, sc->base_reg, ®); + reg &= ~PLLE_BASE_LOCK_OVERRIDE; + WR4(sc, sc->base_reg, reg); + + RD4(sc, PLLE_AUX, ®); + reg |= PLLE_AUX_ENABLE_SWCTL; + reg &= ~PLLE_AUX_SEQ_ENABLE; + WR4(sc, PLLE_AUX, reg); + DELAY(10); + + RD4(sc, sc->misc_reg, ®); + reg |= PLLE_MISC_LOCK_ENABLE; + reg |= PLLE_MISC_IDDQ_SWCTL; + reg &= ~PLLE_MISC_IDDQ_OVERRIDE_VALUE; + reg |= PLLE_MISC_PTS; + reg |= PLLE_MISC_VREG_BG_CTRL_MASK; + reg |= PLLE_MISC_VREG_CTRL_MASK; + WR4(sc, sc->misc_reg, reg); + DELAY(10); + + RD4(sc, PLLE_SS_CNTL, ®); + reg |= PLLE_SS_CNTL_DISABLE; + WR4(sc, PLLE_SS_CNTL, reg); + + RD4(sc, sc->base_reg, ®); + reg = set_divisors(sc, reg, pll_m, pll_n, pll_p); + reg &= ~(PLLE_BASE_DIVCML_MASK << PLLE_BASE_DIVCML_SHIFT); + reg |= pll_cml << PLLE_BASE_DIVCML_SHIFT; + WR4(sc, sc->base_reg, reg); + DELAY(10); + + pll_enable(sc); + rv = wait_for_lock(sc); + if (rv != 0) + return (rv); + + RD4(sc, PLLE_SS_CNTL, ®); + reg &= ~PLLE_SS_CNTL_SSCCENTER; + reg &= ~PLLE_SS_CNTL_SSCINVERT; + reg &= ~PLLE_SS_CNTL_COEFFICIENTS_MASK; + reg |= PLLE_SS_CNTL_COEFFICIENTS_VAL; + WR4(sc, PLLE_SS_CNTL, reg); + reg &= ~PLLE_SS_CNTL_SSCBYP; + reg &= ~PLLE_SS_CNTL_BYPASS_SS; + WR4(sc, PLLE_SS_CNTL, reg); + DELAY(10); + + reg &= ~PLLE_SS_CNTL_INTERP_RESET; + WR4(sc, PLLE_SS_CNTL, reg); + DELAY(10); + + /* HW control of brick pll. */ + RD4(sc, sc->misc_reg, ®); + reg &= ~PLLE_MISC_IDDQ_SWCTL; + WR4(sc, sc->misc_reg, reg); + + RD4(sc, PLLE_AUX, ®); + reg |= PLLE_AUX_USE_LOCKDET; + reg |= PLLE_AUX_SEQ_START_STATE; + reg &= ~PLLE_AUX_ENABLE_SWCTL; + reg &= ~PLLE_AUX_SS_SWCTL; + WR4(sc, PLLE_AUX, reg); + reg |= PLLE_AUX_SEQ_START_STATE; + DELAY(10); + reg |= PLLE_AUX_SEQ_ENABLE; + WR4(sc, PLLE_AUX, reg); + + RD4(sc, XUSBIO_PLL_CFG0, ®); + reg |= XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET; + reg |= XUSBIO_PLL_CFG0_SEQ_START_STATE; + reg &= ~XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL; + reg &= ~XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL; + WR4(sc, XUSBIO_PLL_CFG0, reg); + DELAY(10); + + reg |= XUSBIO_PLL_CFG0_SEQ_ENABLE; + WR4(sc, XUSBIO_PLL_CFG0, reg); + + + /* Enable HW control and unreset SATA PLL. */ + RD4(sc, SATA_PLL_CFG0, ®); + reg &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL; + reg &= ~SATA_PLL_CFG0_PADPLL_RESET_OVERRIDE_VALUE; + reg |= SATA_PLL_CFG0_PADPLL_USE_LOCKDET; + reg &= ~SATA_PLL_CFG0_SEQ_IN_SWCTL; + reg &= ~SATA_PLL_CFG0_SEQ_RESET_INPUT_VALUE; + reg &= ~SATA_PLL_CFG0_SEQ_LANE_PD_INPUT_VALUE; + reg &= ~SATA_PLL_CFG0_SEQ_PADPLL_PD_INPUT_VALUE; + reg &= ~SATA_PLL_CFG0_SEQ_ENABLE; + reg |= SATA_PLL_CFG0_SEQ_START_STATE; + WR4(sc, SATA_PLL_CFG0, reg); + DELAY(10); + reg |= SATA_PLL_CFG0_SEQ_ENABLE; + WR4(sc, SATA_PLL_CFG0, reg); + + /* Enable HW control of PCIe PLL. */ + RD4(sc, PCIE_PLL_CFG0, ®); + reg |= PCIE_PLL_CFG0_SEQ_ENABLE; + WR4(sc, PCIE_PLL_CFG0, reg); + + return (0); +} + +static int +tegra124_pll_set_gate(struct clknode *clknode, bool enable) +{ + int rv; + struct pll_sc *sc; + + sc = clknode_get_softc(clknode); + if (enable == 0) { + rv = pll_disable(sc); + return(rv); + } + + if (sc->type == PLL_E) + rv = plle_enable(sc); + else + rv = pll_enable(sc); + return (rv); +} + +static int +pll_set_std(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags, + uint32_t m, uint32_t n, uint32_t p) +{ + uint32_t reg; + struct mnp_bits *mnp_bits; + int rv; + + mnp_bits = &sc->mnp_bits; + if (m >= (1 << mnp_bits->m_width)) + return (ERANGE); + if (n >= (1 << mnp_bits->n_width)) + return (ERANGE); + if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width)) + return (ERANGE); + + if (flags & CLK_SET_DRYRUN) { + if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && + (*fout != (((fin / m) * n) /p))) + return (ERANGE); + + *fout = ((fin / m) * n) /p; + return (0); + } + + pll_disable(sc); + + /* take pll out of IDDQ */ + if (sc->iddq_reg != 0) + MD4(sc, sc->iddq_reg, sc->iddq_mask, 0); + + RD4(sc, sc->base_reg, ®); + reg = set_masked(reg, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width); + reg = set_masked(reg, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width); + reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift, + mnp_bits->p_width); + WR4(sc, sc->base_reg, reg); + + /* Enable PLL. */ + RD4(sc, sc->base_reg, ®); + reg |= PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + + /* Enable lock detection. */ + RD4(sc, sc->misc_reg, ®); + reg |= sc->lock_enable; + WR4(sc, sc->misc_reg, reg); + + rv = wait_for_lock(sc); + if (rv != 0) { + /* Disable PLL */ + RD4(sc, sc->base_reg, ®); + reg &= ~PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + return (rv); + } + RD4(sc, sc->misc_reg, ®); + + pll_enable(sc); + *fout = ((fin / m) * n) / p; + return 0; +} + +static int +plla_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t m, n, p; + + p = 1; + m = 5; + n = (*fout * p * m + fin / 2)/ fin; + dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); + return (pll_set_std(sc, fin, fout, flags, m, n, p)); +} + +static int +pllc_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t m, n, p; + + + p = 2; + m = 1; + n = (*fout * p * m + fin / 2)/ fin; + dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); + return (pll_set_std( sc, fin, fout, flags, m, n, p)); +} + +static int +plld2_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t m, n, p; + + p = 2; + m = 1; + n = (*fout * p * m + fin / 2)/ fin; + dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); + return (pll_set_std(sc, fin, fout, flags, m, n, p)); +} + + + +static int +pllrefe_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t m, n, p; + + m = 1; + p = 1; + n = *fout * p * m / fin; + return (pll_set_std(sc, fin, fout, flags, m, n, p)); +} + +static int +pllx_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) +{ + uint32_t reg; + uint32_t m, n, p; + struct mnp_bits *mnp_bits; + int rv; + + mnp_bits = &sc->mnp_bits; + + p = 1; + m = 1; + n = (*fout * p * m + fin / 2)/ fin; + dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); + + if (m >= (1 << mnp_bits->m_width)) + return (ERANGE); + if (n >= (1 << mnp_bits->n_width)) + return (ERANGE); + if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width)) + return (ERANGE); + + if (flags & CLK_SET_DRYRUN) { + if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && + (*fout != (((fin / m) * n) /p))) + return (ERANGE); + *fout = ((fin / m) * n) /p; + return (0); + } + + /* Set bypass. */ + RD4(sc, sc->base_reg, ®); + reg |= PLL_BASE_BYPASS; + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, ®); + DELAY(100); + + /* Set PLL. */ + RD4(sc, sc->base_reg, ®); + reg = set_masked(reg, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width); + reg = set_masked(reg, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width); + reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift, + mnp_bits->p_width); + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, ®); + DELAY(100); + + /* Enable PLL. */ + RD4(sc, sc->base_reg, ®); + reg |= PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + + /* Enable lock detection */ + RD4(sc, sc->misc_reg, ®); + reg |= sc->lock_enable; + WR4(sc, sc->misc_reg, reg); + + rv = wait_for_lock(sc); + if (rv != 0) { + /* Disable PLL */ + RD4(sc, sc->base_reg, ®); + reg &= ~PLL_BASE_ENABLE; + WR4(sc, sc->base_reg, reg); + return (rv); + } + RD4(sc, sc->misc_reg, ®); + + /* Clear bypass. */ + RD4(sc, sc->base_reg, ®); + reg &= ~PLL_BASE_BYPASS; + WR4(sc, sc->base_reg, reg); + *fout = ((fin / m) * n) / p; + return (0); +} + +static int +tegra124_pll_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + *stop = 1; + int rv; + struct pll_sc *sc; + + sc = clknode_get_softc(clknode); + dprintf("%s: Requested freq: %llu, input freq: %llu\n", __func__, + *fout, fin); + switch (sc->type) { + case PLL_A: + rv = plla_set_freq(sc, fin, fout, flags); + break; + case PLL_C: + rv = pllc_set_freq(sc, fin, fout, flags); + break; + case PLL_D2: + rv = plld2_set_freq(sc, fin, fout, flags); + break; + + case PLL_REFE: + rv = pllrefe_set_freq(sc, fin, fout, flags); + break; + + case PLL_X: + rv = pllx_set_freq(sc, fin, fout, flags); + break; + + case PLL_U: + if (*fout == 480000000) /* PLLU is fixed to 480 MHz */ + rv = 0; + else + rv = ERANGE; + break; + default: + rv = ENXIO; + break; + } + return (rv); +} + + +static int +tegra124_pll_init(struct clknode *clk, device_t dev) +{ + struct pll_sc *sc; + uint32_t reg; + + sc = clknode_get_softc(clk); + + /* If PLL is enabled, enable lock detect too. */ + RD4(sc, sc->base_reg, ®); + if (reg & PLL_BASE_ENABLE) { + RD4(sc, sc->misc_reg, ®); + reg |= sc->lock_enable; + WR4(sc, sc->misc_reg, reg); + } + + clknode_init_parent_idx(clk, 0); + return(0); +} + +static int +tegra124_pll_recalc(struct clknode *clk, uint64_t *freq) +{ + struct pll_sc *sc; + uint32_t m, n, p, pr; + uint32_t reg, misc_reg; + int locked; + + sc = clknode_get_softc(clk); + + RD4(sc, sc->base_reg, ®); + RD4(sc, sc->misc_reg, &misc_reg); + + get_divisors(sc, &m, &n, &pr); + if (sc->type != PLL_E) + p = reg_to_pdiv(sc, pr); + else + p = 2 * (pr - 1); + locked = is_locked(sc); + + dprintf("%s: %s (0x%08x, 0x%08x) - m: %d, n: %d, p: %d (%d): " + "e: %d, r: %d, o: %d - %s\n", __func__, + clknode_get_name(clk), reg, misc_reg, m, n, p, pr, + (reg >> 30) & 1, (reg >> 29) & 1, (reg >> 28) & 1, + locked ? "locked" : "unlocked"); + + if ((m == 0) || (n == 0) || (p == 0)) { + *freq = 0; + return (EINVAL); + } + *freq = ((*freq / m) * n) / p; + return (0); +} + +static int +pll_register(struct clkdom *clkdom, struct clk_pll_def *clkdef) +{ + struct clknode *clk; + struct pll_sc *sc; + + clk = clknode_create(clkdom, &tegra124_pll_class, &clkdef->clkdef); + if (clk == NULL) + return (ENXIO); + + sc = clknode_get_softc(clk); + sc->clkdev = clknode_get_device(clk); + sc->type = clkdef->type; + sc->base_reg = clkdef->base_reg; + sc->misc_reg = clkdef->misc_reg; + sc->lock_mask = clkdef->lock_mask; + sc->lock_enable = clkdef->lock_enable; + sc->iddq_reg = clkdef->iddq_reg; + sc->iddq_mask = clkdef->iddq_mask; + sc->flags = clkdef->flags; + sc->pdiv_table = clkdef->pdiv_table; + sc->mnp_bits = clkdef->mnp_bits; + clknode_register(clkdom, clk); + return (0); +} + +static void config_utmi_pll(struct tegra124_car_softc *sc) +{ + uint32_t reg; + /* + * XXX Simplified UTMIP settings for 12MHz base clock. + */ +#define ENABLE_DELAY_COUNT 0x02 +#define STABLE_COUNT 0x2F +#define ACTIVE_DELAY_COUNT 0x04 +#define XTAL_FREQ_COUNT 0x76 + + CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG2, ®); + reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0); + reg |= UTMIP_PLL_CFG2_STABLE_COUNT(STABLE_COUNT); + reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); + reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(ACTIVE_DELAY_COUNT); + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN; + CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG2, reg); + + CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, ®); + reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(ENABLE_DELAY_COUNT); + reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0); + reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(XTAL_FREQ_COUNT); + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN; + CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg); + + /* Prepare UTMIP requencer. */ + CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, ®); + reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL; + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE; + CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg); + + /* Powerup UTMIP. */ + CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, ®); + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg); + DELAY(10); + + /* SW override for UTMIPLL */ + CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, ®); + reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE; + CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg); + DELAY(10); + + /* HW control of UTMIPLL. */ + CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, ®); + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE; + CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg); +} + +void +tegra124_init_plls(struct tegra124_car_softc *sc) +{ + int i, rv; + + for (i = 0; i < nitems(pll_clks); i++) { + rv = pll_register(sc->clkdom, pll_clks + i); + if (rv != 0) + panic("pll_register failed"); + } + config_utmi_pll(sc); + +} diff --git a/sys/arm/nvidia/tegra124/tegra124_clk_super.c b/sys/arm/nvidia/tegra124/tegra124_clk_super.c new file mode 100644 index 0000000..7f32eba --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_clk_super.c @@ -0,0 +1,265 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 "tegra124_car.h" + + +/* Flags */ +#define SMF_HAVE_DIVIDER_2 1 + +struct super_mux_def { + struct clknode_init_def clkdef; + uint32_t base_reg; + uint32_t flags; + int src_pllx; + int src_div2; +}; + +#define PLIST(x) static const char *x[] +#define SM(_id, cn, pl, r, x, d, f) \ +{ \ + .clkdef.id = _id, \ + .clkdef.name = cn, \ + .clkdef.parent_names = pl, \ + .clkdef.parent_cnt = nitems(pl), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .base_reg = r, \ + .src_pllx = x, \ + .src_div2 = d, \ + .flags = f, \ +} + +PLIST(cclk_g_parents) = { + "clk_m", "pllC_out0", "clk_s", "pllM_out0", + "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0", + "pllX_out", NULL, NULL, NULL, + NULL, NULL, NULL,NULL, // "dfllCPU_out0" +}; + +PLIST(cclk_lp_parents) = { + "clk_m", "pllC_out0", "clk_s", "pllM_out0", + "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0", + "pllX_out", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + "pllX_out0" +}; + +PLIST(sclk_parents) = { + "clk_m", "pllC_out1", "pllP_out4", "pllP_out0", + "pllP_out2", "pllC_out0", "clk_s", "pllM_out1", +}; + +static struct super_mux_def super_mux_def[] = { + SM(TEGRA124_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY, 0, 0, 0), + SM(TEGRA124_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY, 8, 16, SMF_HAVE_DIVIDER_2), + SM(TEGRA124_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY, 0, 0, 0), +}; + +static int super_mux_init(struct clknode *clk, device_t dev); +static int super_mux_set_mux(struct clknode *clk, int idx); + +struct super_mux_sc { + device_t clkdev; + uint32_t base_reg; + int src_pllx; + int src_div2; + uint32_t flags; + + int mux; +}; + +static clknode_method_t super_mux_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, super_mux_init), + CLKNODEMETHOD(clknode_set_mux, super_mux_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(tegra124_super_mux, tegra124_super_mux_class, super_mux_methods, + sizeof(struct super_mux_sc), clknode_class); + +/* Mux status. */ +#define SUPER_MUX_STATE_STDBY 0 +#define SUPER_MUX_STATE_IDLE 1 +#define SUPER_MUX_STATE_RUN 2 +#define SUPER_MUX_STATE_IRQ 3 +#define SUPER_MUX_STATE_FIQ 4 + +/* Mux register bits. */ +#define SUPER_MUX_STATE_BIT_SHIFT 28 +#define SUPER_MUX_STATE_BIT_MASK 0xF +/* State is Priority encoded */ +#define SUPER_MUX_STATE_BIT_STDBY 0x00 +#define SUPER_MUX_STATE_BIT_IDLE 0x01 +#define SUPER_MUX_STATE_BIT_RUN 0x02 +#define SUPER_MUX_STATE_BIT_IRQ 0x04 +#define SUPER_MUX_STATE_BIT_FIQ 0x08 + +#define SUPER_MUX_MUX_WIDTH 4 +#define SUPER_MUX_LP_DIV2_BYPASS (1 << 16) + +static uint32_t +super_mux_get_state(uint32_t reg) +{ + reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK; + if (reg & SUPER_MUX_STATE_BIT_FIQ) + return (SUPER_MUX_STATE_FIQ); + if (reg & SUPER_MUX_STATE_BIT_IRQ) + return (SUPER_MUX_STATE_IRQ); + if (reg & SUPER_MUX_STATE_BIT_RUN) + return (SUPER_MUX_STATE_RUN); + if (reg & SUPER_MUX_STATE_BIT_IDLE) + return (SUPER_MUX_STATE_IDLE); + return (SUPER_MUX_STATE_STDBY); +} + +static int +super_mux_init(struct clknode *clk, device_t dev) +{ + struct super_mux_sc *sc; + uint32_t reg; + int shift, state; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + RD4(sc, sc->base_reg, ®); + DEVICE_UNLOCK(sc); + state = super_mux_get_state(reg); + + if ((state != SUPER_MUX_STATE_RUN) && + (state != SUPER_MUX_STATE_IDLE)) { + panic("Unexpected super mux state: %u", state); + } + + shift = state * SUPER_MUX_MUX_WIDTH; + + sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1); + + /* + * CCLKLP uses PLLX/2 as source if LP_DIV2_BYPASS isn't set + * and source mux is set to PLLX. + */ + if (sc->flags & SMF_HAVE_DIVIDER_2) { + if (((reg & SUPER_MUX_LP_DIV2_BYPASS) == 0) && + (sc->mux == sc->src_pllx)) + sc->mux = sc->src_div2; + } + clknode_init_parent_idx(clk, sc->mux); + + return(0); +} + +static int +super_mux_set_mux(struct clknode *clk, int idx) +{ + + struct super_mux_sc *sc; + int shift, state; + uint32_t reg, dummy; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(sc); + RD4(sc, sc->base_reg, ®); + state = super_mux_get_state(reg); + + if ((state != SUPER_MUX_STATE_RUN) && + (state != SUPER_MUX_STATE_IDLE)) { + panic("Unexpected super mux state: %u", state); + } + + shift = state * SUPER_MUX_MUX_WIDTH; + sc->mux = idx; + if (sc->flags & SMF_HAVE_DIVIDER_2) { + if (idx == sc->src_div2) { + idx = sc->src_pllx; + reg &= ~SUPER_MUX_LP_DIV2_BYPASS; + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, &dummy); + } else if (idx == sc->src_pllx) { + reg = SUPER_MUX_LP_DIV2_BYPASS; + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, &dummy); + } + } + reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift); + reg |= idx << shift; + WR4(sc, sc->base_reg, reg); + RD4(sc, sc->base_reg, &dummy); + DEVICE_UNLOCK(sc); + + return(0); +} + +static int +super_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef) +{ + struct clknode *clk; + struct super_mux_sc *sc; + + clk = clknode_create(clkdom, &tegra124_super_mux_class, + &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->clkdev = clknode_get_device(clk); + sc->base_reg = clkdef->base_reg; + sc->src_pllx = clkdef->src_pllx; + sc->src_div2 = clkdef->src_div2; + sc->flags = clkdef->flags; + + clknode_register(clkdom, clk); + return (0); +} + +void +tegra124_super_mux_clock(struct tegra124_car_softc *sc) +{ + int i, rv; + + for (i = 0; i < nitems(super_mux_def); i++) { + rv = super_mux_register(sc->clkdom, &super_mux_def[i]); + if (rv != 0) + panic("super_mux_register failed"); + } + +} diff --git a/sys/arm/nvidia/tegra124/tegra124_coretemp.c b/sys/arm/nvidia/tegra124/tegra124_coretemp.c new file mode 100644 index 0000000..c29fe8e --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_coretemp.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 "tegra_soctherm_if.h" + + +enum therm_info { + CORETEMP_TEMP, + CORETEMP_DELTA, + CORETEMP_RESOLUTION, + CORETEMP_TJMAX, +}; + +struct tegra124_coretemp_softc { + device_t dev; + int overheat_log; + int core_max_temp; + int cpu_id; + device_t tsens_dev; + intptr_t tsens_id; +}; + +static int +coretemp_get_val_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev; + int val, temp, rv; + struct tegra124_coretemp_softc *sc; + enum therm_info type; + char stemp[16]; + + + dev = (device_t) arg1; + sc = device_get_softc(dev); + type = arg2; + + + rv = TEGRA_SOCTHERM_GET_TEMPERATURE(sc->tsens_dev, sc->dev, + sc->tsens_id, &temp); + if (rv != 0) { + device_printf(sc->dev, + "Cannot read temperature sensor %d: %d\n", + sc->tsens_id, rv); + return (rv); + } + + switch (type) { + case CORETEMP_TEMP: + val = temp / 100; + val += 2731; + break; + case CORETEMP_DELTA: + val = (sc->core_max_temp - temp) / 1000; + break; + case CORETEMP_RESOLUTION: + val = 1; + break; + case CORETEMP_TJMAX: + val = sc->core_max_temp / 100; + val += 2731; + break; + } + + + if ((temp > sc->core_max_temp) && !sc->overheat_log) { + sc->overheat_log = 1; + + /* + * Check for Critical Temperature Status and Critical + * Temperature Log. It doesn't really matter if the + * current temperature is invalid because the "Critical + * Temperature Log" bit will tell us if the Critical + * Temperature has * been reached in past. It's not + * directly related to the current temperature. + * + * If we reach a critical level, allow devctl(4) + * to catch this and shutdown the system. + */ + device_printf(dev, "critical temperature detected, " + "suggest system shutdown\n"); + snprintf(stemp, sizeof(stemp), "%d", val); + devctl_notify("coretemp", "Thermal", stemp, + "notify=0xcc"); + } else { + sc->overheat_log = 0; + } + + return (sysctl_handle_int(oidp, 0, val, req)); +} + +static int +tegra124_coretemp_ofw_parse(struct tegra124_coretemp_softc *sc) +{ + int rv, ncells; + phandle_t node, xnode; + pcell_t *cells; + + node = OF_peer(0); + node = ofw_bus_find_child(node, "thermal-zones"); + if (node <= 0) { + device_printf(sc->dev, "Cannot find 'thermal-zones'.\n"); + return (ENXIO); + } + + node = ofw_bus_find_child(node, "cpu"); + if (node <= 0) { + device_printf(sc->dev, "Cannot find 'cpu'\n"); + return (ENXIO); + } + rv = ofw_bus_parse_xref_list_alloc(node, "thermal-sensors", + "#thermal-sensor-cells", 0, &xnode, &ncells, &cells); + if (rv != 0) { + device_printf(sc->dev, + "Cannot parse 'thermal-sensors' property.\n"); + return (ENXIO); + } + if (ncells != 1) { + device_printf(sc->dev, + "Invalid format of 'thermal-sensors' property(%d).\n", + ncells); + return (ENXIO); + } + + sc->tsens_id = 0x100 + sc->cpu_id; //cells[0]; + free(cells, M_OFWPROP); + + sc->tsens_dev = OF_device_from_xref(xnode); + if (sc->tsens_dev == NULL) { + device_printf(sc->dev, + "Cannot find thermal sensors device."); + return (ENXIO); + } + return (0); +} + +static void +tegra124_coretemp_identify(driver_t *driver, device_t parent) +{ + + if (device_find_child(parent, "tegra124_coretemp", -1) != NULL) + return; + if (BUS_ADD_CHILD(parent, 0, "tegra124_coretemp", -1) == NULL) + device_printf(parent, "add child failed\n"); +} + +static int +tegra124_coretemp_probe(device_t dev) +{ + + device_set_desc(dev, "CPU Frequency Control"); + return (0); +} + +static int +tegra124_coretemp_attach(device_t dev) +{ + struct tegra124_coretemp_softc *sc; + device_t pdev; + struct sysctl_oid *oid; + struct sysctl_ctx_list *ctx; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->cpu_id = device_get_unit(dev); + sc->core_max_temp = 102000; + pdev = device_get_parent(dev); + + rv = tegra124_coretemp_ofw_parse(sc); + if (rv != 0) + return (rv); + + ctx = device_get_sysctl_ctx(dev); + + oid = SYSCTL_ADD_NODE(ctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), OID_AUTO, + "coretemp", CTLFLAG_RD, NULL, "Per-CPU thermal information"); + + /* + * Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp. + */ + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), + OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK", + "Current temperature"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "delta", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_DELTA, + coretemp_get_val_sysctl, "I", + "Delta between TCC activation and current temperature"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "resolution", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_RESOLUTION, + coretemp_get_val_sysctl, "I", + "Resolution of CPU thermal sensor"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "tjmax", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_TJMAX, + coretemp_get_val_sysctl, "IK", + "TCC activation temperature"); + + return (0); +} + +static int +tegra124_coretemp_detach(device_t dev) +{ + struct tegra124_coretemp_softc *sc; + + sc = device_get_softc(dev); + return (0); +} + + +static device_method_t tegra124_coretemp_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, tegra124_coretemp_identify), + DEVMETHOD(device_probe, tegra124_coretemp_probe), + DEVMETHOD(device_attach, tegra124_coretemp_attach), + DEVMETHOD(device_detach, tegra124_coretemp_detach), + + + DEVMETHOD_END +}; + +static devclass_t tegra124_coretemp_devclass; +static driver_t tegra124_coretemp_driver = { + "tegra124_coretemp", + tegra124_coretemp_methods, + sizeof(struct tegra124_coretemp_softc), +}; + +DRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver, + tegra124_coretemp_devclass, 0, 0); diff --git a/sys/arm/nvidia/tegra124/tegra124_cpufreq.c b/sys/arm/nvidia/tegra124/tegra124_cpufreq.c new file mode 100644 index 0000000..1227c81 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_cpufreq.c @@ -0,0 +1,583 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 "cpufreq_if.h" + +#define XXX + +/* CPU voltage table entry */ +struct speedo_entry { + uint64_t freq; /* Frequency point */ + int c0; /* Coeeficient values for */ + int c1; /* quadratic equation: */ + int c2; /* c2 * speedo^2 + c1 * speedo + c0 */ +}; + +struct cpu_volt_def { + int min_uvolt; /* Min allowed CPU voltage */ + int max_uvolt; /* Max allowed CPU voltage */ + int step_uvolt; /* Step of CPU voltage */ + int speedo_scale; /* Scaling factor for cvt */ + int speedo_nitems; /* Size of speedo table */ + struct speedo_entry *speedo_tbl; /* CPU voltage table */ +}; + +struct cpu_speed_point { + uint64_t freq; /* Frequecy */ + int uvolt; /* Requested voltage */ +}; + +static struct speedo_entry tegra124_speedo_dpll_tbl[] = +{ + { 204000000ULL, 1112619, -29295, 402}, + { 306000000ULL, 1150460, -30585, 402}, + { 408000000ULL, 1190122, -31865, 402}, + { 510000000ULL, 1231606, -33155, 402}, + { 612000000ULL, 1274912, -34435, 402}, + { 714000000ULL, 1320040, -35725, 402}, + { 816000000ULL, 1366990, -37005, 402}, + { 918000000ULL, 1415762, -38295, 402}, + {1020000000ULL, 1466355, -39575, 402}, + {1122000000ULL, 1518771, -40865, 402}, + {1224000000ULL, 1573009, -42145, 402}, + {1326000000ULL, 1629068, -43435, 402}, + {1428000000ULL, 1686950, -44715, 402}, + {1530000000ULL, 1746653, -46005, 402}, + {1632000000ULL, 1808179, -47285, 402}, + {1734000000ULL, 1871526, -48575, 402}, + {1836000000ULL, 1936696, -49855, 402}, + {1938000000ULL, 2003687, -51145, 402}, + {2014500000ULL, 2054787, -52095, 402}, + {2116500000ULL, 2124957, -53385, 402}, + {2218500000ULL, 2196950, -54665, 402}, + {2320500000ULL, 2270765, -55955, 402}, + {2320500000ULL, 2270765, -55955, 402}, + {2422500000ULL, 2346401, -57235, 402}, + {2524500000ULL, 2437299, -58535, 402}, +}; + +static struct cpu_volt_def tegra124_cpu_volt_dpll_def = +{ + .min_uvolt = 900000, /* 0.9 V */ + .max_uvolt = 1260000, /* 1.26 */ + .step_uvolt = 10000, /* 10 mV */ + .speedo_scale = 100, + .speedo_nitems = nitems(tegra124_speedo_dpll_tbl), + .speedo_tbl = tegra124_speedo_dpll_tbl, +}; + +static struct speedo_entry tegra124_speedo_pllx_tbl[] = +{ + { 204000000ULL, 800000, 0, 0}, + { 306000000ULL, 800000, 0, 0}, + { 408000000ULL, 800000, 0, 0}, + { 510000000ULL, 800000, 0, 0}, + { 612000000ULL, 800000, 0, 0}, + { 714000000ULL, 800000, 0, 0}, + { 816000000ULL, 820000, 0, 0}, + { 918000000ULL, 840000, 0, 0}, + {1020000000ULL, 880000, 0, 0}, + {1122000000ULL, 900000, 0, 0}, + {1224000000ULL, 930000, 0, 0}, + {1326000000ULL, 960000, 0, 0}, + {1428000000ULL, 990000, 0, 0}, + {1530000000ULL, 1020000, 0, 0}, + {1632000000ULL, 1070000, 0, 0}, + {1734000000ULL, 1100000, 0, 0}, + {1836000000ULL, 1140000, 0, 0}, + {1938000000ULL, 1180000, 0, 0}, + {2014500000ULL, 1220000, 0, 0}, + {2116500000ULL, 1260000, 0, 0}, + {2218500000ULL, 1310000, 0, 0}, + {2320500000ULL, 1360000, 0, 0}, + {2397000000ULL, 1400000, 0, 0}, + {2499000000ULL, 1400000, 0, 0}, +}; + + +static struct cpu_volt_def tegra124_cpu_volt_pllx_def = +{ + .min_uvolt = 900000, /* 0.9 V */ + .max_uvolt = 1260000, /* 1.26 */ + .step_uvolt = 10000, /* 10 mV */ + .speedo_scale = 100, + .speedo_nitems = nitems(tegra124_speedo_pllx_tbl), + .speedo_tbl = tegra124_speedo_pllx_tbl, +}; + +static uint64_t cpu_freq_tbl[] = { + 204000000ULL, + 306000000ULL, + 408000000ULL, + 510000000ULL, + 612000000ULL, + 714000000ULL, + 816000000ULL, + 918000000ULL, + 1020000000ULL, + 1122000000ULL, + 1224000000ULL, + 1326000000ULL, + 1428000000ULL, + 1530000000ULL, + 1632000000ULL, + 1734000000ULL, + 1836000000ULL, + 1938000000ULL, + 2014000000ULL, + 2116000000ULL, + 2218000000ULL, + 2320000000ULL, + 2320000000ULL, + 2422000000ULL, + 2524000000ULL, +}; + +static uint64_t cpu_max_freq[] = { + 2014500000ULL, + 2320500000ULL, + 2116500000ULL, + 2524500000ULL, +}; + +struct tegra124_cpufreq_softc { + device_t dev; + phandle_t node; + + regulator_t supply_vdd_cpu; + clk_t clk_cpu_g; + clk_t clk_cpu_lp; + clk_t clk_pll_x; + clk_t clk_pll_p; + clk_t clk_dfll; + + int process_id; + int speedo_id; + int speedo_value; + + uint64_t cpu_max_freq; + struct cpu_volt_def *cpu_def; + struct cpu_speed_point *speed_points; + int nspeed_points; + + struct cpu_speed_point *act_speed_point; + + int latency; +}; + +static int cpufreq_lowest_freq = 1; +TUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq); + +#define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div)) + +#define ROUND_UP(val, div) ((((val) + ((div) - 1)) / (div)) * (div)) +#define ROUND_DOWN(val, div) (((val) / (div)) * (div)) + +/* + * Compute requesetd voltage for given frequency and SoC process variations, + * - compute base voltage from speedo value using speedo table + * - round up voltage to next regulator step + * - clamp it to regulator limits + */ +static int +freq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq) +{ + int uv, scale, min_uvolt, max_uvolt, step_uvolt; + struct speedo_entry *ent; + int i; + + /* Get speedo entry with higher frequency */ + ent = NULL; + for (i = 0; i < sc->cpu_def->speedo_nitems; i++) { + if (sc->cpu_def->speedo_tbl[i].freq >= freq) { + ent = &sc->cpu_def->speedo_tbl[i]; + break; + } + } + if (ent == NULL) + ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1]; + scale = sc->cpu_def->speedo_scale; + + + /* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */ + uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale); + uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) + + ent->c0; + step_uvolt = sc->cpu_def->step_uvolt; + /* Round up it to next regulator step */ + uv = ROUND_UP(uv, step_uvolt); + + /* Clamp result */ + min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt); + max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt); + if (uv < min_uvolt) + uv = min_uvolt; + if (uv > max_uvolt) + uv = max_uvolt; + return (uv); + +} + +static void +build_speed_points(struct tegra124_cpufreq_softc *sc) { + int i; + + sc->nspeed_points = nitems(cpu_freq_tbl); + sc->speed_points = malloc(sizeof(struct cpu_speed_point) * + sc->nspeed_points, M_DEVBUF, M_NOWAIT); + for (i = 0; i < sc->nspeed_points; i++) { + sc->speed_points[i].freq = cpu_freq_tbl[i]; + sc->speed_points[i].uvolt = freq_to_voltage(sc, + cpu_freq_tbl[i]); + } +} + +static struct cpu_speed_point * +get_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq) +{ + int i; + + if (sc->speed_points[0].freq >= freq) + return (sc->speed_points + 0); + + for (i = 0; i < sc->nspeed_points - 1; i++) { + if (sc->speed_points[i + 1].freq > freq) + return (sc->speed_points + i); + } + + return (sc->speed_points + sc->nspeed_points - 1); +} + +static int +tegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count) +{ + struct tegra124_cpufreq_softc *sc; + int i, j, max_cnt; + + if (sets == NULL || count == NULL) + return (EINVAL); + + sc = device_get_softc(dev); + memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count)); + + max_cnt = min(sc->nspeed_points, *count); + for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) { + if (sc->cpu_max_freq < sc->speed_points[j].freq) + continue; + sets[i].freq = sc->speed_points[j].freq / 1000000; + sets[i].volts = sc->speed_points[j].uvolt / 1000; + sets[i].lat = sc->latency; + sets[i].dev = dev; + i++; + } + *count = i; + + return (0); +} + +static int +set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq) +{ + struct cpu_speed_point *point; + int rv; + + point = get_speed_point(sc, freq); + + if (sc->act_speed_point->uvolt < point->uvolt) { + /* set cpu voltage */ + rv = regulator_set_voltage(sc->supply_vdd_cpu, + point->uvolt, point->uvolt); + DELAY(10000); + if (rv != 0) + return (rv); + } + rv = clk_set_freq(sc->clk_cpu_g, point->freq, CLK_SET_ROUND_DOWN); + if (rv != 0) { + device_printf(sc->dev, "Can't set CPU clock frequency\n"); + return (rv); + } + + if (sc->act_speed_point->uvolt > point->uvolt) { + /* set cpu voltage */ + rv = regulator_set_voltage(sc->supply_vdd_cpu, + point->uvolt, point->uvolt); + if (rv != 0) + return (rv); + } + + sc->act_speed_point = point; + + return (0); +} + +static int +tegra124_cpufreq_set(device_t dev, const struct cf_setting *cf) +{ + struct tegra124_cpufreq_softc *sc; + uint64_t freq; + int rv; + + if (cf == NULL || cf->freq < 0) + return (EINVAL); + + sc = device_get_softc(dev); + + freq = cf->freq; + if (freq < cpufreq_lowest_freq) + freq = cpufreq_lowest_freq; + freq *= 1000000; + if (freq >= sc->cpu_max_freq) + freq = sc->cpu_max_freq; + rv = set_cpu_freq(sc, freq); + + return (rv); +} + +static int +tegra124_cpufreq_get(device_t dev, struct cf_setting *cf) +{ + struct tegra124_cpufreq_softc *sc; + + if (cf == NULL) + return (EINVAL); + + sc = device_get_softc(dev); + memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); + cf->dev = NULL; + cf->freq = sc->act_speed_point->freq / 1000000; + cf->volts = sc->act_speed_point->uvolt / 1000; + /* Transition latency in us. */ + cf->lat = sc->latency; + /* Driver providing this setting. */ + cf->dev = dev; + + return (0); +} + + +static int +tegra124_cpufreq_type(device_t dev, int *type) +{ + + if (type == NULL) + return (EINVAL); + *type = CPUFREQ_TYPE_ABSOLUTE; + + return (0); +} + +static int +get_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node) +{ + int rv; + device_t parent_dev; + + parent_dev = device_get_parent(sc->dev); + rv = regulator_get_by_ofw_property(parent_dev, "vdd-cpu-supply", + &sc->supply_vdd_cpu); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n"); + return (rv); + } + + rv = clk_get_by_ofw_name(parent_dev, "cpu_g", &sc->clk_cpu_g); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(parent_dev, "cpu_lp", &sc->clk_cpu_lp); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n"); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(parent_dev, "pll_x", &sc->clk_pll_x); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pll_x' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(parent_dev, "pll_p", &sc->clk_pll_p); + if (rv != 0) { + device_printf(parent_dev, "Cannot get 'pll_p' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(parent_dev, "dfll", &sc->clk_dfll); + if (rv != 0) { + /* XXX DPLL is not implemented yet */ +/* + device_printf(sc->dev, "Cannot get 'dfll' clock\n"); + return (ENXIO); +*/ + } + return (0); +} + +static void +tegra124_cpufreq_identify(driver_t *driver, device_t parent) +{ + + if (device_find_child(parent, "tegra124_cpufreq", -1) != NULL) + return; + if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", -1) == NULL) + device_printf(parent, "add child failed\n"); +} + +static int +tegra124_cpufreq_probe(device_t dev) +{ + + if (device_get_unit(dev) != 0) + return (ENXIO); + device_set_desc(dev, "CPU Frequency Control"); + + return (0); +} + +static int +tegra124_cpufreq_attach(device_t dev) +{ + struct tegra124_cpufreq_softc *sc; + uint64_t freq; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->node = ofw_bus_get_node(device_get_parent(dev)); + + sc->process_id = tegra_sku_info.cpu_process_id; + sc->speedo_id = tegra_sku_info.cpu_speedo_id; + sc->speedo_value = tegra_sku_info.cpu_speedo_value; + + /* Tegra 124 */ + /* XXX DPLL is not implemented yet */ + if (1) + sc->cpu_def = &tegra124_cpu_volt_pllx_def; + else + sc->cpu_def = &tegra124_cpu_volt_dpll_def; + + + rv = get_fdt_resources(sc, sc->node); + if (rv != 0) { + return (rv); + } + + build_speed_points(sc); + + rv = clk_get_freq(sc->clk_cpu_g, &freq); + if (rv != 0) { + device_printf(dev, "Can't get CPU clock frequency\n"); + return (rv); + } + if (sc->speedo_id < nitems(cpu_max_freq)) + sc->cpu_max_freq = cpu_max_freq[sc->speedo_id]; + else + sc->cpu_max_freq = cpu_max_freq[0]; + sc->act_speed_point = get_speed_point(sc, freq); + + /* Set safe startup CPU frequency. */ + rv = set_cpu_freq(sc, 1632000000); + if (rv != 0) { + device_printf(dev, "Can't set initial CPU clock frequency\n"); + return (rv); + } + + /* This device is controlled by cpufreq(4). */ + cpufreq_register(dev); + + return (0); +} + +static int +tegra124_cpufreq_detach(device_t dev) +{ + struct tegra124_cpufreq_softc *sc; + + sc = device_get_softc(dev); + cpufreq_unregister(dev); + + if (sc->supply_vdd_cpu != NULL) + regulator_release(sc->supply_vdd_cpu); + + if (sc->clk_cpu_g != NULL) + clk_release(sc->clk_cpu_g); + if (sc->clk_cpu_lp != NULL) + clk_release(sc->clk_cpu_lp); + if (sc->clk_pll_x != NULL) + clk_release(sc->clk_pll_x); + if (sc->clk_pll_p != NULL) + clk_release(sc->clk_pll_p); + if (sc->clk_dfll != NULL) + clk_release(sc->clk_dfll); + return (0); +} + +static device_method_t tegra124_cpufreq_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, tegra124_cpufreq_identify), + DEVMETHOD(device_probe, tegra124_cpufreq_probe), + DEVMETHOD(device_attach, tegra124_cpufreq_attach), + DEVMETHOD(device_detach, tegra124_cpufreq_detach), + + /* cpufreq interface */ + DEVMETHOD(cpufreq_drv_set, tegra124_cpufreq_set), + DEVMETHOD(cpufreq_drv_get, tegra124_cpufreq_get), + DEVMETHOD(cpufreq_drv_settings, tegra124_cpufreq_settings), + DEVMETHOD(cpufreq_drv_type, tegra124_cpufreq_type), + + DEVMETHOD_END +}; + +static devclass_t tegra124_cpufreq_devclass; +static driver_t tegra124_cpufreq_driver = { + "tegra124_cpufreq", + tegra124_cpufreq_methods, + sizeof(struct tegra124_cpufreq_softc), +}; + +DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver, + tegra124_cpufreq_devclass, 0, 0); diff --git a/sys/arm/nvidia/tegra124/tegra124_machdep.c b/sys/arm/nvidia/tegra124/tegra124_machdep.c new file mode 100644 index 0000000..e74a05b --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_machdep.c @@ -0,0 +1,173 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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. + */ + +#define _ARM32_BUS_DMA_PRIVATE +#include "opt_platform.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "platform_if.h" + +#define PMC_PHYSBASE 0x7000e400 +#define PMC_SIZE 0x400 +#define PMC_CONTROL_REG 0x0 +#define PMC_SCRATCH0 0x50 +#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31) +#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30) +#define PMC_SCRATCH0_MODE_RCM (1 << 1) +#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \ + PMC_SCRATCH0_MODE_BOOTLOADER | \ + PMC_SCRATCH0_MODE_RCM) + +struct fdt_fixup_entry fdt_fixup_table[] = { + { NULL, NULL } +}; + +struct arm32_dma_range * +bus_dma_get_range(void) +{ + + return (NULL); +} + +int +bus_dma_get_range_nb(void) +{ + + return (0); +} + +static vm_offset_t +tegra124_lastaddr(platform_t plat) +{ + + return (arm_devmap_lastaddr()); +} + +static int +tegra124_attach(platform_t plat) +{ + + return (0); +} + +static void +tegra124_late_init(platform_t plat) +{ + +} + +/* + * Set up static device mappings. + * + */ +static int +tegra124_devmap_init(platform_t plat) +{ + + arm_devmap_add_entry(0x70000000, 0x01000000); + return (0); +} + +void +cpu_reset(void) +{ + bus_space_handle_t pmc; + uint32_t reg; + + printf("Resetting...\n"); + bus_space_map(fdtbus_bs_tag, PMC_PHYSBASE, PMC_SIZE, 0, &pmc); + + reg = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0); + reg &= PMC_SCRATCH0_MODE_MASK; + bus_space_write_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0, + reg | PMC_SCRATCH0_MODE_BOOTLOADER); /* boot to bootloader */ + bus_space_read_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0); + + reg = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG); + spinlock_enter(); + dsb(); + bus_space_write_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG, reg | 0x10); + bus_space_read_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG); + while(1) + ; + +} + +/* + * Early putc routine for EARLY_PRINTF support. To use, add to kernel config: + * option SOCDEV_PA=0x02000000 + * option SOCDEV_VA=0x02000000 + * option EARLY_PRINTF + */ +#if 0 +static void +tegra124_early_putc(int c) +{ + volatile uint32_t * UART_STAT_REG = (uint32_t *)0x02020098; + volatile uint32_t * UART_TX_REG = (uint32_t *)0x02020040; + const uint32_t UART_TXRDY = (1 << 3); + + while ((*UART_STAT_REG & UART_TXRDY) == 0) + continue; + *UART_TX_REG = c; +} +early_putc_t *early_putc = tegra124_early_putc; +#endif + +static platform_method_t tegra124_methods[] = { + PLATFORMMETHOD(platform_attach, tegra124_attach), + PLATFORMMETHOD(platform_lastaddr, tegra124_lastaddr), + PLATFORMMETHOD(platform_devmap_init, tegra124_devmap_init), + PLATFORMMETHOD(platform_late_init, tegra124_late_init), +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, tegra124_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, tegra124_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; + +FDT_PLATFORM_DEF(tegra124, "Nvidia Jetson-TK1", 0, "nvidia,jetson-tk1"); diff --git a/sys/arm/nvidia/tegra124/tegra124_mp.c b/sys/arm/nvidia/tegra124/tegra124_mp.c new file mode 100644 index 0000000..36b3b7c --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_mp.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define PMC_PHYSBASE 0x7000e400 +#define PMC_SIZE 0x400 +#define PMC_CONTROL_REG 0x0 +#define PMC_PWRGATE_TOGGLE 0x30 +#define PCM_PWRGATE_TOGGLE_START (1 << 8) +#define PMC_PWRGATE_STATUS 0x38 + +#define TEGRA_EXCEPTION_VECTORS_BASE 0x6000F000 /* exception vectors */ +#define TEGRA_EXCEPTION_VECTORS_SIZE 1024 +#define TEGRA_EXCEPTION_VECTOR_ENTRY 0x100 + +void +tegra124_mp_setmaxid(platform_t plat) +{ + int ncpu; + + /* If we've already set the global vars don't bother to do it again. */ + if (mp_ncpus != 0) + return; + + /* Read current CP15 Cache Size ID Register */ + ncpu = cp15_l2ctlr_get(); + ncpu = CPUV7_L2CTLR_NPROC(ncpu); + + mp_ncpus = ncpu; + mp_maxid = ncpu - 1; +} + +void +tegra124_mp_start_ap(platform_t plat) +{ + bus_space_handle_t pmc; + bus_space_handle_t exvec; + int i; + uint32_t val; + uint32_t mask; + + if (bus_space_map(fdtbus_bs_tag, PMC_PHYSBASE, PMC_SIZE, 0, &pmc) != 0) + panic("Couldn't map the PMC\n"); + if (bus_space_map(fdtbus_bs_tag, TEGRA_EXCEPTION_VECTORS_BASE, + TEGRA_EXCEPTION_VECTORS_SIZE, 0, &exvec) != 0) + panic("Couldn't map the exception vectors\n"); + + bus_space_write_4(fdtbus_bs_tag, exvec , TEGRA_EXCEPTION_VECTOR_ENTRY, + pmap_kextract((vm_offset_t)mpentry)); + bus_space_read_4(fdtbus_bs_tag, exvec , TEGRA_EXCEPTION_VECTOR_ENTRY); + + + /* Wait until POWERGATE is ready (max 20 APB cycles). */ + do { + val = bus_space_read_4(fdtbus_bs_tag, pmc, + PMC_PWRGATE_TOGGLE); + } while ((val & PCM_PWRGATE_TOGGLE_START) != 0); + + for (i = 1; i < mp_ncpus; i++) { + val = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_PWRGATE_STATUS); + mask = 1 << (i + 8); /* cpu mask */ + if ((val & mask) == 0) { + /* Wait until POWERGATE is ready (max 20 APB cycles). */ + do { + val = bus_space_read_4(fdtbus_bs_tag, pmc, + PMC_PWRGATE_TOGGLE); + } while ((val & PCM_PWRGATE_TOGGLE_START) != 0); + bus_space_write_4(fdtbus_bs_tag, pmc, + PMC_PWRGATE_TOGGLE, + PCM_PWRGATE_TOGGLE_START | (8 + i)); + + /* Wait until CPU is powered */ + do { + val = bus_space_read_4(fdtbus_bs_tag, pmc, + PMC_PWRGATE_STATUS); + } while ((val & mask) == 0); + } + + } + armv7_sev(); + bus_space_unmap(fdtbus_bs_tag, pmc, PMC_SIZE); + bus_space_unmap(fdtbus_bs_tag, exvec, TEGRA_EXCEPTION_VECTORS_SIZE); +} diff --git a/sys/arm/nvidia/tegra124/tegra124_mp.h b/sys/arm/nvidia/tegra124/tegra124_mp.h new file mode 100644 index 0000000..57785aa --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_mp.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 _TEGRA124_MP_H_ +#define _TEGRA124_MP_H_ + +void tegra124_mp_setmaxid(platform_t plat); +void tegra124_mp_start_ap(platform_t plat); + +#endif /*_TEGRA124_MP_H_*/ \ No newline at end of file diff --git a/sys/arm/nvidia/tegra124/tegra124_pmc.c b/sys/arm/nvidia/tegra124/tegra124_pmc.c new file mode 100644 index 0000000..7ff7307 --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_pmc.c @@ -0,0 +1,566 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define PMC_CNTRL 0x000 +#define PMC_CNTRL_CPUPWRGOOD_SEL_MASK (0x3 << 20) +#define PMC_CNTRL_CPUPWRGOOD_SEL_SHIFT 20 +#define PMC_CNTRL_CPUPWRGOOD_EN (1 << 19) +#define PMC_CNTRL_FUSE_OVERRIDE (1 << 18) +#define PMC_CNTRL_INTR_POLARITY (1 << 17) +#define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) +#define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) +#define PMC_CNTRL_SIDE_EFFECT_LP0 (1 << 14) +#define PMC_CNTRL_AOINIT (1 << 13) +#define PMC_CNTRL_PWRGATE_DIS (1 << 12) +#define PMC_CNTRL_SYSCLK_OE (1 << 11) +#define PMC_CNTRL_SYSCLK_POLARITY (1 << 10) +#define PMC_CNTRL_PWRREQ_OE (1 << 9) +#define PMC_CNTRL_PWRREQ_POLARITY (1 << 8) +#define PMC_CNTRL_BLINK_EN (1 << 7) +#define PMC_CNTRL_GLITCHDET_DIS (1 << 6) +#define PMC_CNTRL_LATCHWAKE_EN (1 << 5) +#define PMC_CNTRL_MAIN_RST (1 << 4) +#define PMC_CNTRL_KBC_RST (1 << 3) +#define PMC_CNTRL_RTC_RST (1 << 2) +#define PMC_CNTRL_RTC_CLK_DIS (1 << 1) +#define PMC_CNTRL_KBC_CLK_DIS (1 << 0) + +#define PMC_DPD_SAMPLE 0x020 + +#define PMC_CLAMP_STATUS 0x02C +#define PMC_CLAMP_STATUS_PARTID(x) (1 << ((x) & 0x1F)) + +#define PMC_PWRGATE_TOGGLE 0x030 +#define PMC_PWRGATE_TOGGLE_START (1 << 8) +#define PMC_PWRGATE_TOGGLE_PARTID(x) (((x) & 0x1F) << 0) + +#define PMC_REMOVE_CLAMPING_CMD 0x034 +#define PMC_REMOVE_CLAMPING_CMD_PARTID(x) (1 << ((x) & 0x1F)) + +#define PMC_PWRGATE_STATUS 0x038 +#define PMC_PWRGATE_STATUS_PARTID(x) (1 << ((x) & 0x1F)) + +#define PMC_SCRATCH0 0x050 +#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31) +#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30) +#define PMC_SCRATCH0_MODE_RCM (1 << 1) +#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \ + PMC_SCRATCH0_MODE_BOOTLOADER | \ + PMC_SCRATCH0_MODE_RCM) + +#define PMC_CPUPWRGOOD_TIMER 0x0c8 +#define PMC_CPUPWROFF_TIMER 0x0cc + +#define PMC_SCRATCH41 0x140 + +#define PMC_SENSOR_CTRL 0x1b0 +#define PMC_SENSOR_CTRL_BLOCK_SCRATCH_WRITE (1 << 2) +#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1) +#define PMC_SENSOR_CTRL_ENABLE_PG (1 << 0) + +#define PMC_IO_DPD_REQ 0x1b8 +#define PMC_IO_DPD_REQ_CODE_IDLE (0 << 30) +#define PMC_IO_DPD_REQ_CODE_OFF (1 << 30) +#define PMC_IO_DPD_REQ_CODE_ON (2 << 30) +#define PMC_IO_DPD_REQ_CODE_MASK (3 << 30) + +#define PMC_IO_DPD_STATUS 0x1bc +#define PMC_IO_DPD_STATUS_HDMI (1 << 28) +#define PMC_IO_DPD2_REQ 0x1c0 +#define PMC_IO_DPD2_STATUS 0x1c4 +#define PMC_IO_DPD2_STATUS_HV (1 << 6) +#define PMC_SEL_DPD_TIM 0x1c8 + +#define PMC_SCRATCH54 0x258 +#define PMC_SCRATCH54_DATA_SHIFT 8 +#define PMC_SCRATCH54_ADDR_SHIFT 0 + +#define PMC_SCRATCH55 0x25c +#define PMC_SCRATCH55_RST_ENABLE (1 << 31) +#define PMC_SCRATCH55_CNTRL_TYPE (1 << 30) +#define PMC_SCRATCH55_CNTRL_ID_SHIFT 27 +#define PMC_SCRATCH55_CNTRL_ID_MASK 0x07 +#define PMC_SCRATCH55_PINMUX_SHIFT 24 +#define PMC_SCRATCH55_PINMUX_MASK 0x07 +#define PMC_SCRATCH55_CHECKSUM_SHIFT 16 +#define PMC_SCRATCH55_CHECKSUM_MASK 0xFF +#define PMC_SCRATCH55_16BITOP (1 << 15) +#define PMC_SCRATCH55_I2CSLV1_SHIFT 0 +#define PMC_SCRATCH55_I2CSLV1_MASK 0x7F + +#define PMC_GPU_RG_CNTRL 0x2d4 + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +#define PMC_LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define PMC_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define PMC_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ + device_get_nameunit(_sc->dev), "tegra124_pmc", MTX_DEF) +#define PMC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx); +#define PMC_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED); +#define PMC_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED); + +struct tegra124_pmc_softc { + device_t dev; + struct resource *mem_res; + clk_t clk; + struct mtx mtx; + + uint32_t rate; + enum tegra_suspend_mode suspend_mode; + uint32_t cpu_good_time; + uint32_t cpu_off_time; + uint32_t core_osc_time; + uint32_t core_pmu_time; + uint32_t core_off_time; + int corereq_high; + int sysclkreq_high; + int combined_req; + int cpu_pwr_good_en; + uint32_t lp0_vec_phys; + uint32_t lp0_vec_size; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-pmc", 1}, + {NULL, 0}, +}; + +static struct tegra124_pmc_softc *pmc_sc; + +static inline struct tegra124_pmc_softc * +tegra124_pmc_get_sc(void) +{ + if (pmc_sc == NULL) + panic("To early call to Tegra PMC driver.\n"); + return (pmc_sc); +} + +static int +tegra124_pmc_set_powergate(struct tegra124_pmc_softc *sc, + enum tegra_powergate_id id, int ena) +{ + uint32_t reg; + int i; + + PMC_LOCK(sc); + + reg = RD4(sc, PMC_PWRGATE_STATUS) & PMC_PWRGATE_STATUS_PARTID(id); + if (((reg != 0) && ena) || ((reg == 0) && !ena)) { + PMC_UNLOCK(sc); + return (0); + } + + for (i = 100; i > 0; i--) { + reg = RD4(sc, PMC_PWRGATE_TOGGLE); + if ((reg & PMC_PWRGATE_TOGGLE_START) == 0) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, + "Timeout when waiting for TOGGLE_START\n"); + + WR4(sc, PMC_PWRGATE_TOGGLE, + PMC_PWRGATE_TOGGLE_START | PMC_PWRGATE_TOGGLE_PARTID(id)); + + for (i = 100; i > 0; i--) { + reg = RD4(sc, PMC_PWRGATE_TOGGLE); + if ((reg & PMC_PWRGATE_TOGGLE_START) == 0) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, + "Timeout when waiting for TOGGLE_START\n"); + PMC_UNLOCK(sc); + return (0); +} + +int +tegra_powergate_remove_clamping(enum tegra_powergate_id id) +{ + struct tegra124_pmc_softc *sc; + uint32_t reg; + enum tegra_powergate_id swid; + int i; + + sc = tegra124_pmc_get_sc(); + + if (id == TEGRA_POWERGATE_3D) { + WR4(sc, PMC_GPU_RG_CNTRL, 0); + return (0); + } + + reg = RD4(sc, PMC_PWRGATE_STATUS); + if ((reg & PMC_PWRGATE_STATUS_PARTID(id)) == 0) + panic("Attempt to remove clamping for unpowered partition.\n"); + + if (id == TEGRA_POWERGATE_PCX) + swid = TEGRA_POWERGATE_VDE; + else if (id == TEGRA_POWERGATE_VDE) + swid = TEGRA_POWERGATE_PCX; + else + swid = id; + WR4(sc, PMC_REMOVE_CLAMPING_CMD, PMC_REMOVE_CLAMPING_CMD_PARTID(swid)); + + for (i = 100; i > 0; i--) { + reg = RD4(sc, PMC_REMOVE_CLAMPING_CMD); + if ((reg & PMC_REMOVE_CLAMPING_CMD_PARTID(swid)) == 0) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, "Timeout when remove clamping\n"); + + reg = RD4(sc, PMC_CLAMP_STATUS); + if ((reg & PMC_CLAMP_STATUS_PARTID(id)) != 0) + panic("Cannot remove clamping\n"); + + return (0); +} + +int +tegra_powergate_is_powered(enum tegra_powergate_id id) +{ + struct tegra124_pmc_softc *sc; + uint32_t reg; + + sc = tegra124_pmc_get_sc(); + + reg = RD4(sc, PMC_PWRGATE_STATUS); + return ((reg & PMC_PWRGATE_STATUS_PARTID(id)) ? 1 : 0); +} + +int +tegra_powergate_power_on(enum tegra_powergate_id id) +{ + struct tegra124_pmc_softc *sc; + int rv, i; + + sc = tegra124_pmc_get_sc(); + + rv = tegra124_pmc_set_powergate(sc, id, 1); + if (rv != 0) { + device_printf(sc->dev, "Cannot set powergate: %d\n", id); + return (rv); + } + + for (i = 100; i > 0; i--) { + if (tegra_powergate_is_powered(id)) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, "Timeout when waiting on power up\n"); + + return (rv); +} + +int +tegra_powergate_power_off(enum tegra_powergate_id id) +{ + struct tegra124_pmc_softc *sc; + int rv, i; + + sc = tegra124_pmc_get_sc(); + + rv = tegra124_pmc_set_powergate(sc, id, 0); + if (rv != 0) { + device_printf(sc->dev, "Cannot set powergate: %d\n", id); + return (rv); + } + for (i = 100; i > 0; i--) { + if (!tegra_powergate_is_powered(id)) + break; + DELAY(1); + } + if (i <= 0) + device_printf(sc->dev, "Timeout when waiting on power off\n"); + + return (rv); +} + +int +tegra_powergate_sequence_power_up(enum tegra_powergate_id id, clk_t clk, + hwreset_t rst) +{ + struct tegra124_pmc_softc *sc; + int rv; + + sc = tegra124_pmc_get_sc(); + + rv = hwreset_assert(rst); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert reset\n"); + return (rv); + } + + rv = clk_stop(clk); + if (rv != 0) { + device_printf(sc->dev, "Cannot stop clock\n"); + goto clk_fail; + } + + rv = tegra_powergate_power_on(id); + if (rv != 0) { + device_printf(sc->dev, "Cannot power on powergate\n"); + goto clk_fail; + } + + rv = clk_enable(clk); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable clock\n"); + goto clk_fail; + } + DELAY(20); + + rv = tegra_powergate_remove_clamping(id); + if (rv != 0) { + device_printf(sc->dev, "Cannot remove clamping\n"); + goto fail; + } + rv = hwreset_deassert(rst); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset reset\n"); + goto fail; + } + return 0; + +fail: + clk_disable(clk); +clk_fail: + hwreset_assert(rst); + tegra_powergate_power_off(id); + return (rv); +} + +static int +tegra124_pmc_parse_fdt(struct tegra124_pmc_softc *sc, phandle_t node) +{ + int rv; + uint32_t tmp; + uint32_t tmparr[2]; + + rv = OF_getencprop(node, "nvidia,suspend-mode", &tmp, sizeof(tmp)); + if (rv > 0) { + switch (tmp) { + case 0: + sc->suspend_mode = TEGRA_SUSPEND_LP0; + break; + + case 1: + sc->suspend_mode = TEGRA_SUSPEND_LP1; + break; + + case 2: + sc->suspend_mode = TEGRA_SUSPEND_LP2; + break; + + default: + sc->suspend_mode = TEGRA_SUSPEND_NONE; + break; + } + } + + rv = OF_getencprop(node, "nvidia,cpu-pwr-good-time", &tmp, sizeof(tmp)); + if (rv > 0) { + sc->cpu_good_time = tmp; + sc->suspend_mode = TEGRA_SUSPEND_NONE; + } + + rv = OF_getencprop(node, "nvidia,cpu-pwr-off-time", &tmp, sizeof(tmp)); + if (rv > 0) { + sc->cpu_off_time = tmp; + sc->suspend_mode = TEGRA_SUSPEND_NONE; + } + + rv = OF_getencprop(node, "nvidia,core-pwr-good-time", tmparr, + sizeof(tmparr)); + if (rv == sizeof(tmparr)) { + sc->core_osc_time = tmparr[0]; + sc->core_pmu_time = tmparr[1]; + sc->suspend_mode = TEGRA_SUSPEND_NONE; + } + + rv = OF_getencprop(node, "nvidia,core-pwr-off-time", &tmp, sizeof(tmp)); + if (rv > 0) { + sc->core_off_time = tmp; + sc->suspend_mode = TEGRA_SUSPEND_NONE; + } + + sc->corereq_high = + OF_hasprop(node, "nvidia,core-power-req-active-high"); + sc->sysclkreq_high = + OF_hasprop(node, "nvidia,sys-clock-req-active-high"); + sc->combined_req = + OF_hasprop(node, "nvidia,combined-power-req"); + sc->cpu_pwr_good_en = + OF_hasprop(node, "nvidia,cpu-pwr-good-en"); + + rv = OF_getencprop(node, "nvidia,lp0-vec", tmparr, sizeof(tmparr)); + if (rv == sizeof(tmparr)) { + + sc->lp0_vec_phys = tmparr[0]; + sc->core_pmu_time = tmparr[1]; + sc->lp0_vec_size = TEGRA_SUSPEND_NONE; + if (sc->suspend_mode == TEGRA_SUSPEND_LP0) + sc->suspend_mode = TEGRA_SUSPEND_LP1; + } + return 0; +} + +static int +tegra124_pmc_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, "Tegra PMC"); + return (BUS_PROBE_DEFAULT); +} + +static int +tegra124_pmc_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static int +tegra124_pmc_attach(device_t dev) +{ + struct tegra124_pmc_softc *sc; + int rid, rv; + uint32_t reg; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + rv = tegra124_pmc_parse_fdt(sc, node); + if (rv != 0) { + device_printf(sc->dev, "Cannot parse FDT data\n"); + return (rv); + } + + rv = clk_get_by_ofw_name(sc->dev, "pclk", &sc->clk); + if (rv != 0) { + device_printf(sc->dev, "Cannot get \"pclk\" clock\n"); + return (ENXIO); + } + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + PMC_LOCK_INIT(sc); + + /* Enable CPU power request. */ + reg = RD4(sc, PMC_CNTRL); + reg |= PMC_CNTRL_CPU_PWRREQ_OE; + WR4(sc, PMC_CNTRL, reg); + + /* Set sysclk output polarity */ + reg = RD4(sc, PMC_CNTRL); + if (sc->sysclkreq_high) + reg &= ~PMC_CNTRL_SYSCLK_POLARITY; + else + reg |= PMC_CNTRL_SYSCLK_POLARITY; + WR4(sc, PMC_CNTRL, reg); + + /* Enable sysclk request. */ + reg = RD4(sc, PMC_CNTRL); + reg |= PMC_CNTRL_SYSCLK_OE; + WR4(sc, PMC_CNTRL, reg); + + /* + * Remove HDMI from deep power down mode. + * XXX mote this to HDMI driver + */ + reg = RD4(sc, PMC_IO_DPD_STATUS); + reg &= ~ PMC_IO_DPD_STATUS_HDMI; + WR4(sc, PMC_IO_DPD_STATUS, reg); + + reg = RD4(sc, PMC_IO_DPD2_STATUS); + reg &= ~ PMC_IO_DPD2_STATUS_HV; + WR4(sc, PMC_IO_DPD2_STATUS, reg); + + if (pmc_sc != NULL) + panic("tegra124_pmc: double driver attach"); + pmc_sc = sc; + return (0); +} + +static device_method_t tegra124_pmc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra124_pmc_probe), + DEVMETHOD(device_attach, tegra124_pmc_attach), + DEVMETHOD(device_detach, tegra124_pmc_detach), + + DEVMETHOD_END +}; + +static driver_t tegra124_pmc_driver = { + "tegra124_pmc", + tegra124_pmc_methods, + sizeof(struct tegra124_pmc_softc), +}; + +static devclass_t tegra124_pmc_devclass; +EARLY_DRIVER_MODULE(tegra124_pmc, simplebus, tegra124_pmc_driver, + tegra124_pmc_devclass, 0, 0, 70); diff --git a/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c b/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c new file mode 100644 index 0000000..e06d4bf --- /dev/null +++ b/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c @@ -0,0 +1,603 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "phy_if.h" + +#define XUSB_PADCTL_USB2_PAD_MUX 0x004 + +#define XUSB_PADCTL_ELPG_PROGRAM 0x01C +#define ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26) +#define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25) +#define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24) + +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040 +#define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19) +#define IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (0xf<< 12) +#define IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1) + +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044 +#define IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6) +#define IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5) +#define IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4) + + +#define XUSB_PADCTL_USB3_PAD_MUX 0x134 + +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138 +#define IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27) +#define IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24) +#define IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3) +#define IOPHY_PLL_S0_CTL1_PLL_RST_L (1 << 1) +#define IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0) + +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13C +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140 +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL4 0x144 + +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148 +#define IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1) +#define IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0) + +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14C +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL3 0x150 +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL4 0x154 +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158 +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15C + +struct lane_cfg { + char *function; + char **lanes; + int iddq; +}; + +struct xusbpadctl_softc { + device_t dev; + struct resource *mem_res; + hwreset_t rst; + int phy_ena_cnt; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-xusb-padctl", 1}, + {NULL, 0}, +}; + +struct padctl_lane { + const char *name; + bus_size_t reg; + uint32_t shift; + uint32_t mask; + int iddq; + char **mux; + int nmux; +}; + +static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"}; +static char *usb_mux[] = {"snps", "xusb"}; +static char *pci_mux[] = {"pcie", "usb3", "sata", "rsvd"}; + +#define LANE(n, r, s, m, i, mx) \ +{ \ + .name = n, \ + .reg = r, \ + .shift = s, \ + .mask = m, \ + .iddq = i, \ + .mux = mx, \ + .nmux = nitems(mx), \ +} + +static const struct padctl_lane lanes_tbl[] = { + LANE("otg-0", XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, -1, otg_mux), + LANE("otg-1", XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, -1, otg_mux), + LANE("otg-2", XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, -1, otg_mux), + LANE("ulpi-0", XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, -1, usb_mux), + LANE("hsic-0", XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, -1, usb_mux), + LANE("hsic-1", XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, -1, usb_mux), + LANE("pcie-0", XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, 1, pci_mux), + LANE("pcie-1", XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, 2, pci_mux), + LANE("pcie-2", XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, 3, pci_mux), + LANE("pcie-3", XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, 4, pci_mux), + LANE("pcie-4", XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, 5, pci_mux), + LANE("sata-0", XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, 6, pci_mux), +}; + +static int +xusbpadctl_mux_function(const struct padctl_lane *lane, char *fnc_name) +{ + int i; + + for (i = 0; i < lane->nmux; i++) { + if (strcmp(fnc_name, lane->mux[i]) == 0) + return (i); + } + + return (-1); +} + +static int +xusbpadctl_config_lane(struct xusbpadctl_softc *sc, char *lane_name, + const struct padctl_lane *lane, struct lane_cfg *cfg) +{ + + int tmp; + uint32_t reg; + + reg = bus_read_4(sc->mem_res, lane->reg); + if (cfg->function != NULL) { + tmp = xusbpadctl_mux_function(lane, cfg->function); + if (tmp == -1) { + device_printf(sc->dev, + "Unknown function %s for lane %s\n", cfg->function, + lane_name); + return (EINVAL); + } + reg &= ~(lane->mask << lane->shift); + reg |= (tmp & lane->mask) << lane->shift; + } + if (cfg->iddq != -1) { + if (lane->iddq == -1) { + device_printf(sc->dev, "Invalid IDDQ for lane %s\n", + lane_name); + return (EINVAL); + } + if (cfg->iddq != 0) + reg &= ~(1 << lane->iddq); + else + reg |= 1 << lane->iddq; + } + + bus_write_4(sc->mem_res, lane->reg, reg); + return (0); +} + +static const struct padctl_lane * +xusbpadctl_search_lane(char *lane_name) +{ + int i; + + for (i = 0; i < nitems(lanes_tbl); i++) { + if (strcmp(lane_name, lanes_tbl[i].name) == 0) + return (&lanes_tbl[i]); + } + + return (NULL); +} + +static int +xusbpadctl_config_node(struct xusbpadctl_softc *sc, char *lane_name, + struct lane_cfg *cfg) +{ + const struct padctl_lane *lane; + int rv; + + lane = xusbpadctl_search_lane(lane_name); + if (lane == NULL) { + device_printf(sc->dev, "Unknown lane: %s\n", lane_name); + return (ENXIO); + } + rv = xusbpadctl_config_lane(sc, lane_name, lane, cfg); + return (rv); +} + +static int +xusbpadctl_read_node(struct xusbpadctl_softc *sc, phandle_t node, + struct lane_cfg *cfg, char **lanes, int *llanes) +{ + int rv; + + *llanes = OF_getprop_alloc(node, "nvidia,lanes", 1, (void **)lanes); + if (*llanes <= 0) + return (ENOENT); + + /* Read function (mux) settings. */ + rv = OF_getprop_alloc(node, "nvidia,function", 1, + (void **)&cfg->function); + if (rv <= 0) + cfg->function = NULL; + /* Read numeric properties. */ + rv = OF_getencprop(node, "nvidia,iddq", &cfg->iddq, + sizeof(cfg->iddq)); + if (rv <= 0) + cfg->iddq = -1; + return (0); +} + +static int +xusbpadctl_process_node(struct xusbpadctl_softc *sc, phandle_t node) +{ + struct lane_cfg cfg; + char *lanes, *lname; + int i, len, llanes, rv; + + rv = xusbpadctl_read_node(sc, node, &cfg, &lanes, &llanes); + if (rv != 0) + return (rv); + + len = 0; + lname = lanes; + do { + i = strlen(lname) + 1; + rv = xusbpadctl_config_node(sc, lname, &cfg); + if (rv != 0) + device_printf(sc->dev, + "Cannot configure lane: %s: %d\n", lname, rv); + + len += i; + lname += i; + } while (len < llanes); + + if (lanes != NULL) + free(lanes, M_OFWPROP); + if (cfg.function != NULL) + free(cfg.function, M_OFWPROP); + return (rv); +} + + +static int +xusbpadctl_pinctrl_cfg(device_t dev, phandle_t cfgxref) +{ + struct xusbpadctl_softc *sc; + phandle_t node, cfgnode; + int rv; + + sc = device_get_softc(dev); + cfgnode = OF_node_from_xref(cfgxref); + + rv = 0; + for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + rv = xusbpadctl_process_node(sc, node); + if (rv != 0) + return (rv); + } + + return (rv); +} + +static int +xusbpadctl_phy_pcie_powerup(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + int i; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL2); + reg |= IOPHY_PLL_P0_CTL2_REFCLKBUF_EN; + reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_EN; + reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_SEL; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + reg |= IOPHY_PLL_P0_CTL1_PLL_RST; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); + DELAY(100); + + for (i = 0; i < 100; i++) { + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) + return (0); + DELAY(10); + } + + return (ETIMEDOUT); +} + + +static int +xusbpadctl_phy_pcie_powerdown(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); + reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); + DELAY(100); + return (0); + +} + +static int +xusbpadctl_phy_sata_powerup(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + int i; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + + for (i = 100; i >= 0; i--) { + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + if (reg & IOPHY_PLL_S0_CTL1_PLL1_LOCKDET) + break; + DELAY(100); + } + if (i <= 0) { + device_printf(sc->dev, "Failed to power up SATA phy\n"); + return (ETIMEDOUT); + } + + return (0); +} + +static int +xusbpadctl_phy_sata_powerdown(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); + reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; + reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); + reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; + reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ; + bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); + DELAY(100); + + return (0); +} + +static int +xusbpadctl_phy_powerup(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + return (0); +} + +static int +xusbpadctl_phy_powerdown(struct xusbpadctl_softc *sc) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); + reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; + bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); + DELAY(100); + + return (0); +} + +static int +xusbpadctl_phy_enable(device_t dev, intptr_t id, bool enable) +{ + struct xusbpadctl_softc *sc; + int rv; + + sc = device_get_softc(dev); + + if ((id != TEGRA_XUSB_PADCTL_PCIE) && + (id != TEGRA_XUSB_PADCTL_SATA)) { + device_printf(dev, "Unknown phy: %d\n", id); + return (ENXIO); + } + + rv = 0; + if (enable) { + if (sc->phy_ena_cnt == 0) { + rv = xusbpadctl_phy_powerup(sc); + if (rv != 0) + return (rv); + } + sc->phy_ena_cnt++; + } + + if (id == TEGRA_XUSB_PADCTL_PCIE) { + if (enable) + rv = xusbpadctl_phy_pcie_powerup(sc); + else + rv = xusbpadctl_phy_pcie_powerdown(sc); + if (rv != 0) + return (rv); + } else if (id == TEGRA_XUSB_PADCTL_SATA) { + if (enable) + rv = xusbpadctl_phy_sata_powerup(sc); + else + rv = xusbpadctl_phy_sata_powerdown(sc); + if (rv != 0) + return (rv); + } + if (!enable) { + if (sc->phy_ena_cnt == 1) { + rv = xusbpadctl_phy_powerdown(sc); + if (rv != 0) + return (rv); + } + sc->phy_ena_cnt--; + } + + return (0); +} + +static int +xusbpadctl_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, "Tegra XUSB phy"); + return (BUS_PROBE_DEFAULT); +} + +static int +xusbpadctl_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static int +xusbpadctl_attach(device_t dev) +{ + struct xusbpadctl_softc * sc; + int rid, rv; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + rv = hwreset_get_by_ofw_name(dev, "padctl", &sc->rst); + if (rv != 0) { + device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv); + return (rv); + } + rv = hwreset_deassert(sc->rst); + if (rv != 0) { + device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv); + return (rv); + } + + /* Register as a pinctrl device and use default configuration */ + fdt_pinctrl_register(dev, NULL); + fdt_pinctrl_configure_by_name(dev, "default"); + phy_register_provider(dev); + + return (0); +} + + +static device_method_t tegra_xusbpadctl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, xusbpadctl_probe), + DEVMETHOD(device_attach, xusbpadctl_attach), + DEVMETHOD(device_detach, xusbpadctl_detach), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure, xusbpadctl_pinctrl_cfg), + + /* phy interface */ + DEVMETHOD(phy_enable, xusbpadctl_phy_enable), + + DEVMETHOD_END +}; + +static driver_t tegra_xusbpadctl_driver = { + "tegra_xusbpadctl", + tegra_xusbpadctl_methods, + sizeof(struct xusbpadctl_softc), +}; + +static devclass_t tegra_xusbpadctl_devclass; + +EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver, + tegra_xusbpadctl_devclass, 0, 0, 73); diff --git a/sys/arm/nvidia/tegra_abpmisc.c b/sys/arm/nvidia/tegra_abpmisc.c new file mode 100644 index 0000000..83ee44f --- /dev/null +++ b/sys/arm/nvidia/tegra_abpmisc.c @@ -0,0 +1,194 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * SoC misc configuration and indentification driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#define PMC_STRAPPING_OPT_A 0 /* 0x464 */ + +#define PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT 4 +#define PMC_STRAPPING_OPT_A_RAM_CODE_MASK_LONG \ + (0xf << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT) +#define PMC_STRAPPING_OPT_A_RAM_CODE_MASK_SHORT \ + (0x3 << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT) + + +#define ABP_RD4(_sc, _r) bus_read_4((_sc)->abp_misc_res, (_r)) +#define STR_RD4(_sc, _r) bus_read_4((_sc)->strap_opt_res, (_r)) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-apbmisc", 1}, + {NULL, 0} +}; + +struct tegra_abpmisc_softc { + device_t dev; + + struct resource *abp_misc_res; + struct resource *strap_opt_res; +}; + +static struct tegra_abpmisc_softc *dev_sc; + +static void +tegra_abpmisc_read_revision(struct tegra_abpmisc_softc *sc) +{ + uint32_t id, chip_id, minor_rev; + int rev; + + id = ABP_RD4(sc, 4); + chip_id = (id >> 8) & 0xff; + minor_rev = (id >> 16) & 0xf; + + switch (minor_rev) { + case 1: + rev = TEGRA_REVISION_A01; + break; + case 2: + rev = TEGRA_REVISION_A02; + break; + case 3: + rev = TEGRA_REVISION_A03; + break; + case 4: + rev = TEGRA_REVISION_A04; + break; + default: + rev = TEGRA_REVISION_UNKNOWN; + } + + tegra_sku_info.chip_id = chip_id; + tegra_sku_info.revision = rev; +} + +static int +tegra_abpmisc_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 int +tegra_abpmisc_attach(device_t dev) +{ + int rid; + struct tegra_abpmisc_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->abp_misc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->abp_misc_res == NULL) { + device_printf(dev, "Cannot map ABP misc registers.\n"); + goto fail; + } + + rid = 1; + sc->strap_opt_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->strap_opt_res == NULL) { + device_printf(dev, "Cannot map strapping options registers.\n"); + goto fail; + } + + tegra_abpmisc_read_revision(sc); + + /* XXX - Hack - address collision with pinmux. */ + if (sc->abp_misc_res != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res); + sc->abp_misc_res = NULL; + } + + dev_sc = sc; + return (bus_generic_attach(dev)); + +fail: + if (sc->abp_misc_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res); + if (sc->strap_opt_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->strap_opt_res); + + return (ENXIO); +} + +static int +tegra_abpmisc_detach(device_t dev) +{ + struct tegra_abpmisc_softc *sc; + + sc = device_get_softc(dev); + if (sc->abp_misc_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res); + if (sc->strap_opt_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->strap_opt_res); + return (bus_generic_detach(dev)); +} + +static device_method_t tegra_abpmisc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_abpmisc_probe), + DEVMETHOD(device_attach, tegra_abpmisc_attach), + DEVMETHOD(device_detach, tegra_abpmisc_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(tegra_abpmisc, tegra_abpmisc_driver, tegra_abpmisc_methods, + sizeof(struct tegra_abpmisc_softc)); +static devclass_t tegra_abpmisc_devclass; +EARLY_DRIVER_MODULE(tegra_abpmisc, simplebus, tegra_abpmisc_driver, + tegra_abpmisc_devclass, 0, 0, BUS_PASS_TIMER); diff --git a/sys/arm/nvidia/tegra_ahci.c b/sys/arm/nvidia/tegra_ahci.c new file mode 100644 index 0000000..4afba17 --- /dev/null +++ b/sys/arm/nvidia/tegra_ahci.c @@ -0,0 +1,627 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * AHCI driver for Tegra SoCs. + */ +#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 AHCI_WR4(_sc, _r, _v) bus_write_4((_sc)->ctlr.r_mem, (_r), (_v)) +#define AHCI_RD4(_sc, _r) bus_read_4((_sc)->ctlr.r_mem, (_r)) +#define SATA_WR4(_sc, _r, _v) bus_write_4((_sc)->sata_mem, (_r), (_v)) +#define SATA_RD4(_sc, _r) bus_read_4((_sc)->sata_mem, (_r)) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-ahci", 1}, + {NULL, 0} +}; + +struct tegra_ahci_sc { + struct ahci_controller ctlr; /* Must be first */ + device_t dev; + struct resource *sata_mem; + clk_t clk_sata; + clk_t clk_sata_oob; + clk_t clk_pll_e; + clk_t clk_cml; + hwreset_t hwreset_sata; + hwreset_t hwreset_sata_oob; + hwreset_t hwreset_sata_cold; + regulator_t supply_hvdd; + regulator_t supply_vddio; + regulator_t supply_avdd; + regulator_t supply_target_5v; + regulator_t supply_target_12v; + phy_t phy; +}; + +struct sata_pad_calibration { + uint32_t gen1_tx_amp; + uint32_t gen1_tx_peak; + uint32_t gen2_tx_amp; + uint32_t gen2_tx_peak; +}; + +static const struct sata_pad_calibration tegra124_pad_calibration[] = { + {0x18, 0x04, 0x18, 0x0a}, + {0x0e, 0x04, 0x14, 0x0a}, + {0x0e, 0x07, 0x1a, 0x0e}, + {0x14, 0x0e, 0x1a, 0x0e}, +}; + +#define SATA_CONFIGURATION 0x180 +#define SATA_CONFIGURATION_EN_FPCI (1 << 0) + +#define SATA_FPCI_BAR5 0x94 +#define SATA_FPCI_BAR5_START_SHIFT 4 + +#define SATA_INTR_MASK 0x188 +#define SATA_INTR_MASK_IP_INT_MASK (1 << 16) + +#define SCFG_OFFSET 0x1000 + +#define T_SATA0_CFG_1 0x04 +#define T_SATA0_CFG_1_IO_SPACE (1 << 0) +#define T_SATA0_CFG_1_MEMORY_SPACE (1 << 1) +#define T_SATA0_CFG_1_BUS_MASTER (1 << 2) +#define T_SATA0_CFG_1_SERR (1 << 8) + +#define T_SATA0_CFG_9 0x24 +#define T_SATA0_CFG_9_BASE_ADDRESS_SHIFT 13 + +#define T_SATA0_AHCI_HBA_CAP_BKDR 0x300 +#define T_SATA0_BKDOOR_CC 0x4a4 +#define T_SATA0_CFG_SATA 0x54c +#define T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN (1 << 12) + +#define T_SATA0_CFG_MISC 0x550 +#define T_SATA0_INDEX 0x680 + +#define T_SATA0_CHX_PHY_CTRL1_GEN1 0x690 +#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK 0xff +#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT 8 +#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK 0xff +#define T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT 0 + + +#define T_SATA0_CHX_PHY_CTRL1_GEN2 0x694 +#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK 0xff +#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT 12 +#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK 0xff +#define T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT 0 + +#define T_SATA0_CHX_PHY_CTRL2 0x69c +#define T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1 0x23 + +#define T_SATA0_CHX_PHY_CTRL11 0x6d0 +#define T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ (0x2800 << 16) + +#define FUSE_SATA_CALIB 0x124 +#define FUSE_SATA_CALIB_MASK 0x3 + + +#define SATA_AUX_MISC_CNTL 0x1108 +#define SATA_AUX_PAD_PLL_CTRL_0 0x1120 +#define SATA_AUX_PAD_PLL_CTRL_1 0x1124 +#define SATA_AUX_PAD_PLL_CTRL_2 0x1128 +#define SATA_AUX_PAD_PLL_CTRL_3 0x112c + +#define T_AHCI_HBA_CCC_PORTS 0x0018 +#define T_AHCI_HBA_CAP_BKDR 0x00A0 +#define T_AHCI_HBA_CAP_BKDR_S64A (1 << 31) +#define T_AHCI_HBA_CAP_BKDR_SNCQ (1 << 30) +#define T_AHCI_HBA_CAP_BKDR_SSNTF (1 << 29) +#define T_AHCI_HBA_CAP_BKDR_SMPS (1 << 28) +#define T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP (1 << 27) +#define T_AHCI_HBA_CAP_BKDR_SALP (1 << 26) +#define T_AHCI_HBA_CAP_BKDR_SAL (1 << 25) +#define T_AHCI_HBA_CAP_BKDR_SUPP_CLO (1 << 24) +#define T_AHCI_HBA_CAP_BKDR_INTF_SPD_SUPP(x) (((x) & 0xF) << 20) +#define T_AHCI_HBA_CAP_BKDR_SUPP_NONZERO_OFFSET (1 << 19) +#define T_AHCI_HBA_CAP_BKDR_SUPP_AHCI_ONLY (1 << 18) +#define T_AHCI_HBA_CAP_BKDR_SUPP_PM (1 << 17) +#define T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING (1 << 16) +#define T_AHCI_HBA_CAP_BKDR_PIO_MULT_DRQ_BLK (1 << 15) +#define T_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP (1 << 14) +#define T_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP (1 << 13) +#define T_AHCI_HBA_CAP_BKDR_NUM_CMD_SLOTS(x) (((x) & 0x1F) << 8) +#define T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING (1 << 7) +#define T_AHCI_HBA_CAP_BKDR_ENCL_MGMT_SUPP (1 << 6) +#define T_AHCI_HBA_CAP_BKDR_EXT_SATA (1 << 5) +#define T_AHCI_HBA_CAP_BKDR_NUM_PORTS(x) (((x) & 0xF) << 0) + +#define T_AHCI_PORT_BKDR 0x0170 + +#define T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE_VAL(x) (((x) & 0xFF) << 24) +#define T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE_VAL(x) (((x) & 0x1F) << 16) +#define T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE (1 << 15) +#define T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE (1 << 14) +#define T_AHCI_PORT_BKDR_PXDEVSLP_DM(x) (((x) & 0xF) << 10) +#define T_AHCI_PORT_BKDR_PORT_UNCONNECTED (1 << 9) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_CLAMP_THIS_CH (1 << 8) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_UNCLAMP (1 << 7) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_CLAMP (1 << 6) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_UNCLAMP (1 << 5) +#define T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_CLAMP (1 << 4) +#define T_AHCI_PORT_BKDR_HOTPLUG_CAP (1 << 3) +#define T_AHCI_PORT_BKDR_MECH_SWITCH (1 << 2) +#define T_AHCI_PORT_BKDR_COLD_PRSN_DET (1 << 1) +#define T_AHCI_PORT_BKDR_EXT_SATA_SUPP (1 << 0) + +static int +get_fdt_resources(struct tegra_ahci_sc *sc, phandle_t node) +{ + int rv; + + + rv = regulator_get_by_ofw_property(sc->dev, "hvdd-supply", + &sc->supply_hvdd ); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'hvdd' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "vddio-supply", + &sc->supply_vddio); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'vddio' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "avdd-supply", + &sc->supply_avdd); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'avdd' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "target-5v-supply", + &sc->supply_target_5v); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'target-5v' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "target-12v-supply", + &sc->supply_target_12v); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'target-12v' regulator\n"); + return (ENXIO); + } + + rv = hwreset_get_by_ofw_name(sc->dev, "sata", &sc->hwreset_sata ); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "sata-oob", + &sc->hwreset_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata oob' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "sata-cold", + &sc->hwreset_sata_cold); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata cold' reset\n"); + return (ENXIO); + } + + rv = phy_get_by_ofw_name(sc->dev, "sata-phy", &sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata' phy\n"); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(sc->dev, "sata", &sc->clk_sata); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "sata-oob", &sc->clk_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sata oob' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "cml1", &sc->clk_cml); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'cml1' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "pll_e", &sc->clk_pll_e); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pll_e' clock\n"); + return (ENXIO); + } + return (0); +} + +static int +enable_fdt_resources(struct tegra_ahci_sc *sc) +{ + int rv; + + rv = regulator_enable(sc->supply_hvdd); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'hvdd' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_vddio); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'vddio' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_avdd); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'avdd' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_target_5v); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'target-5v' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_target_12v); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'sc->target-12v' regulator\n"); + return (rv); + } + + /* Stop clocks */ + clk_stop(sc->clk_sata); + clk_stop(sc->clk_sata_oob); + tegra_powergate_power_off(TEGRA_POWERGATE_SAX); + + rv = hwreset_assert(sc->hwreset_sata); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'sata' reset\n"); + return (rv); + } + rv = hwreset_assert(sc->hwreset_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'sata oob' reset\n"); + return (rv); + } + + rv = hwreset_assert(sc->hwreset_sata_cold); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'sata cold' reset\n"); + return (rv); + } + rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_SAX, + sc->clk_sata, sc->hwreset_sata); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'SAX' powergate\n"); + return (rv); + } + + rv = clk_enable(sc->clk_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'sata oob' clock\n"); + return (rv); + } + rv = clk_enable(sc->clk_cml); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'cml' clock\n"); + return (rv); + } + rv = clk_enable(sc->clk_pll_e); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'pll e' clock\n"); + return (rv); + } + + rv = hwreset_deassert(sc->hwreset_sata_cold); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset 'sata cold' reset\n"); + return (rv); + } + rv = hwreset_deassert(sc->hwreset_sata_oob); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset 'sata oob' reset\n"); + return (rv); + } + + rv = phy_enable(sc->dev, sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable SATA phy\n"); + return (rv); + } + + return (0); +} + +static int +tegra_ahci_ctrl_init(struct tegra_ahci_sc *sc) +{ + uint32_t val; + const struct sata_pad_calibration *calib; + + val = SATA_RD4(sc, SATA_CONFIGURATION); + val |= SATA_CONFIGURATION_EN_FPCI; + SATA_WR4(sc, SATA_CONFIGURATION, val); + + + /* Pad calibration. */ + val = tegra_fuse_read_4(FUSE_SATA_CALIB); + calib = tegra124_pad_calibration + (val & FUSE_SATA_CALIB_MASK); + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 1); + + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1); + val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK << + T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT); + val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK << + T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT); + val |= calib->gen1_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT; + val |= calib->gen1_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1, val); + + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2); + val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK << + T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT); + val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK << + T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT); + val |= calib->gen2_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT; + val |= calib->gen2_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2, val); + + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11, + T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ); + + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2, + T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1); + + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 0); + + /* Set device ID. */ + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA); + val |= T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val); + + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_BKDOOR_CC, 0x01060100); + + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA); + val &= ~T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val); + + /* Enable IO & memory access, bus master mode */ + val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_1); + val |= T_SATA0_CFG_1_IO_SPACE; + val |= T_SATA0_CFG_1_MEMORY_SPACE; + val |= T_SATA0_CFG_1_BUS_MASTER; + val |= T_SATA0_CFG_1_SERR; + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_1, val); + + /* SATA MMIO. */ + SATA_WR4(sc, SATA_FPCI_BAR5, 0x10000 << SATA_FPCI_BAR5_START_SHIFT); + /* AHCI bar */ + SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_9, + 0x08000 << T_SATA0_CFG_9_BASE_ADDRESS_SHIFT); + + /* Unmask interrupts. */ + val = SATA_RD4(sc, SATA_INTR_MASK); + val |= SATA_INTR_MASK_IP_INT_MASK; + SATA_WR4(sc, SATA_INTR_MASK, val); + + return (0); +} + +static int +tegra_ahci_ctlr_reset(device_t dev) +{ + struct tegra_ahci_sc *sc; + int rv; + uint32_t reg; + + sc = device_get_softc(dev); + rv = ahci_ctlr_reset(dev); + if (rv != 0) + return (0); + AHCI_WR4(sc, T_AHCI_HBA_CCC_PORTS, 1); + + /* Overwrite AHCI capabilites. */ + reg = AHCI_RD4(sc, T_AHCI_HBA_CAP_BKDR); + reg &= ~T_AHCI_HBA_CAP_BKDR_NUM_PORTS(~0); + reg |= T_AHCI_HBA_CAP_BKDR_NUM_PORTS(0); + reg |= T_AHCI_HBA_CAP_BKDR_EXT_SATA; + reg |= T_AHCI_HBA_CAP_BKDR_ENCL_MGMT_SUPP; + reg |= T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING; + reg |= T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING; + reg |= T_AHCI_HBA_CAP_BKDR_SUPP_PM; + reg |= T_AHCI_HBA_CAP_BKDR_SUPP_CLO; + reg |= T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP; + AHCI_WR4(sc, T_AHCI_HBA_CAP_BKDR, reg); + + /* Overwrite AHCI portcapabilites. */ + reg = AHCI_RD4(sc, T_AHCI_PORT_BKDR); + reg |= T_AHCI_PORT_BKDR_COLD_PRSN_DET; + reg |= T_AHCI_PORT_BKDR_HOTPLUG_CAP; + reg |= T_AHCI_PORT_BKDR_EXT_SATA_SUPP; + AHCI_WR4(sc, T_AHCI_PORT_BKDR, reg); + + return (0); +} + +static int +tegra_ahci_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_copy(dev, "AHCI SATA controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_ahci_attach(device_t dev) +{ + struct tegra_ahci_sc *sc; + struct ahci_controller *ctlr; + phandle_t node; + int rv, rid; + + sc = device_get_softc(dev); + sc->dev = dev; + ctlr = &sc->ctlr; + node = ofw_bus_get_node(dev); + + ctlr->r_rid = 0; + ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &ctlr->r_rid, RF_ACTIVE); + if (ctlr->r_mem == NULL) + return (ENXIO); + + rid = 1; + sc->sata_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (sc->sata_mem == NULL) { + rv = ENXIO; + goto fail; + } + rv = get_fdt_resources(sc, node); + if (rv != 0) { + device_printf(sc->dev, "Failed to allocate FDT resource(s)\n"); + goto fail; + } + + rv = enable_fdt_resources(sc); + if (rv != 0) { + device_printf(sc->dev, "Failed to enable FDT resource(s)\n"); + goto fail; + } + rv = tegra_ahci_ctrl_init(sc); + if (rv != 0) { + device_printf(sc->dev, "Failed to initialize controller)\n"); + goto fail; + } + + /* Setup controller defaults. */ + ctlr->msi = 0; + ctlr->numirqs = 1; + ctlr->ccc = 0; + + /* Reset controller. */ + rv = tegra_ahci_ctlr_reset(dev); + if (rv != 0) + goto fail; + rv = ahci_attach(dev); + return (rv); + +fail: + /* XXX FDT stuff */ + if (sc->sata_mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->sata_mem); + if (ctlr->r_mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, + ctlr->r_mem); + return (rv); +} + +static int +tegra_ahci_detach(device_t dev) +{ + + ahci_detach(dev); + return (0); +} + +static int +tegra_ahci_suspend(device_t dev) +{ + struct tegra_ahci_sc *sc = device_get_softc(dev); + + bus_generic_suspend(dev); + /* Disable interupts, so the state change(s) doesn't trigger. */ + ATA_OUTL(sc->ctlr.r_mem, AHCI_GHC, + ATA_INL(sc->ctlr.r_mem, AHCI_GHC) & (~AHCI_GHC_IE)); + return (0); +} + +static int +tegra_ahci_resume(device_t dev) +{ + int res; + + if ((res = tegra_ahci_ctlr_reset(dev)) != 0) + return (res); + ahci_ctlr_setup(dev); + return (bus_generic_resume(dev)); +} + +devclass_t genahci_devclass; +static device_method_t genahci_methods[] = { + DEVMETHOD(device_probe, tegra_ahci_probe), + DEVMETHOD(device_attach, tegra_ahci_attach), + DEVMETHOD(device_detach, tegra_ahci_detach), + DEVMETHOD(device_suspend, tegra_ahci_suspend), + DEVMETHOD(device_resume, tegra_ahci_resume), + DEVMETHOD(bus_print_child, ahci_print_child), + DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), + DEVMETHOD(bus_release_resource, ahci_release_resource), + DEVMETHOD(bus_setup_intr, ahci_setup_intr), + DEVMETHOD(bus_teardown_intr, ahci_teardown_intr), + DEVMETHOD(bus_child_location_str, ahci_child_location_str), + DEVMETHOD(bus_get_dma_tag, ahci_get_dma_tag), + + DEVMETHOD_END +}; +static driver_t genahci_driver = { + "ahci", + genahci_methods, + sizeof(struct tegra_ahci_sc) +}; +DRIVER_MODULE(genahci, simplebus, genahci_driver, genahci_devclass, NULL, NULL); diff --git a/sys/arm/nvidia/tegra_efuse.c b/sys/arm/nvidia/tegra_efuse.c new file mode 100644 index 0000000..ae3f9ef --- /dev/null +++ b/sys/arm/nvidia/tegra_efuse.c @@ -0,0 +1,368 @@ +/*- + * Copyright (c) 2015 Michal Meloun + * 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 + + +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_sc)->fuse_begin + (_r)) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-efuse", 1}, + {NULL, 0} +}; + +struct tegra_efuse_softc { + device_t dev; + struct resource *mem_res; + + int fuse_begin; + clk_t clk; + hwreset_t reset; +}; +struct tegra_efuse_softc *dev_sc; + +struct tegra_sku_info tegra_sku_info; +static char *tegra_rev_name[] = { + [TEGRA_REVISION_UNKNOWN] = "unknown", + [TEGRA_REVISION_A01] = "A01", + [TEGRA_REVISION_A02] = "A02", + [TEGRA_REVISION_A03] = "A03", + [TEGRA_REVISION_A03p] = "A03 prime", + [TEGRA_REVISION_A04] = "A04", +}; + +/* Tegra30 and later */ +#define FUSE_VENDOR_CODE 0x100 +#define FUSE_FAB_CODE 0x104 +#define FUSE_LOT_CODE_0 0x108 +#define FUSE_LOT_CODE_1 0x10c +#define FUSE_WAFER_ID 0x110 +#define FUSE_X_COORDINATE 0x114 +#define FUSE_Y_COORDINATE 0x118 + +/* ---------------------- Tegra 124 specific code & data --------------- */ +#define TEGRA124_FUSE_BEGIN 0x100 + +#define TEGRA124_CPU_PROCESS_CORNERS 2 +#define TEGRA124_GPU_PROCESS_CORNERS 2 +#define TEGRA124_SOC_PROCESS_CORNERS 2 + +#define TEGRA124_FUSE_SKU_INFO 0x10 +#define TEGRA124_FUSE_CPU_SPEEDO_0 0x14 +#define TEGRA124_FUSE_CPU_IDDQ 0x18 +#define TEGRA124_FUSE_FT_REV 0x28 +#define TEGRA124_FUSE_CPU_SPEEDO_1 0x2c +#define TEGRA124_FUSE_CPU_SPEEDO_2 0x30 +#define TEGRA124_FUSE_SOC_SPEEDO_0 0x34 +#define TEGRA124_FUSE_SOC_SPEEDO_1 0x38 +#define TEGRA124_FUSE_SOC_SPEEDO_2 0x3c +#define TEGRA124_FUSE_SOC_IDDQ 0x40 +#define TEGRA124_FUSE_GPU_IDDQ 0x128 + +enum { + TEGRA124_THRESHOLD_INDEX_0, + TEGRA124_THRESHOLD_INDEX_1, + TEGRA124_THRESHOLD_INDEX_COUNT, +}; + +static uint32_t tegra124_cpu_process_speedos[][TEGRA124_CPU_PROCESS_CORNERS] = +{ + {2190, UINT_MAX}, + {0, UINT_MAX}, +}; + +static uint32_t tegra124_gpu_process_speedos[][TEGRA124_GPU_PROCESS_CORNERS] = +{ + {1965, UINT_MAX}, + {0, UINT_MAX}, +}; + +static uint32_t tegra124_soc_process_speedos[][TEGRA124_SOC_PROCESS_CORNERS] = +{ + {2101, UINT_MAX}, + {0, UINT_MAX}, +}; + +static void +tegra124_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc, + struct tegra_sku_info *sku, int *threshold) +{ + + /* Assign to default */ + sku->cpu_speedo_id = 0; + sku->soc_speedo_id = 0; + sku->gpu_speedo_id = 0; + *threshold = TEGRA124_THRESHOLD_INDEX_0; + + switch (sku->sku_id) { + case 0x00: /* Eng sku */ + case 0x0F: + case 0x23: + /* Using the default */ + break; + case 0x83: + sku->cpu_speedo_id = 2; + break; + + case 0x1F: + case 0x87: + case 0x27: + sku->cpu_speedo_id = 2; + sku->soc_speedo_id = 0; + sku->gpu_speedo_id = 1; + *threshold = TEGRA124_THRESHOLD_INDEX_0; + break; + case 0x81: + case 0x21: + case 0x07: + sku->cpu_speedo_id = 1; + sku->soc_speedo_id = 1; + sku->gpu_speedo_id = 1; + *threshold = TEGRA124_THRESHOLD_INDEX_1; + break; + case 0x49: + case 0x4A: + case 0x48: + sku->cpu_speedo_id = 4; + sku->soc_speedo_id = 2; + sku->gpu_speedo_id = 3; + *threshold = TEGRA124_THRESHOLD_INDEX_1; + break; + default: + device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id); + break; + } +} + + +static void +tegra124_init_speedo(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku) +{ + int i, threshold; + + sku->sku_id = RD4(sc, TEGRA124_FUSE_SKU_INFO); + sku->soc_iddq_value = RD4(sc, TEGRA124_FUSE_SOC_IDDQ); + sku->cpu_iddq_value = RD4(sc, TEGRA124_FUSE_CPU_IDDQ); + sku->gpu_iddq_value = RD4(sc, TEGRA124_FUSE_GPU_IDDQ); + sku->soc_speedo_value = RD4(sc, TEGRA124_FUSE_SOC_SPEEDO_0); + sku->cpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_0); + sku->gpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_2); + + if (sku->cpu_speedo_value == 0) { + device_printf(sc->dev, "CPU Speedo value is not fused.\n"); + return; + } + + tegra124_rev_sku_to_speedo_ids(sc, sku, &threshold); + + for (i = 0; i < TEGRA124_SOC_PROCESS_CORNERS; i++) { + if (sku->soc_speedo_value < + tegra124_soc_process_speedos[threshold][i]) + break; + } + sku->soc_process_id = i; + + for (i = 0; i < TEGRA124_CPU_PROCESS_CORNERS; i++) { + if (sku->cpu_speedo_value < + tegra124_cpu_process_speedos[threshold][i]) + break; + } + sku->cpu_process_id = i; + + for (i = 0; i < TEGRA124_GPU_PROCESS_CORNERS; i++) { + if (sku->gpu_speedo_value < + tegra124_gpu_process_speedos[threshold][i]) + break; + } + sku->gpu_process_id = i; + +} + +/* ----------------- End of Tegra 124 specific code & data --------------- */ + +uint32_t +tegra_fuse_read_4(int addr) { + + if (dev_sc == NULL) + panic("tegra_fuse_read_4 called too early"); + return (RD4(dev_sc, addr)); +} + + +static void +tegra_efuse_dump_sku() +{ + printf(" TEGRA SKU Info:\n"); + printf(" chip_id: %u\n", tegra_sku_info.chip_id); + printf(" sku_id: %u\n", tegra_sku_info.sku_id); + printf(" cpu_process_id: %u\n", tegra_sku_info.cpu_process_id); + printf(" cpu_speedo_id: %u\n", tegra_sku_info.cpu_speedo_id); + printf(" cpu_speedo_value: %u\n", tegra_sku_info.cpu_speedo_value); + printf(" cpu_iddq_value: %u\n", tegra_sku_info.cpu_iddq_value); + printf(" soc_process_id: %u\n", tegra_sku_info.soc_process_id); + printf(" soc_speedo_id: %u\n", tegra_sku_info.soc_speedo_id); + printf(" soc_speedo_value: %u\n", tegra_sku_info.soc_speedo_value); + printf(" soc_iddq_value: %u\n", tegra_sku_info.soc_iddq_value); + printf(" gpu_process_id: %u\n", tegra_sku_info.gpu_process_id); + printf(" gpu_speedo_id: %u\n", tegra_sku_info.gpu_speedo_id); + printf(" gpu_speedo_value: %u\n", tegra_sku_info.gpu_speedo_value); + printf(" gpu_iddq_value: %u\n", tegra_sku_info.gpu_iddq_value); + printf(" revision: %s\n", tegra_rev_name[tegra_sku_info.revision]); +} + +static int +tegra_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 int +tegra_efuse_attach(device_t dev) +{ + int rv, rid; + phandle_t node; + struct tegra_efuse_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + /* Get the memory resource for the register mapping. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot map registers.\n"); + rv = ENXIO; + goto fail; + } + + /* OFW resources. */ + rv = clk_get_by_ofw_name(dev, "fuse", &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get fuse clock: %d\n", rv); + goto fail; + } + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock: %d\n", rv); + goto fail; + } + rv = hwreset_get_by_ofw_name(sc->dev, "fuse", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get fuse reset\n"); + goto fail; + } + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot clear reset\n"); + goto fail; + } + + /* Tegra124 specific init. */ + sc->fuse_begin = TEGRA124_FUSE_BEGIN; + tegra124_init_speedo(sc, &tegra_sku_info); + + dev_sc = sc; + + if (bootverbose) + tegra_efuse_dump_sku(); + return (bus_generic_attach(dev)); + +fail: + dev_sc = NULL; + if (sc->clk != NULL) + clk_release(sc->clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (rv); +} + +static int +tegra_efuse_detach(device_t dev) +{ + struct tegra_efuse_softc *sc; + + sc = device_get_softc(dev); + dev_sc = NULL; + if (sc->clk != NULL) + clk_release(sc->clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (bus_generic_detach(dev)); +} + +static device_method_t tegra_efuse_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_efuse_probe), + DEVMETHOD(device_attach, tegra_efuse_attach), + DEVMETHOD(device_detach, tegra_efuse_detach), + + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(tegra_efuse, tegra_efuse_driver, tegra_efuse_methods, + sizeof(struct tegra_efuse_softc)); +static devclass_t tegra_efuse_devclass; +EARLY_DRIVER_MODULE(tegra_efuse, simplebus, tegra_efuse_driver, + tegra_efuse_devclass, 0, 0, BUS_PASS_TIMER); diff --git a/sys/arm/nvidia/tegra_efuse.h b/sys/arm/nvidia/tegra_efuse.h new file mode 100644 index 0000000..36804d06 --- /dev/null +++ b/sys/arm/nvidia/tegra_efuse.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 _TEGRA_EFUSE_H_ + +enum tegra_revision { + TEGRA_REVISION_UNKNOWN = 0, + TEGRA_REVISION_A01, + TEGRA_REVISION_A02, + TEGRA_REVISION_A03, + TEGRA_REVISION_A03p, + TEGRA_REVISION_A04, +}; + +struct tegra_sku_info { + u_int chip_id; + u_int sku_id; + u_int cpu_process_id; + u_int cpu_speedo_id; + u_int cpu_speedo_value; + u_int cpu_iddq_value; + u_int soc_process_id; + u_int soc_speedo_id; + u_int soc_speedo_value; + u_int soc_iddq_value; + u_int gpu_process_id; + u_int gpu_speedo_id; + u_int gpu_speedo_value; + u_int gpu_iddq_value; + enum tegra_revision revision; +}; + +extern struct tegra_sku_info tegra_sku_info; +uint32_t tegra_fuse_read_4(int addr); + +#endif /* _TEGRA_EFUSE_H_ */ diff --git a/sys/arm/nvidia/tegra_ehci.c b/sys/arm/nvidia/tegra_ehci.c new file mode 100644 index 0000000..e7f7b22 --- /dev/null +++ b/sys/arm/nvidia/tegra_ehci.c @@ -0,0 +1,322 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * EHCI driver for Tegra SoCs. + */ +#include "opt_bus.h" +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbdevs.h" + +#define TEGRA_EHCI_REG_OFF 0x100 +#define TEGRA_EHCI_REG_SIZE 0x100 + +/* Compatible devices. */ +#define TEGRA124_EHCI 1 +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-ehci", (uintptr_t)TEGRA124_EHCI}, + {NULL, 0}, +}; + +struct tegra_ehci_softc { + ehci_softc_t ehci_softc; + device_t dev; + struct resource *ehci_mem_res; /* EHCI core regs. */ + struct resource *ehci_irq_res; /* EHCI core IRQ. */ + int usb_alloc_called; + clk_t clk; + phy_t phy; + hwreset_t reset; +}; + +static void +tegra_ehci_post_reset(struct ehci_softc *ehci_softc) +{ + uint32_t usbmode; + + /* Force HOST mode. */ + usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_LPM); + usbmode &= ~EHCI_UM_CM; + usbmode |= EHCI_UM_CM_HOST; + device_printf(ehci_softc->sc_bus.bdev, "set host controller mode\n"); + EOWRITE4(ehci_softc, EHCI_USBMODE_LPM, usbmode); +} + +static int +tegra_ehci_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Nvidia Tegra EHCI controller"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +tegra_ehci_detach(device_t dev) +{ + struct tegra_ehci_softc *sc; + ehci_softc_t *esc; + + sc = device_get_softc(dev); + + esc = &sc->ehci_softc; + if (sc->clk != NULL) + clk_release(sc->clk); + if (esc->sc_bus.bdev != NULL) + device_delete_child(dev, esc->sc_bus.bdev); + if (esc->sc_flags & EHCI_SCFLG_DONEINIT) + ehci_detach(esc); + if (esc->sc_intr_hdl != NULL) + bus_teardown_intr(dev, esc->sc_irq_res, + esc->sc_intr_hdl); + if (sc->ehci_irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, + sc->ehci_irq_res); + if (sc->ehci_mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, + sc->ehci_mem_res); + if (sc->usb_alloc_called) + usb_bus_mem_free_all(&esc->sc_bus, &ehci_iterate_hw_softc); + + /* During module unload there are lots of children leftover. */ + device_delete_children(dev); + + return (0); +} + +static int +tegra_ehci_attach(device_t dev) +{ + struct tegra_ehci_softc *sc; + ehci_softc_t *esc; + int rv, rid; + uint64_t freq; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + esc = &sc->ehci_softc; + + /* Allocate resources. */ + rid = 0; + sc->ehci_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->ehci_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + rv = ENXIO; + goto out; + } + + rid = 0; + sc->ehci_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->ehci_irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + rv = ENXIO; + goto out; + } + + rv = hwreset_get_by_ofw_name(dev, "usb", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get reset\n"); + rv = ENXIO; + goto out; + } + + rv = phy_get_by_ofw_property(sc->dev, "nvidia,phy", &sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'nvidia,phy' phy\n"); + rv = ENXIO; + goto out; + } + + rv = clk_get_by_ofw_index(sc->dev, 0, &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get clock\n"); + goto out; + } + + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock\n"); + goto out; + } + + freq = 0; + rv = clk_get_freq(sc->clk, &freq); + if (rv != 0) { + device_printf(dev, "Cannot get clock frequency\n"); + goto out; + } + + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot clear reset: %d\n", rv); + rv = ENXIO; + goto out; + } + + rv = phy_enable(sc->dev, sc->phy); + if (rv != 0) { + device_printf(dev, "Cannot enable phy: %d\n", rv); + goto out; + } + + /* Fill data for EHCI driver. */ + esc->sc_vendor_get_port_speed = ehci_get_port_speed_hostc; + esc->sc_vendor_post_reset = tegra_ehci_post_reset; + esc->sc_io_tag = rman_get_bustag(sc->ehci_mem_res); + esc->sc_bus.parent = dev; + esc->sc_bus.devices = esc->sc_devices; + esc->sc_bus.devices_max = EHCI_MAX_DEVICES; + esc->sc_bus.dma_bits = 32; + + /* Allocate all DMA memory. */ + rv = usb_bus_mem_alloc_all(&esc->sc_bus, USB_GET_DMA_TAG(dev), + &ehci_iterate_hw_softc); + sc->usb_alloc_called = 1; + if (rv != 0) { + device_printf(dev, "usb_bus_mem_alloc_all() failed\n"); + rv = ENOMEM; + goto out; + } + + /* + * Set handle to USB related registers subregion used by + * generic EHCI driver. + */ + rv = bus_space_subregion(esc->sc_io_tag, + rman_get_bushandle(sc->ehci_mem_res), + TEGRA_EHCI_REG_OFF, TEGRA_EHCI_REG_SIZE, &esc->sc_io_hdl); + if (rv != 0) { + device_printf(dev, "Could not create USB memory subregion\n"); + rv = ENXIO; + goto out; + } + + /* Setup interrupt handler. */ + rv = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO, NULL, + (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl); + if (rv != 0) { + device_printf(dev, "Could not setup IRQ\n"); + goto out; + } + + /* Add USB bus device. */ + esc->sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (esc->sc_bus.bdev == NULL) { + device_printf(dev, "Could not add USB device\n"); + goto out; + } + device_set_ivars(esc->sc_bus.bdev, &esc->sc_bus); + + esc->sc_id_vendor = USB_VENDOR_FREESCALE; + strlcpy(esc->sc_vendor, "Nvidia", sizeof(esc->sc_vendor)); + + /* Set flags that affect ehci_init() behavior. */ + esc->sc_flags |= EHCI_SCFLG_TT; + esc->sc_flags |= EHCI_SCFLG_NORESTERM; + rv = ehci_init(esc); + if (rv != 0) { + device_printf(dev, "USB init failed: %d\n", + rv); + goto out; + } + esc->sc_flags |= EHCI_SCFLG_DONEINIT; + + /* Probe the bus. */ + rv = device_probe_and_attach(esc->sc_bus.bdev); + if (rv != 0) { + device_printf(dev, + "device_probe_and_attach() failed\n"); + goto out; + } + return (0); + +out: + tegra_ehci_detach(dev); + return (rv); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_ehci_probe), + DEVMETHOD(device_attach, tegra_ehci_attach), + DEVMETHOD(device_detach, tegra_ehci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + DEVMETHOD_END +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(struct tegra_ehci_softc) +}; + +static devclass_t ehci_devclass; +DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1); \ No newline at end of file diff --git a/sys/arm/nvidia/tegra_gpio.c b/sys/arm/nvidia/tegra_gpio.c new file mode 100644 index 0000000..f9a1d4d --- /dev/null +++ b/sys/arm/nvidia/tegra_gpio.c @@ -0,0 +1,480 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * Tegra GPIO driver. + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + + +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->sc_mtx, \ + device_get_nameunit(_sc->sc_dev), "tegra_gpio", MTX_DEF) +#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +#define GPIO_BANK_OFFS 0x100 /* Bank offset */ +#define GPIO_NUM_BANKS 8 /* Total number per bank */ +#define GPIO_REGS_IN_BANK 4 /* Total registers in bank */ +#define GPIO_PINS_IN_REG 8 /* Total pin in register */ + +#define GPIO_BANKNUM(n) ((n) / (GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG)) +#define GPIO_PORTNUM(n) (((n) / GPIO_PINS_IN_REG) % GPIO_REGS_IN_BANK) +#define GPIO_BIT(n) ((n) % GPIO_PINS_IN_REG) + +#define GPIO_REGNUM(n) (GPIO_BANKNUM(n) * GPIO_BANK_OFFS + \ + GPIO_PORTNUM(n) * 4) + +#define NGPIO ((GPIO_NUM_BANKS * GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG) - 8) + +/* Register offsets */ +#define GPIO_CNF 0x00 +#define GPIO_OE 0x10 +#define GPIO_OUT 0x20 +#define GPIO_IN 0x30 +#define GPIO_INT_STA 0x40 +#define GPIO_INT_ENB 0x50 +#define GPIO_INT_LVL 0x60 +#define GPIO_INT_CLR 0x70 +#define GPIO_MSK_CNF 0x80 +#define GPIO_MSK_OE 0x90 +#define GPIO_MSK_OUT 0xA0 +#define GPIO_MSK_INT_STA 0xC0 +#define GPIO_MSK_INT_ENB 0xD0 +#define GPIO_MSK_INT_LVL 0xE0 + +char *tegra_gpio_port_names[] = { + "A", "B", "C", "D", /* Bank 0 */ + "E", "F", "G", "H", /* Bank 1 */ + "I", "J", "K", "L", /* Bank 2 */ + "M", "N", "O", "P", /* Bank 3 */ + "Q", "R", "S", "T", /* Bank 4 */ + "U", "V", "W", "X", /* Bank 5 */ + "Y", "Z", "AA", "BB", /* Bank 5 */ + "CC", "DD", "EE" /* Bank 5 */ +}; + +struct tegra_gpio_softc { + device_t dev; + device_t sc_busdev; + struct mtx sc_mtx; + struct resource *mem_res; + struct resource *irq_res; + void *gpio_ih; + int gpio_npins; + struct gpio_pin gpio_pins[NGPIO]; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-gpio", 1}, + {NULL, 0} +}; + +static inline void +gpio_write_masked(struct tegra_gpio_softc *sc, bus_size_t reg, + struct gpio_pin *pin, uint32_t val) +{ + uint32_t tmp; + int bit; + + bit = GPIO_BIT(pin->gp_pin); + tmp = 0x100 << bit; /* mask */ + tmp |= (val & 1) << bit; /* value */ + bus_write_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin), tmp); +} +static inline uint32_t +gpio_read(struct tegra_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin) +{ + int bit; + uint32_t val; + + bit = GPIO_BIT(pin->gp_pin); + val = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin)); + return (val >> bit) & 1; +} + +static void +tegra_gpio_pin_configure(struct tegra_gpio_softc *sc, struct gpio_pin *pin, + unsigned int flags) +{ + + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 0) + return; + + /* Manage input/output */ + pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); + if (flags & GPIO_PIN_OUTPUT) { + pin->gp_flags |= GPIO_PIN_OUTPUT; + gpio_write_masked(sc, GPIO_MSK_OE, pin, 1); + } else { + pin->gp_flags |= GPIO_PIN_INPUT; + gpio_write_masked(sc, GPIO_MSK_OE, pin, 0); + } +} + +static device_t +tegra_gpio_get_bus(device_t dev) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + return (sc->sc_busdev); +} + +static int +tegra_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = NGPIO - 1; + return (0); +} + +static int +tegra_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *caps = sc->gpio_pins[pin].gp_caps; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct tegra_gpio_softc *sc; + int cnf; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]); + if (cnf == 0) { + GPIO_UNLOCK(sc); + return (ENXIO); + } + *flags = sc->gpio_pins[pin].gp_flags; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct tegra_gpio_softc *sc; + int cnf; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]); + if (cnf == 0) { + /* XXX - allow this for while .... + GPIO_UNLOCK(sc); + return (ENXIO); + */ + gpio_write_masked(sc, GPIO_MSK_CNF, &sc->gpio_pins[pin], 1); + } + tegra_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + GPIO_LOCK(sc); + gpio_write_masked(sc, GPIO_MSK_OUT, &sc->gpio_pins[pin], value); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *val = gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + gpio_write_masked(sc, GPIO_MSK_OE, &sc->gpio_pins[pin], + gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]) ^ 1); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +tegra_gpio_intr(void *arg) +{ + struct tegra_gpio_softc *sc; + uint32_t val; + int i; + + sc = arg; + for (i = 0; i < NGPIO; i += GPIO_PINS_IN_REG) { + /* Clear interrupt */ + val = bus_read_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(i)); + val &= bus_read_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(i)); + bus_write_4(sc->mem_res, GPIO_INT_CLR + GPIO_REGNUM(i), val); + /* Interrupt handling */ +#ifdef not_yet + for (j = 0; j < GPIO_PINS_IN_REG; j++) { + if (val & (1 << j)) + handle_irq(i + j); + } + */ +#endif + } + return (FILTER_HANDLED); +} + +static int +tegra_gpio_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Tegra GPIO Controller"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +tegra_gpio_detach(device_t dev) +{ + struct tegra_gpio_softc *sc; + + sc = device_get_softc(dev); + + KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); + + gpiobus_detach_bus(dev); + if (sc->gpio_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->gpio_ih); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + mtx_destroy(&sc->sc_mtx); + + return(0); +} + +static int +tegra_gpio_attach(device_t dev) +{ + struct tegra_gpio_softc *sc; + int i, rid; + + sc = device_get_softc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Allocate bus_space resources. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + tegra_gpio_detach(dev); + return (ENXIO); + } + + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + tegra_gpio_detach(dev); + return (ENXIO); + } + + sc->dev = dev; + sc->gpio_npins = NGPIO; + + if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, + tegra_gpio_intr, NULL, sc, &sc->gpio_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + tegra_gpio_detach(dev); + return (ENXIO); + } + + for (i = 0; i < sc->gpio_npins; i++) { + sc->gpio_pins[i].gp_pin = i; + sc->gpio_pins[i].gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; + snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "gpio_%s.%d", + tegra_gpio_port_names[ i / GPIO_PINS_IN_REG], + i % GPIO_PINS_IN_REG); + sc->gpio_pins[i].gp_flags = + gpio_read(sc, GPIO_OE, &sc->gpio_pins[i]) != 0 ? + GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; + } + + sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) { + tegra_gpio_detach(dev); + return (ENXIO); + } + + return (bus_generic_attach(dev)); +} + +static int +tegra_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent, + int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + + if (gcells != 2) + return (ERANGE); + *pin = gpios[0]; + *flags= gpios[1]; + return (0); +} + +static phandle_t +tegra_gpio_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t tegra_gpio_methods[] = { + DEVMETHOD(device_probe, tegra_gpio_probe), + DEVMETHOD(device_attach, tegra_gpio_attach), + DEVMETHOD(device_detach, tegra_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, tegra_gpio_get_bus), + DEVMETHOD(gpio_pin_max, tegra_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, tegra_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, tegra_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, tegra_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, tegra_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, tegra_gpio_pin_get), + DEVMETHOD(gpio_pin_set, tegra_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, tegra_gpio_pin_toggle), + DEVMETHOD(gpio_map_gpios, tegra_map_gpios), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, tegra_gpio_get_node), + + DEVMETHOD_END +}; + +static driver_t tegra_gpio_driver = { + "gpio", + tegra_gpio_methods, + sizeof(struct tegra_gpio_softc), +}; +static devclass_t tegra_gpio_devclass; + +EARLY_DRIVER_MODULE(tegra_gpio, simplebus, tegra_gpio_driver, + tegra_gpio_devclass, 0, 0, 70); diff --git a/sys/arm/nvidia/tegra_i2c.c b/sys/arm/nvidia/tegra_i2c.c new file mode 100644 index 0000000..65d8935 --- /dev/null +++ b/sys/arm/nvidia/tegra_i2c.c @@ -0,0 +1,804 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * I2C driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "iicbus_if.h" + +#define I2C_CNFG 0x000 +#define I2C_CNFG_MSTR_CLR_BUS_ON_TIMEOUT (1 << 15) +#define I2C_CNFG_DEBOUNCE_CNT(x) (((x) & 0x07) << 12) +#define I2C_CNFG_NEW_MASTER_FSM (1 << 11) +#define I2C_CNFG_PACKET_MODE_EN (1 << 10) +#define I2C_CNFG_SEND (1 << 9) +#define I2C_CNFG_NOACK (1 << 8) +#define I2C_CNFG_CMD2 (1 << 7) +#define I2C_CNFG_CMD1 (1 << 6) +#define I2C_CNFG_START (1 << 5) +#define I2C_CNFG_SLV2 (1 << 4) +#define I2C_CNFG_LENGTH_SHIFT 1 +#define I2C_CNFG_LENGTH_MASK 0x7 +#define I2C_CNFG_A_MOD (1 << 0) + +#define I2C_CMD_ADDR0 0x004 +#define I2C_CMD_ADDR1 0x008 +#define I2C_CMD_DATA1 0x00c +#define I2C_CMD_DATA2 0x010 +#define I2C_STATUS 0x01c +#define I2C_SL_CNFG 0x020 +#define I2C_SL_RCVD 0x024 +#define I2C_SL_STATUS 0x028 +#define I2C_SL_ADDR1 0x02c +#define I2C_SL_ADDR2 0x030 +#define I2C_TLOW_SEXT 0x034 +#define I2C_SL_DELAY_COUNT 0x03c +#define I2C_SL_INT_MASK 0x040 +#define I2C_SL_INT_SOURCE 0x044 +#define I2C_SL_INT_SET 0x048 +#define I2C_TX_PACKET_FIFO 0x050 +#define I2C_RX_FIFO 0x054 +#define I2C_PACKET_TRANSFER_STATUS 0x058 +#define I2C_FIFO_CONTROL 0x05c +#define I2C_FIFO_CONTROL_SLV_TX_FIFO_TRIG(x) (((x) & 0x07) << 13) +#define I2C_FIFO_CONTROL_SLV_RX_FIFO_TRIG(x) (((x) & 0x07) << 10) +#define I2C_FIFO_CONTROL_SLV_TX_FIFO_FLUSH (1 << 9) +#define I2C_FIFO_CONTROL_SLV_RX_FIFO_FLUSH (1 << 8) +#define I2C_FIFO_CONTROL_TX_FIFO_TRIG(x) (((x) & 0x07) << 5) +#define I2C_FIFO_CONTROL_RX_FIFO_TRIG(x) (((x) & 0x07) << 2) +#define I2C_FIFO_CONTROL_TX_FIFO_FLUSH (1 << 1) +#define I2C_FIFO_CONTROL_RX_FIFO_FLUSH (1 << 0) + +#define I2C_FIFO_STATUS 0x060 +#define I2C_FIFO_STATUS_SLV_XFER_ERR_REASON (1 << 25) +#define I2C_FIFO_STATUS_TX_FIFO_SLV_EMPTY_CNT(x) (((x) >> 20) & 0xF) +#define I2C_FIFO_STATUS_RX_FIFO_SLV_FULL_CNT(x) (((x) >> 16) & 0xF) +#define I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(x) (((x) >> 4) & 0xF) +#define I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(x) (((x) >> 0) & 0xF) + +#define I2C_INTERRUPT_MASK_REGISTER 0x064 +#define I2C_INTERRUPT_STATUS_REGISTER 0x068 +#define I2C_INT_SLV_ACK_WITHHELD (1 << 28) +#define I2C_INT_SLV_RD2WR (1 << 27) +#define I2C_INT_SLV_WR2RD (1 << 26) +#define I2C_INT_SLV_PKT_XFER_ERR (1 << 25) +#define I2C_INT_SLV_TX_BUFFER_REQ (1 << 24) +#define I2C_INT_SLV_RX_BUFFER_FILLED (1 << 23) +#define I2C_INT_SLV_PACKET_XFER_COMPLETE (1 << 22) +#define I2C_INT_SLV_TFIFO_OVF (1 << 21) +#define I2C_INT_SLV_RFIFO_UNF (1 << 20) +#define I2C_INT_SLV_TFIFO_DATA_REQ (1 << 17) +#define I2C_INT_SLV_RFIFO_DATA_REQ (1 << 16) +#define I2C_INT_BUS_CLEAR_DONE (1 << 11) +#define I2C_INT_TLOW_MEXT_TIMEOUT (1 << 10) +#define I2C_INT_TLOW_SEXT_TIMEOUT (1 << 9) +#define I2C_INT_TIMEOUT (1 << 8) +#define I2C_INT_PACKET_XFER_COMPLETE (1 << 7) +#define I2C_INT_ALL_PACKETS_XFER_COMPLETE (1 << 6) +#define I2C_INT_TFIFO_OVR (1 << 5) +#define I2C_INT_RFIFO_UNF (1 << 4) +#define I2C_INT_NOACK (1 << 3) +#define I2C_INT_ARB_LOST (1 << 2) +#define I2C_INT_TFIFO_DATA_REQ (1 << 1) +#define I2C_INT_RFIFO_DATA_REQ (1 << 0) +#define I2C_ERROR_MASK (I2C_INT_ARB_LOST | I2C_INT_NOACK | \ + I2C_INT_RFIFO_UNF | I2C_INT_TFIFO_OVR) + +#define I2C_CLK_DIVISOR 0x06c +#define I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT 16 +#define I2C_CLK_DIVISOR_STD_FAST_MODE_MASK 0xffff +#define I2C_CLK_DIVISOR_HSMODE_SHIFT 0 +#define I2C_CLK_DIVISOR_HSMODE_MASK 0xffff +#define I2C_INTERRUPT_SOURCE_REGISTER 0x070 +#define I2C_INTERRUPT_SET_REGISTER 0x074 +#define I2C_SLV_TX_PACKET_FIFO 0x07c +#define I2C_SLV_PACKET_STATUS 0x080 +#define I2C_BUS_CLEAR_CONFIG 0x084 +#define I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(x) (((x) & 0xFF) << 16) +#define I2C_BUS_CLEAR_CONFIG_BC_STOP_COND (1 << 2) +#define I2C_BUS_CLEAR_CONFIG_BC_TERMINATE (1 << 1) +#define I2C_BUS_CLEAR_CONFIG_BC_ENABLE (1 << 0) + +#define I2C_BUS_CLEAR_STATUS 0x088 +#define I2C_BUS_CLEAR_STATUS_BC_STATUS (1 << 0) + +#define I2C_CONFIG_LOAD 0x08c +#define I2C_CONFIG_LOAD_TIMEOUT_CONFIG_LOAD (1 << 2) +#define I2C_CONFIG_LOAD_SLV_CONFIG_LOAD (1 << 1) +#define I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD (1 << 0) + +#define I2C_INTERFACE_TIMING_0 0x094 +#define I2C_INTERFACE_TIMING_1 0x098 +#define I2C_HS_INTERFACE_TIMING_0 0x09c +#define I2C_HS_INTERFACE_TIMING_1 0x0a0 + +/* Protocol header 0 */ +#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 +#define PACKET_HEADER0_HEADER_SIZE_MASK 0x3 +#define PACKET_HEADER0_PACKET_ID_SHIFT 16 +#define PACKET_HEADER0_PACKET_ID_MASK 0xff +#define PACKET_HEADER0_CONT_ID_SHIFT 12 +#define PACKET_HEADER0_CONT_ID_MASK 0xf +#define PACKET_HEADER0_PROTOCOL_I2C (1 << 4) +#define PACKET_HEADER0_TYPE_SHIFT 0 +#define PACKET_HEADER0_TYPE_MASK 0x7 + +/* I2C header */ +#define I2C_HEADER_HIGHSPEED_MODE (1 << 22) +#define I2C_HEADER_CONT_ON_NAK (1 << 21) +#define I2C_HEADER_SEND_START_BYTE (1 << 20) +#define I2C_HEADER_READ (1 << 19) +#define I2C_HEADER_10BIT_ADDR (1 << 18) +#define I2C_HEADER_IE_ENABLE (1 << 17) +#define I2C_HEADER_REPEAT_START (1 << 16) +#define I2C_HEADER_CONTINUE_XFER (1 << 15) +#define I2C_HEADER_MASTER_ADDR_SHIFT 12 +#define I2C_HEADER_MASTER_ADDR_MASK 0x7 +#define I2C_HEADER_SLAVE_ADDR_SHIFT 0 +#define I2C_HEADER_SLAVE_ADDR_MASK 0x3ff + +#define I2C_CLK_DIVISOR_STD_FAST_MODE 0x19 +#define I2C_CLK_MULTIPLIER_STD_FAST_MODE 8 + +#define I2C_REQUEST_TIMEOUT (5 * hz) + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +#define LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define SLEEP(_sc, timeout) \ + mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", timeout); +#define LOCK_INIT(_sc) \ + mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_i2c", MTX_DEF) +#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) +#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) +#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-i2c", 1}, + {NULL, 0} +}; +enum tegra_i2c_xfer_type { + XFER_STOP, /* Send stop condition after xfer */ + XFER_REPEAT_START, /* Send repeated start after xfer */ + XFER_CONTINUE /* Don't send nothing */ +} ; + +struct tegra_i2c_softc { + device_t dev; + struct mtx mtx; + + struct resource *mem_res; + struct resource *irq_res; + void *irq_h; + + device_t iicbus; + clk_t clk; + hwreset_t reset; + uint32_t core_freq; + uint32_t bus_freq; + int bus_inuse; + + struct iic_msg *msg; + int msg_idx; + uint32_t bus_err; + int done; +}; + +static int +tegra_i2c_flush_fifo(struct tegra_i2c_softc *sc) +{ + int timeout; + uint32_t reg; + + reg = RD4(sc, I2C_FIFO_CONTROL); + reg |= I2C_FIFO_CONTROL_TX_FIFO_FLUSH | I2C_FIFO_CONTROL_RX_FIFO_FLUSH; + WR4(sc, I2C_FIFO_CONTROL, reg); + + timeout = 10; + while (timeout > 0) { + reg = RD4(sc, I2C_FIFO_CONTROL); + reg &= I2C_FIFO_CONTROL_TX_FIFO_FLUSH | + I2C_FIFO_CONTROL_RX_FIFO_FLUSH; + if (reg == 0) + break; + DELAY(10); + } + if (timeout <= 0) { + device_printf(sc->dev, "FIFO flush timedout\n"); + return (ETIMEDOUT); + } + return (0); +} + +static void +tegra_i2c_setup_clk(struct tegra_i2c_softc *sc, int clk_freq) +{ + int div; + + div = ((sc->core_freq / clk_freq) / 10) - 1; + if ((sc->core_freq / (10 * (div + 1))) > clk_freq) + div++; + if (div > 65535) + div = 65535; + WR4(sc, I2C_CLK_DIVISOR, + (1 << I2C_CLK_DIVISOR_HSMODE_SHIFT) | + (div << I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT)); +} + +static void +tegra_i2c_bus_clear(struct tegra_i2c_softc *sc) +{ + int timeout; + uint32_t reg, status; + + WR4(sc, I2C_BUS_CLEAR_CONFIG, + I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(18) | + I2C_BUS_CLEAR_CONFIG_BC_STOP_COND | + I2C_BUS_CLEAR_CONFIG_BC_TERMINATE); + + WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD); + for (timeout = 1000; timeout > 0; timeout--) { + if (RD4(sc, I2C_CONFIG_LOAD) == 0) + break; + DELAY(10); + } + if (timeout <= 0) + device_printf(sc->dev, "config load timeouted\n"); + reg = RD4(sc, I2C_BUS_CLEAR_CONFIG); + reg |= I2C_BUS_CLEAR_CONFIG_BC_ENABLE; + WR4(sc, I2C_BUS_CLEAR_CONFIG,reg); + + for (timeout = 1000; timeout > 0; timeout--) { + if ((RD4(sc, I2C_BUS_CLEAR_CONFIG) & + I2C_BUS_CLEAR_CONFIG_BC_ENABLE) == 0) + break; + DELAY(10); + } + if (timeout <= 0) + device_printf(sc->dev, "bus clear timeouted\n"); + + status = RD4(sc, I2C_BUS_CLEAR_STATUS); + if ((status & I2C_BUS_CLEAR_STATUS_BC_STATUS) == 0) + device_printf(sc->dev, "bus clear failed\n"); +} + +static int +tegra_i2c_hw_init(struct tegra_i2c_softc *sc) +{ + int rv, timeout; + + /* Reset the core. */ + rv = hwreset_assert(sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert reset\n"); + return (rv); + } + DELAY(10); + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot clear reset\n"); + return (rv); + } + + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); + WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF); + WR4(sc, I2C_CNFG, I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN | + I2C_CNFG_DEBOUNCE_CNT(2)); + + tegra_i2c_setup_clk(sc, sc->bus_freq); + + WR4(sc, I2C_FIFO_CONTROL, I2C_FIFO_CONTROL_TX_FIFO_TRIG(7) | + I2C_FIFO_CONTROL_RX_FIFO_TRIG(0)); + + WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD); + for (timeout = 1000; timeout > 0; timeout--) { + if (RD4(sc, I2C_CONFIG_LOAD) == 0) + break; + DELAY(10); + } + if (timeout <= 0) + device_printf(sc->dev, "config load timeouted\n"); + + tegra_i2c_bus_clear(sc); + return (0); +} + +static int +tegra_i2c_tx(struct tegra_i2c_softc *sc) +{ + uint32_t reg; + int cnt, i; + + if (sc->msg_idx >= sc->msg->len) + panic("Invalid call to tegra_i2c_tx\n"); + + while(sc->msg_idx < sc->msg->len) { + reg = RD4(sc, I2C_FIFO_STATUS); + if (I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(reg) == 0) + break; + cnt = min(4, sc->msg->len - sc->msg_idx); + reg = 0; + for (i = 0; i < cnt; i++) { + reg |= sc->msg->buf[sc->msg_idx] << (i * 8); + sc->msg_idx++; + } + WR4(sc, I2C_TX_PACKET_FIFO, reg); + } + if (sc->msg_idx >= sc->msg->len) + return (0); + return (sc->msg->len - sc->msg_idx - 1); +} + +static int +tegra_i2c_rx(struct tegra_i2c_softc *sc) +{ + uint32_t reg; + int cnt, i; + + if (sc->msg_idx >= sc->msg->len) + panic("Invalid call to tegra_i2c_rx\n"); + + while(sc->msg_idx < sc->msg->len) { + reg = RD4(sc, I2C_FIFO_STATUS); + if (I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(reg) == 0) + break; + cnt = min(4, sc->msg->len - sc->msg_idx); + reg = RD4(sc, I2C_RX_FIFO); + for (i = 0; i < cnt; i++) { + sc->msg->buf[sc->msg_idx] = (reg >> (i * 8)) & 0xFF; + sc->msg_idx++; + } + } + + if (sc->msg_idx >= sc->msg->len) + return (0); + return (sc->msg->len - sc->msg_idx - 1); +} + +static void +tegra_i2c_intr(void *arg) +{ + struct tegra_i2c_softc *sc; + uint32_t status, reg; + int rv; + + sc = (struct tegra_i2c_softc *)arg; + + LOCK(sc); + status = RD4(sc, I2C_INTERRUPT_SOURCE_REGISTER); + if (sc->msg == NULL) { + /* Unexpected interrupt - disable FIFOs, clear reset. */ + reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); + reg &= ~I2C_INT_TFIFO_DATA_REQ; + reg &= ~I2C_INT_RFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); + WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status); + UNLOCK(sc); + return; + } + + if ((status & I2C_ERROR_MASK) != 0) { + if (status & I2C_INT_NOACK) + sc->bus_err = IIC_ENOACK; + if (status & I2C_INT_ARB_LOST) + sc->bus_err = IIC_EBUSERR; + if ((status & I2C_INT_TFIFO_OVR) || + (status & I2C_INT_RFIFO_UNF)) + sc->bus_err = IIC_EBUSERR; + sc->done = 1; + } else if ((status & I2C_INT_RFIFO_DATA_REQ) && + (sc->msg != NULL) && (sc->msg->flags & IIC_M_RD)) { + rv = tegra_i2c_rx(sc); + if (rv == 0) { + reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); + reg &= ~I2C_INT_RFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg); + } + } else if ((status & I2C_INT_TFIFO_DATA_REQ) && + (sc->msg != NULL) && !(sc->msg->flags & IIC_M_RD)) { + rv = tegra_i2c_tx(sc); + if (rv == 0) { + reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); + reg &= ~I2C_INT_TFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg); + } + } else if ((status & I2C_INT_RFIFO_DATA_REQ) || + (status & I2C_INT_TFIFO_DATA_REQ)) { + device_printf(sc->dev, "Unexpected data interrupt: 0x%08X\n", + status); + reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER); + reg &= ~I2C_INT_TFIFO_DATA_REQ; + reg &= ~I2C_INT_RFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg); + } + if (status & I2C_INT_PACKET_XFER_COMPLETE) + sc->done = 1; + WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status); + if (sc->done) { + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); + wakeup(&(sc->done)); + } + UNLOCK(sc); +} + +static void +tegra_i2c_start_msg(struct tegra_i2c_softc *sc, struct iic_msg *msg, + enum tegra_i2c_xfer_type xtype) +{ + uint32_t tmp, mask; + + /* Packet header. */ + tmp = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | + PACKET_HEADER0_PROTOCOL_I2C | + (1 << PACKET_HEADER0_CONT_ID_SHIFT) | + (1 << PACKET_HEADER0_PACKET_ID_SHIFT); + WR4(sc, I2C_TX_PACKET_FIFO, tmp); + + + /* Packet size. */ + WR4(sc, I2C_TX_PACKET_FIFO, msg->len - 1); + + /* I2C header. */ + tmp = I2C_HEADER_IE_ENABLE; + if (xtype == XFER_CONTINUE) + tmp |= I2C_HEADER_CONTINUE_XFER; + else if (xtype == XFER_REPEAT_START) + tmp |= I2C_HEADER_REPEAT_START; + tmp |= msg->slave << I2C_HEADER_SLAVE_ADDR_SHIFT; + if (msg->flags & IIC_M_RD) { + tmp |= I2C_HEADER_READ; + tmp |= 1 << I2C_HEADER_SLAVE_ADDR_SHIFT; + } else + tmp &= ~(1 << I2C_HEADER_SLAVE_ADDR_SHIFT); + + WR4(sc, I2C_TX_PACKET_FIFO, tmp); + + /* Interrupt mask. */ + mask = I2C_INT_NOACK | I2C_INT_ARB_LOST | I2C_INT_PACKET_XFER_COMPLETE; + if (msg->flags & IIC_M_RD) + mask |= I2C_INT_RFIFO_DATA_REQ; + else + mask |= I2C_INT_TFIFO_DATA_REQ; + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, mask); +} + +static int +tegra_i2c_poll(struct tegra_i2c_softc *sc) +{ + int timeout; + + for(timeout = 10000; timeout > 0; timeout--) { + UNLOCK(sc); + tegra_i2c_intr(sc); + LOCK(sc); + if (sc->done != 0) + break; + DELAY(1); + } + if (timeout <= 0) + return (ETIMEDOUT); + return (0); +} + +static int +tegra_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) +{ + int rv, i; + struct tegra_i2c_softc *sc; + enum tegra_i2c_xfer_type xtype; + + sc = device_get_softc(dev); + LOCK(sc); + + /* Get the bus. */ + while (sc->bus_inuse == 1) + SLEEP(sc, 0); + sc->bus_inuse = 1; + + rv = 0; + for (i = 0; i < nmsgs; i++) { + sc->msg = &msgs[i]; + sc->msg_idx = 0; + sc->bus_err = 0; + sc->done = 0; + /* Check for valid parameters. */ + if (sc->msg == NULL || sc->msg->buf == NULL || + sc->msg->len == 0) { + rv = EINVAL; + break; + } + + /* Get flags for next transfer. */ + if (i == (nmsgs - 1)) { + if (msgs[i].flags & IIC_M_NOSTOP) + xtype = XFER_CONTINUE; + else + xtype = XFER_STOP; + } else { + if (msgs[i + 1].flags & IIC_M_NOSTART) + xtype = XFER_CONTINUE; + else + xtype = XFER_REPEAT_START; + } + tegra_i2c_start_msg(sc, sc->msg, xtype); + if (cold) + rv = tegra_i2c_poll(sc); + else + rv = msleep(&sc->done, &sc->mtx, PZERO, "iic", + I2C_REQUEST_TIMEOUT); + + WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0); + WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF); + if (rv == 0) + rv = sc->bus_err; + if (rv != 0) + break; + } + + if (rv != 0) { + tegra_i2c_hw_init(sc); + tegra_i2c_flush_fifo(sc); + } + + sc->msg = NULL; + sc->msg_idx = 0; + sc->bus_err = 0; + sc->done = 0; + + /* Wake up the processes that are waiting for the bus. */ + sc->bus_inuse = 0; + wakeup(sc); + UNLOCK(sc); + + return (rv); +} + +static int +tegra_i2c_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + struct tegra_i2c_softc *sc; + int busfreq; + + sc = device_get_softc(dev); + busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); + sc = device_get_softc(dev); + LOCK(sc); + tegra_i2c_setup_clk(sc, busfreq); + UNLOCK(sc); + return (0); +} + +static int +tegra_i2c_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 int +tegra_i2c_attach(device_t dev) +{ + int rv, rid; + phandle_t node; + struct tegra_i2c_softc *sc; + uint64_t freq; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + LOCK_INIT(sc); + + /* Get the memory resource for the register mapping. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot map registers.\n"); + rv = ENXIO; + goto fail; + } + + /* Allocate our IRQ resource. */ + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate interrupt.\n"); + rv = ENXIO; + goto fail; + } + + /* FDT resources. */ + rv = clk_get_by_ofw_name(dev, "div-clk", &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get i2c clock: %d\n", rv); + goto fail; + } + rv = hwreset_get_by_ofw_name(sc->dev, "i2c", &sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot get i2c reset\n"); + return (ENXIO); + } + rv = OF_getencprop(node, "clock-frequency", &sc->bus_freq, + sizeof(sc->bus_freq)); + if (rv != sizeof(sc->bus_freq)) { + sc->bus_freq = 100000; + goto fail; + } + + /* Request maximum frequency for I2C block 136MHz (408MHz / 3). */ + rv = clk_set_freq(sc->clk, 136000000, CLK_SET_ROUND_DOWN); + if (rv != 0) { + device_printf(dev, "Cannot set clock frequency\n"); + goto fail; + } + rv = clk_get_freq(sc->clk, &freq); + if (rv != 0) { + device_printf(dev, "Cannot get clock frequency\n"); + goto fail; + } + sc->core_freq = (uint32_t)freq; + + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock: %d\n", rv); + goto fail; + } + + /* Init hardware. */ + rv = tegra_i2c_hw_init(sc); + if (rv) { + device_printf(dev, "tegra_i2c_activate failed\n"); + goto fail; + } + + /* Setup interrupt. */ + rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, tegra_i2c_intr, sc, &sc->irq_h); + if (rv) { + device_printf(dev, "Cannot setup interrupt.\n"); + goto fail; + } + + /* Attach the iicbus. */ + sc->iicbus = device_add_child(dev, "iicbus", -1); + if (sc->iicbus == NULL) { + device_printf(dev, "Could not allocate iicbus instance.\n"); + rv = ENXIO; + goto fail; + } + + /* Probe and attach the iicbus. */ + return (bus_generic_attach(dev)); + +fail: + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + LOCK_DESTROY(sc); + + return (rv); +} + +static int +tegra_i2c_detach(device_t dev) +{ + struct tegra_i2c_softc *sc; + int rv; + + sc = device_get_softc(dev); + tegra_i2c_hw_init(sc); + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + LOCK_DESTROY(sc); + if (sc->iicbus) + rv = device_delete_child(dev, sc->iicbus); + return (bus_generic_detach(dev)); +} + +static phandle_t +tegra_i2c_get_node(device_t bus, device_t dev) +{ + + /* Share controller node with iibus device. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t tegra_i2c_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_i2c_probe), + DEVMETHOD(device_attach, tegra_i2c_attach), + DEVMETHOD(device_detach, tegra_i2c_detach), + + /* Bus interface */ + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + + /* OFW methods */ + DEVMETHOD(ofw_bus_get_node, tegra_i2c_get_node), + + /* iicbus interface */ + DEVMETHOD(iicbus_callback, iicbus_null_callback), + DEVMETHOD(iicbus_reset, tegra_i2c_iicbus_reset), + DEVMETHOD(iicbus_transfer, tegra_i2c_transfer), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(iichb, tegra_i2c_driver, tegra_i2c_methods, + sizeof(struct tegra_i2c_softc)); +static devclass_t tegra_i2c_devclass; +EARLY_DRIVER_MODULE(iichb, simplebus, tegra_i2c_driver, tegra_i2c_devclass, 0, + 0, 73); diff --git a/sys/arm/nvidia/tegra_lic.c b/sys/arm/nvidia/tegra_lic.c new file mode 100644 index 0000000..2e3e0a0 --- /dev/null +++ b/sys/arm/nvidia/tegra_lic.c @@ -0,0 +1,265 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * Local interrupt controller driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "pic_if.h" + +#define LIC_VIRQ_CPU 0x00 +#define LIC_VIRQ_COP 0x04 +#define LIC_VFRQ_CPU 0x08 +#define LIC_VFRQ_COP 0x0c +#define LIC_ISR 0x10 +#define LIC_FIR 0x14 +#define LIC_FIR_SET 0x18 +#define LIC_FIR_CLR 0x1c +#define LIC_CPU_IER 0x20 +#define LIC_CPU_IER_SET 0x24 +#define LIC_CPU_IER_CLR 0x28 +#define LIC_CPU_IEP_CLASS 0x2C +#define LIC_COP_IER 0x30 +#define LIC_COP_IER_SET 0x34 +#define LIC_COP_IER_CLR 0x38 +#define LIC_COP_IEP_CLASS 0x3c + +#define WR4(_sc, _b, _r, _v) bus_write_4((_sc)->mem_res[_b], (_r), (_v)) +#define RD4(_sc, _b, _r) bus_read_4((_sc)->mem_res[_b], (_r)) + +static struct resource_spec lic_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { SYS_RES_MEMORY, 2, RF_ACTIVE }, + { SYS_RES_MEMORY, 3, RF_ACTIVE }, + { SYS_RES_MEMORY, 4, RF_ACTIVE }, + { -1, 0 } +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-ictlr", 1}, + {NULL, 0} +}; + +struct tegra_lic_sc { + device_t dev; + struct resource *mem_res[nitems(lic_spec)]; + device_t parent; +}; + +static int +tegra_lic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + return (PIC_REGISTER(sc->parent, isrc, is_percpu)); +} + +static int +tegra_lic_unregister(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + return (PIC_UNREGISTER(sc->parent, isrc)); +} + +static void +tegra_lic_enable_source(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_ENABLE_SOURCE(sc->parent, isrc); +} + +static void +tegra_lic_disable_source(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_DISABLE_SOURCE(sc->parent, isrc); +} + +static void +tegra_lic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_ENABLE_INTR(sc->parent, isrc); +} + +static void +tegra_lic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_PRE_ITHREAD(sc->parent, isrc); +} + + +static void +tegra_lic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_POST_ITHREAD(sc->parent, isrc); +} + +static void +tegra_lic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + PIC_POST_FILTER(sc->parent, isrc); +} + +#ifdef SMP +static int +tegra_lic_bind(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_lic_sc *sc = device_get_softc(dev); + + return (PIC_BIND(sc->parent, isrc)); +} +#endif + +static int +tegra_lic_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 int +tegra_lic_attach(device_t dev) +{ + struct tegra_lic_sc *sc; + phandle_t node; + phandle_t parent_xref; + int i, rv; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + rv = OF_getencprop(node, "interrupt-parent", &parent_xref, + sizeof(parent_xref)); + if (rv <= 0) { + device_printf(dev, "Cannot read parent node property\n"); + goto fail; + } + sc->parent = OF_device_from_xref(parent_xref); + if (sc->parent == NULL) { + device_printf(dev, "Cannott find parent controller\n"); + goto fail; + } + + if (bus_alloc_resources(dev, lic_spec, sc->mem_res)) { + device_printf(dev, "Cannott allocate resources\n"); + goto fail; + } + + /* Disable all interrupts, route all to irq */ + for (i = 0; i < nitems(lic_spec); i++) { + if (sc->mem_res[i] == NULL) + continue; + WR4(sc, i, LIC_CPU_IER_CLR, 0xFFFFFFFF); + WR4(sc, i, LIC_CPU_IEP_CLASS, 0); + } + + + if (intr_pic_register(dev, OF_xref_from_node(node)) != 0) { + device_printf(dev, "Cannot register PIC\n"); + goto fail; + } + return (0); + +fail: + bus_release_resources(dev, lic_spec, sc->mem_res); + return (ENXIO); +} + +static int +tegra_lic_detach(device_t dev) +{ + struct tegra_lic_sc *sc; + int i; + + sc = device_get_softc(dev); + for (i = 0; i < nitems(lic_spec); i++) { + if (sc->mem_res[i] == NULL) + continue; + bus_release_resource(dev, SYS_RES_MEMORY, i, + sc->mem_res[i]); + } + return (0); +} + +static device_method_t tegra_lic_methods[] = { + DEVMETHOD(device_probe, tegra_lic_probe), + DEVMETHOD(device_attach, tegra_lic_attach), + DEVMETHOD(device_detach, tegra_lic_detach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_register, tegra_lic_register), + DEVMETHOD(pic_unregister, tegra_lic_unregister), + DEVMETHOD(pic_enable_source, tegra_lic_enable_source), + DEVMETHOD(pic_disable_source, tegra_lic_disable_source), + DEVMETHOD(pic_enable_intr, tegra_lic_enable_intr), + DEVMETHOD(pic_pre_ithread, tegra_lic_pre_ithread), + DEVMETHOD(pic_post_ithread, tegra_lic_post_ithread), + DEVMETHOD(pic_post_filter, tegra_lic_post_filter), +#ifdef SMP + DEVMETHOD(pic_bind, tegra_lic_bind), +#endif + DEVMETHOD_END +}; +devclass_t tegra_lic_devclass; +DEFINE_CLASS_0(tegra_lic, tegra_lic_driver, tegra_lic_methods, + sizeof(struct tegra_lic_sc)); +EARLY_DRIVER_MODULE(tegra_lic, simplebus, tegra_lic_driver, tegra_lic_devclass, + NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE + 1); diff --git a/sys/arm/nvidia/tegra_pcie.c b/sys/arm/nvidia/tegra_pcie.c new file mode 100644 index 0000000..1287e01 --- /dev/null +++ b/sys/arm/nvidia/tegra_pcie.c @@ -0,0 +1,1692 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * Nvidia Integrated PCI/PCI-Express controller driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ofw_bus_if.h" +#include "pcib_if.h" + +#include + +/* --- Move to ofw_pci.c/.h ----------------------- */ + +struct tegra_pci_range { + /* parsed phys.hi */ + int nonrelocatable; + int prefetchable; + int aliased; + int space_code; /* In native format (not shifted)*/ + int bus; + int device; + int function; + int reg; + pci_addr_t pci_addr; /* PCI Address */ + bus_addr_t host_addr; /* Host bus address*/ + bus_size_t size; /* Range size */ +}; + +static int +tegra_pci_get_ranges(phandle_t node, struct tegra_pci_range **ranges) +{ + int host_address_cells, pci_address_cells, size_cells; + cell_t *base_ranges; + ssize_t nbase_ranges; + int nranges; + int i, j, k; + uint32_t flags; + uint64_t tmp; + + host_address_cells = 1; + pci_address_cells = 3; + size_cells = 2; + OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells, + sizeof(host_address_cells)); + OF_getencprop(node, "#address-cells", &pci_address_cells, + sizeof(pci_address_cells)); + OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells)); + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges <= 0) + return (-1); + nranges = nbase_ranges / sizeof(cell_t) / + (pci_address_cells + host_address_cells + size_cells); + + *ranges = malloc(nranges * sizeof(struct tegra_pci_range), + 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 < nranges; i++) { + flags = base_ranges[j++]; + (*ranges)[i].nonrelocatable = + flags & OFW_PCI_PHYS_HI_NONRELOCATABLE ? 1 : 0; + (*ranges)[i].prefetchable = + flags & OFW_PCI_PHYS_HI_PREFETCHABLE ? 1 : 0; + (*ranges)[i].aliased = + flags & OFW_PCI_PHYS_HI_ALIASED ? 1 : 0; + (*ranges)[i].space_code = flags & OFW_PCI_PHYS_HI_SPACEMASK; + (*ranges)[i].bus = OFW_PCI_PHYS_HI_BUS(flags); + (*ranges)[i].device = OFW_PCI_PHYS_HI_DEVICE(flags); + (*ranges)[i].function = OFW_PCI_PHYS_HI_FUNCTION(flags); + (*ranges)[i].reg = flags & OFW_PCI_PHYS_HI_REGISTERMASK; + + tmp = 0; + for (k = 0; k < pci_address_cells - 1; k++) { + tmp <<= 32; + tmp |= base_ranges[j++]; + } + (*ranges)[i].pci_addr = (pci_addr_t)tmp; + + tmp = 0; + for (k = 0; k < host_address_cells; k++) { + tmp <<= 32; + tmp |= base_ranges[j++]; + } + (*ranges)[i].host_addr = (bus_addr_t)tmp; + tmp = 0; + + for (k = 0; k < size_cells; k++) { + tmp <<= 32; + tmp |= base_ranges[j++]; + } + (*ranges)[i].size = (bus_size_t)tmp; + } + + free(base_ranges, M_DEVBUF); + return (nranges); +} + +/* -------------------------------------------------------------------------- */ +#define AFI_AXI_BAR0_SZ 0x000 +#define AFI_AXI_BAR1_SZ 0x004 +#define AFI_AXI_BAR2_SZ 0x008 +#define AFI_AXI_BAR3_SZ 0x00c +#define AFI_AXI_BAR4_SZ 0x010 +#define AFI_AXI_BAR5_SZ 0x014 +#define AFI_AXI_BAR0_START 0x018 +#define AFI_AXI_BAR1_START 0x01c +#define AFI_AXI_BAR2_START 0x020 +#define AFI_AXI_BAR3_START 0x024 +#define AFI_AXI_BAR4_START 0x028 +#define AFI_AXI_BAR5_START 0x02c +#define AFI_FPCI_BAR0 0x030 +#define AFI_FPCI_BAR1 0x034 +#define AFI_FPCI_BAR2 0x038 +#define AFI_FPCI_BAR3 0x03c +#define AFI_FPCI_BAR4 0x040 +#define AFI_FPCI_BAR5 0x044 +#define AFI_MSI_BAR_SZ 0x060 +#define AFI_MSI_FPCI_BAR_ST 0x064 +#define AFI_MSI_AXI_BAR_ST 0x068 + + +#define AFI_AXI_BAR6_SZ 0x134 +#define AFI_AXI_BAR7_SZ 0x138 +#define AFI_AXI_BAR8_SZ 0x13c +#define AFI_AXI_BAR6_START 0x140 +#define AFI_AXI_BAR7_START 0x144 +#define AFI_AXI_BAR8_START 0x148 +#define AFI_FPCI_BAR6 0x14c +#define AFI_FPCI_BAR7 0x150 +#define AFI_FPCI_BAR8 0x154 + +#define AFI_CONFIGURATION 0x0ac +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) + +#define AFI_FPCI_ERROR_MASKS 0x0b0 +#define AFI_INTR_MASK 0x0b4 +#define AFI_INTR_MASK_MSI_MASK (1 << 8) +#define AFI_INTR_MASK_INT_MASK (1 << 0) + +#define AFI_INTR_CODE 0x0b8 +#define AFI_INTR_CODE_MASK 0xf +#define AFI_INTR_CODE_INT_CODE_INI_SLVERR 1 +#define AFI_INTR_CODE_INT_CODE_INI_DECERR 2 +#define AFI_INTR_CODE_INT_CODE_TGT_SLVERR 3 +#define AFI_INTR_CODE_INT_CODE_TGT_DECERR 4 +#define AFI_INTR_CODE_INT_CODE_TGT_WRERR 5 +#define AFI_INTR_CODE_INT_CODE_SM_MSG 6 +#define AFI_INTR_CODE_INT_CODE_DFPCI_DECERR 7 +#define AFI_INTR_CODE_INT_CODE_AXI_DECERR 8 +#define AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT 9 +#define AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE 10 +#define AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE 11 +#define AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE 12 +#define AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE 13 +#define AFI_INTR_CODE_INT_CODE_P2P_ERROR 14 + + +#define AFI_INTR_SIGNATURE 0x0bc +#define AFI_UPPER_FPCI_ADDRESS 0x0c0 +#define AFI_SM_INTR_ENABLE 0x0c4 +#define AFI_SM_INTR_RP_DEASSERT (1 << 14) +#define AFI_SM_INTR_RP_ASSERT (1 << 13) +#define AFI_SM_INTR_HOTPLUG (1 << 12) +#define AFI_SM_INTR_PME (1 << 11) +#define AFI_SM_INTR_FATAL_ERROR (1 << 10) +#define AFI_SM_INTR_UNCORR_ERROR (1 << 9) +#define AFI_SM_INTR_CORR_ERROR (1 << 8) +#define AFI_SM_INTR_INTD_DEASSERT (1 << 7) +#define AFI_SM_INTR_INTC_DEASSERT (1 << 6) +#define AFI_SM_INTR_INTB_DEASSERT (1 << 5) +#define AFI_SM_INTR_INTA_DEASSERT (1 << 4) +#define AFI_SM_INTR_INTD_ASSERT (1 << 3) +#define AFI_SM_INTR_INTC_ASSERT (1 << 2) +#define AFI_SM_INTR_INTB_ASSERT (1 << 1) +#define AFI_SM_INTR_INTA_ASSERT (1 << 0) + +#define AFI_AFI_INTR_ENABLE 0x0c8 +#define AFI_AFI_INTR_ENABLE_CODE(code) (1 << (code)) + +#define AFI_PCIE_CONFIG 0x0f8 +#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) +#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0x6 +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1 (0x1 << 20) + +#define AFI_FUSE 0x104 +#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) + +#define AFI_PEX0_CTRL 0x110 +#define AFI_PEX1_CTRL 0x118 +#define AFI_PEX2_CTRL 0x128 +#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4) +#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) +#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) +#define AFI_PEX_CTRL_RST_L (1 << 0) + +#define AFI_AXI_BAR6_SZ 0x134 +#define AFI_AXI_BAR7_SZ 0x138 +#define AFI_AXI_BAR8_SZ 0x13c +#define AFI_AXI_BAR6_START 0x140 +#define AFI_AXI_BAR7_START 0x144 +#define AFI_AXI_BAR8_START 0x148 +#define AFI_FPCI_BAR6 0x14c +#define AFI_FPCI_BAR7 0x150 +#define AFI_FPCI_BAR8 0x154 +#define AFI_PLLE_CONTROL 0x160 +#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) +#define AFI_PLLE_CONTROL_BYPASS_PCIE2PLLE_CONTROL (1 << 8) +#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) +#define AFI_PLLE_CONTROL_PCIE2PLLE_CONTROL_EN (1 << 0) + +#define AFI_PEXBIAS_CTRL 0x168 + +/* FPCI Address space */ +#define FPCI_MAP_IO 0xfdfc000000ULL +#define FPCI_MAP_TYPE0_CONFIG 0xfdfc000000ULL +#define FPCI_MAP_TYPE1_CONFIG 0xfdff000000ULL +#define FPCI_MAP_EXT_TYPE0_CONFIG 0xfe00000000ULL +#define FPCI_MAP_EXT_TYPE1_CONFIG 0xfe10000000ULL + +/* Configuration space */ +#define RP_VEND_XP 0x00000F00 +#define RP_VEND_XP_DL_UP (1 << 30) + +#define RP_PRIV_MISC 0x00000FE0 +#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0) + +#define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 +#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 + +#define TEGRA_PCIE_LINKUP_TIMEOUT 200 + +#define DEBUG +#ifdef DEBUG +#define debugf(fmt, args...) do { printf(fmt,##args); } while (0) +#else +#define debugf(fmt, args...) +#endif + +/* + * Configuration space format: + * [27:24] extended register + * [23:16] bus + * [15:11] slot (device) + * [10: 8] function + * [ 7: 0] register + */ +#define PCI_CFG_EXT_REG(reg) ((((reg) >> 8) & 0x0f) << 24) +#define PCI_CFG_BUS(bus) (((bus) & 0xff) << 16) +#define PCI_CFG_DEV(dev) (((dev) & 0x1f) << 11) +#define PCI_CFG_FUN(fun) (((fun) & 0x07) << 8) +#define PCI_CFG_BASE_REG(reg) ((reg) & 0xff) + +#define PADS_WR4(_sc, _r, _v) bus_write_4((_sc)-pads_mem_res, (_r), (_v)) +#define PADS_RD4(_sc, _r) bus_read_4((_sc)->pads_mem_res, (_r)) +#define AFI_WR4(_sc, _r, _v) bus_write_4((_sc)->afi_mem_res, (_r), (_v)) +#define AFI_RD4(_sc, _r) bus_read_4((_sc)->afi_mem_res, (_r)) + +static struct { + bus_size_t axi_start; + bus_size_t fpci_start; + bus_size_t size; +} bars[] = { + {AFI_AXI_BAR0_START, AFI_FPCI_BAR0, AFI_AXI_BAR0_SZ}, /* BAR 0 */ + {AFI_AXI_BAR1_START, AFI_FPCI_BAR1, AFI_AXI_BAR1_SZ}, /* BAR 1 */ + {AFI_AXI_BAR2_START, AFI_FPCI_BAR2, AFI_AXI_BAR2_SZ}, /* BAR 2 */ + {AFI_AXI_BAR3_START, AFI_FPCI_BAR3, AFI_AXI_BAR3_SZ}, /* BAR 3 */ + {AFI_AXI_BAR4_START, AFI_FPCI_BAR4, AFI_AXI_BAR4_SZ}, /* BAR 4 */ + {AFI_AXI_BAR5_START, AFI_FPCI_BAR5, AFI_AXI_BAR5_SZ}, /* BAR 5 */ + {AFI_AXI_BAR6_START, AFI_FPCI_BAR6, AFI_AXI_BAR6_SZ}, /* BAR 6 */ + {AFI_AXI_BAR7_START, AFI_FPCI_BAR7, AFI_AXI_BAR7_SZ}, /* BAR 7 */ + {AFI_AXI_BAR8_START, AFI_FPCI_BAR8, AFI_AXI_BAR8_SZ}, /* BAR 8 */ + {AFI_MSI_AXI_BAR_ST, AFI_MSI_FPCI_BAR_ST, AFI_MSI_BAR_SZ}, /* MSI 9 */ +}; + +/* Compatible devices. */ +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-pcie", 1}, + {NULL, 0}, +}; + +struct tegra_pcib_port { + int enabled; + int port_idx; /* chip port index */ + int num_lanes; /* number of lanes */ + bus_size_t afi_pex_ctrl; /* offset of afi_pex_ctrl */ + + /* Config space properties. */ + bus_addr_t rp_base_addr; /* PA of config window */ + bus_size_t rp_size; /* size of config window */ + bus_space_handle_t cfg_handle; /* handle of config window */ +}; + +#define TEGRA_PCIB_MAX_PORTS 3 +struct tegra_pcib_softc { + device_t dev; + struct mtx mtx; + struct ofw_bus_iinfo pci_iinfo; + struct rman pref_mem_rman; + struct rman mem_rman; + struct rman io_rman; + struct resource *pads_mem_res; + struct resource *afi_mem_res; + struct resource *cfg_mem_res; + struct resource *irq_res; + struct resource *msi_irq_res; + void *intr_cookie; + void *msi_intr_cookie; + + struct tegra_pci_range mem_range; + struct tegra_pci_range pref_mem_range; + struct tegra_pci_range io_range; + + phy_t phy; + clk_t clk_pex; + clk_t clk_afi; + clk_t clk_pll_e; + clk_t clk_cml; + hwreset_t hwreset_pex; + hwreset_t hwreset_afi; + hwreset_t hwreset_pcie_x; + regulator_t supply_avddio_pex; + regulator_t supply_dvddio_pex; + regulator_t supply_avdd_pex_pll; + regulator_t supply_hvdd_pex; + regulator_t supply_hvdd_pex_pll_e; + regulator_t supply_vddio_pex_ctl; + regulator_t supply_avdd_pll_erefe; + + int busnr; /* host bridge bus number */ + uint32_t msi_bitmap; + bus_addr_t cfg_base_addr; /* base address of config */ + bus_size_t cfg_cur_offs; /* currently mapped window */ + bus_space_handle_t cfg_handle; /* handle of config window */ + bus_space_tag_t bus_tag; /* tag of config window */ + int lanes_cfg; + int num_ports; + struct tegra_pcib_port *ports[TEGRA_PCIB_MAX_PORTS]; +}; + +/* ------------------------------------------------------------------------- */ +/* + * Resource manager + */ +static int +tegra_pcib_rman_init(struct tegra_pcib_softc *sc) +{ + int err; + char buf[64]; + + /* Memory management. */ + sc->pref_mem_rman.rm_type = RMAN_ARRAY; + snprintf(buf, sizeof(buf), "%s prefetchable memory space", + device_get_nameunit(sc->dev)); + sc->pref_mem_rman.rm_descr = strdup(buf, M_DEVBUF); + err = rman_init(&sc->pref_mem_rman); + if (err) + return (err); + + sc->mem_rman.rm_type = RMAN_ARRAY; + snprintf(buf, sizeof(buf), "%s non prefetchable memory space", + device_get_nameunit(sc->dev)); + sc->mem_rman.rm_descr = strdup(buf, M_DEVBUF); + err = rman_init(&sc->mem_rman); + if (err) + return (err); + + sc->io_rman.rm_type = RMAN_ARRAY; + snprintf(buf, sizeof(buf), "%s I/O space", + device_get_nameunit(sc->dev)); + sc->io_rman.rm_descr = strdup(buf, M_DEVBUF); + err = rman_init(&sc->io_rman); + if (err) { + rman_fini(&sc->mem_rman); + return (err); + } + + err = rman_manage_region(&sc->pref_mem_rman, + sc->pref_mem_range.host_addr, + sc->pref_mem_range.host_addr + sc->pref_mem_range.size - 1); + if (err) + goto error; + err = rman_manage_region(&sc->mem_rman, + sc->mem_range.host_addr, + sc->mem_range.host_addr + sc->mem_range.size - 1); + if (err) + goto error; + err = rman_manage_region(&sc->io_rman, + sc->io_range.pci_addr, + sc->io_range.pci_addr + sc->io_range.size - 1); + if (err) + goto error; + return (0); + +error: + rman_fini(&sc->pref_mem_rman); + rman_fini(&sc->mem_rman); + rman_fini(&sc->io_rman); + return (err); +} + +static struct rman * +tegra_pcib_rman(struct tegra_pcib_softc *sc, int type, u_int flags) +{ + + switch (type) { + case SYS_RES_IOPORT: + return (&sc->io_rman); + case SYS_RES_MEMORY: + if (flags & RF_PREFETCHABLE) + return (&sc->pref_mem_rman); + else + return (&sc->mem_rman); + default: + break; + } + + return (NULL); +} + +static struct resource * +tegra_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct tegra_pcib_softc *sc; + struct rman *rm; + struct resource *res; + + debugf("%s: enter %d start %lx end %lx count %lx\n", __func__, + type, start, end, count); + sc = device_get_softc(dev); + +#if defined(NEW_PCIB) && defined(PCI_RES_BUS) + if (type == PCI_RES_BUS) { + return (pci_domain_alloc_bus(0, child, rid, start, end, count, + flags)); + } +#endif + + rm = tegra_pcib_rman(sc, type, flags); + + if (rm == NULL) { + res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, + type, rid, start, end, count, flags); + + return (res); + } + + if (bootverbose) { + device_printf(dev, + "rman_reserve_resource: start=%#lx, end=%#lx, count=%#lx\n", + start, end, count); + } + + res = rman_reserve_resource(rm, start, end, count, flags, child); + if (res == NULL) + goto fail; + rman_set_rid(res, *rid); + if (flags & RF_ACTIVE) { + if (bus_activate_resource(child, type, *rid, res)) { + rman_release_resource(res); + goto fail; + } + } + return (res); + +fail: + if (bootverbose) { + device_printf(dev, "%s FAIL: type=%d, rid=%d, " + "start=%016lx, end=%016lx, count=%016lx, flags=%x\n", + __func__, type, *rid, start, end, count, flags); + } + + return (NULL); +} + +static int +tegra_pcib_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *res) +{ + struct tegra_pcib_softc *sc; + struct rman *rm; + + sc = device_get_softc(dev); + debugf("%s: %d rid %x\n", __func__, type, rid); + +#if defined(NEW_PCIB) && defined(PCI_RES_BUS) + if (type == PCI_RES_BUS) + return (pci_domain_release_bus(0, child, rid, res)); +#endif + + rm = tegra_pcib_rman(sc, type, rman_get_flags(res)); + if (rm != NULL) { + KASSERT(rman_is_region_manager(res, rm), ("rman mismatch")); + rman_release_resource(res); + } + + return (bus_generic_release_resource(dev, child, type, rid, res)); +} + +static int +tegra_pcib_adjust_resource(device_t dev, device_t child, int type, + struct resource *res, u_long start, u_long end) +{ + struct tegra_pcib_softc *sc; + struct rman *rm; + + sc = device_get_softc(dev); + debugf("%s: %d start %lx end %lx \n", __func__, + type, start, end); + +#if defined(NEW_PCIB) && defined(PCI_RES_BUS) + if (type == PCI_RES_BUS) + return (pci_domain_adjust_bus(0, child, res, start, end)); +#endif + + rm = tegra_pcib_rman(sc, type, rman_get_flags(res)); + if (rm != NULL) + return (rman_adjust_resource(res, start, end)); + return (bus_generic_adjust_resource(dev, child, type, res, start, end)); +} +extern bus_space_tag_t fdtbus_bs_tag; +static int +tegra_pcib_pcie_activate_resource(device_t dev, device_t child, int type, + int rid, struct resource *r) +{ + struct tegra_pcib_softc *sc; + vm_offset_t start; + void *p; + int rv; + + sc = device_get_softc(dev); + rv = rman_activate_resource(r); + if (rv != 0) + return (rv); + switch(type) { + case SYS_RES_IOPORT: + start = rman_get_start(r) + sc->io_range.host_addr; + break; + default: + start = rman_get_start(r); + rman_get_start(r); + break; + } + + if (bootverbose) + printf("%s: start %zx, len %ld\n", __func__, start, + rman_get_size(r)); + + p = pmap_mapdev(start, (vm_size_t)rman_get_size(r)); + rman_set_virtual(r, p); + rman_set_bustag(r, fdtbus_bs_tag); + rman_set_bushandle(r, (u_long)p); + return (0); +} + +/* ------------------------------------------------------------------------- */ +/* + * IVARs + */ +static int +tegra_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct tegra_pcib_softc *sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + *result = sc->busnr; + return (0); + case PCIB_IVAR_DOMAIN: + *result = device_get_unit(dev); + return (0); + } + + return (ENOENT); +} + +static int +tegra_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct tegra_pcib_softc *sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + sc->busnr = value; + return (0); + } + + return (ENOENT); +} + +static int +tegra_pcib_maxslots(device_t dev) +{ + return (16); +} + + +static int +tegra_pcib_route_interrupt(device_t bus, device_t dev, int pin) +{ + struct tegra_pcib_softc *sc; + + sc = device_get_softc(bus); + device_printf(bus, "route pin %d for device %d.%d to %lu\n", + pin, pci_get_slot(dev), pci_get_function(dev), + rman_get_start(sc->irq_res)); + + return (rman_get_start(sc->irq_res)); +} + +static int +tegra_pcbib_map_cfg(struct tegra_pcib_softc *sc, u_int bus, u_int slot, + u_int func, u_int reg) +{ + bus_size_t offs; + int rv; + + offs = sc->cfg_base_addr; + offs |= PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | PCI_CFG_FUN(func) | + PCI_CFG_EXT_REG(reg); + if ((sc->cfg_handle != 0) && (sc->cfg_cur_offs == offs)) + return (0); + if (sc->cfg_handle != 0) + bus_space_unmap(sc->bus_tag, sc->cfg_handle, 0x800); + + rv = bus_space_map(sc->bus_tag, offs, 0x800, 0, &sc->cfg_handle); + if (rv != 0) + device_printf(sc->dev, "Cannot map config space\n"); + else + sc->cfg_cur_offs = offs; + return (rv); +} + +static uint32_t +tegra_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, int bytes) +{ + struct tegra_pcib_softc *sc; + bus_space_handle_t hndl; + uint32_t off; + uint32_t val; + int rv, i; + + sc = device_get_softc(dev); + if (bus == 0) { + if (func != 0) + return (0xFFFFFFFF); + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if ((sc->ports[i] != NULL) && + (sc->ports[i]->port_idx == slot)) { + hndl = sc->ports[i]->cfg_handle; + off = reg & 0xFFF; + break; + } + } + if (i >= TEGRA_PCIB_MAX_PORTS) + return (0xFFFFFFFF); + } else { + rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg); + if (rv != 0) + return (0xFFFFFFFF); + hndl = sc->cfg_handle; + off = PCI_CFG_BASE_REG(reg); + } + + val = bus_space_read_4(sc->bus_tag, hndl, off & ~3); + switch (bytes) { + case 4: + break; + case 2: + if (off & 3) + val >>= 16; + val &= 0xffff; + break; + case 1: + val >>= ((off & 3) << 3); + val &= 0xff; + break; + } + return val; +} + +static void +tegra_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t val, int bytes) +{ + struct tegra_pcib_softc *sc; + bus_space_handle_t hndl; + uint32_t off; + uint32_t val2; + int rv, i; + + sc = device_get_softc(dev); + if (bus == 0) { + if (func != 0) + return; + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if ((sc->ports[i] != NULL) && + (sc->ports[i]->port_idx == slot)) { + hndl = sc->ports[i]->cfg_handle; + off = reg & 0xFFF; + break; + } + } + if (i >= TEGRA_PCIB_MAX_PORTS) + return; + } else { + rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg); + if (rv != 0) + return; + hndl = sc->cfg_handle; + off = PCI_CFG_BASE_REG(reg); + } + + switch (bytes) { + case 4: + bus_space_write_4(sc->bus_tag, hndl, off, val); + break; + case 2: + val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3); + val2 &= ~(0xffff << ((off & 3) << 3)); + val2 |= ((val & 0xffff) << ((off & 3) << 3)); + bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2); + break; + case 1: + val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3); + val2 &= ~(0xff << ((off & 3) << 3)); + val2 |= ((val & 0xff) << ((off & 3) << 3)); + bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2); + break; + } +} + +static int tegra_pci_intr(void *arg) +{ + struct tegra_pcib_softc *sc = arg; + uint32_t code, signature; + + code = bus_read_4(sc->afi_mem_res, AFI_INTR_CODE) & AFI_INTR_CODE_MASK; + signature = bus_read_4(sc->afi_mem_res, AFI_INTR_SIGNATURE); + bus_write_4(sc->afi_mem_res, AFI_INTR_CODE, 0); + if (code == AFI_INTR_CODE_INT_CODE_SM_MSG) + return(FILTER_STRAY); + + printf("tegra_pci_intr: code %x sig %x\n", code, signature); + return (FILTER_HANDLED); +} + +#if defined(TEGRA_PCI_MSI) +static int +tegra_pcib_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, + uint32_t *data) +{ + struct tegra_pcib_softc *sc; + + sc = device_get_softc(dev); + irq = irq - MSI_IRQ; + + /* validate parameters */ + if (isclr(&sc->msi_bitmap, irq)) { + device_printf(dev, "invalid MSI 0x%x\n", irq); + return (EINVAL); + } + + tegra_msi_data(irq, addr, data); + + debugf("%s: irq: %d addr: %jx data: %x\n", + __func__, irq, *addr, *data); + + return (0); +} + +static int +tegra_pcib_alloc_msi(device_t dev, device_t child, int count, + int maxcount __unused, int *irqs) +{ + struct tegra_pcib_softc *sc; + u_int start = 0, i; + + if (powerof2(count) == 0 || count > MSI_IRQ_NUM) + return (EINVAL); + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); + + for (start = 0; (start + count) < MSI_IRQ_NUM; start++) { + for (i = start; i < start + count; i++) { + if (isset(&sc->msi_bitmap, i)) + break; + } + if (i == start + count) + break; + } + + if ((start + count) == MSI_IRQ_NUM) { + mtx_unlock(&sc->mtx); + return (ENXIO); + } + + for (i = start; i < start + count; i++) { + setbit(&sc->msi_bitmap, i); + irqs[i] = MSI_IRQ + i; + } + debugf("%s: start: %x count: %x\n", __func__, start, count); + + mtx_unlock(&sc->mtx); + return (0); +} + +static int +tegra_pcib_release_msi(device_t dev, device_t child, int count, int *irqs) +{ + struct tegra_pcib_softc *sc; + u_int i; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); + + for (i = 0; i < count; i++) + clrbit(&sc->msi_bitmap, irqs[i] - MSI_IRQ); + + mtx_unlock(&sc->mtx); + return (0); +} +#endif + +static bus_size_t +tegra_pcib_pex_ctrl(struct tegra_pcib_softc *sc, int port) +{ + if (port >= TEGRA_PCIB_MAX_PORTS) + panic("invalid port number: %d\n", port); + + if (port == 0) + return (AFI_PEX0_CTRL); + else if (port == 1) + return (AFI_PEX1_CTRL); + else if (port == 2) + return (AFI_PEX2_CTRL); + else + panic("invalid port number: %d\n", port); +} + +static int +tegra_pcib_enable_fdt_resources(struct tegra_pcib_softc *sc) +{ + int rv; + + rv = hwreset_assert(sc->hwreset_pcie_x); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'pcie_x' reset\n"); + return (rv); + } + rv = hwreset_assert(sc->hwreset_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'afi' reset\n"); + return (rv); + } + rv = hwreset_assert(sc->hwreset_pex); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert 'pex' reset\n"); + return (rv); + } + + tegra_powergate_power_off(TEGRA_POWERGATE_PCX); + + /* Power supplies. */ + rv = regulator_enable(sc->supply_avddio_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avddio_pex' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_dvddio_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'dvddio_pex' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_avdd_pex_pll); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avdd-pex-pll' regulator\n"); + return (rv); + } + + rv = regulator_enable(sc->supply_hvdd_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'hvdd-pex-supply' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_hvdd_pex_pll_e); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'hvdd-pex-pll-e-supply' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_vddio_pex_ctl); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'vddio-pex-ctl' regulator\n"); + return (rv); + } + rv = regulator_enable(sc->supply_avdd_pll_erefe); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'avdd-pll-erefe-supply' regulator\n"); + return (rv); + } + + rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCX, + sc->clk_pex, sc->hwreset_pex); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'PCX' powergate\n"); + return (rv); + } + + rv = hwreset_deassert(sc->hwreset_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset 'afi' reset\n"); + return (rv); + } + + rv = clk_enable(sc->clk_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'afi' clock\n"); + return (rv); + } + + rv = clk_enable(sc->clk_cml); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'cml' clock\n"); + return (rv); + } + + rv = clk_enable(sc->clk_pll_e); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'pll_e' clock\n"); + return (rv); + } + return (0); +} + +static struct tegra_pcib_port * +tegra_pcib_parse_port(struct tegra_pcib_softc *sc, phandle_t node) +{ + struct tegra_pcib_port *port; + uint32_t tmp[5]; + char tmpstr[6]; + int rv; + + port = malloc(sizeof(struct tegra_pcib_port), M_DEVBUF, M_WAITOK); + + rv = OF_getprop(node, "status", tmpstr, sizeof(tmpstr)); + if (rv <= 0 || strcmp(tmpstr, "okay") == 0 || + strcmp(tmpstr, "ok") == 0) + port->enabled = 1; + else + port->enabled = 0; + + rv = OF_getencprop(node, "assigned-addresses", tmp, sizeof(tmp)); + if (rv != sizeof(tmp)) { + device_printf(sc->dev, "Cannot parse assigned-address: %d\n", + rv); + goto fail; + } + port->rp_base_addr = tmp[2]; + port->rp_size = tmp[4]; + port->port_idx = OFW_PCI_PHYS_HI_DEVICE(tmp[0]) - 1; + if (port->port_idx >= TEGRA_PCIB_MAX_PORTS) { + device_printf(sc->dev, "Invalid port index: %d\n", + port->port_idx); + goto fail; + } + /* XXX - TODO: + * Implement proper function for parsing pci "reg" property: + * - it have PCI bus format + * - its relative to matching "assigned-addresses" + */ + rv = OF_getencprop(node, "reg", tmp, sizeof(tmp)); + if (rv != sizeof(tmp)) { + device_printf(sc->dev, "Cannot parse reg: %d\n", rv); + goto fail; + } + port->rp_base_addr += tmp[2]; + + rv = OF_getencprop(node, "nvidia,num-lanes", &port->num_lanes, + sizeof(port->num_lanes)); + if (rv != sizeof(port->num_lanes)) { + device_printf(sc->dev, "Cannot parse nvidia,num-lanes: %d\n", + rv); + goto fail; + } + if (port->num_lanes > 4) { + device_printf(sc->dev, "Invalid nvidia,num-lanes: %d\n", + port->num_lanes); + goto fail; + } + + port->afi_pex_ctrl = tegra_pcib_pex_ctrl(sc, port->port_idx); + sc->lanes_cfg |= port->num_lanes << (4 * port->port_idx); + + return (port); +fail: + free(port, M_DEVBUF); + return (NULL); +} + + +static int +tegra_pcib_parse_fdt_resources(struct tegra_pcib_softc *sc, phandle_t node) +{ + phandle_t child; + struct tegra_pcib_port *port; + int rv; + + /* Power supplies. */ + rv = regulator_get_by_ofw_property(sc->dev, "avddio-pex-supply", + &sc->supply_avddio_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'avddio-pex' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "dvddio-pex-supply", + &sc->supply_dvddio_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'dvddio-pex' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "avdd-pex-pll-supply", + &sc->supply_avdd_pex_pll); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'avdd-pex-pll' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "hvdd-pex-supply", + &sc->supply_hvdd_pex); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'hvdd-pex' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "hvdd-pex-pll-e-supply", + &sc->supply_hvdd_pex_pll_e); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'hvdd-pex-pll-e' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "vddio-pex-ctl-supply", + &sc->supply_vddio_pex_ctl); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'vddio-pex-ctl' regulator\n"); + return (ENXIO); + } + rv = regulator_get_by_ofw_property(sc->dev, "avdd-pll-erefe-supply", + &sc->supply_avdd_pll_erefe); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get 'avdd-pll-erefe' regulator\n"); + return (ENXIO); + } + + /* Resets. */ + rv = hwreset_get_by_ofw_name(sc->dev, "pex", &sc->hwreset_pex); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pex' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "afi", &sc->hwreset_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'afi' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "pcie_x", &sc->hwreset_pcie_x); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pcie_x' reset\n"); + return (ENXIO); + } + + /* Clocks. */ + rv = clk_get_by_ofw_name(sc->dev, "pex", &sc->clk_pex); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pex' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "afi", &sc->clk_afi); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'afi' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "pll_e", &sc->clk_pll_e); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pll_e' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "cml", &sc->clk_cml); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'cml' clock\n"); + return (ENXIO); + } + + /* Phy. */ + rv = phy_get_by_ofw_name(sc->dev, "pcie", &sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pcie' phy\n"); + return (ENXIO); + } + + /* Ports */ + sc->num_ports = 0; + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + port = tegra_pcib_parse_port(sc, child); + if (port == NULL) { + device_printf(sc->dev, "Cannot parse PCIe port node\n"); + return (ENXIO); + } + sc->ports[sc->num_ports++] = port; + } + + return (0); +} + +static int +tegra_pcib_decode_ranges(struct tegra_pcib_softc *sc, + struct tegra_pci_range *ranges, int nranges) +{ + int i; + + for (i = 2; i < nranges; i++) { + if (ranges[i].space_code == OFW_PCI_PHYS_HI_SPACE_IO) { + if (sc->io_range.size != 0) { + device_printf(sc->dev, + "Duplicated IO range found in DT\n"); + return (ENXIO); + } + sc->io_range = ranges[i]; + } + if ((ranges[i].space_code == OFW_PCI_PHYS_HI_SPACE_MEM32) && + !ranges[i].prefetchable) { + if (sc->mem_range.size != 0) { + device_printf(sc->dev, + "Duplicated memory range found in DT\n"); + return (ENXIO); + } + sc->mem_range = ranges[i]; + } + if ((ranges[i].space_code == OFW_PCI_PHYS_HI_SPACE_MEM32) && + ranges[i].prefetchable) { + if (sc->pref_mem_range.size != 0) { + device_printf(sc->dev, + "Duplicated memory range found in DT\n"); + return (ENXIO); + } + sc->pref_mem_range = ranges[i]; + } + } + if ((sc->io_range.size == 0) || (sc->mem_range.size == 0) + || (sc->pref_mem_range.size == 0)) { + device_printf(sc->dev, + " Not all required ranges are found in DT\n"); + return (ENXIO); + } + return (0); +} + +/* + * Hardware config. + */ +static int +tegra_pcib_wait_for_link(struct tegra_pcib_softc *sc, + struct tegra_pcib_port *port) +{ + uint32_t reg; + int i; + + + /* Setup link detection. */ + reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, + RP_PRIV_MISC, 4); + reg &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; + reg |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; + tegra_pcib_write_config(sc->dev, 0, port->port_idx, 0, + RP_PRIV_MISC, reg, 4); + + for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) { + reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, + RP_VEND_XP, 4); + if (reg & RP_VEND_XP_DL_UP) + break; + + } + if (i <= 0) + return (ETIMEDOUT); + + for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) { + reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0, + RP_LINK_CONTROL_STATUS, 4); + if (reg & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + break; + + } + if (i <= 0) + return (ETIMEDOUT); + return (0); +} + +static void +tegra_pcib_port_enable(struct tegra_pcib_softc *sc, int port_num) +{ + struct tegra_pcib_port *port; + uint32_t reg; + int rv; + + port = sc->ports[port_num]; + + /* Put port to reset. */ + reg = AFI_RD4(sc, port->afi_pex_ctrl); + reg &= ~AFI_PEX_CTRL_RST_L; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + AFI_RD4(sc, port->afi_pex_ctrl); + DELAY(10); + + /* Enable clocks. */ + reg |= AFI_PEX_CTRL_REFCLK_EN; + reg |= AFI_PEX_CTRL_CLKREQ_EN; + reg |= AFI_PEX_CTRL_OVERRIDE_EN; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + AFI_RD4(sc, port->afi_pex_ctrl); + DELAY(100); + + /* Release reset. */ + reg |= AFI_PEX_CTRL_RST_L; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + + rv = tegra_pcib_wait_for_link(sc, port); + if (bootverbose) + device_printf(sc->dev, " port %d (%d lane%s): Link is %s\n", + port->port_idx, port->num_lanes, + port->num_lanes > 1 ? "s": "", + rv == 0 ? "up": "down"); +} + + +static void +tegra_pcib_port_disable(struct tegra_pcib_softc *sc, uint32_t port_num) +{ + struct tegra_pcib_port *port; + uint32_t reg; + + port = sc->ports[port_num]; + + /* Put port to reset. */ + reg = AFI_RD4(sc, port->afi_pex_ctrl); + reg &= ~AFI_PEX_CTRL_RST_L; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + AFI_RD4(sc, port->afi_pex_ctrl); + DELAY(10); + + /* Disable clocks. */ + reg &= ~AFI_PEX_CTRL_CLKREQ_EN; + reg &= ~AFI_PEX_CTRL_REFCLK_EN; + AFI_WR4(sc, port->afi_pex_ctrl, reg); + + if (bootverbose) + device_printf(sc->dev, " port %d (%d lane%s): Disabled\n", + port->port_idx, port->num_lanes, + port->num_lanes > 1 ? "s": ""); +} + +static void +tegra_pcib_set_bar(struct tegra_pcib_softc *sc, int bar, uint32_t axi, + uint64_t fpci, uint32_t size, int is_memory) +{ + uint32_t fpci_reg; + uint32_t axi_reg; + uint32_t size_reg; + + axi_reg = axi & ~0xFFF; + size_reg = size >> 12; + fpci_reg = (uint32_t)(fpci >> 8) & ~0xF; + fpci_reg |= is_memory ? 0x1 : 0x0; + AFI_WR4(sc, bars[bar].axi_start, axi_reg); + AFI_WR4(sc, bars[bar].size, size_reg); + AFI_WR4(sc, bars[bar].fpci_start, fpci_reg); +} + +static int +tegra_pcib_enable(struct tegra_pcib_softc *sc, uint32_t port) +{ + int rv; + int i; + uint32_t reg; + + rv = tegra_pcib_enable_fdt_resources(sc); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable FDT resources\n"); + return (rv); + } + /* Enable PLLE control. */ + reg = AFI_RD4(sc, AFI_PLLE_CONTROL); + reg &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; + reg |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; + AFI_WR4(sc, AFI_PLLE_CONTROL, reg); + + /* Set bias pad. */ + AFI_WR4(sc, AFI_PEXBIAS_CTRL, 0); + + /* Configure mode and ports. */ + reg = AFI_RD4(sc, AFI_PCIE_CONFIG); + reg &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; + if (sc->lanes_cfg == 0x14) { + if (bootverbose) + device_printf(sc->dev, + "Using x1,x4 configuration\n"); + reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1; + } else if (sc->lanes_cfg == 0x12) { + if (bootverbose) + device_printf(sc->dev, + "Using x1,x2 configuration\n"); + reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1; + } else { + device_printf(sc->dev, + "Unsupported lanes configuration: 0x%X\n", sc->lanes_cfg); + } + reg |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL; + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if ((sc->ports[i] != NULL)) + reg &= + ~AFI_PCIE_CONFIG_PCIE_DISABLE(sc->ports[i]->port_idx); + } + AFI_WR4(sc, AFI_PCIE_CONFIG, reg); + + /* Enable Gen2 support. */ + reg = AFI_RD4(sc, AFI_FUSE); + reg &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + AFI_WR4(sc, AFI_FUSE, reg); + + /* Enable PCIe phy. */ + rv = phy_enable(sc->dev, sc->phy); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable phy\n"); + return (rv); + } + + rv = hwreset_deassert(sc->hwreset_pcie_x); + if (rv != 0) { + device_printf(sc->dev, "Cannot unreset 'pci_x' reset\n"); + return (rv); + } + + /* Enable config space. */ + reg = AFI_RD4(sc, AFI_CONFIGURATION); + reg |= AFI_CONFIGURATION_EN_FPCI; + AFI_WR4(sc, AFI_CONFIGURATION, reg); + + /* Enable AFI errors. */ + reg = 0; + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_SLVERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_DECERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_SLVERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_DECERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_WRERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_SM_MSG); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_DFPCI_DECERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_AXI_DECERR); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE); + reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_P2P_ERROR); + AFI_WR4(sc, AFI_AFI_INTR_ENABLE, reg); + AFI_WR4(sc, AFI_SM_INTR_ENABLE, 0xffffffff); + + /* Enable INT, disable MSI. */ + AFI_WR4(sc, AFI_INTR_MASK, AFI_INTR_MASK_INT_MASK); + + /* Mask all FPCI errors. */ + AFI_WR4(sc, AFI_FPCI_ERROR_MASKS, 0); + + /* Setup AFI translation windows. */ + /* BAR 0 - type 1 extended configuration. */ + tegra_pcib_set_bar(sc, 0, rman_get_start(sc->cfg_mem_res), + FPCI_MAP_EXT_TYPE1_CONFIG, rman_get_size(sc->cfg_mem_res), 0); + + /* BAR 1 - downstream I/O. */ + tegra_pcib_set_bar(sc, 1, sc->io_range.host_addr, FPCI_MAP_IO, + sc->io_range.size, 0); + + /* BAR 2 - downstream prefetchable memory 1:1. */ + tegra_pcib_set_bar(sc, 2, sc->pref_mem_range.host_addr, + sc->pref_mem_range.host_addr, sc->pref_mem_range.size, 1); + + /* BAR 3 - downstream not prefetchable memory 1:1 .*/ + tegra_pcib_set_bar(sc, 3, sc->mem_range.host_addr, + sc->mem_range.host_addr, sc->mem_range.size, 1); + + /* BAR 3-8 clear. */ + tegra_pcib_set_bar(sc, 4, 0, 0, 0, 0); + tegra_pcib_set_bar(sc, 5, 0, 0, 0, 0); + tegra_pcib_set_bar(sc, 6, 0, 0, 0, 0); + tegra_pcib_set_bar(sc, 7, 0, 0, 0, 0); + tegra_pcib_set_bar(sc, 8, 0, 0, 0, 0); + + /* MSI BAR - clear. */ + tegra_pcib_set_bar(sc, 9, 0, 0, 0, 0); + return(0); +} + +static int +tegra_pcib_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Nvidia Integrated PCI/PCI-E Controller"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +tegra_pcib_attach(device_t dev) +{ + struct tegra_pcib_softc *sc; + phandle_t node; + uint32_t unit; + int rv; + int rid; + int nranges; + struct tegra_pci_range *ranges; + struct tegra_pcib_port *port; + int i; + + sc = device_get_softc(dev); + sc->dev = dev; + unit = fdt_get_unit(dev); + mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF); + + + node = ofw_bus_get_node(dev); + + rv = tegra_pcib_parse_fdt_resources(sc, node); + if (rv != 0) { + device_printf(dev, "Cannot get FDT resources\n"); + return (rv); + } + + nranges = tegra_pci_get_ranges(node, &ranges); + if (nranges != 5) { + device_printf(sc->dev, "Unexpected number of ranges: %d\n", + nranges); + rv = ENXIO; + goto out; + } + + /* Allocate bus_space resources. */ + rid = 0; + sc->pads_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->pads_mem_res == NULL) { + device_printf(dev, "Cannot allocate PADS register\n"); + rv = ENXIO; + goto out; + } + /* + * XXX - FIXME + * tag for config space is not filled when RF_ALLOCATED flag is used. + */ + sc->bus_tag = rman_get_bustag(sc->pads_mem_res); + + rid = 1; + sc->afi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->afi_mem_res == NULL) { + device_printf(dev, "Cannot allocate AFI register\n"); + rv = ENXIO; + goto out; + } + + rid = 2; + sc->cfg_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ALLOCATED); + if (sc->cfg_mem_res == NULL) { + device_printf(dev, "Cannot allocate config space memory\n"); + rv = ENXIO; + goto out; + } + sc->cfg_base_addr = rman_get_start(sc->cfg_mem_res); + + + /* Map RP slots */ + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if (sc->ports[i] == NULL) + continue; + port = sc->ports[i]; + rv = bus_space_map(sc->bus_tag, port->rp_base_addr, + port->rp_size, 0, &port->cfg_handle); + if (rv != 0) { + device_printf(sc->dev, "Cannot allocate memory for " + "port: %d\n", i); + rv = ENXIO; + goto out; + } + } + + /* + * Get PCI interrupt info. + */ + ofw_bus_setup_iinfo(node, &sc->pci_iinfo, sizeof(pcell_t)); + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + rv = ENXIO; + goto out; + } + + rid = 1; + sc->msi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate MSI IRQ resources\n"); + rv = ENXIO; + goto out; + } + + if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + tegra_pci_intr, NULL, sc, &sc->intr_cookie)) { + device_printf(dev, "cannot setup interrupt handler\n"); + rv = ENXIO; + goto out; + } + + /* Memory management. */ + rv = tegra_pcib_decode_ranges(sc, ranges, nranges); + if (rv != 0) + goto out; + + rv = tegra_pcib_rman_init(sc); + if (rv != 0) + goto out; + free(ranges, M_DEVBUF); + ranges = NULL; + + /* + * Enable PCIE device. + */ + rv = tegra_pcib_enable(sc, unit); + if (rv != 0) + goto out; + for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) { + if (sc->ports[i] == NULL) + continue; + if (sc->ports[i]->enabled) + tegra_pcib_port_enable(sc, i); + else + tegra_pcib_port_disable(sc, i); + } + + device_add_child(dev, "pci", -1); + + return (bus_generic_attach(dev)); + +out: + if (ranges != NULL) + free(ranges, M_DEVBUF); + + return (rv); +} + + +static device_method_t tegra_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_pcib_probe), + DEVMETHOD(device_attach, tegra_pcib_attach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, tegra_pcib_read_ivar), + DEVMETHOD(bus_write_ivar, tegra_pcib_write_ivar), + DEVMETHOD(bus_alloc_resource, tegra_pcib_alloc_resource), + DEVMETHOD(bus_adjust_resource, tegra_pcib_adjust_resource), + DEVMETHOD(bus_release_resource, tegra_pcib_release_resource), + DEVMETHOD(bus_activate_resource, tegra_pcib_pcie_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, tegra_pcib_maxslots), + DEVMETHOD(pcib_read_config, tegra_pcib_read_config), + DEVMETHOD(pcib_write_config, tegra_pcib_write_config), + DEVMETHOD(pcib_route_interrupt, tegra_pcib_route_interrupt), + +#if defined(TEGRA_PCI_MSI) + DEVMETHOD(pcib_alloc_msi, tegra_pcib_alloc_msi), + DEVMETHOD(pcib_release_msi, tegra_pcib_release_msi), + DEVMETHOD(pcib_map_msi, tegra_pcib_map_msi), +#endif + + /* OFW bus interface */ + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +static driver_t tegra_pcib_driver = { + "pcib", + tegra_pcib_methods, + sizeof(struct tegra_pcib_softc), +}; + +devclass_t pcib_devclass; + +DRIVER_MODULE(pcib, simplebus, tegra_pcib_driver, pcib_devclass, 0, 0); \ No newline at end of file diff --git a/sys/arm/nvidia/tegra_pinmux.c b/sys/arm/nvidia/tegra_pinmux.c new file mode 100644 index 0000000..6123746 --- /dev/null +++ b/sys/arm/nvidia/tegra_pinmux.c @@ -0,0 +1,804 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * Pin multiplexer driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* Pin multipexor register. */ +#define TEGRA_MUX_FUNCTION_MASK 0x03 +#define TEGRA_MUX_FUNCTION_SHIFT 0 +#define TEGRA_MUX_PUPD_MASK 0x03 +#define TEGRA_MUX_PUPD_SHIFT 2 +#define TEGRA_MUX_TRISTATE_SHIFT 4 +#define TEGRA_MUX_ENABLE_INPUT_SHIFT 5 +#define TEGRA_MUX_OPEN_DRAIN_SHIFT 6 +#define TEGRA_MUX_LOCK_SHIFT 7 +#define TEGRA_MUX_IORESET_SHIFT 8 +#define TEGRA_MUX_RCV_SEL_SHIFT 9 + + +/* Pin goup register. */ +#define TEGRA_GRP_HSM_SHIFT 2 +#define TEGRA_GRP_SCHMT_SHIFT 3 +#define TEGRA_GRP_DRV_TYPE_SHIFT 6 +#define TEGRA_GRP_DRV_TYPE_MASK 0x03 +#define TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT 28 +#define TEGRA_GRP_DRV_DRVDN_SLWR_MASK 0x03 +#define TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT 30 +#define TEGRA_GRP_DRV_DRVUP_SLWF_MASK 0x03 + +struct pinmux_softc { + device_t dev; + struct resource *pad_mem_res; + struct resource *mux_mem_res; + struct resource *mipi_mem_res; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-pinmux", 1}, + {NULL, 0}, +}; + +enum prop_id { + PROP_ID_PULL, + PROP_ID_TRISTATE, + PROP_ID_ENABLE_INPUT, + PROP_ID_OPEN_DRAIN, + PROP_ID_LOCK, + PROP_ID_IORESET, + PROP_ID_RCV_SEL, + PROP_ID_HIGH_SPEED_MODE, + PROP_ID_SCHMITT, + PROP_ID_LOW_POWER_MODE, + PROP_ID_DRIVE_DOWN_STRENGTH, + PROP_ID_DRIVE_UP_STRENGTH, + PROP_ID_SLEW_RATE_FALLING, + PROP_ID_SLEW_RATE_RISING, + PROP_ID_DRIVE_TYPE, + + PROP_ID_MAX_ID +}; + +/* Numeric based parameters. */ +static const struct prop_name { + const char *name; + enum prop_id id; +} prop_names[] = { + {"nvidia,pull", PROP_ID_PULL}, + {"nvidia,tristate", PROP_ID_TRISTATE}, + {"nvidia,enable-input", PROP_ID_ENABLE_INPUT}, + {"nvidia,open-drain", PROP_ID_OPEN_DRAIN}, + {"nvidia,lock", PROP_ID_LOCK}, + {"nvidia,io-reset", PROP_ID_IORESET}, + {"nvidia,rcv-sel", PROP_ID_RCV_SEL}, + {"nvidia,high-speed-mode", PROP_ID_HIGH_SPEED_MODE}, + {"nvidia,schmitt", PROP_ID_SCHMITT}, + {"nvidia,low-power-mode", PROP_ID_LOW_POWER_MODE}, + {"nvidia,pull-down-strength", PROP_ID_DRIVE_DOWN_STRENGTH}, + {"nvidia,pull-up-strength", PROP_ID_DRIVE_UP_STRENGTH}, + {"nvidia,slew-rate-falling", PROP_ID_SLEW_RATE_FALLING}, + {"nvidia,slew-rate-rising", PROP_ID_SLEW_RATE_RISING}, + {"nvidia,drive-type", PROP_ID_DRIVE_TYPE}, +}; + +/* + * configuration for one pin group. + */ +struct pincfg { + char *function; + int params[PROP_ID_MAX_ID]; +}; +#define GPIO_BANK_A 0 +#define GPIO_BANK_B 1 +#define GPIO_BANK_C 2 +#define GPIO_BANK_D 3 +#define GPIO_BANK_E 4 +#define GPIO_BANK_F 5 +#define GPIO_BANK_G 6 +#define GPIO_BANK_H 7 +#define GPIO_BANK_I 8 +#define GPIO_BANK_J 9 +#define GPIO_BANK_K 10 +#define GPIO_BANK_L 11 +#define GPIO_BANK_M 12 +#define GPIO_BANK_N 13 +#define GPIO_BANK_O 14 +#define GPIO_BANK_P 15 +#define GPIO_BANK_Q 16 +#define GPIO_BANK_R 17 +#define GPIO_BANK_S 18 +#define GPIO_BANK_T 19 +#define GPIO_BANK_U 20 +#define GPIO_BANK_V 21 +#define GPIO_BANK_W 22 +#define GPIO_BANK_X 23 +#define GPIO_BANK_Y 24 +#define GPIO_BANK_Z 25 +#define GPIO_BANK_AA 26 +#define GPIO_BANK_BB 27 +#define GPIO_BANK_CC 28 +#define GPIO_BANK_DD 29 +#define GPIO_BANK_EE 30 +#define GPIO_BANK_FF 31 +#define GPIO_NUM(b, p) (8 * (b) + (p)) + +struct tegra_mux { + char *name; + bus_size_t reg; + char *functions[4]; + int gpio_num; +}; + +#define GMUX(r, gb, gi, nm, f1, f2, f3, f4) \ +{ \ + .name = #nm, \ + .reg = r, \ + .gpio_num = GPIO_NUM(GPIO_BANK_##gb, gi), \ + .functions = {#f1, #f2, #f3, #f4}, \ +} + +#define FMUX(r, nm, f1, f2, f3, f4) \ +{ \ + .name = #nm, \ + .reg = r, \ + .gpio_num = -1, \ + .functions = {#f1, #f2, #f3, #f4}, \ +} + +static const struct tegra_mux pin_mux_tbl[] = { + GMUX(0x000, O, 1, ulpi_data0_po1, spi3, hsi, uarta, ulpi), + GMUX(0x004, O, 2, ulpi_data1_po2, spi3, hsi, uarta, ulpi), + GMUX(0x008, O, 3, ulpi_data2_po3, spi3, hsi, uarta, ulpi), + GMUX(0x00C, O, 4, ulpi_data3_po4, spi3, hsi, uarta, ulpi), + GMUX(0x010, O, 5, ulpi_data4_po5, spi2, hsi, uarta, ulpi), + GMUX(0x014, O, 6, ulpi_data5_po6, spi2, hsi, uarta, ulpi), + GMUX(0x018, O, 7, ulpi_data6_po7, spi2, hsi, uarta, ulpi), + GMUX(0x01C, O, 0, ulpi_data7_po0, spi2, hsi, uarta, ulpi), + GMUX(0x020, P, 9, ulpi_clk_py0, spi1, spi5, uartd, ulpi), + GMUX(0x024, P, 1, ulpi_dir_py1, spi1, spi5, uartd, ulpi), + GMUX(0x028, P, 2, ulpi_nxt_py2, spi1, spi5, uartd, ulpi), + GMUX(0x02C, P, 3, ulpi_stp_py3, spi1, spi5, uartd, ulpi), + GMUX(0x030, P, 0, dap3_fs_pp0, i2s2, spi5, displaya, displayb), + GMUX(0x034, P, 1, dap3_din_pp1, i2s2, spi5, displaya, displayb), + GMUX(0x038, P, 2, dap3_dout_pp2, i2s2, spi5, displaya, rsvd4), + GMUX(0x03C, P, 3, dap3_sclk_pp3, i2s2, spi5, rsvd3, displayb), + GMUX(0x040, V, 0, pv0, rsvd1, rsvd2, rsvd3, rsvd4), + GMUX(0x044, V, 1, pv1, rsvd1, rsvd2, rsvd3, rsvd4), + GMUX(0x048, Z, 0, sdmmc1_clk_pz0, sdmmc1, clk12, rsvd3, rsvd4), + GMUX(0x04C, Z, 1, sdmmc1_cmd_pz1, sdmmc1, spdif, spi4, uarta), + GMUX(0x050, Y, 4, sdmmc1_dat3_py4, sdmmc1, spdif, spi4, uarta), + GMUX(0x054, Y, 5, sdmmc1_dat2_py5, sdmmc1, pwm0, spi4, uarta), + GMUX(0x058, Y, 6, sdmmc1_dat1_py6, sdmmc1, pwm1, spi4, uarta), + GMUX(0x05C, Y, 7, sdmmc1_dat0_py7, sdmmc1, rsvd2, spi4, uarta), + GMUX(0x068, W, 5, clk2_out_pw5, extperiph2, rsvd2, rsvd3, rsvd4), + GMUX(0x06C, CC, 5, clk2_req_pcc5, dap, rsvd2, rsvd3, rsvd4), + GMUX(0x110, N, 7, hdmi_int_pn7, rsvd1, rsvd2, rsvd3, rsvd4), + GMUX(0x114, V, 4, ddc_scl_pv4, i2c4, rsvd2, rsvd3, rsvd4), + GMUX(0x118, V, 5, ddc_sda_pv5, i2c4, rsvd2, rsvd3, rsvd4), + GMUX(0x164, V, 3, uart2_rxd_pc3, irda, spdif, uarta, spi4), + GMUX(0x168, C, 2, uart2_txd_pc2, irda, spdif, uarta, spi4), + GMUX(0x16C, J, 6, uart2_rts_n_pj6, uarta, uartb, gmi, spi4), + GMUX(0x170, J, 5, uart2_cts_n_pj5, uarta, uartb, gmi, spi4), + GMUX(0x174, W, 6, uart3_txd_pw6, uartc, rsvd2, gmi, spi4), + GMUX(0x178, W, 7, uart3_rxd_pw7, uartc, rsvd2, gmi, spi4), + GMUX(0x17C, S, 1, uart3_cts_n_pa1, uartc, sdmmc1, dtv, gmi), + GMUX(0x180, C, 0, uart3_rts_n_pc0, uartc, pwm0, dtv, gmi), + GMUX(0x184, U, 0, pu0, owr, uarta, gmi, rsvd4), + GMUX(0x188, U, 1, pu1, rsvd1, uarta, gmi, rsvd4), + GMUX(0x18C, U, 2, pu2, rsvd1, uarta, gmi, rsvd4), + GMUX(0x190, U, 3, pu3, pwm0, uarta, gmi, displayb), + GMUX(0x194, U, 4, pu4, pwm1, uarta, gmi, displayb), + GMUX(0x198, U, 5, pu5, pwm2, uarta, gmi, displayb), + GMUX(0x19C, U, 6, pu6, pwm3, uarta, rsvd3, gmi), + GMUX(0x1A0, C, 5, gen1_i2c_sda_pc5, i2c1, rsvd2, rsvd3, rsvd4), + GMUX(0x1A4, C, 4, gen1_i2c_scl_pc4, i2c1, rsvd2, rsvd3, rsvd4), + GMUX(0x1A8, P, 3, dap4_fs_pp4, i2s3, gmi, dtv, rsvd4), + GMUX(0x1AC, P, 4, dap4_din_pp5, i2s3, gmi, rsvd3, rsvd4), + GMUX(0x1B0, P, 5, dap4_dout_pp6, i2s3, gmi, dtv, rsvd4), + GMUX(0x1B4, P, 7, dap4_sclk_pp7, i2s3, gmi, rsvd3, rsvd4), + GMUX(0x1B8, P, 0, clk3_out_pee0, extperiph3, rsvd2, rsvd3, rsvd4), + GMUX(0x1BC, EE, 1, clk3_req_pee1, dev3, rsvd2, rsvd3, rsvd4), + GMUX(0x1C0, C, 7, pc7, rsvd1, rsvd2, gmi, gmi_alt), + GMUX(0x1C4, I, 5, pi5, sdmmc2, rsvd2, gmi, rsvd4), + GMUX(0x1C8, I, 7, pi7, rsvd1, trace, gmi, dtv), + GMUX(0x1CC, K, 0, pk0, rsvd1, sdmmc3, gmi, soc), + GMUX(0x1D0, K, 1, pk1, sdmmc2, trace, gmi, rsvd4), + GMUX(0x1D4, J, 0, pj0, rsvd1, rsvd2, gmi, usb), + GMUX(0x1D8, J, 2, pj2, rsvd1, rsvd2, gmi, soc), + GMUX(0x1DC, K, 3, pk3, sdmmc2, trace, gmi, ccla), + GMUX(0x1E0, K, 4, pk4, sdmmc2, rsvd2, gmi, gmi_alt), + GMUX(0x1E4, K, 2, pk2, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x1E8, I, 3, pi3, rsvd1, rsvd2, gmi, spi4), + GMUX(0x1EC, I, 6, pi6, rsvd1, rsvd2, gmi, sdmmc2), + GMUX(0x1F0, G, 0, pg0, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x1F4, G, 1, pg1, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x1F8, G, 2, pg2, rsvd1, trace, gmi, rsvd4), + GMUX(0x1FC, G, 3, pg3, rsvd1, trace, gmi, rsvd4), + GMUX(0x200, G, 4, pg4, rsvd1, tmds, gmi, spi4), + GMUX(0x204, G, 5, pg5, rsvd1, rsvd2, gmi, spi4), + GMUX(0x208, G, 6, pg6, rsvd1, rsvd2, gmi, spi4), + GMUX(0x20C, G, 7, pg7, rsvd1, rsvd2, gmi, spi4), + GMUX(0x210, H, 0, ph0, pwm0, trace, gmi, dtv), + GMUX(0x214, H, 1, ph1, pwm1, tmds, gmi, displaya), + GMUX(0x218, H, 2, ph2, pwm2, tmds, gmi, cldvfs), + GMUX(0x21C, H, 3, ph3, pwm3, spi4, gmi, cldvfs), + GMUX(0x220, H, 4, ph4, sdmmc2, rsvd2, gmi, rsvd4), + GMUX(0x224, H, 5, ph5, sdmmc2, rsvd2, gmi, rsvd4), + GMUX(0x228, H, 6, ph6, sdmmc2, trace, gmi, dtv), + GMUX(0x22C, H, 7, ph7, sdmmc2, trace, gmi, dtv), + GMUX(0x230, J, 7, pj7, uartd, rsvd2, gmi, gmi_alt), + GMUX(0x234, B, 0, pb0, uartd, rsvd2, gmi, rsvd4), + GMUX(0x238, B, 1, pb1, uartd, rsvd2, gmi, rsvd4), + GMUX(0x23C, K, 7, pk7, uartd, rsvd2, gmi, rsvd4), + GMUX(0x240, I, 0, pi0, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x244, I, 1, pi1, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x248, I, 2, pi2, sdmmc2, trace, gmi, rsvd4), + GMUX(0x24C, I, 4, pi4, spi4, trace, gmi, displaya), + GMUX(0x250, T, 5, gen2_i2c_scl_pt5, i2c2, rsvd2, gmi, rsvd4), + GMUX(0x254, T, 6, gen2_i2c_sda_pt6, i2c2, rsvd2, gmi, rsvd4), + GMUX(0x258, CC, 4, sdmmc4_clk_pcc4, sdmmc4, rsvd2, gmi, rsvd4), + GMUX(0x25C, T, 7, sdmmc4_cmd_pt7, sdmmc4, rsvd2, gmi, rsvd4), + GMUX(0x260, AA, 0, sdmmc4_dat0_paa0, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x264, AA, 1, sdmmc4_dat1_paa1, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x268, AA, 2, sdmmc4_dat2_paa2, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x26C, AA, 3, sdmmc4_dat3_paa3, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x270, AA, 4, sdmmc4_dat4_paa4, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x274, AA, 5, sdmmc4_dat5_paa5, sdmmc4, spi3, rsvd3, rsvd4), + GMUX(0x278, AA, 6, sdmmc4_dat6_paa6, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x27C, AA, 7, sdmmc4_dat7_paa7, sdmmc4, rsvd2, gmi, rsvd4), + GMUX(0x284, CC, 0, cam_mclk_pcc0, vi, vi_alt1, vi_alt3, sdmmc2), + GMUX(0x288, CC, 1, pcc1, i2s4, rsvd2, rsvd3, sdmmc2), + GMUX(0x28C, BB, 0, pbb0, vgp6, vimclk2, sdmmc2, vimclk2_alt), + GMUX(0x290, BB, 1, cam_i2c_scl_pbb1, vgp1, i2c3, rsvd3, sdmmc2), + GMUX(0x294, BB, 2, cam_i2c_sda_pbb2, vgp2, i2c3, rsvd3, sdmmc2), + GMUX(0x298, BB, 3, pbb3, vgp3, displaya, displayb, sdmmc2), + GMUX(0x29C, BB, 4, pbb4, vgp4, displaya, displayb, sdmmc2), + GMUX(0x2A0, BB, 5, pbb5, vgp5, displaya, rsvd3, sdmmc2), + GMUX(0x2A4, BB, 6, pbb6, i2s4, rsvd2, displayb, sdmmc2), + GMUX(0x2A8, BB, 7, pbb7, i2s4, rsvd2, rsvd3, sdmmc2), + GMUX(0x2AC, CC, 2, pcc2, i2s4, rsvd2, sdmmc3, sdmmc2), + FMUX(0x2B0, jtag_rtck, rtck, rsvd2, rsvd3, rsvd4), + GMUX(0x2B4, Z, 6, pwr_i2c_scl_pz6, i2cpwr, rsvd2, rsvd3, rsvd4), + GMUX(0x2B8, Z, 7, pwr_i2c_sda_pz7, i2cpwr, rsvd2, rsvd3, rsvd4), + GMUX(0x2BC, R, 0, kb_row0_pr0, kbc, rsvd2, rsvd3, rsvd4), + GMUX(0x2C0, R, 1, kb_row1_pr1, kbc, rsvd2, rsvd3, rsvd4), + GMUX(0x2C4, R, 2, kb_row2_pr2, kbc, rsvd2, rsvd3, rsvd4), + GMUX(0x2C8, R, 3, kb_row3_pr3, kbc, displaya, sys, displayb), + GMUX(0x2CC, R, 4, kb_row4_pr4, kbc, displaya, rsvd3, displayb), + GMUX(0x2D0, R, 5, kb_row5_pr5, kbc, displaya, rsvd3, displayb), + GMUX(0x2D4, R, 6, kb_row6_pr6, kbc, displaya, displaya_alt, displayb), + GMUX(0x2D8, R, 7, kb_row7_pr7, kbc, rsvd2, cldvfs, uarta), + GMUX(0x2DC, S, 0, kb_row8_ps0, kbc, rsvd2, cldvfs, uarta), + GMUX(0x2E0, S, 1, kb_row9_ps1, kbc, rsvd2, rsvd3, uarta), + GMUX(0x2E4, S, 2, kb_row10_ps2, kbc, rsvd2, rsvd3, uarta), + GMUX(0x2E8, S, 3, kb_row11_ps3, kbc, rsvd2, rsvd3, irda), + GMUX(0x2EC, S, 4, kb_row12_ps4, kbc, rsvd2, rsvd3, irda), + GMUX(0x2F0, S, 5, kb_row13_ps5, kbc, rsvd2, spi2, rsvd4), + GMUX(0x2F4, S, 6, kb_row14_ps6, kbc, rsvd2, spi2, rsvd4), + GMUX(0x2F8, S, 7, kb_row15_ps7, kbc, soc, rsvd3, rsvd4), + GMUX(0x2FC, Q, 0, kb_col0_pq0, kbc, rsvd2, spi2, rsvd4), + GMUX(0x300, Q, 1, kb_col1_pq1, kbc, rsvd2, spi2, rsvd4), + GMUX(0x304, Q, 2, kb_col2_pq2, kbc, rsvd2, spi2, rsvd4), + GMUX(0x308, Q, 3, kb_col3_pq3, kbc, displaya, pwm2, uarta), + GMUX(0x30C, Q, 4, kb_col4_pq4, kbc, owr, sdmmc3, uarta), + GMUX(0x310, Q, 5, kb_col5_pq5, kbc, rsvd2, sdmmc3, rsvd4), + GMUX(0x314, Q, 6, kb_col6_pq6, kbc, rsvd2, spi2, uartd), + GMUX(0x318, Q, 7, kb_col7_pq7, kbc, rsvd2, spi2, uartd), + GMUX(0x31C, A, 0, clk_32k_out_pa0, blink, soc, rsvd3, rsvd4), + FMUX(0x324, core_pwr_req, pwron, rsvd2, rsvd3, rsvd4), + FMUX(0x328, cpu_pwr_req, cpu, rsvd2, rsvd3, rsvd4), + FMUX(0x32C, pwr_int_n, pmi, rsvd2, rsvd3, rsvd4), + FMUX(0x330, clk_32k_in, clk, rsvd2, rsvd3, rsvd4), + FMUX(0x334, owr, owr, rsvd2, rsvd3, rsvd4), + GMUX(0x338, N, 0, dap1_fs_pn0, i2s0, hda, gmi, rsvd4), + GMUX(0x33C, N, 1, dap1_din_pn1, i2s0, hda, gmi, rsvd4), + GMUX(0x340, N, 2, dap1_dout_pn2, i2s0, hda, gmi, sata), + GMUX(0x344, N, 3, dap1_sclk_pn3, i2s0, hda, gmi, rsvd4), + GMUX(0x348, EE, 2, dap_mclk1_req_pee2, dap, dap1, sata, rsvd4), + GMUX(0x34C, W, 4, dap_mclk1_pw4, extperiph1, dap2, rsvd3, rsvd4), + GMUX(0x350, K, 6, spdif_in_pk6, spdif, rsvd2, rsvd3, i2c3), + GMUX(0x354, K, 5, spdif_out_pk5, spdif, rsvd2, rsvd3, i2c3), + GMUX(0x358, A, 2, dap2_fs_pa2, i2s1, hda, gmi, rsvd4), + GMUX(0x35C, A, 4, dap2_din_pa4, i2s1, hda, gmi, rsvd4), + GMUX(0x360, A, 5, dap2_dout_pa5, i2s1, hda, gmi, rsvd4), + GMUX(0x364, A, 3, dap2_sclk_pa3, i2s1, hda, gmi, rsvd4), + GMUX(0x368, X, 0, dvfs_pwm_px0, spi6, cldvfs, gmi, rsvd4), + GMUX(0x36C, X, 1, gpio_x1_aud_px1, spi6, rsvd2, gmi, rsvd4), + GMUX(0x370, X, 3, gpio_x3_aud_px3, spi6, spi1, gmi, rsvd4), + GMUX(0x374, X, 2, dvfs_clk_px2, spi6, cldvfs, gmi, rsvd4), + GMUX(0x378, X, 4, gpio_x4_aud_px4, gmi, spi1, spi2, dap2), + GMUX(0x37C, X, 5, gpio_x5_aud_px5, gmi, spi1, spi2, rsvd4), + GMUX(0x380, X, 6, gpio_x6_aud_px6, spi6, spi1, spi2, gmi), + GMUX(0x384, X, 7, gpio_x7_aud_px7, rsvd1, spi1, spi2, rsvd4), + GMUX(0x390, A, 6, sdmmc3_clk_pa6, sdmmc3, rsvd2, rsvd3, spi3), + GMUX(0x394, A, 7, sdmmc3_cmd_pa7, sdmmc3, pwm3, uarta, spi3), + GMUX(0x398, B, 7, sdmmc3_dat0_pb7, sdmmc3, rsvd2, rsvd3, spi3), + GMUX(0x39C, B, 6, sdmmc3_dat1_pb6, sdmmc3, pwm2, uarta, spi3), + GMUX(0x3A0, B, 5, sdmmc3_dat2_pb5, sdmmc3, pwm1, displaya, spi3), + GMUX(0x3A4, B, 4, sdmmc3_dat3_pb4, sdmmc3, pwm0, displayb, spi3), + GMUX(0x3BC, DD, 1, pex_l0_rst_n_pdd1, pe0, rsvd2, rsvd3, rsvd4), + GMUX(0x3C0, DD, 2, pex_l0_clkreq_n_pdd2, pe0, rsvd2, rsvd3, rsvd4), + GMUX(0x3C4, DD, 3, pex_wake_n_pdd3, pe, rsvd2, rsvd3, rsvd4), + GMUX(0x3CC, DD, 5, pex_l1_rst_n_pdd5, pe1, rsvd2, rsvd3, rsvd4), + GMUX(0x3D0, DD, 6, pex_l1_clkreq_n_pdd6, pe1, rsvd2, rsvd3, rsvd4), + GMUX(0x3E0, EE, 3, hdmi_cec_pee3, cec, rsvd2, rsvd3, rsvd4), + GMUX(0x3E4, V, 3, sdmmc1_wp_n_pv3, sdmmc1, clk12, spi4, uarta), + GMUX(0x3E8, V, 2, sdmmc3_cd_n_pv2, sdmmc3, owr, rsvd3, rsvd4), + GMUX(0x3EC, W, 2, gpio_w2_aud_pw2, spi6, rsvd2, spi2, i2c1), + GMUX(0x3F0, W, 3, gpio_w3_aud_pw3, spi6, spi1, spi2, i2c1), + GMUX(0x3F4, N, 4, usb_vbus_en0_pn4, usb, rsvd2, rsvd3, rsvd4), + GMUX(0x3F8, N, 5, usb_vbus_en1_pn5, usb, rsvd2, rsvd3, rsvd4), + GMUX(0x3FC, EE, 5, sdmmc3_clk_lb_in_pee5, sdmmc3, rsvd2, rsvd3, rsvd4), + GMUX(0x400, EE, 4, sdmmc3_clk_lb_out_pee4, sdmmc3, rsvd2, rsvd3, rsvd4), + FMUX(0x404, gmi_clk_lb, sdmmc2, rsvd2, gmi, rsvd4), + FMUX(0x408, reset_out_n, rsvd1, rsvd2, rsvd3, reset_out_n), + GMUX(0x40C, T, 0, kb_row16_pt0, kbc, rsvd2, rsvd3, uartc), + GMUX(0x410, T, 1, kb_row17_pt1, kbc, rsvd2, rsvd3, uartc), + GMUX(0x414, FF, 1, usb_vbus_en2_pff1, usb, rsvd2, rsvd3, rsvd4), + GMUX(0x418, FF, 2, pff2, sata, rsvd2, rsvd3, rsvd4), + GMUX(0x430, FF, 0, dp_hpd_pff0, dp, rsvd2, rsvd3, rsvd4), +}; + +struct tegra_grp { + char *name; + bus_size_t reg; + int drvdn_shift; + int drvdn_mask; + int drvup_shift; + int drvup_mask; +}; + +#define GRP(r, nm, dn_s, dn_w, up_s, up_w) \ +{ \ + .name = #nm, \ + .reg = r - 0x868, \ + .drvdn_shift = dn_s, \ + .drvdn_mask = (1 << dn_w) - 1, \ + .drvup_shift = up_s, \ + .drvup_mask = (1 << dn_w) - 1, \ +} + +/* Use register offsets from TRM */ +static const struct tegra_grp pin_grp_tbl[] = { + GRP(0x868, ao1, 12, 5, 20, 5), + GRP(0x86C, ao2, 12, 5, 20, 5), + GRP(0x870, at1, 12, 7, 20, 7), + GRP(0x874, at2, 12, 7, 20, 7), + GRP(0x878, at3, 12, 7, 20, 7), + GRP(0x87C, at4, 12, 7, 20, 7), + GRP(0x880, at5, 14, 5, 19, 5), + GRP(0x884, cdev1, 12, 5, 20, 5), + GRP(0x888, cdev2, 12, 5, 20, 5), + GRP(0x890, dap1, 12, 5, 20, 5), + GRP(0x894, dap2, 12, 5, 20, 5), + GRP(0x898, dap3, 12, 5, 20, 5), + GRP(0x89C, dap4, 12, 5, 20, 5), + GRP(0x8A0, dbg, 12, 5, 20, 5), + GRP(0x8B0, sdio3, 12, 7, 20, 7), + GRP(0x8B4, spi, 12, 5, 20, 5), + GRP(0x8B8, uaa, 12, 5, 20, 5), + GRP(0x8BC, uab, 12, 5, 20, 5), + GRP(0x8C0, uart2, 12, 5, 20, 5), + GRP(0x8C4, uart3, 12, 5, 20, 5), + GRP(0x8EC, sdio1, 12, 7, 20, 7), + GRP(0x8FC, ddc, 12, 5, 20, 5), + GRP(0x900, gma, 14, 5, 20, 5), + GRP(0x910, gme, 14, 5, 19, 5), + GRP(0x914, gmf, 14, 5, 19, 5), + GRP(0x918, gmg, 14, 5, 19, 5), + GRP(0x91C, gmh, 14, 5, 19, 5), + GRP(0x920, owr, 12, 5, 20, 5), + GRP(0x924, uda, 12, 5, 20, 5), + GRP(0x928, gpv, 12, 5, 20, 5), + GRP(0x92C, dev3, 12, 5, 20, 5), + GRP(0x938, cec, 12, 5, 20, 5), + GRP(0x994, at6, 12, 7, 20, 7), + GRP(0x998, dap5, 12, 5, 20, 5), + GRP(0x99C, usb_vbus_en, 12, 5, 20, 5), + GRP(0x9A8, ao3, 12, 5, -1, 0), + GRP(0x9B0, ao0, 12, 5, 20, 5), + GRP(0x9B4, hv0, 12, 5, -1, 0), + GRP(0x9C4, sdio4, 12, 5, 20, 5), + GRP(0x9C8, ao4, 12, 7, 20, 7), +}; + +static const struct tegra_grp * +pinmux_search_grp(char *grp_name) +{ + int i; + + for (i = 0; i < nitems(pin_grp_tbl); i++) { + if (strcmp(grp_name, pin_grp_tbl[i].name) == 0) + return (&pin_grp_tbl[i]); + } + return (NULL); +} + +static const struct tegra_mux * +pinmux_search_mux(char *pin_name) +{ + int i; + + for (i = 0; i < nitems(pin_mux_tbl); i++) { + if (strcmp(pin_name, pin_mux_tbl[i].name) == 0) + return (&pin_mux_tbl[i]); + } + return (NULL); +} + +static int +pinmux_mux_function(const struct tegra_mux *mux, char *fnc_name) +{ + int i; + + for (i = 0; i < 4; i++) { + if (strcmp(fnc_name, mux->functions[i]) == 0) + return (i); + } + return (-1); +} + +static int +pinmux_config_mux(struct pinmux_softc *sc, char *pin_name, + const struct tegra_mux *mux, struct pincfg *cfg) +{ + int tmp; + uint32_t reg; + + reg = bus_read_4(sc->mux_mem_res, mux->reg); + + if (cfg->function != NULL) { + tmp = pinmux_mux_function(mux, cfg->function); + if (tmp == -1) { + device_printf(sc->dev, + "Unknown function %s for pin %s\n", cfg->function, + pin_name); + return (ENXIO); + } + reg &= ~(TEGRA_MUX_FUNCTION_MASK << TEGRA_MUX_FUNCTION_SHIFT); + reg |= (tmp & TEGRA_MUX_FUNCTION_MASK) << + TEGRA_MUX_FUNCTION_SHIFT; + } + if (cfg->params[PROP_ID_PULL] != -1) { + reg &= ~(TEGRA_MUX_PUPD_MASK << TEGRA_MUX_PUPD_SHIFT); + reg |= (cfg->params[PROP_ID_PULL] & TEGRA_MUX_PUPD_MASK) << + TEGRA_MUX_PUPD_SHIFT; + } + if (cfg->params[PROP_ID_TRISTATE] != -1) { + reg &= ~(1 << TEGRA_MUX_TRISTATE_SHIFT); + reg |= (cfg->params[PROP_ID_TRISTATE] & 1) << + TEGRA_MUX_TRISTATE_SHIFT; + } + if (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] != -1) { + reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT); + reg |= (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] & 1) << + TEGRA_MUX_ENABLE_INPUT_SHIFT; + } + if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) { + reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT); + reg |= (cfg->params[PROP_ID_ENABLE_INPUT] & 1) << + TEGRA_MUX_ENABLE_INPUT_SHIFT; + } + if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) { + reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT); + reg |= (cfg->params[PROP_ID_OPEN_DRAIN] & 1) << + TEGRA_MUX_ENABLE_INPUT_SHIFT; + } + if (cfg->params[PROP_ID_LOCK] != -1) { + reg &= ~(1 << TEGRA_MUX_LOCK_SHIFT); + reg |= (cfg->params[PROP_ID_LOCK] & 1) << + TEGRA_MUX_LOCK_SHIFT; + } + if (cfg->params[PROP_ID_IORESET] != -1) { + reg &= ~(1 << TEGRA_MUX_IORESET_SHIFT); + reg |= (cfg->params[PROP_ID_IORESET] & 1) << + TEGRA_MUX_IORESET_SHIFT; + } + if (cfg->params[PROP_ID_RCV_SEL] != -1) { + reg &= ~(1 << TEGRA_MUX_RCV_SEL_SHIFT); + reg |= (cfg->params[PROP_ID_RCV_SEL] & 1) << + TEGRA_MUX_RCV_SEL_SHIFT; + } + bus_write_4(sc->mux_mem_res, mux->reg, reg); + return (0); +} + +static int +pinmux_config_grp(struct pinmux_softc *sc, char *grp_name, + const struct tegra_grp *grp, struct pincfg *cfg) +{ + uint32_t reg; + + reg = bus_read_4(sc->pad_mem_res, grp->reg); + + if (cfg->params[PROP_ID_HIGH_SPEED_MODE] != -1) { + reg &= ~(1 << TEGRA_GRP_HSM_SHIFT); + reg |= (cfg->params[PROP_ID_HIGH_SPEED_MODE] & 1) << + TEGRA_GRP_HSM_SHIFT; + } + if (cfg->params[PROP_ID_SCHMITT] != -1) { + reg &= ~(1 << TEGRA_GRP_SCHMT_SHIFT); + reg |= (cfg->params[PROP_ID_SCHMITT] & 1) << + TEGRA_GRP_SCHMT_SHIFT; + } + if (cfg->params[PROP_ID_DRIVE_TYPE] != -1) { + reg &= ~(TEGRA_GRP_DRV_TYPE_MASK << TEGRA_GRP_DRV_TYPE_SHIFT); + reg |= (cfg->params[PROP_ID_DRIVE_TYPE] & + TEGRA_GRP_DRV_TYPE_MASK) << TEGRA_GRP_DRV_TYPE_SHIFT; + } + if (cfg->params[PROP_ID_SLEW_RATE_RISING] != -1) { + reg &= ~(TEGRA_GRP_DRV_DRVDN_SLWR_MASK << + TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT); + reg |= (cfg->params[PROP_ID_SLEW_RATE_RISING] & + TEGRA_GRP_DRV_DRVDN_SLWR_MASK) << + TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT; + } + if (cfg->params[PROP_ID_SLEW_RATE_FALLING] != -1) { + reg &= ~(TEGRA_GRP_DRV_DRVUP_SLWF_MASK << + TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT); + reg |= (cfg->params[PROP_ID_SLEW_RATE_FALLING] & + TEGRA_GRP_DRV_DRVUP_SLWF_MASK) << + TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT; + } + if ((cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] != -1) && + (grp->drvdn_mask != -1)) { + reg &= ~(grp->drvdn_shift << grp->drvdn_mask); + reg |= (cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] & + grp->drvdn_mask) << grp->drvdn_shift; + } + if ((cfg->params[PROP_ID_DRIVE_UP_STRENGTH] != -1) && + (grp->drvup_mask != -1)) { + reg &= ~(grp->drvup_shift << grp->drvup_mask); + reg |= (cfg->params[PROP_ID_DRIVE_UP_STRENGTH] & + grp->drvup_mask) << grp->drvup_shift; + } + bus_write_4(sc->pad_mem_res, grp->reg, reg); + return (0); +} + +static int +pinmux_config_node(struct pinmux_softc *sc, char *pin_name, struct pincfg *cfg) +{ + const struct tegra_mux *mux; + const struct tegra_grp *grp; + uint32_t reg; + int rv; + + /* Handle MIPI special case first */ + if (strcmp(pin_name, "dsi_b") == 0) { + if (cfg->function == NULL) { + /* nothing to set */ + return (0); + } + reg = bus_read_4(sc->mipi_mem_res, 0); /* register 0x820 */ + if (strcmp(cfg->function, "csi") == 0) + reg &= ~(1 << 1); + else if (strcmp(cfg->function, "dsi_b") == 0) + reg |= (1 << 1); + bus_write_4(sc->mipi_mem_res, 0, reg); /* register 0x820 */ + } + + /* Handle pin muxes */ + mux = pinmux_search_mux(pin_name); + if (mux != NULL) { + if (mux->gpio_num != -1) { + /* XXXX TODO: Reserve gpio here */ + } + rv = pinmux_config_mux(sc, pin_name, mux, cfg); + return (rv); + } + + /* Handle pin groups */ + grp = pinmux_search_grp(pin_name); + if (grp != NULL) { + rv = pinmux_config_grp(sc, pin_name, grp, cfg); + return (rv); + } + + device_printf(sc->dev, "Unknown pin: %s\n", pin_name); + return (ENXIO); +} + +static int +pinmux_read_node(struct pinmux_softc *sc, phandle_t node, struct pincfg *cfg, + char **pins, int *lpins) +{ + int rv, i; + + *lpins = OF_getprop_alloc(node, "nvidia,pins", 1, (void **)pins); + if (*lpins <= 0) + return (ENOENT); + + /* Read function (mux) settings. */ + rv = OF_getprop_alloc(node, "nvidia,function", 1, + (void **)&cfg->function); + if (rv <= 0) + cfg->function = NULL; + + /* Read numeric properties. */ + for (i = 0; i < PROP_ID_MAX_ID; i++) { + rv = OF_getencprop(node, prop_names[i].name, &cfg->params[i], + sizeof(cfg->params[i])); + if (rv <= 0) + cfg->params[i] = -1; + } + return (0); +} + +static int +pinmux_process_node(struct pinmux_softc *sc, phandle_t node) +{ + struct pincfg cfg; + char *pins, *pname; + int i, len, lpins, rv; + + rv = pinmux_read_node(sc, node, &cfg, &pins, &lpins); + if (rv != 0) + return (rv); + + len = 0; + pname = pins; + do { + i = strlen(pname) + 1; + rv = pinmux_config_node(sc, pname, &cfg); + if (rv != 0) + device_printf(sc->dev, + "Cannot configure pin: %s: %d\n", pname, rv); + + len += i; + pname += i; + } while (len < lpins); + + if (pins != NULL) + free(pins, M_OFWPROP); + if (cfg.function != NULL) + free(cfg.function, M_OFWPROP); + return (rv); +} + +static int pinmux_configure(device_t dev, phandle_t cfgxref) +{ + struct pinmux_softc *sc; + phandle_t node, cfgnode; + int rv; + + sc = device_get_softc(dev); + cfgnode = OF_node_from_xref(cfgxref); + + + for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + rv = pinmux_process_node(sc, node); + } + return (0); +} + +static int +pinmux_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, "Tegra pin configuration"); + return (BUS_PROBE_DEFAULT); +} + +static int +pinmux_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static int +pinmux_attach(device_t dev) +{ + struct pinmux_softc * sc; + int rid; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->pad_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->pad_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + rid = 1; + sc->mux_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mux_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + rid = 2; + sc->mipi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mipi_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + /* Register as a pinctrl device and process default configuration */ + fdt_pinctrl_register(dev, NULL); + fdt_pinctrl_configure_by_name(dev, "boot"); + + return (0); +} + + +static device_method_t tegra_pinmux_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pinmux_probe), + DEVMETHOD(device_attach, pinmux_attach), + DEVMETHOD(device_detach, pinmux_detach), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure,pinmux_configure), + + DEVMETHOD_END +}; + +static driver_t tegra_pinmux_driver = { + "tegra_pinmux", + tegra_pinmux_methods, + sizeof(struct pinmux_softc), +}; + +static devclass_t tegra_pinmux_devclass; + +EARLY_DRIVER_MODULE(tegra_pinmux, simplebus, tegra_pinmux_driver, + tegra_pinmux_devclass, 0, 0, 71); diff --git a/sys/arm/nvidia/tegra_pmc.h b/sys/arm/nvidia/tegra_pmc.h new file mode 100644 index 0000000..933c408 --- /dev/null +++ b/sys/arm/nvidia/tegra_pmc.h @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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 _TEGRA_PMC_H_ +#define _TEGRA_PMC_H_ + +enum tegra_suspend_mode { + TEGRA_SUSPEND_NONE = 0, + TEGRA_SUSPEND_LP2, /* CPU voltage off */ + TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */ + TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */ +}; + +/* PARTIDs for powergate */ +enum tegra_powergate_id { + TEGRA_POWERGATE_CRAIL = 0, + TEGRA_POWERGATE_TD = 1, + TEGRA_POWERGATE_VE = 2, + TEGRA_POWERGATE_PCX = 3, + TEGRA_POWERGATE_VDE = 4, + TEGRA_POWERGATE_L2C = 5, + TEGRA_POWERGATE_MPE = 6, + TEGRA_POWERGATE_HEG = 7, + TEGRA_POWERGATE_SAX = 8, + TEGRA_POWERGATE_CE1 = 9, + TEGRA_POWERGATE_CE2 = 10, + TEGRA_POWERGATE_CE3 = 11, + TEGRA_POWERGATE_CELP = 12, + /* */ + TEGRA_POWERGATE_CE0 = 14, + TEGRA_POWERGATE_C0NC = 15, + TEGRA_POWERGATE_C1NC = 16, + TEGRA_POWERGATE_SOR = 17, + TEGRA_POWERGATE_DIS = 18, + TEGRA_POWERGATE_DISB = 19, + TEGRA_POWERGATE_XUSBA = 20, + TEGRA_POWERGATE_XUSBB = 21, + TEGRA_POWERGATE_XUSBC = 22, + TEGRA_POWERGATE_VIC = 23, + TEGRA_POWERGATE_IRAM = 24, + /* */ + TEGRA_POWERGATE_3D = 32 + +}; + +/* PARTIDs for power rails */ +enum tegra_powerrail_id { + TEGRA_IO_RAIL_CSIA = 0, + TEGRA_IO_RAIL_CSIB = 1, + TEGRA_IO_RAIL_DSI = 2, + TEGRA_IO_RAIL_MIPI_BIAS = 3, + TEGRA_IO_RAIL_PEX_BIAS = 4, + TEGRA_IO_RAIL_PEX_CLK1 = 5, + TEGRA_IO_RAIL_PEX_CLK2 = 6, + TEGRA_IO_RAIL_USB0 = 9, + TEGRA_IO_RAIL_USB1 = 10, + TEGRA_IO_RAIL_USB2 = 11, + TEGRA_IO_RAIL_USB_BIAS = 12, + TEGRA_IO_RAIL_NAND = 13, + TEGRA_IO_RAIL_UART = 14, + TEGRA_IO_RAIL_BB = 15, + TEGRA_IO_RAIL_AUDIO = 17, + TEGRA_IO_RAIL_HSIC = 19, + TEGRA_IO_RAIL_COMP = 22, + TEGRA_IO_RAIL_HDMI = 28, + TEGRA_IO_RAIL_PEX_CNTRL = 32, + TEGRA_IO_RAIL_SDMMC1 = 33, + TEGRA_IO_RAIL_SDMMC3 = 34, + TEGRA_IO_RAIL_SDMMC4 = 35, + TEGRA_IO_RAIL_CAM = 36, + TEGRA_IO_RAIL_RES = 37, + TEGRA_IO_RAIL_HV = 38, + TEGRA_IO_RAIL_DSIB = 39, + TEGRA_IO_RAIL_DSIC = 40, + TEGRA_IO_RAIL_DSID = 41, + TEGRA_IO_RAIL_CSIE = 44, + TEGRA_IO_RAIL_LVDS = 57, + TEGRA_IO_RAIL_SYS_DDC = 58, +}; + +int tegra_powergate_is_powered(enum tegra_powergate_id id); +int tegra_powergate_power_on(enum tegra_powergate_id id); +int tegra_powergate_power_off(enum tegra_powergate_id id); +int tegra_powergate_remove_clamping(enum tegra_powergate_id id); +int tegra_powergate_sequence_power_up(enum tegra_powergate_id id, + clk_t clk, hwreset_t rst); +int tegra_io_rail_power_on(int tegra_powerrail_id); +int tegra_io_rail_power_off(int tegra_powerrail_id); + +#endif /*_TEGRA_PMC_H_*/ \ No newline at end of file diff --git a/sys/arm/nvidia/tegra_rtc.c b/sys/arm/nvidia/tegra_rtc.c new file mode 100644 index 0000000..c15ded3 --- /dev/null +++ b/sys/arm/nvidia/tegra_rtc.c @@ -0,0 +1,303 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * RTC driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "clock_if.h" + +#define RTC_CONTROL 0x00 +#define RTC_BUSY 0x04 +#define RTC_BUSY_STATUS (1 << 0) +#define RTC_SECONDS 0x08 +#define RTC_SHADOW_SECONDS 0x0c +#define RTC_MILLI_SECONDS 0x10 +#define RTC_SECONDS_ALARM0 0x14 +#define RTC_SECONDS_ALARM1 0x18 +#define RTC_MILLI_SECONDS_ALARM 0x1c +#define RTC_SECONDS_COUNTDOWN_ALARM 0x20 +#define RTC_MILLI_SECONDS_COUNTDOW_ALARM 0x24 +#define RTC_INTR_MASK 0x28 +#define RTC_INTR_MSEC_CDN_ALARM (1 << 4) +#define RTC_INTR_SEC_CDN_ALARM (1 << 3) +#define RTC_INTR_MSEC_ALARM (1 << 2) +#define RTC_INTR_SEC_ALARM1 (1 << 1) +#define RTC_INTR_SEC_ALARM0 (1 << 0) + +#define RTC_INTR_STATUS 0x2c +#define RTC_INTR_SOURCE 0x30 +#define RTC_INTR_SET 0x34 +#define RTC_CORRECTION_FACTOR 0x38 + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +#define LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define SLEEP(_sc, timeout) \ + mtx_sleep(sc, &sc->mtx, 0, "rtcwait", timeout); +#define LOCK_INIT(_sc) \ + mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_rtc", MTX_DEF) +#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) +#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) +#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-rtc", 1}, + {NULL, 0} +}; + +struct tegra_rtc_softc { + device_t dev; + struct mtx mtx; + + struct resource *mem_res; + struct resource *irq_res; + void *irq_h; + + clk_t clk; + uint32_t core_freq; +}; + +static void +tegra_rtc_wait(struct tegra_rtc_softc *sc) +{ + int timeout; + + for (timeout = 500; timeout >0; timeout--) { + if ((RD4(sc, RTC_BUSY) & RTC_BUSY_STATUS) == 0) + break; + DELAY(1); + } + if (timeout <= 0) + device_printf(sc->dev, "Device busy timeouted\n"); + +} + +/* + * Get the time of day clock and return it in ts. + * Return 0 on success, an error number otherwise. + */ +static int +tegra_rtc_gettime(device_t dev, struct timespec *ts) +{ + struct tegra_rtc_softc *sc; + struct timeval tv; + uint32_t msec, sec; + + sc = device_get_softc(dev); + + LOCK(sc); + msec = RD4(sc, RTC_MILLI_SECONDS); + sec = RD4(sc, RTC_SHADOW_SECONDS); + UNLOCK(sc); + tv.tv_sec = sec; + tv.tv_usec = msec * 1000; + TIMEVAL_TO_TIMESPEC(&tv, ts); + return (0); +} + + +static int +tegra_rtc_settime(device_t dev, struct timespec *ts) +{ + struct tegra_rtc_softc *sc; + struct timeval tv; + + sc = device_get_softc(dev); + + LOCK(sc); + TIMESPEC_TO_TIMEVAL(&tv, ts); + tegra_rtc_wait(sc); + WR4(sc, RTC_SECONDS, tv.tv_sec); + UNLOCK(sc); + + return (0); +} + + +static void +tegra_rtc_intr(void *arg) +{ + struct tegra_rtc_softc *sc; + uint32_t status; + + sc = (struct tegra_rtc_softc *)arg; + LOCK(sc); + status = RD4(sc, RTC_INTR_STATUS); + WR4(sc, RTC_INTR_STATUS, status); + UNLOCK(sc); +} + +static int +tegra_rtc_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 int +tegra_rtc_attach(device_t dev) +{ + int rv, rid; + struct tegra_rtc_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + LOCK_INIT(sc); + + /* Get the memory resource for the register mapping. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot map registers.\n"); + rv = ENXIO; + goto fail; + } + + /* Allocate our IRQ resource. */ + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate interrupt.\n"); + rv = ENXIO; + goto fail; + } + + /* OFW resources. */ + rv = clk_get_by_ofw_index(dev, 0, &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get i2c clock: %d\n", rv); + goto fail; + } + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock: %d\n", rv); + goto fail; + } + + /* Init hardware. */ + WR4(sc, RTC_SECONDS_ALARM0, 0); + WR4(sc, RTC_SECONDS_ALARM1, 0); + WR4(sc, RTC_INTR_STATUS, 0xFFFFFFFF); + WR4(sc, RTC_INTR_MASK, 0); + + /* Setup interrupt */ + rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, tegra_rtc_intr, sc, &sc->irq_h); + if (rv) { + device_printf(dev, "Cannot setup interrupt.\n"); + goto fail; + } + + /* + * Register as a time of day clock with 1-second resolution. + * + * XXXX Not yet, we don't have support for multiple RTCs + */ + /* clock_register(dev, 1000000); */ + + return (bus_generic_attach(dev)); + +fail: + if (sc->clk != NULL) + clk_release(sc->clk); + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + LOCK_DESTROY(sc); + + return (rv); +} + +static int +tegra_rtc_detach(device_t dev) +{ + struct tegra_rtc_softc *sc; + + sc = device_get_softc(dev); + if (sc->irq_h != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_h); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + LOCK_DESTROY(sc); + return (bus_generic_detach(dev)); +} + +static device_method_t tegra_rtc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_rtc_probe), + DEVMETHOD(device_attach, tegra_rtc_attach), + DEVMETHOD(device_detach, tegra_rtc_detach), + + /* clock interface */ + DEVMETHOD(clock_gettime, tegra_rtc_gettime), + DEVMETHOD(clock_settime, tegra_rtc_settime), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(tegra_rtc, tegra_rtc_driver, tegra_rtc_methods, + sizeof(struct tegra_rtc_softc)); +static devclass_t tegra_rtc_devclass; +DRIVER_MODULE(tegra_rtc, simplebus, tegra_rtc_driver, tegra_rtc_devclass, 0, 0); diff --git a/sys/arm/nvidia/tegra_sdhci.c b/sys/arm/nvidia/tegra_sdhci.c new file mode 100644 index 0000000..e2ec063 --- /dev/null +++ b/sys/arm/nvidia/tegra_sdhci.c @@ -0,0 +1,465 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * SDHCI driver glue for NVIDIA Tegra family + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdhci_if.h" + +/* Tegra SDHOST controller vendor register definitions */ +#define SDMMC_VENDOR_CLOCK_CNTRL 0x100 +#define VENDOR_CLOCK_CNTRL_CLK_SHIFT 8 +#define VENDOR_CLOCK_CNTRL_CLK_MASK 0xFF +#define SDMMC_VENDOR_SYS_SW_CNTRL 0x104 +#define SDMMC_VENDOR_CAP_OVERRIDES 0x10C +#define SDMMC_VENDOR_BOOT_CNTRL 0x110 +#define SDMMC_VENDOR_BOOT_ACK_TIMEOUT 0x114 +#define SDMMC_VENDOR_BOOT_DAT_TIMEOUT 0x118 +#define SDMMC_VENDOR_DEBOUNCE_COUNT 0x11C +#define SDMMC_VENDOR_MISC_CNTRL 0x120 +#define VENDOR_MISC_CTRL_ENABLE_SDR104 0x8 +#define VENDOR_MISC_CTRL_ENABLE_SDR50 0x10 +#define VENDOR_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 +#define VENDOR_MISC_CTRL_ENABLE_DDR50 0x200 +#define SDMMC_MAX_CURRENT_OVERRIDE 0x124 +#define SDMMC_MAX_CURRENT_OVERRIDE_HI 0x128 +#define SDMMC_VENDOR_CLK_GATE_HYSTERESIS_COUNT 0x1D0 +#define SDMMC_VENDOR_PHWRESET_VAL0 0x1D4 +#define SDMMC_VENDOR_PHWRESET_VAL1 0x1D8 +#define SDMMC_VENDOR_PHWRESET_VAL2 0x1DC +#define SDMMC_SDMEMCOMPPADCTRL_0 0x1E0 +#define SDMMC_AUTO_CAL_CONFIG 0x1E4 +#define SDMMC_AUTO_CAL_INTERVAL 0x1E8 +#define SDMMC_AUTO_CAL_STATUS 0x1EC +#define SDMMC_SDMMC_MCCIF_FIFOCTRL 0x1F4 +#define SDMMC_TIMEOUT_WCOAL_SDMMC 0x1F8 + +/* Compatible devices. */ +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-sdhci", 1}, + {NULL, 0}, +}; + +struct tegra_sdhci_softc { + device_t dev; + struct resource * mem_res; + struct resource * irq_res; + void * intr_cookie; + u_int quirks; /* Chip specific quirks */ + u_int caps; /* If we override SDHCI_CAPABILITIES */ + uint32_t max_clk; /* Max possible freq */ + clk_t clk; + hwreset_t reset; + gpio_pin_t gpio_cd; + gpio_pin_t gpio_wp; + gpio_pin_t gpio_power; + + int force_card_present; + struct sdhci_slot slot; + +}; + +static inline uint32_t +RD4(struct tegra_sdhci_softc *sc, bus_size_t off) +{ + + return (bus_read_4(sc->mem_res, off)); +} + +static uint8_t +tegra_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + return (bus_read_1(sc->mem_res, off)); +} + +static uint16_t +tegra_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + return (bus_read_2(sc->mem_res, off)); +} + +static uint32_t +tegra_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) +{ + struct tegra_sdhci_softc *sc; + uint32_t val32; + + sc = device_get_softc(dev); + val32 = bus_read_4(sc->mem_res, off); + /* Force the card-present state if necessary. */ + if (off == SDHCI_PRESENT_STATE && sc->force_card_present) + val32 |= SDHCI_CARD_PRESENT; + return (val32); +} + +static void +tegra_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint32_t *data, bus_size_t count) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_read_multi_4(sc->mem_res, off, data, count); +} + +static void +tegra_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint8_t val) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_write_1(sc->mem_res, off, val); +} + +static void +tegra_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint16_t val) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_write_2(sc->mem_res, off, val); +} + +static void +tegra_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint32_t val) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_write_4(sc->mem_res, off, val); +} + +static void +tegra_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint32_t *data, bus_size_t count) +{ + struct tegra_sdhci_softc *sc; + + sc = device_get_softc(dev); + bus_write_multi_4(sc->mem_res, off, data, count); +} + +static void +tegra_sdhci_intr(void *arg) +{ + struct tegra_sdhci_softc *sc = arg; + + sdhci_generic_intr(&sc->slot); + RD4(sc, SDHCI_INT_STATUS); +} + +static int +tegra_generic_get_ro(device_t brdev, device_t reqdev) +{ + + return (0); +} + +static int +tegra_sdhci_probe(device_t dev) +{ + struct tegra_sdhci_softc *sc; + phandle_t node; + pcell_t cid; + const struct ofw_compat_data *cd; + + sc = device_get_softc(dev); + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "nvidia,tegra124-sdhci")) { + device_set_desc(dev, "Tegra SDHCI controller"); + } else + return (ENXIO); + cd = ofw_bus_search_compatible(dev, compat_data); + if (cd->ocd_data == 0) + return (ENXIO); + + node = ofw_bus_get_node(dev); + + /* Allow dts to patch quirks, slots, and max-frequency. */ + if ((OF_getencprop(node, "quirks", &cid, sizeof(cid))) > 0) + sc->quirks = cid; + if ((OF_getencprop(node, "max-frequency", &cid, sizeof(cid))) > 0) + sc->max_clk = cid; + + return (BUS_PROBE_DEFAULT); +} + +static int +tegra_sdhci_attach(device_t dev) +{ + struct tegra_sdhci_softc *sc; + int rid, rv; + uint64_t freq; + phandle_t node, prop; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (!sc->mem_res) { + device_printf(dev, "cannot allocate memory window\n"); + rv = ENXIO; + goto fail; + } + + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (!sc->irq_res) { + device_printf(dev, "cannot allocate interrupt\n"); + rv = ENXIO; + goto fail; + } + + if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, tegra_sdhci_intr, sc, &sc->intr_cookie)) { + device_printf(dev, "cannot setup interrupt handler\n"); + rv = ENXIO; + goto fail; + } + + rv = hwreset_get_by_ofw_name(sc->dev, "sdhci", &sc->reset); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'sdhci' reset\n"); + goto fail; + } + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot unreset 'sdhci' reset\n"); + goto fail; + } + + gpio_pin_get_by_ofw_property(sc->dev, "cd-gpios", &sc->gpio_cd); + gpio_pin_get_by_ofw_property(sc->dev, "power-gpios", &sc->gpio_power); + gpio_pin_get_by_ofw_property(sc->dev, "wp-gpios", &sc->gpio_wp); + + rv = clk_get_by_ofw_index(dev, 0, &sc->clk); + if (rv != 0) { + + device_printf(dev, "Cannot get clock\n"); + goto fail; + } + + rv = clk_get_by_ofw_index(dev, 0, &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get clock\n"); + goto fail; + } + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable clock\n"); + goto fail; + } + rv = clk_set_freq(sc->clk, 48000000, CLK_SET_ROUND_DOWN); + if (rv != 0) { + device_printf(dev, "Cannot set clock\n"); + } + rv = clk_get_freq(sc->clk, &freq); + if (rv != 0) { + device_printf(dev, "Cannot get clock frequency\n"); + goto fail; + } + if (bootverbose) + device_printf(dev, " Base MMC clock: %lld\n", freq); + + /* Fill slot information. */ + sc->max_clk = (int)freq; + sc->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_MISSING_CAPS; + + /* Limit real slot capabilities. */ + sc->caps = RD4(sc, SDHCI_CAPABILITIES); + if (OF_getencprop(node, "bus-width", &prop, sizeof(prop)) > 0) { + sc->caps &= ~(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA); + switch (prop) { + case 8: + sc->caps |= MMC_CAP_8_BIT_DATA; + /* FALLTHROUGH */ + case 4: + sc->caps |= MMC_CAP_4_BIT_DATA; + break; + case 1: + break; + default: + device_printf(dev, "Bad bus-width value %u\n", prop); + break; + } + } + if (OF_hasprop(node, "non-removable")) + sc->force_card_present = 1; + /* + * Clear clock field, so SDHCI driver uses supplied frequency. + * in sc->slot.max_clk + */ + sc->caps &= ~SDHCI_CLOCK_V3_BASE_MASK; + + sc->slot.quirks = sc->quirks; + sc->slot.max_clk = sc->max_clk; + sc->slot.caps = sc->caps; + + rv = sdhci_init_slot(dev, &sc->slot, 0); + if (rv != 0) { + goto fail; + } + + bus_generic_probe(dev); + bus_generic_attach(dev); + + sdhci_start_slot(&sc->slot); + + return (0); + +fail: + if (sc->gpio_cd != NULL) + gpio_pin_release(sc->gpio_cd); + if (sc->gpio_wp != NULL) + gpio_pin_release(sc->gpio_wp); + if (sc->gpio_power != NULL) + gpio_pin_release(sc->gpio_power); + if (sc->clk != NULL) + clk_release(sc->clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->intr_cookie != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (rv); +} + +static int +tegra_sdhci_detach(device_t dev) +{ + struct tegra_sdhci_softc *sc = device_get_softc(dev); + struct sdhci_slot *slot = &sc->slot; + + bus_generic_detach(dev); + clk_release(sc->clk); + bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); + bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res), + sc->irq_res); + + sdhci_cleanup_slot(slot); + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem_res), + sc->mem_res); + return (0); +} + +static device_method_t tegra_sdhci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_sdhci_probe), + DEVMETHOD(device_attach, tegra_sdhci_attach), + DEVMETHOD(device_detach, tegra_sdhci_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), + DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), + + /* MMC bridge interface */ + DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), + DEVMETHOD(mmcbr_request, sdhci_generic_request), + DEVMETHOD(mmcbr_get_ro, tegra_generic_get_ro), + DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), + DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), + + /* SDHCI registers accessors */ + DEVMETHOD(sdhci_read_1, tegra_sdhci_read_1), + DEVMETHOD(sdhci_read_2, tegra_sdhci_read_2), + DEVMETHOD(sdhci_read_4, tegra_sdhci_read_4), + DEVMETHOD(sdhci_read_multi_4, tegra_sdhci_read_multi_4), + DEVMETHOD(sdhci_write_1, tegra_sdhci_write_1), + DEVMETHOD(sdhci_write_2, tegra_sdhci_write_2), + DEVMETHOD(sdhci_write_4, tegra_sdhci_write_4), + DEVMETHOD(sdhci_write_multi_4, tegra_sdhci_write_multi_4), + + { 0, 0 } +}; + +static devclass_t tegra_sdhci_devclass; + +static driver_t tegra_sdhci_driver = { + "sdhci_tegra", + tegra_sdhci_methods, + sizeof(struct tegra_sdhci_softc), +}; + +DRIVER_MODULE(sdhci_tegra, simplebus, tegra_sdhci_driver, tegra_sdhci_devclass, + 0, 0); +MODULE_DEPEND(sdhci_tegra, sdhci, 1, 1, 1); +DRIVER_MODULE(mmc, sdhci_tegra, mmc_driver, mmc_devclass, NULL, NULL); diff --git a/sys/arm/nvidia/tegra_soctherm.c b/sys/arm/nvidia/tegra_soctherm.c new file mode 100644 index 0000000..3102173 --- /dev/null +++ b/sys/arm/nvidia/tegra_soctherm.c @@ -0,0 +1,696 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + +/* + * Thermometer and thermal zones driver for Tegra SoCs. + * Calibration data and algo are taken from Linux, because this part of SoC + * is undocumented in TRM. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include "tegra_soctherm_if.h" + +/* Per sensors registers - base is 0x0c0*/ +#define TSENSOR_CONFIG0 0x000 +#define TSENSOR_CONFIG0_TALL(x) (((x) & 0xFFFFF) << 8) +#define TSENSOR_CONFIG0_STATUS_CLR (1 << 5) +#define TSENSOR_CONFIG0_TCALC_OVERFLOW (1 << 4) +#define TSENSOR_CONFIG0_OVERFLOW (1 << 3) +#define TSENSOR_CONFIG0_CPTR_OVERFLOW (1 << 2) +#define TSENSOR_CONFIG0_RO_SEL (1 << 1) +#define TSENSOR_CONFIG0_STOP (1 << 0) + +#define TSENSOR_CONFIG1 0x004 +#define TSENSOR_CONFIG1_TEMP_ENABLE (1U << 31) +#define TSENSOR_CONFIG1_TEN_COUNT(x) (((x) & 0x3F) << 24) +#define TSENSOR_CONFIG1_TIDDQ_EN(x) (((x) & 0x3F) << 15) +#define TSENSOR_CONFIG1_TSAMPLE(x) (((x) & 0x3FF) << 0) + +#define TSENSOR_CONFIG2 0x008 +#define TSENSOR_CONFIG2_THERMA(x) (((x) & 0xFFFF) << 16) +#define TSENSOR_CONFIG2_THERMB(x) (((x) & 0xFFFF) << 0) + +#define TSENSOR_STATUS0 0x00c +#define TSENSOR_STATUS0_CAPTURE_VALID (1U << 31) +#define TSENSOR_STATUS0_CAPTURE(x) (((x) >> 0) & 0xffff) + +#define TSENSOR_STATUS1 0x010 +#define TSENSOR_STATUS1_TEMP_VALID (1U << 31) +#define TSENSOR_STATUS1_TEMP(x) (((x) >> 0) & 0xffff) + +#define TSENSOR_STATUS2 0x014 +#define TSENSOR_STATUS2_TEMP_MAX(x) (((x) >> 16) & 0xffff) +#define TSENSOR_STATUS2_TEMP_MIN(x) (((x) >> 0) & 0xffff) + +/* Global registers */ +#define TSENSOR_PDIV 0x1c0 +#define TSENSOR_PDIV_T124 0x8888 +#define TSENSOR_HOTSPOT_OFF 0x1c4 +#define TSENSOR_HOTSPOT_OFF_T124 0x00060600 +#define TSENSOR_TEMP1 0x1c8 +#define TSENSOR_TEMP2 0x1cc + +/* Readbacks */ +#define READBACK_VALUE_MASK 0xff00 +#define READBACK_VALUE_SHIFT 8 +#define READBACK_ADD_HALF (1 << 7) +#define READBACK_NEGATE (1 << 0) + + +/* Fuses */ +#define FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT 0 +#define FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS 13 +#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13 +#define FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS 13 + +#define FUSE_TSENSOR8_CALIB 0x180 +#define FUSE_TSENSOR8_CALIB_CP_TS_BASE(x) (((x) >> 0) & 0x3ff) +#define FUSE_TSENSOR8_CALIB_FT_TS_BASE(x) (((x) >> 10) & 0x7ff) + +#define FUSE_SPARE_REALIGNMENT_REG 0x1fc +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT 0 +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS 6 +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21 +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_BITS 5 +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP(x) (((x) >> 0) & 0x3f) +#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT(x) (((x) >> 21) & 0x1f) + + +#define NOMINAL_CALIB_FT_T124 105 +#define NOMINAL_CALIB_CP_T124 25 + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + +static struct sysctl_ctx_list soctherm_sysctl_ctx; + +struct soctherm_shared_cal { + uint32_t base_cp; + uint32_t base_ft; + int32_t actual_temp_cp; + int32_t actual_temp_ft; +}; +struct tsensor_cfg { + uint32_t tall; + uint32_t tsample; + uint32_t tiddq_en; + uint32_t ten_count; + uint32_t pdiv; + uint32_t tsample_ate; + uint32_t pdiv_ate; +}; + +struct tsensor { + char *name; + int id; + struct tsensor_cfg *cfg; + bus_addr_t sensor_base; + bus_addr_t calib_fuse; + int fuse_corr_alpha; + int fuse_corr_beta; + + int16_t therm_a; + int16_t therm_b; +}; + +struct soctherm_softc { + device_t dev; + struct resource *mem_res; + struct resource *irq_res; + void *irq_ih; + + clk_t tsensor_clk; + clk_t soctherm_clk; + hwreset_t reset; + + int ntsensors; + struct tsensor *tsensors; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-soctherm", 1}, + {NULL, 0}, +}; + +static struct tsensor_cfg t124_tsensor_config = { + .tall = 16300, + .tsample = 120, + .tiddq_en = 1, + .ten_count = 1, + .pdiv = 8, + .tsample_ate = 480, + .pdiv_ate = 8 +}; + + +static struct tsensor t124_tsensors[] = { + { + .name = "cpu0", + .id = TEGRA124_SOCTHERM_SENSOR_CPU, + .cfg = &t124_tsensor_config, + .sensor_base = 0x0c0, + .calib_fuse = 0x098, + .fuse_corr_alpha = 1135400, + .fuse_corr_beta = -6266900, + }, + { + .name = "cpu1", + .id = -1, + .cfg = &t124_tsensor_config, + .sensor_base = 0x0e0, + .calib_fuse = 0x084, + .fuse_corr_alpha = 1122220, + .fuse_corr_beta = -5700700, + }, + { + .name = "cpu2", + .id = -1, + .cfg = &t124_tsensor_config, + .sensor_base = 0x100, + .calib_fuse = 0x088, + .fuse_corr_alpha = 1127000, + .fuse_corr_beta = -6768200, + }, + { + .name = "cpu3", + .id = -1, + .cfg = &t124_tsensor_config, + .sensor_base = 0x120, + .calib_fuse = 0x12c, + .fuse_corr_alpha = 1110900, + .fuse_corr_beta = -6232000, + }, + { + .name = "mem0", + .id = TEGRA124_SOCTHERM_SENSOR_MEM, + .cfg = &t124_tsensor_config, + .sensor_base = 0x140, + .calib_fuse = 0x158, + .fuse_corr_alpha = 1122300, + .fuse_corr_beta = -5936400, + }, + { + .name = "mem1", + .id = -1, + .cfg = &t124_tsensor_config, + .sensor_base = 0x160, + .calib_fuse = 0x15c, + .fuse_corr_alpha = 1145700, + .fuse_corr_beta = -7124600, + }, + { + .name = "gpu", + .id = TEGRA124_SOCTHERM_SENSOR_GPU, + .cfg = &t124_tsensor_config, + .sensor_base = 0x180, + .calib_fuse = 0x154, + .fuse_corr_alpha = 1120100, + .fuse_corr_beta = -6000500, + }, + { + .name = "pllX", + .id = TEGRA124_SOCTHERM_SENSOR_PLLX, + .cfg = &t124_tsensor_config, + .sensor_base = 0x1a0, + .calib_fuse = 0x160, + .fuse_corr_alpha = 1106500, + .fuse_corr_beta = -6729300, + }, +}; + +/* Extract signed integer bitfield from register */ +static int +extract_signed(uint32_t reg, int shift, int bits) +{ + int32_t val; + uint32_t mask; + + mask = (1 << bits) - 1; + val = ((reg >> shift) & mask) << (32 - bits); + val >>= 32 - bits; + return ((int32_t)val); +} + +static inline int64_t div64_s64_precise(int64_t a, int64_t b) +{ + int64_t r, al; + + al = a << 16; + r = (al * 2 + 1) / (2 * b); + return r >> 16; +} + +static void +get_shared_cal(struct soctherm_softc *sc, struct soctherm_shared_cal *cal) +{ + uint32_t val; + int calib_cp, calib_ft; + + val = tegra_fuse_read_4(FUSE_TSENSOR8_CALIB); + cal->base_cp = FUSE_TSENSOR8_CALIB_CP_TS_BASE(val); + cal->base_ft = FUSE_TSENSOR8_CALIB_FT_TS_BASE(val); + + val = tegra_fuse_read_4(FUSE_SPARE_REALIGNMENT_REG); + calib_ft = extract_signed(val, + FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT, + FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_BITS); + calib_cp = extract_signed(val, + FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT, + FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS); + + cal->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + calib_cp; + cal->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + calib_ft; +#ifdef DEBUG + printf("%s: base_cp: %u, base_ft: %d," + " actual_temp_cp: %d, actual_temp_ft: %d\n", + __func__, cal->base_cp, cal->base_ft, + cal->actual_temp_cp, cal->actual_temp_ft); +#endif +} + + +static void +tsensor_calibration(struct tsensor *sensor, struct soctherm_shared_cal *shared) +{ + uint32_t val; + int mult, div, calib_cp, calib_ft; + int actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp; + int temp_a, temp_b; + int64_t tmp; + + val = tegra_fuse_read_4(sensor->calib_fuse); + calib_cp = extract_signed(val, + FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT, + FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS); + actual_tsensor_cp = shared->base_cp * 64 + calib_cp; + + calib_ft = extract_signed(val, + FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT, + FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS); + actual_tsensor_ft = shared->base_ft * 32 + calib_ft; + + delta_sens = actual_tsensor_ft - actual_tsensor_cp; + delta_temp = shared->actual_temp_ft - shared->actual_temp_cp; + mult = sensor->cfg->pdiv * sensor->cfg->tsample_ate; + div = sensor->cfg->tsample * sensor->cfg->pdiv_ate; + + + temp_a = div64_s64_precise((int64_t) delta_temp * (1LL << 13) * mult, + (int64_t) delta_sens * div); + + tmp = (int64_t)actual_tsensor_ft * shared->actual_temp_cp - + (int64_t)actual_tsensor_cp * shared->actual_temp_ft; + temp_b = div64_s64_precise(tmp, (int64_t)delta_sens); + + temp_a = div64_s64_precise((int64_t)temp_a * sensor->fuse_corr_alpha, + 1000000); + temp_b = div64_s64_precise((int64_t)temp_b * sensor->fuse_corr_alpha + + sensor->fuse_corr_beta, 1000000); + sensor->therm_a = (int16_t)temp_a; + sensor->therm_b = (int16_t)temp_b; +#ifdef DEBUG + printf("%s: sensor %s fuse: 0x%08X (0x%04X, 0x%04X)" + " calib_cp: %d(0x%04X), calib_ft: %d(0x%04X)\n", + __func__, sensor->name, val, val & 0x1FFF, (val >> 13) & 0x1FFF, + calib_cp, calib_cp, calib_ft, calib_ft); + printf("therma: 0x%04X(%d), thermb: 0x%04X(%d)\n", + (uint16_t)sensor->therm_a, temp_a, + (uint16_t)sensor->therm_b, sensor->therm_b); +#endif +} + +static void +soctherm_init_tsensor(struct soctherm_softc *sc, struct tsensor *sensor, + struct soctherm_shared_cal *shared_cal) +{ + uint32_t val; + + tsensor_calibration(sensor, shared_cal); + + val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0); + val |= TSENSOR_CONFIG0_STOP; + val |= TSENSOR_CONFIG0_STATUS_CLR; + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val); + + val = TSENSOR_CONFIG0_TALL(sensor->cfg->tall); + val |= TSENSOR_CONFIG0_STOP; + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val); + + val = TSENSOR_CONFIG1_TSAMPLE(sensor->cfg->tsample - 1); + val |= TSENSOR_CONFIG1_TIDDQ_EN(sensor->cfg->tiddq_en); + val |= TSENSOR_CONFIG1_TEN_COUNT(sensor->cfg->ten_count); + val |= TSENSOR_CONFIG1_TEMP_ENABLE; + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG1, val); + + val = TSENSOR_CONFIG2_THERMA((uint16_t)sensor->therm_a) | + TSENSOR_CONFIG2_THERMB((uint16_t)sensor->therm_b); + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG2, val); + + val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0); + val &= ~TSENSOR_CONFIG0_STOP; + WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val); +#ifdef DEBUG + printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X," + " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name, + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0), + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1), + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS0), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS1), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS2) + ); +#endif +} + +static int +soctherm_convert_raw(uint32_t val) +{ + int32_t t; + + t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000; + if (val & READBACK_ADD_HALF) + t += 500; + if (val & READBACK_NEGATE) + t *= -1; + + return t; +} + +static int +soctherm_read_temp(struct soctherm_softc *sc, struct tsensor *sensor, int *temp) +{ + int timeout; + uint32_t val; + + + /* wait for valid sample */ + for (timeout = 1000; timeout > 0; timeout--) { + val = RD4(sc, sensor->sensor_base + TSENSOR_STATUS1); + if ((val & TSENSOR_STATUS1_TEMP_VALID) != 0) + break; + DELAY(100); + } + if (timeout <= 0) + device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name); + *temp = soctherm_convert_raw(val); +#ifdef DEBUG + printf("%s: Raw: 0x%08X, temp: %d\n", __func__, val, *temp); + printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X," + " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name, + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0), + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1), + RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS0), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS1), + RD4(sc, sensor->sensor_base + TSENSOR_STATUS2) + ); +#endif + return 0; +} + +static int +soctherm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val) +{ + struct soctherm_softc *sc; + int i; + + sc = device_get_softc(dev); + /* The direct sensor map starts at 0x100 */ + if (id >= 0x100) { + id -= 0x100; + if (id >= sc->ntsensors) + return (ERANGE); + return(soctherm_read_temp(sc, sc->tsensors + id, val)); + } + /* Linux (DT) compatible thermal zones */ + for (i = 0; i < sc->ntsensors; i++) { + if (sc->tsensors->id == id) + return(soctherm_read_temp(sc, sc->tsensors + id, val)); + } + return (ERANGE); +} + +static int +soctherm_sysctl_temperature(SYSCTL_HANDLER_ARGS) +{ + struct soctherm_softc *sc; + int val; + int rv; + int id; + + /* Write request */ + if (req->newptr != NULL) + return (EINVAL); + + sc = arg1; + id = arg2; + + if (id >= sc->ntsensors) + return (ERANGE); + rv = soctherm_read_temp(sc, sc->tsensors + id, &val); + if (rv != 0) + return (rv); + + val = val / 100; + val += 2731; + rv = sysctl_handle_int(oidp, &val, 0, req); + return (rv); +} + +static int +soctherm_init_sysctl(struct soctherm_softc *sc) +{ + int i; + struct sysctl_oid *oid, *tmp; + + sysctl_ctx_init(&soctherm_sysctl_ctx); + /* create node for hw.temp */ + oid = SYSCTL_ADD_NODE(&soctherm_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature", + CTLFLAG_RD, NULL, ""); + if (oid == NULL) + return (ENXIO); + + /* Add sensors */ + for (i = sc->ntsensors - 1; i >= 0; i--) { + tmp = SYSCTL_ADD_PROC(&soctherm_sysctl_ctx, + SYSCTL_CHILDREN(oid), OID_AUTO, sc->tsensors[i].name, + CTLTYPE_INT | CTLFLAG_RD, sc, i, + soctherm_sysctl_temperature, "IK", "SoC Temperature"); + if (tmp == NULL) + return (ENXIO); + } + + return (0); +} + +static int +soctherm_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); + + device_set_desc(dev, "Tegra temperature sensors"); + return (BUS_PROBE_DEFAULT); +} + +static int +soctherm_attach(device_t dev) +{ + struct soctherm_softc *sc; + phandle_t node; + int i, rid, rv; + struct soctherm_shared_cal shared_calib; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(sc->dev); + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + goto fail; + } + + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + goto fail; + } + +/* + if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, + soctherm_intr, NULL, sc, &sc->irq_ih))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + goto fail; + } +*/ + + /* OWF resources */ + rv = hwreset_get_by_ofw_name(dev, "soctherm", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get fuse reset\n"); + goto fail; + } + rv = clk_get_by_ofw_name(dev, "tsensor", &sc->tsensor_clk); + if (rv != 0) { + device_printf(dev, "Cannot get 'tsensor' clock: %d\n", rv); + goto fail; + } + rv = clk_get_by_ofw_name(dev, "soctherm", &sc->soctherm_clk); + if (rv != 0) { + device_printf(dev, "Cannot get 'soctherm' clock: %d\n", rv); + goto fail; + } + + rv = hwreset_assert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot assert reset\n"); + goto fail; + } + rv = clk_enable(sc->tsensor_clk); + if (rv != 0) { + device_printf(dev, "Cannot enable 'tsensor' clock: %d\n", rv); + goto fail; + } + rv = clk_enable(sc->soctherm_clk); + if (rv != 0) { + device_printf(dev, "Cannot enable 'soctherm' clock: %d\n", rv); + goto fail; + } + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot clear reset\n"); + goto fail; + } + + /* Tegra 124 */ + sc->tsensors = t124_tsensors; + sc->ntsensors = nitems(t124_tsensors); + get_shared_cal(sc, &shared_calib); + + WR4(sc, TSENSOR_PDIV, TSENSOR_PDIV_T124); + WR4(sc, TSENSOR_HOTSPOT_OFF, TSENSOR_HOTSPOT_OFF_T124); + + for (i = 0; i < sc->ntsensors; i++) + soctherm_init_tsensor(sc, sc->tsensors + i, &shared_calib); + + rv = soctherm_init_sysctl(sc); + if (rv != 0) { + device_printf(sc->dev, "Cannot initialize sysctls\n"); + goto fail; + } + + OF_device_register_xref(OF_xref_from_node(node), dev); + return (bus_generic_attach(dev)); + +fail: + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + sysctl_ctx_free(&soctherm_sysctl_ctx); + if (sc->tsensor_clk != NULL) + clk_release(sc->tsensor_clk); + if (sc->soctherm_clk != NULL) + clk_release(sc->soctherm_clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (ENXIO); +} + +static int +soctherm_detach(device_t dev) +{ + struct soctherm_softc *sc; + sc = device_get_softc(dev); + + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + sysctl_ctx_free(&soctherm_sysctl_ctx); + if (sc->tsensor_clk != NULL) + clk_release(sc->tsensor_clk); + if (sc->soctherm_clk != NULL) + clk_release(sc->soctherm_clk); + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (ENXIO); +} + +static device_method_t tegra_soctherm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, soctherm_probe), + DEVMETHOD(device_attach, soctherm_attach), + DEVMETHOD(device_detach, soctherm_detach), + + /* SOCTHERM interface */ + DEVMETHOD(tegra_soctherm_get_temperature, soctherm_get_temp), + + DEVMETHOD_END +}; + +static devclass_t tegra_soctherm_devclass; +DEFINE_CLASS_0(tegra_soctherm, tegra_soctherm_driver, tegra_soctherm_methods, + sizeof(struct soctherm_softc)); +EARLY_DRIVER_MODULE(tegra_soctherm, simplebus, tegra_soctherm_driver, +tegra_soctherm_devclass, 0, 0, 79); diff --git a/sys/arm/nvidia/tegra_soctherm_if.m b/sys/arm/nvidia/tegra_soctherm_if.m new file mode 100644 index 0000000..55ae044 --- /dev/null +++ b/sys/arm/nvidia/tegra_soctherm_if.m @@ -0,0 +1,42 @@ +#- +# Copyright (c) 2016 Michal Meloun +# 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 tegra_soctherm; + + +/** + * Read temperature + */ +METHOD int get_temperature{ + device_t dev; + device_t consumer; + uintptr_t id; + int *val; +}; diff --git a/sys/arm/nvidia/tegra_uart.c b/sys/arm/nvidia/tegra_uart.c new file mode 100644 index 0000000..88b750c --- /dev/null +++ b/sys/arm/nvidia/tegra_uart.c @@ -0,0 +1,252 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + + +/* + * UART driver for Tegra SoCs. + */ +#include "opt_platform.h" + +#include +#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 tegra_softc { + struct ns8250_softc ns8250_base; + clk_t clk; + hwreset_t reset; +}; + +/* + * UART class interface. + */ +static int +tegra_uart_attach(struct uart_softc *sc) +{ + int rv; + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + struct uart_bas *bas = &sc->sc_bas; + + rv = ns8250_bus_attach(sc); + if (rv != 0) + return (rv); + + ns8250->ier_rxbits = 0x1d; + ns8250->ier_mask = 0xc0; + 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 void +tegra_uart_grab(struct uart_softc *sc) +{ + struct uart_bas *bas = &sc->sc_bas; + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + u_char ier; + + /* + * turn off all interrupts to enter polling mode. Leave the + * saved mask alone. We'll restore whatever it was in ungrab. + * All pending interrupt signals are reset when IER is set to 0. + */ + uart_lock(sc->sc_hwmtx); + ier = uart_getreg(bas, REG_IER); + uart_setreg(bas, REG_IER, ier & ns8250->ier_mask); + uart_setreg(bas, REG_FCR, 0); + uart_barrier(bas); + uart_unlock(sc->sc_hwmtx); +} + +static void +tegra_uart_ungrab(struct uart_softc *sc) +{ + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + struct uart_bas *bas = &sc->sc_bas; + + /* + * Restore previous interrupt mask + */ + uart_lock(sc->sc_hwmtx); + uart_setreg(bas, REG_FCR, ns8250->fcr); + uart_setreg(bas, REG_IER, ns8250->ier); + uart_barrier(bas); + uart_unlock(sc->sc_hwmtx); +} + +static kobj_method_t tegra_methods[] = { + KOBJMETHOD(uart_probe, ns8250_bus_probe), + KOBJMETHOD(uart_attach, tegra_uart_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, tegra_uart_grab), + KOBJMETHOD(uart_ungrab, tegra_uart_ungrab), + KOBJMETHOD_END +}; + +static struct uart_class tegra_uart_class = { + "tegra class", + tegra_methods, + sizeof(struct tegra_softc), + .uc_ops = &uart_ns8250_ops, + .uc_range = 8, + .uc_rclk = 0, +}; + +/* Compatible devices. */ +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-uart", (uintptr_t)&tegra_uart_class}, + {NULL, (uintptr_t)NULL}, +}; + +UART_FDT_CLASS(compat_data); + +/* + * UART Driver interface. + */ +static int +uart_fdt_get_shift1(phandle_t node) +{ + pcell_t shift; + + if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) + shift = 2; + return ((int)shift); +} + +static int +tegra_uart_probe(device_t dev) +{ + struct tegra_softc *sc; + phandle_t node; + 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); + sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data; + + rv = hwreset_get_by_ofw_name(dev, "serial", &sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot get 'serial' reset\n"); + return (ENXIO); + } + rv = hwreset_deassert(sc->reset); + if (rv != 0) { + device_printf(dev, "Cannot unreset 'serial' reset\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + shift = uart_fdt_get_shift1(node); + rv = clk_get_by_ofw_index(dev, 0, &sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot get UART clock: %d\n", rv); + return (ENXIO); + } + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Cannot enable UART clock: %d\n", rv); + return (ENXIO); + } + rv = clk_get_freq(sc->clk, &freq); + if (rv != 0) { + device_printf(dev, "Cannot enable UART clock: %d\n", rv); + return (ENXIO); + } + device_printf(dev, "got UART clock: %lld\n", freq); + return (uart_bus_probe(dev, shift, (int)freq, 0, 0)); +} + +static int +tegra_uart_detach(device_t dev) +{ + struct tegra_softc *sc; + + sc = device_get_softc(dev); + if (sc->clk != NULL) { + clk_release(sc->clk); + } + + return (uart_bus_detach(dev)); +} + +static device_method_t tegra_uart_bus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tegra_uart_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, tegra_uart_detach), + { 0, 0 } +}; + +static driver_t tegra_uart_driver = { + uart_driver_name, + tegra_uart_bus_methods, + sizeof(struct tegra_softc), +}; + +DRIVER_MODULE(tegra_uart, simplebus, tegra_uart_driver, uart_devclass, + 0, 0); \ No newline at end of file diff --git a/sys/arm/nvidia/tegra_usbphy.c b/sys/arm/nvidia/tegra_usbphy.c new file mode 100644 index 0000000..40c0714 --- /dev/null +++ b/sys/arm/nvidia/tegra_usbphy.c @@ -0,0 +1,839 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * 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$"); + + +/* + * USB phy driver for Tegra SoCs. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy_if.h" + +#define CTRL_ICUSB_CTRL 0x15c +#define ICUSB_CTR_IC_ENB1 (1 << 3) + +#define CTRL_USB_USBMODE 0x1f8 +#define USB_USBMODE_MASK (3 << 0) +#define USB_USBMODE_HOST (3 << 0) +#define USB_USBMODE_DEVICE (2 << 0) + +#define CTRL_USB_HOSTPC1_DEVLC 0x1b4 +#define USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) +#define USB_HOSTPC1_DEVLC_STS (1 << 28) +#define USB_HOSTPC1_DEVLC_PHCD (1 << 22) + + +#define IF_USB_SUSP_CTRL 0x400 +#define FAST_WAKEUP_RESP (1 << 26) +#define UTMIP_SUSPL1_SET (1 << 25) +#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) +#define USB_SUSP_SET (1 << 14) +#define UTMIP_PHY_ENB (1 << 12) +#define UTMIP_RESET (1 << 11) +#define USB_SUSP_POL (1 << 10) +#define USB_PHY_CLK_VALID_INT_ENB (1 << 9) +#define USB_PHY_CLK_VALID_INT_STS (1 << 8) +#define USB_PHY_CLK_VALID (1 << 7) +#define USB_CLKEN (1 << 6) +#define USB_SUSP_CLR (1 << 5) +#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) +#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) +#define USB_WAKE_ON_RESUME_EN (1 << 2) +#define USB_WAKEUP_INT_ENB (1 << 1) +#define USB_WAKEUP_INT_STS (1 << 0) + +#define IF_USB_PHY_VBUS_SENSORS 0x404 +#define B_SESS_END_SW_VALUE (1 << 4) +#define B_SESS_END_SW_EN (1 << 3) + + +#define UTMIP_XCVR_CFG0 0x808 +#define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25) +#define UTMIP_XCVR_SETUP_MSB(x) ((((x) & 0x70) >> 4) << 22) +#define UTMIP_XCVR_LSBIAS_SEL (1 << 21) +#define UTMIP_XCVR_DISCON_METHOD (1 << 20) +#define UTMIP_FORCE_PDZI_POWERUP (1 << 19) +#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) +#define UTMIP_FORCE_PD2_POWERUP (1 << 17) +#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) +#define UTMIP_FORCE_PD_POWERUP (1 << 15) +#define UTMIP_FORCE_PD_POWERDOWN (1 << 14) +#define UTMIP_XCVR_TERMEN (1 << 13) +#define UTMIP_XCVR_HSLOOPBACK (1 << 12) +#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10) +#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8) +#define UTMIP_XCVR_FSSLEW(x) (((x) & 0x3) << 6) +#define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4) +#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0) + +#define UTMIP_BIAS_CFG0 0x80C +#define UTMIP_IDDIG_C_VAL (1 << 30) +#define UTMIP_IDDIG_C_SEL (1 << 29) +#define UTMIP_IDDIG_B_VAL (1 << 28) +#define UTMIP_IDDIG_B_SEL (1 << 27) +#define UTMIP_IDDIG_A_VAL (1 << 26) +#define UTMIP_IDDIG_A_SEL (1 << 25) +#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24) +#define UTMIP_IDPD_VAL (1 << 23) +#define UTMIP_IDPD_SEL (1 << 22) +#define UTMIP_IDDIG_VAL (1 << 21) +#define UTMIP_IDDIG_SEL (1 << 20) +#define UTMIP_GPI_VAL (1 << 19) +#define UTMIP_GPI_SEL (1 << 18) +#define UTMIP_ACTIVE_TERM_OFFSET(x) (((x) & 0x7) << 15) +#define UTMIP_ACTIVE_PULLUP_OFFSET(x) (((x) & 0x7) << 12) +#define UTMIP_OTGPD (1 << 11) +#define UTMIP_BIASPD (1 << 10) +#define UTMIP_VBUS_LEVEL_LEVEL(x) (((x) & 0x3) << 8) +#define UTMIP_SESS_LEVEL_LEVEL(x) (((x) & 0x3) << 6) +#define UTMIP_HSCHIRP_LEVEL(x) (((x) & 0x3) << 4) +#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2) +#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0) + + +#define UTMIP_HSRX_CFG0 0x810 +#define UTMIP_KEEP_PATT_ON_ACTIVE(x) (((x) & 0x3) << 30) +#define UTMIP_ALLOW_CONSEC_UPDN (1 << 29) +#define UTMIP_REALIGN_ON_NEW_PKT (1 << 28) +#define UTMIP_PCOUNT_UPDN_DIV(x) (((x) & 0xf) << 24) +#define UTMIP_SQUELCH_EOP_DLY(x) (((x) & 0x7) << 21) +#define UTMIP_NO_STRIPPING (1 << 20) +#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) +#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) +#define UTMIP_ELASTIC_OVERRUN_DISABLE (1 << 9) +#define UTMIP_ELASTIC_UNDERRUN_DISABLE (1 << 8) +#define UTMIP_PASS_CHIRP (1 << 7) +#define UTMIP_PASS_FEEDBACK (1 << 6) +#define UTMIP_PCOUNT_INERTIA(x) (((x) & 0x3) << 4) +#define UTMIP_PHASE_ADJUST(x) (((x) & 0x3) << 2) +#define UTMIP_THREE_SYNCBITS (1 << 1) +#define UTMIP_USE4SYNC_TRAN (1 << 0) + +#define UTMIP_HSRX_CFG1 0x814 +#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1F) << 1) +#define UTMIP_HS_ALLOW_KEEP_ALIVE (1 << 0) + +#define UTMIP_TX_CFG0 0x820 +#define UTMIP_FS_PREAMBLE_J (1 << 19) +#define UTMIP_FS_POSTAMBLE_OUTPUT_ENABLE (1 << 18) +#define UTMIP_FS_PREAMBLE_OUTPUT_ENABLE (1 << 17) +#define UTMIP_FSLS_ALLOW_SOP_TX_STUFF_ERR (1 << 16) +#define UTMIP_HS_READY_WAIT_FOR_VALID (1 << 15) +#define UTMIP_HS_TX_IPG_DLY(x) (((x) & 0x1f) << 10) +#define UTMIP_HS_DISCON_EOP_ONLY (1 << 9) +#define UTMIP_HS_DISCON_DISABLE (1 << 8) +#define UTMIP_HS_POSTAMBLE_OUTPUT_ENABLE (1 << 7) +#define UTMIP_HS_PREAMBLE_OUTPUT_ENABLE (1 << 6) +#define UTMIP_SIE_RESUME_ON_LINESTATE (1 << 5) +#define UTMIP_SOF_ON_NO_STUFF (1 << 4) +#define UTMIP_SOF_ON_NO_ENCODE (1 << 3) +#define UTMIP_NO_STUFFING (1 << 2) +#define UTMIP_NO_ENCODING (1 << 1) +#define UTMIP_NO_SYNC_NO_EOP (1 << 0) + +#define UTMIP_MISC_CFG0 0x824 +#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27) +#define UTMIP_DPDM_OBSERVE (1 << 26) +#define UTMIP_KEEP_XCVR_PD_ON_SOFT_DISCON (1 << 25) +#define UTMIP_ALLOW_LS_ON_SOFT_DISCON (1 << 24) +#define UTMIP_FORCE_FS_DISABLE_ON_DEV_CHIRP (1 << 23) +#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22) +#define UTMIP_LS_TO_FS_SKIP_4MS (1 << 21) +#define UTMIP_INJECT_ERROR_TYPE(x) (((x) & 0x3) << 19) +#define UTMIP_FORCE_HS_CLOCK_ON (1 << 18) +#define UTMIP_DISABLE_HS_TERM (1 << 17) +#define UTMIP_FORCE_HS_TERM (1 << 16) +#define UTMIP_DISABLE_PULLUP_DP (1 << 15) +#define UTMIP_DISABLE_PULLUP_DM (1 << 14) +#define UTMIP_DISABLE_PULLDN_DP (1 << 13) +#define UTMIP_DISABLE_PULLDN_DM (1 << 12) +#define UTMIP_FORCE_PULLUP_DP (1 << 11) +#define UTMIP_FORCE_PULLUP_DM (1 << 10) +#define UTMIP_FORCE_PULLDN_DP (1 << 9) +#define UTMIP_FORCE_PULLDN_DM (1 << 8) +#define UTMIP_STABLE_COUNT(x) (((x) & 0x7) << 5) +#define UTMIP_STABLE_ALL (1 << 4) +#define UTMIP_NO_FREE_ON_SUSPEND (1 << 3) +#define UTMIP_NEVER_FREE_RUNNING_TERMS (1 << 2) +#define UTMIP_ALWAYS_FREE_RUNNING_TERMS (1 << 1) +#define UTMIP_COMB_TERMS (1 << 0) + +#define UTMIP_MISC_CFG1 0x828 +#define UTMIP_PHY_XTAL_CLOCKEN (1 << 30) + +#define UTMIP_DEBOUNCE_CFG0 0x82C +#define UTMIP_BIAS_DEBOUNCE_B(x) (((x) & 0xffff) << 16) +#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0) + +#define UTMIP_BAT_CHRG_CFG0 0x830 +#define UTMIP_CHRG_DEBOUNCE_TIMESCALE(x) (((x) & 0x1f) << 8) +#define UTMIP_OP_I_SRC_ENG (1 << 5) +#define UTMIP_ON_SRC_ENG (1 << 4) +#define UTMIP_OP_SRC_ENG (1 << 3) +#define UTMIP_ON_SINK_ENG (1 << 2) +#define UTMIP_OP_SINK_ENG (1 << 1) +#define UTMIP_PD_CHRG (1 << 0) + +#define UTMIP_SPARE_CFG0 0x834 +#define FUSE_HS_IREF_CAP_CFG (1 << 7) +#define FUSE_HS_SQUELCH_LEVEL (1 << 6) +#define FUSE_SPARE (1 << 5) +#define FUSE_TERM_RANGE_ADJ_SEL (1 << 4) +#define FUSE_SETUP_SEL (1 << 3) +#define HS_RX_LATE_SQUELCH (1 << 2) +#define HS_RX_FLUSH_ALAP (1 << 1) +#define HS_RX_IPG_ERROR_ENABLE (1 << 0) + +#define UTMIP_XCVR_CFG1 0x838 +#define UTMIP_XCVR_RPU_RANGE_ADJ(x) (((x) & 0x3) << 26) +#define UTMIP_XCVR_HS_IREF_CAP(x) (((x) & 0x3) << 24) +#define UTMIP_XCVR_SPARE(x) (((x) & 0x3) << 22) +#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18) +#define UTMIP_RCTRL_SW_SET (1 << 17) +#define UTMIP_RCTRL_SW_VAL(x) (((x) & 0x1f) << 12) +#define UTMIP_TCTRL_SW_SET (1 << 11) +#define UTMIP_TCTRL_SW_VAL(x) (((x) & 0x1f) << 6) +#define UTMIP_FORCE_PDDR_POWERUP (1 << 5) +#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4) +#define UTMIP_FORCE_PDCHRP_POWERUP (1 << 3) +#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2) +#define UTMIP_FORCE_PDDISC_POWERUP (1 << 1) +#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0) + +#define UTMIP_BIAS_CFG1 0x83c +#define UTMIP_BIAS_DEBOUNCE_TIMESCALE(x) (((x) & 0x3f) << 8) +#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) +#define UTMIP_VBUS_WAKEUP_POWERDOWN (1 << 2) +#define UTMIP_FORCE_PDTRK_POWERUP (1 << 1) +#define UTMIP_FORCE_PDTRK_POWERDOWN (1 << 0) + +static int usbpby_enable_cnt; + +enum usb_ifc_type { + USB_IFC_TYPE_UNKNOWN = 0, + USB_IFC_TYPE_UTMI, + USB_IFC_TYPE_ULPI +}; + +enum usb_dr_mode { + USB_DR_MODE_UNKNOWN = 0, + USB_DR_MODE_DEVICE, + USB_DR_MODE_HOST, + USB_DR_MODE_OTG +}; + +struct usbphy_softc { + device_t dev; + struct resource *mem_res; + struct resource *pads_res; + clk_t clk_reg; + clk_t clk_pads; + clk_t clk_pllu; + regulator_t supply_vbus; + hwreset_t reset_usb; + hwreset_t reset_pads; + enum usb_ifc_type ifc_type; + enum usb_dr_mode dr_mode; + bool have_utmi_regs; + + /* UTMI params */ + int hssync_start_delay; + int elastic_limit; + int idle_wait_delay; + int term_range_adj; + int xcvr_lsfslew; + int xcvr_lsrslew; + int xcvr_hsslew; + int hssquelch_level; + int hsdiscon_level; + int xcvr_setup; + int xcvr_setup_use_fuses; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra30-usb-phy", 1}, + {NULL, 0}, +}; + +#define RD4(sc, offs) \ + bus_read_4(sc->mem_res, offs) + +#define WR4(sc, offs, val) \ + bus_write_4(sc->mem_res, offs, val) + +static int +reg_wait(struct usbphy_softc *sc, uint32_t reg, uint32_t mask, uint32_t val) +{ + int i; + + for (i = 0; i < 1000; i++) { + if ((RD4(sc, reg) & mask) == val) + return (0); + DELAY(10); + } + return (ETIMEDOUT); +} + +static int +usbphy_utmi_phy_clk(struct usbphy_softc *sc, bool enable) +{ + uint32_t val; + int rv; + + val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC); + if (enable) + val &= ~USB_HOSTPC1_DEVLC_PHCD; + else + val |= USB_HOSTPC1_DEVLC_PHCD; + WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val); + + rv = reg_wait(sc, IF_USB_SUSP_CTRL, USB_PHY_CLK_VALID, + enable ? USB_PHY_CLK_VALID: 0); + if (rv != 0) { + device_printf(sc->dev, "USB phy clock timeout.\n"); + return (ETIMEDOUT); + } + return (0); +} + +static int +usbphy_utmi_enable(struct usbphy_softc *sc) +{ + int rv; + uint32_t val; + + /* Reset phy */ + val = RD4(sc, IF_USB_SUSP_CTRL); + val |= UTMIP_RESET; + WR4(sc, IF_USB_SUSP_CTRL, val); + + + val = RD4(sc, UTMIP_TX_CFG0); + val |= UTMIP_FS_PREAMBLE_J; + WR4(sc, UTMIP_TX_CFG0, val); + + val = RD4(sc, UTMIP_HSRX_CFG0); + val &= ~UTMIP_IDLE_WAIT(~0); + val &= ~UTMIP_ELASTIC_LIMIT(~0); + val |= UTMIP_IDLE_WAIT(sc->idle_wait_delay); + val |= UTMIP_ELASTIC_LIMIT(sc->elastic_limit); + WR4(sc, UTMIP_HSRX_CFG0, val); + + val = RD4(sc, UTMIP_HSRX_CFG1); + val &= ~UTMIP_HS_SYNC_START_DLY(~0); + val |= UTMIP_HS_SYNC_START_DLY(sc->hssync_start_delay); + WR4(sc, UTMIP_HSRX_CFG1, val); + + val = RD4(sc, UTMIP_DEBOUNCE_CFG0); + val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); + val |= UTMIP_BIAS_DEBOUNCE_A(0x7530); /* For 12MHz */ + WR4(sc, UTMIP_DEBOUNCE_CFG0, val); + + val = RD4(sc, UTMIP_MISC_CFG0); + val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; + WR4(sc, UTMIP_MISC_CFG0, val); + + if (sc->dr_mode == USB_DR_MODE_DEVICE) { + val = RD4(sc,IF_USB_SUSP_CTRL); + val &= ~USB_WAKE_ON_CNNT_EN_DEV; + val &= ~USB_WAKE_ON_DISCON_EN_DEV; + WR4(sc, IF_USB_SUSP_CTRL, val); + + val = RD4(sc, UTMIP_BAT_CHRG_CFG0); + val &= ~UTMIP_PD_CHRG; + WR4(sc, UTMIP_BAT_CHRG_CFG0, val); + } else { + val = RD4(sc, UTMIP_BAT_CHRG_CFG0); + val |= UTMIP_PD_CHRG; + WR4(sc, UTMIP_BAT_CHRG_CFG0, val); + } + + usbpby_enable_cnt++; + if (usbpby_enable_cnt == 1) { + rv = hwreset_deassert(sc->reset_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot unreset 'utmi-pads' reset\n"); + return (rv); + } + rv = clk_enable(sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'utmi-pads' clock\n"); + return (rv); + } + + val = bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0); + val &= ~UTMIP_OTGPD; + val &= ~UTMIP_BIASPD; + val &= ~UTMIP_HSSQUELCH_LEVEL(~0); + val &= ~UTMIP_HSDISCON_LEVEL(~0); + val &= ~UTMIP_HSDISCON_LEVEL_MSB(~0); + val |= UTMIP_HSSQUELCH_LEVEL(sc->hssquelch_level); + val |= UTMIP_HSDISCON_LEVEL(sc->hsdiscon_level); + val |= UTMIP_HSDISCON_LEVEL_MSB(sc->hsdiscon_level); + bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val); + + rv = clk_disable(sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot disable 'utmi-pads' clock\n"); + return (rv); + } + } + + val = RD4(sc, UTMIP_XCVR_CFG0); + val &= ~UTMIP_FORCE_PD_POWERDOWN; + val &= ~UTMIP_FORCE_PD2_POWERDOWN ; + val &= ~UTMIP_FORCE_PDZI_POWERDOWN; + val &= ~UTMIP_XCVR_LSBIAS_SEL; + val &= ~UTMIP_XCVR_LSFSLEW(~0); + val &= ~UTMIP_XCVR_LSRSLEW(~0); + val &= ~UTMIP_XCVR_HSSLEW(~0); + val &= ~UTMIP_XCVR_HSSLEW_MSB(~0); + val |= UTMIP_XCVR_LSFSLEW(sc->xcvr_lsfslew); + val |= UTMIP_XCVR_LSRSLEW(sc->xcvr_lsrslew); + val |= UTMIP_XCVR_HSSLEW(sc->xcvr_hsslew); + val |= UTMIP_XCVR_HSSLEW_MSB(sc->xcvr_hsslew); + if (!sc->xcvr_setup_use_fuses) { + val &= ~UTMIP_XCVR_SETUP(~0); + val &= ~UTMIP_XCVR_SETUP_MSB(~0); + val |= UTMIP_XCVR_SETUP(sc->xcvr_setup); + val |= UTMIP_XCVR_SETUP_MSB(sc->xcvr_setup); + } + WR4(sc, UTMIP_XCVR_CFG0, val); + + val = RD4(sc, UTMIP_XCVR_CFG1); + val &= ~UTMIP_FORCE_PDDISC_POWERDOWN; + val &= ~UTMIP_FORCE_PDCHRP_POWERDOWN; + val &= ~UTMIP_FORCE_PDDR_POWERDOWN; + val &= ~UTMIP_XCVR_TERM_RANGE_ADJ(~0); + val |= UTMIP_XCVR_TERM_RANGE_ADJ(sc->term_range_adj); + WR4(sc, UTMIP_XCVR_CFG1, val); + + + val = RD4(sc, UTMIP_BIAS_CFG1); + val &= ~UTMIP_BIAS_PDTRK_COUNT(~0); + val |= UTMIP_BIAS_PDTRK_COUNT(0x5); + WR4(sc, UTMIP_BIAS_CFG1, val); + + val = RD4(sc, UTMIP_SPARE_CFG0); + if (sc->xcvr_setup_use_fuses) + val |= FUSE_SETUP_SEL; + else + val &= ~FUSE_SETUP_SEL; + WR4(sc, UTMIP_SPARE_CFG0, val); + + val = RD4(sc, IF_USB_SUSP_CTRL); + val |= UTMIP_PHY_ENB; + WR4(sc, IF_USB_SUSP_CTRL, val); + + val = RD4(sc, IF_USB_SUSP_CTRL); + val &= ~UTMIP_RESET; + WR4(sc, IF_USB_SUSP_CTRL, val); + + usbphy_utmi_phy_clk(sc, true); + + val = RD4(sc, CTRL_USB_USBMODE); + val &= ~USB_USBMODE_MASK; + if (sc->dr_mode == USB_DR_MODE_HOST) + val |= USB_USBMODE_HOST; + else + val |= USB_USBMODE_DEVICE; + WR4(sc, CTRL_USB_USBMODE, val); + + val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC); + val &= ~USB_HOSTPC1_DEVLC_PTS(~0); + val |= USB_HOSTPC1_DEVLC_PTS(0); + WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val); + + return (0); +} + +static int +usbphy_utmi_disable(struct usbphy_softc *sc) +{ + int rv; + uint32_t val; + + usbphy_utmi_phy_clk(sc, false); + + if (sc->dr_mode == USB_DR_MODE_DEVICE) { + val = RD4(sc, IF_USB_SUSP_CTRL); + val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); + val |= USB_WAKE_ON_CNNT_EN_DEV; + val |= USB_WAKEUP_DEBOUNCE_COUNT(5); + WR4(sc, IF_USB_SUSP_CTRL, val); + } + + val = RD4(sc, IF_USB_SUSP_CTRL); + val |= UTMIP_RESET; + WR4(sc, IF_USB_SUSP_CTRL, val); + + val = RD4(sc, UTMIP_BAT_CHRG_CFG0); + val |= UTMIP_PD_CHRG; + WR4(sc, UTMIP_BAT_CHRG_CFG0, val); + + val = RD4(sc, UTMIP_XCVR_CFG0); + val |= UTMIP_FORCE_PD_POWERDOWN; + val |= UTMIP_FORCE_PD2_POWERDOWN; + val |= UTMIP_FORCE_PDZI_POWERDOWN; + WR4(sc, UTMIP_XCVR_CFG0, val); + + val = RD4(sc, UTMIP_XCVR_CFG1); + val |= UTMIP_FORCE_PDDISC_POWERDOWN; + val |= UTMIP_FORCE_PDCHRP_POWERDOWN; + val |= UTMIP_FORCE_PDDR_POWERDOWN; + WR4(sc, UTMIP_XCVR_CFG1, val); + + usbpby_enable_cnt--; + if (usbpby_enable_cnt <= 0) { + rv = clk_enable(sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'utmi-pads' clock\n"); + return (rv); + } + val =bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0); + val |= UTMIP_OTGPD; + val |= UTMIP_BIASPD; + bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val); + + rv = clk_disable(sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, + "Cannot disable 'utmi-pads' clock\n"); + return (rv); + } + } + return (0); +} + +static int +usbphy_phy_enable(device_t dev, int id, bool enable) +{ + struct usbphy_softc *sc; + int rv = 0; + + sc = device_get_softc(dev); + + if (sc->ifc_type != USB_IFC_TYPE_UTMI) { + device_printf(sc->dev, + "Only UTMI interface is supported.\n"); + return (ENXIO); + } + if (enable) + rv = usbphy_utmi_enable(sc); + else + rv = usbphy_utmi_disable(sc); + + return (rv); +} + +static enum usb_ifc_type +usb_get_ifc_mode(device_t dev, phandle_t node, char *name) +{ + char *tmpstr; + int rv; + enum usb_ifc_type ret; + + rv = OF_getprop_alloc(node, name, 1, (void **)&tmpstr); + if (rv <= 0) + return (USB_IFC_TYPE_UNKNOWN); + + ret = USB_IFC_TYPE_UNKNOWN; + if (strcmp(tmpstr, "utmi") == 0) + ret = USB_IFC_TYPE_UTMI; + else if (strcmp(tmpstr, "ulpi") == 0) + ret = USB_IFC_TYPE_ULPI; + else + device_printf(dev, "Unsupported phy type: %s\n", tmpstr); + free(tmpstr, M_OFWPROP); + return (ret); +} + +static enum usb_dr_mode +usb_get_dr_mode(device_t dev, phandle_t node, char *name) +{ + char *tmpstr; + int rv; + enum usb_dr_mode ret; + + rv = OF_getprop_alloc(node, name, 1, (void **)&tmpstr); + if (rv <= 0) + return (USB_DR_MODE_UNKNOWN); + + ret = USB_DR_MODE_UNKNOWN; + if (strcmp(tmpstr, "device") == 0) + ret = USB_DR_MODE_DEVICE; + else if (strcmp(tmpstr, "host") == 0) + ret = USB_DR_MODE_HOST; + else if (strcmp(tmpstr, "otg") == 0) + ret = USB_DR_MODE_OTG; + else + device_printf(dev, "Unknown dr mode: %s\n", tmpstr); + free(tmpstr, M_OFWPROP); + return (ret); +} + +static int +usbphy_utmi_read_params(struct usbphy_softc *sc, phandle_t node) +{ + int rv; + + rv = OF_getencprop(node, "nvidia,hssync-start-delay", + &sc->hssync_start_delay, sizeof (sc->hssync_start_delay)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,elastic-limit", + &sc->elastic_limit, sizeof (sc->elastic_limit)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,idle-wait-delay", + &sc->idle_wait_delay, sizeof (sc->idle_wait_delay)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,term-range-adj", + &sc->term_range_adj, sizeof (sc->term_range_adj)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,xcvr-lsfslew", + &sc->xcvr_lsfslew, sizeof (sc->xcvr_lsfslew)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,xcvr-lsrslew", + &sc->xcvr_lsrslew, sizeof (sc->xcvr_lsrslew)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,xcvr-hsslew", + &sc->xcvr_hsslew, sizeof (sc->xcvr_hsslew)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,hssquelch-level", + &sc->hssquelch_level, sizeof (sc->hssquelch_level)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getencprop(node, "nvidia,hsdiscon-level", + &sc->hsdiscon_level, sizeof (sc->hsdiscon_level)); + if (rv <= 0) + return (ENXIO); + + rv = OF_getproplen(node, "nvidia,xcvr-setup-use-fuses"); + if (rv >= 1) { + sc->xcvr_setup_use_fuses = 1; + } else { + rv = OF_getencprop(node, "nvidia,xcvr-setup", + &sc->xcvr_setup, sizeof (sc->xcvr_setup)); + if (rv <= 0) + return (ENXIO); + } + + return (0); +} + +static int +usbphy_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, "Tegra USB phy"); + return (BUS_PROBE_DEFAULT); +} + +static int +usbphy_attach(device_t dev) +{ + struct usbphy_softc * sc; + int rid, rv; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + rid = 1; + sc->pads_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + + rv = hwreset_get_by_ofw_name(sc->dev, "usb", &sc->reset_usb); + if (rv != 0) { + device_printf(dev, "Cannot get 'usb' reset\n"); + return (ENXIO); + } + rv = hwreset_get_by_ofw_name(sc->dev, "utmi-pads", &sc->reset_pads); + if (rv != 0) { + device_printf(dev, "Cannot get 'utmi-pads' reset\n"); + return (ENXIO); + } + + rv = clk_get_by_ofw_name(sc->dev, "reg", &sc->clk_reg); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'reg' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "pll_u", &sc->clk_pllu); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'pll_u' clock\n"); + return (ENXIO); + } + rv = clk_get_by_ofw_name(sc->dev, "utmi-pads", &sc->clk_pads); + if (rv != 0) { + device_printf(sc->dev, "Cannot get 'utmi-pads' clock\n"); + return (ENXIO); + } + + rv = hwreset_deassert(sc->reset_usb); + if (rv != 0) { + device_printf(dev, "Cannot unreset 'usb' reset\n"); + return (ENXIO); + } + + rv = clk_enable(sc->clk_pllu); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'pllu' clock\n"); + return (ENXIO); + } + rv = clk_enable(sc->clk_reg); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable 'reg' clock\n"); + return (ENXIO); + } + if (OF_hasprop(node, "nvidia,has-utmi-pad-registers")) + sc->have_utmi_regs = true; + + sc->dr_mode = usb_get_dr_mode(dev, node, "dr_mode"); + if (sc->dr_mode == USB_DR_MODE_UNKNOWN) + sc->dr_mode = USB_DR_MODE_HOST; + + sc->ifc_type = usb_get_ifc_mode(dev, node, "phy_type"); + + /* We supports only utmi phy mode for now .... */ + if (sc->ifc_type != USB_IFC_TYPE_UTMI) { + device_printf(dev, "Unsupported phy type\n"); + return (ENXIO); + } + rv = usbphy_utmi_read_params(sc, node); + if (rv < 0) + return rv; + + if (OF_hasprop(node, "vbus-supply")) { + rv = regulator_get_by_ofw_property(sc->dev, "vbus-supply", + &sc->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot get \"vbus\" regulator\n"); + return (ENXIO); + } + rv = regulator_enable(sc->supply_vbus); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable \"vbus\" regulator\n"); + return (rv); + } + } + + phy_register_provider(dev); + return (0); +} + +static int +usbphy_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static device_method_t tegra_usbphy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, usbphy_probe), + DEVMETHOD(device_attach, usbphy_attach), + DEVMETHOD(device_detach, usbphy_detach), + + /* phy interface */ + DEVMETHOD(phy_enable, usbphy_phy_enable), + + DEVMETHOD_END +}; + +static driver_t tegra_usbphy_driver = { + "tegra_usbphy", + tegra_usbphy_methods, + sizeof(struct usbphy_softc), +}; + +static devclass_t tegra_usbphy_devclass; + +EARLY_DRIVER_MODULE(tegra_usbphy, simplebus, tegra_usbphy_driver, + tegra_usbphy_devclass, 0, 0, 79); diff --git a/sys/boot/fdt/dts/arm/tegra124-jetson-tk1-fbsd.dts b/sys/boot/fdt/dts/arm/tegra124-jetson-tk1-fbsd.dts new file mode 100644 index 0000000..902616b --- /dev/null +++ b/sys/boot/fdt/dts/arm/tegra124-jetson-tk1-fbsd.dts @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2016 Michal Meloun + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * 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 "tegra124-jetson-tk1.dts" + +/ { + chosen { + stdin = &uartd; + stdout = &uartd; + }; + + memory { +/* reg = <0x0 0x80000000 0x0 0x80000000>; */ + reg = <0x0 0x80000000 0x0 0x70000000>; + }; + +}; -- cgit v1.1 From d66d04f246666cfb8f159681847925aab2a39362 Mon Sep 17 00:00:00 2001 From: trasz Date: Wed, 16 Mar 2016 14:00:45 +0000 Subject: Pacify Coverity in a better way, to avoid write-only variable when building without INVARIANTS. MFC after: 1 month Sponsored by: The FreeBSD Foundation --- sys/fs/autofs/autofs_vnops.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sys/fs/autofs/autofs_vnops.c b/sys/fs/autofs/autofs_vnops.c index 5fe7de9..dda886d 100644 --- a/sys/fs/autofs/autofs_vnops.c +++ b/sys/fs/autofs/autofs_vnops.c @@ -370,10 +370,8 @@ static size_t autofs_dirent_reclen(const char *name) { size_t reclen; - int error; - error = autofs_readdir_one(NULL, name, -1, &reclen); - KASSERT(error == 0, ("autofs_readdir_one() failed")); + (void)autofs_readdir_one(NULL, name, -1, &reclen); return (reclen); } -- cgit v1.1 From af3e10e5a78d3af8cef6088748978c6c612757f0 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 16 Mar 2016 15:31:09 +0000 Subject: Remove old COMPAT_FREEBSD options from the ARM kernel configs. We replaced the ABI in 10.0, and have removed support for the old ABI in 11. As such any of these options to provide compatibility prior to 10 are unneeded. Sponsored by: ABT Systems Ltd --- sys/arm/conf/ATMEL | 5 ----- sys/arm/conf/CNS11XXNAS | 5 ----- sys/arm/conf/EFIKA_MX | 3 --- sys/arm/conf/ETHERNUT5 | 3 --- sys/arm/conf/IMX53 | 3 --- sys/arm/conf/NOTES | 4 ++++ sys/arm/conf/SAM9260EK | 3 --- sys/arm/conf/std.arm | 1 + sys/arm/conf/std.armv6 | 2 ++ 9 files changed, 7 insertions(+), 22 deletions(-) diff --git a/sys/arm/conf/ATMEL b/sys/arm/conf/ATMEL index f86f8e8..63cd785 100644 --- a/sys/arm/conf/ATMEL +++ b/sys/arm/conf/ATMEL @@ -57,11 +57,6 @@ options GEOM_PART_BSD # BSD partition scheme options GEOM_PART_MBR # MBR partition scheme options GEOM_PART_GPT # GUID Partition Tables. options GEOM_LABEL # Provides labelization -options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -options COMPAT_FREEBSD7 # Compatible with FreeBSD7 -options COMPAT_FREEBSD9 # Compatible with FreeBSD9 -options COMPAT_FREEBSD10 # Compatible with FreeBSD10 options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options KTRACE # ktrace(1) support options STACK # stack(9) support diff --git a/sys/arm/conf/CNS11XXNAS b/sys/arm/conf/CNS11XXNAS index 2bb5920..6fc8876 100644 --- a/sys/arm/conf/CNS11XXNAS +++ b/sys/arm/conf/CNS11XXNAS @@ -46,11 +46,6 @@ options DDB # Enable the kernel debugger #options DIAGNOSTIC -#options COMPAT_FREEBSD5 -#options COMPAT_FREEBSD6 -#options COMPAT_FREEBSD7n - - options SCHED_ULE # ULE scheduler #options SCHED_4BSD # 4BSD scheduler options GEOM_PART_BSD # BSD partition scheme diff --git a/sys/arm/conf/EFIKA_MX b/sys/arm/conf/EFIKA_MX index 7f978b2..8b4b4aa 100644 --- a/sys/arm/conf/EFIKA_MX +++ b/sys/arm/conf/EFIKA_MX @@ -30,9 +30,6 @@ options SOC_IMX51 options SCHED_4BSD # 4BSD scheduler #options MD_ROOT # MD is a potential root device #options NFSD # Network Filesystem Server -#options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -#options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -#options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options PLATFORM options INCLUDE_CONFIG_FILE # Include this file in kernel diff --git a/sys/arm/conf/ETHERNUT5 b/sys/arm/conf/ETHERNUT5 index 4604ff2..e54513b 100644 --- a/sys/arm/conf/ETHERNUT5 +++ b/sys/arm/conf/ETHERNUT5 @@ -53,9 +53,6 @@ options GEOM_PART_BSD # BSD partition scheme options GEOM_PART_MBR # MBR partition scheme #options GEOM_PART_GPT # GUID Partition Tables. #options GEOM_LABEL # Provides labelization -#options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -#options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -#options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options KTRACE # ktrace(1) support #options STACK # stack(9) support diff --git a/sys/arm/conf/IMX53 b/sys/arm/conf/IMX53 index 9724375..e320562 100644 --- a/sys/arm/conf/IMX53 +++ b/sys/arm/conf/IMX53 @@ -27,9 +27,6 @@ options SOC_IMX53 options SCHED_4BSD # 4BSD scheduler #options NFSD # Network Filesystem Server -#options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -#options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -#options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options PLATFORM options INCLUDE_CONFIG_FILE # Include this file in kernel diff --git a/sys/arm/conf/NOTES b/sys/arm/conf/NOTES index d32e6d6..3cd43af 100644 --- a/sys/arm/conf/NOTES +++ b/sys/arm/conf/NOTES @@ -58,6 +58,10 @@ nooptions SMP nooptions MAXCPU nooptions COMPAT_FREEBSD4 +nooptions COMPAT_FREEBSD5 +nooptions COMPAT_FREEBSD6 +nooptions COMPAT_FREEBSD7 +nooptions COMPAT_FREEBSD9 nooption PPC_PROBE_CHIPSET nodevice fdc diff --git a/sys/arm/conf/SAM9260EK b/sys/arm/conf/SAM9260EK index 1edee77..7248046 100644 --- a/sys/arm/conf/SAM9260EK +++ b/sys/arm/conf/SAM9260EK @@ -64,9 +64,6 @@ options GEOM_PART_BSD # BSD partition scheme options GEOM_PART_MBR # MBR partition scheme #options GEOM_PART_GPT # GUID Partition Tables. #options GEOM_LABEL # Provides labelization -#options COMPAT_FREEBSD5 # Compatible with FreeBSD5 -#options COMPAT_FREEBSD6 # Compatible with FreeBSD6 -#options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options KTRACE # ktrace(1) support #options STACK # stack(9) support diff --git a/sys/arm/conf/std.arm b/sys/arm/conf/std.arm index 88675f0..b9ac640 100644 --- a/sys/arm/conf/std.arm +++ b/sys/arm/conf/std.arm @@ -2,4 +2,5 @@ # # $FreeBSD$ +options COMPAT_FREEBSD10 # Compatible with FreeBSD10 diff --git a/sys/arm/conf/std.armv6 b/sys/arm/conf/std.armv6 index 142c7d3..4dc4906 100644 --- a/sys/arm/conf/std.armv6 +++ b/sys/arm/conf/std.armv6 @@ -39,6 +39,8 @@ options KBD_INSTALL_CDEV # install a CDEV entry in /dev options FREEBSD_BOOT_LOADER # Process metadata passed from loader(8) options VFP # Enable floating point hardware support +options COMPAT_FREEBSD10 # Compatible with FreeBSD10 + # DTrace support options KDTRACE_HOOKS # Kernel DTrace hooks options DDB_CTF # all architectures - kernel ELF linker loads CTF data -- cgit v1.1 From c4c446f0d8d562b32f5750aa8d0c7ddc8e0cab60 Mon Sep 17 00:00:00 2001 From: imp Date: Wed, 16 Mar 2016 16:56:28 +0000 Subject: Fix debug printf --- sys/dev/ppbus/vpo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/dev/ppbus/vpo.c b/sys/dev/ppbus/vpo.c index a4f5226..f508275 100644 --- a/sys/dev/ppbus/vpo.c +++ b/sys/dev/ppbus/vpo.c @@ -316,7 +316,7 @@ vpo_action(struct cam_sim *sim, union ccb *ccb) } #ifdef VP0_DEBUG device_printf(vpo->vpo_dev, "XPT_SCSI_IO (0x%x) request\n", - scsiio_cdb_ptr(csio)); + *scsiio_cdb_ptr(csio)); #endif vpo_intr(vpo, csio); -- cgit v1.1 From da0e05bf035f3db2c19ef9748cb45de9f4dffe6d Mon Sep 17 00:00:00 2001 From: bdrewery Date: Wed, 16 Mar 2016 18:39:48 +0000 Subject: Remove incorrect BUGS entry about asserting lock not held. For non-WITNESS< assertion support for SA_UNLOCKED was added in r125421 and made to panic in r126316. MFC after: 1 week --- share/man/man9/sx.9 | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/share/man/man9/sx.9 b/share/man/man9/sx.9 index a9931a9..677f8b7 100644 --- a/share/man/man9/sx.9 +++ b/share/man/man9/sx.9 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 13, 2016 +.Dd March 16, 2016 .Dt SX 9 .Os .Sh NAME @@ -320,11 +320,6 @@ end up sleeping while holding a mutex, which is not allowed. .Xr rwlock 9 , .Xr sema 9 .Sh BUGS -Currently there is no way to assert that a lock is not held. -This is not possible in the -.No non- Ns Dv WITNESS -case for asserting that this thread -does not hold a shared lock. In the .No non- Ns Dv WITNESS case, the -- cgit v1.1 From 3d35db397a53560a2365e3b346108ea6cb5ac519 Mon Sep 17 00:00:00 2001 From: emaste Date: Wed, 16 Mar 2016 18:55:17 +0000 Subject: Remove armeb FreeBSD 6 compat shim r296861 addressed a build failure due to undefined SYS_freebsd6_lseek by adding a COMPAT_FREEBSD6 conditional, but we do not support FreeBSD 6 compatibility on armeb anyway so remove it completely. Reviewed by: andrew, bz Differential Revision: https://reviews.freebsd.org/D5643 --- sys/arm/arm/vm_machdep.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sys/arm/arm/vm_machdep.c b/sys/arm/arm/vm_machdep.c index 70d6c70..fdab1c6 100644 --- a/sys/arm/arm/vm_machdep.c +++ b/sys/arm/arm/vm_machdep.c @@ -191,11 +191,7 @@ cpu_set_syscall_retval(struct thread *td, int error) register_t code = ap[_QUAD_LOWWORD]; if (td->td_proc->p_sysent->sv_mask) code &= td->td_proc->p_sysent->sv_mask; - fixup = ( -#if defined(COMPAT_FREEBSD6) && defined(SYS_freebsd6_lseek) - code != SYS_freebsd6_lseek && -#endif - code != SYS_lseek) ? 1 : 0; + fixup = (code != SYS_lseek); } #endif -- cgit v1.1 From 76345b1fe4fffb6395f8a0ff06df0dcdf34d261c Mon Sep 17 00:00:00 2001 From: np Date: Wed, 16 Mar 2016 19:36:11 +0000 Subject: cxgbe(4): Remove a couple of pointless assignments in sysctl_meminfo. Do not display range if start = stop (this is a workaround for some unused regions). --- sys/dev/cxgbe/t4_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index 77777f8..ef65a18 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -6287,6 +6287,9 @@ mem_region_show(struct sbuf *sb, const char *name, unsigned int from, { unsigned int size; + if (from == to) + return; + size = to - from + 1; if (size == 0) return; @@ -6390,13 +6393,10 @@ sysctl_meminfo(SYSCTL_HANDLER_ARGS) md++; if (t4_read_reg(sc, A_LE_DB_CONFIG) & F_HASHEN) { - if (chip_id(sc) <= CHELSIO_T5) { - hi = t4_read_reg(sc, A_LE_DB_TID_HASHBASE) / 4; + if (chip_id(sc) <= CHELSIO_T5) md->base = t4_read_reg(sc, A_LE_DB_HASH_TID_BASE); - } else { - hi = t4_read_reg(sc, A_LE_DB_HASH_TID_BASE); + else md->base = t4_read_reg(sc, A_LE_DB_HASH_TBL_BASE_ADDR); - } md->limit = 0; } else { md->base = 0; -- cgit v1.1 From 39c3dc6b781ddce01de02a834d1348b4df627e10 Mon Sep 17 00:00:00 2001 From: np Date: Wed, 16 Mar 2016 19:41:00 +0000 Subject: cxgbe(4): Update some register settings in the default configuration files to match the "uwire" configuration. --- sys/dev/cxgbe/firmware/t4fw_cfg.txt | 23 +++++++------- sys/dev/cxgbe/firmware/t5fw_cfg.txt | 63 ++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/sys/dev/cxgbe/firmware/t4fw_cfg.txt b/sys/dev/cxgbe/firmware/t4fw_cfg.txt index 0e13122..01dc345 100644 --- a/sys/dev/cxgbe/firmware/t4fw_cfg.txt +++ b/sys/dev/cxgbe/firmware/t4fw_cfg.txt @@ -10,7 +10,7 @@ [global] rss_glb_config_mode = basicvirtual - rss_glb_config_options = tnlmapen, hashtoeplitz, tnlalllkp + rss_glb_config_options = tnlmapen,hashtoeplitz,tnlalllkp sge_timer_value = 1, 5, 10, 50, 100, 200 # usecs @@ -20,28 +20,29 @@ # disable TP_PARA_REG3.RxFragEn reg[0x7d6c] = 0x00000000/0x00007000 - # TP_SHIFT_CNT - reg[0x7dc0] = 0x62f8849 + reg[0x7dc0] = 0x0e2f8849 # TP_SHIFT_CNT filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe filterMask = protocol, fcoe - # TP rx and tx channels (0 = auto). + tp_pmrx = 38, 512 + tp_pmrx_pagesize = 64K + + # TP number of RX channels (0 = auto) tp_nrxch = 0 - tp_ntxch = 0 - # TP rx and tx payload memory (% of the total EDRAM + DDR3). - tp_pmrx = 38, 512 tp_pmtx = 60, 512 - tp_pmrx_pagesize = 64K tp_pmtx_pagesize = 64K - # cluster, lan, or wan. - tp_tcptuning = lan + # TP number of TX channels (0 = auto) + tp_ntxch = 0 # TP OFLD MTUs tp_mtus = 88, 256, 512, 576, 808, 1024, 1280, 1488, 1500, 2002, 2048, 4096, 4352, 8192, 9000, 9600 + # cluster, lan, or wan. + tp_tcptuning = lan + # PFs 0-3. These get 8 MSI/8 MSI-X vectors each. VFs are supported by # these 4 PFs only. Not used here at all. [function "0"] @@ -166,7 +167,7 @@ [fini] version = 0x1 - checksum = 0x98210e18 + checksum = 0x5a34a3ce # # $FreeBSD$ # diff --git a/sys/dev/cxgbe/firmware/t5fw_cfg.txt b/sys/dev/cxgbe/firmware/t5fw_cfg.txt index 4ae6c99..d4f8f88 100644 --- a/sys/dev/cxgbe/firmware/t5fw_cfg.txt +++ b/sys/dev/cxgbe/firmware/t5fw_cfg.txt @@ -10,12 +10,33 @@ [global] rss_glb_config_mode = basicvirtual - rss_glb_config_options = tnlmapen, hashtoeplitz, tnlalllkp + rss_glb_config_options = tnlmapen,hashtoeplitz,tnlalllkp # PL_TIMEOUT register - pl_timeout_value = 200 # the timeout value in units of us + pl_timeout_value = 10000 # the timeout value in units of us - sge_timer_value = 1, 5, 10, 50, 100, 200 # usecs + # SGE_THROTTLE_CONTROL + bar2throttlecount = 500 # bar2throttlecount in us + + sge_timer_value = 1, 5, 10, 50, 100, 200 # SGE_TIMER_VALUE* in usecs + + reg[0x1124] = 0x00000400/0x00000400 # SGE_CONTROL2, enable VFIFO; if + # SGE_VFIFO_SIZE is not set, then + # firmware will set it up in function + # of number of egress queues used + + reg[0x1130] = 0x00d5ffeb # SGE_DBP_FETCH_THRESHOLD, fetch + # threshold set to queue depth + # minus 128-entries for FL and HP + # queues, and 0xfff for LP which + # prompts the firmware to set it up + # in function of egress queues + # used + + reg[0x113c] = 0x0002ffc0 # SGE_VFIFO_SIZE, set to 0x2ffc0 which + # prompts the firmware to set it up in + # function of number of egress queues + # used # enable TP_OUT_CONFIG.IPIDSPLITMODE reg[0x7d04] = 0x00010000/0x00010000 @@ -26,34 +47,38 @@ # enable TP_PARA_REG6.EnableCSnd reg[0x7d78] = 0x00000400/0x00000000 - # TP_SHIFT_CNT - reg[0x7dc0] = 0x62f8849 - - # TP_GLOBAL_CONFIG - reg[0x7d08] = 0x00000800/0x00000800 # set IssFromCplEnable - - # TP_PARA_REG0 - reg[0x7d60] = 0x06000000/0x07000000 # set InitCWND to 6 + reg[0x7dc0] = 0x0e2f8849 # TP_SHIFT_CNT filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe filterMask = protocol, fcoe - # TP rx and tx channels (0 = auto). + tp_pmrx = 38, 512 + tp_pmrx_pagesize = 64K + + # TP number of RX channels (0 = auto) tp_nrxch = 0 - tp_ntxch = 0 - # TP rx and tx payload memory (% of the total EDRAM + DDR3). - tp_pmrx = 38, 512 tp_pmtx = 60, 512 - tp_pmrx_pagesize = 64K tp_pmtx_pagesize = 64K - # cluster, lan, or wan. - tp_tcptuning = lan + # TP number of TX channels (0 = auto) + tp_ntxch = 0 # TP OFLD MTUs tp_mtus = 88, 256, 512, 576, 808, 1024, 1280, 1488, 1500, 2002, 2048, 4096, 4352, 8192, 9000, 9600 + # TP_GLOBAL_CONFIG + reg[0x7d08] = 0x00000800/0x00000800 # set IssFromCplEnable + + # TP_PC_CONFIG + reg[0x7d48] = 0x00000000/0x00000400 # clear EnableFLMError + + # TP_PARA_REG0 + reg[0x7d60] = 0x06000000/0x07000000 # set InitCWND to 6 + + # cluster, lan, or wan. + tp_tcptuning = lan + # MC configuration mc_mode_brc[0] = 1 # mc0 - 1: enable BRC, 0: enable RBC mc_mode_brc[1] = 1 # mc1 - 1: enable BRC, 0: enable RBC @@ -186,7 +211,7 @@ [fini] version = 0x1 - checksum = 0x7044b7fd + checksum = 0xdd685b # # $FreeBSD$ # -- cgit v1.1 From 691b4852d0fe4e6dd384fe4b1da789de561c10ab Mon Sep 17 00:00:00 2001 From: np Date: Wed, 16 Mar 2016 19:43:44 +0000 Subject: cxgbe(4): Enable additional capabilities in the default configuration files. All features with FreeBSD drivers of some kind are now in the default configuration. --- sys/dev/cxgbe/firmware/t4fw_cfg.txt | 12 ++++++++---- sys/dev/cxgbe/firmware/t5fw_cfg.txt | 12 ++++++++---- sys/dev/cxgbe/t4_main.c | 24 +++++++++++++++++++++--- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/sys/dev/cxgbe/firmware/t4fw_cfg.txt b/sys/dev/cxgbe/firmware/t4fw_cfg.txt index 01dc345..17c24e6 100644 --- a/sys/dev/cxgbe/firmware/t4fw_cfg.txt +++ b/sys/dev/cxgbe/firmware/t4fw_cfg.txt @@ -25,13 +25,13 @@ filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe filterMask = protocol, fcoe - tp_pmrx = 38, 512 + tp_pmrx = 36, 512 tp_pmrx_pagesize = 64K # TP number of RX channels (0 = auto) tp_nrxch = 0 - tp_pmtx = 60, 512 + tp_pmtx = 46, 512 tp_pmtx_pagesize = 64K # TP number of TX channels (0 = auto) @@ -92,10 +92,14 @@ pmask = all # driver will mask off features it won't use - protocol = ofld + protocol = ofld, rddp, rdmac, iscsi_initiator_pdu, iscsi_target_pdu tp_l2t = 4096 tp_ddp = 2 + tp_ddp_iscsi = 2 + tp_stag = 2 + tp_pbl = 5 + tp_rq = 7 # TCAM has 8K cells; each region must start at a multiple of 128 cell. # Each entry in these categories takes 4 cells each. nhash will use the @@ -167,7 +171,7 @@ [fini] version = 0x1 - checksum = 0x5a34a3ce + checksum = 0x76b034e0 # # $FreeBSD$ # diff --git a/sys/dev/cxgbe/firmware/t5fw_cfg.txt b/sys/dev/cxgbe/firmware/t5fw_cfg.txt index d4f8f88..9cb7c83 100644 --- a/sys/dev/cxgbe/firmware/t5fw_cfg.txt +++ b/sys/dev/cxgbe/firmware/t5fw_cfg.txt @@ -52,13 +52,13 @@ filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe filterMask = protocol, fcoe - tp_pmrx = 38, 512 + tp_pmrx = 36, 512 tp_pmrx_pagesize = 64K # TP number of RX channels (0 = auto) tp_nrxch = 0 - tp_pmtx = 60, 512 + tp_pmtx = 46, 512 tp_pmtx_pagesize = 64K # TP number of TX channels (0 = auto) @@ -136,10 +136,14 @@ pmask = all # driver will mask off features it won't use - protocol = ofld + protocol = ofld, rddp, rdmac, iscsi_initiator_pdu, iscsi_target_pdu, iscsi_t10dif tp_l2t = 4096 tp_ddp = 2 + tp_ddp_iscsi = 2 + tp_stag = 2 + tp_pbl = 5 + tp_rq = 7 # TCAM has 8K cells; each region must start at a multiple of 128 cell. # Each entry in these categories takes 4 cells each. nhash will use the @@ -211,7 +215,7 @@ [fini] version = 0x1 - checksum = 0xdd685b + checksum = 0xebb87494 # # $FreeBSD$ # diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index ef65a18..0caa419 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -334,7 +334,8 @@ TUNABLE_INT("hw.cxgbe.nbmcaps_allowed", &t4_nbmcaps_allowed); static int t4_linkcaps_allowed = 0; /* No DCBX, PPP, etc. by default */ TUNABLE_INT("hw.cxgbe.linkcaps_allowed", &t4_linkcaps_allowed); -static int t4_switchcaps_allowed = 0; +static int t4_switchcaps_allowed = FW_CAPS_CONFIG_SWITCH_INGRESS | + FW_CAPS_CONFIG_SWITCH_EGRESS; TUNABLE_INT("hw.cxgbe.switchcaps_allowed", &t4_switchcaps_allowed); static int t4_niccaps_allowed = FW_CAPS_CONFIG_NIC; @@ -343,13 +344,13 @@ TUNABLE_INT("hw.cxgbe.niccaps_allowed", &t4_niccaps_allowed); static int t4_toecaps_allowed = -1; TUNABLE_INT("hw.cxgbe.toecaps_allowed", &t4_toecaps_allowed); -static int t4_rdmacaps_allowed = 0; +static int t4_rdmacaps_allowed = -1; TUNABLE_INT("hw.cxgbe.rdmacaps_allowed", &t4_rdmacaps_allowed); static int t4_tlscaps_allowed = 0; TUNABLE_INT("hw.cxgbe.tlscaps_allowed", &t4_tlscaps_allowed); -static int t4_iscsicaps_allowed = 0; +static int t4_iscsicaps_allowed = -1; TUNABLE_INT("hw.cxgbe.iscsicaps_allowed", &t4_iscsicaps_allowed); static int t4_fcoecaps_allowed = 0; @@ -9103,9 +9104,26 @@ tweak_tunables(void) if (t4_toecaps_allowed == -1) t4_toecaps_allowed = FW_CAPS_CONFIG_TOE; + + if (t4_rdmacaps_allowed == -1) { + t4_rdmacaps_allowed = FW_CAPS_CONFIG_RDMA_RDDP | + FW_CAPS_CONFIG_RDMA_RDMAC; + } + + if (t4_iscsicaps_allowed == -1) { + t4_iscsicaps_allowed = FW_CAPS_CONFIG_ISCSI_INITIATOR_PDU | + FW_CAPS_CONFIG_ISCSI_TARGET_PDU | + FW_CAPS_CONFIG_ISCSI_T10DIF; + } #else if (t4_toecaps_allowed == -1) t4_toecaps_allowed = 0; + + if (t4_rdmacaps_allowed == -1) + t4_rdmacaps_allowed = 0; + + if (t4_iscsicaps_allowed == -1) + t4_iscsicaps_allowed = 0; #endif #ifdef DEV_NETMAP -- cgit v1.1 From 7d1fd13da94eb69908d25dc487cad4b4ea50c4d0 Mon Sep 17 00:00:00 2001 From: np Date: Wed, 16 Mar 2016 19:46:22 +0000 Subject: cxgbe(4): Enable PFs 0-3, and allow creation of SR-IOV VFs on these PFs in the default configuration files. --- sys/dev/cxgbe/firmware/t4fw_cfg.txt | 128 ++++++++++++++++++++++++++++-------- sys/dev/cxgbe/firmware/t5fw_cfg.txt | 128 ++++++++++++++++++++++++++++-------- 2 files changed, 202 insertions(+), 54 deletions(-) diff --git a/sys/dev/cxgbe/firmware/t4fw_cfg.txt b/sys/dev/cxgbe/firmware/t4fw_cfg.txt index 17c24e6..43820a0 100644 --- a/sys/dev/cxgbe/firmware/t4fw_cfg.txt +++ b/sys/dev/cxgbe/firmware/t4fw_cfg.txt @@ -44,38 +44,58 @@ tp_tcptuning = lan # PFs 0-3. These get 8 MSI/8 MSI-X vectors each. VFs are supported by -# these 4 PFs only. Not used here at all. +# these 4 PFs only. [function "0"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "0/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x1 [function "1"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "1/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x2 [function "2"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "2/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x4 [function "3"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "3/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x8 # PF4 is the resource-rich PF that the bus/nexus driver attaches to. # It gets 32 MSI/128 MSI-X vectors. @@ -87,7 +107,7 @@ niqflint = 512 nethctrl = 1024 neq = 2048 - nexactf = 328 + nexactf = 280 cmask = all pmask = all @@ -135,6 +155,60 @@ nexactf = 8 nfilter = 16 +# For Virtual functions, we only allow NIC functionality and we only allow +# access to one port (1 << PF). Note that because of limitations in the +# Scatter Gather Engine (SGE) hardware which checks writes to VF KDOORBELL +# and GTS registers, the number of Ingress and Egress Queues must be a power +# of 2. +# +[function "0/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x1 + +[function "1/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x2 + +[function "2/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x4 + +[function "3/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x8 + # MPS has 192K buffer space for ingress packets from the wire as well as # loopback path of the L2 switch. [port "0"] @@ -171,7 +245,7 @@ [fini] version = 0x1 - checksum = 0x76b034e0 + checksum = 0xbec0621 # # $FreeBSD$ # diff --git a/sys/dev/cxgbe/firmware/t5fw_cfg.txt b/sys/dev/cxgbe/firmware/t5fw_cfg.txt index 9cb7c83..9e16da5 100644 --- a/sys/dev/cxgbe/firmware/t5fw_cfg.txt +++ b/sys/dev/cxgbe/firmware/t5fw_cfg.txt @@ -88,38 +88,58 @@ # TPT error. # PFs 0-3. These get 8 MSI/8 MSI-X vectors each. VFs are supported by -# these 4 PFs only. Not used here at all. +# these 4 PFs only. [function "0"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "0/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x1 [function "1"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "1/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x2 [function "2"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "2/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x4 [function "3"] - nvf = 16 - nvi = 1 - rssnvi = 0 -[function "3/*"] - nvi = 1 - rssnvi = 0 + nvf = 4 + wx_caps = all + r_caps = all + nvi = 2 + rssnvi = 2 + niqflint = 4 + nethctrl = 4 + neq = 8 + nexactf = 4 + cmask = all + pmask = 0x8 # PF4 is the resource-rich PF that the bus/nexus driver attaches to. # It gets 32 MSI/128 MSI-X vectors. @@ -131,7 +151,7 @@ niqflint = 512 nethctrl = 1024 neq = 2048 - nexactf = 328 + nexactf = 456 cmask = all pmask = all @@ -179,6 +199,60 @@ nexactf = 8 nfilter = 16 +# For Virtual functions, we only allow NIC functionality and we only allow +# access to one port (1 << PF). Note that because of limitations in the +# Scatter Gather Engine (SGE) hardware which checks writes to VF KDOORBELL +# and GTS registers, the number of Ingress and Egress Queues must be a power +# of 2. +# +[function "0/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x1 + +[function "1/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x2 + +[function "2/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x4 + +[function "3/*"] + wx_caps = 0x82 + r_caps = 0x86 + nvi = 1 + rssnvi = 1 + niqflint = 2 + nethctrl = 2 + neq = 4 + nexactf = 2 + cmask = all + pmask = 0x8 + # MPS has 192K buffer space for ingress packets from the wire as well as # loopback path of the L2 switch. [port "0"] @@ -215,7 +289,7 @@ [fini] version = 0x1 - checksum = 0xebb87494 + checksum = 0x2d7417e5 # # $FreeBSD$ # -- cgit v1.1 From ecffce941a074abbd944385dc1cc659f4df1b04d Mon Sep 17 00:00:00 2001 From: glebius Date: Wed, 16 Mar 2016 22:33:12 +0000 Subject: Due to invalid use of a signed intermediate value in the bounds checking during argument validity verification, unbound zero'ing of the process LDT and adjacent memory can be initiated from usermode. Submitted by: CORE Security Patch by: kib Security: SA-16:15 --- sys/amd64/amd64/sys_machdep.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/amd64/amd64/sys_machdep.c b/sys/amd64/amd64/sys_machdep.c index 7d9a748..7f6c50e 100644 --- a/sys/amd64/amd64/sys_machdep.c +++ b/sys/amd64/amd64/sys_machdep.c @@ -580,8 +580,8 @@ amd64_set_ldt(td, uap, descs) struct i386_ldt_args *uap; struct user_segment_descriptor *descs; { - int error = 0, i; - int largest_ld; + int error = 0; + unsigned int largest_ld, i; struct mdproc *mdp = &td->td_proc->p_md; struct proc_ldt *pldt; struct user_segment_descriptor *dp; -- cgit v1.1 From a321b98ea08fb6600ad382eb3a742c879d8efe4c Mon Sep 17 00:00:00 2001 From: obrien Date: Wed, 16 Mar 2016 23:06:34 +0000 Subject: Moving libxo to properly tracked, 3rd-Party imported handling. Reviewed by: phil, sjg --- dist/.travis.yml | 12 + dist/Copyright | 23 + dist/INSTALL.md | 15 + dist/LICENSE | 23 + dist/Makefile.am | 102 + dist/README.md | 64 + dist/bin/Makefile.am | 29 + dist/bin/Zaliases | 29 + dist/bin/setup.sh | 33 + dist/build/.create | 0 dist/configure.ac | 452 ++ dist/doc/Makefile.am | 70 + dist/doc/libxo.txt | 3757 +++++++++++ dist/encoder/Makefile.am | 9 + dist/encoder/cbor/Makefile.am | 51 + dist/encoder/cbor/enc_cbor.c | 365 ++ dist/encoder/test/Makefile.am | 51 + dist/encoder/test/enc_test.c | 30 + dist/install-sh | 527 ++ dist/libxo-config.in | 119 + dist/libxo/Makefile.am | 89 + dist/libxo/add.man | 29 + dist/libxo/add.man.in | 29 + dist/libxo/gen-wide.sh | 76 + dist/libxo/libxo.3 | 313 + dist/libxo/libxo.c | 7659 ++++++++++++++++++++++ dist/libxo/xo.h | 596 ++ dist/libxo/xo_attr.3 | 60 + dist/libxo/xo_buf.h | 158 + dist/libxo/xo_config.h | 247 + dist/libxo/xo_create.3 | 67 + dist/libxo/xo_emit.3 | 104 + dist/libxo/xo_emit_err.3 | 72 + dist/libxo/xo_encoder.c | 375 ++ dist/libxo/xo_encoder.h | 116 + dist/libxo/xo_err.3 | 74 + dist/libxo/xo_error.3 | 41 + dist/libxo/xo_finish.3 | 39 + dist/libxo/xo_flush.3 | 35 + dist/libxo/xo_format.5 | 943 +++ dist/libxo/xo_humanize.h | 169 + dist/libxo/xo_message.3 | 68 + dist/libxo/xo_no_setlocale.3 | 43 + dist/libxo/xo_open_container.3 | 188 + dist/libxo/xo_open_list.3 | 158 + dist/libxo/xo_open_marker.3 | 105 + dist/libxo/xo_parse_args.3 | 148 + dist/libxo/xo_set_allocator.3 | 54 + dist/libxo/xo_set_flags.3 | 139 + dist/libxo/xo_set_info.3 | 102 + dist/libxo/xo_set_options.3 | 31 + dist/libxo/xo_set_style.3 | 53 + dist/libxo/xo_set_syslog_enterprise_id.3 | 36 + dist/libxo/xo_set_version.3 | 34 + dist/libxo/xo_set_writer.3 | 56 + dist/libxo/xo_syslog.3 | 79 + dist/libxo/xo_syslog.c | 706 ++ dist/libxo/xo_wcwidth.h | 313 + dist/m4/libtool.m4 | 8369 ++++++++++++++++++++++++ dist/m4/ltoptions.m4 | 437 ++ dist/m4/ltsugar.m4 | 123 + dist/m4/ltversion.m4 | 23 + dist/m4/lt~obsolete.m4 | 98 + dist/packaging/libxo.pc.in | 11 + dist/packaging/libxo.rb.base.in | 20 + dist/packaging/libxo.spec.in | 44 + dist/tests/Makefile.am | 32 + dist/tests/core/Makefile.am | 129 + dist/tests/core/saved/test_01.E.err | 0 dist/tests/core/saved/test_01.E.out | 119 + dist/tests/core/saved/test_01.H.err | 0 dist/tests/core/saved/test_01.H.out | 1 + dist/tests/core/saved/test_01.HIPx.err | 0 dist/tests/core/saved/test_01.HIPx.out | 303 + dist/tests/core/saved/test_01.HP.err | 0 dist/tests/core/saved/test_01.HP.out | 303 + dist/tests/core/saved/test_01.J.err | 0 dist/tests/core/saved/test_01.J.out | 2 + dist/tests/core/saved/test_01.JP.err | 0 dist/tests/core/saved/test_01.JP.out | 106 + dist/tests/core/saved/test_01.T.err | 0 dist/tests/core/saved/test_01.T.out | 47 + dist/tests/core/saved/test_01.X.err | 0 dist/tests/core/saved/test_01.X.out | 1 + dist/tests/core/saved/test_01.XP.err | 0 dist/tests/core/saved/test_01.XP.out | 96 + dist/tests/core/saved/test_01.err | 0 dist/tests/core/saved/test_01.out | 38 + dist/tests/core/saved/test_02.E.err | 0 dist/tests/core/saved/test_02.E.out | 68 + dist/tests/core/saved/test_02.H.err | 0 dist/tests/core/saved/test_02.H.out | 7 + dist/tests/core/saved/test_02.HIPx.err | 0 dist/tests/core/saved/test_02.HIPx.out | 225 + dist/tests/core/saved/test_02.HP.err | 0 dist/tests/core/saved/test_02.HP.out | 225 + dist/tests/core/saved/test_02.J.err | 0 dist/tests/core/saved/test_02.J.out | 2 + dist/tests/core/saved/test_02.JP.err | 0 dist/tests/core/saved/test_02.JP.out | 82 + dist/tests/core/saved/test_02.T.err | 1 + dist/tests/core/saved/test_02.T.out | 37 + dist/tests/core/saved/test_02.X.err | 0 dist/tests/core/saved/test_02.X.out | 7 + dist/tests/core/saved/test_02.XP.err | 0 dist/tests/core/saved/test_02.XP.out | 87 + dist/tests/core/saved/test_02.err | 0 dist/tests/core/saved/test_02.out | 38 + dist/tests/core/saved/test_03.E.err | 0 dist/tests/core/saved/test_03.E.out | 22 + dist/tests/core/saved/test_03.H.err | 0 dist/tests/core/saved/test_03.H.out | 1 + dist/tests/core/saved/test_03.HIPx.err | 0 dist/tests/core/saved/test_03.HIPx.out | 21 + dist/tests/core/saved/test_03.HP.err | 0 dist/tests/core/saved/test_03.HP.out | 21 + dist/tests/core/saved/test_03.J.err | 0 dist/tests/core/saved/test_03.J.out | 2 + dist/tests/core/saved/test_03.JP.err | 0 dist/tests/core/saved/test_03.JP.out | 21 + dist/tests/core/saved/test_03.T.err | 0 dist/tests/core/saved/test_03.T.out | 3 + dist/tests/core/saved/test_03.X.err | 0 dist/tests/core/saved/test_03.X.out | 1 + dist/tests/core/saved/test_03.XP.err | 0 dist/tests/core/saved/test_03.XP.out | 17 + dist/tests/core/saved/test_03.err | 0 dist/tests/core/saved/test_03.out | 3 + dist/tests/core/saved/test_04.E.err | 0 dist/tests/core/saved/test_04.E.out | 22 + dist/tests/core/saved/test_04.H.err | 0 dist/tests/core/saved/test_04.H.out | 1 + dist/tests/core/saved/test_04.HIPx.err | 0 dist/tests/core/saved/test_04.HIPx.out | 20 + dist/tests/core/saved/test_04.HP.err | 0 dist/tests/core/saved/test_04.HP.out | 20 + dist/tests/core/saved/test_04.J.err | 0 dist/tests/core/saved/test_04.J.out | 2 + dist/tests/core/saved/test_04.JP.err | 0 dist/tests/core/saved/test_04.JP.out | 21 + dist/tests/core/saved/test_04.T.err | 0 dist/tests/core/saved/test_04.T.out | 4 + dist/tests/core/saved/test_04.X.err | 0 dist/tests/core/saved/test_04.X.out | 1 + dist/tests/core/saved/test_04.XP.err | 0 dist/tests/core/saved/test_04.XP.out | 17 + dist/tests/core/saved/test_05.E.err | 0 dist/tests/core/saved/test_05.E.out | 96 + dist/tests/core/saved/test_05.H.err | 0 dist/tests/core/saved/test_05.H.out | 1 + dist/tests/core/saved/test_05.HIPx.err | 0 dist/tests/core/saved/test_05.HIPx.out | 212 + dist/tests/core/saved/test_05.HP.err | 0 dist/tests/core/saved/test_05.HP.out | 212 + dist/tests/core/saved/test_05.J.err | 0 dist/tests/core/saved/test_05.J.out | 3 + dist/tests/core/saved/test_05.JP.err | 0 dist/tests/core/saved/test_05.JP.out | 92 + dist/tests/core/saved/test_05.T.err | 0 dist/tests/core/saved/test_05.T.out | 39 + dist/tests/core/saved/test_05.X.err | 0 dist/tests/core/saved/test_05.X.out | 1 + dist/tests/core/saved/test_05.XP.err | 0 dist/tests/core/saved/test_05.XP.out | 85 + dist/tests/core/saved/test_06.E.err | 0 dist/tests/core/saved/test_06.E.out | 22 + dist/tests/core/saved/test_06.H.err | 0 dist/tests/core/saved/test_06.H.out | 1 + dist/tests/core/saved/test_06.HIPx.err | 0 dist/tests/core/saved/test_06.HIPx.out | 21 + dist/tests/core/saved/test_06.HP.err | 0 dist/tests/core/saved/test_06.HP.out | 21 + dist/tests/core/saved/test_06.J.err | 0 dist/tests/core/saved/test_06.J.out | 2 + dist/tests/core/saved/test_06.JP.err | 0 dist/tests/core/saved/test_06.JP.out | 21 + dist/tests/core/saved/test_06.T.err | 0 dist/tests/core/saved/test_06.T.out | 3 + dist/tests/core/saved/test_06.X.err | 0 dist/tests/core/saved/test_06.X.out | 1 + dist/tests/core/saved/test_06.XP.err | 0 dist/tests/core/saved/test_06.XP.out | 17 + dist/tests/core/saved/test_07.E.err | 0 dist/tests/core/saved/test_07.E.out | 76 + dist/tests/core/saved/test_07.H.err | 0 dist/tests/core/saved/test_07.H.out | 1 + dist/tests/core/saved/test_07.HIPx.err | 0 dist/tests/core/saved/test_07.HIPx.out | 107 + dist/tests/core/saved/test_07.HP.err | 0 dist/tests/core/saved/test_07.HP.out | 107 + dist/tests/core/saved/test_07.J.err | 0 dist/tests/core/saved/test_07.J.out | 2 + dist/tests/core/saved/test_07.JP.err | 0 dist/tests/core/saved/test_07.JP.out | 71 + dist/tests/core/saved/test_07.T.err | 0 dist/tests/core/saved/test_07.T.out | 19 + dist/tests/core/saved/test_07.X.err | 0 dist/tests/core/saved/test_07.X.out | 1 + dist/tests/core/saved/test_07.XP.err | 0 dist/tests/core/saved/test_07.XP.out | 65 + dist/tests/core/saved/test_08.E.err | 18 + dist/tests/core/saved/test_08.E.out | 186 + dist/tests/core/saved/test_08.H.err | 18 + dist/tests/core/saved/test_08.H.out | 1 + dist/tests/core/saved/test_08.HIPx.err | 18 + dist/tests/core/saved/test_08.HIPx.out | 264 + dist/tests/core/saved/test_08.HP.err | 18 + dist/tests/core/saved/test_08.HP.out | 264 + dist/tests/core/saved/test_08.J.err | 18 + dist/tests/core/saved/test_08.J.out | 2 + dist/tests/core/saved/test_08.JP.err | 18 + dist/tests/core/saved/test_08.JP.out | 185 + dist/tests/core/saved/test_08.T.err | 18 + dist/tests/core/saved/test_08.T.out | 52 + dist/tests/core/saved/test_08.X.err | 18 + dist/tests/core/saved/test_08.X.out | 1 + dist/tests/core/saved/test_08.XP.err | 18 + dist/tests/core/saved/test_08.XP.out | 165 + dist/tests/core/saved/test_09.E.err | 0 dist/tests/core/saved/test_09.E.out | 40 + dist/tests/core/saved/test_09.H.err | 0 dist/tests/core/saved/test_09.H.out | 1 + dist/tests/core/saved/test_09.HIPx.err | 0 dist/tests/core/saved/test_09.HIPx.out | 93 + dist/tests/core/saved/test_09.HP.err | 0 dist/tests/core/saved/test_09.HP.out | 93 + dist/tests/core/saved/test_09.J.err | 0 dist/tests/core/saved/test_09.J.out | 2 + dist/tests/core/saved/test_09.JP.err | 0 dist/tests/core/saved/test_09.JP.out | 39 + dist/tests/core/saved/test_09.T.err | 0 dist/tests/core/saved/test_09.T.out | 25 + dist/tests/core/saved/test_09.X.err | 0 dist/tests/core/saved/test_09.X.out | 1 + dist/tests/core/saved/test_09.XP.err | 0 dist/tests/core/saved/test_09.XP.out | 29 + dist/tests/core/saved/test_10.E.err | 0 dist/tests/core/saved/test_10.E.out | 126 + dist/tests/core/saved/test_10.H.err | 0 dist/tests/core/saved/test_10.H.out | 1 + dist/tests/core/saved/test_10.HIPx.err | 0 dist/tests/core/saved/test_10.HIPx.out | 316 + dist/tests/core/saved/test_10.HP.err | 0 dist/tests/core/saved/test_10.HP.out | 316 + dist/tests/core/saved/test_10.J.err | 0 dist/tests/core/saved/test_10.J.out | 2 + dist/tests/core/saved/test_10.JP.err | 0 dist/tests/core/saved/test_10.JP.out | 113 + dist/tests/core/saved/test_10.T.err | 0 dist/tests/core/saved/test_10.T.out | 48 + dist/tests/core/saved/test_10.X.err | 0 dist/tests/core/saved/test_10.X.out | 1 + dist/tests/core/saved/test_10.XP.err | 0 dist/tests/core/saved/test_10.XP.out | 100 + dist/tests/core/saved/test_10.err | 0 dist/tests/core/saved/test_10.out | 38 + dist/tests/core/saved/test_11.E.err | 0 dist/tests/core/saved/test_11.E.out | 26 + dist/tests/core/saved/test_11.H.err | 0 dist/tests/core/saved/test_11.H.out | 16 + dist/tests/core/saved/test_11.HIPx.err | 0 dist/tests/core/saved/test_11.HIPx.out | 16 + dist/tests/core/saved/test_11.HP.err | 0 dist/tests/core/saved/test_11.HP.out | 16 + dist/tests/core/saved/test_11.J.err | 0 dist/tests/core/saved/test_11.J.out | 18 + dist/tests/core/saved/test_11.JP.err | 0 dist/tests/core/saved/test_11.JP.out | 22 + dist/tests/core/saved/test_11.T.err | 0 dist/tests/core/saved/test_11.T.out | 16 + dist/tests/core/saved/test_11.X.err | 0 dist/tests/core/saved/test_11.X.out | 17 + dist/tests/core/saved/test_11.XP.err | 0 dist/tests/core/saved/test_11.XP.out | 18 + dist/tests/core/test_01.c | 177 + dist/tests/core/test_02.c | 147 + dist/tests/core/test_03.c | 61 + dist/tests/core/test_04.c | 63 + dist/tests/core/test_05.c | 143 + dist/tests/core/test_06.c | 63 + dist/tests/core/test_07.c | 97 + dist/tests/core/test_08.c | 157 + dist/tests/core/test_09.c | 114 + dist/tests/core/test_10.c | 212 + dist/tests/core/test_11.c | 109 + dist/tests/gettext/Makefile.am | 224 + dist/tests/gettext/gt_01.c | 115 + dist/tests/gettext/gt_01.pot | 105 + dist/tests/gettext/ldns.pot | 28 + dist/tests/gettext/po/pig_latin/gt_01.po | 109 + dist/tests/gettext/po/pig_latin/ldns.po | 30 + dist/tests/gettext/po/pig_latin/strerror.po | 459 ++ dist/tests/gettext/saved/gt_01.H.err | 0 dist/tests/gettext/saved/gt_01.H.out | 1 + dist/tests/gettext/saved/gt_01.HIPx.err | 0 dist/tests/gettext/saved/gt_01.HIPx.out | 139 + dist/tests/gettext/saved/gt_01.HP.err | 0 dist/tests/gettext/saved/gt_01.HP.out | 139 + dist/tests/gettext/saved/gt_01.J.err | 0 dist/tests/gettext/saved/gt_01.J.out | 2 + dist/tests/gettext/saved/gt_01.JP.err | 0 dist/tests/gettext/saved/gt_01.JP.out | 53 + dist/tests/gettext/saved/gt_01.T.err | 0 dist/tests/gettext/saved/gt_01.T.out | 17 + dist/tests/gettext/saved/gt_01.X.err | 0 dist/tests/gettext/saved/gt_01.X.out | 1 + dist/tests/gettext/saved/gt_01.XP.err | 0 dist/tests/gettext/saved/gt_01.XP.out | 49 + dist/tests/gettext/strerror.pot | 468 ++ dist/tests/xo/Makefile.am | 90 + dist/tests/xo/saved/xo_01.H.err | 0 dist/tests/xo/saved/xo_01.H.out | 1 + dist/tests/xo/saved/xo_01.HIPx.err | 0 dist/tests/xo/saved/xo_01.HIPx.out | 52 + dist/tests/xo/saved/xo_01.HP.err | 0 dist/tests/xo/saved/xo_01.HP.out | 52 + dist/tests/xo/saved/xo_01.J.err | 0 dist/tests/xo/saved/xo_01.J.out | 1 + dist/tests/xo/saved/xo_01.JP.err | 0 dist/tests/xo/saved/xo_01.JP.out | 22 + dist/tests/xo/saved/xo_01.T.err | 0 dist/tests/xo/saved/xo_01.T.out | 4 + dist/tests/xo/saved/xo_01.X.err | 0 dist/tests/xo/saved/xo_01.X.out | 1 + dist/tests/xo/saved/xo_01.XP.err | 0 dist/tests/xo/saved/xo_01.XP.out | 22 + dist/tests/xo/xo_01.sh | 27 + dist/warnings.mk | 57 + dist/xo/Makefile.am | 43 + dist/xo/xo.1 | 173 + dist/xo/xo.c | 440 ++ dist/xohtml/Makefile.am | 42 + dist/xohtml/external/jquery.js | 9300 +++++++++++++++++++++++++++ dist/xohtml/external/jquery.qtip.css | 599 ++ dist/xohtml/external/jquery.qtip.js | 2656 ++++++++ dist/xohtml/xohtml.1 | 99 + dist/xohtml/xohtml.css | 1040 +++ dist/xohtml/xohtml.js | 54 + dist/xohtml/xohtml.sh.in | 75 + dist/xolint/Makefile.am | 21 + dist/xolint/xolint.1 | 79 + dist/xolint/xolint.pl | 699 ++ dist/xopo/Makefile.am | 43 + dist/xopo/xopo.1 | 77 + dist/xopo/xopo.c | 292 + 345 files changed, 54139 insertions(+) create mode 100644 dist/.travis.yml create mode 100644 dist/Copyright create mode 100644 dist/INSTALL.md create mode 100644 dist/LICENSE create mode 100644 dist/Makefile.am create mode 100644 dist/README.md create mode 100644 dist/bin/Makefile.am create mode 100644 dist/bin/Zaliases create mode 100755 dist/bin/setup.sh create mode 100644 dist/build/.create create mode 100644 dist/configure.ac create mode 100644 dist/doc/Makefile.am create mode 100644 dist/doc/libxo.txt create mode 100644 dist/encoder/Makefile.am create mode 100644 dist/encoder/cbor/Makefile.am create mode 100644 dist/encoder/cbor/enc_cbor.c create mode 100644 dist/encoder/test/Makefile.am create mode 100644 dist/encoder/test/enc_test.c create mode 100755 dist/install-sh create mode 100644 dist/libxo-config.in create mode 100644 dist/libxo/Makefile.am create mode 100644 dist/libxo/add.man create mode 100644 dist/libxo/add.man.in create mode 100755 dist/libxo/gen-wide.sh create mode 100644 dist/libxo/libxo.3 create mode 100644 dist/libxo/libxo.c create mode 100644 dist/libxo/xo.h create mode 100644 dist/libxo/xo_attr.3 create mode 100644 dist/libxo/xo_buf.h create mode 100644 dist/libxo/xo_config.h create mode 100644 dist/libxo/xo_create.3 create mode 100644 dist/libxo/xo_emit.3 create mode 100644 dist/libxo/xo_emit_err.3 create mode 100644 dist/libxo/xo_encoder.c create mode 100644 dist/libxo/xo_encoder.h create mode 100644 dist/libxo/xo_err.3 create mode 100644 dist/libxo/xo_error.3 create mode 100644 dist/libxo/xo_finish.3 create mode 100644 dist/libxo/xo_flush.3 create mode 100644 dist/libxo/xo_format.5 create mode 100644 dist/libxo/xo_humanize.h create mode 100644 dist/libxo/xo_message.3 create mode 100644 dist/libxo/xo_no_setlocale.3 create mode 100644 dist/libxo/xo_open_container.3 create mode 100644 dist/libxo/xo_open_list.3 create mode 100644 dist/libxo/xo_open_marker.3 create mode 100644 dist/libxo/xo_parse_args.3 create mode 100644 dist/libxo/xo_set_allocator.3 create mode 100644 dist/libxo/xo_set_flags.3 create mode 100644 dist/libxo/xo_set_info.3 create mode 100644 dist/libxo/xo_set_options.3 create mode 100644 dist/libxo/xo_set_style.3 create mode 100644 dist/libxo/xo_set_syslog_enterprise_id.3 create mode 100644 dist/libxo/xo_set_version.3 create mode 100644 dist/libxo/xo_set_writer.3 create mode 100644 dist/libxo/xo_syslog.3 create mode 100644 dist/libxo/xo_syslog.c create mode 100644 dist/libxo/xo_wcwidth.h create mode 100644 dist/m4/libtool.m4 create mode 100644 dist/m4/ltoptions.m4 create mode 100644 dist/m4/ltsugar.m4 create mode 100644 dist/m4/ltversion.m4 create mode 100644 dist/m4/lt~obsolete.m4 create mode 100644 dist/packaging/libxo.pc.in create mode 100644 dist/packaging/libxo.rb.base.in create mode 100644 dist/packaging/libxo.spec.in create mode 100644 dist/tests/Makefile.am create mode 100644 dist/tests/core/Makefile.am create mode 100644 dist/tests/core/saved/test_01.E.err create mode 100644 dist/tests/core/saved/test_01.E.out create mode 100644 dist/tests/core/saved/test_01.H.err create mode 100644 dist/tests/core/saved/test_01.H.out create mode 100644 dist/tests/core/saved/test_01.HIPx.err create mode 100644 dist/tests/core/saved/test_01.HIPx.out create mode 100644 dist/tests/core/saved/test_01.HP.err create mode 100644 dist/tests/core/saved/test_01.HP.out create mode 100644 dist/tests/core/saved/test_01.J.err create mode 100644 dist/tests/core/saved/test_01.J.out create mode 100644 dist/tests/core/saved/test_01.JP.err create mode 100644 dist/tests/core/saved/test_01.JP.out create mode 100644 dist/tests/core/saved/test_01.T.err create mode 100644 dist/tests/core/saved/test_01.T.out create mode 100644 dist/tests/core/saved/test_01.X.err create mode 100644 dist/tests/core/saved/test_01.X.out create mode 100644 dist/tests/core/saved/test_01.XP.err create mode 100644 dist/tests/core/saved/test_01.XP.out create mode 100644 dist/tests/core/saved/test_01.err create mode 100644 dist/tests/core/saved/test_01.out create mode 100644 dist/tests/core/saved/test_02.E.err create mode 100644 dist/tests/core/saved/test_02.E.out create mode 100644 dist/tests/core/saved/test_02.H.err create mode 100644 dist/tests/core/saved/test_02.H.out create mode 100644 dist/tests/core/saved/test_02.HIPx.err create mode 100644 dist/tests/core/saved/test_02.HIPx.out create mode 100644 dist/tests/core/saved/test_02.HP.err create mode 100644 dist/tests/core/saved/test_02.HP.out create mode 100644 dist/tests/core/saved/test_02.J.err create mode 100644 dist/tests/core/saved/test_02.J.out create mode 100644 dist/tests/core/saved/test_02.JP.err create mode 100644 dist/tests/core/saved/test_02.JP.out create mode 100644 dist/tests/core/saved/test_02.T.err create mode 100644 dist/tests/core/saved/test_02.T.out create mode 100644 dist/tests/core/saved/test_02.X.err create mode 100644 dist/tests/core/saved/test_02.X.out create mode 100644 dist/tests/core/saved/test_02.XP.err create mode 100644 dist/tests/core/saved/test_02.XP.out create mode 100644 dist/tests/core/saved/test_02.err create mode 100644 dist/tests/core/saved/test_02.out create mode 100644 dist/tests/core/saved/test_03.E.err create mode 100644 dist/tests/core/saved/test_03.E.out create mode 100644 dist/tests/core/saved/test_03.H.err create mode 100644 dist/tests/core/saved/test_03.H.out create mode 100644 dist/tests/core/saved/test_03.HIPx.err create mode 100644 dist/tests/core/saved/test_03.HIPx.out create mode 100644 dist/tests/core/saved/test_03.HP.err create mode 100644 dist/tests/core/saved/test_03.HP.out create mode 100644 dist/tests/core/saved/test_03.J.err create mode 100644 dist/tests/core/saved/test_03.J.out create mode 100644 dist/tests/core/saved/test_03.JP.err create mode 100644 dist/tests/core/saved/test_03.JP.out create mode 100644 dist/tests/core/saved/test_03.T.err create mode 100644 dist/tests/core/saved/test_03.T.out create mode 100644 dist/tests/core/saved/test_03.X.err create mode 100644 dist/tests/core/saved/test_03.X.out create mode 100644 dist/tests/core/saved/test_03.XP.err create mode 100644 dist/tests/core/saved/test_03.XP.out create mode 100644 dist/tests/core/saved/test_03.err create mode 100644 dist/tests/core/saved/test_03.out create mode 100644 dist/tests/core/saved/test_04.E.err create mode 100644 dist/tests/core/saved/test_04.E.out create mode 100644 dist/tests/core/saved/test_04.H.err create mode 100644 dist/tests/core/saved/test_04.H.out create mode 100644 dist/tests/core/saved/test_04.HIPx.err create mode 100644 dist/tests/core/saved/test_04.HIPx.out create mode 100644 dist/tests/core/saved/test_04.HP.err create mode 100644 dist/tests/core/saved/test_04.HP.out create mode 100644 dist/tests/core/saved/test_04.J.err create mode 100644 dist/tests/core/saved/test_04.J.out create mode 100644 dist/tests/core/saved/test_04.JP.err create mode 100644 dist/tests/core/saved/test_04.JP.out create mode 100644 dist/tests/core/saved/test_04.T.err create mode 100644 dist/tests/core/saved/test_04.T.out create mode 100644 dist/tests/core/saved/test_04.X.err create mode 100644 dist/tests/core/saved/test_04.X.out create mode 100644 dist/tests/core/saved/test_04.XP.err create mode 100644 dist/tests/core/saved/test_04.XP.out create mode 100644 dist/tests/core/saved/test_05.E.err create mode 100644 dist/tests/core/saved/test_05.E.out create mode 100644 dist/tests/core/saved/test_05.H.err create mode 100644 dist/tests/core/saved/test_05.H.out create mode 100644 dist/tests/core/saved/test_05.HIPx.err create mode 100644 dist/tests/core/saved/test_05.HIPx.out create mode 100644 dist/tests/core/saved/test_05.HP.err create mode 100644 dist/tests/core/saved/test_05.HP.out create mode 100644 dist/tests/core/saved/test_05.J.err create mode 100644 dist/tests/core/saved/test_05.J.out create mode 100644 dist/tests/core/saved/test_05.JP.err create mode 100644 dist/tests/core/saved/test_05.JP.out create mode 100644 dist/tests/core/saved/test_05.T.err create mode 100644 dist/tests/core/saved/test_05.T.out create mode 100644 dist/tests/core/saved/test_05.X.err create mode 100644 dist/tests/core/saved/test_05.X.out create mode 100644 dist/tests/core/saved/test_05.XP.err create mode 100644 dist/tests/core/saved/test_05.XP.out create mode 100644 dist/tests/core/saved/test_06.E.err create mode 100644 dist/tests/core/saved/test_06.E.out create mode 100644 dist/tests/core/saved/test_06.H.err create mode 100644 dist/tests/core/saved/test_06.H.out create mode 100644 dist/tests/core/saved/test_06.HIPx.err create mode 100644 dist/tests/core/saved/test_06.HIPx.out create mode 100644 dist/tests/core/saved/test_06.HP.err create mode 100644 dist/tests/core/saved/test_06.HP.out create mode 100644 dist/tests/core/saved/test_06.J.err create mode 100644 dist/tests/core/saved/test_06.J.out create mode 100644 dist/tests/core/saved/test_06.JP.err create mode 100644 dist/tests/core/saved/test_06.JP.out create mode 100644 dist/tests/core/saved/test_06.T.err create mode 100644 dist/tests/core/saved/test_06.T.out create mode 100644 dist/tests/core/saved/test_06.X.err create mode 100644 dist/tests/core/saved/test_06.X.out create mode 100644 dist/tests/core/saved/test_06.XP.err create mode 100644 dist/tests/core/saved/test_06.XP.out create mode 100644 dist/tests/core/saved/test_07.E.err create mode 100644 dist/tests/core/saved/test_07.E.out create mode 100644 dist/tests/core/saved/test_07.H.err create mode 100644 dist/tests/core/saved/test_07.H.out create mode 100644 dist/tests/core/saved/test_07.HIPx.err create mode 100644 dist/tests/core/saved/test_07.HIPx.out create mode 100644 dist/tests/core/saved/test_07.HP.err create mode 100644 dist/tests/core/saved/test_07.HP.out create mode 100644 dist/tests/core/saved/test_07.J.err create mode 100644 dist/tests/core/saved/test_07.J.out create mode 100644 dist/tests/core/saved/test_07.JP.err create mode 100644 dist/tests/core/saved/test_07.JP.out create mode 100644 dist/tests/core/saved/test_07.T.err create mode 100644 dist/tests/core/saved/test_07.T.out create mode 100644 dist/tests/core/saved/test_07.X.err create mode 100644 dist/tests/core/saved/test_07.X.out create mode 100644 dist/tests/core/saved/test_07.XP.err create mode 100644 dist/tests/core/saved/test_07.XP.out create mode 100644 dist/tests/core/saved/test_08.E.err create mode 100644 dist/tests/core/saved/test_08.E.out create mode 100644 dist/tests/core/saved/test_08.H.err create mode 100644 dist/tests/core/saved/test_08.H.out create mode 100644 dist/tests/core/saved/test_08.HIPx.err create mode 100644 dist/tests/core/saved/test_08.HIPx.out create mode 100644 dist/tests/core/saved/test_08.HP.err create mode 100644 dist/tests/core/saved/test_08.HP.out create mode 100644 dist/tests/core/saved/test_08.J.err create mode 100644 dist/tests/core/saved/test_08.J.out create mode 100644 dist/tests/core/saved/test_08.JP.err create mode 100644 dist/tests/core/saved/test_08.JP.out create mode 100644 dist/tests/core/saved/test_08.T.err create mode 100644 dist/tests/core/saved/test_08.T.out create mode 100644 dist/tests/core/saved/test_08.X.err create mode 100644 dist/tests/core/saved/test_08.X.out create mode 100644 dist/tests/core/saved/test_08.XP.err create mode 100644 dist/tests/core/saved/test_08.XP.out create mode 100644 dist/tests/core/saved/test_09.E.err create mode 100644 dist/tests/core/saved/test_09.E.out create mode 100644 dist/tests/core/saved/test_09.H.err create mode 100644 dist/tests/core/saved/test_09.H.out create mode 100644 dist/tests/core/saved/test_09.HIPx.err create mode 100644 dist/tests/core/saved/test_09.HIPx.out create mode 100644 dist/tests/core/saved/test_09.HP.err create mode 100644 dist/tests/core/saved/test_09.HP.out create mode 100644 dist/tests/core/saved/test_09.J.err create mode 100644 dist/tests/core/saved/test_09.J.out create mode 100644 dist/tests/core/saved/test_09.JP.err create mode 100644 dist/tests/core/saved/test_09.JP.out create mode 100644 dist/tests/core/saved/test_09.T.err create mode 100644 dist/tests/core/saved/test_09.T.out create mode 100644 dist/tests/core/saved/test_09.X.err create mode 100644 dist/tests/core/saved/test_09.X.out create mode 100644 dist/tests/core/saved/test_09.XP.err create mode 100644 dist/tests/core/saved/test_09.XP.out create mode 100644 dist/tests/core/saved/test_10.E.err create mode 100644 dist/tests/core/saved/test_10.E.out create mode 100644 dist/tests/core/saved/test_10.H.err create mode 100644 dist/tests/core/saved/test_10.H.out create mode 100644 dist/tests/core/saved/test_10.HIPx.err create mode 100644 dist/tests/core/saved/test_10.HIPx.out create mode 100644 dist/tests/core/saved/test_10.HP.err create mode 100644 dist/tests/core/saved/test_10.HP.out create mode 100644 dist/tests/core/saved/test_10.J.err create mode 100644 dist/tests/core/saved/test_10.J.out create mode 100644 dist/tests/core/saved/test_10.JP.err create mode 100644 dist/tests/core/saved/test_10.JP.out create mode 100644 dist/tests/core/saved/test_10.T.err create mode 100644 dist/tests/core/saved/test_10.T.out create mode 100644 dist/tests/core/saved/test_10.X.err create mode 100644 dist/tests/core/saved/test_10.X.out create mode 100644 dist/tests/core/saved/test_10.XP.err create mode 100644 dist/tests/core/saved/test_10.XP.out create mode 100644 dist/tests/core/saved/test_10.err create mode 100644 dist/tests/core/saved/test_10.out create mode 100644 dist/tests/core/saved/test_11.E.err create mode 100644 dist/tests/core/saved/test_11.E.out create mode 100644 dist/tests/core/saved/test_11.H.err create mode 100644 dist/tests/core/saved/test_11.H.out create mode 100644 dist/tests/core/saved/test_11.HIPx.err create mode 100644 dist/tests/core/saved/test_11.HIPx.out create mode 100644 dist/tests/core/saved/test_11.HP.err create mode 100644 dist/tests/core/saved/test_11.HP.out create mode 100644 dist/tests/core/saved/test_11.J.err create mode 100644 dist/tests/core/saved/test_11.J.out create mode 100644 dist/tests/core/saved/test_11.JP.err create mode 100644 dist/tests/core/saved/test_11.JP.out create mode 100644 dist/tests/core/saved/test_11.T.err create mode 100644 dist/tests/core/saved/test_11.T.out create mode 100644 dist/tests/core/saved/test_11.X.err create mode 100644 dist/tests/core/saved/test_11.X.out create mode 100644 dist/tests/core/saved/test_11.XP.err create mode 100644 dist/tests/core/saved/test_11.XP.out create mode 100644 dist/tests/core/test_01.c create mode 100644 dist/tests/core/test_02.c create mode 100644 dist/tests/core/test_03.c create mode 100644 dist/tests/core/test_04.c create mode 100644 dist/tests/core/test_05.c create mode 100644 dist/tests/core/test_06.c create mode 100644 dist/tests/core/test_07.c create mode 100644 dist/tests/core/test_08.c create mode 100644 dist/tests/core/test_09.c create mode 100644 dist/tests/core/test_10.c create mode 100644 dist/tests/core/test_11.c create mode 100644 dist/tests/gettext/Makefile.am create mode 100644 dist/tests/gettext/gt_01.c create mode 100644 dist/tests/gettext/gt_01.pot create mode 100644 dist/tests/gettext/ldns.pot create mode 100644 dist/tests/gettext/po/pig_latin/gt_01.po create mode 100644 dist/tests/gettext/po/pig_latin/ldns.po create mode 100644 dist/tests/gettext/po/pig_latin/strerror.po create mode 100644 dist/tests/gettext/saved/gt_01.H.err create mode 100644 dist/tests/gettext/saved/gt_01.H.out create mode 100644 dist/tests/gettext/saved/gt_01.HIPx.err create mode 100644 dist/tests/gettext/saved/gt_01.HIPx.out create mode 100644 dist/tests/gettext/saved/gt_01.HP.err create mode 100644 dist/tests/gettext/saved/gt_01.HP.out create mode 100644 dist/tests/gettext/saved/gt_01.J.err create mode 100644 dist/tests/gettext/saved/gt_01.J.out create mode 100644 dist/tests/gettext/saved/gt_01.JP.err create mode 100644 dist/tests/gettext/saved/gt_01.JP.out create mode 100644 dist/tests/gettext/saved/gt_01.T.err create mode 100644 dist/tests/gettext/saved/gt_01.T.out create mode 100644 dist/tests/gettext/saved/gt_01.X.err create mode 100644 dist/tests/gettext/saved/gt_01.X.out create mode 100644 dist/tests/gettext/saved/gt_01.XP.err create mode 100644 dist/tests/gettext/saved/gt_01.XP.out create mode 100644 dist/tests/gettext/strerror.pot create mode 100644 dist/tests/xo/Makefile.am create mode 100644 dist/tests/xo/saved/xo_01.H.err create mode 100644 dist/tests/xo/saved/xo_01.H.out create mode 100644 dist/tests/xo/saved/xo_01.HIPx.err create mode 100644 dist/tests/xo/saved/xo_01.HIPx.out create mode 100644 dist/tests/xo/saved/xo_01.HP.err create mode 100644 dist/tests/xo/saved/xo_01.HP.out create mode 100644 dist/tests/xo/saved/xo_01.J.err create mode 100644 dist/tests/xo/saved/xo_01.J.out create mode 100644 dist/tests/xo/saved/xo_01.JP.err create mode 100644 dist/tests/xo/saved/xo_01.JP.out create mode 100644 dist/tests/xo/saved/xo_01.T.err create mode 100644 dist/tests/xo/saved/xo_01.T.out create mode 100644 dist/tests/xo/saved/xo_01.X.err create mode 100644 dist/tests/xo/saved/xo_01.X.out create mode 100644 dist/tests/xo/saved/xo_01.XP.err create mode 100644 dist/tests/xo/saved/xo_01.XP.out create mode 100755 dist/tests/xo/xo_01.sh create mode 100644 dist/warnings.mk create mode 100644 dist/xo/Makefile.am create mode 100644 dist/xo/xo.1 create mode 100644 dist/xo/xo.c create mode 100644 dist/xohtml/Makefile.am create mode 100644 dist/xohtml/external/jquery.js create mode 100644 dist/xohtml/external/jquery.qtip.css create mode 100644 dist/xohtml/external/jquery.qtip.js create mode 100644 dist/xohtml/xohtml.1 create mode 100644 dist/xohtml/xohtml.css create mode 100644 dist/xohtml/xohtml.js create mode 100644 dist/xohtml/xohtml.sh.in create mode 100644 dist/xolint/Makefile.am create mode 100644 dist/xolint/xolint.1 create mode 100755 dist/xolint/xolint.pl create mode 100644 dist/xopo/Makefile.am create mode 100644 dist/xopo/xopo.1 create mode 100644 dist/xopo/xopo.c diff --git a/dist/.travis.yml b/dist/.travis.yml new file mode 100644 index 0000000..1173578 --- /dev/null +++ b/dist/.travis.yml @@ -0,0 +1,12 @@ +language: c + +script: printenv && uname -a && ls -l && /bin/sh -x ./bin/setup.sh && cd build && ../configure --enable-warnings && make && sudo make install && make test + +notifications: + recipients: + - libslax-noise@googlegroups.com + +branches: + only: + - master + - develop diff --git a/dist/Copyright b/dist/Copyright new file mode 100644 index 0000000..94ba75e --- /dev/null +++ b/dist/Copyright @@ -0,0 +1,23 @@ +Copyright (c) 2014 Juniper Networks, Inc. +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. diff --git a/dist/INSTALL.md b/dist/INSTALL.md new file mode 100644 index 0000000..70b80bc --- /dev/null +++ b/dist/INSTALL.md @@ -0,0 +1,15 @@ + + +## Instructions for building libxo + +Instructions for building libxo are now available in the +[wiki](http://juniper.github.io/libxo/libxo-manual.html#getting-libxo). diff --git a/dist/LICENSE b/dist/LICENSE new file mode 100644 index 0000000..874da7b --- /dev/null +++ b/dist/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2014, Juniper Networks +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. diff --git a/dist/Makefile.am b/dist/Makefile.am new file mode 100644 index 0000000..e050bc4 --- /dev/null +++ b/dist/Makefile.am @@ -0,0 +1,102 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = libxo xo xopo xolint xohtml tests doc encoder +bin_SCRIPTS=libxo-config +dist_doc_DATA = Copyright + +EXTRA_DIST = \ + libxo-config.in \ + warnings.mk \ + README.md \ + INSTALL.md \ + packaging/libxo.spec + +.PHONY: test tests + +test tests: + @(cd tests ; ${MAKE} test) + +errors: + @(cd tests/errors ; ${MAKE} test) + +docs: + @(cd doc ; ${MAKE} docs) + + +DIST_FILES_DIR = ~/Dropbox/dist-files/ +GH_PAGES_DIR = gh-pages/ +GH_PAGES_DIR_VER = gh-pages/${PACKAGE_VERSION} +PACKAGE_FILE = ${PACKAGE_TARNAME}-${PACKAGE_VERSION}.tar.gz + +upload: dist upload-docs + @echo "Remember to run:" + @echo " gt tag ${PACKAGE_VERSION}" + +upload-docs: docs + @echo "Uploading libxo-manual.html ... " + @-[ -d ${GH_PAGES_DIR} ] \ + && echo "Updating manual on gh-pages ..." \ + && mkdir -p ${GH_PAGES_DIR_VER} \ + && cp doc/libxo-manual.html ${GH_PAGES_DIR} \ + && cp doc/libxo-manual.html ${GH_PAGES_DIR_VER} \ + && (cd ${GH_PAGES_DIR} \ + && git add ${PACKAGE_VERSION} \ + && git add libxo-manual.html \ + && git commit -m 'new docs' \ + libxo-manual.html ${PACKAGE_VERSION} \ + && git push origin gh-pages ) ; true + +pkgconfigdir=$(libdir)/pkgconfig +pkgconfig_DATA = packaging/${PACKAGE_NAME}.pc + +get-wiki: + git clone https://github.com/Juniper/${PACKAGE_NAME}.wiki.git wiki + +get-gh-pages: + git clone https://github.com/Juniper/${PACKAGE_NAME}.git \ + gh-pages -b gh-pages + +UPDATE_PACKAGE_FILE = \ + -e "s;__SHA1__;$$SHA1;" \ + -e "s;__SHA256__;SHA256 (textproc/${PACKAGE_FILE}) = $$SHA256;" \ + -e "s;__SIZE__;SIZE (textproc/${PACKAGE_FILE}) = $$SIZE;" + +GH_PACKAGING_DIR = ${PACKAGE_VERSION}/packaging +GH_PAGES_PACKAGE_DIR = ${GH_PAGES_DIR}/${GH_PACKAGING_DIR} + +packages: + @-[ -d ${GH_PAGES_DIR} ] && set -x \ + && echo "Updating packages on gh-pages ..." \ + && SHA1="`openssl sha1 ${PACKAGE_FILE} | awk '{print $$2}'`" \ + && SHA256="`openssl sha256 ${PACKAGE_FILE} | awk '{print $$2}'`" \ + && SIZE="`ls -l ${PACKAGE_FILE} | awk '{print $$5}'`" \ + && echo "... ${GH_PAGES_PACKAGE_DIR}/${PACKAGE_NAME}.rb ..." \ + && sed ${UPDATE_PACKAGE_FILE} \ + packaging/${PACKAGE_NAME}.rb.base \ + > ${GH_PAGES_PACKAGE_DIR}/${PACKAGE_NAME}.rb \ + && echo "... ${GH_PAGES_PACKAGE_DIR}/${PACKAGE_NAME}.spec ..." \ + && cp packaging/${PACKAGE_NAME}.spec \ + ${GH_PAGES_PACKAGE_DIR}/${PACKAGE_NAME}.spec \ + && (cd ${GH_PAGES_DIR} \ + && git add ${GH_PACKAGING_DIR} \ + && git add ${GH_PACKAGING_DIR}/libxo.rb \ + ${GH_PACKAGING_DIR}/libxo.spec \ + && git commit -m 'new packaging data' \ + ${GH_PACKAGING_DIR} \ + && git push origin gh-pages ) ; true + +ANALYZE_DIR = ~/trash/libxo +ANALYZE_CMD = scan-build-mp-3.6 + +analyze: + ${ANALYZE_CMD} -o ${ANALYZE_DIR} ${MAKE} diff --git a/dist/README.md b/dist/README.md new file mode 100644 index 0000000..e9b3b4b --- /dev/null +++ b/dist/README.md @@ -0,0 +1,64 @@ +libxo +===== + +libxo - A Library for Generating Text, XML, JSON, and HTML Output + +The libxo library allows an application to generate text, XML, JSON, +and HTML output using a common set of function calls. The application +decides at run time which output style should be produced. The +application calls a function "xo_emit" to product output that is +described in a format string. A "field descriptor" tells libxo what +the field is and what it means. + +``` + xo_emit(" {:lines/%7ju/%ju} {:words/%7ju/%ju} " + "{:characters/%7ju/%ju}{d:filename/%s}\n", + linect, wordct, charct, file); +``` + +Output can then be generated in various style, using the "--libxo" +option: + +``` + % wc /etc/motd + 25 165 1140 /etc/motd + % wc --libxo xml,pretty,warn /etc/motd + + + /etc/motd + 25 + 165 + 1140 + + + % wc --libxo json,pretty,warn /etc/motd + { + "wc": { + "file": [ + { + "filename": "/etc/motd", + "lines": 25, + "words": 165, + "characters": 1140 + } + ] + } + } + % wc --libxo html,pretty,warn /etc/motd +
+
+
25
+
+
165
+
+
1140
+
+
/etc/motd
+
+``` + +View the beautiful documentation at: + +http://juniper.github.io/libxo/libxo-manual.html + +[![Analytics](https://ga-beacon.appspot.com/UA-56056421-1/Juniper/libxo/Readme)](https://github.com/Juniper/libxo) diff --git a/dist/bin/Makefile.am b/dist/bin/Makefile.am new file mode 100644 index 0000000..3bda1be --- /dev/null +++ b/dist/bin/Makefile.am @@ -0,0 +1,29 @@ +# +# Copyright 2013, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +ACLOCAL_AMFLAGS = -I m4 + +EXTRA_DIST = gt setup.sh + +GT_INSTALL_DIR = ${prefix}/bin +GT_INSTALL_FILES = gt + +install-data-hook: + @echo "Installing gt ... " + @-mkdir -p ${GT_INSTALL_DIR} + @for file in ${GT_INSTALL_FILES} ; do \ + if [ -f $$file ]; then \ + rfile=$$file ; \ + else \ + rfile=${srcdir}/$$file ; \ + fi ; \ + mdir=${GT_INSTALL_DIR}/ ; \ + mkdir -p $$mdir ; \ + cp $$rfile $$mdir/ ; \ + done + @${CHMOD} a+x ${GT_INSTALL_DIR}/gt diff --git a/dist/bin/Zaliases b/dist/bin/Zaliases new file mode 100644 index 0000000..04cdec7 --- /dev/null +++ b/dist/bin/Zaliases @@ -0,0 +1,29 @@ +set top_src=`pwd` +alias Zautoreconf "(cd $top_src ; autoreconf)" + +set opts=' \ +--with-libslax-prefix=/Users/phil/work/root \ +--enable-debug \ +--enable-warnings \ +--enable-printflike \ +--with-gettext=/opt/local \ +--prefix ${HOME}/work/root \ +' +set opts=`echo $opts` + +setenv CONFIGURE_OPTS "$opts" +setenv ADB_PATH $top_src/build/libxo/.libs + +alias Zconfigure "(cd $top_src/build; ../configure $opts)" +alias Zbuild "(cd $top_src/build; make \!* )" +alias mi "(cd $top_src/build; make && make install); ." + +mkdir -p build +cd build + + +alias xx 'cc -I.. -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Werror -Waggregate-return -Wcast-align -Wcast-qual -Wchar-subscripts -Wcomment -Wformat -Wimplicit -Wmissing-declarations -Wnested-externs -Wparentheses -Wreturn-type -Wshadow -Wswitch -Wtrigraphs -Wuninitialized -Wunused -Wwrite-strings -fno-inline-functions-called-once -g -O2 -o xtest -DUNIT_TEST libxo.c' + +alias mm "make CFLAGS='-O0 -g'" + +alias mmi 'mm && mi' diff --git a/dist/bin/setup.sh b/dist/bin/setup.sh new file mode 100755 index 0000000..f49dd48 --- /dev/null +++ b/dist/bin/setup.sh @@ -0,0 +1,33 @@ +# +# Copyright 2013, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + + +if [ ! -f configure ]; then + vers=`autoreconf --version | head -1` + echo "Using" $vers + + mkdir -p m4 + + autoreconf --install + + if [ ! -f configure ]; then + echo "Failed to create configure script" + exit 1 + fi +fi + +echo "Creating build directory ..." +mkdir build + +echo "Setup is complete. To build libslax:" + +echo " 1) Type 'cd build ; ../configure' to configure libslax" +echo " 2) Type 'make' to build libslax" +echo " 3) Type 'make install' to install libslax" + +exit 0 diff --git a/dist/build/.create b/dist/build/.create new file mode 100644 index 0000000..e69de29 diff --git a/dist/configure.ac b/dist/configure.ac new file mode 100644 index 0000000..1783120 --- /dev/null +++ b/dist/configure.ac @@ -0,0 +1,452 @@ +# +# $Id$ +# +# See ./INSTALL for more info +# + +# +# Release numbering: even numbered dot releases are official ones, and +# odd numbers are development ones. The svn version of this file will +# only (ONLY!) ever (EVER!) contain odd numbers, so I'll always know if +# a particular user has the dist or svn release. +# + +AC_PREREQ(2.2) +AC_INIT([libxo], [0.4.5], [phil@juniper.net]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign -Wno-portability]) + +# Support silent build rules. Requires at least automake-1.11. +# Disable with "configure --disable-silent-rules" or "make V=1" +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_PROG_CC +AM_PROG_AR +AC_PROG_INSTALL +AC_CONFIG_MACRO_DIR([m4]) +AC_PROG_LN_S + +# Must be after AC_PROG_AR +LT_INIT([dlopen shared]) + +AC_PATH_PROG(BASENAME, basename, /usr/bin/basename) +AC_PATH_PROG(BISON, bison, /usr/bin/bison) +AC_PATH_PROG(CAT, cat, /bin/cat) +AC_PATH_PROG(CHMOD, chmod, /bin/chmod) +AC_PATH_PROG(CP, cp, /bin/cp) +AC_PATH_PROG(DIFF, diff, /usr/bin/diff) +AC_PATH_PROG(MKDIR, mkdir, /bin/mkdir) +AC_PATH_PROG(MV, mv, /bin/mv) +AC_PATH_PROG(RM, rm, /bin/rm) +AC_PATH_PROG(SED, sed, /bin/sed) + +AC_STDC_HEADERS + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_INLINE +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_ALLOCA +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_CHECK_FUNCS([bzero memmove strchr strcspn strerror strspn]) +AC_CHECK_FUNCS([sranddev srand strlcpy]) +AC_CHECK_FUNCS([fdopen getrusage]) +AC_CHECK_FUNCS([gettimeofday ctime]) +AC_CHECK_FUNCS([getpass]) +AC_CHECK_FUNCS([getprogname]) +AC_CHECK_FUNCS([sysctlbyname]) +AC_CHECK_FUNCS([flock]) +AC_CHECK_FUNCS([asprintf]) +AC_CHECK_FUNCS([__flbf]) +AC_CHECK_FUNCS([sysctlbyname]) + + +AC_CHECK_HEADERS([dlfcn.h]) +AC_CHECK_HEADERS([dlfcn.h]) +AC_CHECK_HEADERS([stdio_ext.h]) +AC_CHECK_HEADERS([tzfile.h]) +AC_CHECK_HEADERS([stdtime/tzfile.h]) +AC_CHECK_FUNCS([dlfunc]) + +AC_CHECK_HEADERS([sys/time.h]) +AC_CHECK_HEADERS([ctype.h errno.h stdio.h stdlib.h]) +AC_CHECK_HEADERS([string.h sys/param.h unistd.h ]) +AC_CHECK_HEADERS([sys/sysctl.h]) +AC_CHECK_HEADERS([threads.h]) + +dnl humanize_number(3) is a great function, but it's not standard. +dnl Note Macosx has the function in libutil.a but doesn't ship the +dnl header file, so I'll need to carry my own implementation. See: +dnl https://devforums.apple.com/thread/271121 +AC_CHECK_HEADERS([libutil.h]) +AC_CHECK_LIB([util], [humanize_number], + [HAVE_HUMANIZE_NUMBER=$ac_cv_header_libutil_h], + [HAVE_HUMANIZE_NUMBER=no]) + +AC_MSG_RESULT(humanize_number results: :${HAVE_HUMANIZE_NUMBER}:${ac_cv_header_libutil_h}:) + +if test "$HAVE_HUMANIZE_NUMBER" = "yes"; then + AC_DEFINE([HAVE_HUMANIZE_NUMBER], [1], [humanize_number(3)]) +fi + +AM_CONDITIONAL([HAVE_HUMANIZE_NUMBER], [test "$HAVE_HUMANIZE_NUMBER" = "yes"]) + +AC_ARG_ENABLE([gettext], + [ --disable-gettext Turn off support for gettext], + [GETTEXT_ENABLE=$enableval], + [GETTEXT_ENABLE=yes]) + +dnl Looking for gettext(), assumably in libintl +AC_ARG_WITH(gettext, + [ --with-gettext=[PFX] Specify location of gettext installation], + [GETTEXT_PREFIX=$withval], + [GETTEXT_PREFIX=/usr], +) + +HAVE_GETTEXT=no + +if test "$GETTEXT_ENABLE" != "no"; then + + AC_MSG_CHECKING([gettext in ${GETTEXT_PREFIX}]) + + _save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -I${GETTEXT_PREFIX}/include -L${GETTEXT_PREFIX}/lib -Werror -lintl" + AC_LINK_IFELSE([AC_LANG_SOURCE([[#include ] + [int main() {char *cp = dgettext(NULL, "xx"); return 0; }]])], + [HAVE_GETTEXT=yes], + [HAVE_GETTEXT=no]) + CFLAGS="$_save_cflags" + + AC_MSG_RESULT([$HAVE_GETTEXT]) + + if test "$HAVE_GETTEXT" != "yes"; then + GETTEXT_PREFIX=/opt/local + AC_MSG_CHECKING([gettext in ${GETTEXT_PREFIX}]) + + _save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -I${GETTEXT_PREFIX}/include -L${GETTEXT_PREFIX}/lib -Werror -lintl" + AC_LINK_IFELSE([AC_LANG_SOURCE([[#include ] + [int main() {char *cp = dgettext(NULL, "xx"); return 0; }]])], + [HAVE_GETTEXT=yes], + [HAVE_GETTEXT=no]) + CFLAGS="$_save_cflags" + + AC_MSG_RESULT([$HAVE_GETTEXT]) + fi +fi + +if test "$HAVE_GETTEXT" = "yes"; then + AC_DEFINE([HAVE_GETTEXT], [1], [gettext(3)]) + GETTEXT_CFLAGS="-I${GETTEXT_PREFIX}/include" + GETTEXT_LIBS="-L${GETTEXT_PREFIX}/lib -lintl" +else + GETTEXT_PREFIX=none + GETTEXT_CFLAGS= + GETTEXT_LIBS= +fi +AC_SUBST(GETTEXT_CFLAGS) +AC_SUBST(GETTEXT_LIBS) + +GETTEXT_BINDIR=${GETTEXT_PREFIX}/bin +AC_SUBST(GETTEXT_BINDIR) +GETTEXT_LIBDIR=${GETTEXT_PREFIX}/lib +AC_SUBST(GETTEXT_LIBDIR) + +AM_CONDITIONAL([HAVE_GETTEXT], [test "$HAVE_GETTEXT" = "yes"]) + +dnl Looking for how to do thread-local variables +AC_ARG_WITH(threads, + [ --with-threads=[STYLE] Specify style of thread-local support (none)], + [THREAD_LOCAL=$withval], + [THREAD_LOCAL=unknown], +) + +AC_MSG_CHECKING([thread-locals are ${THREAD_LOCAL}]) + +if test "$THREAD_LOCAL" = "unknown"; then + AC_LINK_IFELSE([AC_LANG_SOURCE([[] + [__thread int foo; int main() { foo++; return foo; }]])], + [THREAD_LOCAL=before], + [THREAD_LOCAL=unknown]) + + AC_MSG_RESULT([$THREAD_LOCAL]) +fi + +if test "$THREAD_LOCAL" = "unknown"; then + AC_LINK_IFELSE([AC_LANG_SOURCE([[] + [int __thread foo; int main() { foo++; return foo; }]])], + [THREAD_LOCAL=after], + [THREAD_LOCAL=unknown]) + AC_MSG_RESULT([$THREAD_LOCAL]) +fi + +if test "$THREAD_LOCAL" = "unknown"; then + AC_LINK_IFELSE([AC_LANG_SOURCE([[] + [__declspec(int) foo; int main() { foo++; return foo; }]])], + [THREAD_LOCAL=declspec], + [THREAD_LOCAL=unknown]) + AC_MSG_RESULT([$THREAD_LOCAL]) +fi + +if test "$THREAD_LOCAL" != "unknown"; then + AC_DEFINE_UNQUOTED([HAVE_THREAD_LOCAL], + THREAD_LOCAL_${THREAD_LOCAL}, [thread-local setting]) +fi + +dnl Looking for libcrypto.... +AC_CHECK_LIB([crypto], [MD5_Init]) +AM_CONDITIONAL([HAVE_LIBCRYPTO], [test "$HAVE_LIBCRYPTO" != "no"]) + +AC_CHECK_MEMBER([struct sockaddr_un.sun_len], + [HAVE_SUN_LEN=yes ; + AC_DEFINE([HAVE_SUN_LEN], [1], [Have struct sockaddr_un.sun_len])], + [HAS_SUN_LEN=no], [[#include ]]) + +AC_CHECK_DECLS([__isthreaded], [], [], [#include ]) +HAVE_ISTHREADED=${ac_cv_have_decl___isthreaded} + +dnl +dnl Some packages need to be checked against version numbers so we +dnl define a function here for later use +dnl +AC_DEFUN([VERSION_TO_NUMBER], +[`$1 | sed -e 's/lib.* //' | awk 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 1000 + [$]2) * 1000 + [$]3;}'`]) + +LIBSLAX_CONFIG_PREFIX="" +LIBSLAX_SRC="" + +AC_ARG_WITH(libslax-prefix, + [ --with-libslax-prefix=[PFX] Specify location of libslax config], + LIBSLAX_CONFIG_PREFIX=$withval +) + +AC_MSG_CHECKING(for libslax) +if test "x$LIBSLAX_CONFIG_PREFIX" != "x" +then + SLAX_CONFIG=${LIBSLAX_CONFIG_PREFIX}/bin/slax-config +else + SLAX_CONFIG=slax-config +fi + +dnl +dnl make sure slax-config is executable, +dnl test version and init our variables +dnl + +if ${SLAX_CONFIG} --libs > /dev/null 2>&1 +then + LIBSLAX_VERSION=`$SLAX_CONFIG --version` + SLAX_BINDIR="`$SLAX_CONFIG --bindir | head -1`" + SLAX_OXTRADOCDIR="`$SLAX_CONFIG --oxtradoc | head -1`" + AC_MSG_RESULT($LIBSLAX_VERSION found) + HAVE_OXTRADOC=yes +else + LIBSLAX_VERSION= + SLAX_BINDIR= + SLAX_OXTRADOCDIR= + AC_MSG_RESULT([no]) + HAVE_OXTRADOC=no +fi +AM_CONDITIONAL([HAVE_OXTRADOC], [test "$HAVE_OXTRADOC" != "no"]) + +AC_SUBST(SLAX_BINDIR) +AC_SUBST(SLAX_OXTRADOCDIR) + +AC_MSG_CHECKING([whether to build with warnings]) +AC_ARG_ENABLE([warnings], + [ --enable-warnings Turn on compiler warnings], + [LIBXO_WARNINGS=$enableval], + [LIBXO_WARNINGS=no]) +AC_MSG_RESULT([$LIBXO_WARNINGS]) +AM_CONDITIONAL([LIBXO_WARNINGS_HIGH], [test "$LIBXO_WARNINGS" != "no"]) + +AC_MSG_CHECKING([whether to build with debugging]) +AC_ARG_ENABLE([debug], + [ --enable-debug Turn on debugging], + [LIBXO_DEBUG=yes; AC_DEFINE([LIBXO_DEBUG], [1], [Enable debugging])], + [LIBXO_DEBUG=no]) +AC_MSG_RESULT([$LIBXO_DEBUG]) +AM_CONDITIONAL([LIBXO_DEBUG], [test "$LIBXO_DEBUG" != "no"]) + +AC_MSG_CHECKING([whether to build with text-only rendering]) +AC_ARG_ENABLE([text-only], + [ --enable-text-only Turn on text-only rendering], + [LIBXO_TEXT_ONLY=yes; AC_DEFINE([LIBXO_TEXT_ONLY], [1], [Enable text-only rendering])], + [LIBXO_TEXT_ONLY=no]) +AC_MSG_RESULT([$LIBXO_TEXT_ONLY]) +AM_CONDITIONAL([LIBXO_TEXT_ONLY], [test "$LIBXO_TEXT_ONLY" != "no"]) + +AC_MSG_CHECKING([whether to build with local wcwidth implementation]) +AC_ARG_ENABLE([wcwidth], + [ --disable-wcwidth Disable local wcwidth implementation], + [LIBXO_WCWIDTH=$enableval], + [LIBXO_WCWIDTH=yes]) +AC_MSG_RESULT([$LIBXO_WCWIDTH]) +if test "${LIBXO_WCWIDTH}" != "no"; then + AC_DEFINE([LIBXO_WCWIDTH], [1], [Enable local wcwidth implementation]) +fi + +AC_CHECK_LIB([m], [lrint]) +AM_CONDITIONAL([HAVE_LIBM], [test "$HAVE_LIBM" != "no"]) + +AC_MSG_CHECKING([compiler for gcc]) +HAVE_GCC=no +if test "${CC}" != ""; then + HAVE_GCC=`${CC} --version 2>&1 | grep GCC` + if test "${HAVE_GCC}" != ""; then + HAVE_GCC=yes + else + HAVE_GCC=no + fi +fi +AC_MSG_RESULT([$HAVE_GCC]) +AM_CONDITIONAL([HAVE_GCC], [test "$HAVE_GCC" = "yes"]) + +AC_MSG_CHECKING([whether to build with printflike]) +AC_ARG_ENABLE([printflike], + [ --enable-printflike Enable use of GCC __printflike attribute], + [HAVE_PRINTFLIKE=yes; + AC_DEFINE([HAVE_PRINTFLIKE], [1], [Support printflike])], + [HAVE_PRINTFLIKE=no]) +AC_MSG_RESULT([$HAVE_PRINTFLIKE]) +AM_CONDITIONAL([HAVE_PRINTFLIKE], [test "$HAVE_PRINTFLIKE" != ""]) + +AC_MSG_CHECKING([whether to build with LIBXO_OPTIONS]) +AC_ARG_ENABLE([libxo-options], + [ --disable-libxo-options Turn off support for LIBXO_OPTIONS], + [LIBXO_OPTS=$enableval], + [LIBXO_OPTS=yes]) +AC_MSG_RESULT([$LIBXO_OPTS]) +AM_CONDITIONAL([NO_LIBXO_OPTIONS], [test "$LIBXO_OPTS" != "yes"]) + +case $host_os in + darwin*) + LIBTOOL=glibtool + XO_LIBEXT=dylib + ;; + Linux*|linux*) + CFLAGS="-D_GNU_SOURCE $CFLAGS" + LDFLAGS=-ldl + XO_LIBEXT=so + ;; + cygwin*|CYGWIN*) + LDFLAGS=-no-undefined + XO_LIBEXT=ddl + ;; +esac + +case $prefix in + NONE) + prefix=/usr/local + ;; +esac + +XO_LIBS=-lxo +XO_SRCDIR=${srcdir} +XO_LIBDIR=${libdir} +XO_BINDIR=${bindir} +XO_INCLUDEDIR=${includedir} + +AC_SUBST(XO_SRCDIR) +AC_SUBST(XO_LIBDIR) +AC_SUBST(XO_BINDIR) +AC_SUBST(XO_INCLUDEDIR) +AC_SUBST(XO_LIBEXT) + +AC_ARG_WITH(encoder-dir, + [ --with-encoder-dir=[DIR] Specify location of encoder libraries], + [XO_ENCODERDIR=$withval], + [XO_ENCODERDIR=$libdir/libxo/encoder] +) +AC_SUBST(XO_ENCODERDIR) + +AC_ARG_WITH(share-dir, + [ --with-share-dir=[DIR] Specify location of shared files], + [XO_SHAREDIR=$withval], + [XO_SHAREDIR=$datarootdir/libxo] +) +XO_SHAREDIR=`echo $XO_SHAREDIR | sed "s;\\${prefix};$prefix;"` +AC_SUBST(XO_SHAREDIR) + +dnl for the spec file +RELDATE=`date +'%Y-%m-%d%n'` +AC_SUBST(RELDATE) + +AC_MSG_RESULT(Using configure dir $ac_abs_confdir) + +if test -d $ac_abs_confdir/.git ; then + extra=`git branch | awk '/\*/ { print $2 }'` + if test "$extra" != "" -a "$extra" != "master" + then + LIBXO_VERSION_EXTRA="-git-$extra" + fi +fi + +LIBXO_VERSION=$PACKAGE_VERSION +LIBXO_VERSION_NUMBER=VERSION_TO_NUMBER(echo $PACKAGE_VERSION) +AC_SUBST(LIBXO_VERSION) +AC_SUBST(LIBXO_VERSION_NUMBER) +AC_SUBST(LIBXO_VERSION_EXTRA) + +AC_DEFINE_UNQUOTED(LIBXO_VERSION, ["$LIBXO_VERSION"], + [Version number as dotted value]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION_NUMBER, [$LIBXO_VERSION_NUMBER], + [Version number as a number]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION_STRING, ["$LIBXO_VERSION_NUMBER"], + [Version number as string]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION_EXTRA, ["$LIBXO_VERSION_EXTRA"], + [Version number extra information]) + +AC_CONFIG_HEADERS([libxo/xo_config.h]) +AC_CONFIG_FILES([ + Makefile + libxo-config + xohtml/xohtml.sh + libxo/Makefile + libxo/add.man + encoder/Makefile + encoder/cbor/Makefile + encoder/test/Makefile + xo/Makefile + xolint/Makefile + xohtml/Makefile + xopo/Makefile + packaging/libxo.pc + doc/Makefile + tests/Makefile + tests/core/Makefile + tests/gettext/Makefile + tests/xo/Makefile + packaging/libxo.spec + packaging/libxo.rb.base +]) +AC_OUTPUT + +AC_MSG_NOTICE([summary of build options: + + libxo version: ${VERSION} ${LIBXO_VERSION_EXTRA} + host type: ${host} / ${host_os} + install prefix: ${prefix} + srcdir: ${XO_SRCDIR} + libdir: ${XO_LIBDIR} + bindir: ${XO_BINDIR} + includedir: ${XO_INCLUDEDIR} + share dir: ${XO_SHAREDIR} + extensions dir: ${XO_ENCODERDIR} + oxtradoc dir: ${SLAX_OXTRADOCDIR} + + compiler: ${CC} (${HAVE_GCC:-no}) + compiler flags: ${CFLAGS} + library types: Shared=${enable_shared}, Static=${enable_static} + + warnings: ${LIBXO_WARNINGS:-no} + debug: ${LIBXO_DEBUG:-no} + printf-like: ${HAVE_PRINTFLIKE:-no} + libxo-options: ${LIBXO_OPTS:-no} + text-only: ${LIBXO_TEXT_ONLY:-no} + gettext: ${HAVE_GETTEXT:-no} (${GETTEXT_PREFIX}) + isthreaded: ${HAVE_ISTHREADED:-no} + thread-local: ${THREAD_LOCAL:-no} + local wcwidth: ${LIBXO_WCWIDTH:-no} +]) diff --git a/dist/doc/Makefile.am b/dist/doc/Makefile.am new file mode 100644 index 0000000..16d6ba5 --- /dev/null +++ b/dist/doc/Makefile.am @@ -0,0 +1,70 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +if HAVE_OXTRADOC +OXTRADOC_DIR = ${SLAX_OXTRADOCDIR} +OXTRADOC_PREFIX = ${OXTRADOC_DIR} +OXTRADOC = ${OXTRADOC_DIR}/oxtradoc +SLAXPROC_BINDIR = ${SLAX_BINDIR} + +XML2RFC = ${OXTRADOC_DIR}/xml2rfc.tcl +XML2HTMLDIR = ${OXTRADOC_DIR} +XML2HTMLBIN = ${XML2HTMLDIR}/rfc2629-to-html.slax +SLAXPROC = ${SLAX_BINDIR}/slaxproc + +SLAXPROC_ARGS = \ + -a oxtradoc-dir ${OXTRADOC_DIR} \ + -a oxtradoc-install-dir ${OXTRADOC_DIR} \ + -a anchor-prefix docs + +SLAXPROC_ARGS_INLINE = \ + -a oxtradoc-inline yes + +SLAXPROC_ARGS += ${SLAXPROC_ARGS_INLINE} + +XML2HTML = \ + ${SLAXPROC} -g -e -I ${OXTRADOC_DIR} -I . \ + ${SLAXPROC_ARGS} \ + ${XML2HTMLBIN} + +OX_ARGS = -P ${OXTRADOC_PREFIX} -L ${OXTRADOC_PREFIX} +OX_ARGS += -S ${SLAXPROC} -p doc +OX_CMD = ${PERL} ${PERLOPTS} ${OXTRADOC} ${OX_ARGS} +OXTRADOC_CMD = ${OX_CMD} + +OUTPUT = libxo-manual +INPUT = libxo + +EXTRA_DIST = \ + ${INPUT}.txt \ + ${OUTPUT}.html \ + ${OUTPUT}.txt + +doc docs: ${OUTPUT}.txt ${OUTPUT}.html + +${OUTPUT}.txt: ${INPUT}.txt ${OXTRADOC} xolint.txt + ${OXTRADOC_CMD} -m text -o $@ $< + +${OUTPUT}.html: ${INPUT}.txt ${OXTRADOC} ${XML2HTMLBIN} xolint.txt + ${OXTRADOC_CMD} -m html -o $@ $< + +xolint.txt: ${top_srcdir}/xolint/xolint.pl + perl ${top_srcdir}/xolint/xolint.pl -D > xolint.txt + +CLEANFILES = \ +xolint.txt \ +${INPUT}.xml \ +${INPUT}.txt \ +${INPUT}.fxml \ +${INPUT}.html +else +doc docs: + @${ECHO} "The 'oxtradoc' tool is not installed; see libslax.org" +endif diff --git a/dist/doc/libxo.txt b/dist/doc/libxo.txt new file mode 100644 index 0000000..1e7acc7 --- /dev/null +++ b/dist/doc/libxo.txt @@ -0,0 +1,3757 @@ +# +# Copyright (c) 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. +# Phil Shafer, July 2014 +# + +* Overview + +libxo - A Library for Generating Text, XML, JSON, and HTML Output + +You want to prepare for the future, but you need to live in the +present. You'd love a flying car, but need to get to work today. You +want to support features like XML, JSON, and HTML rendering to allow +integration with NETCONF, REST, and web browsers, but you need to make +text output for command line users. And you don't want multiple code +paths that can't help but get out of sync. None of this "if (xml) +{... } else {...}" logic. And ifdefs are right out. But you'd +really, really like all the fancy features that modern encoding +formats can provide. libxo can help. + +The libxo library allows an application to generate text, XML, JSON, +and HTML output using a common set of function calls. The application +decides at run time which output style should be produced. The +application calls a function "xo_emit" to product output that is +described in a format string. A "field descriptor" tells libxo what +the field is and what it means. Each field descriptor is placed in +braces with a printf-like format string (^format-strings^): + + xo_emit(" {:lines/%7ju} {:words/%7ju} " + "{:characters/%7ju} {d:filename/%s}\n", + linect, wordct, charct, file); + +Each field can have a role, with the 'value' role being the default, +and the role tells libxo how and when to render that field. Output +can then be generated in various style, using the "--libxo" option: + + % wc /etc/motd + 25 165 1140 /etc/motd + % wc --libxo xml,pretty,warn /etc/motd + + + 25 + 165 + 1140 + /etc/motd + + + % wc --libxo json,pretty,warn /etc/motd + { + "wc": { + "file": [ + { + "lines": 25, + "words": 165, + "characters": 1140, + "filename": "/etc/motd" + } + ] + } + } + % wc --libxo html,pretty,warn /etc/motd +
+
+
25
+
+
165
+
+
1140
+
+
/etc/motd
+
+ +** Getting libxo + +libxo lives on github as: + + https://github.com/Juniper/libxo + +The latest release of libxo is available at: + + https://github.com/Juniper/libxo/releases + +We are following the branching scheme from +^http://nvie.com/posts/a-successful-git-branching-model/^ which means +we will do development under the "develop" branch, and release from +the "master" branch. To clone a developer tree, run the following +command: + + git clone https://github.com/Juniper/libxo.git -b develop + +We're using semantic release numbering, as defined in +^http://semver.org/spec/v2.0.0.html^. + +libxo is open source, distributed under the BSD license. It shipped +as part of the FreeBSD operating system starting with release 11.0. + +Issues, problems, and bugs should be directly to the issues page on +our github site. + +*** Downloading libxo Source Code + +You can retrieve the source for libxo in two ways: + +A) Use a "distfile" for a specific release. We use +github to maintain our releases. Visit +github release page (^https://github.com/Juniper/libxo/releases^) +to see the list of releases. To download the latest, look for the +release with the green "Latest release" button and the green +"libxo-RELEASE.tar.gz" button under that section. + +After downloading that release's distfile, untar it as follows: + + tar -zxf libxo-RELEASE.tar.gz + cd libxo-RELEASE + +[Note: for Solaris users, your "tar" command lacks the "-z" flag, +so you'll need to substitute "gzip -dc "file" | tar xf -" instead of +"tar -zxf "file"".] + +B) Use the current build from github. This gives you the most recent +source code, which might be less stable than a specific release. To +build libxo from the git repo: + + git clone https://github.com/Juniper/libxo.git + cd libxo + +_BE AWARE_: The github repository does _not_ contain the files +generated by "autoreconf", with the notable exception of the "m4" +directory. Since these files (depcomp, configure, missing, +install-sh, etc) are generated files, we keep them out of the source +code repository. + +This means that if you download the a release distfile, these files +will be ready and you'll just need to run "configure", but if you +download the source code from svn, then you'll need to run +"autoreconf" by hand. This step is done for you by the "setup.sh" +script, described in the next section. + +*** Building libxo + +To build libxo, you'll need to set up the build, run the "configure" +script, run the "make" command, and run the regression tests. + +The following is a summary of the commands needed. These commands are +explained in detail in the rest of this section. + + sh bin/setup.sh + cd build + ../configure + make + make test + sudo make install + +The following sections will walk thru each of these steps with +additional details and options, but the above directions should be all +that's needed. + +**** Setting up the build + +[If you downloaded a distfile, you can skip this step.] + +Run the "setup.sh" script to set up the build. This script runs the +"autoreconf" command to generate the "configure" script and other +generated files. + + sh bin/setup.sh + +Note: We're are currently using autoreconf version 2.69. + +**** Running the "configure" Script + +Configure (and autoconf in general) provides a means of building +software in diverse environments. Our configure script supports +a set of options that can be used to adjust to your operating +environment. Use "configure --help" to view these options. + +We use the "build" directory to keep object files and generated files +away from the source tree. + +To run the configure script, change into the "build" directory, and +run the "configure" script. Add any required options to the +"../configure" command line. + + cd build + ../configure + +Expect to see the "configure" script generate the following error: + + /usr/bin/rm: cannot remove `libtoolT': No such file or directory + +This error is harmless and can be safely ignored. + +By default, libxo installs architecture-independent files, including +extension library files, in the /usr/local directories. To specify an +installation prefix other than /usr/local for all installation files, +include the --prefix=prefix option and specify an alternate +location. To install just the extension library files in a different, +user-defined location, include the --with-extensions-dir=dir option +and specify the location where the extension libraries will live. + + cd build + ../configure [OPTION]... [VAR=VALUE]... + +**** Running the "make" command + +Once the "configure" script is run, build the images using the "make" +command: + + make + +**** Running the Regression Tests + +libxo includes a set of regression tests that can be run to ensure +the software is working properly. These test are optional, but will +help determine if there are any issues running libxo on your +machine. To run the regression tests: + + make test + +**** Installing libxo + +Once the software is built, you'll need to install libxo using the +"make install" command. If you are the root user, or the owner of the +installation directory, simply issue the command: + + make install + +If you are not the "root" user and are using the "sudo" package, use: + + sudo make install + +Verify the installation by viewing the output of "xo --version": + + % xo --version + libxo version 0.3.5-git-develop + xo version 0.3.5-git-develop + +* Formatting with libxo + +Most unix commands emit text output aimed at humans. It is designed +to be parsed and understood by a user. Humans are gifted at +extracting details and pattern matching in such output. Often +programmers need to extract information from this human-oriented +output. Programmers use tools like grep, awk, and regular expressions +to ferret out the pieces of information they need. Such solutions are +fragile and require maintenance when output contents change or evolve, +along with testing and validation. + +Modern tool developers favor encoding schemes like XML and JSON, +which allow trivial parsing and extraction of data. Such formats are +simple, well understood, hierarchical, easily parsed, and often +integrate easier with common tools and environments. Changes to +content can be done in ways that do not break existing users of the +data, which can reduce maintenance costs and increase feature velocity. + +In addition, modern reality means that more output ends up in web +browsers than in terminals, making HTML output valuable. + +libxo allows a single set of function calls in source code to generate +traditional text output, as well as XML and JSON formatted data. HTML +can also be generated; "
" elements surround the traditional text +output, with attributes that detail how to render the data. + +A single libxo function call in source code is all that's required: + + xo_emit("Connecting to {:host}.{:domain}...\n", host, domain); + + TEXT: + Connecting to my-box.example.com... + XML: + my-box + example.com + JSON: + "host": "my-box", + "domain": "example.com" + HTML: +
+
Connecting to
+
my-box
+
.
+
example.com
+
...
+
+ +** Encoding Styles + +There are four encoding styles supported by libxo: + +- TEXT output can be display on a terminal session, allowing +compatibility with traditional command line usage. +- XML output is suitable for tools like XPath and protocols like +NETCONF. +- JSON output can be used for RESTful APIs and integration with +languages like Javascript and Python. +- HTML can be matched with a small CSS file to permit rendering in any +HTML5 browser. + +In general, XML and JSON are suitable for encoding data, while TEXT is +suited for terminal output and HTML is suited for display in a web +browser (see ^xohtml^). + +*** Text Output + +Most traditional programs generate text output on standard output, +with contents like: + + 36 ./src + 40 ./bin + 90 . + +In this example (taken from du source code), the code to generate this +data might look like: + + printf("%d\t%s\n", num_blocks, path); + +Simple, direct, obvious. But it's only making text output. Imagine +using a single code path to make TEXT, XML, JSON or HTML, deciding at +run time which to generate. + +libxo expands on the idea of printf format strings to make a single +format containing instructions for creating multiple output styles: + + xo_emit("{:blocks/%d}\t{:path/%s}\n", num_blocks, path); + +This line will generate the same text output as the earlier printf +call, but also has enough information to generate XML, JSON, and HTML. + +The following sections introduce the other formats. + +*** XML Output + +XML output consists of a hierarchical set of elements, each encoded +with a start tag and an end tag. The element should be named for data +value that it is encoding: + + + 36 + ./src + + + 40 + ./bin + + + 90 + . + + +XML is a W3C standard for encoding data. See w3c.org/TR/xml for +additional information. + +*** JSON Output + +JSON output consists of a hierarchical set of objects and lists, each +encoded with a quoted name, a colon, and a value. If the value is a +string, it must be quoted, but numbers are not quoted. Objects are +encoded using braces; lists are encoded using square brackets. +Data inside objects and lists is separated using commas: + + items: [ + { "blocks": 36, "path" : "./src" }, + { "blocks": 40, "path" : "./bin" }, + { "blocks": 90, "path" : "./" } + ] + +*** HTML Output + +HTML output is designed to allow the output to be rendered in a web +browser with minimal effort. Each piece of output data is rendered +inside a
element, with a class name related to the role of the +data. By using a small set of class attribute values, a CSS +stylesheet can render the HTML into rich text that mirrors the +traditional text content. + +Additional attributes can be enabled to provide more details about the +data, including data type, description, and an XPath location. + +
+
36
+
+
./src
+
+
+
40
+
+
./bin
+
+
+
90
+
+
./
+
+ +** Format Strings @format-strings@ + +libxo uses format strings to control the rendering of data into the +various output styles. Each format string contains a set of zero or +more field descriptions, which describe independent data fields. Each +field description contains a set of modifiers, a content string, and +zero, one, or two format descriptors. The modifiers tell libxo what +the field is and how to treat it, while the format descriptors are +formatting instructions using printf-style format strings, telling +libxo how to format the field. The field description is placed inside +a set of braces, with a colon (":") after the modifiers and a slash +("/") before each format descriptors. Text may be intermixed with +field descriptions within the format string. + +The field description is given as follows: + + '{' [ role | modifier ]* [',' long-names ]* ':' [ content ] + [ '/' field-format [ '/' encoding-format ]] '}' + +The role describes the function of the field, while the modifiers +enable optional behaviors. The contents, field-format, and +encoding-format are used in varying ways, based on the role. These +are described in the following sections. + +In the following example, three field descriptors appear. The first +is a padding field containing three spaces of padding, the second is a +label ("In stock"), and the third is a value field ("in-stock"). The +in-stock field has a "%u" format that will parse the next argument +passed to the xo_emit function as an unsigned integer. + + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", 65); + +This single line of code can generate text (" In stock: 65\n"), XML +("65"), JSON ('"in-stock": 6'), or HTML (too +lengthy to be listed here). + +While roles and modifiers typically use single character for brevity, +there are alternative names for each which allow more verbose +formatting strings. These names must be preceded by a comma, and may +follow any single-character values: + + xo_emit("{L,white,colon:In stock}{,key:in-stock/%u}\n", 65); + +*** Field Roles + +Field roles are optional, and indicate the role and formatting of the +content. The roles are listed below; only one role is permitted: + +|---+--------------+-------------------------------------------------| +| R | Name | Description | +|---+--------------+-------------------------------------------------| +| C | color | Field has color and effect controls | +| D | decoration | Field is non-text (e.g., colon, comma) | +| E | error | Field is an error message | +| G | gettext | Call gettext(3) on the format string | +| L | label | Field is text that prefixes a value | +| N | note | Field is text that follows a value | +| P | padding | Field is spaces needed for vertical alignment | +| T | title | Field is a title value for headings | +| U | units | Field is the units for the previous value field | +| V | value | Field is the name of field (the default) | +| W | warning | Field is a warning message | +| [ | start-anchor | Begin a section of anchored variable-width text | +| ] | stop-anchor | End a section of anchored variable-width text | +|---+--------------+-------------------------------------------------| + + EXAMPLE: + xo_emit("{L:Free}{D::}{P: }{:free/%u} {U:Blocks}\n", + free_blocks); + +When a role is not provided, the "value" role is used as the default. + +Roles and modifiers can also use more verbose names, when preceeded by +a comma: + + EXAMPLE: + xo_emit("{,label:Free}{,decoration::}{,padding: }" + "{,value:free/%u} {,units:Blocks}\n", + free_blocks); + +**** The Color Role ({C:}) @color-role@ + +Colors and effects control how text values are displayed; they are +used for display styles (TEXT and HTML). + + xo_emit("{C:bold}{:value}{C:no-bold}\n", value); + +Colors and effects remain in effect until modified by other "C"-role +fields. + + xo_emit("{C:bold}{C:inverse}both{C:no-bold}only inverse\n"); + +If the content is empty, the "reset" action is performed. + + xo_emit("{C:both,underline}{:value}{C:}\n", value); + +The content should be a comma-separated list of zero or more colors or +display effects. + + xo_emit("{C:bold,inverse}Ugly{C:no-bold,no-inverse}\n"); + +The color content can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): + + xo_emit("{C:/%s%s}{:value}{C:}", need_bold ? "bold" : "", + need_underline ? "underline" : "", value); + +Color names are prefixed with either "fg-" or "bg-" to change the +foreground and background colors, respectively. + + xo_emit("{C:/fg-%s,bg-%s}{Lwc:Cost}{:cost/%u}{C:reset}\n", + fg_color, bg_color, cost); + +The following table lists the supported effects: + +|---------------+-------------------------------------------------| +| Name | Description | +|---------------+-------------------------------------------------| +| bg-XXXXX | Change background color | +| bold | Start bold text effect | +| fg-XXXXX | Change foreground color | +| inverse | Start inverse (aka reverse) text effect | +| no-bold | Stop bold text effect | +| no-inverse | Stop inverse (aka reverse) text effect | +| no-underline | Stop underline text effect | +| normal | Reset effects (only) | +| reset | Reset colors and effects (restore defaults) | +| underline | Start underline text effect | +|---------------+-------------------------------------------------| + +The following color names are supported: + +|---------+--------------------------------------------| +| Name | Description | +|---------+--------------------------------------------| +| black | | +| blue | | +| cyan | | +| default | Default color for foreground or background | +| green | | +| magenta | | +| red | | +| white | | +| yellow | | +|---------+--------------------------------------------| + +**** The Decoration Role ({D:}) + +Decorations are typically punctuation marks such as colons, +semi-colons, and commas used to decorate the text and make it simpler +for human readers. By marking these distinctly, HTML usage scenarios +can use CSS to direct their display parameters. + + xo_emit("{D:((}{:name}{D:))}\n", name); + +**** The Gettext Role ({G:}) @gettext-role@ + +libxo supports internationalization (i18n) through its use of +gettext(3). Use the "{G:}" role to request that the remaining part of +the format string, following the "{G:}" field, be handled using +gettext(). + +Since gettext() uses the string as the key into the message catalog, +libxo uses a simplified version of the format string that removes +unimportant field formatting and modifiers, stopping minor formatting +changes from impacting the expensive translation process. A developer +change such as changing "/%06d" to "/%08d" should not force hand +inspection of all .po files. + +The simplified version can be generated for a single message using the +"xopo -s " command, or an entire .pot can be translated using +the "xopo -f -o " command. + + xo_emit("{G:}Invalid token\n"); + +The {G:} role allows a domain name to be set. gettext calls will +continue to use that domain name until the current format string +processing is complete, enabling a library function to emit strings +using it's own catalog. The domain name can be either static as the +content of the field, or a format can be used to get the domain name +from the arguments. + + xo_emit("{G:libc}Service unavailable in restricted mode\n"); + +See ^howto-i18n^ for additional details. + +**** The Label Role ({L:}) + +Labels are text that appears before a value. + + xo_emit("{Lwc:Cost}{:cost/%u}\n", cost); + +**** The Note Role ({N:}) + +Notes are text that appears after a value. + + xo_emit("{:cost/%u} {N:per year}\n", cost); + +**** The Padding Role ({P:}) @padding-role@ + +Padding represents whitespace used before and between fields. + +The padding content can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): + + xo_emit("{P: }{Lwc:Cost}{:cost/%u}\n", cost); + xo_emit("{P:/%30s}{Lwc:Cost}{:cost/%u}\n", "", cost); + +**** The Title Role ({T:}) + +Title are heading or column headers that are meant to be displayed to +the user. The title can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): + + xo_emit("{T:Interface Statistics}\n"); + xo_emit("{T:/%20.20s}{T:/%6.6s}\n", "Item Name", "Cost"); + +Title fields have an extra convenience feature; if both content and +format are specified, instead of looking to the argument list for a +value, the content is used, allowing a mixture of format and content +within the field descriptor: + + xo_emit("{T:Name/%20s}{T:Count/%6s}\n"); + +Since the incoming argument is a string, the format must be "%s" or +something suitable. + +**** The Units Role ({U:}) + +Units are the dimension by which values are measured, such as degrees, +miles, bytes, and decibels. The units field carries this information +for the previous value field. + + xo_emit("{Lwc:Distance}{:distance/%u}{Uw:miles}\n", miles); + +Note that the sense of the 'w' modifier is reversed for units; +a blank is added before the contents, rather than after it. + +When the XOF_UNITS flag is set, units are rendered in XML as the +"units" attribute: + + 50 + +Units can also be rendered in HTML as the "data-units" attribute: + +
50
+ +**** The Value Role ({V:} and {:}) + +The value role is used to represent the a data value that is +interesting for the non-display output styles (XML and JSON). Value +is the default role; if no other role designation is given, the field +is a value. The field name must appear within the field descriptor, +followed by one or two format descriptors. The first format +descriptor is used for display styles (TEXT and HTML), while the +second one is used for encoding styles (XML and JSON). If no second +format is given, the encoding format defaults to the first format, +with any minimum width removed. If no first format is given, both +format descriptors default to "%s". + + xo_emit("{:length/%02u}x{:width/%02u}x{:height/%02u}\n", + length, width, height); + xo_emit("{:author} wrote \"{:poem}\" in {:year/%4d}\n, + author, poem, year); + +**** The Anchor Roles ({[:} and {]:}) @anchor-role@ + +The anchor roles allow a set of strings by be padded as a group, +but still be visible to xo_emit as distinct fields. Either the start +or stop anchor can give a field width and it can be either directly in +the descriptor or passed as an argument. Any fields between the start +and stop anchor are padded to meet the minimum width given. + +To give a width directly, encode it as the content of the anchor tag: + + xo_emit("({[:10}{:min/%d}/{:max/%d}{]:})\n", min, max); + +To pass a width as an argument, use "%d" as the format, which must +appear after the "/". Note that only "%d" is supported for widths. +Using any other value could ruin your day. + + xo_emit("({[:/%d}{:min/%d}/{:max/%d}{]:})\n", width, min, max); + +If the width is negative, padding will be added on the right, suitable +for left justification. Otherwise the padding will be added to the +left of the fields between the start and stop anchors, suitable for +right justification. If the width is zero, nothing happens. If the +number of columns of output between the start and stop anchors is less +than the absolute value of the given width, nothing happens. + +Widths over 8k are considered probable errors and not supported. If +XOF_WARN is set, a warning will be generated. + +*** Field Modifiers + +Field modifiers are flags which modify the way content emitted for +particular output styles: + +|---+---------------+-------------------------------------------------| +| M | Name | Description | +|---+---------------+-------------------------------------------------| +| c | colon | A colon (":") is appended after the label | +| d | display | Only emit field for display styles (text/HTML) | +| e | encoding | Only emit for encoding styles (XML/JSON) | +| g | gettext | Call gettext on field's render content | +| h | humanize (hn) | Format large numbers in human-readable style | +| | hn-space | Humanize: Place space between numeric and unit | +| | hn-decimal | Humanize: Add a decimal digit, if number < 10 | +| | hn-1000 | Humanize: Use 1000 as divisor instead of 1024 | +| k | key | Field is a key, suitable for XPath predicates | +| l | leaf-list | Field is a leaf-list | +| n | no-quotes | Do not quote the field when using JSON style | +| p | plural | Gettext: Use comma-separated plural form | +| q | quotes | Quote the field when using JSON style | +| t | trim | Trim leading and trailing whitespace | +| w | white | A blank (" ") is appended after the label | +|---+---------------+-------------------------------------------------| + +Roles and modifiers can also use more verbose names, when preceeded by +a comma. For example, the modifier string "Lwc" (or "L,white,colon") +means the field has a label role (text that describes the next field) +and should be followed by a colon ('c') and a space ('w'). The +modifier string "Vkq" (or ":key,quote") means the field has a value +role (the default role), that it is a key for the current instance, +and that the value should be quoted when encoded for JSON. + +**** The Colon Modifier ({c:}) + +The colon modifier appends a single colon to the data value: + + EXAMPLE: + xo_emit("{Lc:Name}{:name}\n", "phil"); + TEXT: + Name:phil + +The colon modifier is only used for the TEXT and HTML output +styles. It is commonly combined with the space modifier ('{w:}'). +It is purely a convenience feature. + +**** The Display Modifier ({d:}) + +The display modifier indicated the field should only be generated for +the display output styles, TEXT and HTML. + + EXAMPLE: + xo_emit("{Lcw:Name}{d:name} {:id/%d}\n", "phil", 1); + TEXT: + Name: phil 1 + XML: + 1 + +The display modifier is the opposite of the encoding modifier, and +they are often used to give to distinct views of the underlying data. + +**** The Encoding Modifier ({e:}) @e-modifier@ + +The display modifier indicated the field should only be generated for +the display output styles, TEXT and HTML. + + EXAMPLE: + xo_emit("{Lcw:Name}{:name} {e:id/%d}\n", "phil", 1); + TEXT: + Name: phil + XML: + phil1 + +The encoding modifier is the opposite of the display modifier, and +they are often used to give to distinct views of the underlying data. + +**** The Gettext Modifier ({g:}) @gettext-modifier@ + +The gettext modifier is used to translate individual fields using the +gettext domain (typically set using the "{G:}" role) and current +language settings. Once libxo renders the field value, it is passed +to gettext(3), where it is used as a key to find the native language +translation. + +In the following example, the strings "State" and "full" are passed +to gettext() to find locale-based translated strings. + + xo_emit("{Lgwc:State}{g:state}\n", "full"); + +See ^gettext-role^, ^plural-modifier^, and ^howto-i18n^ for additional +details. + +**** The Humanize Modifier ({h:}) + +The humanize modifier is used to render large numbers as in a +human-readable format. While numbers like "44470272" are completely +readable to computers and savants, humans will generally find "44M" +more meaningful. + +"hn" can be used as an alias for "humanize". + +The humanize modifier only affects display styles (TEXT and HMTL). +The "no-humanize" option (See ^LIBXO_OPTIONS^) will block the function of +the humanize modifier. + +There are a number of modifiers that affect details of humanization. +These are only available in as full names, not single characters. The +"hn-space" modifier places a space between the number and any +multiplier symbol, such as "M" or "K" (ex: "44 K"). The "hn-decimal" +modifier will add a decimal point and a single tenths digit when the number is +less than 10 (ex: "4.4K"). The "hn-1000" modifier will use 1000 as divisor +instead of 1024, following the JEDEC-standard instead of the more +natural binary powers-of-two tradition. + + EXAMPLE: + xo_emit("{h:input/%u}, {h,hn-space:output/%u}, " + "{h,hn-decimal:errors/%u}, {h,hn-1000:capacity/%u}, " + "{h,hn-decimal:remaining/%u}\n", + input, output, errors, capacity, remaining); + TEXT: + 21, 57 K, 96M, 44M, 1.2G + +In the HTML style, the original numeric value is rendered in the +"data-number" attribute on the
element: + +
96M
+ +**** The Key Modifier ({k:}) + +The key modifier is used to indicate that a particular field helps +uniquely identify an instance of list data. + + EXAMPLE: + xo_open_list("user"); + for (i = 0; i < num_users; i++) { + xo_open_instance("user"); + xo_emit("User {k:name} has {:count} tickets\n", + user[i].u_name, user[i].u_tickets); + xo_close_instance("user"); + } + xo_close_list("user"); + +Currently the key modifier is only used when generating XPath value +for the HTML output style when XOF_XPATH is set, but other uses are +likely in the near future. + +**** The Leaf-List Modifier ({l:}) + +The leaf-list modifier is used to distinguish lists where each +instance consists of only a single value. In XML, these are +rendered as single elements, where JSON renders them as arrays. + + EXAMPLE: + for (i = 0; i < num_users; i++) { + xo_emit("Member {l:user}\n", user[i].u_name); + } + XML: + phil + pallavi + JSON: + "user": [ "phil", "pallavi" ] + +The name of the field must match the name of the leaf list. + +**** The No-Quotes Modifier ({n:}) + +The no-quotes modifier (and its twin, the 'quotes' modifier) affect +the quoting of values in the JSON output style. JSON uses quotes for +string value, but no quotes for numeric, boolean, and null data. +xo_emit applies a simple heuristic to determine whether quotes are +needed, but often this needs to be controlled by the caller. + + EXAMPLE: + const char *bool = is_true ? "true" : "false"; + xo_emit("{n:fancy/%s}", bool); + JSON: + "fancy": true + +**** The Plural Modifier ({p:}) @plural-modifier@ + +The plural modifier selects the appropriate plural form of an +expression based on the most recent number emitted and the current +language settings. The contents of the field should be the singular +and plural English values, separated by a comma: + + xo_emit("{:bytes} {Ngp:byte,bytes}\n", bytes); + +The plural modifier is meant to work with the gettext modifier ({g:}) +but can work independently. See ^gettext-modifier^. + +When used without the gettext modifier or when the message does not +appear in the message catalog, the first token is chosen when the last +numeric value is equal to 1; otherwise the second value is used, +mimicking the simple pluralization rules of English. + +When used with the gettext modifier, the ngettext(3) function is +called to handle the heavy lifting, using the message catalog to +convert the singular and plural forms into the native language. + +**** The Quotes Modifier ({q:}) + +The quotes modifier (and its twin, the 'no-quotes' modifier) affect +the quoting of values in the JSON output style. JSON uses quotes for +string value, but no quotes for numeric, boolean, and null data. +xo_emit applies a simple heuristic to determine whether quotes are +needed, but often this needs to be controlled by the caller. + + EXAMPLE: + xo_emit("{q:time/%d}", 2014); + JSON: + "year": "2014" + +**** The White Space Modifier ({w:}) + +The white space modifier appends a single space to the data value: + + EXAMPLE: + xo_emit("{Lw:Name}{:name}\n", "phil"); + TEXT: + Name phil + +The white space modifier is only used for the TEXT and HTML output +styles. It is commonly combined with the colon modifier ('{c:}'). +It is purely a convenience feature. + +Note that the sense of the 'w' modifier is reversed for the units role +({Uw:}); a blank is added before the contents, rather than after it. + +*** Field Formatting + +The field format is similar to the format string for printf(3). Its +use varies based on the role of the field, but generally is used to +format the field's contents. + +If the format string is not provided for a value field, it defaults to +"%s". + +Note a field definition can contain zero or more printf-style +'directives', which are sequences that start with a '%' and end with +one of following characters: "diouxXDOUeEfFgGaAcCsSp". Each directive +is matched by one of more arguments to the xo_emit function. + +The format string has the form: + + '%' format-modifier * format-character + +The format- modifier can be: +- a '#' character, indicating the output value should be prefixed with +'0x', typically to indicate a base 16 (hex) value. +- a minus sign ('-'), indicating the output value should be padded on +the right instead of the left. +- a leading zero ('0') indicating the output value should be padded on the +left with zeroes instead of spaces (' '). +- one or more digits ('0' - '9') indicating the minimum width of the +argument. If the width in columns of the output value is less that +the minumum width, the value will be padded to reach the minimum. +- a period followed by one or more digits indicating the maximum +number of bytes which will be examined for a string argument, or the maximum +width for a non-string argument. When handling ASCII strings this +functions as the field width but for multi-byte characters, a single +character may be composed of multiple bytes. +xo_emit will never dereference memory beyond the given number of bytes. +- a second period followed by one or more digits indicating the maximum +width for a string argument. This modifier cannot be given for non-string +arguments. +- one or more 'h' characters, indicating shorter input data. +- one or more 'l' characters, indicating longer input data. +- a 'z' character, indicating a 'size_t' argument. +- a 't' character, indicating a 'ptrdiff_t' argument. +- a ' ' character, indicating a space should be emitted before +positive numbers. +- a '+' character, indicating sign should emitted before any number. + +Note that 'q', 'D', 'O', and 'U' are considered deprecated and will be +removed eventually. + +The format character is described in the following table: + +|-----+-----------------+----------------------| +| Ltr | Argument Type | Format | +|-----+-----------------+----------------------| +| d | int | base 10 (decimal) | +| i | int | base 10 (decimal) | +| o | int | base 8 (octal) | +| u | unsigned | base 10 (decimal) | +| x | unsigned | base 16 (hex) | +| X | unsigned long | base 16 (hex) | +| D | long | base 10 (decimal) | +| O | unsigned long | base 8 (octal) | +| U | unsigned long | base 10 (decimal) | +| e | double | [-]d.ddde+-dd | +| E | double | [-]d.dddE+-dd | +| f | double | [-]ddd.ddd | +| F | double | [-]ddd.ddd | +| g | double | as 'e' or 'f' | +| G | double | as 'E' or 'F' | +| a | double | [-]0xh.hhhp[+-]d | +| A | double | [-]0Xh.hhhp[+-]d | +| c | unsigned char | a character | +| C | wint_t | a character | +| s | char * | a UTF-8 string | +| S | wchar_t * | a unicode/WCS string | +| p | void * | '%#lx' | +|-----+-----------------+----------------------| + +The 'h' and 'l' modifiers affect the size and treatment of the +argument: + +|-----+-------------+--------------------| +| Mod | d, i | o, u, x, X | +|-----+-------------+--------------------| +| hh | signed char | unsigned char | +| h | short | unsigned short | +| l | long | unsigned long | +| ll | long long | unsigned long long | +| j | intmax_t | uintmax_t | +| t | ptrdiff_t | ptrdiff_t | +| z | size_t | size_t | +| q | quad_t | u_quad_t | +|-----+-------------+--------------------| + +*** UTF-8 and Locale Strings + +For strings, the 'h' and 'l' modifiers affect the interpretation of +the bytes pointed to argument. The default '%s' string is a 'char *' +pointer to a string encoded as UTF-8. Since UTF-8 is compatible with +ASCII data, a normal 7-bit ASCII string can be used. '%ls' expects a +'wchar_t *' pointer to a wide-character string, encoded as a 32-bit +Unicode values. '%hs' expects a 'char *' pointer to a multi-byte +string encoded with the current locale, as given by the LC_CTYPE, +LANG, or LC_ALL environment varibles. The first of this list of +variables is used and if none of the variables are set, the locale +defaults to "UTF-8". + +For example, a function is passed a locale-base name, a hat size, +and a time value. The hat size is formatted in a UTF-8 (ASCII) +string, and the time value is formatted into a wchar_t string. + + void print_order (const char *name, int size, + struct tm *timep) { + char buf[32]; + const char *size_val = "unknown"; + + if (size > 0) + snprintf(buf, sizeof(buf), "%d", size); + size_val = buf; + } + + wchar_t when[32]; + wcsftime(when, sizeof(when), L"%d%b%y", timep); + + xo_emit("The hat for {:name/%hs} is {:size/%s}.\n", + name, size_val); + xo_emit("It was ordered on {:order-time/%ls}.\n", + when); + } + +It is important to note that xo_emit will perform the conversion +required to make appropriate output. Text style output uses the +current locale (as described above), while XML, JSON, and HTML use +UTF-8. + +UTF-8 and locale-encoded strings can use multiple bytes to encode one +column of data. The traditional "precision'" (aka "max-width") value +for "%s" printf formatting becomes overloaded since it specifies both +the number of bytes that can be safely referenced and the maximum +number of columns to emit. xo_emit uses the precision as the former, +and adds a third value for specifying the maximum number of columns. + +In this example, the name field is printed with a minimum of 3 columns +and a maximum of 6. Up to ten bytes of data at the location given by +'name' are in used in filling those columns. + + xo_emit("{:name/%3.10.6s}", name); + +*** Characters Outside of Field Definitions + +Characters in the format string that are not part of a field +definition are copied to the output for the TEXT style, and are +ignored for the JSON and XML styles. For HTML, these characters are +placed in a
with class "text". + + EXAMPLE: + xo_emit("The hat is {:size/%s}.\n", size_val); + TEXT: + The hat is extra small. + XML: + extra small + JSON: + "size": "extra small" + HTML: +
The hat is
+
extra small
+
.
+ +*** "%m" Is Supported + +libxo supports the '%m' directive, which formats the error message +associated with the current value of "errno". It is the equivalent +of "%s" with the argument strerror(errno). + + xo_emit("{:filename} cannot be opened: {:error/%m}", filename); + xo_emit("{:filename} cannot be opened: {:error/%s}", + filename, strerror(errno)); + +*** "%n" Is Not Supported + +libxo does not support the '%n' directive. It's a bad idea and we +just don't do it. + +*** The Encoding Format (eformat) + +The "eformat" string is the format string used when encoding the field +for JSON and XML. If not provided, it defaults to the primary format +with any minimum width removed. If the primary is not given, both +default to "%s". + +*** Content Strings + +For padding and labels, the content string is considered the content, +unless a format is given. + +*** Argument Validation @printf-like@ + +Many compilers and tool chains support validation of printf-like +arguments. When the format string fails to match the argument list, +a warning is generated. This is a valuable feature and while the +formatting strings for libxo differ considerably from printf, many of +these checks can still provide build-time protection against bugs. + +libxo provide variants of functions that provide this ability, if the +"--enable-printflike" option is passed to the "configure" script. +These functions use the "_p" suffix, like "xo_emit_p()", +xo_emit_hp()", etc. + +The following are features of libxo formatting strings that are +incompatible with printf-like testing: + +- implicit formats, where "{:tag}" has an implicit "%s"; +- the "max" parameter for strings, where "{:tag/%4.10.6s}" means up to +ten bytes of data can be inspected to fill a minimum of 4 columns and +a maximum of 6; +- percent signs in strings, where "{:filled}%" makes a single, +trailing percent sign; +- the "l" and "h" modifiers for strings, where "{:tag/%hs}" means +locale-based string and "{:tag/%ls}" means a wide character string; +- distinct encoding formats, where "{:tag/#%s/%s}" means the display +styles (text and HTML) will use "#%s" where other styles use "%s"; + +If none of these features are in use by your code, then using the "_p" +variants might be wise. + +|------------------+------------------------| +| Function | printf-like Equivalent | +|------------------+------------------------| +| xo_emit_hv | xo_emit_hvp | +| xo_emit_h | xo_emit_hp | +| xo_emit | xo_emit_p | +| xo_emit_warn_hcv | xo_emit_warn_hcvp | +| xo_emit_warn_hc | xo_emit_warn_hcp | +| xo_emit_warn_c | xo_emit_warn_cp | +| xo_emit_warn | xo_emit_warn_p | +| xo_emit_warnx_ | xo_emit_warnx_p | +| xo_emit_err | xo_emit_err_p | +| xo_emit_errx | xo_emit_errx_p | +| xo_emit_errc | xo_emit_errc_p | +|------------------+------------------------| + +*** Example + +In this example, the value for the number of items in stock is emitted: + + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", + instock); + +This call will generate the following output: + + TEXT: + In stock: 144 + XML: + 144 + JSON: + "in-stock": 144, + HTML: +
+
+
In stock
+
:
+
+
144
+
+ +Clearly HTML wins the verbosity award, and this output does +not include XOF_XPATH or XOF_INFO data, which would expand the +penultimate line to: + +
144
+ +** Command-line Arguments + +libxo uses command line options to trigger rendering behavior. The +following options are recognised: + +- --libxo +- --libxo= +- --libxo: + +Options is a comma-separated list of tokens that correspond to output +styles, flags, or features: + +|-------------+-------------------------------------------------------| +| Token | Action | +|-------------+-------------------------------------------------------| +| color | Enable colors/effects for display styles (TEXT, HTML) | +| dtrt | Enable "Do The Right Thing" mode | +| html | Emit HTML output | +| indent=xx | Set the indentation level | +| info | Add info attributes (HTML) | +| json | Emit JSON output | +| keys | Emit the key attribute for keys (XML) | +| log-gettext | Log (via stderr) each gettext(3) string lookup | +| log-syslog | Log (via stderr) each syslog message (via xo_syslog) | +| no-humanize | Ignore the {h:} modifier (TEXT, HTML) | +| no-locale | Do not initialize the locale setting | +| no-top | Do not emit a top set of braces (JSON) | +| not-first | Pretend the 1st output item was not 1st (JSON) | +| pretty | Emit pretty-printed output | +| text | Emit TEXT output | +| underscores | Replace XML-friendly "-"s with JSON friendly "_"s e | +| units | Add the 'units' (XML) or 'data-units (HTML) attribute | +| warn | Emit warnings when libxo detects bad calls | +| warn-xml | Emit warnings in XML | +| xml | Emit XML output | +| xpath | Add XPath expressions (HTML) | +|-------------+-------------------------------------------------------| + +The brief options are detailed in ^LIBXO_OPTIONS^. + +** Representing Hierarchy + +For XML and JSON, individual fields appear inside hierarchies which +provide context and meaning to the fields. Unfortunately, these +encoding have a basic disconnect between how lists is similar objects +are represented. + +XML encodes lists as set of sequential elements: + + phil + pallavi + sjg + +JSON encodes lists using a single name and square brackets: + + "user": [ "phil", "pallavi", "sjg" ] + +This means libxo needs three distinct indications of hierarchy: one +for containers of hierarchy appear only once for any specific parent, +one for lists, and one for each item in a list. + +*** Containers + +A "container" is an element of a hierarchy that appears only once +under any specific parent. The container has no value, but serves to +contain other nodes. + +To open a container, call xo_open_container() or +xo_open_container_h(). The former uses the default handle and +the latter accepts a specific handle. + + int xo_open_container_h (xo_handle_t *xop, const char *name); + int xo_open_container (const char *name); + +To close a level, use the xo_close_container() or +xo_close_container_h() functions: + + int xo_close_container_h (xo_handle_t *xop, const char *name); + int xo_close_container (const char *name); + +Each open call must have a matching close call. If the XOF_WARN flag +is set and the name given does not match the name of the currently open +container, a warning will be generated. + + Example: + + xo_open_container("top"); + xo_open_container("system"); + xo_emit("{:host-name/%s%s%s", hostname, + domainname ? "." : "", domainname ?: ""); + xo_close_container("system"); + xo_close_container("top"); + + Sample Output: + Text: + my-host.example.org + XML: + + + my-host.example.org + + + JSON: + "top" : { + "system" : { + "host-name": "my-host.example.org" + } + } + HTML: +
my-host.example.org
+ +*** Lists and Instances + +A list is set of one or more instances that appear under the same +parent. The instances contain details about a specific object. One +can think of instances as objects or records. A call is needed to +open and close the list, while a distinct call is needed to open and +close each instance of the list: + + xo_open_list("item"); + + for (ip = list; ip->i_title; ip++) { + xo_open_instance("item"); + xo_emit("{L:Item} '{:name/%s}':\n", ip->i_title); + xo_close_instance("item"); + } + + xo_close_list("item"); + +Getting the list and instance calls correct is critical to the proper +generation of XML and JSON data. + +*** DTRT Mode + +Some users may find tracking the names of open containers, lists, and +instances inconvenient. libxo offers a "Do The Right Thing" mode, where +libxo will track the names of open containers, lists, and instances so +the close function can be called without a name. To enable DTRT mode, +turn on the XOF_DTRT flag prior to making any other libxo output. + + xo_set_flags(NULL, XOF_DTRT); + +Each open and close function has a version with the suffix "_d", which +will close the open container, list, or instance: + + xo_open_container("top"); + ... + xo_close_container_d(); + +This also works for lists and instances: + + xo_open_list("item"); + for (...) { + xo_open_instance("item"); + xo_emit(...); + xo_close_instance_d(); + } + xo_close_list_d(); + +Note that the XOF_WARN flag will also cause libxo to track open +containers, lists, and instances. A warning is generated when the +name given to the close function and the name recorded do not match. + +*** Markers + +Markers are used to protect and restore the state of open constructs. +While a marker is open, no other open constructs can be closed. When +a marker is closed, all constructs open since the marker was opened +will be closed. + +Markers use names which are not user-visible, allowing the caller to +choose appropriate internal names. + +In this example, the code whiffles through a list of fish, calling a +function to emit details about each fish. The marker "fish-guts" is +used to ensure that any constructs opened by the function are closed +properly. + + for (i = 0; fish[i]; i++) { + xo_open_instance("fish"); + xo_open_marker("fish-guts"); + dump_fish_details(i); + xo_close_marker("fish-guts"); + } + +** Handles @handles@ + +libxo uses "handles" to control its rendering functionality. The +handle contains state and buffered data, as well as callback functions +to process data. + +A default handle is used when a NULL is passed to functions accepting +a handle. This handle is initialized to write its data to stdout +using the default style of text (XO_STYLE_TEXT). + +For the convenience of callers, the libxo library includes handle-less +functions that implicitly use the default handle. Any function that +takes a handle will use the default handle is a value of NULL is +passed in place of a valid handle. + +For example, the following are equivalent: + + xo_emit("test"); + xo_emit_h(NULL, "test"); + +Handles are created using xo_create() and destroy using xo_destroy(). + +** UTF-8 + +All strings for libxo must be UTF-8. libxo will handle turning them +into locale-based strings for display to the user. + +The only exception is argument formatted using the "%ls" format, which +require a wide character string (wchar_t *) as input. libxo will +convert these arguments as needed to either UTF-8 (for XML, JSON, and +HTML styles) or locale-based strings for display in text style. + + xo_emit("Alll strings are utf-8 content {:tag/%ls}", + L"except for wide strings"); + +"%S" is equivalent to "%ls". + +* The libxo API + +This section gives details about the functions in libxo, how to call +them, and the actions they perform. + +** Handles + +Handles give an abstraction for libxo that encapsulates the state of a +stream of output. Handles have the data type "xo_handle_t" and are +opaque to the caller. + +The library has a default handle that is automatically initialized. +By default, this handle will send text style output to standard output. +The xo_set_style and xo_set_flags functions can be used to change this +behavior. + +Many libxo functions take a handle as their first parameter; most that +do not use the default handle. Any function taking a handle can +be passed NULL to access the default handle. + +For the typical command that is generating output on standard output, +there is no need to create an explicit handle, but they are available +when needed, e.g., for daemons that generate multiple streams of +output. + +*** xo_create + +A handle can be allocated using the xo_create() function: + + xo_handle_t *xo_create (unsigned style, unsigned flags); + + Example: + xo_handle_t *xop = xo_create(XO_STYLE_JSON, XOF_WARN); + .... + xo_emit_h(xop, "testing\n"); + +See also ^styles^ and ^flags^. + +*** xo_create_to_file + +By default, libxo writes output to standard output. A convenience +function is provided for situations when output should be written to +a different file: + + xo_handle_t *xo_create_to_file (FILE *fp, unsigned style, + unsigned flags); + +Use the XOF_CLOSE_FP flag to trigger a call to fclose() for +the FILE pointer when the handle is destroyed. + +*** xo_set_writer + +The xo_set_writer function allows custom 'write' functions +which can tailor how libxo writes data. An opaque argument is +recorded and passed back to the write function, allowing the function +to acquire context information. The 'close' function can +release this opaque data and any other resources as needed. +The flush function can flush buffered data associated with the opaque +object. + + void xo_set_writer (xo_handle_t *xop, void *opaque, + xo_write_func_t write_func, + xo_close_func_t close_func); + xo_flush_func_t flush_func); + +*** xo_set_style + +To set the style, use the xo_set_style() function: + + void xo_set_style(xo_handle_t *xop, unsigned style); + +To use the default handle, pass a NULL handle: + + xo_set_style(NULL, XO_STYLE_XML); + +**** Output Styles (XO_STYLE_*) @styles@ + +The libxo functions accept a set of output styles: + +|---------------+-------------------------| +| Flag | Description | +|---------------+-------------------------| +| XO_STYLE_TEXT | Traditional text output | +| XO_STYLE_XML | XML encoded data | +| XO_STYLE_JSON | JSON encoded data | +| XO_STYLE_HTML | HTML encoded data | +|---------------+-------------------------| + +**** xo_set_style_name + +The xo_set_style_name() can be used to set the style based on a name +encoded as a string: + + int xo_set_style_name (xo_handle_t *xop, const char *style); + +The name can be any of the styles: "text", "xml", "json", or "html". + + EXAMPLE: + xo_set_style_name(NULL, "html"); + +*** xo_set_flags + +To set the flags, use the xo_set_flags() function: + + void xo_set_flags(xo_handle_t *xop, unsigned flags); + +To use the default handle, pass a NULL handle: + + xo_set_style(NULL, XO_STYLE_XML); + +**** Flags (XOF_*) @flags@ + +The set of valid flags include: + +|-------------------+----------------------------------------| +| Flag | Description | +|-------------------+----------------------------------------| +| XOF_CLOSE_FP | Close file pointer on xo_destroy() | +| XOF_COLOR | Enable color and effects in output | +| XOF_COLOR_ALLOWED | Allow color/effect for terminal output | +| XOF_DTRT | Enable "do the right thing" mode | +| XOF_INFO | Display info data attributes (HTML) | +| XOF_KEYS | Emit the key attribute (XML) | +| XOF_NO_ENV | Do not use the LIBXO_OPTIONS env var | +| XOF_NO_HUMANIZE | Display humanization (TEXT, HTML) | +| XOF_PRETTY | Make 'pretty printed' output | +| XOF_UNDERSCORES | Replaces hyphens with underscores | +| XOF_UNITS | Display units (XML, HMTL) | +| XOF_WARN | Generate warnings for broken calls | +| XOF_WARN_XML | Generate warnings in XML on stdout | +| XOF_XPATH | Emit XPath expressions (HTML) | +| XOF_COLUMNS | Force xo_emit to return columns used | +| XOF_FLUSH | Flush output after each xo_emit call | +|-------------------+----------------------------------------| + +The XOF_CLOSE_FP flag will trigger the call of the close_func +(provided via xo_set_writer()) when the handle is destroyed. + +The XOF_COLOR flag enables color and effects in output regardless of +output device, while the XOF_COLOR_ALLOWED flag allows color and +effects only if the output device is a terminal. + +The XOF_PRETTY flag requests 'pretty printing', which will trigger the +addition of indentation and newlines to enhance the readability of +XML, JSON, and HTML output. Text output is not affected. + +The XOF_WARN flag requests that warnings will trigger diagnostic +output (on standard error) when the library notices errors during +operations, or with arguments to functions. Without warnings enabled, +such conditions are ignored. + +Warnings allow developers to debug their interaction with libxo. +The function "xo_failure" can used as a breakpoint for a debugger, +regardless of whether warnings are enabled. + +If the style is XO_STYLE_HTML, the following additional flags can be +used: + +|---------------+-----------------------------------------| +| Flag | Description | +|---------------+-----------------------------------------| +| XOF_XPATH | Emit "data-xpath" attributes | +| XOF_INFO | Emit additional info fields | +|---------------+-----------------------------------------| + +The XOF_XPATH flag enables the emission of XPath expressions detailing +the hierarchy of XML elements used to encode the data field, if the +XPATH style of output were requested. + +The XOF_INFO flag encodes additional informational fields for HTML +output. See ^info^ for details. + +If the style is XO_STYLE_XML, the following additional flags can be +used: + +|---------------+-----------------------------------------| +| Flag | Description | +|---------------+-----------------------------------------| +| XOF_KEYS | Flag 'key' fields for xml | +|---------------+-----------------------------------------| + +The XOF_KEYS flag adds 'key' attribute to the XML encoding for +field definitions that use the 'k' modifier. The key attribute has +the value "key": + + xo_emit("{k:name}", item); + + XML: + truck + +**** xo_clear_flags + +The xo_clear_flags() function turns off the given flags in a specific +handle. + + void xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags); + +**** xo_set_options + +The xo_set_options() function accepts a comma-separated list of styles +and flags and enables them for a specific handle. + + int xo_set_options (xo_handle_t *xop, const char *input); + +The options are identical to those listed in ^command-line-arguments^. + +*** xo_destroy + +The xo_destroy function releases a handle and any resources it is +using. Calling xo_destroy with a NULL handle will release any +resources associated with the default handle. + + void xo_destroy(xo_handle_t *xop); + +** Emitting Content (xo_emit) + +The following functions are used to emit output: + + int xo_emit (const char *fmt, ...); + int xo_emit_h (xo_handle_t *xop, const char *fmt, ...); + int xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap); + +The "fmt" argument is a string containing field descriptors as +specified in ^format-strings^. The use of a handle is optional and +NULL can be passed to access the internal 'default' handle. See +^handles^. + +The remaining arguments to xo_emit() and xo_emit_h() are a set of +arguments corresponding to the fields in the format string. Care must +be taken to ensure the argument types match the fields in the format +string, since an inappropriate cast can ruin your day. The vap +argument to xo_emit_hv() points to a variable argument list that can +be used to retrieve arguments via va_arg(). + +*** Attributes (xo_attr) @xo_attr@ + +The xo_attr() function emits attributes for the XML output style. + + + int xo_attr (const char *name, const char *fmt, ...); + int xo_attr_h (xo_handle_t *xop, const char *name, + const char *fmt, ...); + int xo_attr_hv (xo_handle_t *xop, const char *name, + const char *fmt, va_list vap); + +The name parameter give the name of the attribute to be encoded. The +fmt parameter gives a printf-style format string used to format the +value of the attribute using any remaining arguments, or the vap +parameter passed to xo_attr_hv(). + + EXAMPLE: + xo_attr("seconds", "%ld", (unsigned long) login_time); + struct tm *tmp = localtime(login_time); + strftime(buf, sizeof(buf), "%R", tmp); + xo_emit("Logged in at {:login-time}\n", buf); + XML: + 00:14 + +xo_attr is placed on the next container, instance, leaf, or leaf list +that is emitted. + +Since attributes are only emitted in XML, their use should be limited +to meta-data and additional or redundant representations of data +already emitted in other form. + +*** Flushing Output (xo_flush) + +libxo buffers data, both for performance and consistency, but also to +allow some advanced features to work properly. At various times, the +caller may wish to flush any data buffered within the library. The +xo_flush() call is used for this: + + void xo_flush (void); + void xo_flush_h (xo_handle_t *xop); + +Calling xo_flush also triggers the flush function associated with the +handle. For the default handle, this is equivalent to +"fflush(stdio);". + +*** Finishing Output (xo_finish) + +When the program is ready to exit or close a handle, a call to +xo_finish() is required. This flushes any buffered data, closes +open libxo constructs, and completes any pending operations. + + int xo_finish (void); + int xo_finish_h (xo_handle_t *xop); + void xo_finish_atexit (void); + +Calling this function is vital to the proper operation of libxo, +especially for the non-TEXT output styles. + +xo_finish_atexit is suitable for use with atexit(3). + +** Emitting Hierarchy + +libxo represents to types of hierarchy: containers and lists. A +container appears once under a given parent where a list contains +instances that can appear multiple times. A container is used to hold +related fields and to give the data organization and scope. + +To create a container, use the xo_open_container and +xo_close_container functions: + + int xo_open_container (const char *name); + int xo_open_container_h (xo_handle_t *xop, const char *name); + int xo_open_container_hd (xo_handle_t *xop, const char *name); + int xo_open_container_d (const char *name); + + int xo_close_container (const char *name); + int xo_close_container_h (xo_handle_t *xop, const char *name); + int xo_close_container_hd (xo_handle_t *xop); + int xo_close_container_d (void); + +The name parameter gives the name of the container, encoded in UTF-8. +Since ASCII is a proper subset of UTF-8, traditional C strings can be +used directly. + +The close functions with the "_d" suffix are used in "Do The Right +Thing" mode, where the name of the open containers, lists, and +instances are maintained internally by libxo to allow the caller to +avoid keeping track of the open container name. + +Use the XOF_WARN flag to generate a warning if the name given on the +close does not match the current open container. + +For TEXT and HTML output, containers are not rendered into output +text, though for HTML they are used when the XOF_XPATH flag is set. + + EXAMPLE: + xo_open_container("system"); + xo_emit("The host name is {:host-name}\n", hn); + xo_close_container("system"); + XML: + foo + +*** Lists and Instances + +Lists are sequences of instances of homogeneous data objects. Two +distinct levels of calls are needed to represent them in our output +styles. Calls must be made to open and close a list, and for each +instance of data in that list, calls must be make to open and close +that instance. + +The name given to all calls must be identical, and it is strongly +suggested that the name be singular, not plural, as a matter of +style and usage expectations. + + EXAMPLE: + xo_open_list("user"); + for (i = 0; i < num_users; i++) { + xo_open_instance("user"); + xo_emit("{k:name}:{:uid/%u}:{:gid/%u}:{:home}\n", + pw[i].pw_name, pw[i].pw_uid, + pw[i].pw_gid, pw[i].pw_dir); + xo_close_instance("user"); + } + xo_close_list("user"); + TEXT: + phil:1001:1001:/home/phil + pallavi:1002:1002:/home/pallavi + XML: + + phil + 1001 + 1001 + /home/phil + + + pallavi + 1002 + 1002 + /home/pallavi + + JSON: + user: [ + { + "name": "phil", + "uid": 1001, + "gid": 1001, + "home": "/home/phil", + }, + { + "name": "pallavi", + "uid": 1002, + "gid": 1002, + "home": "/home/pallavi", + } + ] + +** Support Functions + +*** Parsing Command-line Arguments (xo_parse_args) @xo_parse_args@ + +The xo_parse_args() function is used to process a program's +arguments. libxo-specific options are processed and removed +from the argument list so the calling application does not +need to process them. If successful, a new value for argc +is returned. On failure, a message it emitted and -1 is returned. + + argc = xo_parse_args(argc, argv); + if (argc < 0) + exit(EXIT_FAILURE); + +Following the call to xo_parse_args, the application can process the +remaining arguments in a normal manner. See ^command-line-arguments^ +for a description of valid arguments. + +*** xo_set_program + +The xo_set_program function sets name of the program as reported by +functions like xo_failure, xo_warn, xo_err, etc. The program name is +initialized by xo_parse_args, but subsequent calls to xo_set_program +can override this value. + + xo_set_program(argv[0]); + +Note that the value is not copied, so the memory passed to +xo_set_program (and xo_parse_args) must be maintained by the caller. + +*** xo_set_version + +The xo_set_version function records a version number to be emitted as +part of the data for encoding styles (XML and JSON). This version +number is suitable for tracking changes in the content, allowing a +user of the data to discern which version of the data model is in use. + + void xo_set_version (const char *version); + void xo_set_version_h (xo_handle_t *xop, const char *version); + +*** Field Information (xo_info_t) @info@ + +HTML data can include additional information in attributes that +begin with "data-". To enable this, three things must occur: + +First the application must build an array of xo_info_t structures, +one per tag. The array must be sorted by name, since libxo uses a +binary search to find the entry that matches names from format +instructions. + +Second, the application must inform libxo about this information using +the xo_set_info() call: + + typedef struct xo_info_s { + const char *xi_name; /* Name of the element */ + const char *xi_type; /* Type of field */ + const char *xi_help; /* Description of field */ + } xo_info_t; + + void xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count); + +Like other libxo calls, passing NULL for the handle tells libxo to use +the default handle. + +If the count is -1, libxo will count the elements of infop, but there +must be an empty element at the end. More typically, the number is +known to the application: + + xo_info_t info[] = { + { "in-stock", "number", "Number of items in stock" }, + { "name", "string", "Name of the item" }, + { "on-order", "number", "Number of items on order" }, + { "sku", "string", "Stock Keeping Unit" }, + { "sold", "number", "Number of items sold" }, + }; + int info_count = (sizeof(info) / sizeof(info[0])); + ... + xo_set_info(NULL, info, info_count); + +Third, the emission of info must be triggered with the XOF_INFO flag +using either the xo_set_flags() function or the "--libxo=info" command +line argument. + +The type and help values, if present, are emitted as the "data-type" +and "data-help" attributes: + +
GRO-000-533
+ +*** Memory Allocation + +The xo_set_allocator function allows libxo to be used in environments +where the standard realloc() and free() functions are not available. + + void xo_set_allocator (xo_realloc_func_t realloc_func, + xo_free_func_t free_func); + +realloc_func should expect the same arguments as realloc(3) and return +a pointer to memory following the same convention. free_func will +receive the same argument as free(3) and should release it, as +appropriate for the environment. + +By default, the standard realloc() and free() functions are used. + +*** LIBXO_OPTIONS @LIBXO_OPTIONS@ + +The environment variable "LIBXO_OPTIONS" can be set to a string of +options: + +|--------+---------------------------------------------| +| Option | Action | +|--------+---------------------------------------------| +| c | Enable color/effects for TEXT/HTML | +| F | Force line-buffered flushing | +| H | Enable HTML output (XO_STYLE_HTML) | +| I | Enable info output (XOF_INFO) | +| i | Indent by | +| J | Enable JSON output (XO_STYLE_JSON) | +| k | Add keys to XPATH expressions in HTML | +| n | Disable humanization (TEXT, HTML) | +| P | Enable pretty-printed output (XOF_PRETTY) | +| T | Enable text output (XO_STYLE_TEXT) | +| U | Add units to HTML output | +| u | Change "-"s to "_"s in element names (JSON) | +| W | Enable warnings (XOF_WARN) | +| X | Enable XML output (XO_STYLE_XML) | +| x | Enable XPath data (XOF_XPATH) | +|--------+---------------------------------------------| + +For example, warnings can be enabled by: + + % env LIBXO_OPTIONS=W my-app + +Complete HTML output can be generated with: + + % env LIBXO_OPTIONS=HXI my-app + +Since environment variables are inherited, child processes will have +the same options, which may be undesirable, making the use of the +"--libxo" option is preferable in most situations. + +*** Errors, Warnings, and Messages + +Many programs make use of the standard library functions err() and +warn() to generate errors and warnings for the user. libxo wants to +pass that information via the current output style, and provides +compatible functions to allow this: + + void xo_warn (const char *fmt, ...); + void xo_warnx (const char *fmt, ...); + void xo_warn_c (int code, const char *fmt, ...); + void xo_warn_hc (xo_handle_t *xop, int code, + const char *fmt, ...); + void xo_err (int eval, const char *fmt, ...); + void xo_errc (int eval, int code, const char *fmt, ...); + void xo_errx (int eval, const char *fmt, ...); + void xo_message (const char *fmt, ...); + void xo_message_c (int code, const char *fmt, ...); + void xo_message_hc (xo_handle_t *xop, int code, + const char *fmt, ...); + void xo_message_hcv (xo_handle_t *xop, int code, + const char *fmt, va_list vap); + +These functions display the program name, a colon, a formatted message +based on the arguments, and then optionally a colon and an error +message associated with either "errno" or the "code" parameter. + + EXAMPLE: + if (open(filename, O_RDONLY) < 0) + xo_err(1, "cannot open file '%s'", filename); + +*** xo_error + +The xo_error function can be used for generic errors that should be +reported over the handle, rather than to stderr. The xo_error +function behaves like xo_err for TEXT and HTML output styles, but puts +the error into XML or JSON elements: + + EXAMPLE:: + xo_error("Does not %s", "compute"); + XML:: + Does not compute + JSON:: + "error": { "message": "Does not compute" } + +*** xo_no_setlocale + +libxo automatically initializes the locale based on setting of the +environment variables LC_CTYPE, LANG, and LC_ALL. The first of this +list of variables is used and if none of the variables, the locale +defaults to "UTF-8". The caller may wish to avoid this behavior, and +can do so by calling the xo_no_setlocale() function. + + void xo_no_setlocale (void); + +** Emitting syslog Messages + +syslog is the system logging facility used throughout the unix world. +Messages are sent from commands, applications, and daemons to a +hierarchy of servers, where they are filtered, saved, and forwarded +based on configuration behaviors. + +syslog is an older protocol, originally documented only in source +code. By the time RFC 3164 published, variation and mutation left the +leading "" string as only common content. RFC 5424 defines a new +version (version 1) of syslog and introduces structured data into the +messages. Structured data is a set of name/value pairs transmitted +distinctly alongside the traditional text message, allowing filtering +on precise values instead of regular expressions. + +These name/value pairs are scoped by a two-part identifier; an +enterprise identifier names the party responsible for the message +catalog and a name identifying that message. Enterprise IDs are +defined by IANA, the Internet Assigned Numbers Authority: + +https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers + +Use the ^xo_set_syslog_enterprise_id^() function to set the Enterprise +ID, as needed. + +The message name should follow the conventions in ^good-field-names^, +as should the fields within the message. + + /* Both of these calls are optional */ + xo_set_syslog_enterprise_id(32473); + xo_open_log("my-program", 0, LOG_DAEMON); + + /* Generate a syslog message */ + xo_syslog(LOG_ERR, "upload-failed", + "error <%d> uploading file '{:filename}' " + "as '{:target/%s:%s}'", + code, filename, protocol, remote); + + xo_syslog(LOG_INFO, "poofd-invalid-state", + "state {:current/%u} is invalid {:connection/%u}", + state, conn); + +The developer should be aware that the message name may be used in the +future to allow access to further information, including +documentation. Care should be taken to choose quality, descriptive +names. + +*** Priority, Facility, and Flags @priority@ + +The xo_syslog, xo_vsyslog, and xo_open_log functions accept a set of +flags which provide the priority of the message, the source facility, +and some additional features. These values are OR'd together to +create a single integer argument: + + xo_syslog(LOG_ERR | LOG_AUTH, "login-failed", + "Login failed; user '{:user}' from host '{:address}'", + user, addr); + +These values are defined in . + +The priority value indicates the importance and potential impact of +each message. + +|-------------+-------------------------------------------------------| +| Priority | Description | +|-------------+-------------------------------------------------------| +| LOG_EMERG | A panic condition, normally broadcast to all users | +| LOG_ALERT | A condition that should be corrected immediately | +| LOG_CRIT | Critical conditions | +| LOG_ERR | Generic errors | +| LOG_WARNING | Warning messages | +| LOG_NOTICE | Non-error conditions that might need special handling | +| LOG_INFO | Informational messages | +| LOG_DEBUG | Developer-oriented messages | +|-------------+-------------------------------------------------------| + +The facility value indicates the source of message, in fairly generic +terms. + +|---------------+-------------------------------------------------| +| Facility | Description | +|---------------+-------------------------------------------------| +| LOG_AUTH | The authorization system (e.g. login(1)) | +| LOG_AUTHPRIV | As LOG_AUTH, but logged to a privileged file | +| LOG_CRON | The cron daemon: cron(8) | +| LOG_DAEMON | System daemons, not otherwise explicitly listed | +| LOG_FTP | The file transfer protocol daemons | +| LOG_KERN | Messages generated by the kernel | +| LOG_LPR | The line printer spooling system | +| LOG_MAIL | The mail system | +| LOG_NEWS | The network news system | +| LOG_SECURITY | Security subsystems, such as ipfw(4) | +| LOG_SYSLOG | Messages generated internally by syslogd(8) | +| LOG_USER | Messages generated by user processes (default) | +| LOG_UUCP | The uucp system | +| LOG_LOCAL0..7 | Reserved for local use | +|---------------+-------------------------------------------------| + +In addition to the values listed above, xo_open_log accepts a set of +addition flags requesting specific behaviors. + +|------------+----------------------------------------------------| +| Flag | Description | +|------------+----------------------------------------------------| +| LOG_CONS | If syslogd fails, attempt to write to /dev/console | +| LOG_NDELAY | Open the connection to syslogd(8) immediately | +| LOG_PERROR | Write the message also to standard error output | +| LOG_PID | Log the process id with each message | +|------------+----------------------------------------------------| + +*** xo_syslog + +Use the xo_syslog function to generate syslog messages by calling it +with a log priority and facility, a message name, a format string, and +a set of arguments. The priority/facility argument are discussed +above, as is the message name. + +The format string follows the same conventions as xo_emit's format +string, with each field being rendered as an SD-PARAM pair. + + xo_syslog(LOG_ERR, "poofd-missing-file", + "'{:filename}' not found: {:error/%m}", filename); + + ... [poofd-missing-file@32473 filename="/etc/poofd.conf" + error="Permission denied"] '/etc/poofd.conf' not + found: Permission denied + +*** Support functions + +**** xo_vsyslog + +xo_vsyslog is identical in function to xo_syslog, but takes the set of +arguments using a va_list. + + void my_log (const char *name, const char *fmt, ...) + { + va_list vap; + va_start(vap, fmt); + xo_vsyslog(LOG_ERR, name, fmt, vap); + va_end(vap); + } + +**** xo_open_log + +xo_open_log functions similar to openlog(3), allowing customization of +the program name, the log facility number, and the additional option +flags described in ^priority^. + + void + xo_open_log (const char *ident, int logopt, int facility); + +**** xo_close_log + +xo_close_log functions similar to closelog(3), closing the log file +and releasing any associated resources. + + void + xo_close_log (void); + +**** xo_set_logmask + +xo_set_logmask function similar to setlogmask(3), restricting the set +of generated log event to those whose associated bit is set in +maskpri. Use LOG_MASK(pri) to find the appropriate bit, or +LOG_UPTO(toppri) to create a mask for all priorities up to and +including toppri. + + int + xo_set_logmask (int maskpri); + + Example: + setlogmask(LOG_UPTO(LOG_WARN)); + +**** xo_set_syslog_enterprise_id + +Use the xo_set_syslog_enterprise_id to supply a platform- or +application-specific enterprise id. This value is used in any +future syslog messages. + +Ideally, the operating system should supply a default value via the +"kern.syslog.enterprise_id" sysctl value. Lacking that, the +application should provide a suitable value. + + void + xo_set_syslog_enterprise_id (unsigned short eid); + +Enterprise IDs are administered by IANA, the Internet Assigned Number +Authority. The complete list is EIDs on their web site: + + https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers + +New EIDs can be requested from IANA using the following page: + + http://pen.iana.org/pen/PenApplication.page + +Each software development organization that defines a set of syslog +messages should register their own EID and use that value in their +software to ensure that messages can be uniquely identified by the +combination of EID + message name. + +** Creating Custom Encoders + +The number of encoding schemes in current use is staggering, with new +and distinct schemes appearing daily. While libxo provide XML, JSON, +HMTL, and text natively, there are requirements for other encodings. + +Rather than bake support for all possible encoders into libxo, the API +allows them to be defined externally. libxo can then interfaces with +these encoding modules using a simplistic API. libxo processes all +functions calls, handles state transitions, performs all formatting, +and then passes the results as operations to a customized encoding +function, which implements specific encoding logic as required. This +means your encoder doesn't need to detect errors with unbalanced +open/close operations but can rely on libxo to pass correct data. + +By making a simple API, libxo internals are not exposed, insulating the +encoder and the library from future or internal changes. + +The three elements of the API are: +- loading +- initialization +- operations + +The following sections provide details about these topics. + +libxo source contain an encoder for Concise Binary Object +Representation, aka CBOR (RFC 7049) which can be used as used as an +example for the API. + +*** Loading Encoders + +Encoders can be registered statically or discovered dynamically. +Applications can choose to call the xo_encoder_register() +function to explicitly register encoders, but more typically they are +built as shared libraries, placed in the libxo/extensions directory, +and loaded based on name. libxo looks for a file with the name of the encoder +and an extension of ".enc". This can be a file or a symlink to the +shared library file that supports the encoder. + + % ls -1 lib/libxo/extensions/*.enc + lib/libxo/extensions/cbor.enc + lib/libxo/extensions/test.enc + +*** Encoder Initialization + +Each encoder must export a symbol used to access the library, which +must have the following signature: + + int xo_encoder_library_init (XO_ENCODER_INIT_ARGS); + +XO_ENCODER_INIT_ARGS is a macro defined in xo_encoder.h that defines +an argument called "arg", a pointer of the type +xo_encoder_init_args_t. This structure contains two fields: + +- xei_version is the version number of the API as implemented within +libxo. This version is currently as 1 using XO_ENCODER_VERSION. This +number can be checked to ensure compatibility. The working assumption +is that all versions should be backward compatible, but each side may +need to accurately know the version supported by the other side. +xo_encoder_library_init can optionally check this value, and must then +set it to the version number used by the encoder, allowing libxo to +detect version differences and react accordingly. For example, if +version 2 adds new operations, then libxo will know that an encoding +library that set xei_version to 1 cannot be expected to handle those +new operations. + +- xei_handler must be set to a pointer to a function of type +xo_encoder_func_t, as defined in xo_encoder.h. This function +takes a set of parameters: +-- xop is a pointer to the opaque xo_handle_t structure +-- op is an integer representing the current operation +-- name is a string whose meaning differs by operation +-- value is a string whose meaning differs by operation +-- private is an opaque structure provided by the encoder + +Additional arguments may be added in the future, so handler functions +should use the XO_ENCODER_HANDLER_ARGS macro. An appropriate +"extern" declaration is provided to help catch errors. + +Once the encoder initialization function has completed processing, it +should return zero to indicate that no error has occurred. A non-zero +return code will cause the handle initialization to fail. + +*** Operations + +The encoder API defines a set of operations representing the +processing model of libxo. Content is formatted within libxo, and +callbacks are made to the encoder's handler function when data is +ready to be processed. + +|-----------------------+---------------------------------------| +| Operation | Meaning (Base function) | +|-----------------------+---------------------------------------| +| XO_OP_CREATE | Called when the handle is created | +| XO_OP_OPEN_CONTAINER | Container opened (xo_open_container) | +| XO_OP_CLOSE_CONTAINER | Container closed (xo_close_container) | +| XO_OP_OPEN_LIST | List opened (xo_open_list) | +| XO_OP_CLOSE_LIST | List closed (xo_close_list) | +| XO_OP_OPEN_LEAF_LIST | Leaf list opened (xo_open_leaf_list) | +| XO_OP_CLOSE_LEAF_LIST | Leaf list closed (xo_close_leaf_list) | +| XO_OP_OPEN_INSTANCE | Instance opened (xo_open_instance) | +| XO_OP_CLOSE_INSTANCE | Instance closed (xo_close_instance) | +| XO_OP_STRING | Field with Quoted UTF-8 string | +| XO_OP_CONTENT | Field with content | +| XO_OP_FINISH | Finish any pending output | +| XO_OP_FLUSH | Flush any buffered output | +| XO_OP_DESTROY | Clean up resources | +| XO_OP_ATTRIBUTE | An attribute name/value pair | +| XO_OP_VERSION | A version string | +|-----------------------+---------------------------------------| + +For all the open and close operations, the name parameter holds the +name of the construct. For string, content, and attribute operations, +the name parameter is the name of the field and the value parameter is +the value. "string" are differentiated from "content" to allow differing +treatment of true, false, null, and numbers from real strings, though +content values are formatted as strings before the handler is called. +For version operations, the value parameter contains the version. + +All strings are encoded in UTF-8. + +* The "xo" Utility + +The "xo" utility allows command line access to the functionality of +the libxo library. Using "xo", shell scripts can emit XML, JSON, and +HTML using the same commands that emit text output. + +The style of output can be selected using a specific option: "-X" for +XML, "-J" for JSON, "-H" for HTML, or "-T" for TEXT, which is the +default. The "--style ardinalyayammetersgrayErmissionpay eniedday6otuslay-oyay-eltayay \ No newline at end of file diff --git a/dist/tests/gettext/saved/gt_01.XP.err b/dist/tests/gettext/saved/gt_01.XP.err new file mode 100644 index 0000000..e69de29 diff --git a/dist/tests/gettext/saved/gt_01.XP.out b/dist/tests/gettext/saved/gt_01.XP.out new file mode 100644 index 0000000..eac4284 --- /dev/null +++ b/dist/tests/gettext/saved/gt_01.XP.out @@ -0,0 +1,49 @@ + + amingflay + ordsway + urningbay + ymay + ouchcay + amingflay + ordsway + urningbay + ymay + ouchcay + 0 + 1 + 2 + 3 + 4 + 1234 + 1234 + foop + 4321 + + 1234 + foop + 4321 + + 1234 + foop + 4321 + + 3 + 1.2.3 + Tue Jun 23 18:47:09 UTC 2015 + <__warning> + gt_01.test + Nableuay otay ectulatobjay orwardfay elocipingvay + ectulatobjay + Ermissionpay eniedday + + <__warning> + gt_01.test + automaticyay ynchronizationsay ofyay ardinalyay ammetersgray ailedfay + + ardinalyay + ammetersgray + Ermissionpay eniedday + + 6 + otuslay-oyay-eltayay + diff --git a/dist/tests/gettext/strerror.pot b/dist/tests/gettext/strerror.pot new file mode 100644 index 0000000..c63e6bd --- /dev/null +++ b/dist/tests/gettext/strerror.pot @@ -0,0 +1,468 @@ +# +# Copyright (c) 1982, 1985, 1993 +# The Regents of the University of California. 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. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. +# +# List of system errors ala strerror() and sys_errlist +# Phil Shafer , 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-01 16:15-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Received {:received} {N:byte,bytes} from {:from}#{:port} in {:time} ms\n" +msgstr "" + +# 0 - ENOERROR +msgid "No error: 0" +msgstr "" + +# 1 - EPERM +msgid "Operation not permitted" +msgstr "" + +# 2 - ENOENT +msgid "No such file or directory" +msgstr "" + +# 3 - ESRCH +msgid "No such process" +msgstr "" + +# 4 - EINTR +msgid "Interrupted system call" +msgstr "" + +# 5 - EIO +msgid "Input/output error" +msgstr "" + +# 6 - ENXIO +msgid "Device not configured" +msgstr "" + +# 7 - E2BIG +msgid "Argument list too long" +msgstr "" + +# 8 - ENOEXEC +msgid "Exec format error" +msgstr "" + +# 9 - EBADF +msgid "Bad file descriptor" +msgstr "" + +# 10 - ECHILD +msgid "No child processes" +msgstr "" + +# 11 - EDEADLK +msgid "Resource deadlock avoided" +msgstr "" + +# 12 - ENOMEM +msgid "Cannot allocate memory" +msgstr "" + +# 13 - EACCES +msgid "Permission denied" +msgstr "" + +# 14 - EFAULT +msgid "Bad address" +msgstr "" + +# 15 - ENOTBLK +msgid "Block device required" +msgstr "" + +# 16 - EBUSY +msgid "Device busy" +msgstr "" + +# 17 - EEXIST +msgid "File exists" +msgstr "" + +# 18 - EXDEV +msgid "Cross-device link" +msgstr "" + +# 19 - ENODEV +msgid "Operation not supported by device" +msgstr "" + +# 20 - ENOTDIR +msgid "Not a directory" +msgstr "" + +# 21 - EISDIR +msgid "Is a directory" +msgstr "" + +# 22 - EINVAL +msgid "Invalid argument" +msgstr "" + +# 23 - ENFILE +msgid "Too many open files in system" +msgstr "" + +# 24 - EMFILE +msgid "Too many open files" +msgstr "" + +# 25 - ENOTTY +msgid "Inappropriate ioctl for device" +msgstr "" + +# 26 - ETXTBSY +msgid "Text file busy" +msgstr "" + +# 27 - EFBIG +msgid "File too large" +msgstr "" + +# 28 - ENOSPC +msgid "No space left on device" +msgstr "" + +# 29 - ESPIPE +msgid "Illegal seek" +msgstr "" + +# 30 - EROFS +msgid "Read-only file system" +msgstr "" + +# 31 - EMLINK +msgid "Too many links" +msgstr "" + +# 32 - EPIPE +msgid "Broken pipe" +msgstr "" + +# +# math software +# + +# 33 - EDOM +msgid "Numerical argument out of domain" +msgstr "" + +# 34 - ERANGE +msgid "Result too large" +msgstr "" + +# +# non-blocking and interrupt i/o +# + +# 35 - EAGAIN +# 35 - EWOULDBLOCK +msgid "Resource temporarily unavailable" +msgstr "" + +# 36 - EINPROGRESS +msgid "Operation now in progress" +msgstr "" + +# 37 - EALREADY +msgid "Operation already in progress" +msgstr "" + + +# +# ipc/network software -- argument errors +# + +# 38 - ENOTSOCK +msgid "Socket operation on non-socket" +msgstr "" + +# 39 - EDESTADDRREQ +msgid "Destination address required" +msgstr "" + +# 40 - EMSGSIZE +msgid "Message too long" +msgstr "" + +# 41 - EPROTOTYPE +msgid "Protocol wrong type for socket" +msgstr "" + +# 42 - ENOPROTOOPT +msgid "Protocol not available" +msgstr "" + +# 43 - EPROTONOSUPPORT +msgid "Protocol not supported" +msgstr "" + +# 44 - ESOCKTNOSUPPORT +msgid "Socket type not supported" +msgstr "" + +# 45 - EOPNOTSUPP +msgid "Operation not supported" +msgstr "" + +# 46 - EPFNOSUPPORT +msgid "Protocol family not supported" +msgstr "" + +# 47 - EAFNOSUPPORT +msgid "Address family not supported by protocol family" +msgstr "" + +# 48 - EADDRINUSE +msgid "Address already in use" +msgstr "" + +# 49 - EADDRNOTAVAIL +msgid "Can't assign requested address" +msgstr "" + +# +# ipc/network software -- operational errors +# + +# 50 - ENETDOWN +msgid "Network is down" +msgstr "" + +# 51 - ENETUNREACH +msgid "Network is unreachable" +msgstr "" + +# 52 - ENETRESET +msgid "Network dropped connection on reset" +msgstr "" + +# 53 - ECONNABORTED +msgid "Software caused connection abort" +msgstr "" + +# 54 - ECONNRESET +msgid "Connection reset by peer" +msgstr "" + +# 55 - ENOBUFS +msgid "No buffer space available" +msgstr "" + +# 56 - EISCONN +msgid "Socket is already connected" +msgstr "" + +# 57 - ENOTCONN +msgid "Socket is not connected" +msgstr "" + +# 58 - ESHUTDOWN +msgid "Can't send after socket shutdown" +msgstr "" + +# 59 - ETOOMANYREFS +msgid "Too many references: can't splice" +msgstr "" + +# 60 - ETIMEDOUT +msgid "Operation timed out" +msgstr "" + +# 61 - ECONNREFUSED +msgid "Connection refused" +msgstr "" + +# 62 - ELOOP +msgid "Too many levels of symbolic links" +msgstr "" + +# 63 - ENAMETOOLONG +msgid "File name too long" +msgstr "" + +# +# should be rearranged +# + +# 64 - EHOSTDOWN +msgid "Host is down" +msgstr "" + +# 65 - EHOSTUNREACH +msgid "No route to host" +msgstr "" + +# 66 - ENOTEMPTY +msgid "Directory not empty" +msgstr "" + +# +# quotas & mush +# + +# 67 - EPROCLIM +msgid "Too many processes" +msgstr "" + +# 68 - EUSERS +msgid "Too many users" +msgstr "" + +# 69 - EDQUOT +msgid "Disc quota exceeded" +msgstr "" + +# +# Network File System +# + +# 70 - ESTALE +msgid "Stale NFS file handle" +msgstr "" + +# 71 - EREMOTE +msgid "Too many levels of remote in path" +msgstr "" + +# 72 - EBADRPC +msgid "RPC struct is bad" +msgstr "" + +# 73 - ERPCMISMATCH +msgid "RPC version wrong" +msgstr "" + +# 74 - EPROGUNAVAIL +msgid "RPC prog. not avail" +msgstr "" + +# 75 - EPROGMISMATCH +msgid "Program version wrong" +msgstr "" + +# 76 - EPROCUNAVAIL +msgid "Bad procedure for program" +msgstr "" + +# 77 - ENOLCK +msgid "No locks available" +msgstr "" + +# 78 - ENOSYS +msgid "Function not implemented" +msgstr "" + +# 79 - EFTYPE +msgid "Inappropriate file type or format" +msgstr "" + +# 80 - EAUTH +msgid "Authentication error" +msgstr "" + +# 81 - ENEEDAUTH +msgid "Need authenticator" +msgstr "" + +# 82 - EIDRM +msgid "Identifier removed" +msgstr "" + +# 83 - ENOMSG +msgid "No message of desired type" +msgstr "" + +# 84 - EOVERFLOW +msgid "Value too large to be stored in data type" +msgstr "" + +# 85 - ECANCELED +msgid "Operation canceled" +msgstr "" + +# 86 - EILSEQ +msgid "Illegal byte sequence" +msgstr "" + +# 87 - ENOATTR +msgid "Attribute not found" +msgstr "" + +# +# General +# + +# 88 - EDOOFUS +msgid "Programming error" +msgstr "" + +# 89 - EBADMSG +msgid "Bad message" +msgstr "" + +# 90 - EMULTIHOP +msgid "Multihop attempted" +msgstr "" + +# 91 - ENOLINK +msgid "Link has been severed" +msgstr "" + +# 92 - EPROTO +msgid "Protocol error" +msgstr "" + +# 93 - ENOTCAPABLE +msgid "Capabilities insufficient" +msgstr "" + +# 94 - ECAPMODE +msgid "Not permitted in capability mode" +msgstr "" + +# 95 - ENOTRECOVERABLE +msgid "State not recoverable" +msgstr "" + +# 96 - EOWNERDEAD +msgid "Previous owner died" +msgstr "" diff --git a/dist/tests/xo/Makefile.am b/dist/tests/xo/Makefile.am new file mode 100644 index 0000000..1687f09 --- /dev/null +++ b/dist/tests/xo/Makefile.am @@ -0,0 +1,90 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +AM_CFLAGS = -I${top_srcdir} -I${top_srcdir}/libxo + +# Ick: maintained by hand! +TEST_CASES = \ +xo_01.sh + +X=\ +xo_02.sh \ +xo_03.sh \ +xo_04.sh \ +xo_05.sh \ +xo_06.sh + +# TEST_CASES := $(shell cd ${srcdir} ; echo *.c ) + +EXTRA_DIST = \ + ${TEST_CASES} \ + ${addprefix saved/, ${TEST_CASES:.sh=.T.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.T.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.XP.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.XP.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.JP.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.JP.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.HP.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.HP.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.X.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.X.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.J.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.J.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.H.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.H.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.HIPx.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.HIPx.out}} + +S2O = | ${SED} '1,/@@/d' + +all: + +#TEST_TRACE = set -x ; + +XO=../../xo/xo + +TEST_ONE = \ + LIBXO_OPTIONS=:W$$fmt \ + ${CHECKER} sh ${srcdir}/$$base.sh ${XO} ${TEST_OPTS} \ + > out/$$base.$$fmt.out 2> out/$$base.$$fmt.err ; \ + ${DIFF} -Nu ${srcdir}/saved/$$base.$$fmt.out out/$$base.$$fmt.out ${S2O} ; \ + ${DIFF} -Nu ${srcdir}/saved/$$base.$$fmt.err out/$$base.$$fmt.err ${S2O} + +TEST_FORMATS = T XP JP HP X J H HIPx + +test tests: ${bin_PROGRAMS} + @${MKDIR} -p out + -@ ${TEST_TRACE} (for test in ${TEST_CASES} ; do \ + base=`${BASENAME} $$test .sh` ; \ + (for fmt in ${TEST_FORMATS}; do \ + echo "... $$test ... $$fmt ..."; \ + ${TEST_ONE}; \ + true; \ + done) \ + done) + +one: + -@(test=${TEST_CASE}; data=${TEST_DATA}; ${TEST_ONE} ; true) + +accept: + -@(for test in ${TEST_CASES} ; do \ + base=`${BASENAME} $$test .sh` ; \ + (for fmt in ${TEST_FORMATS}; do \ + echo "... $$test ... $$fmt ..."; \ + ${CP} out/$$base.$$fmt.out ${srcdir}/saved/$$base.$$fmt.out ; \ + ${CP} out/$$base.$$fmt.err ${srcdir}/saved/$$base.$$fmt.err ; \ + done) \ + done) + +CLEANFILES = +CLEANDIRS = out + +clean-local: + rm -rf ${CLEANDIRS} diff --git a/dist/tests/xo/saved/xo_01.H.err b/dist/tests/xo/saved/xo_01.H.err new file mode 100644 index 0000000..e69de29 diff --git a/dist/tests/xo/saved/xo_01.H.out b/dist/tests/xo/saved/xo_01.H.out new file mode 100644 index 0000000..dd82a1c --- /dev/null +++ b/dist/tests/xo/saved/xo_01.H.out @@ -0,0 +1 @@ +
Item
one
is
number
001
,
color
:
red
Item
two
is
number
002
,
color
:
blue
Item
three
is
number
003
,
color
:
green
Item
four
is
number
004
,
color
:
yellow
\ No newline at end of file diff --git a/dist/tests/xo/saved/xo_01.HIPx.err b/dist/tests/xo/saved/xo_01.HIPx.err new file mode 100644 index 0000000..e69de29 diff --git a/dist/tests/xo/saved/xo_01.HIPx.out b/dist/tests/xo/saved/xo_01.HIPx.out new file mode 100644 index 0000000..12e36b1 --- /dev/null +++ b/dist/tests/xo/saved/xo_01.HIPx.out @@ -0,0 +1,52 @@ +
+
Item
+
one
+
is
+
number
+
+
001
+
,
+
color
+
:
+
+
red
+
+
+
Item
+
two
+
is
+
number
+
+
002
+
,
+
color
+
:
+
+
blue
+
+
+
Item
+
three
+
is
+
number
+
+
003
+
,
+
color
+
:
+
+
green
+
+
+
Item
+
four
+
is
+
number
+
+
004
+
,
+
color
+
:
+
+
yellow
+
diff --git a/dist/tests/xo/saved/xo_01.HP.err b/dist/tests/xo/saved/xo_01.HP.err new file mode 100644 index 0000000..e69de29 diff --git a/dist/tests/xo/saved/xo_01.HP.out b/dist/tests/xo/saved/xo_01.HP.out new file mode 100644 index 0000000..de91936 --- /dev/null +++ b/dist/tests/xo/saved/xo_01.HP.out @@ -0,0 +1,52 @@ +
+
Item
+
one
+
is
+
number
+
+
001
+
,
+
color
+
:
+
+
red
+
+
+
Item
+
two
+
is
+
number
+
+
002
+
,
+
color
+
:
+
+
blue
+
+
+
Item
+
three
+
is
+
number
+
+
003
+
,
+
color
+
:
+
+
green
+
+
+
Item
+
four
+
is
+
number
+
+
004
+
,
+
color
+
:
+
+
yellow
+
diff --git a/dist/tests/xo/saved/xo_01.J.err b/dist/tests/xo/saved/xo_01.J.err new file mode 100644 index 0000000..e69de29 diff --git a/dist/tests/xo/saved/xo_01.J.out b/dist/tests/xo/saved/xo_01.J.out new file mode 100644 index 0000000..86ce4ef --- /dev/null +++ b/dist/tests/xo/saved/xo_01.J.out @@ -0,0 +1 @@ +"top": {"item": {"name":"one","value":1,"color":"red"}, "item": {"name":"two","value":2,"color":"blue"}, "item": {"name":"three","value":3,"color":"green"}, "item": {"name":"four","value":4,"color":"yellow"}} diff --git a/dist/tests/xo/saved/xo_01.JP.err b/dist/tests/xo/saved/xo_01.JP.err new file mode 100644 index 0000000..e69de29 diff --git a/dist/tests/xo/saved/xo_01.JP.out b/dist/tests/xo/saved/xo_01.JP.out new file mode 100644 index 0000000..5a25b17 --- /dev/null +++ b/dist/tests/xo/saved/xo_01.JP.out @@ -0,0 +1,22 @@ +"top": { + "item": { + "name": "one", + "value": 1, + "color": "red" + }, + "item": { + "name": "two", + "value": 2, + "color": "blue" + }, + "item": { + "name": "three", + "value": 3, + "color": "green" + }, + "item": { + "name": "four", + "value": 4, + "color": "yellow" + } +} diff --git a/dist/tests/xo/saved/xo_01.T.err b/dist/tests/xo/saved/xo_01.T.err new file mode 100644 index 0000000..e69de29 diff --git a/dist/tests/xo/saved/xo_01.T.out b/dist/tests/xo/saved/xo_01.T.out new file mode 100644 index 0000000..ed2ea35 --- /dev/null +++ b/dist/tests/xo/saved/xo_01.T.out @@ -0,0 +1,4 @@ +Item one is number 001, color: red +Item two is number 002, color: blue +Item three is number 003, color: green +Item four is number 004, color: yellow diff --git a/dist/tests/xo/saved/xo_01.X.err b/dist/tests/xo/saved/xo_01.X.err new file mode 100644 index 0000000..e69de29 diff --git a/dist/tests/xo/saved/xo_01.X.out b/dist/tests/xo/saved/xo_01.X.out new file mode 100644 index 0000000..7539566 --- /dev/null +++ b/dist/tests/xo/saved/xo_01.X.out @@ -0,0 +1 @@ +one1redtwo2bluethree3greenfour4yellow \ No newline at end of file diff --git a/dist/tests/xo/saved/xo_01.XP.err b/dist/tests/xo/saved/xo_01.XP.err new file mode 100644 index 0000000..e69de29 diff --git a/dist/tests/xo/saved/xo_01.XP.out b/dist/tests/xo/saved/xo_01.XP.out new file mode 100644 index 0000000..7f069c8 --- /dev/null +++ b/dist/tests/xo/saved/xo_01.XP.out @@ -0,0 +1,22 @@ + + + one + 1 + red + + + two + 2 + blue + + + three + 3 + green + + + four + 4 + yellow + + diff --git a/dist/tests/xo/xo_01.sh b/dist/tests/xo/xo_01.sh new file mode 100755 index 0000000..8de9410 --- /dev/null +++ b/dist/tests/xo/xo_01.sh @@ -0,0 +1,27 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +XO=$1 +shift + +XOP="${XO} --warn --depth 1 --leading-xpath /top" + +${XO} --open top + +NF= +for i in one:1:red two:2:blue three:3:green four:4:yellow ; do + set `echo $i | sed 's/:/ /g'` + ${XOP} ${NF} --wrap item \ + 'Item {k:name} is {Lw:number}{:value/%03d/%d}, {Lwc:color}{:color}\n' \ + $1 $2 $3 + NF=--not-first +done + +${XO} --close top \ No newline at end of file diff --git a/dist/warnings.mk b/dist/warnings.mk new file mode 100644 index 0000000..c07ac37 --- /dev/null +++ b/dist/warnings.mk @@ -0,0 +1,57 @@ +# +# $Id$ +# +# Copyright 2011, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. +# +# Commonly used sets of warnings +# + +MIN_WARNINGS?= -W -Wall + +LOW_WARNINGS?= ${MIN_WARNINGS} \ + -Wstrict-prototypes \ + -Wmissing-prototypes \ + -Wpointer-arith + +MEDIUM_WARNINGS?= ${LOW_WARNINGS} -Werror + +HIGH_WARNINGS?= ${MEDIUM_WARNINGS} \ + -Waggregate-return \ + -Wcast-align \ + -Wcast-qual \ + -Wchar-subscripts \ + -Wcomment \ + -Wformat \ + -Wimplicit \ + -Wmissing-declarations \ + -Wnested-externs \ + -Wparentheses \ + -Wreturn-type \ + -Wshadow \ + -Wswitch \ + -Wtrigraphs \ + -Wuninitialized \ + -Wunused \ + -Wwrite-strings + +HIGHER_WARNINGS?= ${HIGH_WARNINGS} \ + -Winline \ + -Wbad-function-cast \ + -Wpacked \ + -Wpadded \ + -Wstrict-aliasing + +ifeq "${LIBXO_WARNINGS}" "HIGH" +WARNINGS += ${HIGH_WARNINGS} +else +WARNINGS += ${LOW_WARNINGS} +endif + +ifeq "${GCC_WARNINGS}" "yes" +WARNINGS += -fno-inline-functions-called-once +endif diff --git a/dist/xo/Makefile.am b/dist/xo/Makefile.am new file mode 100644 index 0000000..ca01de3 --- /dev/null +++ b/dist/xo/Makefile.am @@ -0,0 +1,43 @@ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +if LIBXO_WARNINGS_HIGH +LIBXO_WARNINGS = HIGH +endif +include ${top_srcdir}/warnings.mk + +AM_CFLAGS = \ + -DLIBXO_XMLSOFT_NEED_PRIVATE \ + -I${top_builddir} \ + -I${top_srcdir} \ + -I${top_srcdir}/libxo \ + ${WARNINGS} + +LIBS = \ + ${LIBXO_LIBS} + +bin_PROGRAMS = xo + +xo_SOURCES = xo.c +#xo_LDADD = ../libxo/libxo.la +#xo_LDFLAGS = -static + +LDADD = \ + ${top_builddir}/libxo/libxo.la + +if HAVE_HUMANIZE_NUMBER +LDADD += -lutil +endif + +man_MANS = xo.1 + +EXTRA_DIST = xo.1 + +install-data-hook: + for file in ${man_MANS}; do \ + cat ../libxo/add.man >> ${DESTDIR}${man1dir}/$$file ; done diff --git a/dist/xo/xo.1 b/dist/xo/xo.1 new file mode 100644 index 0000000..54a97e7 --- /dev/null +++ b/dist/xo/xo.1 @@ -0,0 +1,173 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt XO 1 +.Os +.Sh NAME +.Nm xo +.Nd emit formatted output based on format string and arguments +.Sh SYNOPSIS +.Nm +.Op Fl options +.Op Ar argument... +.Sh DESCRIPTION +The +.Nm +utility allows command line access to the functionality of +the +.Nm libxo +library. +Using +.Nm , +shell scripts can emit +.Em XML , +.Em JSON , +or +.Em HTML +using the same commands that emit text output. +.Pp +.Bl -tag -width "12345678901234567" +.It Fl "-close " +Close tags for the given path +.It Fl "-depth " +Set the depth for pretty printing +.It Fl "-help" +Display this help text +.It Fl "-html OR -H" +Generate HTML output +.It Fl "-json OR -J" +Generate JSON output +.It Fl "-leading-xpath " +Add a prefix to generated XPaths (HTML) +.It Fl "-open " +Open tags for the given path +.It Fl "-pretty OR -p" +Make 'pretty' output (add indent, newlines) +.It Fl "-style