f0a5e81af4
While the xin32k clk was implemented in rk3399_cru as a fixed rate clock, migrate it to rk805 as we will also need the 2nd clock 'rtc_clko_wifi' for SDIO and BT. Both clocks remain fixed rate, and while the 1st one is always on (though that is not expressed in the clk framework), the 2nd one we can toggle on/off. Reviewed-by: manu Tested-by: manu MFC-after: 2 weeks Differential Revision: https://reviews.freebsd.org/D26870
973 lines
23 KiB
C
973 lines
23 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2018 Emmanuel Vadot <manu@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/param.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/clock.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/rman.h>
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/iicbus/iiconf.h>
|
|
#include <dev/iicbus/iicbus.h>
|
|
|
|
#include <dev/ofw/ofw_bus.h>
|
|
#include <dev/ofw/ofw_bus_subr.h>
|
|
|
|
#include <dev/extres/clk/clk.h>
|
|
#include <dev/extres/regulator/regulator.h>
|
|
|
|
#include <arm64/rockchip/rk805reg.h>
|
|
|
|
#include "clock_if.h"
|
|
#include "regdev_if.h"
|
|
|
|
MALLOC_DEFINE(M_RK805_REG, "RK805 regulator", "RK805 power regulator");
|
|
|
|
/* #define dprintf(sc, format, arg...) device_printf(sc->base_dev, "%s: " format, __func__, arg) */
|
|
#define dprintf(sc, format, arg...)
|
|
|
|
enum rk_pmic_type {
|
|
RK805 = 1,
|
|
RK808,
|
|
};
|
|
|
|
static struct ofw_compat_data compat_data[] = {
|
|
{"rockchip,rk805", RK805},
|
|
{"rockchip,rk808", RK808},
|
|
{NULL, 0}
|
|
};
|
|
|
|
struct rk805_regdef {
|
|
intptr_t id;
|
|
char *name;
|
|
uint8_t enable_reg;
|
|
uint8_t enable_mask;
|
|
uint8_t voltage_reg;
|
|
uint8_t voltage_mask;
|
|
int voltage_min;
|
|
int voltage_max;
|
|
int voltage_step;
|
|
int voltage_nstep;
|
|
};
|
|
|
|
struct rk805_reg_sc {
|
|
struct regnode *regnode;
|
|
device_t base_dev;
|
|
struct rk805_regdef *def;
|
|
phandle_t xref;
|
|
struct regnode_std_param *param;
|
|
};
|
|
|
|
struct reg_list {
|
|
TAILQ_ENTRY(reg_list) next;
|
|
struct rk805_reg_sc *reg;
|
|
};
|
|
|
|
struct rk805_softc {
|
|
device_t dev;
|
|
struct mtx mtx;
|
|
struct resource * res[1];
|
|
void * intrcookie;
|
|
struct intr_config_hook intr_hook;
|
|
enum rk_pmic_type type;
|
|
|
|
TAILQ_HEAD(, reg_list) regs;
|
|
int nregs;
|
|
};
|
|
|
|
static int rk805_regnode_status(struct regnode *regnode, int *status);
|
|
static int rk805_regnode_set_voltage(struct regnode *regnode, int min_uvolt,
|
|
int max_uvolt, int *udelay);
|
|
static int rk805_regnode_get_voltage(struct regnode *regnode, int *uvolt);
|
|
|
|
static struct rk805_regdef rk805_regdefs[] = {
|
|
{
|
|
.id = RK805_DCDC1,
|
|
.name = "DCDC_REG1",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x11,
|
|
.voltage_reg = RK805_DCDC1_ON_VSEL,
|
|
.voltage_mask = 0x3F,
|
|
.voltage_min = 712500,
|
|
.voltage_max = 1450000,
|
|
.voltage_step = 12500,
|
|
.voltage_nstep = 64,
|
|
},
|
|
{
|
|
.id = RK805_DCDC2,
|
|
.name = "DCDC_REG2",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x22,
|
|
.voltage_reg = RK805_DCDC2_ON_VSEL,
|
|
.voltage_mask = 0x3F,
|
|
.voltage_min = 712500,
|
|
.voltage_max = 1450000,
|
|
.voltage_step = 12500,
|
|
.voltage_nstep = 64,
|
|
},
|
|
{
|
|
.id = RK805_DCDC3,
|
|
.name = "DCDC_REG3",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x44,
|
|
},
|
|
{
|
|
.id = RK805_DCDC4,
|
|
.name = "DCDC_REG4",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x88,
|
|
.voltage_reg = RK805_DCDC4_ON_VSEL,
|
|
.voltage_mask = 0x3F,
|
|
.voltage_min = 800000,
|
|
.voltage_max = 3500000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 28,
|
|
},
|
|
{
|
|
.id = RK805_LDO1,
|
|
.name = "LDO_REG1",
|
|
.enable_reg = RK805_LDO_EN,
|
|
.enable_mask = 0x11,
|
|
.voltage_reg = RK805_LDO1_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 800000,
|
|
.voltage_max = 3400000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 27,
|
|
},
|
|
{
|
|
.id = RK805_LDO2,
|
|
.name = "LDO_REG2",
|
|
.enable_reg = RK805_LDO_EN,
|
|
.enable_mask = 0x22,
|
|
.voltage_reg = RK805_LDO2_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 800000,
|
|
.voltage_max = 3400000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 27,
|
|
},
|
|
{
|
|
.id = RK805_LDO3,
|
|
.name = "LDO_REG3",
|
|
.enable_reg = RK805_LDO_EN,
|
|
.enable_mask = 0x44,
|
|
.voltage_reg = RK805_LDO3_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 800000,
|
|
.voltage_max = 3400000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 27,
|
|
},
|
|
};
|
|
|
|
static struct rk805_regdef rk808_regdefs[] = {
|
|
{
|
|
.id = RK805_DCDC1,
|
|
.name = "DCDC_REG1",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x1,
|
|
.voltage_reg = RK805_DCDC1_ON_VSEL,
|
|
.voltage_mask = 0x3F,
|
|
.voltage_min = 712500,
|
|
.voltage_max = 1500000,
|
|
.voltage_step = 12500,
|
|
.voltage_nstep = 64,
|
|
},
|
|
{
|
|
.id = RK805_DCDC2,
|
|
.name = "DCDC_REG2",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x2,
|
|
.voltage_reg = RK805_DCDC2_ON_VSEL,
|
|
.voltage_mask = 0x3F,
|
|
.voltage_min = 712500,
|
|
.voltage_max = 1500000,
|
|
.voltage_step = 12500,
|
|
.voltage_nstep = 64,
|
|
},
|
|
{
|
|
/* BUCK3 voltage is calculated based on external resistor */
|
|
.id = RK805_DCDC3,
|
|
.name = "DCDC_REG3",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x4,
|
|
},
|
|
{
|
|
.id = RK805_DCDC4,
|
|
.name = "DCDC_REG4",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x8,
|
|
.voltage_reg = RK805_DCDC4_ON_VSEL,
|
|
.voltage_mask = 0xF,
|
|
.voltage_min = 1800000,
|
|
.voltage_max = 3300000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 16,
|
|
},
|
|
{
|
|
.id = RK808_LDO1,
|
|
.name = "LDO_REG1",
|
|
.enable_reg = RK808_LDO_EN,
|
|
.enable_mask = 0x1,
|
|
.voltage_reg = RK805_LDO1_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 1800000,
|
|
.voltage_max = 3400000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 17,
|
|
},
|
|
{
|
|
.id = RK808_LDO2,
|
|
.name = "LDO_REG2",
|
|
.enable_reg = RK808_LDO_EN,
|
|
.enable_mask = 0x2,
|
|
.voltage_reg = RK805_LDO2_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 1800000,
|
|
.voltage_max = 3400000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 17,
|
|
},
|
|
{
|
|
.id = RK808_LDO3,
|
|
.name = "LDO_REG3",
|
|
.enable_reg = RK808_LDO_EN,
|
|
.enable_mask = 0x4,
|
|
.voltage_reg = RK805_LDO3_ON_VSEL,
|
|
.voltage_mask = 0xF,
|
|
.voltage_min = 800000,
|
|
.voltage_max = 2500000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 18,
|
|
},
|
|
{
|
|
.id = RK808_LDO4,
|
|
.name = "LDO_REG4",
|
|
.enable_reg = RK808_LDO_EN,
|
|
.enable_mask = 0x8,
|
|
.voltage_reg = RK808_LDO4_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 1800000,
|
|
.voltage_max = 3400000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 17,
|
|
},
|
|
{
|
|
.id = RK808_LDO5,
|
|
.name = "LDO_REG5",
|
|
.enable_reg = RK808_LDO_EN,
|
|
.enable_mask = 0x10,
|
|
.voltage_reg = RK808_LDO5_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 1800000,
|
|
.voltage_max = 3400000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 17,
|
|
},
|
|
{
|
|
.id = RK808_LDO6,
|
|
.name = "LDO_REG6",
|
|
.enable_reg = RK808_LDO_EN,
|
|
.enable_mask = 0x20,
|
|
.voltage_reg = RK808_LDO6_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 800000,
|
|
.voltage_max = 2500000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 18,
|
|
},
|
|
{
|
|
.id = RK808_LDO7,
|
|
.name = "LDO_REG7",
|
|
.enable_reg = RK808_LDO_EN,
|
|
.enable_mask = 0x40,
|
|
.voltage_reg = RK808_LDO7_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 800000,
|
|
.voltage_max = 2500000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 18,
|
|
},
|
|
{
|
|
.id = RK808_LDO8,
|
|
.name = "LDO_REG8",
|
|
.enable_reg = RK808_LDO_EN,
|
|
.enable_mask = 0x80,
|
|
.voltage_reg = RK808_LDO8_ON_VSEL,
|
|
.voltage_mask = 0x1F,
|
|
.voltage_min = 1800000,
|
|
.voltage_max = 3400000,
|
|
.voltage_step = 100000,
|
|
.voltage_nstep = 17,
|
|
},
|
|
{
|
|
.id = RK808_SWITCH1,
|
|
.name = "SWITCH_REG1",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x20,
|
|
.voltage_min = 3000000,
|
|
.voltage_max = 3000000,
|
|
},
|
|
{
|
|
.id = RK808_SWITCH2,
|
|
.name = "SWITCH_REG2",
|
|
.enable_reg = RK805_DCDC_EN,
|
|
.enable_mask = 0x40,
|
|
.voltage_min = 3000000,
|
|
.voltage_max = 3000000,
|
|
},
|
|
};
|
|
|
|
static int
|
|
rk805_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
|
|
{
|
|
int err;
|
|
|
|
err = iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT);
|
|
return (err);
|
|
}
|
|
|
|
static int
|
|
rk805_write(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
|
|
{
|
|
|
|
return (iicdev_writeto(dev, reg, data, size, IIC_INTRWAIT));
|
|
}
|
|
|
|
static int
|
|
rk805_regnode_init(struct regnode *regnode)
|
|
{
|
|
struct rk805_reg_sc *sc;
|
|
struct regnode_std_param *param;
|
|
int rv, udelay, uvolt, status;
|
|
|
|
sc = regnode_get_softc(regnode);
|
|
dprintf(sc, "Regulator %s init called\n", sc->def->name);
|
|
param = regnode_get_stdparam(regnode);
|
|
if (param->min_uvolt == 0)
|
|
return (0);
|
|
|
|
/* Check that the regulator is preset to the correct voltage */
|
|
rv = rk805_regnode_get_voltage(regnode, &uvolt);
|
|
if (rv != 0)
|
|
return(rv);
|
|
|
|
if (uvolt >= param->min_uvolt && uvolt <= param->max_uvolt)
|
|
return(0);
|
|
/*
|
|
* Set the regulator at the correct voltage if it is not enabled.
|
|
* Do not enable it, this is will be done either by a
|
|
* consumer or by regnode_set_constraint if boot_on is true
|
|
*/
|
|
rv = rk805_regnode_status(regnode, &status);
|
|
if (rv != 0 || status == REGULATOR_STATUS_ENABLED)
|
|
return (rv);
|
|
|
|
rv = rk805_regnode_set_voltage(regnode, param->min_uvolt,
|
|
param->max_uvolt, &udelay);
|
|
if (udelay != 0)
|
|
DELAY(udelay);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
static int
|
|
rk805_regnode_enable(struct regnode *regnode, bool enable, int *udelay)
|
|
{
|
|
struct rk805_reg_sc *sc;
|
|
uint8_t val;
|
|
|
|
sc = regnode_get_softc(regnode);
|
|
|
|
dprintf(sc, "%sabling regulator %s\n",
|
|
enable ? "En" : "Dis",
|
|
sc->def->name);
|
|
rk805_read(sc->base_dev, sc->def->enable_reg, &val, 1);
|
|
if (enable)
|
|
val |= sc->def->enable_mask;
|
|
else
|
|
val &= ~sc->def->enable_mask;
|
|
rk805_write(sc->base_dev, sc->def->enable_reg, &val, 1);
|
|
|
|
*udelay = 0;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
rk805_regnode_reg_to_voltage(struct rk805_reg_sc *sc, uint8_t val, int *uv)
|
|
{
|
|
if (val < sc->def->voltage_nstep)
|
|
*uv = sc->def->voltage_min + val * sc->def->voltage_step;
|
|
else
|
|
*uv = sc->def->voltage_min +
|
|
(sc->def->voltage_nstep * sc->def->voltage_step);
|
|
}
|
|
|
|
static int
|
|
rk805_regnode_voltage_to_reg(struct rk805_reg_sc *sc, int min_uvolt,
|
|
int max_uvolt, uint8_t *val)
|
|
{
|
|
uint8_t nval;
|
|
int nstep, uvolt;
|
|
|
|
nval = 0;
|
|
uvolt = sc->def->voltage_min;
|
|
|
|
for (nstep = 0; nstep < sc->def->voltage_nstep && uvolt < min_uvolt;
|
|
nstep++) {
|
|
++nval;
|
|
uvolt += sc->def->voltage_step;
|
|
}
|
|
if (uvolt > max_uvolt)
|
|
return (EINVAL);
|
|
|
|
*val = nval;
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
rk805_regnode_status(struct regnode *regnode, int *status)
|
|
{
|
|
struct rk805_reg_sc *sc;
|
|
uint8_t val;
|
|
|
|
sc = regnode_get_softc(regnode);
|
|
|
|
*status = 0;
|
|
rk805_read(sc->base_dev, sc->def->enable_reg, &val, 1);
|
|
if (val & sc->def->enable_mask)
|
|
*status = REGULATOR_STATUS_ENABLED;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
rk805_regnode_set_voltage(struct regnode *regnode, int min_uvolt,
|
|
int max_uvolt, int *udelay)
|
|
{
|
|
struct rk805_reg_sc *sc;
|
|
uint8_t val;
|
|
int uvolt;
|
|
|
|
sc = regnode_get_softc(regnode);
|
|
|
|
if (!sc->def->voltage_step)
|
|
return (ENXIO);
|
|
|
|
dprintf(sc, "Setting %s to %d<->%d uvolts\n",
|
|
sc->def->name,
|
|
min_uvolt,
|
|
max_uvolt);
|
|
rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1);
|
|
if (rk805_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0)
|
|
return (ERANGE);
|
|
|
|
rk805_write(sc->base_dev, sc->def->voltage_reg, &val, 1);
|
|
|
|
rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1);
|
|
|
|
*udelay = 0;
|
|
|
|
rk805_regnode_reg_to_voltage(sc, val, &uvolt);
|
|
dprintf(sc, "Regulator %s set to %d uvolt\n",
|
|
sc->def->name,
|
|
uvolt);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
rk805_regnode_get_voltage(struct regnode *regnode, int *uvolt)
|
|
{
|
|
struct rk805_reg_sc *sc;
|
|
uint8_t val;
|
|
|
|
sc = regnode_get_softc(regnode);
|
|
|
|
if (sc->def->voltage_min == sc->def->voltage_max) {
|
|
*uvolt = sc->def->voltage_min;
|
|
return (0);
|
|
}
|
|
|
|
if (!sc->def->voltage_step)
|
|
return (ENXIO);
|
|
|
|
rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1);
|
|
rk805_regnode_reg_to_voltage(sc, val & sc->def->voltage_mask, uvolt);
|
|
|
|
dprintf(sc, "Regulator %s is at %d uvolt\n",
|
|
sc->def->name,
|
|
*uvolt);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static regnode_method_t rk805_regnode_methods[] = {
|
|
/* Regulator interface */
|
|
REGNODEMETHOD(regnode_init, rk805_regnode_init),
|
|
REGNODEMETHOD(regnode_enable, rk805_regnode_enable),
|
|
REGNODEMETHOD(regnode_status, rk805_regnode_status),
|
|
REGNODEMETHOD(regnode_set_voltage, rk805_regnode_set_voltage),
|
|
REGNODEMETHOD(regnode_get_voltage, rk805_regnode_get_voltage),
|
|
REGNODEMETHOD(regnode_check_voltage, regnode_method_check_voltage),
|
|
REGNODEMETHOD_END
|
|
};
|
|
DEFINE_CLASS_1(rk805_regnode, rk805_regnode_class, rk805_regnode_methods,
|
|
sizeof(struct rk805_reg_sc), regnode_class);
|
|
|
|
static struct rk805_reg_sc *
|
|
rk805_reg_attach(device_t dev, phandle_t node,
|
|
struct rk805_regdef *def)
|
|
{
|
|
struct rk805_reg_sc *reg_sc;
|
|
struct regnode_init_def initdef;
|
|
struct regnode *regnode;
|
|
|
|
memset(&initdef, 0, sizeof(initdef));
|
|
if (regulator_parse_ofw_stdparam(dev, node, &initdef) != 0) {
|
|
device_printf(dev, "cannot create regulator\n");
|
|
return (NULL);
|
|
}
|
|
if (initdef.std_param.min_uvolt == 0)
|
|
initdef.std_param.min_uvolt = def->voltage_min;
|
|
if (initdef.std_param.max_uvolt == 0)
|
|
initdef.std_param.max_uvolt = def->voltage_max;
|
|
initdef.id = def->id;
|
|
initdef.ofw_node = node;
|
|
|
|
regnode = regnode_create(dev, &rk805_regnode_class, &initdef);
|
|
if (regnode == NULL) {
|
|
device_printf(dev, "cannot create regulator\n");
|
|
return (NULL);
|
|
}
|
|
|
|
reg_sc = regnode_get_softc(regnode);
|
|
reg_sc->regnode = regnode;
|
|
reg_sc->base_dev = dev;
|
|
reg_sc->def = def;
|
|
reg_sc->xref = OF_xref_from_node(node);
|
|
reg_sc->param = regnode_get_stdparam(regnode);
|
|
|
|
regnode_register(regnode);
|
|
|
|
return (reg_sc);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/* Clock class and method */
|
|
struct rk805_clk_sc {
|
|
device_t base_dev;
|
|
};
|
|
|
|
#define CLK32OUT_REG 0x20
|
|
#define CLK32OUT_CLKOUT2_EN 1
|
|
|
|
static int
|
|
rk805_clk_set_gate_1(struct clknode *clk, bool enable)
|
|
{
|
|
struct rk805_clk_sc *sc;
|
|
uint8_t val;
|
|
|
|
sc = clknode_get_softc(clk);
|
|
|
|
rk805_read(sc->base_dev, CLK32OUT_REG, &val, sizeof(val));
|
|
if (enable)
|
|
val |= CLK32OUT_CLKOUT2_EN;
|
|
else
|
|
val &= ~CLK32OUT_CLKOUT2_EN;
|
|
rk805_write(sc->base_dev, CLK32OUT_REG, &val, 1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
rk805_clk_recalc(struct clknode *clk, uint64_t *freq)
|
|
{
|
|
|
|
*freq = 32768;
|
|
return (0);
|
|
}
|
|
|
|
static clknode_method_t rk805_clk_clknode_methods_0[] = {
|
|
CLKNODEMETHOD(clknode_recalc_freq, rk805_clk_recalc),
|
|
CLKNODEMETHOD_END
|
|
};
|
|
|
|
DEFINE_CLASS_1(rk805_clk_clknode_0, rk805_clk_clknode_class_0,
|
|
rk805_clk_clknode_methods_0, sizeof(struct rk805_clk_sc),
|
|
clknode_class);
|
|
|
|
static clknode_method_t rk805_clk_clknode_methods_1[] = {
|
|
CLKNODEMETHOD(clknode_set_gate, rk805_clk_set_gate_1),
|
|
CLKNODEMETHOD_END
|
|
};
|
|
|
|
DEFINE_CLASS_1(rk805_clk_clknode_1, rk805_clk_clknode_class_1,
|
|
rk805_clk_clknode_methods_1, sizeof(struct rk805_clk_sc),
|
|
rk805_clk_clknode_class_0);
|
|
|
|
static int
|
|
rk805_export_clocks(device_t dev)
|
|
{
|
|
struct clkdom *clkdom;
|
|
struct clknode_init_def clkidef;
|
|
struct clknode *clk;
|
|
struct rk805_clk_sc *clksc;
|
|
const char **clknames;
|
|
phandle_t node;
|
|
int nclks, rv;
|
|
|
|
node = ofw_bus_get_node(dev);
|
|
|
|
/* clock-output-names are optional. Could use them for clkidef.name. */
|
|
nclks = ofw_bus_string_list_to_array(node, "clock-output-names",
|
|
&clknames);
|
|
|
|
clkdom = clkdom_create(dev);
|
|
|
|
memset(&clkidef, 0, sizeof(clkidef));
|
|
clkidef.id = 0;
|
|
clkidef.name = (nclks = 2) ? clknames[0] : "clk32kout1";
|
|
clk = clknode_create(clkdom, &rk805_clk_clknode_class_0, &clkidef);
|
|
if (clk == NULL) {
|
|
device_printf(dev, "Cannot create '%s'.\n", clkidef.name);
|
|
return (ENXIO);
|
|
}
|
|
clksc = clknode_get_softc(clk);
|
|
clksc->base_dev = dev;
|
|
clknode_register(clkdom, clk);
|
|
|
|
memset(&clkidef, 0, sizeof(clkidef));
|
|
clkidef.id = 1;
|
|
clkidef.name = (nclks = 2) ? clknames[1] : "clk32kout2";
|
|
clk = clknode_create(clkdom, &rk805_clk_clknode_class_1, &clkidef);
|
|
if (clk == NULL) {
|
|
device_printf(dev, "Cannot create '%s'.\n", clkidef.name);
|
|
return (ENXIO);
|
|
}
|
|
clksc = clknode_get_softc(clk);
|
|
clksc->base_dev = dev;
|
|
clknode_register(clkdom, clk);
|
|
|
|
rv = clkdom_finit(clkdom);
|
|
if (rv != 0) {
|
|
device_printf(dev, "Cannot finalize clkdom initialization: "
|
|
"%d\n", rv);
|
|
return (ENXIO);
|
|
}
|
|
|
|
if (bootverbose)
|
|
clkdom_dump(clkdom);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static int
|
|
rk805_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, "RockChip RK805 PMIC");
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
static void
|
|
rk805_start(void *pdev)
|
|
{
|
|
struct rk805_softc *sc;
|
|
device_t dev;
|
|
uint8_t data[2];
|
|
int err;
|
|
|
|
dev = pdev;
|
|
sc = device_get_softc(dev);
|
|
sc->dev = dev;
|
|
|
|
/* No version register in RK808 */
|
|
if (bootverbose && sc->type == RK805) {
|
|
err = rk805_read(dev, RK805_CHIP_NAME, data, 1);
|
|
if (err != 0) {
|
|
device_printf(dev, "Cannot read chip name reg\n");
|
|
return;
|
|
}
|
|
err = rk805_read(dev, RK805_CHIP_VER, data + 1, 1);
|
|
if (err != 0) {
|
|
device_printf(dev, "Cannot read chip version reg\n");
|
|
return;
|
|
}
|
|
device_printf(dev, "Chip Name: %x\n",
|
|
data[0] << 4 | ((data[1] >> 4) & 0xf));
|
|
device_printf(dev, "Chip Version: %x\n", data[1] & 0xf);
|
|
}
|
|
|
|
/* Register this as a 1Hz clock */
|
|
clock_register(dev, 1000000);
|
|
|
|
config_intrhook_disestablish(&sc->intr_hook);
|
|
}
|
|
|
|
static int
|
|
rk805_gettime(device_t dev, struct timespec *ts)
|
|
{
|
|
struct bcd_clocktime bct;
|
|
uint8_t data[7];
|
|
uint8_t ctrl;
|
|
int error;
|
|
|
|
/* Latch the RTC value into the shadow registers and set 24hr mode */
|
|
error = rk805_read(dev, RK805_RTC_CTRL, &ctrl, 1);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
ctrl |= RK805_RTC_READSEL;
|
|
ctrl &= ~(RK805_RTC_AMPM_MODE | RK805_RTC_GET_TIME);
|
|
error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1);
|
|
if (error != 0)
|
|
return (error);
|
|
ctrl |= RK805_RTC_GET_TIME;
|
|
error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1);
|
|
if (error != 0)
|
|
return (error);
|
|
ctrl &= ~RK805_RTC_GET_TIME;
|
|
error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
/* This works as long as RK805_RTC_SECS = 0 */
|
|
error = rk805_read(dev, RK805_RTC_SECS, data, 7);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
/*
|
|
* If the reported year is earlier than 2019, assume the clock is unset.
|
|
* This is both later than the reset value for the RK805 and RK808 as
|
|
* well as being prior to the current time.
|
|
*/
|
|
if (data[RK805_RTC_YEARS] < 0x19)
|
|
return (EINVAL);
|
|
|
|
memset(&bct, 0, sizeof(bct));
|
|
bct.year = data[RK805_RTC_YEARS];
|
|
bct.mon = data[RK805_RTC_MONTHS] & RK805_RTC_MONTHS_MASK;
|
|
bct.day = data[RK805_RTC_DAYS] & RK805_RTC_DAYS_MASK;
|
|
bct.hour = data[RK805_RTC_HOURS] & RK805_RTC_HOURS_MASK;
|
|
bct.min = data[RK805_RTC_MINUTES] & RK805_RTC_MINUTES_MASK;
|
|
bct.sec = data[RK805_RTC_SECS] & RK805_RTC_SECS_MASK;
|
|
bct.dow = data[RK805_RTC_WEEKS] & RK805_RTC_WEEKS_MASK;
|
|
/* The day of week is reported as 1-7 with 1 = Monday */
|
|
if (bct.dow == 7)
|
|
bct.dow = 0;
|
|
bct.ispm = 0;
|
|
|
|
if (bootverbose)
|
|
device_printf(dev, "Read RTC: %02x-%02x-%02x %02x:%02x:%02x\n",
|
|
bct.year, bct.mon, bct.day, bct.hour, bct.min, bct.sec);
|
|
|
|
return (clock_bcd_to_ts(&bct, ts, false));
|
|
}
|
|
|
|
static int
|
|
rk805_settime(device_t dev, struct timespec *ts)
|
|
{
|
|
struct bcd_clocktime bct;
|
|
uint8_t data[7];
|
|
int error;
|
|
uint8_t ctrl;
|
|
|
|
clock_ts_to_bcd(ts, &bct, false);
|
|
|
|
/* This works as long as RK805_RTC_SECS = 0 */
|
|
data[RK805_RTC_YEARS] = bct.year;
|
|
data[RK805_RTC_MONTHS] = bct.mon;
|
|
data[RK805_RTC_DAYS] = bct.day;
|
|
data[RK805_RTC_HOURS] = bct.hour;
|
|
data[RK805_RTC_MINUTES] = bct.min;
|
|
data[RK805_RTC_SECS] = bct.sec;
|
|
data[RK805_RTC_WEEKS] = bct.dow;
|
|
/* The day of week is reported as 1-7 with 1 = Monday */
|
|
if (data[RK805_RTC_WEEKS] == 0)
|
|
data[RK805_RTC_WEEKS] = 7;
|
|
|
|
error = rk805_read(dev, RK805_RTC_CTRL, &ctrl, 1);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
ctrl |= RK805_RTC_CTRL_STOP;
|
|
ctrl &= ~RK805_RTC_AMPM_MODE;
|
|
error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
error = rk805_write(dev, RK805_RTC_SECS, data, 7);
|
|
ctrl &= ~RK805_RTC_CTRL_STOP;
|
|
rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1);
|
|
|
|
if (bootverbose)
|
|
device_printf(dev,
|
|
"Set RTC at %04x-%02x-%02x %02x:%02x:%02x[.%09ld]\n",
|
|
bct.year, bct.mon, bct.day, bct.hour, bct.min, bct.sec,
|
|
bct.nsec);
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
rk805_attach(device_t dev)
|
|
{
|
|
struct rk805_softc *sc;
|
|
struct rk805_reg_sc *reg;
|
|
struct rk805_regdef *regdefs;
|
|
struct reg_list *regp;
|
|
phandle_t rnode, child;
|
|
int error, i;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
|
|
error = rk805_export_clocks(dev);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
sc->intr_hook.ich_func = rk805_start;
|
|
sc->intr_hook.ich_arg = dev;
|
|
if (config_intrhook_establish(&sc->intr_hook) != 0)
|
|
return (ENOMEM);
|
|
|
|
switch (sc->type) {
|
|
case RK805:
|
|
regdefs = rk805_regdefs;
|
|
sc->nregs = nitems(rk805_regdefs);
|
|
break;
|
|
case RK808:
|
|
regdefs = rk808_regdefs;
|
|
sc->nregs = nitems(rk808_regdefs);
|
|
break;
|
|
default:
|
|
device_printf(dev, "Unknown type %d\n", sc->type);
|
|
return (ENXIO);
|
|
}
|
|
|
|
TAILQ_INIT(&sc->regs);
|
|
|
|
rnode = ofw_bus_find_child(ofw_bus_get_node(dev), "regulators");
|
|
if (rnode > 0) {
|
|
for (i = 0; i < sc->nregs; i++) {
|
|
child = ofw_bus_find_child(rnode,
|
|
regdefs[i].name);
|
|
if (child == 0)
|
|
continue;
|
|
if (OF_hasprop(child, "regulator-name") != 1)
|
|
continue;
|
|
reg = rk805_reg_attach(dev, child, ®defs[i]);
|
|
if (reg == NULL) {
|
|
device_printf(dev,
|
|
"cannot attach regulator %s\n",
|
|
regdefs[i].name);
|
|
continue;
|
|
}
|
|
regp = malloc(sizeof(*regp), M_DEVBUF, M_WAITOK | M_ZERO);
|
|
regp->reg = reg;
|
|
TAILQ_INSERT_TAIL(&sc->regs, regp, next);
|
|
if (bootverbose)
|
|
device_printf(dev, "Regulator %s attached\n",
|
|
regdefs[i].name);
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
rk805_detach(device_t dev)
|
|
{
|
|
|
|
/* We cannot detach regulators */
|
|
return (EBUSY);
|
|
}
|
|
|
|
static int
|
|
rk805_map(device_t dev, phandle_t xref, int ncells,
|
|
pcell_t *cells, intptr_t *id)
|
|
{
|
|
struct rk805_softc *sc;
|
|
struct reg_list *regp;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
TAILQ_FOREACH(regp, &sc->regs, next) {
|
|
if (regp->reg->xref == xref) {
|
|
*id = regp->reg->def->id;
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
return (ERANGE);
|
|
}
|
|
|
|
static device_method_t rk805_methods[] = {
|
|
DEVMETHOD(device_probe, rk805_probe),
|
|
DEVMETHOD(device_attach, rk805_attach),
|
|
DEVMETHOD(device_detach, rk805_detach),
|
|
|
|
/* regdev interface */
|
|
DEVMETHOD(regdev_map, rk805_map),
|
|
|
|
/* Clock interface */
|
|
DEVMETHOD(clock_gettime, rk805_gettime),
|
|
DEVMETHOD(clock_settime, rk805_settime),
|
|
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t rk805_driver = {
|
|
"rk805_pmu",
|
|
rk805_methods,
|
|
sizeof(struct rk805_softc),
|
|
};
|
|
|
|
static devclass_t rk805_devclass;
|
|
|
|
EARLY_DRIVER_MODULE(rk805, iicbus, rk805_driver, rk805_devclass, 0, 0,
|
|
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
|
|
MODULE_DEPEND(rk805, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
|
|
MODULE_VERSION(rk805, 1);
|