svn commit: r342004 - in head/sys: arm/allwinner arm64/conf conf

Emmanuel Vadot manu at FreeBSD.org
Wed Dec 12 20:58:44 UTC 2018


Author: manu
Date: Wed Dec 12 20:58:43 2018
New Revision: 342004
URL: https://svnweb.freebsd.org/changeset/base/342004

Log:
  arm64: allwinner: Add pwm driver
  
  Add a pwm driver for Allwinner PWM
  Add pwm and aw_pwm to the GENERIC kernel

Added:
  head/sys/arm/allwinner/aw_pwm.c   (contents, props changed)
Modified:
  head/sys/arm64/conf/GENERIC
  head/sys/conf/files.arm64

Added: head/sys/arm/allwinner/aw_pwm.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/allwinner/aw_pwm.c	Wed Dec 12 20:58:43 2018	(r342004)
@@ -0,0 +1,340 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Emmanuel Vadot <manu at 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$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dev/pwm/pwmbus.h>
+
+#include "pwm_if.h"
+
+#define	AW_PWM_CTRL			0x00
+#define	 AW_PWM_CTRL_PRESCALE_MASK	0xF
+#define	 AW_PWM_CTRL_EN			(1 << 4)
+#define	 AW_PWM_CTRL_ACTIVE_LEVEL_HIGH	(1 << 5)
+#define	 AW_PWM_CTRL_GATE		(1 << 6)
+#define	 AW_PWM_CTRL_MODE_MASK		0x80
+#define	 AW_PWM_CTRL_PULSE_MODE		(1 << 7)
+#define	 AW_PWM_CTRL_CYCLE_MODE		(0 << 7)
+#define	 AW_PWM_CTRL_PULSE_START	(1 << 8)
+#define	 AW_PWM_CTRL_CLK_BYPASS		(1 << 9)
+#define	 AW_PWM_CTRL_PERIOD_BUSY	(1 << 28)
+
+#define	AW_PWM_PERIOD			0x04
+#define	AW_PWM_PERIOD_TOTAL_MASK	0xFFFF
+#define	AW_PWM_PERIOD_TOTAL_SHIFT	16
+#define	AW_PWM_PERIOD_ACTIVE_MASK	0xFFFF
+#define	AW_PWM_PERIOD_ACTIVE_SHIFT	0
+
+#define	AW_PWM_MAX_FREQ			24000000
+
+#define	NS_PER_SEC	1000000000
+
+static struct ofw_compat_data compat_data[] = {
+	{ "allwinner,sun5i-a13-pwm",		1 },
+	{ NULL,					0 }
+};
+
+static struct resource_spec aw_pwm_spec[] = {
+	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
+	{ -1, 0 }
+};
+
+struct aw_pwm_softc {
+	device_t	dev;
+	device_t	busdev;
+	clk_t		clk;
+	struct resource	*res;
+
+	uint64_t	clk_freq;
+	uint64_t	period;
+	uint64_t	duty;
+	uint32_t	flags;
+	bool		enabled;
+};
+
+static uint32_t aw_pwm_clk_prescaler[] = {
+	120,
+	180,
+	240,
+	360,
+	480,
+	0,
+	0,
+	0,
+	12000,
+	24000,
+	36000,
+	48000,
+	72000,
+	0,
+	0,
+	1,
+};
+
+#define	AW_PWM_READ(sc, reg)		bus_read_4((sc)->res, (reg))
+#define	AW_PWM_WRITE(sc, reg, val)	bus_write_4((sc)->res, (reg), (val))
+
+static int aw_pwm_probe(device_t dev);
+static int aw_pwm_attach(device_t dev);
+static int aw_pwm_detach(device_t dev);
+
+static int
+aw_pwm_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, "Allwinner PWM");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_pwm_attach(device_t dev)
+{
+	struct aw_pwm_softc *sc;
+	/* uint32_t reg; */
+	int error;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+	if (error != 0) {
+		device_printf(dev, "cannot get clock\n");
+		goto fail;
+	}
+	error = clk_enable(sc->clk);
+
+	error = clk_get_freq(sc->clk, &sc->clk_freq);
+
+	if (bus_alloc_resources(dev, aw_pwm_spec, &sc->res) != 0) {
+		device_printf(dev, "cannot allocate resources for device\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	if ((sc->busdev = pwmbus_attach_bus(dev)) == NULL)
+		device_printf(dev, "Cannot attach pwm bus\n");
+
+	return (0);
+
+fail:
+	aw_pwm_detach(dev);
+	return (error);
+}
+
+static int
+aw_pwm_detach(device_t dev)
+{
+	struct aw_pwm_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	bus_generic_detach(sc->dev);
+
+	bus_release_resources(dev, aw_pwm_spec, &sc->res);
+
+	return (0);
+}
+
+static int
+aw_pwm_channel_max(device_t dev, int *nchannel)
+{
+
+	*nchannel = 1;
+
+	return (0);
+}
+
+static int
+aw_pwm_channel_config(device_t dev, int channel, uint64_t period, uint64_t duty)
+{
+	struct aw_pwm_softc *sc;
+	uint64_t period_freq, duty_freq;
+	uint64_t clk_rate, div;
+	uint32_t reg;
+	int prescaler;
+	int i;
+
+	sc = device_get_softc(dev);
+
+	period_freq = NS_PER_SEC / period;
+	if (period_freq > AW_PWM_MAX_FREQ)
+		return (EINVAL);
+	duty_freq = NS_PER_SEC / duty;
+	if (duty_freq < period_freq) {
+		device_printf(sc->dev, "duty < period\n");
+		return (EINVAL);
+	}
+
+	/* First test without prescaler */
+	clk_rate = AW_PWM_MAX_FREQ;
+	prescaler = AW_PWM_CTRL_PRESCALE_MASK;
+	div = AW_PWM_MAX_FREQ / period_freq;
+	if ((div - 1) > AW_PWM_PERIOD_TOTAL_MASK) {
+		/* Test all prescaler */
+		for (i = 0; i < nitems(aw_pwm_clk_prescaler); i++) {
+			if (aw_pwm_clk_prescaler[i] == 0)
+				continue;
+			div = (AW_PWM_MAX_FREQ * aw_pwm_clk_prescaler[i]) / period_freq;
+			if ((div - 1) < AW_PWM_PERIOD_TOTAL_MASK ) {
+				prescaler = i;
+				clk_rate = AW_PWM_MAX_FREQ / aw_pwm_clk_prescaler[i];
+				break;
+			}
+		}
+		if (prescaler == AW_PWM_CTRL_PRESCALE_MASK)
+			return (EINVAL);
+	}
+
+	reg = AW_PWM_READ(sc, AW_PWM_CTRL);
+	if (reg & AW_PWM_CTRL_PERIOD_BUSY) {
+		device_printf(sc->dev, "pwm busy\n");
+		return (EBUSY);
+	}
+
+	/* Write the prescalar */
+	reg &= ~AW_PWM_CTRL_PRESCALE_MASK;
+	reg |= prescaler;
+	AW_PWM_WRITE(sc, AW_PWM_CTRL, reg);
+
+	/* Write the total/active cycles */
+	reg = ((clk_rate / period_freq) << AW_PWM_PERIOD_TOTAL_SHIFT) |
+	  ((clk_rate / duty_freq) << AW_PWM_PERIOD_ACTIVE_SHIFT);
+	AW_PWM_WRITE(sc, AW_PWM_PERIOD, reg);
+
+	sc->period = period;
+	sc->duty = duty;
+
+	return (0);
+}
+
+static int
+aw_pwm_channel_get_config(device_t dev, int channel, uint64_t *period, uint64_t *duty)
+{
+	struct aw_pwm_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	*period = sc->period;
+	*duty = sc->duty;
+
+	return (0);
+}
+
+static int
+aw_pwm_channel_enable(device_t dev, int channel, bool enable)
+{
+	struct aw_pwm_softc *sc;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+
+	if (enable && sc->enabled)
+		return (0);
+
+	reg = AW_PWM_READ(sc, AW_PWM_CTRL);
+	if (enable)
+		reg |= AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN;
+	else
+		reg &= ~(AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN);
+
+	AW_PWM_WRITE(sc, AW_PWM_CTRL, reg);
+
+	sc->enabled = enable;
+
+	return (0);
+}
+
+static int
+aw_pwm_channel_is_enabled(device_t dev, int channel, bool *enabled)
+{
+	struct aw_pwm_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	*enabled = sc->enabled;
+
+	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),
+
+	DEVMETHOD_END
+};
+
+static driver_t aw_pwm_driver = {
+	"pwm",
+	aw_pwm_methods,
+	sizeof(struct aw_pwm_softc),
+};
+
+static devclass_t aw_pwm_devclass;
+
+DRIVER_MODULE(aw_pwm, simplebus, aw_pwm_driver, aw_pwm_devclass, 0, 0);
+SIMPLEBUS_PNP_INFO(compat_data);

Modified: head/sys/arm64/conf/GENERIC
==============================================================================
--- head/sys/arm64/conf/GENERIC	Wed Dec 12 20:56:56 2018	(r342003)
+++ head/sys/arm64/conf/GENERIC	Wed Dec 12 20:58:43 2018	(r342004)
@@ -240,6 +240,10 @@ device		aw_thermal	# Allwinner Thermal Sensor Controll
 device		spibus
 device		bcm2835_spi	# Broadcom BCM283x SPI bus
 
+# PWM
+device		pwm
+device		aw_pwm
+
 # Console
 device		vt
 device		kbdmux

Modified: head/sys/conf/files.arm64
==============================================================================
--- head/sys/conf/files.arm64	Wed Dec 12 20:56:56 2018	(r342003)
+++ head/sys/conf/files.arm64	Wed Dec 12 20:58:43 2018	(r342004)
@@ -31,6 +31,7 @@ arm/allwinner/aw_gpio.c		optional	gpio aw_gpio fdt
 arm/allwinner/aw_mmc.c		optional	mmc aw_mmc fdt | mmccam aw_mmc fdt
 arm/allwinner/aw_nmi.c		optional	aw_nmi fdt \
 	compile-with "${NORMAL_C} -I$S/gnu/dts/include"
+arm/allwinner/aw_pwm.c		optional	aw_pwm fdt
 arm/allwinner/aw_rsb.c		optional	aw_rsb fdt
 arm/allwinner/aw_rtc.c		optional	aw_rtc fdt
 arm/allwinner/aw_sid.c		optional	aw_sid nvmem fdt


More information about the svn-src-head mailing list