Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 26 Jun 2019 17:28:56 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r349430 - in stable/12: etc/mtree include share/man/man4 share/man/man9 sys/arm/allwinner sys/arm/conf sys/arm/ti/am335x sys/conf sys/dev/pwm sys/modules sys/modules/allwinner sys/modul...
Message-ID:  <201906261728.x5QHSuNc078844@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ian
Date: Wed Jun 26 17:28:55 2019
New Revision: 349430
URL: https://svnweb.freebsd.org/changeset/base/349430

Log:
  MFC r343826, r346698, r349057-r349060, r349073-r349077, r349080-r349086, r349088,
      r349091-r349097, r349115, r349119, r349130-r349132, r349143-r349145,
      r349164-r349168, r349174, r349269-r349273
  
  r343826 by yuripv:
  pwm.8: fix markup in synopsis, add -f description
  
  r346698 by manu:
  arm: allwinner: aw_pwm: compile it as module too
  
  r349057:
  Allow pwm(9) components to be selected individually, while 'device pwm'
  still includes it all.
  
  r349058:
  In detach(), check for failure of bus_generic_detach(), only release
  resources if they got allocated (because detach() gets called from attach()
  to handle various failures), and delete the pwmbus child if it got created.
  
  r349059:
  Don't call pwmbus_attach_bus(), because it may not be present if this
  driver is compiled into the kernel but pwmbus will be loaded as a module
  when needed (and because of that, pwmbus_attach_bus() is going away in
  the near future).  Instead, just directly do what that function did:
  register the fdt xfef handle, and attach the pwmbus.
  
  r349060:
  Handle failure to enable the clock or obtain its frequency.
  
  r349073:
  Do not include pwm.h here, it is purely a userland interface file containing
  ioctl defintions for the pwmc driver. It is not part of the pwmbus interface.
  
  r349074:
  Move/rename the sys/pwm.h header file to dev/pwm/pwmc.h.  The file contains
  ioctl definitions and related datatypes that allow userland control of pwm
  hardware via the pwmc device.  The new name and location better reflects its
  assocation with a single device driver.
  
  r349075:
  Remove pwmbus_attach_bus(), it no longer has any callers.  Also remove a
  couple prototypes for functions that never existed (and never will).
  
  r349076:
  Use device_delete_children() instead of a locally-rolled copy of it that
  leaks the device-list memory.
  
  r349077:
  Add a missing #include.  I suspect this used to get included via some header
  pollution that was cleaned up recently, and this file got missed in the
  cleanup because it's not attached to the build unless you specifically
  request this device in a custom kernel config.
  
  r349080:
  Make pwmbus driver and devclass vars static; they're not mentioned in any
  header file, so they can't be used outside this file anyway.
  
  r349081:
  Unwrap prototype lines so that return type and function name are on the
  same line.  No functional changes.
  
  r349082:
  Spell unsigned int as u_int and channel as chan; eliminates the need to wrap
  some long lines.
  
  r349083:
  Give the aw_pwm driver a module version.
  
  r349084:
  Rename the channel_max method to channel_count, because that's what it's
  returning.  (If the channel count is 2, then the max channel number is 1.)
  
  r349085:
  Destroy the cdev on device detach.  Also, make the driver and devclass
  static, because nothing outside this file needs them.
  
  r349086:
  Restructure the pwm device hirearchy and interfaces.
  
  The pwm and pwmbus interfaces were nearly identical, this merges them into a
  single pwmbus interface.  The pwmbus driver now implements the pwmbus
  interface by simply passing all calls through to its parent (the hardware
  driver).  The channel_count method moves from pwm to pwmbus, and the
  get_bus method is deleted (just no longer needed).
  
  The net effect is that the interface for doing pwm stuff is now the same
  regardless of whether you're a child of pwmbus, or some random driver
  elsewhere in the hierarchy that is bypassing the pwmbus layer and is talking
  directly to the hardware driver via cross-hierarchy connections established
  using fdt data.
  
  The pwmc driver is now a child of pwmbus, instead of being its sibling
  (that's why the get_bus method is no longer needed; pwmc now gets the
  device_t of the bus using device_get_parent()).
  
  r349088:
  Make pwm channel numbers unsigned.
  
  r349091:
  The pwm interface was replaced with pwmbus, include the right header file.
  
  r349092:
  Make channel number unsigned, and spell unsigned int u_int.  This should
  have been part of r349088.
  
  r349093:
  This code no longer uses fdt/ofw stuff, no need to include ofw headers.
  
  r349094:
  Add module makefiles for pwm.
  
  r349095:
  Split the dtb MODULES_EXTRA line to a series of += lines, making it easier
  to maintain and keep in alphabetical order, and paving the way for adding
  some other modules that aren't dtb-related.
  
  r349096:
  Add module makefiles for Texas Instruments ARM SoCs.
  
  The natural place to look for them based on how other SoCs are organized
  would be sys/modules/ti, but that's already taken.  Drop a clue into
  modules/ti/Makefile directing people to modules/arm_ti if they're looking
  for ARM modules.
  
  r349097:
  Build SoC-specific modules with GENERIC for the SoCs that have them.
  
  r349115:
  Rename pwmbus.h to ofw_pwm.h, because after all the recent changes, there
  is nothing left in the file that related to pwmbus at all.  It just contains
  prototypes for the functions implemented in dev/pwm.ofw_pwm.c, so name it
  accordingly and fix the include protect wrappers to match.
  
  A new pwmbus.h will be coming along in a future commit.
  
  r349119:
  Rework pwmbus and pwmc so that each child will handle a single PWM channel.
  
  Previously, there was a pwmc instance for each instance of pwm hardware
  regardless of how many pwm channels that hardware supported.  Now there
  will be a pwmc instance for each channel when the hardware supports
  multiple channels.  With a separate instance for each channel, we can have
  "named channels" in userland by making devfs alias entries in /dev/pwm.
  
  These changes add support for ivars to pwmbus, and use an ivar to track the
  channel number for each child.  It also adds support for hinted children.
  
  In pwmc, the driver checks for a label hint, and if present, it's used to
  create an alias for the cdev in /dev/pwm.  It's not anticipated that hints
  will be heavily used, but it's easy to do and allows quick ad-hoc creation
  of named channels from userland by using kenv to create hint.pwmc.N.label=
  hints.  Upcoming changes will add FDT support, and most labels will
  probably be specified that way.
  
  r349130:
  Add ofw_pwmbus to enumerate pwmbus devices on systems configured with fdt
  data.  Also, add fdt support to pwmc.
  
  r349131:
  Implement the ofw_bus_get_node method in aw_pwm(4) so that ofw_pwmbus can
  find its metadata for instantiating children.
  
  r349132:
  Add back a const qualifier I somehow fumbled away between test-building
  and committing recent changes.
  
  r349143:
  Put the pwmc cdev filenames under the pwm directory along with any label
  names.  I.e., everything related to pwm now goes in /dev/pwm.  This will
  make it easier for userland tools to turn an unqualified name into a fully
  qualified pathname, whether it's the base pwmcX.Y name or a label name.
  
  r349144:
  Follow changes in the pwmc(4) driver in relation to device filenames.
  
  The driver now names its cdev nodes pwmcX.Y where X is unit number and
  Y is the channel within that unit.  Change the default device name from
  pwmc0 to pwmc0.0.  The driver now puts cdev files and label aliases in
  the /dev/pwm directory, so allow the user to provide unqualified names
  with -f and automatically prepend the /dev/pwm part for them.
  
  Update the examples in the manpage to show the new device name format
  and location within /dev/pwm.
  
  r349145:
  Put periods at the ends of argument descriptions.  Explain the relationship
  between the period and duty arguments.
  
  r349164:
  Remove everything related to channels from the pwmc public interface, now
  that there is a pwmc(4) instance per channel and the channel number is
  maintained as a driver ivar rather than being passed in from userland.
  
  r349165:
  Explain the relationship between PWM hardware channels being controlled and
  pwmc(4) device filenames.  Also, use uppercase PWM when the term is being
  used as an acronym, and expand the acronym where it's first used.
  
  r349166:
  Rearrange the argument checking and processing so that enable and disable
  can be combined with configuring the period and duty cycle (the same ioctl
  sets all 3 values at once, so there's no reason to require the user to run
  the program twice to get all 3 things set).
  
  r349167:
  Oops, it seems I left out the word 'cycle', fix it.
  
  r349168:
  Add a pwmc(4) manpage.
  
  r349174:
  Handle labels specified with hints even on FDT systems.  Hints are the
  easiest thing for a user to control (via loader.conf or kenv+kldload), so
  handle them in addition to any label specified via the FDT data.
  
  r349269:
  Some mundane tweaks and cleanups to help de-clutter the diffs of some
  upcoming functional changes.
  
  Add an ofw_compat_data table for probing compat strings, and use it to add
  PNP data.  Remove some stray semicolons at the end of macro definitions,
  and add a PWM_LOCK_ASSERT macro to round out the usual suite.  Move the
  device_t and driver_methods structs to the end of the file.  Tweak comments.
  
  r349270:
  Add support for the PWM(9) API.  This allows configuring the pwm output using
  pwm(9), but also maintains the historical sysctl config interface for
  compatiblity with existing apps.  The two config systems are not compatible
  with each other; if you use both interfaces to change configurations you're
  likely to end up with incorrect output or none at all.
  
  r349271:
  Catch up with recent changes in pwmbus(9).  The pwm(9) and pwmbus(9)
  interfaces were unified into pwmbus(9), and the PWMBUS_CHANNEL_MAX method
  was renamed PWMBUS_CHANNEL_COUNT.  The pwmbus_attach_bus() function just
  went away completely.  Also, fix a few typos such as s/is/if/.
  
  r349272:
  Do some general cleanup and light wordsmithing.
  
  Sort methods alphabetically.  Wrap long lines.  Start sentences on a new
  line.  Remove contractions (not because it's a good idea, just to silence
  igor).  Add some explanation of the units for the period and duty arguments
  and the convention for channel numbers.
  
  r349273:
  Add pwm to the armv7 GENERIC kernel, it's now used by TI and Allwinner.

Added:
  stable/12/share/man/man4/pwmc.4
     - copied unchanged from r349168, head/share/man/man4/pwmc.4
  stable/12/sys/dev/pwm/ofw_pwm.h
     - copied unchanged from r349115, head/sys/dev/pwm/ofw_pwm.h
  stable/12/sys/dev/pwm/ofw_pwmbus.c
     - copied unchanged from r349132, head/sys/dev/pwm/ofw_pwmbus.c
  stable/12/sys/dev/pwm/pwmc.h
     - copied, changed from r349074, head/sys/dev/pwm/pwmc.h
  stable/12/sys/modules/allwinner/aw_pwm/
     - copied from r346698, head/sys/modules/allwinner/aw_pwm/
  stable/12/sys/modules/arm_ti/
     - copied from r349097, head/sys/modules/arm_ti/
  stable/12/sys/modules/pwm/
     - copied from r349097, head/sys/modules/pwm/
Replaced:
  stable/12/sys/dev/pwm/pwmbus.h
     - copied, changed from r349119, head/sys/dev/pwm/pwmbus.h
Deleted:
  stable/12/share/man/man9/pwm.9
  stable/12/sys/dev/pwm/pwm_if.m
  stable/12/sys/sys/pwm.h
Modified:
  stable/12/etc/mtree/BSD.include.dist
  stable/12/include/Makefile
  stable/12/share/man/man4/Makefile
  stable/12/share/man/man9/Makefile
  stable/12/share/man/man9/pwmbus.9
  stable/12/sys/arm/allwinner/aw_pwm.c
  stable/12/sys/arm/conf/GENERIC
  stable/12/sys/arm/ti/am335x/am335x_dmtpps.c
  stable/12/sys/arm/ti/am335x/am335x_ehrpwm.c
  stable/12/sys/conf/files
  stable/12/sys/dev/pwm/ofw_pwm.c
  stable/12/sys/dev/pwm/pwmbus.c
  stable/12/sys/dev/pwm/pwmbus_if.m
  stable/12/sys/dev/pwm/pwmc.c
  stable/12/sys/modules/Makefile
  stable/12/sys/modules/allwinner/Makefile
  stable/12/sys/modules/allwinner/aw_pwm/Makefile
  stable/12/sys/modules/pwm/pwmbus/Makefile
  stable/12/sys/modules/ti/Makefile
  stable/12/usr.sbin/pwm/pwm.8
  stable/12/usr.sbin/pwm/pwm.c
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/etc/mtree/BSD.include.dist
==============================================================================
--- stable/12/etc/mtree/BSD.include.dist	Wed Jun 26 17:21:30 2019	(r349429)
+++ stable/12/etc/mtree/BSD.include.dist	Wed Jun 26 17:28:55 2019	(r349430)
@@ -152,6 +152,8 @@
         ..
         ppbus
         ..
+        pwm
+        ..
         smbus
         ..
         speaker

Modified: stable/12/include/Makefile
==============================================================================
--- stable/12/include/Makefile	Wed Jun 26 17:21:30 2019	(r349429)
+++ stable/12/include/Makefile	Wed Jun 26 17:28:55 2019	(r349430)
@@ -46,8 +46,8 @@ LSUBDIRS=	cam/ata cam/mmc cam/nvme cam/scsi \
 	dev/acpica dev/agp dev/an dev/bktr dev/ciss dev/filemon dev/firewire \
 	dev/hwpmc dev/hyperv \
 	dev/ic dev/iicbus dev/io dev/mfi dev/mmc dev/nvme \
-	dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/smbus \
-	dev/speaker dev/tcp_log dev/veriexec dev/vkbd dev/wi \
+	dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/pwm \
+	dev/smbus dev/speaker dev/tcp_log dev/veriexec dev/vkbd dev/wi \
 	fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/nullfs \
 	fs/procfs fs/smbfs fs/udf fs/unionfs \
 	geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \

Modified: stable/12/share/man/man4/Makefile
==============================================================================
--- stable/12/share/man/man4/Makefile	Wed Jun 26 17:21:30 2019	(r349429)
+++ stable/12/share/man/man4/Makefile	Wed Jun 26 17:28:55 2019	(r349430)
@@ -428,6 +428,7 @@ MAN=	aac.4 \
 	pts.4 \
 	pty.4 \
 	puc.4 \
+	pwmc.4 \
 	${_qlxge.4} \
 	${_qlxgb.4} \
 	${_qlxgbe.4} \

Copied: stable/12/share/man/man4/pwmc.4 (from r349168, head/share/man/man4/pwmc.4)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/12/share/man/man4/pwmc.4	Wed Jun 26 17:28:55 2019	(r349430, copy of r349168, head/share/man/man4/pwmc.4)
@@ -0,0 +1,212 @@
+.\"
+.\" Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
+.\"
+.\" 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$
+.\"
+.Dd June 17, 2019
+.Dt PWMC 4
+.Os
+.Sh NAME
+.Nm pwmc
+.Nd PWM (Pulse Width Modulation) control device driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device pwmbus"
+.Cd "device pwmc"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+pwmc_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides device-control access to a channel of PWM hardware.
+Each instance of a
+.Nm
+device is associated with a single PWM output channel.
+.Pp
+Some PWM hardware is organized with multiple channels sharing a
+common clock or other resources.
+In such cases, a separate
+.Nm
+instance will exist for each channel, but changing the period or
+duty cycle of any one channel may affect other channels within the
+hardware which share the same resources.
+Consult the documentation for the underlying PWM hardware device driver
+for details on channels that share resources.
+.Pp
+An instance of
+.Nm
+creates a character device named
+.Pa /dev/pwm/pwmcX.Y
+where
+.Va X
+is a sequential number assigned to each PWM hardware controller
+as it is discovered by the system, and
+.Va Y
+is the channel number within that hardware controller.
+The driver can be configured to create aliases that point to the
+.Pa pwmcX.Y
+entries, in effect creating named channels.
+.Pp
+The
+.Nm
+driver provides control of a PWM channel with the following
+.Xr ioctl 2
+calls and data structures, defined in
+.In dev/pwm/pwmc.h :
+.Bl -tag -width indent
+.It Dv PWMGETSTATE Pq Vt "struct pwm_state"
+Retrieve the current state of the channel.
+.It Dv PWMSETSTATE Pq Vt "struct pwm_state"
+Set the current state of the channel.
+All parameters are updated on every call.
+To change just one of the values, use
+.Dv PWMGETSTATE
+to get the current state and then submit the same data back with
+just the appropriate value changed.
+.El
+.Pp
+The
+.Va pwm_state
+structure is defined as follows:
+.Bd -literal
+struct pwm_state {
+	u_int		period;
+	u_int		duty;
+	uint32_t	flags;
+	bool		enable;
+};
+.Ed
+.Pp
+.Bl -tag -width period
+.It Va period
+The duration, in nanoseconds, of one complete on-off cycle.
+.It Va duty
+The duration, in nanoseconds, of the on portion of one cycle.
+.It Va flags
+Flags that affect the output signal can be bitwise-ORed together.
+The following flags are currently defined:
+.Pp
+.Bl -tag -width PWM_POLARITY_INVERTED -compact
+.It Dv PWM_POLARITY_INVERTED
+Invert the signal polarity.
+.El
+.It Va enable
+.Va
+False to disable the output signal or true to enable it.
+.El
+.Sh HINTS CONFIGURATION
+On a
+.Xr device.hints 5
+based system, such as
+.Li MIPS ,
+these values are configurable for
+.Nm :
+.Bl -tag -width indent
+.It Va hint.pwmc.%d.at
+The pwmbus instance the
+.Nm
+instance is attached to.
+.It Va hint.pwmc.%d.channel
+The hardware channel number the instance is attached to.
+Channel numbers count up from zero.
+.It Va hint.pwmc.%d.label
+If this optional hint is set, the driver creates an alias in
+.Pa /dev/pwm
+with the given name, which points to the instance.
+.El
+.Sh FDT CONFIGURATION
+On an
+.Xr fdt 4
+based system, a
+.Nm
+device is described with a child node of the pwm hardware controller node.
+When the hardware supports multiple channels within the controller, it is
+not necessary to include a
+.Nm
+child node for every channel the hardware supports.
+Define only the channels you need to control.
+.Pp
+The following properties are required for a
+.Nm
+device node:
+.Bl -tag -width indent
+.It Va compatible
+Must be the string "freebsd,pwmc".
+.It Va reg
+The hardware channel number.
+.El
+.Pp
+The following properties are optional for the
+.Nm
+device node:
+.Bl -tag -width indent
+.It Va label
+A string containing only characters legal in a file name.
+The driver creates an alias with the given name in
+.Pa /dev/pwm
+which points to the instance's
+.Pa /dev/pwm/pwmcX.Y
+device entry.
+.El
+.Pp
+Example of a PWM hardware node containing one
+.Nm
+child node:
+.Bd -literal
+&ehrpwm0 {
+    status = "okay";
+    pinctrl-names = "default";
+    pinctrl-0 = <&ehrpwm0_AB_pins>;
+
+    pwmcontrol@0 {
+        compatible = "freebsd,pwmc";
+        reg = <0>;
+        label = "backlight";
+    };
+};
+.Ed
+.Sh FILES
+.Bl -tag -width -compact
+.It Pa /dev/pwm/pwmc*
+.El
+.Sh SEE ALSO
+.Xr fdt 4 ,
+.Xr device.hints 5 ,
+.Xr pwm 8 ,
+.Xr pwm 9
+.Sh HISTORY
+The
+.Nm
+driver
+appeared in
+.Fx 13.0 .

Modified: stable/12/share/man/man9/Makefile
==============================================================================
--- stable/12/share/man/man9/Makefile	Wed Jun 26 17:21:30 2019	(r349429)
+++ stable/12/share/man/man9/Makefile	Wed Jun 26 17:28:55 2019	(r349430)
@@ -269,7 +269,6 @@ MAN=	accept_filter.9 \
 	proc_rwmem.9 \
 	pseudofs.9 \
 	psignal.9 \
-	pwm.9 \
 	pwmbus.9 \
 	random.9 \
 	random_harvest.9 \
@@ -1671,6 +1670,7 @@ MLINKS+=proc_rwmem.9 proc_readmem.9 \
 MLINKS+=psignal.9 gsignal.9 \
 	psignal.9 pgsignal.9 \
 	psignal.9 tdsignal.9
+MLINKS+=pwmbus.9 pwm.9
 MLINKS+=random.9 arc4rand.9 \
 	random.9 arc4random.9 \
 	random.9 read_random.9 \

Modified: stable/12/share/man/man9/pwmbus.9
==============================================================================
--- stable/12/share/man/man9/pwmbus.9	Wed Jun 26 17:21:30 2019	(r349429)
+++ stable/12/share/man/man9/pwmbus.9	Wed Jun 26 17:28:55 2019	(r349430)
@@ -22,70 +22,85 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd November 12, 2018
+.Dd June 21, 2019
 .Dt PWMBUS 9
 .Os
 .Sh NAME
 .Nm pwmbus ,
-.Nm pwmbus_attach_bus ,
-.Nm PWMBUS_GET_BUS ,
 .Nm PWMBUS_CHANNEL_CONFIG ,
+.Nm PWMBUS_CHANNEL_COUNT ,
+.Nm PWMBUS_CHANNEL_ENABLE ,
 .Nm PWMBUS_CHANNEL_GET_CONFIG ,
-.Nm PWMBUS_CHANNEL_SET_FLAGS ,
 .Nm PWMBUS_CHANNEL_GET_FLAGS ,
-.Nm PWMBUS_CHANNEL_ENABLE ,
 .Nm PWMBUS_CHANNEL_IS_ENABLED ,
-.Nm PWMBUS_CHANNEL_MAX
+.Nm PWMBUS_CHANNEL_SET_FLAGS ,
+.Nm PWMBUS_GET_BUS
 .Nd PWMBUS methods
 .Sh SYNOPSIS
 .Cd "device pwm"
 .In "pwmbus_if.h"
-.Ft device_t
-.Fn pwmbus_attach_bus "device_t dev"
 .Ft int
 .Fn PWMBUS_CHANNEL_CONFIG "device_t bus" "int channel" "uint64_t period" "uint64_t duty"
 .Ft int
-.Fn PWMBUS_CHANNEL_GET_CONFIG "device_t bus" "int channel" "uint64_t *period" "uint64_t *duty"
+.Fn PWMBUS_CHANNEL_COUNT "device_t bus" "int channel" "int *nchannel"
 .Ft int
-.Fn PWMBUS_CHANNEL_SET_FLAGS "device_t bus" "int channel" "uint32_t flags"
+.Fn PWMBUS_CHANNEL_ENABLE "device_t bus" "int channel" "bool enable"
 .Ft int
+.Fn PWMBUS_CHANNEL_GET_CONFIG "device_t bus" "int channel" "uint64_t *period" "uint64_t *duty"
+.Ft int
 .Fn PWMBUS_CHANNEL_GET_FLAGS "device_t bus" "int channel" "uint32_t *flags"
 .Ft int
-.Fn PWMBUS_CHANNEL_ENABLE "device_t bus" "int channel" "bool enable"
-.Ft int
 .Fn PWMBUS_CHANNEL_IS_ENABLED "device_t bus" "int channel" "bool *enabled"
 .Ft int
-.Fn PWMBUS_CHANNEL_MAX "device_t bus" "int channel" "int *nchannel"
+.Fn PWMBUS_CHANNEL_SET_FLAGS "device_t bus" "int channel" "uint32_t flags"
 .Sh DESCRIPTION
-The PWMBUS (Pulse-Width Modulation) interface allows the device driver to register to a global
-bus so other devices in the kernel can use them in a generic way
+The PWMBUS (Pulse-Width Modulation) interface allows a device driver to
+register to a global bus so other devices in the kernel can use them in a
+generic way.
+.Pp
+For all
+.Nm
+methods, the
+.Va period
+argument is the duration in nanoseconds of one complete on-off cycle, and the
+.Va duty
+argument is the duration in nanoseconds of the on portion of that cycle.
+.Pp
+Some PWM hardware is organized as a single controller with multiple channels.
+Channel numbers count up from zero.
+When multiple channels are present, they sometimes share a common clock or 
+other resources.
+In such cases, changing the period or duty cycle of any one channel may affect 
+other channels within the hardware which share the same resources.
+Consult the documentation for the underlying PWM hardware device driver for 
+details on channels that share resources.
 .Sh INTERFACE
 .Bl -tag -width indent
-.It Fn pwmbus_attach_bus "device_t dev"
-Attach the
-.Nm pwmbus
-to the device driver
 .It Fn PWMBUS_CHANNEL_CONFIG "device_t bus" "int channel" "uint64_t period" "uint64_t duty"
-Configure the period and duty (in nanoseconds) in the PWM controller on the bus for the specified channel.
+Configure the period and duty (in nanoseconds) in the PWM controller on the bus
+for the specified channel.
 Returns 0 on success or
 .Er EINVAL
-is the values are not supported by the controller or
+if the values are not supported by the controller or
 .Er EBUSY
-is the PWMBUS controller is in use and doesn't support changing the value on the fly.
+if the PWMBUS controller is in use and does not support changing the value on
+the fly.
+.It Fn PWMBUS_CHANNEL_COUNT "device_t bus" "int *nchannel"
+Get the number of channels supported by the controller.
+.It Fn PWMBUS_CHANNEL_ENABLE "device_t bus" "int channel" "bool enable"
+Enable the PWM channel.
 .It Fn PWMBUS_CHANNEL_GET_CONFIG "device_t bus" "int channel" "uint64_t *period" "uint64_t *duty"
 Get the current configuration of the period and duty for the specified channel.
-.It Fn PWMBUS_CHANNEL_SET_FLAGS "device_t bus" "int channel" "uint32_t flags"
-Set the flags of the channel (like inverted polarity), if the driver or controller
-doesn't support this a default method is used.
 .It Fn PWMBUS_CHANNEL_GET_FLAGS "device_t bus" "int channel" "uint32_t *flags"
-Get the current flags for the channel, if the driver or controller
-doesn't support this, a default method is used.
-.It Fn PWMBUS_CHANNEL_ENABLE "device_t bus" "int channel" "bool enable"
-Enable the PWM channel.
-.It Fn PWMBUS_CHANNEL_ISENABLED "device_t bus" "int channel" "bool *enable"
-Test if the PWM channel is enabled.
-.It PWMBUS_CHANNEL_MAX "device_t bus" "int channel" "int *nchannel"
-Get the maximum number of channel supported by the controller.
+Get the current flags for the channel.
+If the driver or controller
+does not support this, a default method returns a flags value of zero.
+.It Fn PWMBUS_CHANNEL_IS_ENABLED "device_t bus" "int channel" "bool *enable"
+Test whether the PWM channel is enabled.
+.It Fn PWMBUS_CHANNEL_SET_FLAGS "device_t bus" "int channel" "uint32_t flags"
+Set the flags of the channel (such as inverted polarity).
+If the driver or controller does not support this a do-nothing default method
+is used.
 .El
 .Sh HISTORY
 The

Modified: stable/12/sys/arm/allwinner/aw_pwm.c
==============================================================================
--- stable/12/sys/arm/allwinner/aw_pwm.c	Wed Jun 26 17:21:30 2019	(r349429)
+++ stable/12/sys/arm/allwinner/aw_pwm.c	Wed Jun 26 17:28:55 2019	(r349430)
@@ -44,10 +44,8 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/extres/clk/clk.h>
 
-#include <dev/pwm/pwmbus.h>
+#include "pwmbus_if.h"
 
-#include "pwm_if.h"
-
 #define	AW_PWM_CTRL			0x00
 #define	 AW_PWM_CTRL_PRESCALE_MASK	0xF
 #define	 AW_PWM_CTRL_EN			(1 << 4)
@@ -138,6 +136,7 @@ aw_pwm_attach(device_t dev)
 	struct aw_pwm_softc *sc;
 	uint64_t clk_freq;
 	uint32_t reg;
+	phandle_t node;
 	int error;
 
 	sc = device_get_softc(dev);
@@ -149,8 +148,16 @@ aw_pwm_attach(device_t dev)
 		goto fail;
 	}
 	error = clk_enable(sc->clk);
+	if (error != 0) {
+		device_printf(dev, "cannot enable clock\n");
+		goto fail;
+	}
 
 	error = clk_get_freq(sc->clk, &sc->clk_freq);
+	if (error != 0) {
+		device_printf(dev, "cannot get clock frequency\n");
+		goto fail;
+	}
 
 	if (bus_alloc_resources(dev, aw_pwm_spec, &sc->res) != 0) {
 		device_printf(dev, "cannot allocate resources for device\n");
@@ -158,9 +165,6 @@ aw_pwm_attach(device_t dev)
 		goto fail;
 	}
 
-	if ((sc->busdev = pwmbus_attach_bus(dev)) == NULL)
-		device_printf(dev, "Cannot attach pwm bus\n");
-
 	/* Read the configuration left by U-Boot */
 	reg = AW_PWM_READ(sc, AW_PWM_CTRL);
 	if (reg & (AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN))
@@ -170,7 +174,7 @@ aw_pwm_attach(device_t dev)
 	reg &= AW_PWM_CTRL_PRESCALE_MASK;
 	if (reg > nitems(aw_pwm_clk_prescaler)) {
 		device_printf(dev, "Bad prescaler %x, cannot guess current settings\n", reg);
-		goto out;
+		goto skipcfg;
 	}
 	clk_freq = sc->clk_freq / aw_pwm_clk_prescaler[reg];
 
@@ -180,9 +184,18 @@ aw_pwm_attach(device_t dev)
 	sc->duty = NS_PER_SEC /
 		(clk_freq / ((reg >> AW_PWM_PERIOD_ACTIVE_SHIFT) & AW_PWM_PERIOD_ACTIVE_MASK));
 
-out:
-	return (0);
+skipcfg:
+	/*
+	 * Note that we don't check for failure to attach pwmbus -- even without
+	 * it we can still service clients who connect via fdt xref data.
+	 */
+	node = ofw_bus_get_node(dev);
+	OF_device_register_xref(OF_xref_from_node(node), dev);
 
+	sc->busdev = device_add_child(dev, "pwmbus", -1);
+
+	return (bus_generic_attach(dev));
+
 fail:
 	aw_pwm_detach(dev);
 	return (error);
@@ -192,18 +205,37 @@ static int
 aw_pwm_detach(device_t dev)
 {
 	struct aw_pwm_softc *sc;
+	int error;
 
 	sc = device_get_softc(dev);
 
-	bus_generic_detach(sc->dev);
+	if ((error = bus_generic_detach(sc->dev)) != 0) {
+		device_printf(sc->dev, "cannot detach child devices\n");
+		return (error);
+	}
 
-	bus_release_resources(dev, aw_pwm_spec, &sc->res);
+	if (sc->busdev != NULL)
+		device_delete_child(dev, sc->busdev);
 
+	if (sc->res != NULL)
+		bus_release_resources(dev, aw_pwm_spec, &sc->res);
+
 	return (0);
 }
 
+static phandle_t
+aw_pwm_get_node(device_t bus, device_t dev)
+{
+
+	/*
+	 * Share our controller node with our pwmbus child; it instantiates
+	 * devices by walking the children contained within our node.
+	 */
+	return ofw_bus_get_node(bus);
+}
+
 static int
-aw_pwm_channel_max(device_t dev, int *nchannel)
+aw_pwm_channel_count(device_t dev, u_int *nchannel)
 {
 
 	*nchannel = 1;
@@ -212,7 +244,7 @@ aw_pwm_channel_max(device_t dev, int *nchannel)
 }
 
 static int
-aw_pwm_channel_config(device_t dev, int channel, unsigned int period, unsigned int duty)
+aw_pwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
 {
 	struct aw_pwm_softc *sc;
 	uint64_t period_freq, duty_freq;
@@ -275,7 +307,7 @@ aw_pwm_channel_config(device_t dev, int channel, unsig
 }
 
 static int
-aw_pwm_channel_get_config(device_t dev, int channel, unsigned int *period, unsigned int *duty)
+aw_pwm_channel_get_config(device_t dev, u_int channel, u_int *period, u_int *duty)
 {
 	struct aw_pwm_softc *sc;
 
@@ -288,7 +320,7 @@ aw_pwm_channel_get_config(device_t dev, int channel, u
 }
 
 static int
-aw_pwm_channel_enable(device_t dev, int channel, bool enable)
+aw_pwm_channel_enable(device_t dev, u_int channel, bool enable)
 {
 	struct aw_pwm_softc *sc;
 	uint32_t reg;
@@ -312,7 +344,7 @@ aw_pwm_channel_enable(device_t dev, int channel, bool 
 }
 
 static int
-aw_pwm_channel_is_enabled(device_t dev, int channel, bool *enabled)
+aw_pwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
 {
 	struct aw_pwm_softc *sc;
 
@@ -323,29 +355,22 @@ aw_pwm_channel_is_enabled(device_t dev, int channel, b
 	return (0);
 }
 
-static device_t
-aw_pwm_get_bus(device_t dev)
-{
-	struct aw_pwm_softc *sc;
-
-	sc = device_get_softc(dev);
-
-	return (sc->busdev);
-}
 static device_method_t aw_pwm_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		aw_pwm_probe),
 	DEVMETHOD(device_attach,	aw_pwm_attach),
 	DEVMETHOD(device_detach,	aw_pwm_detach),
 
-	/* pwm interface */
-	DEVMETHOD(pwm_get_bus,			aw_pwm_get_bus),
-	DEVMETHOD(pwm_channel_max,		aw_pwm_channel_max),
-	DEVMETHOD(pwm_channel_config,		aw_pwm_channel_config),
-	DEVMETHOD(pwm_channel_get_config,	aw_pwm_channel_get_config),
-	DEVMETHOD(pwm_channel_enable,		aw_pwm_channel_enable),
-	DEVMETHOD(pwm_channel_is_enabled,	aw_pwm_channel_is_enabled),
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_node,	aw_pwm_get_node),
 
+	/* pwmbus interface */
+	DEVMETHOD(pwmbus_channel_count,		aw_pwm_channel_count),
+	DEVMETHOD(pwmbus_channel_config,	aw_pwm_channel_config),
+	DEVMETHOD(pwmbus_channel_get_config,	aw_pwm_channel_get_config),
+	DEVMETHOD(pwmbus_channel_enable,	aw_pwm_channel_enable),
+	DEVMETHOD(pwmbus_channel_is_enabled,	aw_pwm_channel_is_enabled),
+
 	DEVMETHOD_END
 };
 
@@ -358,4 +383,5 @@ static driver_t aw_pwm_driver = {
 static devclass_t aw_pwm_devclass;
 
 DRIVER_MODULE(aw_pwm, simplebus, aw_pwm_driver, aw_pwm_devclass, 0, 0);
+MODULE_VERSION(aw_pwm, 1);
 SIMPLEBUS_PNP_INFO(compat_data);

Modified: stable/12/sys/arm/conf/GENERIC
==============================================================================
--- stable/12/sys/arm/conf/GENERIC	Wed Jun 26 17:21:30 2019	(r349429)
+++ stable/12/sys/arm/conf/GENERIC	Wed Jun 26 17:28:55 2019	(r349430)
@@ -174,6 +174,9 @@ device		ti_spi
 # ADC support
 device		ti_adc
 
+# PWM
+device		pwm
+
 # Watchdog support
 # If we don't enable the watchdog driver, the BealeBone could potentially
 # reboot automatically because the boot loader might have enabled the
@@ -266,4 +269,16 @@ device		imx6_snvs		# On-chip RTC
 
 # Flattened Device Tree
 options 	FDT			# Configure using FDT/DTB data
-makeoptions	MODULES_EXTRA="dtb/allwinner dtb/am335x dtb/imx6 dtb/nvidia dtb/rpi dtb/zynq dtb/omap4"
+makeoptions	MODULES_EXTRA+="dtb/allwinner"
+makeoptions	MODULES_EXTRA+="dtb/am335x"
+makeoptions	MODULES_EXTRA+="dtb/imx6"
+makeoptions	MODULES_EXTRA+="dtb/nvidia"
+makeoptions	MODULES_EXTRA+="dtb/omap4"
+makeoptions	MODULES_EXTRA+="dtb/rpi"
+makeoptions	MODULES_EXTRA+="dtb/zynq"
+
+# SOC-specific modules
+makeoptions	MODULES_EXTRA+="allwinner"
+makeoptions	MODULES_EXTRA+="arm_ti"
+makeoptions	MODULES_EXTRA+="imx"
+

Modified: stable/12/sys/arm/ti/am335x/am335x_dmtpps.c
==============================================================================
--- stable/12/sys/arm/ti/am335x/am335x_dmtpps.c	Wed Jun 26 17:21:30 2019	(r349429)
+++ stable/12/sys/arm/ti/am335x/am335x_dmtpps.c	Wed Jun 26 17:28:55 2019	(r349430)
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/kernel.h>
 #include <sys/module.h>
 #include <sys/malloc.h>
+#include <sys/mutex.h>
 #include <sys/rman.h>
 #include <sys/taskqueue.h>
 #include <sys/timepps.h>

Modified: stable/12/sys/arm/ti/am335x/am335x_ehrpwm.c
==============================================================================
--- stable/12/sys/arm/ti/am335x/am335x_ehrpwm.c	Wed Jun 26 17:21:30 2019	(r349429)
+++ stable/12/sys/arm/ti/am335x/am335x_ehrpwm.c	Wed Jun 26 17:28:55 2019	(r349430)
@@ -45,21 +45,43 @@ __FBSDID("$FreeBSD$");
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
+#include "pwmbus_if.h"
+
 #include "am335x_pwm.h"
 
+/*******************************************************************************
+ * Enhanced resolution PWM driver.  Many of the advanced featues of the hardware
+ * are not supported by this driver.  What is implemented here is simple
+ * variable-duty-cycle PWM output.
+ *
+ * Note that this driver was historically configured using a set of sysctl
+ * variables/procs, and later gained support for the PWM(9) API.  The sysctl
+ * code is still present to support existing apps, but that interface is
+ * considered deprecated.
+ *
+ * An important caveat is that the original sysctl interface and the new PWM API
+ * cannot both be used at once.  If both interfaces are used to change
+ * configuration, it's quite likely you won't get the expected results.  Also,
+ * reading the sysctl values after configuring via PWM will not return the right
+ * results.
+ ******************************************************************************/
+
 /* In ticks */
 #define	DEFAULT_PWM_PERIOD	1000
 #define	PWM_CLOCK		100000000UL
 
+#define	NS_PER_SEC		1000000000
+
 #define	PWM_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
 #define	PWM_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
+#define	PWM_LOCK_ASSERT(_sc)    mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
 #define	PWM_LOCK_INIT(_sc)	mtx_init(&(_sc)->sc_mtx, \
     device_get_nameunit(_sc->sc_dev), "am335x_ehrpwm softc", MTX_DEF)
 #define	PWM_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_mtx)
 
-#define	EPWM_READ2(_sc, reg)	bus_read_2((_sc)->sc_mem_res, reg);
+#define	EPWM_READ2(_sc, reg)	bus_read_2((_sc)->sc_mem_res, reg)
 #define	EPWM_WRITE2(_sc, reg, value)	\
-    bus_write_2((_sc)->sc_mem_res, reg, value);
+    bus_write_2((_sc)->sc_mem_res, reg, value)
 
 #define	EPWM_TBCTL		0x00
 #define		TBCTL_FREERUN		(2 << 14)
@@ -119,6 +141,11 @@ __FBSDID("$FreeBSD$");
 #define		AQCTL_ZRO_TOGGLE	(3 << 0)
 #define	EPWM_AQSFRC		0x1a
 #define	EPWM_AQCSFRC		0x1c
+#define		AQCSFRC_OFF		0
+#define		AQCSFRC_LO		1
+#define		AQCSFRC_HI		2
+#define		AQCSFRC_MASK		3
+#define		AQCSFRC(chan, hilo)	((hilo) << (2 * chan))
 
 /* Trip-Zone module */
 #define	EPWM_TZCTL		0x28
@@ -135,12 +162,21 @@ static device_detach_t am335x_ehrpwm_detach;
 
 static int am335x_ehrpwm_clkdiv[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
 
+struct ehrpwm_channel {
+	u_int	duty;		/* on duration, in ns */
+	bool	enabled;	/* channel enabled? */
+	bool	inverted;	/* signal inverted? */
+};
+#define	NUM_CHANNELS	2
+
 struct am335x_ehrpwm_softc {
 	device_t		sc_dev;
+	device_t		sc_busdev;
 	struct mtx		sc_mtx;
 	struct resource		*sc_mem_res;
 	int			sc_mem_rid;
-	/* sysctl for configuration */
+
+	/* Things used for configuration via sysctl [deprecated]. */
 	int			sc_pwm_clkdiv;
 	int			sc_pwm_freq;
 	struct sysctl_oid	*sc_clkdiv_oid;
@@ -151,25 +187,132 @@ struct am335x_ehrpwm_softc {
 	uint32_t		sc_pwm_period;
 	uint32_t		sc_pwm_dutyA;
 	uint32_t		sc_pwm_dutyB;
-};
 
-static device_method_t am335x_ehrpwm_methods[] = {
-	DEVMETHOD(device_probe,		am335x_ehrpwm_probe),
-	DEVMETHOD(device_attach,	am335x_ehrpwm_attach),
-	DEVMETHOD(device_detach,	am335x_ehrpwm_detach),
-
-	DEVMETHOD_END
+	/* Things used for configuration via pwm(9) api. */
+	u_int			sc_clkfreq; /* frequency in Hz */
+	u_int			sc_clktick; /* duration in ns */
+	u_int			sc_period;  /* duration in ns */
+	struct ehrpwm_channel	sc_channels[NUM_CHANNELS];
 };
 
-static driver_t am335x_ehrpwm_driver = {
-	"am335x_ehrpwm",
-	am335x_ehrpwm_methods,
-	sizeof(struct am335x_ehrpwm_softc),
+static struct ofw_compat_data compat_data[] = {
+	{"ti,am33xx-ehrpwm",    true},
+	{NULL,                  false},
 };
+SIMPLEBUS_PNP_INFO(compat_data);
 
-static devclass_t am335x_ehrpwm_devclass;
+static void
+am335x_ehrpwm_cfg_duty(struct am335x_ehrpwm_softc *sc, u_int chan, u_int duty)
+{
+	u_int tbcmp;
 
+	if (duty == 0)
+		tbcmp = 0;
+	else
+		tbcmp = max(1, duty / sc->sc_clktick);
+
+	sc->sc_channels[chan].duty = tbcmp * sc->sc_clktick;
+
+	PWM_LOCK_ASSERT(sc);
+	EPWM_WRITE2(sc, (chan == 0) ? EPWM_CMPA : EPWM_CMPB, tbcmp);
+}
+
 static void
+am335x_ehrpwm_cfg_enable(struct am335x_ehrpwm_softc *sc, u_int chan, bool enable)
+{
+	uint16_t regval;
+
+	sc->sc_channels[chan].enabled = enable;
+
+	/*
+	 * Turn off any existing software-force of the channel, then force
+	 * it in the right direction (high or low) if it's not being enabled.
+	 */
+	PWM_LOCK_ASSERT(sc);
+	regval = EPWM_READ2(sc, EPWM_AQCSFRC);
+	regval &= ~AQCSFRC(chan, AQCSFRC_MASK);
+	if (!sc->sc_channels[chan].enabled) {
+		if (sc->sc_channels[chan].inverted)
+			regval |= AQCSFRC(chan, AQCSFRC_HI);
+		else
+			regval |= AQCSFRC(chan, AQCSFRC_LO);
+	}
+	EPWM_WRITE2(sc, EPWM_AQCSFRC, regval);
+}
+
+static bool
+am335x_ehrpwm_cfg_period(struct am335x_ehrpwm_softc *sc, u_int period)
+{
+	uint16_t regval;
+	u_int clkdiv, hspclkdiv, pwmclk, pwmtick, tbprd;
+
+	/* Can't do a period shorter than 2 clock ticks. */
+	if (period < 2 * NS_PER_SEC / PWM_CLOCK) {
+		sc->sc_clkfreq = 0;
+		sc->sc_clktick = 0;
+		sc->sc_period  = 0;
+		return (false);
+	}
+
+	/*
+	 * Figure out how much we have to divide down the base 100MHz clock so
+	 * that we can express the requested period as a 16-bit tick count.
+	 */
+	tbprd = 0;
+	for (clkdiv = 0; clkdiv < 8; ++clkdiv) {
+		const u_int cd = 1 << clkdiv;
+		for (hspclkdiv = 0; hspclkdiv < 8; ++hspclkdiv) {
+			const u_int cdhs = max(1, hspclkdiv * 2);
+			pwmclk = PWM_CLOCK / (cd * cdhs);
+			pwmtick = NS_PER_SEC / pwmclk;
+			if (period / pwmtick < 65536) {
+				tbprd = period / pwmtick;
+				break;
+			}
+		}
+		if (tbprd != 0)
+			break;
+	}
+
+	/* Handle requested period too long for available clock divisors. */
+	if (tbprd == 0)
+		return (false);
+
+	/*
+	 * If anything has changed from the current settings, reprogram the
+	 * clock divisors and period register.
+	 */
+	if (sc->sc_clkfreq != pwmclk || sc->sc_clktick != pwmtick ||
+	    sc->sc_period != tbprd * pwmtick) {
+
+		sc->sc_clkfreq = pwmclk;
+		sc->sc_clktick = pwmtick;
+		sc->sc_period  = tbprd * pwmtick;
+	
+		PWM_LOCK_ASSERT(sc);
+		regval = EPWM_READ2(sc, EPWM_TBCTL);
+		regval &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
+		regval |= TBCTL_CLKDIV(clkdiv) | TBCTL_HSPCLKDIV(hspclkdiv);
+		EPWM_WRITE2(sc, EPWM_TBCTL, regval);
+		EPWM_WRITE2(sc, EPWM_TBPRD, tbprd - 1);
+#if 0
+		device_printf(sc->sc_dev, "clkdiv %u hspclkdiv %u tbprd %u "
+		    "clkfreq %u Hz clktick %u ns period got %u requested %u\n",
+		    clkdiv, hspclkdiv, tbprd - 1,
+		    sc->sc_clkfreq, sc->sc_clktick, sc->sc_period, period);
+#endif
+		/*
+		 * If the period changed, that invalidates the current CMP
+		 * registers (duty values), just zero them out.
+		 */
+		am335x_ehrpwm_cfg_duty(sc, 0, 0);
+		am335x_ehrpwm_cfg_duty(sc, 1, 0);
+	}
+
+	return (true);
+}
+
+static void
 am335x_ehrpwm_freq(struct am335x_ehrpwm_softc *sc)
 {
 	int clkdiv;
@@ -331,13 +474,89 @@ am335x_ehrpwm_sysctl_period(SYSCTL_HANDLER_ARGS)
 }
 
 static int
+am335x_ehrpwm_channel_count(device_t dev, u_int *nchannel)
+{
+
+	*nchannel = NUM_CHANNELS;
+
+	return (0);
+}
+
+static int
+am335x_ehrpwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
+{
+	struct am335x_ehrpwm_softc *sc;
+	bool status;
+
+	if (channel >= NUM_CHANNELS)
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+
+	PWM_LOCK(sc);
+	status = am335x_ehrpwm_cfg_period(sc, period);
+	if (status)
+		am335x_ehrpwm_cfg_duty(sc, channel, duty);
+	PWM_UNLOCK(sc);
+
+	return (status ? 0 : EINVAL);
+}
+
+static int
+am335x_ehrpwm_channel_get_config(device_t dev, u_int channel, 
+    u_int *period, u_int *duty)
+{
+	struct am335x_ehrpwm_softc *sc;
+
+	if (channel >= NUM_CHANNELS)
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	*period = sc->sc_period;
+	*duty = sc->sc_channels[channel].duty;
+	return (0);
+}
+
+static int
+am335x_ehrpwm_channel_enable(device_t dev, u_int channel, bool enable)
+{
+	struct am335x_ehrpwm_softc *sc;
+
+	if (channel >= NUM_CHANNELS)
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+
+	PWM_LOCK(sc);
+	am335x_ehrpwm_cfg_enable(sc, channel, enable);
+	PWM_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+am335x_ehrpwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
+{
+	struct am335x_ehrpwm_softc *sc;
+
+	if (channel >= NUM_CHANNELS)
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+
+	*enabled = sc->sc_channels[channel].enabled;
+
+	return (0);
+}
+
+static int
 am335x_ehrpwm_probe(device_t dev)
 {
 
 	if (!ofw_bus_status_okay(dev))
 		return (ENXIO);
 
-	if (!ofw_bus_is_compatible(dev, "ti,am33xx-ehrpwm"))
+	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
 		return (ENXIO);
 
 	device_set_desc(dev, "AM335x EHRPWM");
@@ -365,7 +584,7 @@ am335x_ehrpwm_attach(device_t dev)
 		goto fail;
 	}
 
-	/* Init backlight interface */
+	/* Init sysctl interface */
 	ctx = device_get_sysctl_ctx(sc->sc_dev);
 	tree = device_get_sysctl_tree(sc->sc_dev);
 
@@ -414,7 +633,13 @@ am335x_ehrpwm_attach(device_t dev)
 	EPWM_WRITE2(sc, EPWM_TZCTL, 0xf);
 	reg = EPWM_READ2(sc, EPWM_TZFLG);
 
-	return (0);
+	if ((sc->sc_busdev = device_add_child(dev, "pwmbus", -1)) == NULL) {
+		device_printf(dev, "Cannot add child pwmbus\n");
+		// This driver can still do things even without the bus child.
+	}
+
+	bus_generic_probe(dev);
+	return (bus_generic_attach(dev));
 fail:
 	PWM_LOCK_DESTROY(sc);
 	if (sc->sc_mem_res)
@@ -428,13 +653,22 @@ static int
 am335x_ehrpwm_detach(device_t dev)
 {
 	struct am335x_ehrpwm_softc *sc;
+	int error;
 
 	sc = device_get_softc(dev);
 
+	if ((error = bus_generic_detach(sc->sc_dev)) != 0)
+		return (error);
+
 	PWM_LOCK(sc);
+
+	if (sc->sc_busdev != NULL)
+		device_delete_child(dev, sc->sc_busdev);
+
 	if (sc->sc_mem_res)
 		bus_release_resource(dev, SYS_RES_MEMORY,
 		    sc->sc_mem_rid, sc->sc_mem_res);
+
 	PWM_UNLOCK(sc);
 
 	PWM_LOCK_DESTROY(sc);
@@ -442,6 +676,44 @@ am335x_ehrpwm_detach(device_t dev)
 	return (0);
 }
 
+static phandle_t

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906261728.x5QHSuNc078844>