1283276Sgonzo/*- 2283276Sgonzo * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org> 3283276Sgonzo * All rights reserved. 4283276Sgonzo * 5283276Sgonzo * Redistribution and use in source and binary forms, with or without 6283276Sgonzo * modification, are permitted provided that the following conditions 7283276Sgonzo * are met: 8283276Sgonzo * 1. Redistributions of source code must retain the above copyright 9283276Sgonzo * notice, this list of conditions and the following disclaimer. 10283276Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11283276Sgonzo * notice, this list of conditions and the following disclaimer in the 12283276Sgonzo * documentation and/or other materials provided with the distribution. 13283276Sgonzo * 14283276Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15283276Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16283276Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17283276Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18283276Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19283276Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20283276Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21283276Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22283276Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23283276Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24283276Sgonzo * SUCH DAMAGE. 25283276Sgonzo */ 26283276Sgonzo 27283276Sgonzo#include <sys/cdefs.h> 28283276Sgonzo__FBSDID("$FreeBSD$"); 29283276Sgonzo 30283276Sgonzo#include <sys/param.h> 31283276Sgonzo#include <sys/systm.h> 32283276Sgonzo#include <sys/bus.h> 33283276Sgonzo#include <sys/kernel.h> 34283276Sgonzo#include <sys/limits.h> 35283276Sgonzo#include <sys/lock.h> 36283276Sgonzo#include <sys/module.h> 37283276Sgonzo#include <sys/mutex.h> 38283276Sgonzo#include <sys/resource.h> 39283276Sgonzo#include <sys/rman.h> 40283276Sgonzo#include <sys/sysctl.h> 41283276Sgonzo 42283276Sgonzo#include <machine/bus.h> 43283276Sgonzo 44283276Sgonzo#include <dev/fdt/fdt_common.h> 45283276Sgonzo#include <dev/ofw/openfirm.h> 46283276Sgonzo#include <dev/ofw/ofw_bus.h> 47283276Sgonzo#include <dev/ofw/ofw_bus_subr.h> 48283276Sgonzo 49283276Sgonzo#include "am335x_pwm.h" 50283276Sgonzo 51283276Sgonzo#define ECAP_TSCTR 0x00 52283276Sgonzo#define ECAP_CAP1 0x08 53283276Sgonzo#define ECAP_CAP2 0x0C 54283276Sgonzo#define ECAP_CAP3 0x10 55283276Sgonzo#define ECAP_CAP4 0x14 56283276Sgonzo#define ECAP_ECCTL2 0x2A 57283276Sgonzo#define ECCTL2_MODE_APWM (1 << 9) 58283276Sgonzo#define ECCTL2_SYNCO_SEL (3 << 6) 59283276Sgonzo#define ECCTL2_TSCTRSTOP_FREERUN (1 << 4) 60283276Sgonzo 61283276Sgonzo#define ECAP_READ2(_sc, reg) bus_read_2((_sc)->sc_mem_res, reg); 62283276Sgonzo#define ECAP_WRITE2(_sc, reg, value) \ 63283276Sgonzo bus_write_2((_sc)->sc_mem_res, reg, value); 64283276Sgonzo#define ECAP_READ4(_sc, reg) bus_read_4((_sc)->sc_mem_res, reg); 65283276Sgonzo#define ECAP_WRITE4(_sc, reg, value) \ 66283276Sgonzo bus_write_4((_sc)->sc_mem_res, reg, value); 67283276Sgonzo 68283276Sgonzo#define PWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 69283276Sgonzo#define PWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 70283276Sgonzo#define PWM_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \ 71283276Sgonzo device_get_nameunit(_sc->sc_dev), "am335x_ecap softc", MTX_DEF) 72283276Sgonzo#define PWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) 73283276Sgonzo 74283276Sgonzostatic device_probe_t am335x_ecap_probe; 75283276Sgonzostatic device_attach_t am335x_ecap_attach; 76283276Sgonzostatic device_detach_t am335x_ecap_detach; 77283276Sgonzo 78283276Sgonzostruct am335x_ecap_softc { 79283276Sgonzo device_t sc_dev; 80283276Sgonzo struct mtx sc_mtx; 81283276Sgonzo struct resource *sc_mem_res; 82283276Sgonzo int sc_mem_rid; 83283276Sgonzo}; 84283276Sgonzo 85283276Sgonzostatic device_method_t am335x_ecap_methods[] = { 86283276Sgonzo DEVMETHOD(device_probe, am335x_ecap_probe), 87283276Sgonzo DEVMETHOD(device_attach, am335x_ecap_attach), 88283276Sgonzo DEVMETHOD(device_detach, am335x_ecap_detach), 89283276Sgonzo 90283276Sgonzo DEVMETHOD_END 91283276Sgonzo}; 92283276Sgonzo 93283276Sgonzostatic driver_t am335x_ecap_driver = { 94283276Sgonzo "am335x_ecap", 95283276Sgonzo am335x_ecap_methods, 96283276Sgonzo sizeof(struct am335x_ecap_softc), 97283276Sgonzo}; 98283276Sgonzo 99283276Sgonzostatic devclass_t am335x_ecap_devclass; 100283276Sgonzo 101283276Sgonzo/* 102283276Sgonzo * API function to set period/duty cycles for ECAPx 103283276Sgonzo */ 104283276Sgonzoint 105283276Sgonzoam335x_pwm_config_ecap(int unit, int period, int duty) 106283276Sgonzo{ 107283276Sgonzo device_t dev; 108283276Sgonzo struct am335x_ecap_softc *sc; 109283276Sgonzo uint16_t reg; 110283276Sgonzo 111283276Sgonzo dev = devclass_get_device(am335x_ecap_devclass, unit); 112283276Sgonzo if (dev == NULL) 113283276Sgonzo return (ENXIO); 114283276Sgonzo 115283276Sgonzo if (duty > period) 116283276Sgonzo return (EINVAL); 117283276Sgonzo 118283276Sgonzo if (period == 0) 119283276Sgonzo return (EINVAL); 120283276Sgonzo 121283276Sgonzo sc = device_get_softc(dev); 122283276Sgonzo PWM_LOCK(sc); 123283276Sgonzo 124283276Sgonzo reg = ECAP_READ2(sc, ECAP_ECCTL2); 125283276Sgonzo reg |= ECCTL2_MODE_APWM | ECCTL2_TSCTRSTOP_FREERUN | ECCTL2_SYNCO_SEL; 126283276Sgonzo ECAP_WRITE2(sc, ECAP_ECCTL2, reg); 127283276Sgonzo 128283276Sgonzo /* CAP3 in APWM mode is APRD shadow register */ 129283276Sgonzo ECAP_WRITE4(sc, ECAP_CAP3, period - 1); 130283276Sgonzo 131283276Sgonzo /* CAP4 in APWM mode is ACMP shadow register */ 132283276Sgonzo ECAP_WRITE4(sc, ECAP_CAP4, duty); 133283276Sgonzo /* Restart counter */ 134283276Sgonzo ECAP_WRITE4(sc, ECAP_TSCTR, 0); 135283276Sgonzo 136283276Sgonzo PWM_UNLOCK(sc); 137283276Sgonzo 138283276Sgonzo return (0); 139283276Sgonzo} 140283276Sgonzo 141283276Sgonzostatic int 142283276Sgonzoam335x_ecap_probe(device_t dev) 143283276Sgonzo{ 144283276Sgonzo 145283276Sgonzo if (!ofw_bus_status_okay(dev)) 146283276Sgonzo return (ENXIO); 147283276Sgonzo 148283276Sgonzo if (!ofw_bus_is_compatible(dev, "ti,am33xx-ecap")) 149283276Sgonzo return (ENXIO); 150283276Sgonzo 151283276Sgonzo device_set_desc(dev, "AM335x eCAP"); 152283276Sgonzo 153283276Sgonzo return (BUS_PROBE_DEFAULT); 154283276Sgonzo} 155283276Sgonzo 156283276Sgonzostatic int 157283276Sgonzoam335x_ecap_attach(device_t dev) 158283276Sgonzo{ 159283276Sgonzo struct am335x_ecap_softc *sc; 160283276Sgonzo 161283276Sgonzo sc = device_get_softc(dev); 162283276Sgonzo sc->sc_dev = dev; 163283276Sgonzo 164283276Sgonzo PWM_LOCK_INIT(sc); 165283276Sgonzo 166283276Sgonzo sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 167283276Sgonzo &sc->sc_mem_rid, RF_ACTIVE); 168283276Sgonzo if (sc->sc_mem_res == NULL) { 169283276Sgonzo device_printf(dev, "cannot allocate memory resources\n"); 170283276Sgonzo goto fail; 171283276Sgonzo } 172283276Sgonzo 173283276Sgonzo return (0); 174283276Sgonzo 175283276Sgonzofail: 176283276Sgonzo PWM_LOCK_DESTROY(sc); 177283276Sgonzo return (ENXIO); 178283276Sgonzo} 179283276Sgonzo 180283276Sgonzostatic int 181283276Sgonzoam335x_ecap_detach(device_t dev) 182283276Sgonzo{ 183283276Sgonzo struct am335x_ecap_softc *sc; 184283276Sgonzo 185283276Sgonzo sc = device_get_softc(dev); 186283276Sgonzo 187283276Sgonzo PWM_LOCK(sc); 188283276Sgonzo if (sc->sc_mem_res) 189283276Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 190283276Sgonzo sc->sc_mem_rid, sc->sc_mem_res); 191283276Sgonzo PWM_UNLOCK(sc); 192283276Sgonzo 193283276Sgonzo PWM_LOCK_DESTROY(sc); 194283276Sgonzo 195283276Sgonzo 196283276Sgonzo return (0); 197283276Sgonzo} 198283276Sgonzo 199283276SgonzoDRIVER_MODULE(am335x_ecap, am335x_pwmss, am335x_ecap_driver, am335x_ecap_devclass, 0, 0); 200283276SgonzoMODULE_VERSION(am335x_ecap, 1); 201283276SgonzoMODULE_DEPEND(am335x_ecap, am335x_pwmss, 1, 1, 1); 202