Add support for Allwinner A64 thermal sensors.
This commit is contained in:
parent
1738b325d0
commit
d69d5ab04f
@ -39,15 +39,21 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/rman.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/module.h>
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/extres/clk/clk.h>
|
||||
#include <dev/extres/hwreset/hwreset.h>
|
||||
|
||||
#include <arm/allwinner/aw_sid.h>
|
||||
|
||||
#define THS_CTRL0 0x00
|
||||
#define THS_CTRL1 0x04
|
||||
#define ADC_CALI_EN (1 << 17)
|
||||
#define THS_CTRL2 0x40
|
||||
#define SENSOR_ACQ1_SHIFT 16
|
||||
#define SENSOR2_EN (1 << 2)
|
||||
@ -55,8 +61,16 @@ __FBSDID("$FreeBSD$");
|
||||
#define SENSOR0_EN (1 << 0)
|
||||
#define THS_INTC 0x44
|
||||
#define THS_INTS 0x48
|
||||
#define THS2_DATA_IRQ_STS (1 << 10)
|
||||
#define THS1_DATA_IRQ_STS (1 << 9)
|
||||
#define THS0_DATA_IRQ_STS (1 << 8)
|
||||
#define SHUT_INT2_STS (1 << 6)
|
||||
#define SHUT_INT1_STS (1 << 5)
|
||||
#define SHUT_INT0_STS (1 << 4)
|
||||
#define ALARM_INT2_STS (1 << 2)
|
||||
#define ALARM_INT1_STS (1 << 1)
|
||||
#define ALARM_INT0_STS (1 << 0)
|
||||
#define THS_FILTER 0x70
|
||||
#define FILTER_EN (1 << 2)
|
||||
#define THS_CALIB0 0x74
|
||||
#define THS_CALIB1 0x78
|
||||
#define THS_DATA0 0x80
|
||||
@ -64,38 +78,93 @@ __FBSDID("$FreeBSD$");
|
||||
#define THS_DATA2 0x88
|
||||
#define DATA_MASK 0xfff
|
||||
|
||||
#define TEMP_BASE 2719
|
||||
#define TEMP_MUL 1000
|
||||
#define TEMP_DIV 14186
|
||||
#define TEMP_TO_K 273
|
||||
#define ADC_ACQUIRE_TIME (24 - 1)
|
||||
#define A83T_ADC_ACQUIRE_TIME 0x17
|
||||
#define A83T_FILTER 0x4
|
||||
#define A83T_INTC 0x1000
|
||||
#define A83T_TEMP_BASE 2719000
|
||||
#define A83T_TEMP_DIV 14186
|
||||
#define A83T_CLK_RATE 24000000
|
||||
|
||||
#define A64_ADC_ACQUIRE_TIME 0x190
|
||||
#define A64_FILTER 0x6
|
||||
#define A64_INTC 0x18000
|
||||
#define A64_TEMP_BASE 2170000
|
||||
#define A64_TEMP_DIV 8560
|
||||
#define A64_CLK_RATE 4000000
|
||||
|
||||
#define TEMP_C_TO_K 273
|
||||
#define SENSOR_ENABLE_ALL (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN)
|
||||
#define SHUT_INT_ALL (SHUT_INT0_STS|SHUT_INT1_STS|SHUT_INT2_STS)
|
||||
|
||||
enum aw_thermal_sensor {
|
||||
THS_SENSOR_CPU_CLUSTER0,
|
||||
THS_SENSOR_CPU_CLUSTER1,
|
||||
THS_SENSOR_GPU,
|
||||
THS_SENSOR_END = -1
|
||||
};
|
||||
#define MAX_SENSORS 3
|
||||
|
||||
struct aw_thermal_sensor_config {
|
||||
enum aw_thermal_sensor sensor;
|
||||
struct aw_thermal_sensor {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
};
|
||||
|
||||
static const struct aw_thermal_sensor_config a83t_sensor_config[] = {
|
||||
{ .sensor = THS_SENSOR_CPU_CLUSTER0,
|
||||
.name = "cluster0", .desc = "CPU cluster 0 temperature" },
|
||||
{ .sensor = THS_SENSOR_CPU_CLUSTER1,
|
||||
.name = "cluster1", .desc = "CPU cluster 1 temperature" },
|
||||
{ .sensor = THS_SENSOR_GPU,
|
||||
.name = "gpu", .desc = "GPU temperature" },
|
||||
{ .sensor = THS_SENSOR_END }
|
||||
struct aw_thermal_config {
|
||||
struct aw_thermal_sensor sensors[MAX_SENSORS];
|
||||
int nsensors;
|
||||
uint64_t clk_rate;
|
||||
uint32_t adc_acquire_time;
|
||||
uint32_t filter;
|
||||
uint32_t intc;
|
||||
uint32_t temp_base;
|
||||
uint32_t temp_div;
|
||||
};
|
||||
|
||||
static const struct aw_thermal_config a83t_config = {
|
||||
.nsensors = 3,
|
||||
.sensors = {
|
||||
[0] = {
|
||||
.name = "cluster0",
|
||||
.desc = "CPU cluster 0 temperature",
|
||||
},
|
||||
[1] = {
|
||||
.name = "cluster1",
|
||||
.desc = "CPU cluster 1 temperature",
|
||||
},
|
||||
[2] = {
|
||||
.name = "gpu",
|
||||
.desc = "GPU temperature",
|
||||
},
|
||||
},
|
||||
.clk_rate = A83T_CLK_RATE,
|
||||
.adc_acquire_time = A83T_ADC_ACQUIRE_TIME,
|
||||
.filter = A83T_FILTER,
|
||||
.intc = A83T_INTC,
|
||||
.temp_base = A83T_TEMP_BASE,
|
||||
.temp_div = A83T_TEMP_DIV,
|
||||
};
|
||||
|
||||
static const struct aw_thermal_config a64_config = {
|
||||
.nsensors = 3,
|
||||
.sensors = {
|
||||
[0] = {
|
||||
.name = "cpu",
|
||||
.desc = "CPU temperature",
|
||||
},
|
||||
[1] = {
|
||||
.name = "gpu1",
|
||||
.desc = "GPU temperature 1",
|
||||
},
|
||||
[2] = {
|
||||
.name = "gpu2",
|
||||
.desc = "GPU temperature 2",
|
||||
},
|
||||
},
|
||||
.clk_rate = A64_CLK_RATE,
|
||||
.adc_acquire_time = A64_ADC_ACQUIRE_TIME,
|
||||
.filter = A64_FILTER,
|
||||
.intc = A64_INTC,
|
||||
.temp_base = A64_TEMP_BASE,
|
||||
.temp_div = A64_TEMP_DIV,
|
||||
};
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "allwinner,sun8i-a83t-ts", (uintptr_t)&a83t_sensor_config },
|
||||
{ "allwinner,sun8i-a83t-ts", (uintptr_t)&a83t_config },
|
||||
{ "allwinner,sun50i-a64-ts", (uintptr_t)&a64_config },
|
||||
{ NULL, (uintptr_t)NULL }
|
||||
};
|
||||
|
||||
@ -103,17 +172,18 @@ static struct ofw_compat_data compat_data[] = {
|
||||
(void *)ofw_bus_search_compatible((d), compat_data)->ocd_data
|
||||
|
||||
struct aw_thermal_softc {
|
||||
struct resource *res;
|
||||
struct aw_thermal_sensor_config *conf;
|
||||
struct resource *res[2];
|
||||
struct aw_thermal_config *conf;
|
||||
};
|
||||
|
||||
static struct resource_spec aw_thermal_spec[] = {
|
||||
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
|
||||
{ SYS_RES_IRQ, 0, RF_ACTIVE },
|
||||
{ -1, 0 }
|
||||
};
|
||||
|
||||
#define RD4(sc, reg) bus_read_4((sc)->res, (reg))
|
||||
#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
|
||||
#define RD4(sc, reg) bus_read_4((sc)->res[0], (reg))
|
||||
#define WR4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
|
||||
|
||||
static int
|
||||
aw_thermal_init(struct aw_thermal_softc *sc)
|
||||
@ -131,44 +201,73 @@ aw_thermal_init(struct aw_thermal_softc *sc)
|
||||
WR4(sc, THS_CALIB1, calib1);
|
||||
|
||||
/* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */
|
||||
WR4(sc, THS_CTRL0, ADC_ACQUIRE_TIME);
|
||||
WR4(sc, THS_CTRL2, (ADC_ACQUIRE_TIME << SENSOR_ACQ1_SHIFT) |
|
||||
SENSOR_ENABLE_ALL);
|
||||
|
||||
/* Disable interrupts */
|
||||
WR4(sc, THS_INTC, 0);
|
||||
WR4(sc, THS_INTS, RD4(sc, THS_INTS));
|
||||
WR4(sc, THS_CTRL1, ADC_CALI_EN);
|
||||
WR4(sc, THS_CTRL0, sc->conf->adc_acquire_time);
|
||||
WR4(sc, THS_CTRL2, sc->conf->adc_acquire_time << SENSOR_ACQ1_SHIFT);
|
||||
|
||||
/* Enable average filter */
|
||||
WR4(sc, THS_FILTER, RD4(sc, THS_FILTER) | FILTER_EN);
|
||||
WR4(sc, THS_FILTER, sc->conf->filter);
|
||||
|
||||
/* Enable interrupts */
|
||||
WR4(sc, THS_INTS, RD4(sc, THS_INTS));
|
||||
WR4(sc, THS_INTC, sc->conf->intc | SHUT_INT_ALL);
|
||||
|
||||
/* Enable sensors */
|
||||
WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thermal_gettemp(uint32_t val)
|
||||
aw_thermal_reg_to_temp(struct aw_thermal_softc *sc, uint32_t val)
|
||||
{
|
||||
int raw;
|
||||
return ((sc->conf->temp_base - val * 1000) / sc->conf->temp_div);
|
||||
}
|
||||
|
||||
raw = val & DATA_MASK;
|
||||
return (((TEMP_BASE - raw) * TEMP_MUL) / TEMP_DIV) + TEMP_TO_K;
|
||||
static int
|
||||
aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = RD4(sc, THS_DATA0 + (sensor * 4));
|
||||
|
||||
return (aw_thermal_reg_to_temp(sc, val) + TEMP_C_TO_K);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thermal_sysctl(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct aw_thermal_softc *sc;
|
||||
enum aw_thermal_sensor sensor;
|
||||
int val;
|
||||
int sensor, val;
|
||||
|
||||
sc = arg1;
|
||||
sensor = arg2;
|
||||
|
||||
val = aw_thermal_gettemp(RD4(sc, THS_DATA0 + (sensor * 4)));
|
||||
val = aw_thermal_gettemp(sc, sensor);
|
||||
|
||||
return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
|
||||
}
|
||||
|
||||
static void
|
||||
aw_thermal_intr(void *arg)
|
||||
{
|
||||
struct aw_thermal_softc *sc;
|
||||
device_t dev;
|
||||
uint32_t ints;
|
||||
|
||||
dev = arg;
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
ints = RD4(sc, THS_INTS);
|
||||
WR4(sc, THS_INTS, ints);
|
||||
|
||||
if ((ints & SHUT_INT_ALL) != 0) {
|
||||
device_printf(dev,
|
||||
"WARNING - current temperature exceeds safe limits\n");
|
||||
shutdown_nice(RB_POWEROFF);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thermal_probe(device_t dev)
|
||||
{
|
||||
@ -186,29 +285,82 @@ static int
|
||||
aw_thermal_attach(device_t dev)
|
||||
{
|
||||
struct aw_thermal_softc *sc;
|
||||
int i;
|
||||
clk_t clk_ahb, clk_ths;
|
||||
hwreset_t rst;
|
||||
int i, error;
|
||||
void *ih;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
clk_ahb = clk_ths = NULL;
|
||||
rst = NULL;
|
||||
ih = NULL;
|
||||
|
||||
sc->conf = THS_CONF(dev);
|
||||
|
||||
if (bus_alloc_resources(dev, aw_thermal_spec, &sc->res) != 0) {
|
||||
if (bus_alloc_resources(dev, aw_thermal_spec, sc->res) != 0) {
|
||||
device_printf(dev, "cannot allocate resources for device\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
if (aw_thermal_init(sc) != 0)
|
||||
return (ENXIO);
|
||||
if (clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb) == 0) {
|
||||
error = clk_enable(clk_ahb);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot enable ahb clock\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (clk_get_by_ofw_name(dev, 0, "ths", &clk_ths) == 0) {
|
||||
error = clk_set_freq(clk_ths, sc->conf->clk_rate, 0);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot set ths clock rate\n");
|
||||
goto fail;
|
||||
}
|
||||
error = clk_enable(clk_ths);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot enable ths clock\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) {
|
||||
error = hwreset_deassert(rst);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot de-assert reset\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; sc->conf[i].sensor != THS_SENSOR_END; i++)
|
||||
error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
|
||||
NULL, aw_thermal_intr, dev, &ih);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot setup interrupt handler\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (aw_thermal_init(sc) != 0)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < sc->conf->nsensors; i++)
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
||||
OID_AUTO, sc->conf[i].name,
|
||||
OID_AUTO, sc->conf->sensors[i].name,
|
||||
CTLTYPE_INT | CTLFLAG_RD,
|
||||
sc, sc->conf[i].sensor, aw_thermal_sysctl, "IK0",
|
||||
sc->conf[i].desc);
|
||||
sc, i, aw_thermal_sysctl, "IK0",
|
||||
sc->conf->sensors[i].desc);
|
||||
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
if (ih != NULL)
|
||||
bus_teardown_intr(dev, sc->res[1], ih);
|
||||
if (rst != NULL)
|
||||
hwreset_release(rst);
|
||||
if (clk_ahb != NULL)
|
||||
clk_release(clk_ahb);
|
||||
if (clk_ths != NULL)
|
||||
clk_release(clk_ths);
|
||||
bus_release_resources(dev, aw_thermal_spec, sc->res);
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
static device_method_t aw_thermal_methods[] = {
|
||||
|
320
sys/arm/allwinner/clk/aw_thsclk.c
Normal file
320
sys/arm/allwinner/clk/aw_thsclk.c
Normal file
@ -0,0 +1,320 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
|
||||
* 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$
|
||||
*/
|
||||
|
||||
/*
|
||||
* Allwinner THS clocks
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
#include <dev/ofw/ofw_subr.h>
|
||||
|
||||
#include <dev/extres/clk/clk_mux.h>
|
||||
#include <dev/extres/clk/clk_gate.h>
|
||||
|
||||
#include "clkdev_if.h"
|
||||
|
||||
#define SCLK_GATING (1 << 31)
|
||||
#define CLK_SRC_SEL (0x3 << 24)
|
||||
#define CLK_SRC_SEL_SHIFT 24
|
||||
#define CLK_SRC_SEL_MAX 1
|
||||
#define CLK_DIV_RATIO (0x3 << 0)
|
||||
#define CLK_DIV_RATIO_SHIFT 0
|
||||
#define CLK_DIV_RATIO_MAX 3
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
{ "allwinner,sun50i-a64-ths-clk", 1 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
struct aw_thsclk_sc {
|
||||
device_t clkdev;
|
||||
bus_addr_t reg;
|
||||
};
|
||||
|
||||
#define THSCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
|
||||
#define THSCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
|
||||
#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
|
||||
#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
|
||||
|
||||
static int
|
||||
aw_thsclk_init(struct clknode *clk, device_t dev)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val, index;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_READ(sc, &val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT;
|
||||
|
||||
clknode_init_parent_idx(clk, index);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thsclk_set_mux(struct clknode *clk, int index)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
if (index < 0 || index >= CLK_SRC_SEL_MAX)
|
||||
return (ERANGE);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_READ(sc, &val);
|
||||
val &= ~CLK_SRC_SEL;
|
||||
val |= (index << CLK_SRC_SEL_SHIFT);
|
||||
THSCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thsclk_set_gate(struct clknode *clk, bool enable)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_READ(sc, &val);
|
||||
if (enable)
|
||||
val |= SCLK_GATING;
|
||||
else
|
||||
val &= ~SCLK_GATING;
|
||||
THSCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thsclk_recalc_freq(struct clknode *clk, uint64_t *freq)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val, div;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_READ(sc, &val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
switch (val & CLK_DIV_RATIO) {
|
||||
case 3:
|
||||
div = 6;
|
||||
break;
|
||||
default:
|
||||
div = 1 << (val & CLK_DIV_RATIO);
|
||||
break;
|
||||
}
|
||||
|
||||
*freq = *freq / div;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thsclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
|
||||
int flags, int *stop)
|
||||
{
|
||||
struct aw_thsclk_sc *sc;
|
||||
uint32_t val, div, n, best_div, best_n;
|
||||
uint64_t cur_freq;
|
||||
int64_t best_diff, cur_diff;
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
best_diff = (int64_t)*fout;
|
||||
best_n = 0;
|
||||
|
||||
for (div = 0; div <= CLK_DIV_RATIO_MAX; div++) {
|
||||
n = (div == 3) ? 6 : (1 << div);
|
||||
cur_freq = fin / n;
|
||||
cur_diff = (int64_t)*fout - cur_freq;
|
||||
if (cur_diff >= 0 && cur_diff < best_diff) {
|
||||
best_diff = cur_diff;
|
||||
best_div = div;
|
||||
best_n = n;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_diff == (int64_t)*fout || best_n == 0)
|
||||
return (ERANGE);
|
||||
|
||||
DEVICE_LOCK(sc);
|
||||
THSCLK_READ(sc, &val);
|
||||
val &= ~CLK_DIV_RATIO;
|
||||
val |= (best_div << CLK_DIV_RATIO_SHIFT);
|
||||
THSCLK_WRITE(sc, val);
|
||||
DEVICE_UNLOCK(sc);
|
||||
|
||||
*fout = fin / best_n;
|
||||
*stop = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static clknode_method_t aw_thsclk_clknode_methods[] = {
|
||||
/* Device interface */
|
||||
CLKNODEMETHOD(clknode_init, aw_thsclk_init),
|
||||
CLKNODEMETHOD(clknode_set_gate, aw_thsclk_set_gate),
|
||||
CLKNODEMETHOD(clknode_set_mux, aw_thsclk_set_mux),
|
||||
CLKNODEMETHOD(clknode_recalc_freq, aw_thsclk_recalc_freq),
|
||||
CLKNODEMETHOD(clknode_set_freq, aw_thsclk_set_freq),
|
||||
CLKNODEMETHOD_END
|
||||
};
|
||||
DEFINE_CLASS_1(aw_thsclk_clknode, aw_thsclk_clknode_class,
|
||||
aw_thsclk_clknode_methods, sizeof(struct aw_thsclk_sc), clknode_class);
|
||||
|
||||
static int
|
||||
aw_thsclk_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, "Allwinner THS Clock");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
aw_thsclk_attach(device_t dev)
|
||||
{
|
||||
struct clknode_init_def def;
|
||||
struct aw_thsclk_sc *sc;
|
||||
struct clkdom *clkdom;
|
||||
struct clknode *clk;
|
||||
clk_t clk_parent;
|
||||
bus_addr_t paddr;
|
||||
bus_size_t psize;
|
||||
phandle_t node;
|
||||
int error, ncells, i;
|
||||
|
||||
node = ofw_bus_get_node(dev);
|
||||
|
||||
if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
|
||||
device_printf(dev, "cannot parse 'reg' property\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
error = ofw_bus_parse_xref_list_get_length(node, "clocks",
|
||||
"#clock-cells", &ncells);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot get clock count\n");
|
||||
return (error);
|
||||
}
|
||||
|
||||
clkdom = clkdom_create(dev);
|
||||
|
||||
memset(&def, 0, sizeof(def));
|
||||
error = clk_parse_ofw_clk_name(dev, node, &def.name);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot parse clock name\n");
|
||||
error = ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
def.id = 1;
|
||||
def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK);
|
||||
for (i = 0; i < ncells; i++) {
|
||||
error = clk_get_by_ofw_index(dev, 0, i, &clk_parent);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "cannot get clock %d\n", i);
|
||||
goto fail;
|
||||
}
|
||||
def.parent_names[i] = clk_get_name(clk_parent);
|
||||
clk_release(clk_parent);
|
||||
}
|
||||
def.parent_cnt = ncells;
|
||||
|
||||
clk = clknode_create(clkdom, &aw_thsclk_clknode_class, &def);
|
||||
if (clk == NULL) {
|
||||
device_printf(dev, "cannot create clknode\n");
|
||||
error = ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sc = clknode_get_softc(clk);
|
||||
sc->reg = paddr;
|
||||
sc->clkdev = device_get_parent(dev);
|
||||
|
||||
clknode_register(clkdom, clk);
|
||||
|
||||
if (clkdom_finit(clkdom) != 0) {
|
||||
device_printf(dev, "cannot finalize clkdom initialization\n");
|
||||
error = ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bootverbose)
|
||||
clkdom_dump(clkdom);
|
||||
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
return (error);
|
||||
}
|
||||
|
||||
static device_method_t aw_thsclk_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, aw_thsclk_probe),
|
||||
DEVMETHOD(device_attach, aw_thsclk_attach),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static driver_t aw_thsclk_driver = {
|
||||
"aw_thsclk",
|
||||
aw_thsclk_methods,
|
||||
0
|
||||
};
|
||||
|
||||
static devclass_t aw_thsclk_devclass;
|
||||
|
||||
EARLY_DRIVER_MODULE(aw_thsclk, simplebus, aw_thsclk_driver,
|
||||
aw_thsclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
|
@ -167,6 +167,12 @@ device aw_wdog # Allwinner Watchdog
|
||||
# Power management controllers
|
||||
device axp81x # X-Powers AXP81x PMIC
|
||||
|
||||
# EFUSE
|
||||
device aw_sid # Allwinner Secure ID EFUSE
|
||||
|
||||
# Thermal sensors
|
||||
device aw_thermal # Allwinner Thermal Sensor Controller
|
||||
|
||||
# Pseudo devices.
|
||||
device loop # Network loopback
|
||||
device random # Entropy device
|
||||
|
@ -22,6 +22,8 @@ arm/allwinner/aw_nmi.c optional aw_nmi \
|
||||
arm/allwinner/aw_reset.c optional aw_ccu
|
||||
arm/allwinner/aw_rsb.c optional aw_rsb
|
||||
arm/allwinner/aw_rtc.c optional aw_rtc
|
||||
arm/allwinner/aw_sid.c optional aw_sid
|
||||
arm/allwinner/aw_thermal.c optional aw_thermal
|
||||
arm/allwinner/aw_usbphy.c optional ehci aw_usbphy
|
||||
arm/allwinner/aw_wdog.c optional aw_wdog
|
||||
arm/allwinner/axp81x.c optional axp81x
|
||||
@ -33,6 +35,7 @@ arm/allwinner/clk/aw_gate.c optional aw_ccu
|
||||
arm/allwinner/clk/aw_modclk.c optional aw_ccu
|
||||
arm/allwinner/clk/aw_pll.c optional aw_ccu \
|
||||
compile-with "${NORMAL_C} -I$S/gnu/dts/include"
|
||||
arm/allwinner/clk/aw_thsclk.c optional aw_ccu
|
||||
arm/allwinner/clk/aw_usbclk.c optional aw_ccu
|
||||
arm/allwinner/if_awg.c optional awg
|
||||
arm/arm/generic_timer.c standard
|
||||
|
Loading…
x
Reference in New Issue
Block a user