I forgot to add Philippe Mathieu-Daudé in CC for this patch, so I'm fixing it now.
Best regards, Strahinja Jankovic On Sun, Dec 18, 2022 at 10:19 PM Strahinja Jankovic <strahinjapjanko...@gmail.com> wrote: > > This patch adds minimal support for AXP-209 PMU. > Most important is chip ID since U-Boot SPL expects version 0x1. Besides > the chip ID register, reset values for two more registers used by A10 > U-Boot SPL are covered. > > Signed-off-by: Strahinja Jankovic <strahinja.p.janko...@gmail.com> > --- > hw/arm/Kconfig | 1 + > hw/misc/Kconfig | 4 + > hw/misc/allwinner-axp-209.c | 238 ++++++++++++++++++++++++++++++++++++ > hw/misc/meson.build | 1 + > hw/misc/trace-events | 5 + > 5 files changed, 249 insertions(+) > create mode 100644 hw/misc/allwinner-axp-209.c > > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig > index eefe1fd134..67c6e83fe6 100644 > --- a/hw/arm/Kconfig > +++ b/hw/arm/Kconfig > @@ -323,6 +323,7 @@ config ALLWINNER_A10 > select ALLWINNER_A10_DRAMC > select ALLWINNER_EMAC > select ALLWINNER_I2C > + select ALLWINNER_AXP_209 > select SERIAL > select UNIMP > > diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig > index 052fb54310..3855d937fd 100644 > --- a/hw/misc/Kconfig > +++ b/hw/misc/Kconfig > @@ -180,4 +180,8 @@ config ALLWINNER_A10_CCM > config ALLWINNER_A10_DRAMC > bool > > +config ALLWINNER_AXP_209 > + bool > + depends on I2C > + > source macio/Kconfig > diff --git a/hw/misc/allwinner-axp-209.c b/hw/misc/allwinner-axp-209.c > new file mode 100644 > index 0000000000..cf79175034 > --- /dev/null > +++ b/hw/misc/allwinner-axp-209.c > @@ -0,0 +1,238 @@ > +/* > + * AXP-209 Emulation > + * > + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.janko...@gmail.com> > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > THE > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > + * DEALINGS IN THE SOFTWARE. > + * > + * SPDX-License-Identifier: MIT > + */ > + > +#include "qemu/osdep.h" > +#include "qemu/log.h" > +#include "trace.h" > +#include "hw/i2c/i2c.h" > +#include "migration/vmstate.h" > + > +#define TYPE_AXP_209 "allwinner.axp209" > + > +#define AXP_209(obj) \ > + OBJECT_CHECK(AXP209I2CState, (obj), TYPE_AXP_209) > + > +/* registers */ > +enum { > + REG_POWER_STATUS = 0x0u, > + REG_OPERATING_MODE, > + REG_OTG_VBUS_STATUS, > + REG_CHIP_VERSION, > + REG_DATA_CACHE_0, > + REG_DATA_CACHE_1, > + REG_DATA_CACHE_2, > + REG_DATA_CACHE_3, > + REG_DATA_CACHE_4, > + REG_DATA_CACHE_5, > + REG_DATA_CACHE_6, > + REG_DATA_CACHE_7, > + REG_DATA_CACHE_8, > + REG_DATA_CACHE_9, > + REG_DATA_CACHE_A, > + REG_DATA_CACHE_B, > + REG_POWER_OUTPUT_CTRL = 0x12u, > + REG_DC_DC2_OUT_V_CTRL = 0x23u, > + REG_DC_DC2_DVS_CTRL = 0x25u, > + REG_DC_DC3_OUT_V_CTRL = 0x27u, > + REG_LDO2_4_OUT_V_CTRL, > + REG_LDO3_OUT_V_CTRL, > + REG_VBUS_CH_MGMT = 0x30u, > + REG_SHUTDOWN_V_CTRL, > + REG_SHUTDOWN_CTRL, > + REG_CHARGE_CTRL_1, > + REG_CHARGE_CTRL_2, > + REG_SPARE_CHARGE_CTRL, > + REG_PEK_KEY_CTRL, > + REG_DC_DC_FREQ_SET, > + REG_CHR_TEMP_TH_SET, > + REG_CHR_HIGH_TEMP_TH_CTRL, > + REG_IPSOUT_WARN_L1, > + REG_IPSOUT_WARN_L2, > + REG_DISCHR_TEMP_TH_SET, > + REG_DISCHR_HIGH_TEMP_TH_CTRL, > + REG_IRQ_BANK_1_CTRL = 0x40u, > + REG_IRQ_BANK_2_CTRL, > + REG_IRQ_BANK_3_CTRL, > + REG_IRQ_BANK_4_CTRL, > + REG_IRQ_BANK_5_CTRL, > + REG_IRQ_BANK_1_STAT = 0x48u, > + REG_IRQ_BANK_2_STAT, > + REG_IRQ_BANK_3_STAT, > + REG_IRQ_BANK_4_STAT, > + REG_IRQ_BANK_5_STAT, > + REG_ADC_ACIN_V_H = 0x56u, > + REG_ADC_ACIN_V_L, > + REG_ADC_ACIN_CURR_H, > + REG_ADC_ACIN_CURR_L, > + REG_ADC_VBUS_V_H, > + REG_ADC_VBUS_V_L, > + REG_ADC_VBUS_CURR_H, > + REG_ADC_VBUS_CURR_L, > + REG_ADC_INT_TEMP_H, > + REG_ADC_INT_TEMP_L, > + REG_ADC_TEMP_SENS_V_H = 0x62u, > + REG_ADC_TEMP_SENS_V_L, > + REG_ADC_BAT_V_H = 0x78u, > + REG_ADC_BAT_V_L, > + REG_ADC_BAT_DISCHR_CURR_H, > + REG_ADC_BAT_DISCHR_CURR_L, > + REG_ADC_BAT_CHR_CURR_H, > + REG_ADC_BAT_CHR_CURR_L, > + REG_ADC_IPSOUT_V_H, > + REG_ADC_IPSOUT_V_L, > + REG_DC_DC_MOD_SEL = 0x80u, > + REG_ADC_EN_1, > + REG_ADC_EN_2, > + REG_ADC_SR_CTRL, > + REG_ADC_IN_RANGE, > + REG_GPIO1_ADC_IRQ_RISING_TH, > + REG_GPIO1_ADC_IRQ_FALLING_TH, > + REG_TIMER_CTRL = 0x8au, > + REG_VBUS_CTRL_MON_SRP, > + REG_OVER_TEMP_SHUTDOWN = 0x8fu, > + REG_GPIO0_FEAT_SET, > + REG_GPIO_OUT_HIGH_SET, > + REG_GPIO1_FEAT_SET, > + REG_GPIO2_FEAT_SET, > + REG_GPIO_SIG_STATE_SET_MON, > + REG_GPIO3_SET, > + REG_COULOMB_CNTR_CTRL = 0xb8u, > + REG_POWER_MEAS_RES, > + NR_REGS > +}; > + > +#define AXP_209_CHIP_VERSION_ID (0x01) > +#define AXP_209_DC_DC2_OUT_V_CTRL_RESET (0x16) > +#define AXP_209_IRQ_BANK_1_CTRL_RESET (0xd8) > + > +/* A simple I2C slave which returns values of ID or CNT register. */ > +typedef struct AXP209I2CState { > + /*< private >*/ > + I2CSlave i2c; > + /*< public >*/ > + uint8_t regs[NR_REGS]; /* peripheral registers */ > + uint8_t ptr; /* current register index */ > + uint8_t count; /* counter used for tx/rx */ > +} AXP209I2CState; > + > +/* Reset all counters and load ID register */ > +static void axp_209_reset_enter(Object *obj, ResetType type) > +{ > + AXP209I2CState *s = AXP_209(obj); > + > + memset(s->regs, 0, NR_REGS); > + s->ptr = 0; > + s->count = 0; > + s->regs[REG_CHIP_VERSION] = AXP_209_CHIP_VERSION_ID; > + s->regs[REG_DC_DC2_OUT_V_CTRL] = AXP_209_DC_DC2_OUT_V_CTRL_RESET; > + s->regs[REG_IRQ_BANK_1_CTRL] = AXP_209_IRQ_BANK_1_CTRL_RESET; > +} > + > +/* Handle events from master. */ > +static int axp_209_event(I2CSlave *i2c, enum i2c_event event) > +{ > + AXP209I2CState *s = AXP_209(i2c); > + > + s->count = 0; > + > + return 0; > +} > + > +/* Called when master requests read */ > +static uint8_t axp_209_rx(I2CSlave *i2c) > +{ > + AXP209I2CState *s = AXP_209(i2c); > + uint8_t ret = 0xff; > + > + if (s->ptr < NR_REGS) { > + ret = s->regs[s->ptr++]; > + } > + > + trace_allwinner_axp_209_rx(s->ptr - 1, ret); > + > + return ret; > +} > + > +/* > + * Called when master sends write. > + * Update ptr with byte 0, then perform write with second byte. > + */ > +static int axp_209_tx(I2CSlave *i2c, uint8_t data) > +{ > + AXP209I2CState *s = AXP_209(i2c); > + > + if (s->count == 0) { > + /* Store register address */ > + s->ptr = data; > + s->count++; > + trace_allwinner_axp_209_select(data); > + } else { > + trace_allwinner_axp_209_tx(s->ptr, data); > + if (s->ptr == REG_DC_DC2_OUT_V_CTRL) { > + s->regs[s->ptr++] = data; > + } > + } > + > + return 0; > +} > + > +static const VMStateDescription vmstate_axp_209 = { > + .name = TYPE_AXP_209, > + .version_id = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT8_ARRAY(regs, AXP209I2CState, NR_REGS), > + VMSTATE_UINT8(count, AXP209I2CState), > + VMSTATE_UINT8(ptr, AXP209I2CState), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static void axp_209_class_init(ObjectClass *oc, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(oc); > + I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); > + ResettableClass *rc = RESETTABLE_CLASS(oc); > + > + rc->phases.enter = axp_209_reset_enter; > + dc->vmsd = &vmstate_axp_209; > + isc->event = axp_209_event; > + isc->recv = axp_209_rx; > + isc->send = axp_209_tx; > +} > + > +static const TypeInfo axp_209_info = { > + .name = TYPE_AXP_209, > + .parent = TYPE_I2C_SLAVE, > + .instance_size = sizeof(AXP209I2CState), > + .class_init = axp_209_class_init > +}; > + > +static void axp_209_register_devices(void) > +{ > + type_register_static(&axp_209_info); > +} > + > +type_init(axp_209_register_devices); > diff --git a/hw/misc/meson.build b/hw/misc/meson.build > index 9eaa0750b5..7d332851cb 100644 > --- a/hw/misc/meson.build > +++ b/hw/misc/meson.build > @@ -40,6 +40,7 @@ softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: > files('ivshmem.c')) > > softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: > files('allwinner-a10-ccm.c')) > softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true: > files('allwinner-a10-dramc.c')) > +softmmu_ss.add(when: 'CONFIG_ALLWINNER_AXP_209', if_true: > files('allwinner-axp-209.c')) > softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: > files('allwinner-h3-ccu.c')) > specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: > files('allwinner-cpucfg.c')) > softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: > files('allwinner-h3-dramc.c')) > diff --git a/hw/misc/trace-events b/hw/misc/trace-events > index c18bc0605e..f6a7a6901f 100644 > --- a/hw/misc/trace-events > +++ b/hw/misc/trace-events > @@ -1,5 +1,10 @@ > # See docs/devel/tracing.rst for syntax documentation. > > +# allwinner-axp209.c > +allwinner_axp_209_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : > 0x%" PRIx8 > +allwinner_axp_209_select(uint8_t reg) "Accessing reg 0x%" PRIx8 > +allwinner_axp_209_tx(uint8_t reg, uint8_t data) "Write reg 0x%" PRIx8 " : > 0x%" PRIx8 > + > # allwinner-cpucfg.c > allwinner_cpucfg_cpu_reset(uint8_t cpu_id, uint32_t reset_addr) "id %u, > reset_addr 0x%" PRIx32 > allwinner_cpucfg_read(uint64_t offset, uint64_t data, unsigned size) "offset > 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 > -- > 2.30.2 >