diff --git a/src/drivers/i2c/rx6110sa/Kconfig b/src/drivers/i2c/rx6110sa/Kconfig new file mode 100644 index 0000000000..a5608f82f7 --- /dev/null +++ b/src/drivers/i2c/rx6110sa/Kconfig @@ -0,0 +1,5 @@ +config DRIVERS_I2C_RX6110SA + bool + default n + help + Enable support for external RTC chip RX6110 SA. diff --git a/src/drivers/i2c/rx6110sa/Makefile.inc b/src/drivers/i2c/rx6110sa/Makefile.inc new file mode 100644 index 0000000000..44c76a3291 --- /dev/null +++ b/src/drivers/i2c/rx6110sa/Makefile.inc @@ -0,0 +1 @@ +ramstage-$(CONFIG_DRIVERS_I2C_RX6110SA) += rx6110sa.c diff --git a/src/drivers/i2c/rx6110sa/chip.h b/src/drivers/i2c/rx6110sa/chip.h new file mode 100644 index 0000000000..b90a52963b --- /dev/null +++ b/src/drivers/i2c/rx6110sa/chip.h @@ -0,0 +1,26 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2017 Siemens AG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "rx6110sa.h" + +struct drivers_i2c_rx6110sa_config { + /* The day (of the week) is indicated by 7 bits, bit 0 to bit 6. */ + unsigned char user_weekday; /* User day of the week to set */ + unsigned char user_day; /* User day to set */ + unsigned char user_month; /* User month to set */ + unsigned char user_year; /* User year to set */ + unsigned char set_user_date; /* Use user date from device tree */ + unsigned char cof_selection; /* Set up "clock out" frequency */ +}; diff --git a/src/drivers/i2c/rx6110sa/rx6110sa.c b/src/drivers/i2c/rx6110sa/rx6110sa.c new file mode 100644 index 0000000000..743b708c19 --- /dev/null +++ b/src/drivers/i2c/rx6110sa/rx6110sa.c @@ -0,0 +1,146 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2017 Siemens AG. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "chip.h" +#include "rx6110sa.h" + +#define I2C_BUS_NUM (dev->bus->secondary - 1) +#define I2C_DEV_NUM (dev->path.i2c.device) + +/* Set RTC date from coreboot build date. */ +static void rx6110sa_set_build_date(struct device *dev) +{ + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, YEAR_REG, + coreboot_build_date.year); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, MONTH_REG, + coreboot_build_date.month); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, DAY_REG, + coreboot_build_date.day); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, WEEK_REG, + (1 << coreboot_build_date.weekday)); +} + +/* Set RTC date from user defined date (available in e.g. device tree). */ +static void rx6110sa_set_user_date(struct device *dev) +{ + struct drivers_i2c_rx6110sa_config *config = dev->chip_info; + + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, YEAR_REG, + bin2bcd(config->user_year)); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, MONTH_REG, + bin2bcd(config->user_month)); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, DAY_REG, + bin2bcd(config->user_day)); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, WEEK_REG, + (1 << config->user_weekday)); +} + +static void rx6110sa_final(struct device *dev) +{ + uint8_t hour, minute, second, year, month, day; + + /* Read back current RTC date and time and print it to the console. */ + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, HOUR_REG, &hour); + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, MINUTE_REG, &minute); + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, SECOND_REG, &second); + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, YEAR_REG, &year); + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, MONTH_REG, &month); + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, DAY_REG, &day); + + printk(BIOS_INFO, "%s: Current date %02d.%02d.%02d %02d:%02d:%02d\n", + dev->chip_ops->name, bcd2bin(month), bcd2bin(day), + bcd2bin(year), bcd2bin(hour), bcd2bin(minute), bcd2bin(second)); +} + +static void rx6110sa_init(struct device *dev) +{ + struct drivers_i2c_rx6110sa_config *config = dev->chip_info; + uint8_t reg; + + /* Do a dummy read first. */ + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, SECOND_REG, ®); + + /* + * Check VLF-bit which indicates the RTC data loss, such as due to a + * supply voltage drop. + */ + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, FLAG_REGISTER, ®); + + if (!(reg & VLF_BIT)) + /* No voltage low detected, everything is well. */ + return; + + /* + * Voltage low detected, initialize RX6110 SA again. + * Set first some registers to known state. + */ + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, BATTERY_BACKUP_REG, 0x00); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, RESERVED_BIT_REG, RTC_INIT_VALUE); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, DIGITAL_REG, 0x00); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, IRQ_CONTROL_REG, 0x00); + + /* Clear timer enable bit and set frequency of clock output. */ + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, EXTENSION_REG, ®); + reg &= ~(FSEL_MASK | TE_BIT); + reg |= (config->cof_selection << 6); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, EXTENSION_REG, reg); + + /* Clear voltage low detect bit. */ + i2c_readb(I2C_BUS_NUM, I2C_DEV_NUM, FLAG_REGISTER, ®); + reg &= ~VLF_BIT; + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, FLAG_REGISTER, reg); + + /* Before setting the clock stop oscillator. */ + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, CTRL_REG, STOP_BIT); + if (config->set_user_date) { + /* Set user date defined in device tree. */ + printk(BIOS_DEBUG, "%s: Set to user date\n", + dev->chip_ops->name); + rx6110sa_set_user_date(dev); + } else { + /* Set date from coreboot build. */ + printk(BIOS_DEBUG, "%s: Set to coreboot build date\n", + dev->chip_ops->name); + rx6110sa_set_build_date(dev); + } + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, HOUR_REG, 1); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, MINUTE_REG, 0); + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, SECOND_REG, 0); + /* Start oscillator again as the RTC is set up now. */ + i2c_writeb(I2C_BUS_NUM, I2C_DEV_NUM, CTRL_REG, 0x00); +} + +static struct device_operations rx6110sa_ops = { + .read_resources = DEVICE_NOOP, + .set_resources = DEVICE_NOOP, + .enable_resources = DEVICE_NOOP, + .init = rx6110sa_init, + .final = rx6110sa_final +}; + +static void rx6110sa_enable(struct device *dev) +{ + dev->ops = &rx6110sa_ops; +} + +struct chip_operations drivers_i2c_rx6110sa_ops = { + CHIP_NAME("RX6110 SA") + .enable_dev = rx6110sa_enable +}; diff --git a/src/drivers/i2c/rx6110sa/rx6110sa.h b/src/drivers/i2c/rx6110sa/rx6110sa.h new file mode 100644 index 0000000000..99527e0420 --- /dev/null +++ b/src/drivers/i2c/rx6110sa/rx6110sa.h @@ -0,0 +1,59 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Siemens AG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _I2C_RX6110SA_H_ +#define _I2C_RX6110SA_H_ + +/* The address of this RTC is fixed. */ +#define RX6110SA_SLAVE_ADR 0x32 +#define RX6110SA_I2C_CONTROLLER 0 + +/* Register layout */ +#define SECOND_REG 0x10 +#define MINUTE_REG 0x11 +#define HOUR_REG 0x12 +#define WEEK_REG 0x13 +#define DAY_REG 0x14 +#define MONTH_REG 0x15 +#define YEAR_REG 0x16 +#define RESERVED_BIT_REG 0x17 +#define RTC_INIT_VALUE 0x28 +#define ALARM_MINUTE_REG 0x18 +#define ALARM_HOUR_REG 0x19 +#define ALARM_WEEKDAY_REG 0x1A +#define TMR_COUNTER_0_REG 0x1B +#define TMR_COUNTER_1_REG 0x1C +#define EXTENSION_REG 0x1D +#define TE_BIT (1 << 4) +#define FSEL_MASK 0xC0 +#define FLAG_REGISTER 0x1E +#define VLF_BIT (1 << 1) +#define CTRL_REG 0x1F +#define AIE_BIT (1 << 3) +#define TIE_BIT (1 << 4) +#define UIE_BIT (1 << 5) +#define STOP_BIT (1 << 6) +#define TEST_BIT (1 << 7) +#define DIGITAL_REG 0x30 +#define BATTERY_BACKUP_REG 0x31 +#define IRQ_CONTROL_REG 0x32 + +/* Define CLKOUT frequency divider values valid for parameter cof_selection */ +#define COF_OFF 0x00 +#define COF_1_HZ 0x01 +#define COF_1024_HZ 0x02 +#define COF_32768_HZ 0x03 + +#endif /* _I2C_RX6110SA_H_ */