Move Ti AM335x to dev/extres/clk framework.

Re-implement clocks for these SoC by using now standard extres/clk framework.
This is necessary for future expansion of these. The new  implementation
is (due to the size of the patch) only the initial (minimum) version.
It will be updated/expanded with a subsequent set of particular patches.

This patch is also not tested on OMAP4 based boards (BeagleBone),
so all possible issues should be (and will be) fixed by ASAP once
identified.

Submited by:		Oskar Holmlund (oskar.holmlund@ohdata.se)
Differential Revision:  https://reviews.freebsd.org/D25118
This commit is contained in:
Michal Meloun 2020-07-30 14:45:05 +00:00
parent 98369a6980
commit 0050ea2415
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=363700
56 changed files with 5050 additions and 2239 deletions

View File

@ -0,0 +1,192 @@
/*-
* Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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$
*/
/* Based on sys/arm/ti/ti_sysc.c */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_sysc.h>
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
struct ti_am3359_cppi41_softc {
device_t dev;
struct syscon * syscon;
struct resource * res[4];
bus_space_tag_t bst;
bus_space_handle_t bsh;
struct mtx mtx;
};
static struct resource_spec ti_am3359_cppi41_res_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE },
{ SYS_RES_MEMORY, 1, RF_ACTIVE | RF_SHAREABLE },
{ SYS_RES_MEMORY, 2, RF_ACTIVE | RF_SHAREABLE },
{ SYS_RES_MEMORY, 3, RF_ACTIVE | RF_SHAREABLE },
{ -1, 0 }
};
/* Device */
static struct ofw_compat_data compat_data[] = {
{ "ti,am3359-cppi41", 1 },
{ NULL, 0 }
};
static int
ti_am3359_cppi41_write_4(device_t dev, bus_addr_t addr, uint32_t val)
{
struct ti_am3359_cppi41_softc *sc;
sc = device_get_softc(dev);
DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val);
mtx_lock(&sc->mtx);
bus_space_write_4(sc->bst, sc->bsh, addr, val);
mtx_unlock(&sc->mtx);
return (0);
}
static uint32_t
ti_am3359_cppi41_read_4(device_t dev, bus_addr_t addr)
{
struct ti_am3359_cppi41_softc *sc;
uint32_t val;
sc = device_get_softc(dev);
mtx_lock(&sc->mtx);
val = bus_space_read_4(sc->bst, sc->bsh, addr);
mtx_unlock(&sc->mtx);
DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, val);
return (val);
}
/* device interface */
static int
ti_am3359_cppi41_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, "TI AM3359 CPPI 41");
return(BUS_PROBE_DEFAULT);
}
static int
ti_am3359_cppi41_attach(device_t dev)
{
struct ti_am3359_cppi41_softc *sc;
phandle_t node;
uint32_t reg, reset_bit, timeout=10;
uint64_t sysc_address;
device_t parent;
sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, ti_am3359_cppi41_res_spec, sc->res)) {
device_printf(sc->dev, "Cant allocate resources\n");
return (ENXIO);
}
sc->dev = dev;
sc->bst = rman_get_bustag(sc->res[0]);
sc->bsh = rman_get_bushandle(sc->res[0]);
mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
node = ofw_bus_get_node(sc->dev);
/* variant of am335x_usbss.c */
DPRINTF(dev, "-- RESET USB --\n");
parent = device_get_parent(dev);
reset_bit = ti_sysc_get_soft_reset_bit(parent);
if (reset_bit == 0) {
DPRINTF(dev, "Dont have reset bit\n");
return (0);
}
sysc_address = ti_sysc_get_sysc_address_offset_host(parent);
DPRINTF(dev, "sysc_address %x\n", sysc_address);
ti_am3359_cppi41_write_4(dev, sysc_address, reset_bit);
DELAY(100);
reg = ti_am3359_cppi41_read_4(dev, sysc_address);
if ((reg & reset_bit) && timeout--) {
DPRINTF(dev, "Reset still ongoing - wait a little bit longer\n");
DELAY(100);
reg = ti_am3359_cppi41_read_4(dev, sysc_address);
}
if (timeout == 0)
device_printf(dev, "USB Reset timeout\n");
return (0);
}
static device_method_t ti_am3359_cppi41_methods[] = {
DEVMETHOD(device_probe, ti_am3359_cppi41_probe),
DEVMETHOD(device_attach, ti_am3359_cppi41_attach),
DEVMETHOD_END
};
DEFINE_CLASS_1(ti_am3359_cppi41, ti_am3359_cppi41_driver,
ti_am3359_cppi41_methods,sizeof(struct ti_am3359_cppi41_softc),
simplebus_driver);
static devclass_t ti_am3359_cppi41_devclass;
EARLY_DRIVER_MODULE(ti_am3359_cppi41, simplebus, ti_am3359_cppi41_driver,
ti_am3359_cppi41_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ti_am3359_cppi41, 1);
MODULE_DEPEND(ti_am3359_cppi41, ti_sysc, 1, 1, 1);

View File

@ -42,12 +42,13 @@ __FBSDID("$FreeBSD$");
#include <machine/machdep.h> /* For arm_set_delay */
#include <dev/extres/clk/clk.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_hwmods.h>
#include <arm/ti/ti_sysc.h>
#include "am335x_dmtreg.h"
@ -58,7 +59,8 @@ struct am335x_dmtimer_softc {
int tmr_irq_rid;
struct resource * tmr_irq_res;
void *tmr_irq_handler;
uint32_t sysclk_freq;
clk_t clk_fck;
uint64_t sysclk_freq;
uint32_t tclr; /* Cached TCLR register. */
union {
struct timecounter tc;
@ -251,6 +253,7 @@ am335x_dmtimer_probe(device_t dev)
{
char strbuf[32];
int tmr_num;
uint64_t rev_address;
if (!ofw_bus_status_okay(dev))
return (ENXIO);
@ -259,13 +262,22 @@ am335x_dmtimer_probe(device_t dev)
return (ENXIO);
/*
* Get the hardware unit number (the N from ti,hwmods="timerN").
* Get the hardware unit number from address of rev register.
* If this isn't the hardware unit we're going to use for either the
* eventtimer or the timecounter, no point in instantiating the device.
*/
tmr_num = ti_hwmods_get_unit(dev, "timer");
if (tmr_num != ET_TMR_NUM && tmr_num != TC_TMR_NUM)
return (ENXIO);
rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
switch (rev_address) {
case DMTIMER2_REV:
tmr_num = 2;
break;
case DMTIMER3_REV:
tmr_num = 3;
break;
default:
/* Not DMTIMER2 or DMTIMER3 */
return (ENXIO);
}
snprintf(strbuf, sizeof(strbuf), "AM335x DMTimer%d", tmr_num);
device_set_desc_copy(dev, strbuf);
@ -277,23 +289,46 @@ static int
am335x_dmtimer_attach(device_t dev)
{
struct am335x_dmtimer_softc *sc;
clk_ident_t timer_id;
int err;
uint64_t rev_address;
clk_t sys_clkin;
sc = device_get_softc(dev);
sc->dev = dev;
/* Get the base clock frequency. */
if ((err = ti_prcm_clk_get_source_freq(SYS_CLK, &sc->sysclk_freq)) != 0)
return (err);
/* expect one clock */
err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_fck);
if (err != 0) {
device_printf(dev, "Cant find clock index 0. err: %d\n", err);
return (ENXIO);
}
err = clk_get_by_name(dev, "sys_clkin_ck@40", &sys_clkin);
if (err != 0) {
device_printf(dev, "Cant find sys_clkin_ck@40 err: %d\n", err);
return (ENXIO);
}
/* Select M_OSC as DPLL parent */
err = clk_set_parent_by_clk(sc->clk_fck, sys_clkin);
if (err != 0) {
device_printf(dev, "Cant set mux to CLK_M_OSC\n");
return (ENXIO);
}
/* Enable clocks and power on the device. */
if ((timer_id = ti_hwmods_get_clock(dev)) == INVALID_CLK_IDENT)
err = ti_sysc_clock_enable(device_get_parent(dev));
if (err != 0) {
device_printf(dev, "Cant enable sysc clkctrl, err %d\n", err);
return (ENXIO);
if ((err = ti_prcm_clk_set_source(timer_id, SYSCLK_CLK)) != 0)
return (err);
if ((err = ti_prcm_clk_enable(timer_id)) != 0)
return (err);
}
/* Get the base clock frequency. */
err = clk_get_freq(sc->clk_fck, &sc->sysclk_freq);
if (err != 0) {
device_printf(dev, "Cant get sysclk frequency, err %d\n", err);
return (ENXIO);
}
/* Request the memory resources. */
sc->tmr_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
@ -302,7 +337,20 @@ am335x_dmtimer_attach(device_t dev)
return (ENXIO);
}
sc->tmr_num = ti_hwmods_get_unit(dev, "timer");
rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
switch (rev_address) {
case DMTIMER2_REV:
sc->tmr_num = 2;
break;
case DMTIMER3_REV:
sc->tmr_num = 3;
break;
default:
device_printf(dev, "Not timer 2 or 3! %#jx\n",
rev_address);
return (ENXIO);
}
snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num);
/*
@ -334,7 +382,7 @@ static driver_t am335x_dmtimer_driver = {
static devclass_t am335x_dmtimer_devclass;
DRIVER_MODULE(am335x_dmtimer, simplebus, am335x_dmtimer_driver, am335x_dmtimer_devclass, 0, 0);
MODULE_DEPEND(am335x_dmtimer, am335x_prcm, 1, 1, 1);
MODULE_DEPEND(am335x_dmtimer, ti_sysc, 1, 1, 1);
static void
am335x_dmtimer_delay(int usec, void *arg)

View File

@ -43,6 +43,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_platform.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@ -60,9 +62,9 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/extres/clk/clk.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_hwmods.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/ti_pinmux.h>
#include <arm/ti/am335x/am335x_scm_padconf.h>
@ -82,6 +84,8 @@ struct dmtpps_softc {
struct cdev * pps_cdev;
struct pps_state pps_state;
struct mtx pps_mtx;
clk_t clk_fck;
uint64_t sysclk_freq;
};
static int dmtpps_tmr_num; /* Set by probe() */
@ -383,6 +387,7 @@ dmtpps_probe(device_t dev)
{
char strbuf[64];
int tmr_num;
uint64_t rev_address;
if (!ofw_bus_status_okay(dev))
return (ENXIO);
@ -402,7 +407,33 @@ dmtpps_probe(device_t dev)
* Figure out which hardware timer is being probed and see if it matches
* the configured timer number determined earlier.
*/
tmr_num = ti_hwmods_get_unit(dev, "timer");
rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
switch (rev_address) {
case DMTIMER1_1MS_REV:
tmr_num = 1;
break;
case DMTIMER2_REV:
tmr_num = 2;
break;
case DMTIMER3_REV:
tmr_num = 3;
break;
case DMTIMER4_REV:
tmr_num = 4;
break;
case DMTIMER5_REV:
tmr_num = 5;
break;
case DMTIMER6_REV:
tmr_num = 6;
break;
case DMTIMER7_REV:
tmr_num = 7;
break;
default:
return (ENXIO);
}
if (dmtpps_tmr_num != tmr_num)
return (ENXIO);
@ -418,23 +449,73 @@ dmtpps_attach(device_t dev)
{
struct dmtpps_softc *sc;
struct make_dev_args mda;
clk_ident_t timer_id;
int err, sysclk_freq;
int err;
clk_t sys_clkin;
uint64_t rev_address;
sc = device_get_softc(dev);
sc->dev = dev;
/* Get the base clock frequency. */
err = ti_prcm_clk_get_source_freq(SYS_CLK, &sysclk_freq);
/* Figure out which hardware timer this is and set the name string. */
rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
switch (rev_address) {
case DMTIMER1_1MS_REV:
sc->tmr_num = 1;
break;
case DMTIMER2_REV:
sc->tmr_num = 2;
break;
case DMTIMER3_REV:
sc->tmr_num = 3;
break;
case DMTIMER4_REV:
sc->tmr_num = 4;
break;
case DMTIMER5_REV:
sc->tmr_num = 5;
break;
case DMTIMER6_REV:
sc->tmr_num = 6;
break;
case DMTIMER7_REV:
sc->tmr_num = 7;
break;
}
snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num);
/* expect one clock */
err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_fck);
if (err != 0) {
device_printf(dev, "Cant find clock index 0. err: %d\n", err);
return (ENXIO);
}
err = clk_get_by_name(dev, "sys_clkin_ck@40", &sys_clkin);
if (err != 0) {
device_printf(dev, "Cant find sys_clkin_ck@40 err: %d\n", err);
return (ENXIO);
}
/* Select M_OSC as DPLL parent */
err = clk_set_parent_by_clk(sc->clk_fck, sys_clkin);
if (err != 0) {
device_printf(dev, "Cant set mux to CLK_M_OSC\n");
return (ENXIO);
}
/* Enable clocks and power on the device. */
if ((timer_id = ti_hwmods_get_clock(dev)) == INVALID_CLK_IDENT)
err = ti_sysc_clock_enable(device_get_parent(dev));
if (err != 0) {
device_printf(dev, "Cant enable sysc clkctrl, err %d\n", err);
return (ENXIO);
if ((err = ti_prcm_clk_set_source(timer_id, SYSCLK_CLK)) != 0)
return (err);
if ((err = ti_prcm_clk_enable(timer_id)) != 0)
return (err);
}
/* Get the base clock frequency. */
err = clk_get_freq(sc->clk_fck, &sc->sysclk_freq);
if (err != 0) {
device_printf(dev, "Cant get sysclk frequency, err %d\n", err);
return (ENXIO);
}
/* Request the memory resources. */
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&sc->mem_rid, RF_ACTIVE);
@ -442,10 +523,6 @@ dmtpps_attach(device_t dev)
return (ENXIO);
}
/* Figure out which hardware timer this is and set the name string. */
sc->tmr_num = ti_hwmods_get_unit(dev, "timer");
snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num);
/*
* Configure the timer pulse/capture pin to input/capture mode. This is
* required in addition to configuring the pin as input with the pinmux
@ -468,7 +545,7 @@ dmtpps_attach(device_t dev)
sc->tc.tc_name = sc->tmr_name;
sc->tc.tc_get_timecount = dmtpps_get_timecount;
sc->tc.tc_counter_mask = ~0u;
sc->tc.tc_frequency = sysclk_freq;
sc->tc.tc_frequency = sc->sysclk_freq;
sc->tc.tc_quality = 1000;
sc->tc.tc_priv = sc;
@ -541,5 +618,4 @@ static driver_t dmtpps_driver = {
static devclass_t dmtpps_devclass;
DRIVER_MODULE(am335x_dmtpps, simplebus, dmtpps_driver, dmtpps_devclass, 0, 0);
MODULE_DEPEND(am335x_dmtpps, am335x_prcm, 1, 1, 1);
MODULE_DEPEND(am335x_dmtpps, ti_sysc, 1, 1, 1);

View File

@ -73,4 +73,16 @@
#define DMT_TSICR_RESET (1 << 1) /* TSICR perform soft reset */
#define DMT_TCAR2 0x48 /* Capture Reg */
/* Location of revision register from TRM Memory map chapter 2 */
/* L4_WKUP */
#define DMTIMER0_REV 0x05000
#define DMTIMER1_1MS_REV 0x31000
/* L4_PER */
#define DMTIMER2_REV 0x40000
#define DMTIMER3_REV 0x42000
#define DMTIMER4_REV 0x44000
#define DMTIMER5_REV 0x46000
#define DMTIMER6_REV 0x48000
#define DMTIMER7_REV 0x4A000
#endif /* AM335X_DMTREG_H */

View File

@ -155,3 +155,4 @@ DEFINE_CLASS_1(gpio, am335x_gpio_driver, am335x_gpio_methods,
sizeof(struct ti_gpio_softc), ti_gpio_driver);
DRIVER_MODULE(am335x_gpio, simplebus, am335x_gpio_driver, am335x_gpio_devclass,
0, 0);
MODULE_DEPEND(am335x_gpio, ti_sysc, 1, 1, 1);

View File

@ -50,6 +50,8 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <dev/extres/clk/clk.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
@ -65,7 +67,7 @@ __FBSDID("$FreeBSD$");
#include <dev/vt/vt.h>
#endif
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/ti_scm.h>
#include "am335x_lcd.h"
@ -219,6 +221,9 @@ struct am335x_lcd_softc {
/* HDMI framer */
phandle_t sc_hdmi_framer;
eventhandler_tag sc_hdmi_evh;
/* Clock */
clk_t sc_clk_dpll_disp_ck;
};
static void
@ -615,24 +620,28 @@ am335x_lcd_configure(struct am335x_lcd_softc *sc)
uint32_t hbp, hfp, hsw;
uint32_t vbp, vfp, vsw;
uint32_t width, height;
unsigned int ref_freq;
uint64_t ref_freq;
int err;
/*
* try to adjust clock to get double of requested frequency
* HDMI/DVI displays are very sensitive to error in frequncy value
*/
if (ti_prcm_clk_set_source_freq(LCDC_CLK, sc->sc_panel.panel_pxl_clk*2)) {
err = clk_set_freq(sc->sc_clk_dpll_disp_ck, sc->sc_panel.panel_pxl_clk*2,
CLK_SET_ROUND_ANY);
if (err != 0) {
device_printf(sc->sc_dev, "can't set source frequency\n");
return (ENXIO);
}
if (ti_prcm_clk_get_source_freq(LCDC_CLK, &ref_freq)) {
err = clk_get_freq(sc->sc_clk_dpll_disp_ck, &ref_freq);
if (err != 0) {
device_printf(sc->sc_dev, "can't get reference frequency\n");
return (ENXIO);
}
/* Panle initialization */
/* Panel initialization */
dma_size = round_page(sc->sc_panel.panel_width*sc->sc_panel.panel_height*sc->sc_panel.bpp/8);
/*
@ -967,6 +976,13 @@ am335x_lcd_attach(device_t dev)
return (ENXIO);
}
/* Fixme: Cant find any reference in DTS for dpll_disp_ck@498 for now. */
err = clk_get_by_name(dev, "dpll_disp_ck@498", &sc->sc_clk_dpll_disp_ck);
if (err != 0) {
device_printf(dev, "Cant get dpll_disp_ck@49\n");
return (ENXIO);
}
sc->sc_panel.ac_bias = 255;
sc->sc_panel.ac_bias_intrpt = 0;
sc->sc_panel.dma_burst_sz = 16;
@ -989,7 +1005,11 @@ am335x_lcd_attach(device_t dev)
}
}
ti_prcm_clk_enable(LCDC_CLK);
err = ti_sysc_clock_enable(device_get_parent(dev));
if (err != 0) {
device_printf(dev, "Failed to enable sysc clkctrl, err %d\n", err);
return (ENXIO);
}
rid = 0;
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
@ -1081,3 +1101,4 @@ static devclass_t am335x_lcd_devclass;
DRIVER_MODULE(am335x_lcd, simplebus, am335x_lcd_driver, am335x_lcd_devclass, 0, 0);
MODULE_VERSION(am335x_lcd, 1);
MODULE_DEPEND(am335x_lcd, simplebus, 1, 1, 1);
MODULE_DEPEND(am335x_lcd, ti_sysc, 1, 1, 1);

View File

@ -66,9 +66,11 @@ __FBSDID("$FreeBSD$");
#include <sys/rman.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_scm.h>
#include <arm/ti/am335x/am335x_scm.h>
#include <arm/ti/ti_sysc.h>
#include <dev/extres/clk/clk.h>
#include <dev/extres/syscon/syscon.h>
#include "syscon_if.h"
#define USBCTRL_REV 0x00
#define USBCTRL_CTRL 0x14
@ -130,6 +132,7 @@ struct musbotg_super_softc {
struct musbotg_softc sc_otg;
struct resource *sc_mem_res[2];
int sc_irq_rid;
struct syscon *syscon;
};
static void
@ -155,30 +158,33 @@ static void
musbotg_clocks_on(void *arg)
{
struct musbotg_softc *sc;
uint32_t c, reg;
struct musbotg_super_softc *ssc;
uint32_t reg;
sc = arg;
reg = USB_CTRL[sc->sc_id];
ssc = sc->sc_platform_data;
ti_scm_reg_read_4(reg, &c);
c &= ~3; /* Enable power */
c |= 1 << 19; /* VBUS detect enable */
c |= 1 << 20; /* Session end enable */
ti_scm_reg_write_4(reg, c);
reg = SYSCON_READ_4(ssc->syscon, USB_CTRL[sc->sc_id]);
reg &= ~3; /* Enable power */
reg |= 1 << 19; /* VBUS detect enable */
reg |= 1 << 20; /* Session end enable */
SYSCON_WRITE_4(ssc->syscon, USB_CTRL[sc->sc_id], reg);
}
static void
musbotg_clocks_off(void *arg)
{
struct musbotg_softc *sc;
uint32_t c, reg;
struct musbotg_super_softc *ssc;
uint32_t reg;
sc = arg;
reg = USB_CTRL[sc->sc_id];
ssc = sc->sc_platform_data;
/* Disable power to PHY */
ti_scm_reg_read_4(reg, &c);
ti_scm_reg_write_4(reg, c | 3);
reg = SYSCON_READ_4(ssc->syscon, USB_CTRL[sc->sc_id]);
SYSCON_WRITE_4(ssc->syscon, USB_CTRL[sc->sc_id], reg | 3);
}
static void
@ -241,9 +247,42 @@ musbotg_attach(device_t dev)
char mode[16];
int err;
uint32_t reg;
phandle_t opp_table;
clk_t clk_usbotg_fck;
sc->sc_otg.sc_id = device_get_unit(dev);
/* FIXME: The devicetree needs to be updated to get a handle to the gate
* usbotg_fck@47c. see TRM 8.1.12.2 CM_WKUP CM_CLKDCOLDO_DPLL_PER.
*/
err = clk_get_by_name(dev, "usbotg_fck@47c", &clk_usbotg_fck);
if (err) {
device_printf(dev, "Can not find usbotg_fck@47c\n");
return (ENXIO);
}
err = clk_enable(clk_usbotg_fck);
if (err) {
device_printf(dev, "Can not enable usbotg_fck@47c\n");
return (ENXIO);
}
/* FIXME: For now; Go and kidnap syscon from opp-table */
opp_table = OF_finddevice("/opp-table");
if (opp_table == -1) {
device_printf(dev, "Cant find /opp-table\n");
return (ENXIO);
}
if (!OF_hasprop(opp_table, "syscon")) {
device_printf(dev, "/opp-table missing syscon property\n");
return (ENXIO);
}
err = syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon);
if (err) {
device_printf(dev, "Failed to get syscon\n");
return (ENXIO);
}
/* Request the memory resources */
err = bus_alloc_resources(dev, am335x_musbotg_mem_spec,
sc->sc_mem_res);
@ -417,5 +456,7 @@ static driver_t musbotg_driver = {
static devclass_t musbotg_devclass;
DRIVER_MODULE(musbotg, usbss, musbotg_driver, musbotg_devclass, 0, 0);
MODULE_DEPEND(musbotg, usbss, 1, 1, 1);
DRIVER_MODULE(musbotg, ti_sysc, musbotg_driver, musbotg_devclass, 0, 0);
MODULE_DEPEND(musbotg, ti_sysc, 1, 1, 1);
MODULE_DEPEND(musbotg, ti_am3359_cppi41, 1, 1, 1);
MODULE_DEPEND(usbss, usb, 1, 1, 1);

View File

@ -1,884 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
* 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 <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/malloc.h>
#include <sys/rman.h>
#include <sys/timeet.h>
#include <sys/timetc.h>
#include <sys/watchdog.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/intr.h>
#include <arm/ti/tivar.h>
#include <arm/ti/ti_scm.h>
#include <arm/ti/ti_prcm.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <machine/bus.h>
#include "am335x_scm.h"
#define CM_PER 0
#define CM_PER_L4LS_CLKSTCTRL (CM_PER + 0x000)
#define CM_PER_L3S_CLKSTCTRL (CM_PER + 0x004)
#define CM_PER_L3_CLKSTCTRL (CM_PER + 0x00C)
#define CM_PER_CPGMAC0_CLKCTRL (CM_PER + 0x014)
#define CM_PER_LCDC_CLKCTRL (CM_PER + 0x018)
#define CM_PER_USB0_CLKCTRL (CM_PER + 0x01C)
#define CM_PER_TPTC0_CLKCTRL (CM_PER + 0x024)
#define CM_PER_UART5_CLKCTRL (CM_PER + 0x038)
#define CM_PER_MMC0_CLKCTRL (CM_PER + 0x03C)
#define CM_PER_I2C2_CLKCTRL (CM_PER + 0x044)
#define CM_PER_I2C1_CLKCTRL (CM_PER + 0x048)
#define CM_PER_SPI0_CLKCTRL (CM_PER + 0x04C)
#define CM_PER_SPI1_CLKCTRL (CM_PER + 0x050)
#define CM_PER_UART1_CLKCTRL (CM_PER + 0x06C)
#define CM_PER_UART2_CLKCTRL (CM_PER + 0x070)
#define CM_PER_UART3_CLKCTRL (CM_PER + 0x074)
#define CM_PER_UART4_CLKCTRL (CM_PER + 0x078)
#define CM_PER_TIMER7_CLKCTRL (CM_PER + 0x07C)
#define CM_PER_TIMER2_CLKCTRL (CM_PER + 0x080)
#define CM_PER_TIMER3_CLKCTRL (CM_PER + 0x084)
#define CM_PER_TIMER4_CLKCTRL (CM_PER + 0x088)
#define CM_PER_GPIO1_CLKCTRL (CM_PER + 0x0AC)
#define CM_PER_GPIO2_CLKCTRL (CM_PER + 0x0B0)
#define CM_PER_GPIO3_CLKCTRL (CM_PER + 0x0B4)
#define CM_PER_TPCC_CLKCTRL (CM_PER + 0x0BC)
#define CM_PER_EPWMSS1_CLKCTRL (CM_PER + 0x0CC)
#define CM_PER_EPWMSS0_CLKCTRL (CM_PER + 0x0D4)
#define CM_PER_EPWMSS2_CLKCTRL (CM_PER + 0x0D8)
#define CM_PER_L3_INSTR_CLKCTRL (CM_PER + 0x0DC)
#define CM_PER_L3_CLKCTRL (CM_PER + 0x0E0)
#define CM_PER_PRUSS_CLKCTRL (CM_PER + 0x0E8)
#define CM_PER_TIMER5_CLKCTRL (CM_PER + 0x0EC)
#define CM_PER_TIMER6_CLKCTRL (CM_PER + 0x0F0)
#define CM_PER_MMC1_CLKCTRL (CM_PER + 0x0F4)
#define CM_PER_MMC2_CLKCTRL (CM_PER + 0x0F8)
#define CM_PER_TPTC1_CLKCTRL (CM_PER + 0x0FC)
#define CM_PER_TPTC2_CLKCTRL (CM_PER + 0x100)
#define CM_PER_SPINLOCK0_CLKCTRL (CM_PER + 0x10C)
#define CM_PER_MAILBOX0_CLKCTRL (CM_PER + 0x110)
#define CM_PER_OCPWP_L3_CLKSTCTRL (CM_PER + 0x12C)
#define CM_PER_OCPWP_CLKCTRL (CM_PER + 0x130)
#define CM_PER_CPSW_CLKSTCTRL (CM_PER + 0x144)
#define CM_PER_PRUSS_CLKSTCTRL (CM_PER + 0x140)
#define CM_WKUP 0x400
#define CM_WKUP_CLKSTCTRL (CM_WKUP + 0x000)
#define CM_WKUP_CONTROL_CLKCTRL (CM_WKUP + 0x004)
#define CM_WKUP_GPIO0_CLKCTRL (CM_WKUP + 0x008)
#define CM_WKUP_CM_L3_AON_CLKSTCTRL (CM_WKUP + 0x01C)
#define CM_WKUP_CM_CLKSEL_DPLL_MPU (CM_WKUP + 0x02C)
#define CM_WKUP_CM_IDLEST_DPLL_DISP (CM_WKUP + 0x048)
#define CM_WKUP_CM_CLKSEL_DPLL_DISP (CM_WKUP + 0x054)
#define CM_WKUP_CM_CLKDCOLDO_DPLL_PER (CM_WKUP + 0x07C)
#define CM_WKUP_CM_CLKMODE_DPLL_DISP (CM_WKUP + 0x098)
#define CM_WKUP_I2C0_CLKCTRL (CM_WKUP + 0x0B8)
#define CM_WKUP_ADC_TSC_CLKCTRL (CM_WKUP + 0x0BC)
#define CM_DPLL 0x500
#define CLKSEL_TIMER7_CLK (CM_DPLL + 0x004)
#define CLKSEL_TIMER2_CLK (CM_DPLL + 0x008)
#define CLKSEL_TIMER3_CLK (CM_DPLL + 0x00C)
#define CLKSEL_TIMER4_CLK (CM_DPLL + 0x010)
#define CLKSEL_TIMER5_CLK (CM_DPLL + 0x018)
#define CLKSEL_TIMER6_CLK (CM_DPLL + 0x01C)
#define CLKSEL_PRUSS_OCP_CLK (CM_DPLL + 0x030)
#define CM_RTC 0x800
#define CM_RTC_RTC_CLKCTRL (CM_RTC + 0x000)
#define CM_RTC_CLKSTCTRL (CM_RTC + 0x004)
#define PRM_PER 0xC00
#define PRM_PER_RSTCTRL (PRM_PER + 0x00)
#define PRM_DEVICE_OFFSET 0xF00
#define PRM_RSTCTRL (PRM_DEVICE_OFFSET + 0x00)
struct am335x_prcm_softc {
struct resource * res[2];
bus_space_tag_t bst;
bus_space_handle_t bsh;
int attach_done;
};
static struct resource_spec am335x_prcm_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
static struct am335x_prcm_softc *am335x_prcm_sc = NULL;
static int am335x_clk_noop_activate(struct ti_clock_dev *clkdev);
static int am335x_clk_generic_activate(struct ti_clock_dev *clkdev);
static int am335x_clk_gpio_activate(struct ti_clock_dev *clkdev);
static int am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev);
static int am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev);
static int am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
static int am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
static int am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
static int am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
static int am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
static int am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
static int am335x_clk_set_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int freq);
static void am335x_prcm_reset(void);
static int am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev);
static int am335x_clk_musb0_activate(struct ti_clock_dev *clkdev);
static int am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev);
static int am335x_clk_pruss_activate(struct ti_clock_dev *clkdev);
#define AM335X_NOOP_CLOCK_DEV(i) \
{ .id = (i), \
.clk_activate = am335x_clk_noop_activate, \
.clk_deactivate = am335x_clk_noop_deactivate, \
.clk_set_source = am335x_clk_noop_set_source, \
.clk_accessible = NULL, \
.clk_get_source_freq = NULL, \
.clk_set_source_freq = NULL \
}
#define AM335X_GENERIC_CLOCK_DEV(i) \
{ .id = (i), \
.clk_activate = am335x_clk_generic_activate, \
.clk_deactivate = am335x_clk_generic_deactivate, \
.clk_set_source = am335x_clk_generic_set_source, \
.clk_accessible = NULL, \
.clk_get_source_freq = NULL, \
.clk_set_source_freq = NULL \
}
#define AM335X_GPIO_CLOCK_DEV(i) \
{ .id = (i), \
.clk_activate = am335x_clk_gpio_activate, \
.clk_deactivate = am335x_clk_generic_deactivate, \
.clk_set_source = am335x_clk_generic_set_source, \
.clk_accessible = NULL, \
.clk_get_source_freq = NULL, \
.clk_set_source_freq = NULL \
}
#define AM335X_MMCHS_CLOCK_DEV(i) \
{ .id = (i), \
.clk_activate = am335x_clk_generic_activate, \
.clk_deactivate = am335x_clk_generic_deactivate, \
.clk_set_source = am335x_clk_generic_set_source, \
.clk_accessible = NULL, \
.clk_get_source_freq = am335x_clk_hsmmc_get_source_freq, \
.clk_set_source_freq = NULL \
}
struct ti_clock_dev ti_am335x_clk_devmap[] = {
/* System clocks */
{ .id = SYS_CLK,
.clk_activate = NULL,
.clk_deactivate = NULL,
.clk_set_source = NULL,
.clk_accessible = NULL,
.clk_get_source_freq = am335x_clk_get_sysclk_freq,
.clk_set_source_freq = NULL,
},
/* MPU (ARM) core clocks */
{ .id = MPU_CLK,
.clk_activate = NULL,
.clk_deactivate = NULL,
.clk_set_source = NULL,
.clk_accessible = NULL,
.clk_get_source_freq = am335x_clk_get_arm_fclk_freq,
.clk_set_source_freq = NULL,
},
/* CPSW Ethernet Switch core clocks */
{ .id = CPSW_CLK,
.clk_activate = am335x_clk_cpsw_activate,
.clk_deactivate = NULL,
.clk_set_source = NULL,
.clk_accessible = NULL,
.clk_get_source_freq = NULL,
.clk_set_source_freq = NULL,
},
/* Mentor USB HS controller core clocks */
{ .id = MUSB0_CLK,
.clk_activate = am335x_clk_musb0_activate,
.clk_deactivate = NULL,
.clk_set_source = NULL,
.clk_accessible = NULL,
.clk_get_source_freq = NULL,
.clk_set_source_freq = NULL,
},
/* LCD controller clocks */
{ .id = LCDC_CLK,
.clk_activate = am335x_clk_lcdc_activate,
.clk_deactivate = NULL,
.clk_set_source = NULL,
.clk_accessible = NULL,
.clk_get_source_freq = am335x_clk_get_arm_disp_freq,
.clk_set_source_freq = am335x_clk_set_arm_disp_freq,
},
/* UART */
AM335X_NOOP_CLOCK_DEV(UART1_CLK),
AM335X_GENERIC_CLOCK_DEV(UART2_CLK),
AM335X_GENERIC_CLOCK_DEV(UART3_CLK),
AM335X_GENERIC_CLOCK_DEV(UART4_CLK),
AM335X_GENERIC_CLOCK_DEV(UART5_CLK),
AM335X_GENERIC_CLOCK_DEV(UART6_CLK),
/* DMTimer */
AM335X_GENERIC_CLOCK_DEV(TIMER2_CLK),
AM335X_GENERIC_CLOCK_DEV(TIMER3_CLK),
AM335X_GENERIC_CLOCK_DEV(TIMER4_CLK),
AM335X_GENERIC_CLOCK_DEV(TIMER5_CLK),
AM335X_GENERIC_CLOCK_DEV(TIMER6_CLK),
AM335X_GENERIC_CLOCK_DEV(TIMER7_CLK),
/* GPIO, we use hwmods as reference, not units in spec */
AM335X_GPIO_CLOCK_DEV(GPIO1_CLK),
AM335X_GPIO_CLOCK_DEV(GPIO2_CLK),
AM335X_GPIO_CLOCK_DEV(GPIO3_CLK),
AM335X_GPIO_CLOCK_DEV(GPIO4_CLK),
/* I2C we use hwmods as reference, not units in spec */
AM335X_GENERIC_CLOCK_DEV(I2C1_CLK),
AM335X_GENERIC_CLOCK_DEV(I2C2_CLK),
AM335X_GENERIC_CLOCK_DEV(I2C3_CLK),
/* McSPI we use hwmods as reference, not units in spec */
AM335X_GENERIC_CLOCK_DEV(SPI0_CLK),
AM335X_GENERIC_CLOCK_DEV(SPI1_CLK),
/* TSC_ADC */
AM335X_GENERIC_CLOCK_DEV(TSC_ADC_CLK),
/* EDMA */
AM335X_GENERIC_CLOCK_DEV(EDMA_TPCC_CLK),
AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC0_CLK),
AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC1_CLK),
AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC2_CLK),
/* MMCHS */
AM335X_MMCHS_CLOCK_DEV(MMC1_CLK),
AM335X_MMCHS_CLOCK_DEV(MMC2_CLK),
AM335X_MMCHS_CLOCK_DEV(MMC3_CLK),
/* PWMSS */
AM335X_GENERIC_CLOCK_DEV(PWMSS0_CLK),
AM335X_GENERIC_CLOCK_DEV(PWMSS1_CLK),
AM335X_GENERIC_CLOCK_DEV(PWMSS2_CLK),
/* System Mailbox clock */
AM335X_GENERIC_CLOCK_DEV(MAILBOX0_CLK),
/* SPINLOCK */
AM335X_GENERIC_CLOCK_DEV(SPINLOCK0_CLK),
/* PRU-ICSS */
{ .id = PRUSS_CLK,
.clk_activate = am335x_clk_pruss_activate,
.clk_deactivate = NULL,
.clk_set_source = NULL,
.clk_accessible = NULL,
.clk_get_source_freq = NULL,
.clk_set_source_freq = NULL,
},
/* RTC */
AM335X_GENERIC_CLOCK_DEV(RTC_CLK),
{ INVALID_CLK_IDENT, NULL, NULL, NULL, NULL }
};
struct am335x_clk_details {
clk_ident_t id;
uint32_t clkctrl_reg;
uint32_t clksel_reg;
};
#define _CLK_DETAIL(i, c, s) \
{ .id = (i), \
.clkctrl_reg = (c), \
.clksel_reg = (s), \
}
static struct am335x_clk_details g_am335x_clk_details[] = {
/* UART. UART0 clock not controllable. */
_CLK_DETAIL(UART1_CLK, 0, 0),
_CLK_DETAIL(UART2_CLK, CM_PER_UART1_CLKCTRL, 0),
_CLK_DETAIL(UART3_CLK, CM_PER_UART2_CLKCTRL, 0),
_CLK_DETAIL(UART4_CLK, CM_PER_UART3_CLKCTRL, 0),
_CLK_DETAIL(UART5_CLK, CM_PER_UART4_CLKCTRL, 0),
_CLK_DETAIL(UART6_CLK, CM_PER_UART5_CLKCTRL, 0),
/* DMTimer modules */
_CLK_DETAIL(TIMER2_CLK, CM_PER_TIMER2_CLKCTRL, CLKSEL_TIMER2_CLK),
_CLK_DETAIL(TIMER3_CLK, CM_PER_TIMER3_CLKCTRL, CLKSEL_TIMER3_CLK),
_CLK_DETAIL(TIMER4_CLK, CM_PER_TIMER4_CLKCTRL, CLKSEL_TIMER4_CLK),
_CLK_DETAIL(TIMER5_CLK, CM_PER_TIMER5_CLKCTRL, CLKSEL_TIMER5_CLK),
_CLK_DETAIL(TIMER6_CLK, CM_PER_TIMER6_CLKCTRL, CLKSEL_TIMER6_CLK),
_CLK_DETAIL(TIMER7_CLK, CM_PER_TIMER7_CLKCTRL, CLKSEL_TIMER7_CLK),
/* GPIO modules, hwmods start with gpio1 */
_CLK_DETAIL(GPIO1_CLK, CM_WKUP_GPIO0_CLKCTRL, 0),
_CLK_DETAIL(GPIO2_CLK, CM_PER_GPIO1_CLKCTRL, 0),
_CLK_DETAIL(GPIO3_CLK, CM_PER_GPIO2_CLKCTRL, 0),
_CLK_DETAIL(GPIO4_CLK, CM_PER_GPIO3_CLKCTRL, 0),
/* I2C modules, hwmods start with i2c1 */
_CLK_DETAIL(I2C1_CLK, CM_WKUP_I2C0_CLKCTRL, 0),
_CLK_DETAIL(I2C2_CLK, CM_PER_I2C1_CLKCTRL, 0),
_CLK_DETAIL(I2C3_CLK, CM_PER_I2C2_CLKCTRL, 0),
/* McSPI modules, hwmods start with spi0 */
_CLK_DETAIL(SPI0_CLK, CM_PER_SPI0_CLKCTRL, 0),
_CLK_DETAIL(SPI1_CLK, CM_PER_SPI1_CLKCTRL, 0),
/* TSC_ADC module */
_CLK_DETAIL(TSC_ADC_CLK, CM_WKUP_ADC_TSC_CLKCTRL, 0),
/* EDMA modules */
_CLK_DETAIL(EDMA_TPCC_CLK, CM_PER_TPCC_CLKCTRL, 0),
_CLK_DETAIL(EDMA_TPTC0_CLK, CM_PER_TPTC0_CLKCTRL, 0),
_CLK_DETAIL(EDMA_TPTC1_CLK, CM_PER_TPTC1_CLKCTRL, 0),
_CLK_DETAIL(EDMA_TPTC2_CLK, CM_PER_TPTC2_CLKCTRL, 0),
/* MMCHS modules, hwmods start with mmc1*/
_CLK_DETAIL(MMC1_CLK, CM_PER_MMC0_CLKCTRL, 0),
_CLK_DETAIL(MMC2_CLK, CM_PER_MMC1_CLKCTRL, 0),
_CLK_DETAIL(MMC3_CLK, CM_PER_MMC1_CLKCTRL, 0),
/* PWMSS modules */
_CLK_DETAIL(PWMSS0_CLK, CM_PER_EPWMSS0_CLKCTRL, 0),
_CLK_DETAIL(PWMSS1_CLK, CM_PER_EPWMSS1_CLKCTRL, 0),
_CLK_DETAIL(PWMSS2_CLK, CM_PER_EPWMSS2_CLKCTRL, 0),
_CLK_DETAIL(MAILBOX0_CLK, CM_PER_MAILBOX0_CLKCTRL, 0),
_CLK_DETAIL(SPINLOCK0_CLK, CM_PER_SPINLOCK0_CLKCTRL, 0),
/* RTC module */
_CLK_DETAIL(RTC_CLK, CM_RTC_RTC_CLKCTRL, 0),
{ INVALID_CLK_IDENT, 0},
};
/* Read/Write macros */
#define prcm_read_4(reg) \
bus_space_read_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg)
#define prcm_write_4(reg, val) \
bus_space_write_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg, val)
void am335x_prcm_setup_dmtimer(int);
static int
am335x_prcm_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_is_compatible(dev, "ti,am3-prcm")) {
device_set_desc(dev, "AM335x Power and Clock Management");
return(BUS_PROBE_DEFAULT);
}
return (ENXIO);
}
static int
am335x_prcm_attach(device_t dev)
{
struct am335x_prcm_softc *sc = device_get_softc(dev);
if (am335x_prcm_sc)
return (ENXIO);
if (bus_alloc_resources(dev, am335x_prcm_spec, sc->res)) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
}
sc->bst = rman_get_bustag(sc->res[0]);
sc->bsh = rman_get_bushandle(sc->res[0]);
am335x_prcm_sc = sc;
ti_cpu_reset = am335x_prcm_reset;
return (0);
}
static void
am335x_prcm_new_pass(device_t dev)
{
struct am335x_prcm_softc *sc = device_get_softc(dev);
unsigned int sysclk, fclk;
sc = device_get_softc(dev);
if (sc->attach_done ||
bus_current_pass < (BUS_PASS_TIMER + BUS_PASS_ORDER_EARLY)) {
bus_generic_new_pass(dev);
return;
}
sc->attach_done = 1;
if (am335x_clk_get_sysclk_freq(NULL, &sysclk) != 0)
sysclk = 0;
if (am335x_clk_get_arm_fclk_freq(NULL, &fclk) != 0)
fclk = 0;
if (sysclk && fclk)
device_printf(dev, "Clocks: System %u.%01u MHz, CPU %u MHz\n",
sysclk/1000000, (sysclk % 1000000)/100000, fclk/1000000);
else {
device_printf(dev, "can't read frequencies yet (SCM device not ready?)\n");
goto fail;
}
return;
fail:
device_detach(dev);
return;
}
static device_method_t am335x_prcm_methods[] = {
DEVMETHOD(device_probe, am335x_prcm_probe),
DEVMETHOD(device_attach, am335x_prcm_attach),
/* Bus interface */
DEVMETHOD(bus_new_pass, am335x_prcm_new_pass),
{ 0, 0 }
};
static driver_t am335x_prcm_driver = {
"am335x_prcm",
am335x_prcm_methods,
sizeof(struct am335x_prcm_softc),
};
static devclass_t am335x_prcm_devclass;
EARLY_DRIVER_MODULE(am335x_prcm, simplebus, am335x_prcm_driver,
am335x_prcm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(am335x_prcm, 1);
MODULE_DEPEND(am335x_prcm, ti_scm, 1, 1, 1);
static struct am335x_clk_details*
am335x_clk_details(clk_ident_t id)
{
struct am335x_clk_details *walker;
for (walker = g_am335x_clk_details; walker->id != INVALID_CLK_IDENT; walker++) {
if (id == walker->id)
return (walker);
}
return NULL;
}
static int
am335x_clk_noop_activate(struct ti_clock_dev *clkdev)
{
return (0);
}
static int
am335x_clk_generic_activate(struct ti_clock_dev *clkdev)
{
struct am335x_prcm_softc *sc = am335x_prcm_sc;
struct am335x_clk_details* clk_details;
if (sc == NULL)
return ENXIO;
clk_details = am335x_clk_details(clkdev->id);
if (clk_details == NULL)
return (ENXIO);
/* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */
prcm_write_4(clk_details->clkctrl_reg, 2);
while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 2)
DELAY(10);
return (0);
}
static int
am335x_clk_gpio_activate(struct ti_clock_dev *clkdev)
{
struct am335x_prcm_softc *sc = am335x_prcm_sc;
struct am335x_clk_details* clk_details;
if (sc == NULL)
return ENXIO;
clk_details = am335x_clk_details(clkdev->id);
if (clk_details == NULL)
return (ENXIO);
/* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */
/* set *_CLKCTRL register OPTFCLKEN_GPIO_1_G DBCLK[18] to FCLK_EN(1) */
prcm_write_4(clk_details->clkctrl_reg, 2 | (1 << 18));
while ((prcm_read_4(clk_details->clkctrl_reg) &
(3 | (1 << 18) )) != (2 | (1 << 18)))
DELAY(10);
return (0);
}
static int
am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev)
{
return(0);
}
static int
am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev)
{
struct am335x_prcm_softc *sc = am335x_prcm_sc;
struct am335x_clk_details* clk_details;
if (sc == NULL)
return ENXIO;
clk_details = am335x_clk_details(clkdev->id);
if (clk_details == NULL)
return (ENXIO);
/* set *_CLKCTRL register MODULEMODE[1:0] to disable(0) */
prcm_write_4(clk_details->clkctrl_reg, 0);
while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 0)
DELAY(10);
return (0);
}
static int
am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc)
{
return (0);
}
static int
am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc)
{
struct am335x_prcm_softc *sc = am335x_prcm_sc;
struct am335x_clk_details* clk_details;
uint32_t reg;
if (sc == NULL)
return ENXIO;
clk_details = am335x_clk_details(clkdev->id);
if (clk_details == NULL)
return (ENXIO);
switch (clksrc) {
case EXT_CLK:
reg = 0; /* SEL2: TCLKIN clock */
break;
case SYSCLK_CLK:
reg = 1; /* SEL1: CLK_M_OSC clock */
break;
case F32KHZ_CLK:
reg = 2; /* SEL3: CLK_32KHZ clock */
break;
default:
return (ENXIO);
}
prcm_write_4(clk_details->clksel_reg, reg);
while ((prcm_read_4(clk_details->clksel_reg) & 0x3) != reg)
DELAY(10);
return (0);
}
static int
am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq)
{
*freq = 96000000;
return (0);
}
static int
am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq)
{
uint32_t ctrl_status;
/* Read the input clock freq from the control module. */
if (ti_scm_reg_read_4(SCM_CTRL_STATUS, &ctrl_status))
return (ENXIO);
switch ((ctrl_status>>22) & 0x3) {
case 0x0:
/* 19.2Mhz */
*freq = 19200000;
break;
case 0x1:
/* 24Mhz */
*freq = 24000000;
break;
case 0x2:
/* 25Mhz */
*freq = 25000000;
break;
case 0x3:
/* 26Mhz */
*freq = 26000000;
break;
}
return (0);
}
#define DPLL_BYP_CLKSEL(reg) ((reg>>23) & 1)
#define DPLL_DIV(reg) ((reg & 0x7f)+1)
#define DPLL_MULT(reg) ((reg>>8) & 0x7FF)
#define DPLL_MAX_MUL 0x800
#define DPLL_MAX_DIV 0x80
static int
am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq)
{
uint32_t reg;
uint32_t sysclk;
reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_MPU);
/*Check if we are running in bypass */
if (DPLL_BYP_CLKSEL(reg))
return ENXIO;
am335x_clk_get_sysclk_freq(NULL, &sysclk);
*freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg));
return(0);
}
static int
am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq)
{
uint32_t reg;
uint32_t sysclk;
reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_DISP);
/*Check if we are running in bypass */
if (DPLL_BYP_CLKSEL(reg))
return ENXIO;
am335x_clk_get_sysclk_freq(NULL, &sysclk);
*freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg));
return(0);
}
static int
am335x_clk_set_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int freq)
{
uint32_t sysclk;
uint32_t mul, div;
uint32_t i, j;
unsigned int delta, min_delta;
am335x_clk_get_sysclk_freq(NULL, &sysclk);
/* Bypass mode */
prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x4);
/* Make sure it's in bypass mode */
while (!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP)
& (1 << 8)))
DELAY(10);
/* Dumb and non-optimal implementation */
min_delta = freq;
for (i = 1; i < DPLL_MAX_MUL; i++) {
for (j = 1; j < DPLL_MAX_DIV; j++) {
delta = abs(freq - i*(sysclk/j));
if (delta < min_delta) {
mul = i;
div = j;
min_delta = delta;
}
if (min_delta == 0)
break;
}
}
prcm_write_4(CM_WKUP_CM_CLKSEL_DPLL_DISP, (mul << 8) | (div - 1));
/* Locked mode */
prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x7);
int timeout = 10000;
while ((!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP)
& (1 << 0))) && timeout--)
DELAY(10);
return(0);
}
static void
am335x_prcm_reset(void)
{
prcm_write_4(PRM_RSTCTRL, (1<<1));
}
static int
am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev)
{
struct am335x_prcm_softc *sc = am335x_prcm_sc;
if (sc == NULL)
return ENXIO;
/* set MODULENAME to ENABLE */
prcm_write_4(CM_PER_CPGMAC0_CLKCTRL, 2);
/* wait for IDLEST to become Func(0) */
while(prcm_read_4(CM_PER_CPGMAC0_CLKCTRL) & (3<<16));
/*set CLKTRCTRL to SW_WKUP(2) */
prcm_write_4(CM_PER_CPSW_CLKSTCTRL, 2);
/* wait for 125 MHz OCP clock to become active */
while((prcm_read_4(CM_PER_CPSW_CLKSTCTRL) & (1<<4)) == 0);
return(0);
}
static int
am335x_clk_musb0_activate(struct ti_clock_dev *clkdev)
{
struct am335x_prcm_softc *sc = am335x_prcm_sc;
if (sc == NULL)
return ENXIO;
/* set ST_DPLL_CLKDCOLDO(9) to CLK_GATED(1) */
/* set DPLL_CLKDCOLDO_GATE_CTRL(8) to CLK_ENABLE(1)*/
prcm_write_4(CM_WKUP_CM_CLKDCOLDO_DPLL_PER, 0x300);
/*set MODULEMODE to ENABLE(2) */
prcm_write_4(CM_PER_USB0_CLKCTRL, 2);
/* wait for MODULEMODE to become ENABLE(2) */
while ((prcm_read_4(CM_PER_USB0_CLKCTRL) & 0x3) != 2)
DELAY(10);
/* wait for IDLEST to become Func(0) */
while(prcm_read_4(CM_PER_USB0_CLKCTRL) & (3<<16))
DELAY(10);
return(0);
}
static int
am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev)
{
struct am335x_prcm_softc *sc = am335x_prcm_sc;
if (sc == NULL)
return (ENXIO);
/*
* For now set frequency to 2*VGA_PIXEL_CLOCK
*/
am335x_clk_set_arm_disp_freq(clkdev, 25175000*2);
/*set MODULEMODE to ENABLE(2) */
prcm_write_4(CM_PER_LCDC_CLKCTRL, 2);
/* wait for MODULEMODE to become ENABLE(2) */
while ((prcm_read_4(CM_PER_LCDC_CLKCTRL) & 0x3) != 2)
DELAY(10);
/* wait for IDLEST to become Func(0) */
while(prcm_read_4(CM_PER_LCDC_CLKCTRL) & (3<<16))
DELAY(10);
return (0);
}
static int
am335x_clk_pruss_activate(struct ti_clock_dev *clkdev)
{
struct am335x_prcm_softc *sc = am335x_prcm_sc;
if (sc == NULL)
return (ENXIO);
/* Set MODULEMODE to ENABLE(2) */
prcm_write_4(CM_PER_PRUSS_CLKCTRL, 2);
/* Wait for MODULEMODE to become ENABLE(2) */
while ((prcm_read_4(CM_PER_PRUSS_CLKCTRL) & 0x3) != 2)
DELAY(10);
/* Set CLKTRCTRL to SW_WKUP(2) */
prcm_write_4(CM_PER_PRUSS_CLKSTCTRL, 2);
/* Wait for the 200 MHz OCP clock to become active */
while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<4)) == 0)
DELAY(10);
/* Wait for the 200 MHz IEP clock to become active */
while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<5)) == 0)
DELAY(10);
/* Wait for the 192 MHz UART clock to become active */
while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<6)) == 0)
DELAY(10);
/* Select L3F as OCP clock */
prcm_write_4(CLKSEL_PRUSS_OCP_CLK, 0);
while ((prcm_read_4(CLKSEL_PRUSS_OCP_CLK) & 0x3) != 0)
DELAY(10);
/* Clear the RESET bit */
prcm_write_4(PRM_PER_RSTCTRL, prcm_read_4(PRM_PER_RSTCTRL) & ~2);
return (0);
}

View File

@ -46,9 +46,10 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_hwmods.h>
#include <arm/ti/ti_scm.h>
#include <arm/ti/ti_sysc.h>
#include <dev/extres/syscon/syscon.h>
#include "syscon_if.h"
#include "am335x_pwm.h"
#include "am335x_scm.h"
@ -59,6 +60,11 @@ __FBSDID("$FreeBSD$");
#define CLKCONFIG_EPWMCLK_EN (1 << 8)
#define PWMSS_CLKSTATUS 0x0C
/* TRM chapter 2 memory map table 2-3 + VER register location */
#define PWMSS_REV_0 0x0000
#define PWMSS_REV_1 0x2000
#define PWMSS_REV_2 0x4000
static device_probe_t am335x_pwmss_probe;
static device_attach_t am335x_pwmss_attach;
static device_detach_t am335x_pwmss_detach;
@ -66,7 +72,7 @@ static device_detach_t am335x_pwmss_detach;
struct am335x_pwmss_softc {
struct simplebus_softc sc_simplebus;
device_t sc_dev;
clk_ident_t sc_clk;
struct syscon *syscon;
};
static device_method_t am335x_pwmss_methods[] = {
@ -97,36 +103,45 @@ am335x_pwmss_attach(device_t dev)
{
struct am335x_pwmss_softc *sc;
uint32_t reg, id;
phandle_t node;
uint64_t rev_address;
phandle_t node, opp_table;
sc = device_get_softc(dev);
sc->sc_dev = dev;
sc->sc_clk = ti_hwmods_get_clock(dev);
if (sc->sc_clk == INVALID_CLK_IDENT) {
device_printf(dev, "failed to get device id based on ti,hwmods\n");
return (EINVAL);
/* FIXME: For now; Go and kidnap syscon from opp-table */
opp_table = OF_finddevice("/opp-table");
if (opp_table == -1) {
device_printf(dev, "Cant find /opp-table\n");
return (ENXIO);
}
if (!OF_hasprop(opp_table, "syscon")) {
device_printf(dev, "/opp-table doesnt have required syscon property\n");
return (ENXIO);
}
if (syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon) != 0) {
device_printf(dev, "Failed to get syscon\n");
return (ENXIO);
}
ti_prcm_clk_enable(sc->sc_clk);
ti_scm_reg_read_4(SCM_PWMSS_CTRL, &reg);
switch (sc->sc_clk) {
case PWMSS0_CLK:
id = 0;
break;
case PWMSS1_CLK:
id = 1;
break;
ti_sysc_clock_enable(device_get_parent(dev));
case PWMSS2_CLK:
id = 2;
break;
default:
device_printf(dev, "unknown pwmss clock id: %d\n", sc->sc_clk);
return (EINVAL);
rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
switch (rev_address) {
case PWMSS_REV_0:
id = 0;
break;
case PWMSS_REV_1:
id = 1;
break;
case PWMSS_REV_2:
id = 2;
break;
}
reg = SYSCON_READ_4(sc->syscon, SCM_PWMSS_CTRL);
reg |= (1 << id);
ti_scm_reg_write_4(SCM_PWMSS_CTRL, reg);
SYSCON_WRITE_4(sc->syscon, SCM_PWMSS_CTRL, reg);
node = ofw_bus_get_node(dev);
@ -161,3 +176,4 @@ DEFINE_CLASS_1(am335x_pwmss, am335x_pwmss_driver, am335x_pwmss_methods,
static devclass_t am335x_pwmss_devclass;
DRIVER_MODULE(am335x_pwmss, simplebus, am335x_pwmss_driver, am335x_pwmss_devclass, 0, 0);
MODULE_VERSION(am335x_pwmss, 1);
MODULE_DEPEND(am335x_pwmss, ti_sysc, 1, 1, 1);

View File

@ -39,8 +39,9 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/am335x/am335x_rtcvar.h>
#include <arm/ti/am335x/am335x_rtcreg.h>
@ -110,7 +111,7 @@ am335x_rtc_attach(device_t dev)
RTC_LOCK_INIT(sc);
/* Enable the RTC module. */
ti_prcm_clk_enable(RTC_CLK);
ti_sysc_clock_enable(device_get_parent(dev));
rev = RTC_READ4(sc, RTC_REVISION);
device_printf(dev, "AM335X RTC v%d.%d.%d\n",
(rev >> 8) & 0x7, (rev >> 6) & 0x3, rev & 0x3f);
@ -209,3 +210,4 @@ static devclass_t am335x_rtc_devclass;
DRIVER_MODULE(am335x_rtc, simplebus, am335x_rtc_driver, am335x_rtc_devclass, 0, 0);
MODULE_VERSION(am335x_rtc, 1);
MODULE_DEPEND(am335x_rtc, simplebus, 1, 1, 1);
MODULE_DEPEND(am335x_rtc, ti_sysc, 1, 1, 1);

View File

@ -40,11 +40,15 @@ __FBSDID("$FreeBSD$");
#include <arm/ti/ti_cpuid.h>
#include <arm/ti/ti_scm.h>
#include <dev/extres/syscon/syscon.h>
#include "syscon_if.h"
#define TZ_ZEROC 2731
struct am335x_scm_softc {
int sc_last_temp;
struct sysctl_oid *sc_temp_oid;
struct syscon *syscon;
};
static int
@ -60,7 +64,7 @@ am335x_scm_temp_sysctl(SYSCTL_HANDLER_ARGS)
/* Read the temperature and convert to Kelvin. */
for(i = 50; i > 0; i--) {
ti_scm_reg_read_4(SCM_BGAP_CTRL, &reg);
reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL);
if ((reg & SCM_BGAP_EOCZ) == 0)
break;
DELAY(50);
@ -96,6 +100,9 @@ am335x_scm_identify(driver_t *driver, device_t parent)
static int
am335x_scm_probe(device_t dev)
{
/* Just allow the first one */
if (strcmp(device_get_nameunit(dev), "am335x_scm0") != 0)
return (ENXIO);
device_set_desc(dev, "AM335x Control Module Extension");
@ -109,21 +116,40 @@ am335x_scm_attach(device_t dev)
struct sysctl_ctx_list *ctx;
struct sysctl_oid_list *tree;
uint32_t reg;
phandle_t opp_table;
int err;
sc = device_get_softc(dev);
/* FIXME: For now; Go and kidnap syscon from opp-table */
opp_table = OF_finddevice("/opp-table");
if (opp_table == -1) {
device_printf(dev, "Cant find /opp-table\n");
return (ENXIO);
}
if (!OF_hasprop(opp_table, "syscon")) {
device_printf(dev, "/opp-table missing syscon property\n");
return (ENXIO);
}
err = syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon);
if (err) {
device_printf(dev, "Failed to get syscon\n");
return (ENXIO);
}
/* Reset the digital outputs. */
ti_scm_reg_write_4(SCM_BGAP_CTRL, 0);
ti_scm_reg_read_4(SCM_BGAP_CTRL, &reg);
SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, 0);
reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL);
DELAY(500);
/* Set continous mode. */
ti_scm_reg_write_4(SCM_BGAP_CTRL, SCM_BGAP_CONTCONV);
ti_scm_reg_read_4(SCM_BGAP_CTRL, &reg);
SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, SCM_BGAP_CONTCONV);
reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL);
DELAY(500);
/* Start the ADC conversion. */
reg = SCM_BGAP_CLRZ | SCM_BGAP_CONTCONV | SCM_BGAP_SOC;
ti_scm_reg_write_4(SCM_BGAP_CTRL, reg);
SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, reg);
/* Temperature sysctl. */
sc = device_get_softc(dev);
ctx = device_get_sysctl_ctx(dev);
tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
sc->sc_temp_oid = SYSCTL_ADD_PROC(ctx, tree, OID_AUTO,
@ -145,7 +171,7 @@ am335x_scm_detach(device_t dev)
sysctl_remove_oid(sc->sc_temp_oid, 1, 0);
/* Stop the bandgap ADC. */
ti_scm_reg_write_4(SCM_BGAP_CTRL, SCM_BGAP_BGOFF);
SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, SCM_BGAP_BGOFF);
return (0);
}
@ -169,4 +195,4 @@ static devclass_t am335x_scm_devclass;
DRIVER_MODULE(am335x_scm, ti_scm, am335x_scm_driver, am335x_scm_devclass, 0, 0);
MODULE_VERSION(am335x_scm, 1);
MODULE_DEPEND(am335x_scm, ti_scm, 1, 1, 1);
MODULE_DEPEND(am335x_scm, ti_scm_syscon, 1, 1, 1);

View File

@ -0,0 +1,121 @@
/*-
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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/fbio.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/resource.h>
#include <machine/bus.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <vm/pmap.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#define TI_AM335X_USB_PHY 1
#define TI_AM335X_USB_PHY_END 0
static struct ofw_compat_data compat_data[] = {
{ "ti,am335x-usb-phy", TI_AM335X_USB_PHY },
{ NULL, TI_AM335X_USB_PHY_END }
};
struct ti_usb_phy_softc {
device_t dev;
};
static int ti_usb_phy_probe(device_t dev);
static int ti_usb_phy_attach(device_t dev);
static int ti_usb_phy_detach(device_t dev);
static int
ti_usb_phy_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, "TI AM335x USB PHY");
if (!bootverbose)
device_quiet(dev);
return (BUS_PROBE_DEFAULT);
}
static int
ti_usb_phy_attach(device_t dev)
{
struct ti_usb_phy_softc *sc;
phandle_t node;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(dev);
/* FIXME: Add dev/extres/phy/ interface */
return (bus_generic_attach(dev));
}
static int
ti_usb_phy_detach(device_t dev)
{
return (EBUSY);
}
static device_method_t ti_usb_phy_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_usb_phy_probe),
DEVMETHOD(device_attach, ti_usb_phy_attach),
DEVMETHOD(device_detach, ti_usb_phy_detach),
DEVMETHOD_END
};
DEFINE_CLASS_1(ti_usb_phy, ti_usb_phy_driver, ti_usb_phy_methods,
sizeof(struct ti_usb_phy_softc), simplebus_driver);
static devclass_t ti_usb_phy_devclass;
EARLY_DRIVER_MODULE(ti_usb_phy, simplebus, ti_usb_phy_driver,
ti_usb_phy_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);
MODULE_VERSION(ti_usb_phy, 1);
MODULE_DEPEND(ti_usb_phy, ti_sysc, 1, 1, 1);

View File

@ -1,226 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@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 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/stdint.h>
#include <sys/stddef.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_util.h>
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#include <dev/usb/controller/musb_otg.h>
#include <dev/usb/usb_debug.h>
#include <sys/rman.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_scm.h>
#include <arm/ti/am335x/am335x_scm.h>
#define AM335X_USB_PORTS 2
#define USBSS_REVREG 0x00
#define USBSS_SYSCONFIG 0x10
#define USBSS_SYSCONFIG_SRESET 1
#define USBCTRL_REV 0x00
#define USBCTRL_CTRL 0x14
#define USBCTRL_STAT 0x18
#define USBCTRL_IRQ_STAT0 0x30
#define IRQ_STAT0_RXSHIFT 16
#define IRQ_STAT0_TXSHIFT 0
#define USBCTRL_IRQ_STAT1 0x34
#define IRQ_STAT1_DRVVBUS (1 << 8)
#define USBCTRL_INTEN_SET0 0x38
#define USBCTRL_INTEN_SET1 0x3C
#define USBCTRL_INTEN_USB_ALL 0x1ff
#define USBCTRL_INTEN_USB_SOF (1 << 3)
#define USBCTRL_INTEN_CLR0 0x40
#define USBCTRL_INTEN_CLR1 0x44
#define USBCTRL_UTMI 0xE0
#define USBCTRL_UTMI_FSDATAEXT (1 << 1)
#define USBCTRL_MODE 0xE8
#define USBCTRL_MODE_IDDIG (1 << 8)
#define USBCTRL_MODE_IDDIGMUX (1 << 7)
#define USBSS_WRITE4(sc, reg, val) \
bus_write_4((sc)->sc_mem_res, (reg), (val))
#define USBSS_READ4(sc, reg) \
bus_read_4((sc)->sc_mem_res, (reg))
static device_probe_t usbss_probe;
static device_attach_t usbss_attach;
static device_detach_t usbss_detach;
struct usbss_softc {
struct simplebus_softc simplebus_sc;
struct resource *sc_mem_res;
int sc_mem_rid;
};
static int
usbss_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "ti,am33xx-usb"))
return (ENXIO);
device_set_desc(dev, "TI AM33xx integrated USB OTG controller");
return (BUS_PROBE_DEFAULT);
}
static int
usbss_attach(device_t dev)
{
struct usbss_softc *sc = device_get_softc(dev);
int i;
uint32_t rev;
phandle_t node;
/* Request the memory resources */
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&sc->sc_mem_rid, RF_ACTIVE);
if (sc->sc_mem_res == NULL) {
device_printf(dev,
"Error: could not allocate mem resources\n");
return (ENXIO);
}
/* Enable device clocks. */
ti_prcm_clk_enable(MUSB0_CLK);
/*
* Reset USBSS, USB0 and USB1.
* The registers of USB subsystem must not be accessed while the
* reset pulse is active (200ns).
*/
USBSS_WRITE4(sc, USBSS_SYSCONFIG, USBSS_SYSCONFIG_SRESET);
DELAY(100);
i = 10;
while (USBSS_READ4(sc, USBSS_SYSCONFIG) & USBSS_SYSCONFIG_SRESET) {
DELAY(100);
if (i-- == 0) {
device_printf(dev, "reset timeout.\n");
return (ENXIO);
}
}
/* Read the module revision. */
rev = USBSS_READ4(sc, USBSS_REVREG);
device_printf(dev, "TI AM335X USBSS v%d.%d.%d\n",
(rev >> 8) & 7, (rev >> 6) & 3, rev & 63);
node = ofw_bus_get_node(dev);
if (node == -1) {
usbss_detach(dev);
return (ENXIO);
}
simplebus_init(dev, node);
/*
* Allow devices to identify.
*/
bus_generic_probe(dev);
/*
* Now walk the OFW tree and attach top-level devices.
*/
for (node = OF_child(node); node > 0; node = OF_peer(node))
simplebus_add_device(dev, node, 0, NULL, -1, NULL);
return (bus_generic_attach(dev));
}
static int
usbss_detach(device_t dev)
{
struct usbss_softc *sc = device_get_softc(dev);
/* Free resources if any */
if (sc->sc_mem_res)
bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
sc->sc_mem_res);
/* during module unload there are lots of children leftover */
device_delete_children(dev);
return (0);
}
static device_method_t usbss_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, usbss_probe),
DEVMETHOD(device_attach, usbss_attach),
DEVMETHOD(device_detach, usbss_detach),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD_END
};
DEFINE_CLASS_1(usbss, usbss_driver, usbss_methods,
sizeof(struct usbss_softc), simplebus_driver);
static devclass_t usbss_devclass;
DRIVER_MODULE(usbss, simplebus, usbss_driver, usbss_devclass, 0, 0);
MODULE_DEPEND(usbss, usb, 1, 1, 1);

View File

@ -8,7 +8,6 @@ arm/ti/am335x/am335x_gpio.c optional gpio
arm/ti/am335x/am335x_lcd.c optional sc | vt
arm/ti/am335x/am335x_lcd_syscons.c optional sc
arm/ti/am335x/am335x_pmic.c optional am335x_pmic
arm/ti/am335x/am335x_prcm.c standard
arm/ti/am335x/am335x_pwmss.c standard
dev/pwm/pwmbus_if.m standard
arm/ti/am335x/am335x_ehrpwm.c standard
@ -16,8 +15,9 @@ arm/ti/am335x/am335x_ecap.c standard
arm/ti/am335x/am335x_rtc.c optional am335x_rtc
arm/ti/am335x/am335x_scm.c standard
arm/ti/am335x/am335x_scm_padconf.c standard
arm/ti/am335x/am335x_usbss.c optional musb fdt
arm/ti/am335x/am335x_musb.c optional musb fdt
arm/ti/am335x/am335x_usb_phy.c optional musb fdt
arm/ti/am335x/am3359_cppi41.c optional musb fdt
arm/ti/am335x/tda19988.c optional hdmi

View File

@ -0,0 +1,152 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/libkern.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/extres/clk/clk_mux.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "clock_common.h"
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
void
read_clock_cells(device_t dev, struct clock_cell_info *clk) {
ssize_t numbytes_clocks;
phandle_t node, parent, *cells;
int index, ncells, rv;
node = ofw_bus_get_node(dev);
/* Get names of parent clocks */
numbytes_clocks = OF_getproplen(node, "clocks");
clk->num_clock_cells = numbytes_clocks / sizeof(cell_t);
/* Allocate space and get clock cells content */
/* clock_cells / clock_cells_ncells will be freed in
* find_parent_clock_names()
*/
clk->clock_cells = malloc(numbytes_clocks, M_DEVBUF, M_WAITOK|M_ZERO);
clk->clock_cells_ncells = malloc(clk->num_clock_cells*sizeof(uint8_t),
M_DEVBUF, M_WAITOK|M_ZERO);
OF_getencprop(node, "clocks", clk->clock_cells, numbytes_clocks);
/* Count number of clocks */
clk->num_real_clocks = 0;
for (index = 0; index < clk->num_clock_cells; index++) {
rv = ofw_bus_parse_xref_list_alloc(node, "clocks", "#clock-cells",
clk->num_real_clocks, &parent, &ncells, &cells);
if (rv != 0)
continue;
if (cells != NULL)
OF_prop_free(cells);
clk->clock_cells_ncells[index] = ncells;
index += ncells;
clk->num_real_clocks++;
}
}
int
find_parent_clock_names(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def) {
int index, clock_index, err;
bool found_all = true;
clk_t parent;
/* Figure out names */
for (index = 0, clock_index = 0; index < clk->num_clock_cells; index++) {
/* Get name of parent clock */
err = clk_get_by_ofw_index(dev, 0, clock_index, &parent);
if (err != 0) {
clock_index++;
found_all = false;
DPRINTF(dev, "Failed to find clock_cells[%d]=0x%x\n",
index, clk->clock_cells[index]);
index += clk->clock_cells_ncells[index];
continue;
}
def->parent_names[clock_index] = clk_get_name(parent);
clk_release(parent);
DPRINTF(dev, "Found parent clock[%d/%d]: %s\n",
clock_index, clk->num_real_clocks,
def->parent_names[clock_index]);
clock_index++;
index += clk->clock_cells_ncells[index];
}
if (!found_all) {
return 1;
}
free(clk->clock_cells, M_DEVBUF);
free(clk->clock_cells_ncells, M_DEVBUF);
return 0;
}
void
create_clkdef(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def) {
def->id = 1;
clk_parse_ofw_clk_name(dev, ofw_bus_get_node(dev), &def->name);
DPRINTF(dev, "node name: %s\n", def->name);
def->parent_cnt = clk->num_real_clocks;
def->parent_names = malloc(clk->num_real_clocks*sizeof(char *),
M_OFWPROP, M_WAITOK);
}
void
free_clkdef(struct clknode_init_def *def) {
OF_prop_free(__DECONST(char *, def->name));
OF_prop_free(def->parent_names);
}

View File

@ -0,0 +1,43 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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$");
struct clock_cell_info {
cell_t *clock_cells;
uint8_t *clock_cells_ncells;
uint32_t num_clock_cells;
uint8_t num_real_clocks;
};
void read_clock_cells(device_t dev, struct clock_cell_info *clk);
int find_parent_clock_names(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def);
void create_clkdef(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def);
void free_clkdef(struct clknode_init_def *def);

View File

@ -0,0 +1,219 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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/malloc.h>
#include <dev/extres/clk/clk.h>
#include <arm/ti/clk/ti_clk_clkctrl.h>
#include "clkdev_if.h"
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
/*
* clknode for clkctrl, implements gate and mux (for gpioc)
*/
#define GPIO_X_GDBCLK_MASK 0x00040000
#define IDLEST_MASK 0x00030000
#define MODULEMODE_MASK 0x00000003
#define GPIOX_GDBCLK_ENABLE 0x00040000
#define GPIOX_GDBCLK_DISABLE 0x00000000
#define IDLEST_FUNC 0x00000000
#define IDLEST_TRANS 0x00010000
#define IDLEST_IDLE 0x00020000
#define IDLEST_DISABLE 0x00030000
#define MODULEMODE_DISABLE 0x0
#define MODULEMODE_ENABLE 0x2
struct ti_clkctrl_clknode_sc {
device_t dev;
bool gdbclk;
/* omap4-cm range.host + ti,clkctrl reg[0] */
uint32_t register_offset;
};
#define WRITE4(_clk, off, val) \
CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
#define READ4(_clk, off, val) \
CLKDEV_READ_4(clknode_get_device(_clk), off, val)
#define DEVICE_LOCK(_clk) \
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
#define DEVICE_UNLOCK(_clk) \
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
static int
ti_clkctrl_init(struct clknode *clk, device_t dev)
{
struct ti_clkctrl_clknode_sc *sc;
sc = clknode_get_softc(clk);
sc->dev = dev;
clknode_init_parent_idx(clk, 0);
return (0);
}
static int
ti_clkctrl_set_gdbclk_gate(struct clknode *clk, bool enable)
{
struct ti_clkctrl_clknode_sc *sc;
uint32_t val, gpio_x_gdbclk;
uint32_t timeout = 100;
sc = clknode_get_softc(clk);
READ4(clk, sc->register_offset, &val);
DPRINTF(sc->dev, "val(%x) & (%x | %x = %x)\n",
val, GPIO_X_GDBCLK_MASK, MODULEMODE_MASK,
GPIO_X_GDBCLK_MASK | MODULEMODE_MASK);
if (enable) {
val = val & MODULEMODE_MASK;
val |= GPIOX_GDBCLK_ENABLE;
} else {
val = val & MODULEMODE_MASK;
val |= GPIOX_GDBCLK_DISABLE;
}
DPRINTF(sc->dev, "val %x\n", val);
WRITE4(clk, sc->register_offset, val);
/* Wait */
while (timeout) {
READ4(clk, sc->register_offset, &val);
gpio_x_gdbclk = val & GPIO_X_GDBCLK_MASK;
if (enable && (gpio_x_gdbclk == GPIOX_GDBCLK_ENABLE))
break;
else if (!enable && (gpio_x_gdbclk == GPIOX_GDBCLK_DISABLE))
break;
DELAY(10);
timeout--;
}
if (timeout == 0) {
device_printf(sc->dev, "ti_clkctrl_set_gdbclk_gate: Timeout\n");
return (1);
}
return (0);
}
static int
ti_clkctrl_set_gate(struct clknode *clk, bool enable)
{
struct ti_clkctrl_clknode_sc *sc;
uint32_t val, idlest, module;
uint32_t timeout=100;
int err;
sc = clknode_get_softc(clk);
if (sc->gdbclk) {
err = ti_clkctrl_set_gdbclk_gate(clk, enable);
return (err);
}
READ4(clk, sc->register_offset, &val);
if (enable)
WRITE4(clk, sc->register_offset, MODULEMODE_ENABLE);
else
WRITE4(clk, sc->register_offset, MODULEMODE_DISABLE);
while (timeout) {
READ4(clk, sc->register_offset, &val);
idlest = val & IDLEST_MASK;
module = val & MODULEMODE_MASK;
if (enable &&
(idlest == IDLEST_FUNC || idlest == IDLEST_TRANS) &&
module == MODULEMODE_ENABLE)
break;
else if (!enable &&
idlest == IDLEST_DISABLE &&
module == MODULEMODE_DISABLE)
break;
DELAY(10);
timeout--;
}
if (timeout == 0) {
device_printf(sc->dev, "ti_clkctrl_set_gate: Timeout\n");
return (1);
}
return (0);
}
static clknode_method_t ti_clkctrl_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, ti_clkctrl_init),
CLKNODEMETHOD(clknode_set_gate, ti_clkctrl_set_gate),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(ti_clkctrl_clknode, ti_clkctrl_clknode_class,
ti_clkctrl_clknode_methods, sizeof(struct ti_clkctrl_clknode_sc),
clknode_class);
int
ti_clknode_clkctrl_register(struct clkdom *clkdom,
struct ti_clk_clkctrl_def *clkdef)
{
struct clknode *clk;
struct ti_clkctrl_clknode_sc *sc;
clk = clknode_create(clkdom, &ti_clkctrl_clknode_class,
&clkdef->clkdef);
if (clk == NULL) {
return (1);
}
sc = clknode_get_softc(clk);
sc->register_offset = clkdef->register_offset;
sc->gdbclk = clkdef->gdbclk;
if (clknode_register(clkdom, clk) == NULL) {
return (2);
}
return (0);
}

View File

@ -0,0 +1,43 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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 _TI_CLK_CLKCTRL_H_
#define _TI_CLK_CLKCTRL_H_
#include <dev/extres/clk/clk.h>
struct ti_clk_clkctrl_def {
struct clknode_init_def clkdef;
bool gdbclk;
uint32_t register_offset;
};
int ti_clknode_clkctrl_register(struct clkdom *clkdom, struct ti_clk_clkctrl_def *clkdef);
#endif /* _TI_CLK_CLKCTRL_H_ */

View File

@ -0,0 +1,341 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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.
*
* based on sys/arm/allwinner/clkng/aw_clk_np.c
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <dev/extres/clk/clk.h>
#include <arm/ti/clk/ti_clk_dpll.h>
#include "clkdev_if.h"
/*
* clknode for clocks matching the formula :
*
* clk = clkin * n / p
*
*/
struct ti_dpll_clknode_sc {
uint32_t ti_clkmode_offset; /* control */
uint8_t ti_clkmode_flags;
uint32_t ti_idlest_offset;
uint32_t ti_clksel_offset; /* mult-div1 */
struct ti_clk_factor n; /* ti_clksel_mult */
struct ti_clk_factor p; /* ti_clksel_div */
uint32_t ti_autoidle_offset;
};
#define WRITE4(_clk, off, val) \
CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
#define READ4(_clk, off, val) \
CLKDEV_READ_4(clknode_get_device(_clk), off, val)
#define DEVICE_LOCK(_clk) \
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
#define DEVICE_UNLOCK(_clk) \
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
static int
ti_dpll_clk_init(struct clknode *clk, device_t dev)
{
struct ti_dpll_clknode_sc *sc;
sc = clknode_get_softc(clk);
clknode_init_parent_idx(clk, 0);
return (0);
}
/* helper to keep aw_clk_np_find_best "intact" */
static inline uint32_t
ti_clk_factor_get_max(struct ti_clk_factor *factor)
{
uint32_t max;
if (factor->flags & TI_CLK_FACTOR_FIXED)
max = factor->value;
else {
max = (1 << factor->width);
}
return (max);
}
static inline uint32_t
ti_clk_factor_get_min(struct ti_clk_factor *factor)
{
uint32_t min;
if (factor->flags & TI_CLK_FACTOR_FIXED)
min = factor->value;
else if (factor->flags & TI_CLK_FACTOR_ZERO_BASED)
min = 0;
else if (factor->flags & TI_CLK_FACTOR_MIN_VALUE)
min = factor->min_value;
else
min = 1;
return (min);
}
static uint64_t
ti_dpll_clk_find_best(struct ti_dpll_clknode_sc *sc, uint64_t fparent,
uint64_t *fout, uint32_t *factor_n, uint32_t *factor_p)
{
uint64_t cur, best;
uint32_t n, p, max_n, max_p, min_n, min_p;
*factor_n = *factor_p = 0;
max_n = ti_clk_factor_get_max(&sc->n);
max_p = ti_clk_factor_get_max(&sc->p);
min_n = ti_clk_factor_get_min(&sc->n);
min_p = ti_clk_factor_get_min(&sc->p);
for (p = min_p; p <= max_p; ) {
for (n = min_n; n <= max_n; ) {
cur = fparent * n / p;
if (abs(*fout - cur) < abs(*fout - best)) {
best = cur;
*factor_n = n;
*factor_p = p;
}
n++;
}
p++;
}
return (best);
}
static inline uint32_t
ti_clk_get_factor(uint32_t val, struct ti_clk_factor *factor)
{
uint32_t factor_val;
if (factor->flags & TI_CLK_FACTOR_FIXED)
return (factor->value);
factor_val = (val & factor->mask) >> factor->shift;
if (!(factor->flags & TI_CLK_FACTOR_ZERO_BASED))
factor_val += 1;
return (factor_val);
}
static inline uint32_t
ti_clk_factor_get_value(struct ti_clk_factor *factor, uint32_t raw)
{
uint32_t val;
if (factor->flags & TI_CLK_FACTOR_FIXED)
return (factor->value);
if (factor->flags & TI_CLK_FACTOR_ZERO_BASED)
val = raw;
else if (factor->flags & TI_CLK_FACTOR_MAX_VALUE &&
raw > factor->max_value)
val = factor->max_value;
else
val = raw - 1;
return (val);
}
static int
ti_dpll_clk_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
int flags, int *stop)
{
struct ti_dpll_clknode_sc *sc;
uint64_t cur, best;
uint32_t val, n, p, best_n, best_p, timeout;
sc = clknode_get_softc(clk);
best = cur = 0;
best = ti_dpll_clk_find_best(sc, fparent, fout,
&best_n, &best_p);
if ((flags & CLK_SET_DRYRUN) != 0) {
*fout = best;
*stop = 1;
return (0);
}
if ((best < *fout) &&
(flags == CLK_SET_ROUND_DOWN)) {
*stop = 1;
return (ERANGE);
}
if ((best > *fout) &&
(flags == CLK_SET_ROUND_UP)) {
*stop = 1;
return (ERANGE);
}
DEVICE_LOCK(clk);
/* 1 switch PLL to bypass mode */
WRITE4(clk, sc->ti_clkmode_offset, DPLL_EN_MN_BYPASS_MODE);
/* 2 Ensure PLL is in bypass */
timeout = 10000;
do {
DELAY(10);
READ4(clk, sc->ti_idlest_offset, &val);
} while (!(val & ST_MN_BYPASS_MASK) && timeout--);
if (timeout == 0) {
DEVICE_UNLOCK(clk);
return (ERANGE); // FIXME: Better return value?
}
/* 3 Set DPLL_MULT & DPLL_DIV bits */
READ4(clk, sc->ti_clksel_offset, &val);
n = ti_clk_factor_get_value(&sc->n, best_n);
p = ti_clk_factor_get_value(&sc->p, best_p);
val &= ~sc->n.mask;
val &= ~sc->p.mask;
val |= n << sc->n.shift;
val |= p << sc->p.shift;
WRITE4(clk, sc->ti_clksel_offset, val);
/* 4. configure M2, M4, M5 and M6 */
/*
* FIXME: According to documentation M2/M4/M5/M6 can be set "later"
* See note in TRM 8.1.6.7.1
*/
/* 5 Switch over to lock mode */
WRITE4(clk, sc->ti_clkmode_offset, DPLL_EN_LOCK_MODE);
/* 6 Ensure PLL is locked */
timeout = 10000;
do {
DELAY(10);
READ4(clk, sc->ti_idlest_offset, &val);
} while (!(val & ST_DPLL_CLK_MASK) && timeout--);
DEVICE_UNLOCK(clk);
if (timeout == 0) {
return (ERANGE); // FIXME: Better return value?
}
*fout = best;
*stop = 1;
return (0);
}
static int
ti_dpll_clk_recalc(struct clknode *clk, uint64_t *freq)
{
struct ti_dpll_clknode_sc *sc;
uint32_t val, n, p;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->ti_clksel_offset, &val);
DEVICE_UNLOCK(clk);
n = ti_clk_get_factor(val, &sc->n);
p = ti_clk_get_factor(val, &sc->p);
*freq = *freq * n / p;
return (0);
}
static clknode_method_t ti_dpll_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, ti_dpll_clk_init),
CLKNODEMETHOD(clknode_recalc_freq, ti_dpll_clk_recalc),
CLKNODEMETHOD(clknode_set_freq, ti_dpll_clk_set_freq),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(ti_dpll_clknode, ti_dpll_clknode_class, ti_dpll_clknode_methods,
sizeof(struct ti_dpll_clknode_sc), clknode_class);
int
ti_clknode_dpll_register(struct clkdom *clkdom, struct ti_clk_dpll_def *clkdef)
{
struct clknode *clk;
struct ti_dpll_clknode_sc *sc;
clk = clknode_create(clkdom, &ti_dpll_clknode_class, &clkdef->clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->ti_clkmode_offset = clkdef->ti_clkmode_offset;
sc->ti_clkmode_flags = clkdef->ti_clkmode_flags;
sc->ti_idlest_offset = clkdef->ti_idlest_offset;
sc->ti_clksel_offset = clkdef->ti_clksel_offset;
sc->n.shift = clkdef->ti_clksel_mult.shift;
sc->n.mask = clkdef->ti_clksel_mult.mask;
sc->n.width = clkdef->ti_clksel_mult.width;
sc->n.value = clkdef->ti_clksel_mult.value;
sc->n.min_value = clkdef->ti_clksel_mult.min_value;
sc->n.max_value = clkdef->ti_clksel_mult.max_value;
sc->n.flags = clkdef->ti_clksel_mult.flags;
sc->p.shift = clkdef->ti_clksel_div.shift;
sc->p.mask = clkdef->ti_clksel_div.mask;
sc->p.width = clkdef->ti_clksel_div.width;
sc->p.value = clkdef->ti_clksel_div.value;
sc->p.min_value = clkdef->ti_clksel_div.min_value;
sc->p.max_value = clkdef->ti_clksel_div.max_value;
sc->p.flags = clkdef->ti_clksel_div.flags;
sc->ti_autoidle_offset = clkdef->ti_autoidle_offset;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,97 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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 _TI_DPLL_CLOCK_H_
#define _TI_DPLL_CLOCK_H_
#include <dev/extres/clk/clk.h>
/* Registers are described in AM335x TRM chapter 8.1.12.2.* */
/* Register offsets */
#define CM_CLKSEL_DPLL_PERIPH 0x49C
/* CM_IDLEST_DPLL_xxx */
#define ST_MN_BYPASS_MASK 0x0100
#define ST_MN_BYPASS_SHIFT 8
#define ST_DPLL_CLK_MASK 0x0001
/* CM_CLKMODE_DPLL_DPLL_EN feature flag */
#define LOW_POWER_STOP_MODE_FLAG 0x01
#define MN_BYPASS_MODE_FLAG 0x02
#define IDLE_BYPASS_LOW_POWER_MODE_FLAG 0x04
#define IDLE_BYPASS_FAST_RELOCK_MODE_FLAG 0x08
#define LOCK_MODE_FLAG 0x10
/* CM_CLKMODE_DPLL_xxx */
#define DPLL_EN_LOW_POWER_STOP_MODE 0x01
#define DPLL_EN_MN_BYPASS_MODE 0x04
#define DPLL_EN_IDLE_BYPASS_LOW_POWER_MODE 0x05
#define DPLL_EN_IDLE_BYPASS_FAST_RELOCK_MODE 0x06
#define DPLL_EN_LOCK_MODE 0x07
#define TI_CLK_FACTOR_ZERO_BASED 0x0002
#define TI_CLK_FACTOR_FIXED 0x0008
#define TI_CLK_FACTOR_MIN_VALUE 0x0020
#define TI_CLK_FACTOR_MAX_VALUE 0x0040
/* Based on aw_clk_factor sys/arm/allwinner/clkng/aw_clk.h */
struct ti_clk_factor {
uint32_t shift; /* Shift bits for the factor */
uint32_t mask; /* Mask to get the factor */
uint32_t width; /* Number of bits for the factor */
uint32_t value; /* Fixed value */
uint32_t min_value;
uint32_t max_value;
uint32_t flags; /* Flags */
};
struct ti_clk_dpll_def {
struct clknode_init_def clkdef;
uint32_t ti_clkmode_offset; /* control */
uint8_t ti_clkmode_flags;
uint32_t ti_idlest_offset;
uint32_t ti_clksel_offset; /* mult-div1 */
struct ti_clk_factor ti_clksel_mult;
struct ti_clk_factor ti_clksel_div;
uint32_t ti_autoidle_offset;
};
int ti_clknode_dpll_register(struct clkdom *clkdom, struct ti_clk_dpll_def *clkdef);
#endif /* _TI_DPLL_CLOCK_H_ */

353
sys/arm/ti/clk/ti_clkctrl.c Normal file
View File

@ -0,0 +1,353 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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/fbio.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/resource.h>
#include <machine/bus.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <vm/pmap.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/clk/ti_clk_clkctrl.h>
#include <arm/ti/ti_omap4_cm.h>
#include <arm/ti/ti_cpuid.h>
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
#define L4LS_CLKCTRL_38 2
#define L4_WKUP_CLKCTRL_0 1
#define NO_SPECIAL_REG 0
/* Documentation/devicetree/bindings/clock/ti-clkctrl.txt */
#define TI_CLKCTRL_L4_WKUP 5
#define TI_CLKCTRL_L4_SECURE 4
#define TI_CLKCTRL_L4_PER 3
#define TI_CLKCTRL_L4_CFG 2
#define TI_CLKCTRL 1
#define TI_CLKCTRL_END 0
static struct ofw_compat_data compat_data[] = {
{ "ti,clkctrl-l4-wkup", TI_CLKCTRL_L4_WKUP },
{ "ti,clkctrl-l4-secure", TI_CLKCTRL_L4_SECURE },
{ "ti,clkctrl-l4-per", TI_CLKCTRL_L4_PER },
{ "ti,clkctrl-l4-cfg", TI_CLKCTRL_L4_CFG },
{ "ti,clkctrl", TI_CLKCTRL },
{ NULL, TI_CLKCTRL_END }
};
struct ti_clkctrl_softc {
device_t dev;
struct clkdom *clkdom;
};
static int ti_clkctrl_probe(device_t dev);
static int ti_clkctrl_attach(device_t dev);
static int ti_clkctrl_detach(device_t dev);
int clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
phandle_t *cells, struct clknode **clk);
static int
create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg);
static int
ti_clkctrl_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, "TI clkctrl");
return (BUS_PROBE_DEFAULT);
}
static int
ti_clkctrl_attach(device_t dev)
{
struct ti_clkctrl_softc *sc;
phandle_t node;
cell_t *reg;
ssize_t numbytes_reg;
int num_reg, err, ti_clock_cells;
uint32_t index, reg_offset, reg_address;
const char *org_name;
uint64_t parent_offset;
uint8_t special_reg = NO_SPECIAL_REG;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(dev);
/* Sanity check */
err = OF_searchencprop(node, "#clock-cells",
&ti_clock_cells, sizeof(ti_clock_cells));
if (err == -1) {
device_printf(sc->dev, "Failed to get #clock-cells\n");
return (ENXIO);
}
if (ti_clock_cells != 2) {
device_printf(sc->dev, "clock cells(%d) != 2\n",
ti_clock_cells);
return (ENXIO);
}
/* Grab the content of reg properties */
numbytes_reg = OF_getproplen(node, "reg");
if (numbytes_reg == 0) {
device_printf(sc->dev, "reg property empty - check your devicetree\n");
return (ENXIO);
}
num_reg = numbytes_reg / sizeof(cell_t);
reg = malloc(numbytes_reg, M_DEVBUF, M_WAITOK);
OF_getencprop(node, "reg", reg, numbytes_reg);
/* Create clock domain */
sc->clkdom = clkdom_create(sc->dev);
if (sc->clkdom == NULL) {
free(reg, M_DEVBUF);
DPRINTF(sc->dev, "Failed to create clkdom\n");
return (ENXIO);
}
clkdom_set_ofw_mapper(sc->clkdom, clkctrl_ofw_map);
/* Create clock nodes */
/* name */
clk_parse_ofw_clk_name(sc->dev, node, &org_name);
/* Get parent range */
parent_offset = ti_omap4_cm_get_simplebus_base_host(device_get_parent(dev));
/* Check if this is a clkctrl with special registers like gpio */
switch (ti_chip()) {
#ifdef SOC_OMAP4
case CHIP_OMAP_4:
/* FIXME: Todo */
break;
#endif /* SOC_OMAP4 */
#ifdef SOC_TI_AM335X
/* Checkout TRM 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3
* and the DTS.
*/
case CHIP_AM335X:
if (strcmp(org_name, "l4ls-clkctrl@38") == 0)
special_reg = L4LS_CLKCTRL_38;
else if (strcmp(org_name, "l4-wkup-clkctrl@0") == 0)
special_reg = L4_WKUP_CLKCTRL_0;
break;
#endif /* SOC_TI_AM335X */
default:
break;
}
/* reg property has a pair of (base address, length) */
for (index = 0; index < num_reg; index += 2) {
for (reg_offset = 0; reg_offset < reg[index+1]; reg_offset += sizeof(cell_t)) {
err = create_clkctrl(sc, reg, index, reg_offset, parent_offset,
org_name, false);
if (err)
goto cleanup;
/* Create special clkctrl for GDBCLK in GPIO registers */
switch (special_reg) {
case NO_SPECIAL_REG:
break;
case L4LS_CLKCTRL_38:
reg_address = reg[index] + reg_offset-reg[0];
if (reg_address == 0x74 ||
reg_address == 0x78 ||
reg_address == 0x7C)
{
err = create_clkctrl(sc, reg, index, reg_offset,
parent_offset, org_name, true);
if (err)
goto cleanup;
}
break;
case L4_WKUP_CLKCTRL_0:
reg_address = reg[index] + reg_offset - reg[0];
if (reg_address == 0x8)
{
err = create_clkctrl(sc, reg, index, reg_offset,
parent_offset, org_name, true);
if (err)
goto cleanup;
}
break;
} /* switch (special_reg) */
} /* inner for */
} /* for */
err = clkdom_finit(sc->clkdom);
if (err) {
DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err);
err = ENXIO;
goto cleanup;
}
cleanup:
OF_prop_free(__DECONST(char *, org_name));
free(reg, M_DEVBUF);
if (err)
return (err);
return (bus_generic_attach(dev));
}
static int
ti_clkctrl_detach(device_t dev)
{
return (EBUSY);
}
/* modified version of default mapper from clk.c */
int
clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
phandle_t *cells, struct clknode **clk) {
if (ncells == 0)
*clk = clknode_find_by_id(clkdom, 1);
else if (ncells == 1)
*clk = clknode_find_by_id(clkdom, cells[0]);
else if (ncells == 2) {
/* To avoid collision with other IDs just add one.
* All other registers has an offset of 4 from each other.
*/
if (cells[1])
*clk = clknode_find_by_id(clkdom, cells[0]+1);
else
*clk = clknode_find_by_id(clkdom, cells[0]);
}
else
return (ERANGE);
if (*clk == NULL)
return (ENXIO);
return (0);
}
static int
create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg) {
struct ti_clk_clkctrl_def def;
char *name;
size_t name_len;
int err;
name_len = strlen(org_name) + 1 + 5; /* 5 = _xxxx */
name = malloc(name_len, M_OFWPROP, M_WAITOK);
/*
* Check out XX_CLKCTRL-INDEX(offset)-macro dance in
* sys/gnu/dts/dts/include/dt-bindings/clock/am3.h
* sys/gnu/dts/dts/include/dt-bindings/clock/am4.h
* sys/gnu/dts/dts/include/dt-bindings/clock/dra7.h
* reg[0] are in practice the same as the offset described in the dts.
*/
/* special_gdbclk_reg are 0 or 1 */
def.clkdef.id = reg[index] + reg_offset - reg[0] + special_gdbclk_reg;
def.register_offset = parent_offset + reg[index] + reg_offset;
/* Indicate this clkctrl is special and dont use IDLEST/MODULEMODE */
def.gdbclk = special_gdbclk_reg;
/* Make up an uniq name in the namespace for each clkctrl */
snprintf(name, name_len, "%s_%x",
org_name, def.clkdef.id);
def.clkdef.name = (const char *) name;
DPRINTF(sc->dev, "ti_clkctrl_attach: reg[%d]: %s %x\n",
index, def.clkdef.name, def.clkdef.id);
/* No parent name */
def.clkdef.parent_cnt = 0;
/* set flags */
def.clkdef.flags = 0x0;
/* Register the clkctrl */
err = ti_clknode_clkctrl_register(sc->clkdom, &def);
if (err) {
DPRINTF(sc->dev,
"ti_clknode_clkctrl_register[%d:%d] failed %x\n",
index, reg_offset, err);
err = ENXIO;
}
OF_prop_free(name);
return (err);
}
static device_method_t ti_clkctrl_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_clkctrl_probe),
DEVMETHOD(device_attach, ti_clkctrl_attach),
DEVMETHOD(device_detach, ti_clkctrl_detach),
DEVMETHOD_END
};
DEFINE_CLASS_0(ti_clkctrl, ti_clkctrl_driver, ti_clkctrl_methods,
sizeof(struct ti_clkctrl_softc));
static devclass_t ti_clkctrl_devclass;
EARLY_DRIVER_MODULE(ti_clkctrl, simplebus, ti_clkctrl_driver,
ti_clkctrl_devclass, 0, 0, BUS_PASS_BUS+BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ti_clkctrl, 1);

View File

@ -0,0 +1,264 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/libkern.h>
#include <machine/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/extres/clk/clk_div.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "clock_common.h"
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
/*
* Devicetree description
* Documentation/devicetree/bindings/clock/ti/divider.txt
*/
struct ti_divider_softc {
device_t sc_dev;
bool attach_done;
struct clk_div_def div_def;
struct clock_cell_info clock_cell;
struct clkdom *clkdom;
};
static int ti_divider_probe(device_t dev);
static int ti_divider_attach(device_t dev);
static int ti_divider_detach(device_t dev);
#define TI_DIVIDER_CLOCK 2
#define TI_COMPOSITE_DIVIDER_CLOCK 1
#define TI_DIVIDER_END 0
static struct ofw_compat_data compat_data[] = {
{ "ti,divider-clock", TI_DIVIDER_CLOCK },
{ "ti,composite-divider-clock", TI_COMPOSITE_DIVIDER_CLOCK },
{ NULL, TI_DIVIDER_END }
};
static int
register_clk(struct ti_divider_softc *sc) {
int err;
sc->clkdom = clkdom_create(sc->sc_dev);
if (sc->clkdom == NULL) {
DPRINTF(sc->sc_dev, "Failed to create clkdom\n");
return (ENXIO);
}
err = clknode_div_register(sc->clkdom, &sc->div_def);
if (err) {
DPRINTF(sc->sc_dev, "clknode_div_register failed %x\n", err);
return (ENXIO);
}
err = clkdom_finit(sc->clkdom);
if (err) {
DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err);
return (ENXIO);
}
return (0);
}
static int
ti_divider_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, "TI Divider Clock");
return (BUS_PROBE_DEFAULT);
}
static int
ti_divider_attach(device_t dev)
{
struct ti_divider_softc *sc;
phandle_t node;
int err;
cell_t value;
uint32_t ti_max_div;
sc = device_get_softc(dev);
sc->sc_dev = dev;
node = ofw_bus_get_node(dev);
/* Grab the content of reg properties */
OF_getencprop(node, "reg", &value, sizeof(value));
sc->div_def.offset = value;
if (OF_hasprop(node, "ti,bit-shift")) {
OF_getencprop(node, "ti,bit-shift", &value, sizeof(value));
sc->div_def.i_shift = value;
}
if (OF_hasprop(node, "ti,index-starts-at-one")) {
sc->div_def.div_flags = CLK_DIV_ZERO_BASED;
}
if (OF_hasprop(node, "ti,index-power-of-two")) {
/* FIXME: later */
device_printf(sc->sc_dev, "ti,index-power-of-two - Not implemented\n");
/* remember to update i_width a few lines below */
}
if (OF_hasprop(node, "ti,max-div")) {
OF_getencprop(node, "ti,max-div", &value, sizeof(value));
ti_max_div = value;
}
if (OF_hasprop(node, "clock-output-names"))
device_printf(sc->sc_dev, "clock-output-names\n");
if (OF_hasprop(node, "ti,dividers"))
device_printf(sc->sc_dev, "ti,dividers\n");
if (OF_hasprop(node, "ti,min-div"))
device_printf(sc->sc_dev, "ti,min-div - Not implemented\n");
if (OF_hasprop(node, "ti,autoidle-shift"))
device_printf(sc->sc_dev, "ti,autoidle-shift - Not implemented\n");
if (OF_hasprop(node, "ti,set-rate-parent"))
device_printf(sc->sc_dev, "ti,set-rate-parent - Not implemented\n");
if (OF_hasprop(node, "ti,latch-bit"))
device_printf(sc->sc_dev, "ti,latch-bit - Not implemented\n");
/* Figure out the width from ti_max_div */
if (sc->div_def.div_flags)
sc->div_def.i_width = fls(ti_max_div-1);
else
sc->div_def.i_width = fls(ti_max_div);
DPRINTF(sc->sc_dev, "div_def.i_width %x\n", sc->div_def.i_width);
read_clock_cells(sc->sc_dev, &sc->clock_cell);
create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef);
err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef);
if (err) {
/* free_clkdef will be called in ti_divider_new_pass */
DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n");
return (bus_generic_attach(sc->sc_dev));
}
err = register_clk(sc);
if (err) {
/* free_clkdef will be called in ti_divider_new_pass */
DPRINTF(sc->sc_dev, "register_clk failed\n");
return (bus_generic_attach(sc->sc_dev));
}
sc->attach_done = true;
free_clkdef(&sc->div_def.clkdef);
return (bus_generic_attach(sc->sc_dev));
}
static int
ti_divider_detach(device_t dev)
{
return (EBUSY);
}
static void
ti_divider_new_pass(device_t dev)
{
struct ti_divider_softc *sc;
int err;
sc = device_get_softc(dev);
if (sc->attach_done) {
return;
}
err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef);
if (err) {
/* free_clkdef will be called in a later call to ti_divider_new_pass */
DPRINTF(sc->sc_dev, "new_pass find_parent_clock_names failed\n");
return;
}
err = register_clk(sc);
if (err) {
/* free_clkdef will be called in a later call to ti_divider_new_pass */
DPRINTF(sc->sc_dev, "new_pass register_clk failed\n");
return;
}
sc->attach_done = true;
free_clkdef(&sc->div_def.clkdef);
}
static device_method_t ti_divider_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_divider_probe),
DEVMETHOD(device_attach, ti_divider_attach),
DEVMETHOD(device_detach, ti_divider_detach),
/* Bus interface */
DEVMETHOD(bus_new_pass, ti_divider_new_pass),
DEVMETHOD_END
};
DEFINE_CLASS_0(ti_divider, ti_divider_driver, ti_divider_methods,
sizeof(struct ti_divider_softc));
static devclass_t ti_divider_devclass;
EARLY_DRIVER_MODULE(ti_divider, simplebus, ti_divider_driver,
ti_divider_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ti_divider, 1);

View File

@ -0,0 +1,375 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/libkern.h>
#include <machine/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/extres/clk/clk_div.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/clk/ti_clk_dpll.h>
#include "clock_common.h"
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
/*
* Devicetree description
* Documentation/devicetree/bindings/clock/ti/dpll.txt
*/
struct ti_dpll_softc {
device_t dev;
uint8_t dpll_type;
bool attach_done;
struct ti_clk_dpll_def dpll_def;
struct clock_cell_info clock_cell;
struct clkdom *clkdom;
};
static int ti_dpll_probe(device_t dev);
static int ti_dpll_attach(device_t dev);
static int ti_dpll_detach(device_t dev);
#define TI_OMAP3_DPLL_CLOCK 17
#define TI_OMAP3_DPLL_CORE_CLOCK 16
#define TI_OMAP3_DPLL_PER_CLOCK 15
#define TI_OMAP3_DPLL_PER_J_TYPE_CLOCK 14
#define TI_OMAP4_DPLL_CLOCK 13
#define TI_OMAP4_DPLL_X2_CLOCK 12
#define TI_OMAP4_DPLL_CORE_CLOCK 11
#define TI_OMAP4_DPLL_M4XEN_CLOCK 10
#define TI_OMAP4_DPLL_J_TYPE_CLOCK 9
#define TI_OMAP5_MPU_DPLL_CLOCK 8
#define TI_AM3_DPLL_NO_GATE_CLOCK 7
#define TI_AM3_DPLL_J_TYPE_CLOCK 6
#define TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK 5
#define TI_AM3_DPLL_CLOCK 4
#define TI_AM3_DPLL_CORE_CLOCK 3
#define TI_AM3_DPLL_X2_CLOCK 2
#define TI_OMAP2_DPLL_CORE_CLOCK 1
#define TI_DPLL_END 0
static struct ofw_compat_data compat_data[] = {
{ "ti,omap3-dpll-clock", TI_OMAP3_DPLL_CLOCK },
{ "ti,omap3-dpll-core-clock", TI_OMAP3_DPLL_CORE_CLOCK },
{ "ti,omap3-dpll-per-clock", TI_OMAP3_DPLL_PER_CLOCK },
{ "ti,omap3-dpll-per-j-type-clock",TI_OMAP3_DPLL_PER_J_TYPE_CLOCK },
{ "ti,omap4-dpll-clock", TI_OMAP4_DPLL_CLOCK },
{ "ti,omap4-dpll-x2-clock", TI_OMAP4_DPLL_X2_CLOCK },
{ "ti,omap4-dpll-core-clock", TI_OMAP4_DPLL_CORE_CLOCK },
{ "ti,omap4-dpll-m4xen-clock", TI_OMAP4_DPLL_M4XEN_CLOCK },
{ "ti,omap4-dpll-j-type-clock", TI_OMAP4_DPLL_J_TYPE_CLOCK },
{ "ti,omap5-mpu-dpll-clock", TI_OMAP5_MPU_DPLL_CLOCK },
{ "ti,am3-dpll-no-gate-clock", TI_AM3_DPLL_NO_GATE_CLOCK },
{ "ti,am3-dpll-j-type-clock", TI_AM3_DPLL_J_TYPE_CLOCK },
{ "ti,am3-dpll-no-gate-j-type-clock",TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK },
{ "ti,am3-dpll-clock", TI_AM3_DPLL_CLOCK },
{ "ti,am3-dpll-core-clock", TI_AM3_DPLL_CORE_CLOCK },
{ "ti,am3-dpll-x2-clock", TI_AM3_DPLL_X2_CLOCK },
{ "ti,omap2-dpll-core-clock", TI_OMAP2_DPLL_CORE_CLOCK },
{ NULL, TI_DPLL_END }
};
static int
register_clk(struct ti_dpll_softc *sc) {
int err;
sc->clkdom = clkdom_create(sc->dev);
if (sc->clkdom == NULL) {
DPRINTF(sc->dev, "Failed to create clkdom\n");
return (ENXIO);
}
err = ti_clknode_dpll_register(sc->clkdom, &sc->dpll_def);
if (err) {
DPRINTF(sc->dev,
"ti_clknode_dpll_register failed %x\n", err);
return (ENXIO);
}
err = clkdom_finit(sc->clkdom);
if (err) {
DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err);
return (ENXIO);
}
return (0);
}
static int
ti_dpll_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, "TI DPLL Clock");
return (BUS_PROBE_DEFAULT);
}
static int
parse_dpll_reg(struct ti_dpll_softc *sc) {
ssize_t numbytes_regs;
uint32_t num_regs;
phandle_t node;
cell_t reg_cells[4];
if (sc->dpll_type == TI_AM3_DPLL_X2_CLOCK ||
sc->dpll_type == TI_OMAP4_DPLL_X2_CLOCK) {
sc->dpll_def.ti_clksel_mult.value = 2;
sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_FIXED;
sc->dpll_def.ti_clksel_div.value = 1;
sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_FIXED;
return (0);
}
node = ofw_bus_get_node(sc->dev);
numbytes_regs = OF_getproplen(node, "reg");
num_regs = numbytes_regs / sizeof(cell_t);
/* Sanity check */
if (num_regs > 4)
return (ENXIO);
OF_getencprop(node, "reg", reg_cells, numbytes_regs);
switch (sc->dpll_type) {
case TI_AM3_DPLL_NO_GATE_CLOCK:
case TI_AM3_DPLL_J_TYPE_CLOCK:
case TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK:
case TI_AM3_DPLL_CLOCK:
case TI_AM3_DPLL_CORE_CLOCK:
case TI_AM3_DPLL_X2_CLOCK:
if (num_regs != 3)
return (ENXIO);
sc->dpll_def.ti_clkmode_offset = reg_cells[0];
sc->dpll_def.ti_idlest_offset = reg_cells[1];
sc->dpll_def.ti_clksel_offset = reg_cells[2];
break;
case TI_OMAP2_DPLL_CORE_CLOCK:
if (num_regs != 2)
return (ENXIO);
sc->dpll_def.ti_clkmode_offset = reg_cells[0];
sc->dpll_def.ti_clksel_offset = reg_cells[1];
break;
default:
sc->dpll_def.ti_clkmode_offset = reg_cells[0];
sc->dpll_def.ti_idlest_offset = reg_cells[1];
sc->dpll_def.ti_clksel_offset = reg_cells[2];
sc->dpll_def.ti_autoidle_offset = reg_cells[3];
break;
}
/* AM335x */
if (sc->dpll_def.ti_clksel_offset == CM_CLKSEL_DPLL_PERIPH) {
sc->dpll_def.ti_clksel_mult.shift = 8;
sc->dpll_def.ti_clksel_mult.mask = 0x000FFF00;
sc->dpll_def.ti_clksel_mult.width = 12;
sc->dpll_def.ti_clksel_mult.value = 0;
sc->dpll_def.ti_clksel_mult.min_value = 2;
sc->dpll_def.ti_clksel_mult.max_value = 4095;
sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED |
TI_CLK_FACTOR_MIN_VALUE |
TI_CLK_FACTOR_MAX_VALUE;
sc->dpll_def.ti_clksel_div.shift = 0;
sc->dpll_def.ti_clksel_div.mask = 0x000000FF;
sc->dpll_def.ti_clksel_div.width = 8;
sc->dpll_def.ti_clksel_div.value = 0;
sc->dpll_def.ti_clksel_div.min_value = 0;
sc->dpll_def.ti_clksel_div.max_value = 255;
sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE |
TI_CLK_FACTOR_MAX_VALUE;
} else {
sc->dpll_def.ti_clksel_mult.shift = 8;
sc->dpll_def.ti_clksel_mult.mask = 0x0007FF00;
sc->dpll_def.ti_clksel_mult.width = 11;
sc->dpll_def.ti_clksel_mult.value = 0;
sc->dpll_def.ti_clksel_mult.min_value = 2;
sc->dpll_def.ti_clksel_mult.max_value = 2047;
sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED |
TI_CLK_FACTOR_MIN_VALUE |
TI_CLK_FACTOR_MAX_VALUE;
sc->dpll_def.ti_clksel_div.shift = 0;
sc->dpll_def.ti_clksel_div.mask = 0x0000007F;
sc->dpll_def.ti_clksel_div.width = 7;
sc->dpll_def.ti_clksel_div.value = 0;
sc->dpll_def.ti_clksel_div.min_value = 0;
sc->dpll_def.ti_clksel_div.max_value = 127;
sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE |
TI_CLK_FACTOR_MAX_VALUE;
}
DPRINTF(sc->dev, "clkmode %x idlest %x clksel %x autoidle %x\n",
sc->dpll_def.ti_clkmode_offset, sc->dpll_def.ti_idlest_offset,
sc->dpll_def.ti_clksel_offset,
sc->dpll_def.ti_autoidle_offset);
return (0);
}
static int
ti_dpll_attach(device_t dev)
{
struct ti_dpll_softc *sc;
phandle_t node;
int err;
sc = device_get_softc(dev);
sc->dev = dev;
sc->dpll_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
node = ofw_bus_get_node(dev);
/* Grab the content of reg properties */
parse_dpll_reg(sc);
/* default flags (OMAP4&AM335x) not present in the dts at moment */
sc->dpll_def.ti_clkmode_flags = MN_BYPASS_MODE_FLAG | LOCK_MODE_FLAG;
if (OF_hasprop(node, "ti,low-power-stop")) {
sc->dpll_def.ti_clkmode_flags |= LOW_POWER_STOP_MODE_FLAG;
}
if (OF_hasprop(node, "ti,low-power-bypass")) {
sc->dpll_def.ti_clkmode_flags |= IDLE_BYPASS_LOW_POWER_MODE_FLAG;
}
if (OF_hasprop(node, "ti,lock")) {
sc->dpll_def.ti_clkmode_flags |= LOCK_MODE_FLAG;
}
read_clock_cells(sc->dev, &sc->clock_cell);
create_clkdef(sc->dev, &sc->clock_cell, &sc->dpll_def.clkdef);
err = find_parent_clock_names(sc->dev, &sc->clock_cell,
&sc->dpll_def.clkdef);
if (err) {
/* free_clkdef will be called in ti_dpll_new_pass */
DPRINTF(sc->dev, "find_parent_clock_names failed\n");
return (bus_generic_attach(sc->dev));
}
err = register_clk(sc);
if (err) {
/* free_clkdef will be called in ti_dpll_new_pass */
DPRINTF(sc->dev, "register_clk failed\n");
return (bus_generic_attach(sc->dev));
}
sc->attach_done = true;
free_clkdef(&sc->dpll_def.clkdef);
return (bus_generic_attach(sc->dev));
}
static int
ti_dpll_detach(device_t dev)
{
return (EBUSY);
}
static void
ti_dpll_new_pass(device_t dev)
{
struct ti_dpll_softc *sc;
int err;
sc = device_get_softc(dev);
if (sc->attach_done) {
return;
}
err = find_parent_clock_names(sc->dev, &sc->clock_cell,
&sc->dpll_def.clkdef);
if (err) {
/* free_clkdef will be called in a later call to ti_dpll_new_pass */
DPRINTF(sc->dev,
"new_pass find_parent_clock_names failed\n");
return;
}
err = register_clk(sc);
if (err) {
/* free_clkdef will be called in a later call to ti_dpll_new_pass */
DPRINTF(sc->dev, "new_pass register_clk failed\n");
return;
}
sc->attach_done = true;
free_clkdef(&sc->dpll_def.clkdef);
}
static device_method_t ti_dpll_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_dpll_probe),
DEVMETHOD(device_attach, ti_dpll_attach),
DEVMETHOD(device_detach, ti_dpll_detach),
/* Bus interface */
DEVMETHOD(bus_new_pass, ti_dpll_new_pass),
DEVMETHOD_END
};
DEFINE_CLASS_0(ti_dpll, ti_dpll_driver, ti_dpll_methods,
sizeof(struct ti_dpll_softc));
static devclass_t ti_dpll_devclass;
EARLY_DRIVER_MODULE(ti_dpll, simplebus, ti_dpll_driver,
ti_dpll_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ti_dpll, 1);

View File

@ -0,0 +1,266 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/libkern.h>
#include <machine/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/extres/clk/clk_gate.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "clock_common.h"
#define DEBUG_GATE 0
#if DEBUG_GATE
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
/*
* Devicetree description
* Documentation/devicetree/bindings/clock/ti/gate.txt
*/
struct ti_gate_softc {
device_t sc_dev;
bool attach_done;
uint8_t sc_type;
struct clk_gate_def gate_def;
struct clock_cell_info clock_cell;
struct clkdom *clkdom;
};
static int ti_gate_probe(device_t dev);
static int ti_gate_attach(device_t dev);
static int ti_gate_detach(device_t dev);
#define TI_GATE_CLOCK 7
#define TI_WAIT_GATE_CLOCK 6
#define TI_DSS_GATE_CLOCK 5
#define TI_AM35XX_GATE_CLOCK 4
#define TI_CLKDM_GATE_CLOCK 3
#define TI_HSDIV_GATE_CLOCK 2
#define TI_COMPOSITE_NO_WAIT_GATE_CLOCK 1
#define TI_GATE_END 0
static struct ofw_compat_data compat_data[] = {
{ "ti,gate-clock", TI_GATE_CLOCK },
{ "ti,wait-gate-clock", TI_WAIT_GATE_CLOCK },
{ "ti,dss-gate-clock", TI_DSS_GATE_CLOCK },
{ "ti,am35xx-gate-clock", TI_AM35XX_GATE_CLOCK },
{ "ti,clkdm-gate-clock", TI_CLKDM_GATE_CLOCK },
{ "ti,hsdiv-gate-cloc", TI_HSDIV_GATE_CLOCK },
{ "ti,composite-no-wait-gate-clock", TI_COMPOSITE_NO_WAIT_GATE_CLOCK },
{ NULL, TI_GATE_END }
};
static int
ti_gate_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, "TI Gate Clock");
return (BUS_PROBE_DEFAULT);
}
static int
register_clk(struct ti_gate_softc *sc) {
int err;
sc->clkdom = clkdom_create(sc->sc_dev);
if (sc->clkdom == NULL) {
DPRINTF(sc->sc_dev, "Failed to create clkdom\n");
return ENXIO;
}
err = clknode_gate_register(sc->clkdom, &sc->gate_def);
if (err) {
DPRINTF(sc->sc_dev, "clknode_gate_register failed %x\n", err);
return ENXIO;
}
err = clkdom_finit(sc->clkdom);
if (err) {
DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err);
return ENXIO;
}
return (0);
}
static int
ti_gate_attach(device_t dev)
{
struct ti_gate_softc *sc;
phandle_t node;
int err;
cell_t value;
sc = device_get_softc(dev);
sc->sc_dev = dev;
node = ofw_bus_get_node(dev);
/* Get the compatible type */
sc->sc_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
/* Get the content of reg properties */
if (sc->sc_type != TI_CLKDM_GATE_CLOCK) {
OF_getencprop(node, "reg", &value, sizeof(value));
sc->gate_def.offset = value;
}
#if DEBUG_GATE
else {
DPRINTF(sc->sc_dev, "no reg (TI_CLKDM_GATE_CLOCK)\n");
}
#endif
if (OF_hasprop(node, "ti,bit-shift")) {
OF_getencprop(node, "ti,bit-shift", &value, sizeof(value));
sc->gate_def.shift = value;
DPRINTF(sc->sc_dev, "ti,bit-shift => shift %x\n", sc->gate_def.shift);
}
if (OF_hasprop(node, "ti,set-bit-to-disable")) {
sc->gate_def.on_value = 0;
sc->gate_def.off_value = 1;
DPRINTF(sc->sc_dev,
"on_value = 0, off_value = 1 (ti,set-bit-to-disable)\n");
} else {
sc->gate_def.on_value = 1;
sc->gate_def.off_value = 0;
DPRINTF(sc->sc_dev, "on_value = 1, off_value = 0\n");
}
sc->gate_def.gate_flags = 0x0;
read_clock_cells(sc->sc_dev, &sc->clock_cell);
create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef);
/* Calculate mask */
sc->gate_def.mask = (1 << fls(sc->clock_cell.num_real_clocks)) - 1;
DPRINTF(sc->sc_dev, "num_real_clocks %x gate_def.mask %x\n",
sc->clock_cell.num_real_clocks, sc->gate_def.mask);
err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef);
if (err) {
/* free_clkdef will be called in ti_gate_new_pass */
DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n");
return (bus_generic_attach(sc->sc_dev));
}
err = register_clk(sc);
if (err) {
/* free_clkdef will be called in ti_gate_new_pass */
DPRINTF(sc->sc_dev, "register_clk failed\n");
return (bus_generic_attach(sc->sc_dev));
}
sc->attach_done = true;
free_clkdef(&sc->gate_def.clkdef);
return (bus_generic_attach(sc->sc_dev));
}
static int
ti_gate_detach(device_t dev)
{
return (EBUSY);
}
static void
ti_gate_new_pass(device_t dev) {
struct ti_gate_softc *sc;
int err;
sc = device_get_softc(dev);
if (sc->attach_done) {
return;
}
err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef);
if (err) {
/* free_clkdef will be called in later call to ti_gate_new_pass */
DPRINTF(sc->sc_dev, "new_pass find_parent_clock_names failed\n");
return;
}
err = register_clk(sc);
if (err) {
/* free_clkdef will be called in later call to ti_gate_new_pass */
DPRINTF(sc->sc_dev, "new_pass register_clk failed\n");
return;
}
sc->attach_done = true;
free_clkdef(&sc->gate_def.clkdef);
}
static device_method_t ti_gate_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_gate_probe),
DEVMETHOD(device_attach, ti_gate_attach),
DEVMETHOD(device_detach, ti_gate_detach),
/* Bus interface */
DEVMETHOD(bus_new_pass, ti_gate_new_pass),
DEVMETHOD_END
};
DEFINE_CLASS_0(ti_gate, ti_gate_driver, ti_gate_methods,
sizeof(struct ti_gate_softc));
static devclass_t ti_gate_devclass;
EARLY_DRIVER_MODULE(ti_gate, simplebus, ti_gate_driver,
ti_gate_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ti_gate, 1);

View File

@ -0,0 +1,249 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/libkern.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/extres/clk/clk_mux.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "clock_common.h"
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
/*
* Devicetree description
* Documentation/devicetree/bindings/clock/ti/mux.txt
*/
struct ti_mux_softc {
device_t sc_dev;
bool attach_done;
struct clk_mux_def mux_def;
struct clock_cell_info clock_cell;
struct clkdom *clkdom;
};
static int ti_mux_probe(device_t dev);
static int ti_mux_attach(device_t dev);
static int ti_mux_detach(device_t dev);
#define TI_MUX_CLOCK 2
#define TI_COMPOSITE_MUX_CLOCK 1
#define TI_MUX_END 0
static struct ofw_compat_data compat_data[] = {
{ "ti,mux-clock", TI_MUX_CLOCK },
{ "ti,composite-mux-clock", TI_COMPOSITE_MUX_CLOCK },
{ NULL, TI_MUX_END }
};
static int
ti_mux_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, "TI Mux Clock");
return (BUS_PROBE_DEFAULT);
}
static int
register_clk(struct ti_mux_softc *sc) {
int err;
sc->clkdom = clkdom_create(sc->sc_dev);
if (sc->clkdom == NULL) {
DPRINTF(sc->sc_dev, "Failed to create clkdom\n");
return ENXIO;
}
err = clknode_mux_register(sc->clkdom, &sc->mux_def);
if (err) {
DPRINTF(sc->sc_dev, "clknode_mux_register failed %x\n", err);
return ENXIO;
}
err = clkdom_finit(sc->clkdom);
if (err) {
DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err);
return ENXIO;
}
return 0;
}
static int
ti_mux_attach(device_t dev)
{
struct ti_mux_softc *sc;
phandle_t node;
int err;
cell_t value;
sc = device_get_softc(dev);
sc->sc_dev = dev;
node = ofw_bus_get_node(dev);
/* Grab the content of reg properties */
OF_getencprop(node, "reg", &value, sizeof(value));
sc->mux_def.offset = value;
if (OF_hasprop(node, "ti,bit-shift")) {
OF_getencprop(node, "ti,bit-shift", &value, sizeof(value));
sc->mux_def.shift = value;
DPRINTF(sc->sc_dev, "ti,bit-shift => shift %x\n", sc->mux_def.shift);
}
if (OF_hasprop(node, "ti,index-starts-at-one")) {
/* FIXME: Add support in dev/extres/clk */
/*sc->mux_def.mux_flags = ... */
device_printf(sc->sc_dev, "ti,index-starts-at-one - Not implemented\n");
}
if (OF_hasprop(node, "ti,set-rate-parent"))
device_printf(sc->sc_dev, "ti,set-rate-parent - Not implemented\n");
if (OF_hasprop(node, "ti,latch-bit"))
device_printf(sc->sc_dev, "ti,latch-bit - Not implemented\n");
read_clock_cells(sc->sc_dev, &sc->clock_cell);
create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef);
/* Figure out the width from ti_max_div */
if (sc->mux_def.mux_flags)
sc->mux_def.width = fls(sc->clock_cell.num_real_clocks-1);
else
sc->mux_def.width = fls(sc->clock_cell.num_real_clocks);
DPRINTF(sc->sc_dev, "sc->clock_cell.num_real_clocks %x def.width %x\n",
sc->clock_cell.num_real_clocks, sc->mux_def.width);
err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef);
if (err) {
/* free_clkdef will be called in ti_mux_new_pass */
DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n");
return (bus_generic_attach(sc->sc_dev));
}
err = register_clk(sc);
if (err) {
/* free_clkdef will be called in ti_mux_new_pass */
DPRINTF(sc->sc_dev, "register_clk failed\n");
return (bus_generic_attach(sc->sc_dev));
}
sc->attach_done = true;
free_clkdef(&sc->mux_def.clkdef);
return (bus_generic_attach(sc->sc_dev));
}
static void
ti_mux_new_pass(device_t dev)
{
struct ti_mux_softc *sc;
int err;
sc = device_get_softc(dev);
if (sc->attach_done) {
return;
}
err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef);
if (err) {
/* free_clkdef will be called in later call to ti_mux_new_pass */
DPRINTF(sc->sc_dev, "ti_mux_new_pass find_parent_clock_names failed\n");
return;
}
err = register_clk(sc);
if (err) {
/* free_clkdef will be called in later call to ti_mux_new_pass */
DPRINTF(sc->sc_dev, "ti_mux_new_pass register_clk failed\n");
return;
}
sc->attach_done = true;
free_clkdef(&sc->mux_def.clkdef);
}
static int
ti_mux_detach(device_t dev)
{
return (EBUSY);
}
static device_method_t ti_mux_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_mux_probe),
DEVMETHOD(device_attach, ti_mux_attach),
DEVMETHOD(device_detach, ti_mux_detach),
/* Bus interface */
DEVMETHOD(bus_new_pass, ti_mux_new_pass),
DEVMETHOD_END
};
DEFINE_CLASS_0(ti_mux, ti_mux_driver, ti_mux_methods,
sizeof(struct ti_mux_softc));
static devclass_t ti_mux_devclass;
EARLY_DRIVER_MODULE(ti_mux, simplebus, ti_mux_driver,
ti_mux_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ti_mux, 1);

View File

@ -74,7 +74,8 @@ __FBSDID("$FreeBSD$");
#include <net/if_media.h>
#include <net/if_types.h>
#include <arm/ti/ti_scm.h>
#include <dev/extres/syscon/syscon.h>
#include "syscon_if.h"
#include <arm/ti/am335x/am335x_scm.h>
#include <dev/mii/mii.h>
@ -1004,6 +1005,8 @@ cpswp_attach(device_t dev)
struct cpswp_softc *sc;
uint32_t reg;
uint8_t mac_addr[ETHER_ADDR_LEN];
phandle_t opp_table;
struct syscon *syscon;
sc = device_get_softc(dev);
sc->dev = dev;
@ -1047,15 +1050,34 @@ cpswp_attach(device_t dev)
IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
IFQ_SET_READY(&ifp->if_snd);
/* FIXME: For now; Go and kidnap syscon from opp-table */
/* ti,cpsw actually have an optional syscon reference but only for am33xx?? */
opp_table = OF_finddevice("/opp-table");
if (opp_table == -1) {
device_printf(dev, "Cant find /opp-table\n");
cpswp_detach(dev);
return (ENXIO);
}
if (!OF_hasprop(opp_table, "syscon")) {
device_printf(dev, "/opp-table doesnt have required syscon property\n");
cpswp_detach(dev);
return (ENXIO);
}
if (syscon_get_by_ofw_property(dev, opp_table, "syscon", &syscon) != 0) {
device_printf(dev, "Failed to get syscon\n");
cpswp_detach(dev);
return (ENXIO);
}
/* Get high part of MAC address from control module (mac_id[0|1]_hi) */
ti_scm_reg_read_4(SCM_MAC_ID0_HI + sc->unit * 8, &reg);
reg = SYSCON_READ_4(syscon, SCM_MAC_ID0_HI + sc->unit * 8);
mac_addr[0] = reg & 0xFF;
mac_addr[1] = (reg >> 8) & 0xFF;
mac_addr[2] = (reg >> 16) & 0xFF;
mac_addr[3] = (reg >> 24) & 0xFF;
/* Get low part of MAC address from control module (mac_id[0|1]_lo) */
ti_scm_reg_read_4(SCM_MAC_ID0_LO + sc->unit * 8, &reg);
reg = SYSCON_READ_4(syscon, SCM_MAC_ID0_LO + sc->unit * 8);
mac_addr[4] = reg & 0xFF;
mac_addr[5] = (reg >> 8) & 0xFF;

View File

@ -1,14 +1,16 @@
#$FreeBSD$
arm/ti/ti_cpuid.c standard
arm/ti/ti_hwmods.c standard
arm/ti/ti_machdep.c standard
arm/ti/ti_prcm.c standard
arm/ti/ti_omap4_cm.c standard
arm/ti/ti_scm.c standard
arm/ti/ti_scm_syscon.c standard
arm/ti/ti_pinmux.c standard
dev/mbox/mbox_if.m optional ti_mbox
arm/ti/ti_mbox.c optional ti_mbox
arm/ti/ti_pruss.c optional ti_pruss
arm/ti/ti_prm.c optional ti_pruss
arm/ti/ti_wdt.c optional ti_wdt
arm/ti/ti_adc.c optional ti_adc
arm/ti/ti_gpio.c optional gpio
@ -18,6 +20,15 @@ arm/ti/ti_sdhci.c optional sdhci
arm/ti/ti_spi.c optional ti_spi
arm/ti/ti_sysc.c standard
arm/ti/clk/clock_common.c standard
arm/ti/clk/ti_clk_clkctrl.c standard
arm/ti/clk/ti_clkctrl.c standard
arm/ti/clk/ti_clk_dpll.c standard
arm/ti/clk/ti_dpll_clock.c standard
arm/ti/clk/ti_mux_clock.c standard
arm/ti/clk/ti_divider_clock.c standard
arm/ti/clk/ti_gate_clock.c standard
dev/uart/uart_dev_ti8250.c optional uart
dev/uart/uart_dev_ns8250.c optional uart

View File

@ -9,7 +9,7 @@ arm/ti/ti_sdma.c optional ti_sdma
arm/ti/omap4/omap4_gpio.c optional gpio
arm/ti/omap4/omap4_l2cache.c optional pl310
arm/ti/omap4/omap4_prcm_clks.c standard
#arm/ti/omap4/omap4_prcm_clks.c standard
arm/ti/omap4/omap4_scm_padconf.c standard
arm/ti/omap4/omap4_mp.c optional smp
arm/ti/omap4/omap4_wugen.c standard

View File

@ -58,7 +58,7 @@ __FBSDID("$FreeBSD$");
#include <dev/evdev/evdev.h>
#endif
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/ti_adcreg.h>
#include <arm/ti/ti_adcvar.h>
@ -824,7 +824,7 @@ ti_adc_attach(device_t dev)
}
/* Activate the ADC_TSC module. */
err = ti_prcm_clk_enable(TSC_ADC_CLK);
err = ti_sysc_clock_enable(device_get_parent(dev));
if (err)
return (err);
@ -846,7 +846,7 @@ ti_adc_attach(device_t dev)
}
/* Check the ADC revision. */
rev = ADC_READ4(sc, ADC_REVISION);
rev = ADC_READ4(sc, ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
device_printf(dev,
"scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n",
(rev & ADC_REV_SCHEME_MSK) >> ADC_REV_SCHEME_SHIFT,
@ -964,6 +964,7 @@ static devclass_t ti_adc_devclass;
DRIVER_MODULE(ti_adc, simplebus, ti_adc_driver, ti_adc_devclass, 0, 0);
MODULE_VERSION(ti_adc, 1);
MODULE_DEPEND(ti_adc, simplebus, 1, 1, 1);
MODULE_DEPEND(ti_adc, ti_sysc, 1, 1, 1);
#ifdef EVDEV_SUPPORT
MODULE_DEPEND(ti_adc, evdev, 1, 1, 1);
#endif

View File

@ -54,7 +54,7 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_scm.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/ti_edma3.h>
@ -181,8 +181,10 @@ ti_edma3_attach(device_t dev)
return (ENXIO);
}
/* FIXME: Require DTS from Linux kernel 5.7 */
/* FIXME: OK to enable clkctrl here? */
/* Enable Channel Controller */
ti_prcm_clk_enable(EDMA_TPCC_CLK);
ti_sysc_clock_enable(device_get_parent(dev));
reg = ti_edma3_cc_rd_4(TI_EDMA3CC_PID);
@ -218,7 +220,7 @@ static driver_t ti_edma3_driver = {
static devclass_t ti_edma3_devclass;
DRIVER_MODULE(ti_edma3, simplebus, ti_edma3_driver, ti_edma3_devclass, 0, 0);
MODULE_DEPEND(ti_edma3, ti_prcm, 1, 1, 1);
MODULE_DEPEND(ti_edma3, ti_sysc, 1, 1, 1);
static void
ti_edma3_intr_comp(void *arg)
@ -244,11 +246,6 @@ ti_edma3_init(unsigned int eqn)
uint32_t reg;
int i;
/* on AM335x Event queue 0 is always mapped to Transfer Controller 0,
* event queue 1 to TC2, etc. So we are asking PRCM to power on specific
* TC based on what event queue we need to initialize */
ti_prcm_clk_enable(EDMA_TPTC0_CLK + eqn);
/* Clear Event Missed Regs */
ti_edma3_cc_wr_4(TI_EDMA3CC_EMCR, 0xFFFFFFFF);
ti_edma3_cc_wr_4(TI_EDMA3CC_EMCRH, 0xFFFFFFFF);

View File

@ -57,8 +57,7 @@ __FBSDID("$FreeBSD$");
#include <arm/ti/ti_cpuid.h>
#include <arm/ti/ti_gpio.h>
#include <arm/ti/ti_scm.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_hwmods.h>
#include <arm/ti/ti_sysc.h>
#include <dev/gpio/gpiobusvar.h>
#include <dev/ofw/openfirm.h>
@ -117,6 +116,18 @@ __FBSDID("$FreeBSD$");
#define PINS_PER_BANK 32
#define TI_GPIO_MASK(p) (1U << ((p) % PINS_PER_BANK))
#define OMAP4_GPIO1_REV 0x00000
#define OMAP4_GPIO2_REV 0x55000
#define OMAP4_GPIO3_REV 0x57000
#define OMAP4_GPIO4_REV 0x59000
#define OMAP4_GPIO5_REV 0x5b000
#define OMAP4_GPIO6_REV 0x5d000
#define AM335X_GPIO0_REV 0x07000
#define AM335X_GPIO1_REV 0x4C000
#define AM335X_GPIO2_REV 0xAC000
#define AM335X_GPIO3_REV 0xAE000
static int ti_gpio_intr(void *arg);
static int ti_gpio_detach(device_t);
@ -434,7 +445,7 @@ ti_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
oe &= ~TI_GPIO_MASK(pin);
ti_gpio_write_4(sc, TI_GPIO_OE, oe);
TI_GPIO_UNLOCK(sc);
return (0);
}
@ -499,7 +510,7 @@ ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
return (EINVAL);
/*
* Return data from output latch when set as output and from the
* Return data from output latch when set as output and from the
* input register otherwise.
*/
TI_GPIO_LOCK(sc);
@ -553,29 +564,72 @@ ti_gpio_pin_toggle(device_t dev, uint32_t pin)
static int
ti_gpio_bank_init(device_t dev)
{
int pin;
int pin, err;
struct ti_gpio_softc *sc;
uint32_t flags, reg_oe, reg_set, rev;
clk_ident_t clk;
uint64_t rev_address;
sc = device_get_softc(dev);
/* Enable the interface and functional clocks for the module. */
clk = ti_hwmods_get_clock(dev);
if (clk == INVALID_CLK_IDENT) {
device_printf(dev, "failed to get device id based on ti,hwmods\n");
rev_address = ti_sysc_get_rev_address(device_get_parent(dev));
/* AM335x
* sc->sc_bank used in am335x/am335x_gpio.c and omap4/omap4_gpio.c */
switch(ti_chip()) {
#ifdef SOC_OMAP4
case CHIP_OMAP_4:
switch (rev_address) {
case OMAP4_GPIO1_REV:
sc->sc_bank = 0;
break;
case OMAP4_GPIO2_REV:
sc->sc_bank = 1;
break;
case OMAP4_GPIO3_REV:
sc->sc_bank = 2;
break;
case OMAP4_GPIO4_REV:
sc->sc_bank = 3;
break;
case OMAP4_GPIO5_REV:
sc->sc_bank = 4;
break;
case OMAP4_GPIO6_REV:
sc->sc_bank = 5;
break;
}
#endif
#ifdef SOC_TI_AM335X
case CHIP_AM335X:
switch (rev_address) {
case AM335X_GPIO0_REV:
sc->sc_bank = 0;
break;
case AM335X_GPIO1_REV:
sc->sc_bank = 1;
break;
case AM335X_GPIO2_REV:
sc->sc_bank = 2;
break;
case AM335X_GPIO3_REV:
sc->sc_bank = 3;
break;
}
#endif
}
err = ti_sysc_clock_enable(device_get_parent(dev));
if (err) {
device_printf(dev, "Failed to enable clock\n");
return (EINVAL);
}
sc->sc_bank = clk - GPIO1_CLK + ti_first_gpio_bank();
ti_prcm_clk_enable(clk);
/*
* Read the revision number of the module. TI don't publish the
* actual revision numbers, so instead the values have been
* determined by experimentation.
*/
rev = ti_gpio_read_4(sc, TI_GPIO_REVISION);
rev = ti_gpio_read_4(sc,
ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
/* Check the revision. */
if (rev != ti_gpio_rev()) {
@ -669,7 +723,7 @@ ti_gpio_attach(device_t dev)
/* We need to go through each block and ensure the clocks are running and
* the module is enabled. It might be better to do this only when the
* pins are configured which would result in less power used if the GPIO
* pins weren't used ...
* pins weren't used ...
*/
if (sc->sc_mem_res != NULL) {
/* Initialize the GPIO module. */

View File

@ -1,214 +0,0 @@
/*-
* Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
* 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$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_hwmods.h>
struct hwmod {
const char *name;
int clock_id;
};
struct hwmod ti_hwmods[] = {
{"i2c1", I2C1_CLK},
{"i2c2", I2C2_CLK},
{"i2c3", I2C3_CLK},
{"i2c4", I2C4_CLK},
{"i2c5", I2C5_CLK},
{"gpio1", GPIO1_CLK},
{"gpio2", GPIO2_CLK},
{"gpio3", GPIO3_CLK},
{"gpio4", GPIO4_CLK},
{"gpio5", GPIO5_CLK},
{"gpio6", GPIO6_CLK},
{"gpio7", GPIO7_CLK},
{"mmc1", MMC1_CLK},
{"mmc2", MMC2_CLK},
{"mmc3", MMC3_CLK},
{"mmc4", MMC4_CLK},
{"mmc5", MMC5_CLK},
{"mmc6", MMC6_CLK},
{"epwmss0", PWMSS0_CLK},
{"epwmss1", PWMSS1_CLK},
{"epwmss2", PWMSS2_CLK},
{"spi0", SPI0_CLK},
{"spi1", SPI1_CLK},
{"timer1", TIMER1_CLK},
{"timer2", TIMER2_CLK},
{"timer3", TIMER3_CLK},
{"timer4", TIMER4_CLK},
{"timer5", TIMER5_CLK},
{"timer6", TIMER6_CLK},
{"timer7", TIMER7_CLK},
{"uart1", UART1_CLK},
{"uart2", UART2_CLK},
{"uart3", UART3_CLK},
{"uart4", UART4_CLK},
{"uart5", UART5_CLK},
{"uart6", UART6_CLK},
{"uart7", UART7_CLK},
{NULL, 0}
};
static inline int
ti_get_hwmods_prop(phandle_t node, void **name)
{
int len;
if ((len = OF_getprop_alloc(node, "ti,hwmods", name)) > 0)
return (len);
return (OF_getprop_alloc(OF_parent(node), "ti,hwmods", name));
}
clk_ident_t
ti_hwmods_get_clock(device_t dev)
{
phandle_t node;
int len, l;
char *name;
char *buf;
int clk;
struct hwmod *hw;
if ((node = ofw_bus_get_node(dev)) == 0)
return (INVALID_CLK_IDENT);
if ((len = ti_get_hwmods_prop(node, (void **)&name)) <= 0)
return (INVALID_CLK_IDENT);
buf = name;
clk = INVALID_CLK_IDENT;
while ((len > 0) && (clk == INVALID_CLK_IDENT)) {
for (hw = ti_hwmods; hw->name != NULL; ++hw) {
if (strcmp(hw->name, name) == 0) {
clk = hw->clock_id;
break;
}
}
/* Slide to the next sub-string. */
l = strlen(name) + 1;
name += l;
len -= l;
}
if (len > 0)
device_printf(dev, "WARNING: more than one ti,hwmod \n");
OF_prop_free(buf);
return (clk);
}
int ti_hwmods_contains(device_t dev, const char *hwmod)
{
phandle_t node;
int len, l;
char *name;
char *buf;
int result;
if ((node = ofw_bus_get_node(dev)) == 0)
return (0);
if ((len = ti_get_hwmods_prop(node, (void **)&name)) <= 0)
return (0);
buf = name;
result = 0;
while (len > 0) {
if (strcmp(name, hwmod) == 0) {
result = 1;
break;
}
/* Slide to the next sub-string. */
l = strlen(name) + 1;
name += l;
len -= l;
}
OF_prop_free(buf);
return (result);
}
int
ti_hwmods_get_unit(device_t dev, const char *hwmod)
{
phandle_t node;
int l, len, hwmodlen, result;
char *name;
char *buf;
if ((node = ofw_bus_get_node(dev)) == 0)
return (0);
if ((len = ti_get_hwmods_prop(node, (void **)&name)) <= 0)
return (0);
buf = name;
hwmodlen = strlen(hwmod);
result = 0;
while (len > 0) {
if (strncmp(name, hwmod, hwmodlen) == 0) {
result = (int)strtoul(name + hwmodlen, NULL, 10);
break;
}
/* Slide to the next sub-string. */
l = strlen(name) + 1;
name += l;
len -= l;
}
OF_prop_free(buf);
return (result);
}

View File

@ -1,37 +0,0 @@
/*-
* Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
* 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 _TI_HWMODS_H_
#define _TI_HWMODS_H_
clk_ident_t ti_hwmods_get_clock(device_t dev);
int ti_hwmods_contains(device_t dev, const char *hwmod);
/* Returns the N from "hwmodN" in the ti,hwmods property; 0 on failure. */
int ti_hwmods_get_unit(device_t dev, const char *hwmod);
#endif /* _TI_HWMODS_H_ */

View File

@ -63,8 +63,7 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_cpuid.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_hwmods.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/ti_i2c.h>
#include <dev/iicbus/iiconf.h>
@ -79,7 +78,6 @@ __FBSDID("$FreeBSD$");
struct ti_i2c_softc
{
device_t sc_dev;
clk_ident_t clk_id;
struct resource* sc_irq_res;
struct resource* sc_mem_res;
device_t sc_iicbus;
@ -700,7 +698,7 @@ ti_i2c_activate(device_t dev)
* 1. Enable the functional and interface clocks (see Section
* 23.1.5.1.1.1.1).
*/
err = ti_prcm_clk_enable(sc->clk_id);
err = ti_sysc_clock_enable(device_get_parent(dev));
if (err)
return (err);
@ -748,7 +746,7 @@ ti_i2c_deactivate(device_t dev)
}
/* Finally disable the functional and interface clocks. */
ti_prcm_clk_disable(sc->clk_id);
ti_sysc_clock_disable(device_get_parent(dev));
}
static int
@ -818,7 +816,6 @@ static int
ti_i2c_attach(device_t dev)
{
int err, rid;
phandle_t node;
struct ti_i2c_softc *sc;
struct sysctl_ctx_list *ctx;
struct sysctl_oid_list *tree;
@ -827,15 +824,6 @@ ti_i2c_attach(device_t dev)
sc = device_get_softc(dev);
sc->sc_dev = dev;
/* Get the i2c device id from FDT. */
node = ofw_bus_get_node(dev);
/* i2c ti,hwmods bindings is special: it start with index 1 */
sc->clk_id = ti_hwmods_get_clock(dev);
if (sc->clk_id == INVALID_CLK_IDENT) {
device_printf(dev, "failed to get device id using ti,hwmod\n");
return (ENXIO);
}
/* Get the memory resource for the register mapping. */
rid = 0;
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
@ -986,5 +974,5 @@ static devclass_t ti_i2c_devclass;
DRIVER_MODULE(ti_iic, simplebus, ti_i2c_driver, ti_i2c_devclass, 0, 0);
DRIVER_MODULE(iicbus, ti_iic, iicbus_driver, iicbus_devclass, 0, 0);
MODULE_DEPEND(ti_iic, ti_prcm, 1, 1, 1);
MODULE_DEPEND(ti_iic, ti_sysc, 1, 1, 1);
MODULE_DEPEND(ti_iic, iicbus, 1, 1, 1);

View File

@ -50,7 +50,7 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <arm/ti/ti_mbox.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_sysc.h>
#include "mbox_if.h"
@ -102,6 +102,7 @@ static driver_t ti_mbox_driver = {
static devclass_t ti_mbox_devclass;
DRIVER_MODULE(ti_mbox, simplebus, ti_mbox_driver, ti_mbox_devclass, 0, 0);
MODULE_DEPEND(ti_mbox, ti_sysc, 1, 1, 1);
static __inline uint32_t
ti_mbox_reg_read(struct ti_mbox_softc *sc, uint16_t reg)
@ -137,10 +138,11 @@ ti_mbox_attach(device_t dev)
int rid, delay, chan;
uint32_t rev, sysconfig;
if (ti_prcm_clk_enable(MAILBOX0_CLK) != 0) {
if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) {
device_printf(dev, "could not enable MBOX clock\n");
return (ENXIO);
}
sc = device_get_softc(dev);
rid = 0;
mtx_init(&sc->sc_mtx, "TI mbox", NULL, MTX_DEF);
@ -188,7 +190,8 @@ ti_mbox_attach(device_t dev)
*/
ti_mbox_reg_write(sc, TI_MBOX_SYSCONFIG,
ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) | TI_MBOX_SYSCONFIG_SMARTIDLE);
rev = ti_mbox_reg_read(sc, TI_MBOX_REVISION);
rev = ti_mbox_reg_read(sc,
ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
DPRINTF("rev %d\n", rev);
device_printf(dev, "revision %d.%d\n", (rev >> 8) & 0x4, rev & 0x40);
/*

151
sys/arm/ti/ti_omap4_cm.c Normal file
View File

@ -0,0 +1,151 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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.
*
* Based on sys/arm/ti/ti_sysc.c
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/fbio.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/resource.h>
#include <machine/bus.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <vm/pmap.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_omap4_cm.h>
static struct ofw_compat_data compat_data[] = {
{ "ti,omap4-cm", 1 },
{ NULL, 0 }
};
struct ti_omap4_cm_softc {
struct simplebus_softc sc;
device_t dev;
};
uint64_t
ti_omap4_cm_get_simplebus_base_host(device_t dev) {
struct ti_omap4_cm_softc *sc;
sc = device_get_softc(dev);
if (sc->sc.nranges == 0)
return (0);
return (sc->sc.ranges[0].host);
}
static int ti_omap4_cm_probe(device_t dev);
static int ti_omap4_cm_attach(device_t dev);
static int ti_omap4_cm_detach(device_t dev);
static int
ti_omap4_cm_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, "TI OMAP4-CM");
if (!bootverbose)
device_quiet(dev);
return (BUS_PROBE_DEFAULT);
}
static int
ti_omap4_cm_attach(device_t dev)
{
struct ti_omap4_cm_softc *sc;
device_t cdev;
phandle_t node, child;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(dev);
simplebus_init(dev, node);
if (simplebus_fill_ranges(node, &sc->sc) < 0) {
device_printf(dev, "could not get ranges\n");
return (ENXIO);
}
bus_generic_probe(sc->dev);
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 int
ti_omap4_cm_detach(device_t dev)
{
return (EBUSY);
}
static device_method_t ti_omap4_cm_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_omap4_cm_probe),
DEVMETHOD(device_attach, ti_omap4_cm_attach),
DEVMETHOD(device_detach, ti_omap4_cm_detach),
DEVMETHOD_END
};
DEFINE_CLASS_1(ti_omap4_cm, ti_omap4_cm_driver, ti_omap4_cm_methods,
sizeof(struct ti_omap4_cm_softc), simplebus_driver);
static devclass_t ti_omap4_cm_devclass;
EARLY_DRIVER_MODULE(ti_omap4_cm, simplebus, ti_omap4_cm_driver,
ti_omap4_cm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);
EARLY_DRIVER_MODULE(ti_omap4_cm, ofwbus, ti_omap4_cm_driver,
ti_omap4_cm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);

34
sys/arm/ti/ti_omap4_cm.h Normal file
View File

@ -0,0 +1,34 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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 __TI_OMAP4_CM__
#define __TI_OMAP4_CM__
uint64_t ti_omap4_cm_get_simplebus_base_host(device_t dev);
#endif /* __TI_OMAP4_CM__ */

View File

@ -459,3 +459,5 @@ static driver_t ti_pinmux_driver = {
static devclass_t ti_pinmux_devclass;
DRIVER_MODULE(ti_pinmux, simplebus, ti_pinmux_driver, ti_pinmux_devclass, 0, 0);
MODULE_VERSION(ti_pinmux, 1);
MODULE_DEPEND(ti_pinmux, ti_scm, 1, 1, 1);

View File

@ -1,8 +1,310 @@
/*-
* SPDX-License-Identifier: BSD-4-Clause
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2010
* Ben Gray <ben.r.gray@gmail.com>.
* Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
* All rights reserved.
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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$
*/
/* Based on sys/arm/ti/am335x/am335x_prcm.c */
#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/malloc.h>
#include <sys/rman.h>
#include <sys/timeet.h>
#include <sys/timetc.h>
#include <sys/watchdog.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/intr.h>
#include <arm/ti/ti_cpuid.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/tivar.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "clkdev_if.h"
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
struct ti_prcm_softc {
struct simplebus_softc sc_simplebus;
device_t dev;
struct resource * mem_res;
bus_space_tag_t bst;
bus_space_handle_t bsh;
int attach_done;
struct mtx mtx;
};
static struct ti_prcm_softc *ti_prcm_sc = NULL;
static void omap4_prcm_reset(void);
static void am335x_prcm_reset(void);
#define TI_AM3_PRCM 18
#define TI_AM4_PRCM 17
#define TI_OMAP2_PRCM 16
#define TI_OMAP3_PRM 15
#define TI_OMAP3_CM 14
#define TI_OMAP4_CM1 13
#define TI_OMAP4_PRM 12
#define TI_OMAP4_CM2 11
#define TI_OMAP4_SCRM 10
#define TI_OMAP5_PRM 9
#define TI_OMAP5_CM_CORE_AON 8
#define TI_OMAP5_SCRM 7
#define TI_OMAP5_CM_CORE 6
#define TI_DRA7_PRM 5
#define TI_DRA7_CM_CORE_AON 4
#define TI_DRA7_CM_CORE 3
#define TI_DM814_PRCM 2
#define TI_DM816_PRCM 1
#define TI_PRCM_END 0
static struct ofw_compat_data compat_data[] = {
{ "ti,am3-prcm", TI_AM3_PRCM },
{ "ti,am4-prcm", TI_AM4_PRCM },
{ "ti,omap2-prcm", TI_OMAP2_PRCM },
{ "ti,omap3-prm", TI_OMAP3_PRM },
{ "ti,omap3-cm", TI_OMAP3_CM },
{ "ti,omap4-cm1", TI_OMAP4_CM1 },
{ "ti,omap4-prm", TI_OMAP4_PRM },
{ "ti,omap4-cm2", TI_OMAP4_CM2 },
{ "ti,omap4-scrm", TI_OMAP4_SCRM },
{ "ti,omap5-prm", TI_OMAP5_PRM },
{ "ti,omap5-cm-core-aon", TI_OMAP5_CM_CORE_AON },
{ "ti,omap5-scrm", TI_OMAP5_SCRM },
{ "ti,omap5-cm-core", TI_OMAP5_CM_CORE },
{ "ti,dra7-prm", TI_DRA7_PRM },
{ "ti,dra7-cm-core-aon", TI_DRA7_CM_CORE_AON },
{ "ti,dra7-cm-core", TI_DRA7_CM_CORE },
{ "ti,dm814-prcm", TI_DM814_PRCM },
{ "ti,dm816-prcm", TI_DM816_PRCM },
{ NULL, TI_PRCM_END}
};
static int
ti_prcm_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, "TI Power and Clock Management");
return(BUS_PROBE_DEFAULT);
}
static int
ti_prcm_attach(device_t dev)
{
struct ti_prcm_softc *sc;
phandle_t node, child;
int rid;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(sc->dev);
simplebus_init(sc->dev, node);
if (simplebus_fill_ranges(node, &sc->sc_simplebus) < 0) {
device_printf(sc->dev, "could not get ranges\n");
return (ENXIO);
}
if (sc->sc_simplebus.nranges == 0) {
device_printf(sc->dev, "nranges == 0\n");
return (ENXIO);
}
sc->mem_res = bus_alloc_resource(sc->dev, SYS_RES_MEMORY, &rid,
sc->sc_simplebus.ranges[0].host,
(sc->sc_simplebus.ranges[0].host +
sc->sc_simplebus.ranges[0].size - 1),
sc->sc_simplebus.ranges[0].size,
RF_ACTIVE | RF_SHAREABLE);
if (sc->mem_res == NULL) {
return (ENXIO);
}
sc->bst = rman_get_bustag(sc->mem_res);
sc->bsh = rman_get_bushandle(sc->mem_res);
mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
/* Fixme: for xxx_prcm_reset functions.
* Get rid of global variables?
*/
ti_prcm_sc = sc;
switch(ti_chip()) {
#ifdef SOC_OMAP4
case CHIP_OMAP_4:
ti_cpu_reset = omap4_prcm_reset;
break;
#endif
#ifdef SOC_TI_AM335X
case CHIP_AM335X:
ti_cpu_reset = am335x_prcm_reset;
break;
#endif
}
bus_generic_probe(sc->dev);
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
simplebus_add_device(dev, child, 0, NULL, -1, NULL);
}
return (bus_generic_attach(sc->dev));
}
int
ti_prcm_write_4(device_t dev, bus_addr_t addr, uint32_t val)
{
struct ti_prcm_softc *sc;
sc = device_get_softc(dev);
DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val);
bus_space_write_4(sc->bst, sc->bsh, addr, val);
return (0);
}
int
ti_prcm_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
{
struct ti_prcm_softc *sc;
sc = device_get_softc(dev);
*val = bus_space_read_4(sc->bst, sc->bsh, addr);
DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val);
return (0);
}
int
ti_prcm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
{
struct ti_prcm_softc *sc;
uint32_t reg;
sc = device_get_softc(dev);
reg = bus_space_read_4(sc->bst, sc->bsh, addr);
reg &= ~clr;
reg |= set;
bus_space_write_4(sc->bst, sc->bsh, addr, reg);
DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", addr, reg, clr, set);
return (0);
}
void
ti_prcm_device_lock(device_t dev)
{
struct ti_prcm_softc *sc;
sc = device_get_softc(dev);
mtx_lock(&sc->mtx);
}
void
ti_prcm_device_unlock(device_t dev)
{
struct ti_prcm_softc *sc;
sc = device_get_softc(dev);
mtx_unlock(&sc->mtx);
}
static device_method_t ti_prcm_methods[] = {
DEVMETHOD(device_probe, ti_prcm_probe),
DEVMETHOD(device_attach, ti_prcm_attach),
/* clkdev interface */
DEVMETHOD(clkdev_write_4, ti_prcm_write_4),
DEVMETHOD(clkdev_read_4, ti_prcm_read_4),
DEVMETHOD(clkdev_modify_4, ti_prcm_modify_4),
DEVMETHOD(clkdev_device_lock, ti_prcm_device_lock),
DEVMETHOD(clkdev_device_unlock, ti_prcm_device_unlock),
DEVMETHOD_END
};
DEFINE_CLASS_1(ti_prcm, ti_prcm_driver, ti_prcm_methods,
sizeof(struct ti_prcm_softc), simplebus_driver);
static devclass_t ti_prcm_devclass;
EARLY_DRIVER_MODULE(ti_prcm, ofwbus, ti_prcm_driver,
ti_prcm_devclass, 0, 0, BUS_PASS_BUS);
EARLY_DRIVER_MODULE(ti_prcm, simplebus, ti_prcm_driver,
ti_prcm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ti_prcm, 1);
MODULE_DEPEND(ti_prcm, ti_scm, 1, 1, 1);
/* From sys/arm/ti/am335x/am335x_prcm.c
* Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
*/
#define PRM_DEVICE_OFFSET 0xF00
#define AM335x_PRM_RSTCTRL (PRM_DEVICE_OFFSET + 0x00)
static void
am335x_prcm_reset(void)
{
ti_prcm_write_4(ti_prcm_sc->dev, AM335x_PRM_RSTCTRL, (1<<1));
}
/* FIXME: Is this correct - or should the license part be ontop? */
/* From sys/arm/ti/omap4/omap4_prcm_clks.c */
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2011
* Ben Gray <ben.r.gray@gmail.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -13,10 +315,7 @@
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Ben Gray.
* 4. The name of the company nor the name of the author may be used to
* 3. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
@ -31,327 +330,16 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define PRM_RSTCTRL 0x1b00
#define PRM_RSTCTRL_RESET 0x2
/**
* Power, Reset and Clock Management Module
*
* This is a very simple driver wrapper around the PRCM set of registers in
* the OMAP3 chip. It allows you to turn on and off things like the functional
* and interface clocks to the various on-chip modules.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <machine/intr.h>
#include <arm/ti/ti_cpuid.h>
#include <arm/ti/ti_prcm.h>
/**
* ti_*_clk_devmap - Array of clock devices, should be defined one per SoC
*
* This array is typically defined in one of the targeted *_prcm_clk.c
* files and is specific to the given SoC platform. Each entry in the array
* corresponds to an individual clock device.
*/
extern struct ti_clock_dev ti_omap4_clk_devmap[];
extern struct ti_clock_dev ti_am335x_clk_devmap[];
/**
* ti_prcm_clk_dev - returns a pointer to the clock device with given id
* @clk: the ID of the clock device to get
*
* Simply iterates through the clk_devmap global array and returns a pointer
* to the clock device if found.
*
* LOCKING:
* None
*
* RETURNS:
* The pointer to the clock device on success, on failure NULL is returned.
*/
static struct ti_clock_dev *
ti_prcm_clk_dev(clk_ident_t clk)
static void
omap4_prcm_reset(void)
{
struct ti_clock_dev *clk_dev;
/* Find the clock within the devmap - it's a bit inefficent having a for
* loop for this, but this function should only called when a driver is
* being activated so IMHO not a big issue.
*/
clk_dev = NULL;
switch(ti_chip()) {
#ifdef SOC_OMAP4
case CHIP_OMAP_4:
clk_dev = &(ti_omap4_clk_devmap[0]);
break;
#endif
#ifdef SOC_TI_AM335X
case CHIP_AM335X:
clk_dev = &(ti_am335x_clk_devmap[0]);
break;
#endif
}
if (clk_dev == NULL)
panic("No clock devmap found");
while (clk_dev->id != INVALID_CLK_IDENT) {
if (clk_dev->id == clk) {
return (clk_dev);
}
clk_dev++;
}
uint32_t reg;
/* Sanity check we managed to find the clock */
printf("ti_prcm: Failed to find clock device (%d)\n", clk);
return (NULL);
}
/**
* ti_prcm_clk_valid - enables a clock for a particular module
* @clk: identifier for the module to enable, see ti_prcm.h for a list
* of possible modules.
* Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
*
* This function can enable either a functional or interface clock.
*
* The real work done to enable the clock is really done in the callback
* function associated with the clock, this function is simply a wrapper
* around that.
*
* LOCKING:
* Internally locks the driver context.
*
* RETURNS:
* Returns 0 on success or positive error code on failure.
*/
int
ti_prcm_clk_valid(clk_ident_t clk)
{
int ret = 0;
if (ti_prcm_clk_dev(clk) == NULL)
ret = EINVAL;
return (ret);
}
/**
* ti_prcm_clk_enable - enables a clock for a particular module
* @clk: identifier for the module to enable, see ti_prcm.h for a list
* of possible modules.
* Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
*
* This function can enable either a functional or interface clock.
*
* The real work done to enable the clock is really done in the callback
* function associated with the clock, this function is simply a wrapper
* around that.
*
* LOCKING:
* Internally locks the driver context.
*
* RETURNS:
* Returns 0 on success or positive error code on failure.
*/
int
ti_prcm_clk_enable(clk_ident_t clk)
{
struct ti_clock_dev *clk_dev;
int ret;
/* Find the clock within the devmap - it's a bit inefficent having a for
* loop for this, but this function should only called when a driver is
* being activated so IMHO not a big issue.
*/
clk_dev = ti_prcm_clk_dev(clk);
/* Sanity check we managed to find the clock */
if (clk_dev == NULL)
return (EINVAL);
/* Activate the clock */
if (clk_dev->clk_activate)
ret = clk_dev->clk_activate(clk_dev);
else
ret = EINVAL;
return (ret);
}
/**
* ti_prcm_clk_disable - disables a clock for a particular module
* @clk: identifier for the module to enable, see ti_prcm.h for a list
* of possible modules.
* Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
*
* This function can enable either a functional or interface clock.
*
* The real work done to enable the clock is really done in the callback
* function associated with the clock, this function is simply a wrapper
* around that.
*
* LOCKING:
* Internally locks the driver context.
*
* RETURNS:
* Returns 0 on success or positive error code on failure.
*/
int
ti_prcm_clk_disable(clk_ident_t clk)
{
struct ti_clock_dev *clk_dev;
int ret;
/* Find the clock within the devmap - it's a bit inefficent having a for
* loop for this, but this function should only called when a driver is
* being activated so IMHO not a big issue.
*/
clk_dev = ti_prcm_clk_dev(clk);
/* Sanity check we managed to find the clock */
if (clk_dev == NULL)
return (EINVAL);
/* Activate the clock */
if (clk_dev->clk_deactivate)
ret = clk_dev->clk_deactivate(clk_dev);
else
ret = EINVAL;
return (ret);
}
/**
* ti_prcm_clk_set_source - sets the source
* @clk: identifier for the module to enable, see ti_prcm.h for a list
* of possible modules.
* Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
*
* This function can enable either a functional or interface clock.
*
* The real work done to enable the clock is really done in the callback
* function associated with the clock, this function is simply a wrapper
* around that.
*
* LOCKING:
* Internally locks the driver context.
*
* RETURNS:
* Returns 0 on success or positive error code on failure.
*/
int
ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc)
{
struct ti_clock_dev *clk_dev;
int ret;
/* Find the clock within the devmap - it's a bit inefficent having a for
* loop for this, but this function should only called when a driver is
* being activated so IMHO not a big issue.
*/
clk_dev = ti_prcm_clk_dev(clk);
/* Sanity check we managed to find the clock */
if (clk_dev == NULL)
return (EINVAL);
/* Activate the clock */
if (clk_dev->clk_set_source)
ret = clk_dev->clk_set_source(clk_dev, clksrc);
else
ret = EINVAL;
return (ret);
}
/**
* ti_prcm_clk_get_source_freq - gets the source clock frequency
* @clk: identifier for the module to enable, see ti_prcm.h for a list
* of possible modules.
* @freq: pointer to an integer that upon return will contain the src freq
*
* This function returns the frequency of the source clock.
*
* The real work done to enable the clock is really done in the callback
* function associated with the clock, this function is simply a wrapper
* around that.
*
* LOCKING:
* Internally locks the driver context.
*
* RETURNS:
* Returns 0 on success or positive error code on failure.
*/
int
ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq)
{
struct ti_clock_dev *clk_dev;
int ret;
/* Find the clock within the devmap - it's a bit inefficent having a for
* loop for this, but this function should only called when a driver is
* being activated so IMHO not a big issue.
*/
clk_dev = ti_prcm_clk_dev(clk);
/* Sanity check we managed to find the clock */
if (clk_dev == NULL)
return (EINVAL);
/* Get the source frequency of the clock */
if (clk_dev->clk_get_source_freq)
ret = clk_dev->clk_get_source_freq(clk_dev, freq);
else
ret = EINVAL;
return (ret);
}
/**
* ti_prcm_clk_set_source_freq - sets the source clock frequency as close to freq as possible
* @clk: identifier for the module to enable, see ti_prcm.h for a list
* of possible modules.
* @freq: requested freq
*
* LOCKING:
* Internally locks the driver context.
*
* RETURNS:
* Returns 0 on success or positive error code on failure.
*/
int
ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq)
{
struct ti_clock_dev *clk_dev;
int ret;
clk_dev = ti_prcm_clk_dev(clk);
/* Sanity check we managed to find the clock */
if (clk_dev == NULL)
return (EINVAL);
/* Get the source frequency of the clock */
if (clk_dev->clk_set_source_freq)
ret = clk_dev->clk_set_source_freq(clk_dev, freq);
else
ret = EINVAL;
return (ret);
ti_prcm_read_4(ti_prcm_sc->dev, PRM_RSTCTRL, &reg);
reg = reg | PRM_RSTCTRL_RESET;
ti_prcm_write_4(ti_prcm_sc->dev, PRM_RSTCTRL, reg);
ti_prcm_read_4(ti_prcm_sc->dev, PRM_RSTCTRL, &reg);
}

View File

@ -1,9 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-4-Clause
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2010
* Ben Gray <ben.r.gray@gmail.com>.
* All rights reserved.
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -13,197 +11,29 @@
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Ben Gray.
* 4. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
* 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 BEN GRAY 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.
* 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 __TI_PRCM_H__
#define __TI_PRCM_H__
/*
* Texas Instruments - OMAP3xxx series processors
*
* Reference:
* OMAP35x Applications Processor
* Technical Reference Manual
* (omap35xx_techref.pdf)
*/
#ifndef _TI_PRCM_H_
#define _TI_PRCM_H_
int ti_prcm_write_4(device_t dev, bus_addr_t addr, uint32_t val);
int ti_prcm_read_4(device_t dev, bus_addr_t addr, uint32_t *val);
int ti_prcm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set);
void ti_prcm_device_lock(device_t dev);
void ti_prcm_device_unlock(device_t dev);
typedef enum {
INVALID_CLK_IDENT = 0,
/* System clocks, typically you can only call ti_prcm_clk_get_source_freq()
* on these clocks as they are enabled by default.
*/
SYS_CLK = 1,
/* The MPU (ARM) core clock */
MPU_CLK = 20,
/* MMC modules */
MMC1_CLK = 100,
MMC2_CLK,
MMC3_CLK,
MMC4_CLK,
MMC5_CLK,
MMC6_CLK,
/* I2C modules */
I2C1_CLK = 200,
I2C2_CLK,
I2C3_CLK,
I2C4_CLK,
I2C5_CLK,
/* USB module(s) */
USBTLL_CLK = 300,
USBHSHOST_CLK,
USBFSHOST_CLK,
USBP1_PHY_CLK,
USBP2_PHY_CLK,
USBP1_UTMI_CLK,
USBP2_UTMI_CLK,
USBP1_HSIC_CLK,
USBP2_HSIC_CLK,
/* UART modules */
UART1_CLK = 400,
UART2_CLK,
UART3_CLK,
UART4_CLK,
UART5_CLK,
UART6_CLK,
UART7_CLK,
UART8_CLK,
UART9_CLK,
/* General purpose timer modules */
TIMER1_CLK = 500,
TIMER2_CLK,
TIMER3_CLK,
TIMER4_CLK,
TIMER5_CLK,
TIMER6_CLK,
TIMER7_CLK,
TIMER8_CLK,
TIMER9_CLK,
TIMER10_CLK,
TIMER11_CLK,
TIMER12_CLK,
/* McBSP module(s) */
MCBSP1_CLK = 600,
MCBSP2_CLK,
MCBSP3_CLK,
MCBSP4_CLK,
MCBSP5_CLK,
/* General purpose I/O modules */
GPIO1_CLK = 700,
GPIO2_CLK,
GPIO3_CLK,
GPIO4_CLK,
GPIO5_CLK,
GPIO6_CLK,
GPIO7_CLK,
/* sDMA module */
SDMA_CLK = 800,
/* CPSW modules */
CPSW_CLK = 1000,
/* Mentor USB modules */
MUSB0_CLK = 1100,
/* EDMA module */
EDMA_TPCC_CLK = 1200,
EDMA_TPTC0_CLK,
EDMA_TPTC1_CLK,
EDMA_TPTC2_CLK,
/* LCD controller module */
LCDC_CLK = 1300,
/* PWM modules */
PWMSS0_CLK = 1400,
PWMSS1_CLK,
PWMSS2_CLK,
/* Mailbox modules */
MAILBOX0_CLK = 1500,
/* Spinlock modules */
SPINLOCK0_CLK = 1600,
PRUSS_CLK = 1700,
TSC_ADC_CLK = 1800,
/* RTC module */
RTC_CLK = 1900,
/* McSPI */
SPI0_CLK = 2000,
SPI1_CLK,
} clk_ident_t;
/*
*
*/
typedef enum {
SYSCLK_CLK, /* System clock */
EXT_CLK,
F32KHZ_CLK, /* 32KHz clock */
F48MHZ_CLK, /* 48MHz clock */
F64MHZ_CLK, /* 64MHz clock */
F96MHZ_CLK, /* 96MHz clock */
} clk_src_t;
struct ti_clock_dev {
/* The profile of the timer */
clk_ident_t id;
/* A bunch of callbacks associated with the clock device */
int (*clk_activate)(struct ti_clock_dev *clkdev);
int (*clk_deactivate)(struct ti_clock_dev *clkdev);
int (*clk_set_source)(struct ti_clock_dev *clkdev,
clk_src_t clksrc);
int (*clk_accessible)(struct ti_clock_dev *clkdev);
int (*clk_set_source_freq)(struct ti_clock_dev *clkdev,
unsigned int freq);
int (*clk_get_source_freq)(struct ti_clock_dev *clkdev,
unsigned int *freq);
};
int ti_prcm_clk_valid(clk_ident_t clk);
int ti_prcm_clk_enable(clk_ident_t clk);
int ti_prcm_clk_disable(clk_ident_t clk);
int ti_prcm_clk_accessible(clk_ident_t clk);
int ti_prcm_clk_disable_autoidle(clk_ident_t clk);
int ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc);
int ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq);
int ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq);
void ti_prcm_reset(void);
#endif /* _TI_PRCM_H_ */
#endif

210
sys/arm/ti/ti_prm.c Normal file
View File

@ -0,0 +1,210 @@
/*-
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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$
*/
/*
* Power management - simple driver to handle reset and give access to
* memory space region for other drivers through prcm driver.
* Documentation/devicetree/binding/arm/omap/prm-inst.txt
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_prm.h>
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
/* relative to prcm address range */
#define TI_PRM_PER_RSTCTRL 0xC00
struct ti_prm_softc {
device_t dev;
uint8_t type;
bool has_reset;
};
/* Device */
#define TI_OMAP_PRM_INST 10
#define TI_AM3_PRM_INST 5
#define TI_AM4_PRM_INST 4
#define TI_OMAP4_PRM_INST 3
#define TI_OMAP5_PRM_INST 2
#define TI_DRA7_PRM_INST 1
#define TI_END 0
static struct ofw_compat_data compat_data[] = {
{ "ti,am3-prm-inst", TI_AM3_PRM_INST },
{ "ti,am4-prm-inst", TI_AM4_PRM_INST },
{ "ti,omap4-prm-inst", TI_OMAP4_PRM_INST },
{ "ti,omap5-prm-inst", TI_OMAP5_PRM_INST },
{ "ti,dra7-prm-inst", TI_DRA7_PRM_INST },
{ NULL, TI_END }
};
static struct ofw_compat_data required_data[] = {
{ "ti,omap-prm-inst", TI_OMAP_PRM_INST },
{ NULL, TI_END }
};
/* device interface */
static int
ti_prm_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_search_compatible(dev, required_data)->ocd_data == 0)
return (ENXIO);
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
return (ENXIO);
device_set_desc(dev, "TI OMAP Power Management");
return(BUS_PROBE_DEFAULT);
}
static int
ti_prm_attach(device_t dev)
{
struct ti_prm_softc *sc;
phandle_t node;
sc = device_get_softc(dev);
sc->dev = dev;
sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
node = ofw_bus_get_node(sc->dev);
if (OF_hasprop(node, "#reset-cells")) {
sc->has_reset = true;
} else
sc->has_reset = false;
/* Make device visible for other drivers */
OF_device_register_xref(OF_xref_from_node(node), sc->dev);
return (0);
}
static int
ti_prm_detach(device_t dev) {
return (EBUSY);
}
int
ti_prm_reset(device_t dev)
{
struct ti_prm_softc *sc;
int err;
sc = device_get_softc(dev);
if (sc->has_reset == false)
return 1;
err = ti_prm_modify_4(dev, TI_PRM_PER_RSTCTRL, 0x2, 0x00);
return (err);
}
int
ti_prm_write_4(device_t dev, bus_addr_t addr, uint32_t val)
{
struct ti_prm_softc *sc;
device_t parent;
parent = device_get_parent(dev);
sc = device_get_softc(dev);
DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val);
ti_prcm_device_lock(parent);
ti_prcm_write_4(parent, addr, val);
ti_prcm_device_unlock(parent);
return (0);
}
int
ti_prm_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
{
struct ti_prm_softc *sc;
device_t parent;
parent = device_get_parent(dev);
sc = device_get_softc(dev);
ti_prcm_device_lock(parent);
ti_prcm_read_4(parent, addr, val);
ti_prcm_device_unlock(parent);
DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val);
return (0);
}
int
ti_prm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
{
struct ti_prm_softc *sc;
device_t parent;
parent = device_get_parent(dev);
sc = device_get_softc(dev);
ti_prcm_device_lock(parent);
ti_prcm_modify_4(parent, addr, clr, set);
ti_prcm_device_unlock(parent);
DPRINTF(sc->dev, "offset=%lx (clr %x set %x)\n", addr, clr, set);
return (0);
}
static device_method_t ti_prm_methods[] = {
DEVMETHOD(device_probe, ti_prm_probe),
DEVMETHOD(device_attach, ti_prm_attach),
DEVMETHOD(device_detach, ti_prm_detach),
DEVMETHOD_END
};
DEFINE_CLASS_1(ti_prm, ti_prm_driver, ti_prm_methods,
sizeof(struct ti_prm_softc), simplebus_driver);
static devclass_t ti_prm_devclass;
EARLY_DRIVER_MODULE(ti_prm, simplebus, ti_prm_driver,
ti_prm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ti_prm, 1);
MODULE_DEPEND(ti_prm, ti_sysc, 1, 1, 1);

38
sys/arm/ti/ti_prm.h Normal file
View File

@ -0,0 +1,38 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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 __TI_PRM__
#define __TI_PRM__
int ti_prm_reset(device_t dev);
int ti_prm_write_4(device_t dev, bus_addr_t addr, uint32_t val);
int ti_prm_read_4(device_t dev, bus_addr_t addr, uint32_t *val);
int ti_prm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set);
#endif /* __TI_PRM__ */

View File

@ -57,8 +57,11 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_prcm.h>
#include <dev/extres/clk/clk.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/ti_pruss.h>
#include <arm/ti/ti_prm.h>
#ifdef DEBUG
#define DPRINTF(fmt, ...) do { \
@ -161,7 +164,8 @@ static driver_t ti_pruss_driver = {
static devclass_t ti_pruss_devclass;
DRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0);
MODULE_DEPEND(ti_pruss, ti_prcm, 1, 1, 1);
MODULE_DEPEND(ti_pruss, ti_sysc, 1, 1, 1);
MODULE_DEPEND(ti_pruss, ti_prm, 1, 1, 1);
static struct resource_spec ti_pruss_irq_spec[] = {
{ SYS_RES_IRQ, 0, RF_ACTIVE },
@ -515,14 +519,89 @@ static int
ti_pruss_attach(device_t dev)
{
struct ti_pruss_softc *sc;
int rid, i;
int rid, i, err, ncells;
uint32_t reg;
phandle_t node;
clk_t l3_gclk, pruss_ocp_gclk;
phandle_t ti_prm_ref, *cells;
device_t ti_prm_dev;
if (ti_prcm_clk_enable(PRUSS_CLK) != 0) {
device_printf(dev, "could not enable PRUSS clock\n");
rid = 0;
sc = device_get_softc(dev);
node = ofw_bus_get_node(device_get_parent(dev));
if (node <= 0) {
device_printf(dev, "Cant get ofw node\n");
return (ENXIO);
}
sc = device_get_softc(dev);
rid = 0;
/*
* Follow activate pattern from sys/arm/ti/am335x/am335x_prcm.c
* by Damjan Marion
*/
/* Set MODULEMODE to ENABLE(2) */
/* Wait for MODULEMODE to become ENABLE(2) */
if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) {
device_printf(dev, "Could not enable PRUSS clock\n");
return (ENXIO);
}
/* Set CLKTRCTRL to SW_WKUP(2) */
/* Wait for the 200 MHz OCP clock to become active */
/* Wait for the 200 MHz IEP clock to become active */
/* Wait for the 192 MHz UART clock to become active */
/*
* At the moment there is no reference to CM_PER_PRU_ICSS_CLKSTCTRL@140
* in the devicetree. The register reset state are SW_WKUP(2) as default
* so at the moment ignore setting this register.
*/
/* Select L3F as OCP clock */
/* Get the clock and set the parent */
err = clk_get_by_name(dev, "l3_gclk", &l3_gclk);
if (err) {
device_printf(dev, "Cant get l3_gclk err %d\n", err);
return (ENXIO);
}
err = clk_get_by_name(dev, "pruss_ocp_gclk@530", &pruss_ocp_gclk);
if (err) {
device_printf(dev, "Cant get pruss_ocp_gclk@530 err %d\n", err);
return (ENXIO);
}
err = clk_set_parent_by_clk(pruss_ocp_gclk, l3_gclk);
if (err) {
device_printf(dev,
"Cant set pruss_ocp_gclk parent to l3_gclk err %d\n", err);
return (ENXIO);
}
/* Clear the RESET bit */
/* Find the ti_prm */
/* #reset-cells should not been used in this way but... */
err = ofw_bus_parse_xref_list_alloc(node, "resets", "#reset-cells", 0,
&ti_prm_ref, &ncells, &cells);
OF_prop_free(cells);
if (err) {
device_printf(dev,
"Cant fetch \"resets\" reference %x\n", err);
return (ENXIO);
}
ti_prm_dev = OF_device_from_xref(ti_prm_ref);
if (ti_prm_dev == NULL) {
device_printf(dev, "Cant get device from \"resets\"\n");
return (ENXIO);
}
err = ti_prm_reset(ti_prm_dev);
if (err) {
device_printf(dev, "ti_prm_reset failed %d\n", err);
return (ENXIO);
}
/* End of clock activation */
mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF);
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
@ -602,6 +681,9 @@ ti_pruss_attach(device_t dev)
}
}
reg = ti_pruss_reg_read(sc,
ti_sysc_get_sysc_address_offset_host(device_get_parent(dev)));
if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV)
device_printf(dev, "AM33xx PRU-ICSS\n");

View File

@ -1,9 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-4-Clause
* Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
*
* Copyright (c) 2010
* Ben Gray <ben.r.gray@gmail.com>.
* All rights reserved.
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -13,169 +11,152 @@
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Ben Gray.
* 4. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
* 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 BEN GRAY 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.
* 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$
*/
/**
* SCM - System Control Module
*
* Hopefully in the end this module will contain a bunch of utility functions
* for configuring and querying the general system control registers, but for
* now it only does pin(pad) multiplexing.
*
* This is different from the GPIO module in that it is used to configure the
* pins between modules not just GPIO input/output.
*
* This file contains the generic top level driver, however it relies on chip
* specific settings and therefore expects an array of ti_scm_padconf structs
* call ti_padconf_devmap to be located somewhere in the kernel.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/* Based on sys/arm/ti/ti_sysc.c */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/fbio.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <vm/pmap.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/fdt/fdt_pinctrl.h>
#include "ti_scm.h"
#include "ti_cpuid.h"
#define TI_AM3_SCM 14
#define TI_AM4_SCM 13
#define TI_DM814_SCRM 12
#define TI_DM816_SCRM 11
#define TI_OMAP2_SCM 10
#define TI_OMAP3_SCM 9
#define TI_OMAP4_SCM_CORE 8
#define TI_OMAP4_SCM_PADCONF_CORE 7
#define TI_OMAP4_SCM_WKUP 6
#define TI_OMAP4_SCM_PADCONF_WKUP 5
#define TI_OMAP5_SCM_CORE 4
#define TI_OMAP5_SCM_PADCONF_CORE 3
#define TI_OMAP5_SCM_WKUP_PAD_CONF 2
#define TI_DRA7_SCM_CORE 1
#define TI_SCM_END 0
static struct resource_spec ti_scm_res_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */
{ -1, 0 }
static struct ofw_compat_data compat_data[] = {
{ "ti,am3-scm", TI_AM3_SCM },
{ "ti,am4-scm", TI_AM4_SCM },
{ "ti,dm814-scrm", TI_DM814_SCRM },
{ "ti,dm816-scrm", TI_DM816_SCRM },
{ "ti,omap2-scm", TI_OMAP2_SCM },
{ "ti,omap3-scm", TI_OMAP3_SCM },
{ "ti,omap4-scm-core", TI_OMAP4_SCM_CORE },
{ "ti,omap4-scm-padconf-core", TI_OMAP4_SCM_PADCONF_CORE },
{ "ti,omap4-scm-wkup", TI_OMAP4_SCM_WKUP },
{ "ti,omap4-scm-padconf-wkup", TI_OMAP4_SCM_PADCONF_WKUP },
{ "ti,omap5-scm-core", TI_OMAP5_SCM_CORE },
{ "ti,omap5-scm-padconf-core", TI_OMAP5_SCM_PADCONF_CORE },
{ "ti,omap5-scm-wkup-pad-conf", TI_OMAP5_SCM_WKUP_PAD_CONF },
{ "ti,dra7-scm-core", TI_DRA7_SCM_CORE },
{ NULL, TI_SCM_END }
};
static struct ti_scm_softc *ti_scm_sc;
struct ti_scm_softc {
struct simplebus_softc sc;
device_t dev;
};
#define ti_scm_read_4(sc, reg) \
bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
#define ti_scm_write_4(sc, reg, val) \
bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
static int ti_scm_probe(device_t dev);
static int ti_scm_attach(device_t dev);
static int ti_scm_detach(device_t dev);
/*
* Device part of OMAP SCM driver
*/
static int
ti_scm_probe(device_t dev)
{
if (!ti_soc_is_supported())
return (ENXIO);
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "syscon"))
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
return (ENXIO);
if (ti_scm_sc) {
return (EEXIST);
}
device_set_desc(dev, "TI OMAP Control Module");
device_set_desc(dev, "TI Control Module");
return (BUS_PROBE_DEFAULT);
}
/**
* ti_scm_attach - attaches the timer to the simplebus
* @dev: new device
*
* Reserves memory and interrupt resources, stores the softc structure
* globally and registers both the timecount and eventtimer objects.
*
* RETURNS
* Zero on success or ENXIO if an error occuried.
*/
static int
ti_scm_attach(device_t dev)
{
struct ti_scm_softc *sc = device_get_softc(dev);
struct ti_scm_softc *sc;
device_t cdev;
phandle_t node, child;
sc->sc_dev = dev;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(dev);
if (bus_alloc_resources(dev, ti_scm_res_spec, sc->sc_res)) {
device_printf(dev, "could not allocate resources\n");
simplebus_init(dev, node);
if (simplebus_fill_ranges(node, &sc->sc) < 0) {
device_printf(dev, "could not get ranges\n");
return (ENXIO);
}
/* Global timer interface */
sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
ti_scm_sc = sc;
/* Attach platform extensions, if any. */
bus_generic_probe(dev);
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));
}
int
ti_scm_reg_read_4(uint32_t reg, uint32_t *val)
static int
ti_scm_detach(device_t dev)
{
if (!ti_scm_sc)
return (ENXIO);
*val = ti_scm_read_4(ti_scm_sc, reg);
return (0);
return (EBUSY);
}
int
ti_scm_reg_write_4(uint32_t reg, uint32_t val)
{
if (!ti_scm_sc)
return (ENXIO);
ti_scm_write_4(ti_scm_sc, reg, val);
return (0);
}
static device_method_t ti_scm_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_scm_probe),
DEVMETHOD(device_attach, ti_scm_attach),
DEVMETHOD(device_detach, ti_scm_detach),
{ 0, 0 }
DEVMETHOD_END
};
static driver_t ti_scm_driver = {
"ti_scm",
ti_scm_methods,
sizeof(struct ti_scm_softc),
};
DEFINE_CLASS_1(ti_scm, ti_scm_driver, ti_scm_methods,
sizeof(struct ti_scm_softc), simplebus_driver);
static devclass_t ti_scm_devclass;
EARLY_DRIVER_MODULE(ti_scm, simplebus, ti_scm_driver, ti_scm_devclass, 0, 0,
BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
EARLY_DRIVER_MODULE(ti_scm, simplebus, ti_scm_driver,
ti_scm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);
MODULE_VERSION(ti_scm, 1);
MODULE_DEPEND(ti_scm, ti_sysc, 1, 1, 1);

294
sys/arm/ti/ti_scm_syscon.c Normal file
View File

@ -0,0 +1,294 @@
/*-
* Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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$
*/
/* Based on sys/arm/ti/ti_sysc.c */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "syscon_if.h"
#include <dev/extres/syscon/syscon.h>
#include "clkdev_if.h"
#if 0
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
MALLOC_DECLARE(M_SYSCON);
struct ti_scm_syscon_softc {
struct simplebus_softc sc_simplebus;
device_t dev;
struct syscon * syscon;
struct resource * res[1];
bus_space_tag_t bst;
bus_space_handle_t bsh;
struct mtx mtx;
};
static struct resource_spec ti_scm_syscon_res_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE },
{ -1, 0 }
};
/* Device */
static struct ofw_compat_data compat_data[] = {
{ "syscon", 1 },
{ NULL, 0 }
};
/* --- dev/extres/syscon syscon_method_t interface --- */
static int
ti_scm_syscon_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val)
{
struct ti_scm_syscon_softc *sc;
sc = device_get_softc(syscon->pdev);
DPRINTF(sc->dev, "offset=%lx write %x\n", offset, val);
mtx_lock(&sc->mtx);
bus_space_write_4(sc->bst, sc->bsh, offset, val);
mtx_unlock(&sc->mtx);
return (0);
}
static uint32_t
ti_scm_syscon_read_4(struct syscon *syscon, bus_size_t offset)
{
struct ti_scm_syscon_softc *sc;
uint32_t val;
sc = device_get_softc(syscon->pdev);
mtx_lock(&sc->mtx);
val = bus_space_read_4(sc->bst, sc->bsh, offset);
mtx_unlock(&sc->mtx);
DPRINTF(sc->dev, "offset=%lx Read %x\n", offset, val);
return (val);
}
static int
ti_scm_syscon_modify_4(struct syscon *syscon, bus_size_t offset, uint32_t clr, uint32_t set)
{
struct ti_scm_syscon_softc *sc;
uint32_t reg;
sc = device_get_softc(syscon->pdev);
mtx_lock(&sc->mtx);
reg = bus_space_read_4(sc->bst, sc->bsh, offset);
reg &= ~clr;
reg |= set;
bus_space_write_4(sc->bst, sc->bsh, offset, reg);
mtx_unlock(&sc->mtx);
DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", offset, reg, clr, set);
return (0);
}
static syscon_method_t ti_scm_syscon_reg_methods[] = {
SYSCONMETHOD(syscon_read_4, ti_scm_syscon_read_4),
SYSCONMETHOD(syscon_write_4, ti_scm_syscon_write_4),
SYSCONMETHOD(syscon_modify_4, ti_scm_syscon_modify_4),
SYSCONMETHOD_END
};
DEFINE_CLASS_1(ti_scm_syscon_reg, ti_scm_syscon_reg_class, ti_scm_syscon_reg_methods,
0, syscon_class);
/* device interface */
static int
ti_scm_syscon_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, "TI OMAP Control Module Syscon");
return(BUS_PROBE_DEFAULT);
}
static int
ti_scm_syscon_attach(device_t dev)
{
struct ti_scm_syscon_softc *sc;
phandle_t node, child;
int err;
sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, ti_scm_syscon_res_spec, sc->res)) {
device_printf(sc->dev, "Cant allocate resources\n");
return (ENXIO);
}
sc->dev = dev;
sc->bst = rman_get_bustag(sc->res[0]);
sc->bsh = rman_get_bushandle(sc->res[0]);
mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
node = ofw_bus_get_node(sc->dev);
/* dev/extres/syscon interface */
sc->syscon = syscon_create_ofw_node(dev, &ti_scm_syscon_reg_class, node);
if (sc->syscon == NULL) {
device_printf(dev, "Failed to create/register syscon\n");
return (ENXIO);
}
simplebus_init(sc->dev, node);
err = bus_generic_probe(sc->dev);
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
simplebus_add_device(sc->dev, child, 0, NULL, -1, NULL);
}
return (bus_generic_attach(sc->dev));
}
/* syscon interface */
static int
ti_scm_syscon_get_handle(device_t dev, struct syscon **syscon)
{
struct ti_scm_syscon_softc *sc;
sc = device_get_softc(dev);
*syscon = sc->syscon;
if (*syscon == NULL)
return (ENODEV);
return (0);
}
/* clkdev interface */
static int
ti_scm_syscon_clk_write_4(device_t dev, bus_addr_t addr, uint32_t val)
{
struct ti_scm_syscon_softc *sc;
sc = device_get_softc(dev);
DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val);
bus_space_write_4(sc->bst, sc->bsh, addr, val);
return (0);
}
static int
ti_scm_syscon_clk_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
{
struct ti_scm_syscon_softc *sc;
sc = device_get_softc(dev);
*val = bus_space_read_4(sc->bst, sc->bsh, addr);
DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val);
return (0);
}
static int
ti_scm_syscon_clk_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
{
struct ti_scm_syscon_softc *sc;
uint32_t reg;
sc = device_get_softc(dev);
reg = bus_space_read_4(sc->bst, sc->bsh, addr);
reg &= ~clr;
reg |= set;
bus_space_write_4(sc->bst, sc->bsh, addr, reg);
DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", addr, reg, clr, set);
return (0);
}
static void
ti_scm_syscon_clk_device_lock(device_t dev)
{
struct ti_scm_syscon_softc *sc;
sc = device_get_softc(dev);
mtx_lock(&sc->mtx);
}
static void
ti_scm_syscon_clk_device_unlock(device_t dev)
{
struct ti_scm_syscon_softc *sc;
sc = device_get_softc(dev);
mtx_unlock(&sc->mtx);
}
static device_method_t ti_scm_syscon_methods[] = {
DEVMETHOD(device_probe, ti_scm_syscon_probe),
DEVMETHOD(device_attach, ti_scm_syscon_attach),
/* syscon interface */
DEVMETHOD(syscon_get_handle, ti_scm_syscon_get_handle),
/* clkdev interface */
DEVMETHOD(clkdev_write_4, ti_scm_syscon_clk_write_4),
DEVMETHOD(clkdev_read_4, ti_scm_syscon_clk_read_4),
DEVMETHOD(clkdev_modify_4, ti_scm_syscon_clk_modify_4),
DEVMETHOD(clkdev_device_lock, ti_scm_syscon_clk_device_lock),
DEVMETHOD(clkdev_device_unlock, ti_scm_syscon_clk_device_unlock),
DEVMETHOD_END
};
DEFINE_CLASS_1(ti_scm_syscon, ti_scm_syscon_driver, ti_scm_syscon_methods,
sizeof(struct ti_scm_syscon_softc), simplebus_driver);
static devclass_t ti_scm_syscon_devclass;
EARLY_DRIVER_MODULE(ti_scm_syscon, simplebus, ti_scm_syscon_driver,
ti_scm_syscon_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ti_scm_syscon, 1);
MODULE_DEPEND(ti_scm_syscon, ti_scm, 1, 1, 1);

View File

@ -44,10 +44,11 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <machine/intr.h>
#include <arm/ti/ti_cpuid.h>
#include <arm/ti/ti_sysc.h>
#include "gpio_if.h"
#include <dev/extres/clk/clk.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
@ -59,10 +60,10 @@ __FBSDID("$FreeBSD$");
#include <dev/sdhci/sdhci_fdt_gpio.h>
#include "sdhci_if.h"
#include <arm/ti/ti_cpuid.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_hwmods.h>
#include "gpio_if.h"
#include <machine/bus.h>
#include <machine/resource.h>
#include <machine/intr.h>
#include "opt_mmccam.h"
@ -73,10 +74,9 @@ struct ti_sdhci_softc {
struct resource * irq_res;
void * intr_cookie;
struct sdhci_slot slot;
clk_ident_t mmchs_clk_id;
uint32_t mmchs_reg_off;
uint32_t sdhci_reg_off;
uint32_t baseclk_hz;
uint64_t baseclk_hz;
uint32_t cmd_and_mode;
uint32_t sdhci_clkdiv;
boolean_t disable_highspeed;
@ -414,24 +414,32 @@ ti_sdhci_detach(device_t dev)
return (EBUSY);
}
static void
static int
ti_sdhci_hw_init(device_t dev)
{
struct ti_sdhci_softc *sc = device_get_softc(dev);
uint32_t regval;
unsigned long timeout;
clk_t mmc_clk;
int err;
/* Enable the controller and interface/functional clocks */
if (ti_prcm_clk_enable(sc->mmchs_clk_id) != 0) {
if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) {
device_printf(dev, "Error: failed to enable MMC clock\n");
return;
return (ENXIO);
}
/* Get the frequency of the source clock */
if (ti_prcm_clk_get_source_freq(sc->mmchs_clk_id,
&sc->baseclk_hz) != 0) {
device_printf(dev, "Error: failed to get source clock freq\n");
return;
/* FIXME: Devicetree dosent have any reference to mmc_clk */
err = clk_get_by_name(dev, "mmc_clk", &mmc_clk);
if (err) {
device_printf(dev, "Can not find mmc_clk\n");
return (ENXIO);
}
err = clk_get_freq(mmc_clk, &sc->baseclk_hz);
if (err) {
device_printf(dev, "Cant get mmc_clk frequency\n");
/* AM335x TRM 8.1.6.8 table 8-24 96MHz @ OPP100 */
sc->baseclk_hz = 96000000;
}
/* Issue a softreset to the controller */
@ -499,6 +507,8 @@ ti_sdhci_hw_init(device_t dev)
/* Set the initial controller configuration. */
ti_mmchs_write_4(sc, MMCHS_CON, MMCHS_CON_DVAL_8_4MS);
return (0);
}
static int
@ -512,16 +522,9 @@ ti_sdhci_attach(device_t dev)
sc->dev = dev;
/*
* Get the MMCHS device id from FDT. If it's not there use the newbus
* unit number (which will work as long as the devices are in order and
* none are skipped in the fdt). Note that this is a property we made
* up and added in freebsd, it doesn't exist in the published bindings.
* Get the MMCHS device id from FDT. Use rev address to identify the unit.
*/
node = ofw_bus_get_node(dev);
sc->mmchs_clk_id = ti_hwmods_get_clock(dev);
if (sc->mmchs_clk_id == INVALID_CLK_IDENT) {
device_printf(dev, "failed to get clock based on hwmods property\n");
}
/*
* The hardware can inherently do dual-voltage (1p8v, 3p0v) on the first
@ -531,7 +534,8 @@ ti_sdhci_attach(device_t dev)
* that it can set the right values in the CAPA register.
*/
sc->slot.host.caps |= MMC_OCR_LOW_VOLTAGE;
if (sc->mmchs_clk_id == MMC1_CLK || OF_hasprop(node, "ti,dual-volt")) {
if (OF_hasprop(node, "ti,dual-volt")) {
sc->slot.host.caps |= MMC_OCR_290_300 | MMC_OCR_300_310;
}
@ -603,7 +607,11 @@ ti_sdhci_attach(device_t dev)
sc->disable_readonly = true;
/* Initialise the MMCHS hardware. */
ti_sdhci_hw_init(dev);
err = ti_sdhci_hw_init(dev);
if (err != 0) {
/* err should already contain ENXIO from ti_sdhci_hw_init() */
goto fail;
}
/*
* The capabilities register can only express base clock frequencies in
@ -754,6 +762,7 @@ static driver_t ti_sdhci_driver = {
DRIVER_MODULE(sdhci_ti, simplebus, ti_sdhci_driver, ti_sdhci_devclass, NULL,
NULL);
MODULE_DEPEND(sdhci_ti, ti_sysc, 1, 1, 1);
SDHCI_DEPEND(sdhci_ti);
#ifndef MMCCAM

View File

@ -51,7 +51,7 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus_subr.h>
#include <arm/ti/ti_cpuid.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/ti_sdma.h>
#include <arm/ti/ti_sdmareg.h>
@ -79,7 +79,7 @@ __FBSDID("$FreeBSD$");
*/
struct ti_sdma_channel {
/*
/*
* The configuration registers for the given channel, these are modified
* by the set functions and only written to the actual registers when a
* transaction is started.
@ -109,7 +109,7 @@ struct ti_sdma_softc {
struct resource* sc_irq_res;
struct resource* sc_mem_res;
/*
/*
* I guess in theory we should have a mutex per DMA channel for register
* modifications. But since we know we are never going to be run on a SMP
* system, we can use just the single lock for all channels.
@ -119,7 +119,7 @@ struct ti_sdma_softc {
/* Stores the H/W revision read from the registers */
uint32_t sc_hw_rev;
/*
/*
* Bits in the sc_active_channels data field indicate if the channel has
* been activated.
*/
@ -266,7 +266,7 @@ ti_sdma_intr(void *arg)
if (csr & DMA4_CSR_TRANS_ERR) {
device_printf(sc->sc_dev, "Transaction error event on "
"channel %u\n", ch);
/*
/*
* Apparently according to linux code, there is an errata
* that says the channel is not disabled upon this error.
* They explicitly disable the channel here .. since I
@ -1175,10 +1175,11 @@ ti_sdma_attach(device_t dev)
panic("%s: Cannot map registers", device_get_name(dev));
/* Enable the interface and functional clocks */
ti_prcm_clk_enable(SDMA_CLK);
ti_sysc_clock_enable(device_get_parent(dev));
/* Read the sDMA revision register and sanity check it's known */
sc->sc_hw_rev = ti_sdma_read_4(sc, DMA4_REVISION);
sc->sc_hw_rev = ti_sdma_read_4(sc,
ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
device_printf(dev, "sDMA revision %08x\n", sc->sc_hw_rev);
if (!ti_sdma_is_omap4_rev(sc) && !ti_sdma_is_omap3_rev(sc)) {
@ -1213,7 +1214,7 @@ ti_sdma_attach(device_t dev)
}
}
/*
/*
* Install interrupt handlers for the for possible interrupts. Any channel
* can trip one of the four IRQs
*/
@ -1248,4 +1249,4 @@ static driver_t ti_sdma_driver = {
static devclass_t ti_sdma_devclass;
DRIVER_MODULE(ti_sdma, simplebus, ti_sdma_driver, ti_sdma_devclass, 0, 0);
MODULE_DEPEND(ti_sdma, ti_prcm, 1, 1, 1);
MODULE_DEPEND(ti_sdma, ti_sysc, 1, 1, 1);

View File

@ -48,8 +48,7 @@ __FBSDID("$FreeBSD$");
#include <dev/spibus/spi.h>
#include <dev/spibus/spibusvar.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_hwmods.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/ti_spireg.h>
#include <arm/ti/ti_spivar.h>
@ -166,28 +165,15 @@ ti_spi_probe(device_t dev)
static int
ti_spi_attach(device_t dev)
{
int clk_id, err, i, rid, timeout;
int err, i, rid, timeout;
struct ti_spi_softc *sc;
uint32_t rev;
sc = device_get_softc(dev);
sc->sc_dev = dev;
/*
* Get the MMCHS device id from FDT. If it's not there use the newbus
* unit number (which will work as long as the devices are in order and
* none are skipped in the fdt). Note that this is a property we made
* up and added in freebsd, it doesn't exist in the published bindings.
*/
clk_id = ti_hwmods_get_clock(dev);
if (clk_id == INVALID_CLK_IDENT) {
device_printf(dev,
"failed to get clock based on hwmods property\n");
return (EINVAL);
}
/* Activate the McSPI module. */
err = ti_prcm_clk_enable(clk_id);
err = ti_sysc_clock_enable(device_get_parent(dev));
if (err) {
device_printf(dev, "Error: failed to activate source clock\n");
return (err);
@ -245,7 +231,8 @@ ti_spi_attach(device_t dev)
}
/* Print the McSPI module revision. */
rev = TI_SPI_READ(sc, MCSPI_REVISION);
rev = TI_SPI_READ(sc,
ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
device_printf(dev,
"scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n",
(rev >> MCSPI_REVISION_SCHEME_SHIFT) & MCSPI_REVISION_SCHEME_MSK,
@ -592,3 +579,4 @@ static driver_t ti_spi_driver = {
};
DRIVER_MODULE(ti_spi, simplebus, ti_spi_driver, ti_spi_devclass, 0, 0);
MODULE_DEPEND(ti_spi, ti_sysc, 1, 1, 1);

View File

@ -1,6 +1,8 @@
/*-
* Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.org>
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -34,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/fbio.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/rman.h>
#include <sys/resource.h>
#include <machine/bus.h>
@ -47,20 +50,401 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
static struct ofw_compat_data compat_data[] = {
{ "ti,sysc", 1 },
{ NULL, 0 }
};
#include <dev/extres/clk/clk.h>
struct ti_sysc_softc {
struct simplebus_softc sc;
device_t dev;
};
#include <arm/ti/ti_sysc.h>
#include <arm/ti/clk/clock_common.h>
#define DEBUG_SYSC 0
#if DEBUG_SYSC
#define DPRINTF(dev, msg...) device_printf(dev, msg)
#else
#define DPRINTF(dev, msg...)
#endif
/* Documentation/devicetree/bindings/bus/ti-sysc.txt
*
* Documentation/devicetree/clock/clock-bindings.txt
* Defines phandle + optional pair
* Documentation/devicetree/clock/ti-clkctl.txt
*/
static int ti_sysc_probe(device_t dev);
static int ti_sysc_attach(device_t dev);
static int ti_sysc_detach(device_t dev);
#define TI_SYSC_DRA7_MCAN 15
#define TI_SYSC_USB_HOST_FS 14
#define TI_SYSC_DRA7_MCASP 13
#define TI_SYSC_MCASP 12
#define TI_SYSC_OMAP_AES 11
#define TI_SYSC_OMAP3_SHAM 10
#define TI_SYSC_OMAP4_SR 9
#define TI_SYSC_OMAP3630_SR 8
#define TI_SYSC_OMAP3430_SR 7
#define TI_SYSC_OMAP4_TIMER 6
#define TI_SYSC_OMAP2_TIMER 5
/* Above needs special workarounds */
#define TI_SYSC_OMAP4_SIMPLE 4
#define TI_SYSC_OMAP4 3
#define TI_SYSC_OMAP2 2
#define TI_SYSC 1
#define TI_SYSC_END 0
static struct ofw_compat_data compat_data[] = {
{ "ti,sysc-dra7-mcan", TI_SYSC_DRA7_MCAN },
{ "ti,sysc-usb-host-fs", TI_SYSC_USB_HOST_FS },
{ "ti,sysc-dra7-mcasp", TI_SYSC_DRA7_MCASP },
{ "ti,sysc-mcasp", TI_SYSC_MCASP },
{ "ti,sysc-omap-aes", TI_SYSC_OMAP_AES },
{ "ti,sysc-omap3-sham", TI_SYSC_OMAP3_SHAM },
{ "ti,sysc-omap4-sr", TI_SYSC_OMAP4_SR },
{ "ti,sysc-omap3630-sr", TI_SYSC_OMAP3630_SR },
{ "ti,sysc-omap3430-sr", TI_SYSC_OMAP3430_SR },
{ "ti,sysc-omap4-timer", TI_SYSC_OMAP4_TIMER },
{ "ti,sysc-omap2-timer", TI_SYSC_OMAP2_TIMER },
/* Above needs special workarounds */
{ "ti,sysc-omap4-simple", TI_SYSC_OMAP4_SIMPLE },
{ "ti,sysc-omap4", TI_SYSC_OMAP4 },
{ "ti,sysc-omap2", TI_SYSC_OMAP2 },
{ "ti,sysc", TI_SYSC },
{ NULL, TI_SYSC_END }
};
/* reg-names can be "rev", "sysc" and "syss" */
static const char * reg_names[] = { "rev", "sysc", "syss" };
#define REG_REV 0
#define REG_SYSC 1
#define REG_SYSS 2
#define REG_MAX 3
/* master idle / slave idle mode defined in 8.1.3.2.1 / 8.1.3.2.2 */
#include <gnu/dts/include/dt-bindings/bus/ti-sysc.h>
#define SYSC_IDLE_MAX 4
struct sysc_reg {
uint64_t address;
uint64_t size;
};
struct clk_list {
TAILQ_ENTRY(clk_list) next;
clk_t clk;
};
struct ti_sysc_softc {
struct simplebus_softc sc;
bool attach_done;
device_t dev;
int device_type;
struct sysc_reg reg[REG_MAX];
/* Offset from host base address */
uint64_t offset_reg[REG_MAX];
uint32_t ti_sysc_mask;
int32_t ti_sysc_midle[SYSC_IDLE_MAX];
int32_t ti_sysc_sidle[SYSC_IDLE_MAX];
uint32_t ti_sysc_delay_us;
uint32_t ti_syss_mask;
int num_clocks;
TAILQ_HEAD(, clk_list) clk_list;
/* deprecated ti_hwmods */
bool ti_no_reset_on_init;
bool ti_no_idle_on_init;
bool ti_no_idle;
};
/*
* All sysc seems to have a reg["rev"] register.
* Lets use that for identification of which module the driver are connected to.
*/
uint64_t
ti_sysc_get_rev_address(device_t dev) {
struct ti_sysc_softc *sc = device_get_softc(dev);
return (sc->reg[REG_REV].address);
}
uint64_t
ti_sysc_get_rev_address_offset_host(device_t dev) {
struct ti_sysc_softc *sc = device_get_softc(dev);
return (sc->offset_reg[REG_REV]);
}
uint64_t
ti_sysc_get_sysc_address(device_t dev) {
struct ti_sysc_softc *sc = device_get_softc(dev);
return (sc->reg[REG_SYSC].address);
}
uint64_t
ti_sysc_get_sysc_address_offset_host(device_t dev) {
struct ti_sysc_softc *sc = device_get_softc(dev);
return (sc->offset_reg[REG_SYSC]);
}
uint64_t
ti_sysc_get_syss_address(device_t dev) {
struct ti_sysc_softc *sc = device_get_softc(dev);
return (sc->reg[REG_SYSS].address);
}
uint64_t
ti_sysc_get_syss_address_offset_host(device_t dev) {
struct ti_sysc_softc *sc = device_get_softc(dev);
return (sc->offset_reg[REG_SYSS]);
}
/*
* Due no memory region is assigned the sysc driver the children needs to
* handle the practical read/writes to the registers.
* Check if sysc has reset bit.
*/
uint32_t
ti_sysc_get_soft_reset_bit(device_t dev) {
struct ti_sysc_softc *sc = device_get_softc(dev);
switch (sc->device_type) {
case TI_SYSC_OMAP4_TIMER:
case TI_SYSC_OMAP4_SIMPLE:
case TI_SYSC_OMAP4:
if (sc->ti_sysc_mask & SYSC_OMAP4_SOFTRESET) {
return (SYSC_OMAP4_SOFTRESET);
}
break;
case TI_SYSC_OMAP2_TIMER:
case TI_SYSC_OMAP2:
case TI_SYSC:
if (sc->ti_sysc_mask & SYSC_OMAP2_SOFTRESET) {
return (SYSC_OMAP2_SOFTRESET);
}
break;
default:
break;
}
return (0);
}
int
ti_sysc_clock_enable(device_t dev) {
struct clk_list *clkp, *clkp_tmp;
struct ti_sysc_softc *sc = device_get_softc(dev);
int err;
TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) {
err = clk_enable(clkp->clk);
if (err) {
DPRINTF(sc->dev, "clk_enable %s failed %d\n",
clk_get_name(clkp->clk), err);
break;
}
}
return (err);
}
int
ti_sysc_clock_disable(device_t dev) {
struct clk_list *clkp, *clkp_tmp;
struct ti_sysc_softc *sc = device_get_softc(dev);
int err = 0;
TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) {
err = clk_disable(clkp->clk);
if (err) {
DPRINTF(sc->dev, "clk_enable %s failed %d\n",
clk_get_name(clkp->clk), err);
break;
}
}
return (err);
}
static int
parse_regfields(struct ti_sysc_softc *sc) {
phandle_t node;
uint32_t parent_address_cells;
uint32_t parent_size_cells;
cell_t *reg;
ssize_t nreg;
int err, k, reg_i, prop_idx;
uint32_t idx;
node = ofw_bus_get_node(sc->dev);
/* Get parents address and size properties */
err = OF_searchencprop(OF_parent(node), "#address-cells",
&parent_address_cells, sizeof(parent_address_cells));
if (err == -1)
return (ENXIO);
if (!(parent_address_cells == 1 || parent_address_cells == 2)) {
DPRINTF(sc->dev, "Expect parent #address-cells=[1||2]\n");
return (ENXIO);
}
err = OF_searchencprop(OF_parent(node), "#size-cells",
&parent_size_cells, sizeof(parent_size_cells));
if (err == -1)
return (ENXIO);
if (!(parent_size_cells == 1 || parent_size_cells == 2)) {
DPRINTF(sc->dev, "Expect parent #size-cells = [1||2]\n");
return (ENXIO);
}
/* Grab the content of reg properties */
nreg = OF_getproplen(node, "reg");
reg = malloc(nreg, M_DEVBUF, M_WAITOK);
OF_getencprop(node, "reg", reg, nreg);
/* Make sure address & size are 0 */
for (idx = 0; idx < REG_MAX; idx++) {
sc->reg[idx].address = 0;
sc->reg[idx].size = 0;
}
/* Loop through reg-names and figure out which reg-name corresponds to
* index populate the values into the reg array.
*/
for (idx = 0, reg_i = 0; idx < REG_MAX && reg_i < nreg; idx++) {
err = ofw_bus_find_string_index(node, "reg-names",
reg_names[idx], &prop_idx);
if (err != 0)
continue;
for (k = 0; k < parent_address_cells; k++) {
sc->reg[prop_idx].address <<= 32;
sc->reg[prop_idx].address |= reg[reg_i++];
}
for (k = 0; k < parent_size_cells; k++) {
sc->reg[prop_idx].size <<= 32;
sc->reg[prop_idx].size |= reg[reg_i++];
}
if (sc->sc.nranges == 0)
sc->offset_reg[prop_idx] = sc->reg[prop_idx].address;
else
sc->offset_reg[prop_idx] = sc->reg[prop_idx].address -
sc->sc.ranges[REG_REV].host;
DPRINTF(sc->dev, "reg[%s] adress %#jx size %#jx\n",
reg_names[idx],
sc->reg[prop_idx].address,
sc->reg[prop_idx].size);
}
free(reg, M_DEVBUF);
return (0);
}
static void
parse_idle(struct ti_sysc_softc *sc, const char *name, uint32_t *idle) {
phandle_t node;
cell_t value[SYSC_IDLE_MAX];
int len, no, i;
node = ofw_bus_get_node(sc->dev);
if (!OF_hasprop(node, name)) {
return;
}
len = OF_getproplen(node, name);
no = len / sizeof(cell_t);
if (no >= SYSC_IDLE_MAX) {
DPRINTF(sc->dev, "Limit %s\n", name);
no = SYSC_IDLE_MAX-1;
len = no * sizeof(cell_t);
}
OF_getencprop(node, name, value, len);
for (i = 0; i < no; i++) {
idle[i] = value[i];
#if DEBUG_SYSC
DPRINTF(sc->dev, "%s[%d] = %d ",
name, i, value[i]);
switch(value[i]) {
case SYSC_IDLE_FORCE:
DPRINTF(sc->dev, "SYSC_IDLE_FORCE\n");
break;
case SYSC_IDLE_NO:
DPRINTF(sc->dev, "SYSC_IDLE_NO\n");
break;
case SYSC_IDLE_SMART:
DPRINTF(sc->dev, "SYSC_IDLE_SMART\n");
break;
case SYSC_IDLE_SMART_WKUP:
DPRINTF(sc->dev, "SYSC_IDLE_SMART_WKUP\n");
break;
}
#endif
}
for ( ; i < SYSC_IDLE_MAX; i++)
idle[i] = -1;
}
static int
ti_sysc_attach_clocks(struct ti_sysc_softc *sc) {
clk_t *clk;
struct clk_list *clkp;
int index, err;
phandle_t cnode;
clk = malloc(sc->num_clocks*sizeof(clk_t), M_DEVBUF, M_WAITOK | M_ZERO);
cnode = ofw_bus_get_node(sc->dev);
/* Check if all clocks can be found */
for (index = 0; index < sc->num_clocks; index++) {
err = clk_get_by_ofw_index(sc->dev, 0, index, &clk[index]);
if (err != 0) {
free(clk, M_DEVBUF);
return (1);
}
}
/* All clocks are found, add to list */
for (index = 0; index < sc->num_clocks; index++) {
clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO);
clkp->clk = clk[index];
TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next);
}
/* Release the clk array */
free(clk, M_DEVBUF);
return (0);
}
static int
ti_sysc_simplebus_attach_child(device_t dev) {
device_t cdev;
phandle_t node, child;
struct ti_sysc_softc *sc = device_get_softc(dev);
node = ofw_bus_get_node(sc->dev);
for (child = OF_child(node); child > 0; child = OF_peer(child)) {
cdev = simplebus_add_device(sc->dev, child, 0, NULL, -1, NULL);
if (cdev != NULL)
device_probe_and_attach(cdev);
}
return (0);
}
/* Device interface */
static int
ti_sysc_probe(device_t dev)
{
@ -71,8 +455,6 @@ ti_sysc_probe(device_t dev)
return (ENXIO);
device_set_desc(dev, "TI SYSC Interconnect");
if (!bootverbose)
device_quiet(dev);
return (BUS_PROBE_DEFAULT);
}
@ -81,48 +463,160 @@ static int
ti_sysc_attach(device_t dev)
{
struct ti_sysc_softc *sc;
device_t cdev;
phandle_t node, child;
phandle_t node;
int err;
cell_t value;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(dev);
sc->device_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
simplebus_init(dev, node);
node = ofw_bus_get_node(sc->dev);
/* ranges - use simplebus */
simplebus_init(sc->dev, node);
if (simplebus_fill_ranges(node, &sc->sc) < 0) {
device_printf(dev, "could not get ranges\n");
DPRINTF(sc->dev, "could not get ranges\n");
return (ENXIO);
}
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);
if (sc->sc.nranges == 0) {
DPRINTF(sc->dev, "nranges == 0\n");
return (ENXIO);
}
return (bus_generic_attach(dev));
/* Required field reg & reg-names - assume at least "rev" exists */
err = parse_regfields(sc);
if (err) {
DPRINTF(sc->dev, "parse_regfields failed %d\n", err);
return (ENXIO);
}
/* Optional */
if (OF_hasprop(node, "ti,sysc-mask")) {
OF_getencprop(node, "ti,sysc-mask", &value, sizeof(cell_t));
sc->ti_sysc_mask = value;
}
if (OF_hasprop(node, "ti,syss-mask")) {
OF_getencprop(node, "ti,syss-mask", &value, sizeof(cell_t));
sc->ti_syss_mask = value;
}
if (OF_hasprop(node, "ti,sysc-delay-us")) {
OF_getencprop(node, "ti,sysc-delay-us", &value, sizeof(cell_t));
sc->ti_sysc_delay_us = value;
}
DPRINTF(sc->dev, "sysc_mask %x syss_mask %x delay_us %x\n",
sc->ti_sysc_mask, sc->ti_syss_mask, sc->ti_sysc_delay_us);
parse_idle(sc, "ti,sysc-midle", sc->ti_sysc_midle);
parse_idle(sc, "ti,sysc-sidle", sc->ti_sysc_sidle);
if (OF_hasprop(node, "ti,no-reset-on-init"))
sc->ti_no_reset_on_init = true;
else
sc->ti_no_reset_on_init = false;
if (OF_hasprop(node, "ti,no-idle-on-init"))
sc->ti_no_idle_on_init = true;
else
sc->ti_no_idle_on_init = false;
if (OF_hasprop(node, "ti,no-idle"))
sc->ti_no_idle = true;
else
sc->ti_no_idle = false;
DPRINTF(sc->dev,
"no-reset-on-init %d, no-idle-on-init %d, no-idle %d\n",
sc->ti_no_reset_on_init,
sc->ti_no_idle_on_init,
sc->ti_no_idle);
if (OF_hasprop(node, "clocks")) {
struct clock_cell_info cell_info;
read_clock_cells(sc->dev, &cell_info);
free(cell_info.clock_cells, M_DEVBUF);
free(cell_info.clock_cells_ncells, M_DEVBUF);
sc->num_clocks = cell_info.num_real_clocks;
TAILQ_INIT(&sc->clk_list);
err = ti_sysc_attach_clocks(sc);
if (err) {
DPRINTF(sc->dev, "Failed to attach clocks\n");
return (bus_generic_attach(sc->dev));
}
}
err = ti_sysc_simplebus_attach_child(sc->dev);
if (err) {
DPRINTF(sc->dev, "ti_sysc_simplebus_attach_child %d\n",
err);
return (err);
}
sc->attach_done = true;
return (bus_generic_attach(sc->dev));
}
static int
ti_sysc_detach(device_t dev)
{
return (EBUSY);
}
/* Bus interface */
static void
ti_sysc_new_pass(device_t dev)
{
struct ti_sysc_softc *sc;
int err;
phandle_t node;
sc = device_get_softc(dev);
if (sc->attach_done) {
bus_generic_new_pass(sc->dev);
return;
}
node = ofw_bus_get_node(sc->dev);
if (OF_hasprop(node, "clocks")) {
err = ti_sysc_attach_clocks(sc);
if (err) {
DPRINTF(sc->dev, "Failed to attach clocks\n");
return;
}
}
err = ti_sysc_simplebus_attach_child(sc->dev);
if (err) {
DPRINTF(sc->dev,
"ti_sysc_simplebus_attach_child failed %d\n", err);
return;
}
sc->attach_done = true;
bus_generic_attach(sc->dev);
}
static device_method_t ti_sysc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_sysc_probe),
DEVMETHOD(device_attach, ti_sysc_attach),
DEVMETHOD(device_detach, ti_sysc_detach),
/* Bus interface */
DEVMETHOD(bus_new_pass, ti_sysc_new_pass),
DEVMETHOD_END
};
DEFINE_CLASS_1(ti_sysc, ti_sysc_driver, ti_sysc_methods,
sizeof(struct ti_sysc_softc), simplebus_driver);
sizeof(struct ti_sysc_softc), simplebus_driver);
static devclass_t ti_sysc_devclass;
EARLY_DRIVER_MODULE(ti_sysc, simplebus, ti_sysc_driver,
ti_sysc_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);
ti_sysc_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST);

43
sys/arm/ti/ti_sysc.h Normal file
View File

@ -0,0 +1,43 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
*
* 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 __TI_SYSC__
#define __TI_SYSC__
uint64_t ti_sysc_get_rev_address(device_t dev);
uint64_t ti_sysc_get_rev_address_offset_host(device_t dev);
uint64_t ti_sysc_get_sysc_address(device_t dev);
uint64_t ti_sysc_get_sysc_address_offset_host(device_t dev);
uint64_t ti_sysc_get_syss_address(device_t dev);
uint64_t ti_sysc_get_syss_address_offset_host(device_t dev);
int ti_sysc_clock_enable(device_t dev);
int ti_sysc_clock_disable(device_t dev);
uint32_t ti_sysc_get_soft_reset_bit(device_t dev);
#endif /* __TI_SYSC__ */

View File

@ -49,7 +49,6 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_wdt.h>
#ifdef DEBUG
@ -93,6 +92,7 @@ static driver_t ti_wdt_driver = {
static devclass_t ti_wdt_devclass;
DRIVER_MODULE(ti_wdt, simplebus, ti_wdt_driver, ti_wdt_devclass, 0, 0);
MODULE_DEPEND(ti_wdt, ti_sysc, 1, 1, 1);
static __inline uint32_t
ti_wdt_reg_read(struct ti_wdt_softc *sc, uint32_t reg)

View File

@ -57,7 +57,6 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/usb/omap_usb.h>
#include <arm/ti/omap4/pandaboard/pandaboard.h>

View File

@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/usb/omap_usb.h>
/*
@ -139,12 +139,14 @@ omap_uhh_init(struct omap_uhh_softc *isc)
int i;
/* Enable Clocks for high speed USBHOST */
ti_prcm_clk_enable(USBHSHOST_CLK);
ti_sysc_clock_enable(device_get_parent(isc->sc_dev));
/* Read the UHH revision */
isc->uhh_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION);
device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->uhh_rev);
/* FIXME */
#if 0
if (isc->uhh_rev == OMAP_UHH_REV2) {
/* For OMAP44xx devices you have to enable the per-port clocks:
* PHY_MODE - External ULPI clock
@ -200,6 +202,7 @@ omap_uhh_init(struct omap_uhh_softc *isc)
device_printf(isc->sc_dev, "unknown port mode %d for port 1\n", isc->port_mode[1]);
}
}
#endif
/* Put UHH in SmartIdle/SmartStandby mode */
reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG);
@ -327,7 +330,7 @@ omap_uhh_fini(struct omap_uhh_softc *isc)
}
/* Disable functional and interface clocks for the TLL and HOST modules */
ti_prcm_clk_disable(USBHSHOST_CLK);
ti_sysc_clock_disable(device_get_parent(isc->sc_dev));
device_printf(isc->sc_dev, "Clock to USB host has been disabled\n");
}

View File

@ -39,7 +39,7 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_sysc.h>
#include <arm/ti/usb/omap_usb.h>
/*
@ -212,7 +212,7 @@ omap_tll_init(struct omap_tll_softc *sc)
int ret = 0;
/* Enable the USB TLL */
ti_prcm_clk_enable(USBTLL_CLK);
ti_sysc_clock_enable(device_get_parent(sc->sc_dev));
/* Perform TLL soft reset, and wait until reset is complete */
omap_tll_write_4(sc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET);
@ -248,7 +248,7 @@ omap_tll_init(struct omap_tll_softc *sc)
err_sys_status:
/* Disable the TLL clocks */
ti_prcm_clk_disable(USBTLL_CLK);
ti_sysc_clock_disable(device_get_parent(sc->sc_dev));
return(ret);
}
@ -273,7 +273,7 @@ omap_tll_disable(struct omap_tll_softc *sc)
}
/* Disable functional and interface clocks for the TLL and HOST modules */
ti_prcm_clk_disable(USBTLL_CLK);
ti_sysc_clock_disable(device_get_parent(sc->sc_dev));
}
static int

View File

@ -39,9 +39,6 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <machine/bus.h>
#include <arm/ti/ti_prcm.h>
#include <arm/ti/ti_hwmods.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
@ -52,6 +49,8 @@ __FBSDID("$FreeBSD$");
#include <dev/uart/uart_bus.h>
#include <dev/uart/uart_dev_ns8250.h>
#include <arm/ti/ti_sysc.h>
#include "uart_if.h"
/*
@ -74,16 +73,8 @@ static int
ti8250_bus_probe(struct uart_softc *sc)
{
int status;
clk_ident_t clkid;
/* Enable clocks for this device. We can't continue if that fails. */
clkid = ti_hwmods_get_clock(sc->sc_dev);
if (clkid == INVALID_CLK_IDENT) {
device_printf(sc->sc_dev,
"failed to get clock based on hwmods\n");
clkid = UART1_CLK + device_get_unit(sc->sc_dev);
}
if ((status = ti_prcm_clk_enable(clkid)) != 0)
if ((status = ti_sysc_clock_enable(device_get_parent(sc->sc_dev))) != 0)
return (status);
/*

View File

@ -755,7 +755,7 @@ _pst= pst
_sbni= sbni
.endif
.if ${MACHINE_CPUARCH} == "arm"
.if ${MACHINE_ARCH} == "armv7"
_cfi= cfi
_cpsw= cpsw
.endif