From 3cad2de6124623169af1f84e88f2ff66b59130fc Mon Sep 17 00:00:00 2001 From: Werner Zeh Date: Wed, 13 Jul 2016 08:21:22 +0200 Subject: drivers/i2c: Add new driver for RTC type PCF8523 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver enables the usage of an external RTC chip PCF8523 which is connected to the I2C bus. The I2C address of this device is fixed. One can change parameters in device tree so that the used setup can be adapted in device tree to match the configuration of the device on the mainboard. Change-Id: I2d7e161c9e12b720ec4925f1acfd1dd8ee6ee5f5 Signed-off-by: Werner Zeh Reviewed-on: https://review.coreboot.org/15641 Tested-by: build bot (Jenkins) Reviewed-by: Kyösti Mälkki --- src/drivers/i2c/pcf8523/Kconfig | 18 +++++ src/drivers/i2c/pcf8523/Makefile.inc | 16 ++++ src/drivers/i2c/pcf8523/chip.h | 44 +++++++++++ src/drivers/i2c/pcf8523/pcf8523.c | 148 +++++++++++++++++++++++++++++++++++ src/drivers/i2c/pcf8523/pcf8523.h | 97 +++++++++++++++++++++++ 5 files changed, 323 insertions(+) create mode 100644 src/drivers/i2c/pcf8523/Kconfig create mode 100644 src/drivers/i2c/pcf8523/Makefile.inc create mode 100644 src/drivers/i2c/pcf8523/chip.h create mode 100644 src/drivers/i2c/pcf8523/pcf8523.c create mode 100644 src/drivers/i2c/pcf8523/pcf8523.h (limited to 'src/drivers') diff --git a/src/drivers/i2c/pcf8523/Kconfig b/src/drivers/i2c/pcf8523/Kconfig new file mode 100644 index 0000000000..c6d40a277b --- /dev/null +++ b/src/drivers/i2c/pcf8523/Kconfig @@ -0,0 +1,18 @@ +## +## This file is part of the coreboot project. +## +## Copyright 2016 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. +## + +config DRIVERS_I2C_PCF8523 + bool + default n diff --git a/src/drivers/i2c/pcf8523/Makefile.inc b/src/drivers/i2c/pcf8523/Makefile.inc new file mode 100644 index 0000000000..6dc6ea1383 --- /dev/null +++ b/src/drivers/i2c/pcf8523/Makefile.inc @@ -0,0 +1,16 @@ +## +## This file is part of the coreboot project. +## +## Copyright 2016 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. +## + +ramstage-$(CONFIG_DRIVERS_I2C_PCF8523) += pcf8523.c diff --git a/src/drivers/i2c/pcf8523/chip.h b/src/drivers/i2c/pcf8523/chip.h new file mode 100644 index 0000000000..13cd401201 --- /dev/null +++ b/src/drivers/i2c/pcf8523/chip.h @@ -0,0 +1,44 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2016 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 "pcf8523.h" + +struct drivers_i2c_pcf8523_config { + unsigned char cap_sel; /* Internal capacitor selection */ + unsigned char second_int_en; /* Enable IRQ for seconds */ + unsigned char alarm_int_en; /* Enable IRQ for alarm */ + unsigned char correction_int_en;/* Enable IRQ for corrections */ + unsigned char wdt_int_en; /* Enable IRQ for watchdog */ + unsigned char tmrA_int_en; /* Enable IRQ for timer A */ + unsigned char tmrB_int_en; /* Enable IRQ for timer B */ + unsigned char power_mode; /* Set up power mode */ + unsigned char bat_switch_int_en;/* Enable IRQ for battery switch */ + unsigned char bat_low_int_en; /* Enable IRQ for low battery */ + unsigned char offset_mode; /* Set up mode how to handle offset */ + unsigned char offset_val; /* Value for offset adjustment */ + unsigned char tmrA_mode; /* Operation mode of timer A */ + unsigned char tmrA_int_mode; /* IRQ mode for timer A */ + unsigned char tmrB_mode; /* Operation mode for timer B */ + unsigned char tmrB_int_mode; /* IRQ mode for timer B */ + unsigned char cof_selection; /* Set up "clock out" frequency */ + unsigned char tmrA_prescaler; /* Prescaler for timer A */ + unsigned char tmrB_prescaler; /* Prescaler for timer B */ + unsigned char tmrB_pulse_cfg; /* Pulse width config for timer B */ + unsigned char set_user_date; /* Use user date from device tree */ + unsigned char user_year; /* User year to set */ + unsigned char user_month; /* User month to set */ + unsigned char user_day; /* User day to set */ + unsigned char user_weekday; /* User weekday to set */ +}; diff --git a/src/drivers/i2c/pcf8523/pcf8523.c b/src/drivers/i2c/pcf8523/pcf8523.c new file mode 100644 index 0000000000..eb0bf25123 --- /dev/null +++ b/src/drivers/i2c/pcf8523/pcf8523.c @@ -0,0 +1,148 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2016 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 "chip.h" + +/* Set RTC date from coreboot build date. */ +static void pcf8523_set_build_date(struct device *dev) +{ + smbus_write_byte(dev, YEAR_REG, coreboot_build_date.year); + smbus_write_byte(dev, MONTH_REG, coreboot_build_date.month); + smbus_write_byte(dev, WEEKDAY_REG, coreboot_build_date.weekday); + smbus_write_byte(dev, DAY_REG, coreboot_build_date.day); +} + +/* Set RTC date from user defined date (available in e.g. device tree). */ +static void pcf8523_set_user_date(struct device *dev) +{ + struct drivers_i2c_pcf8523_config *config = dev->chip_info; + smbus_write_byte(dev, YEAR_REG, bin2bcd(config->user_year)); + smbus_write_byte(dev, MONTH_REG, bin2bcd(config->user_month)); + smbus_write_byte(dev, DAY_REG, bin2bcd(config->user_day)); + smbus_write_byte(dev, WEEKDAY_REG, bin2bcd(config->user_weekday)); +} + +static void pcf8523_final(struct device *dev) +{ + /* Read back current RTC date and time and print it to the console. */ + printk(BIOS_INFO, "%s: Current date %02d.%02d.%02d %02d:%02d:%02d\n", + dev->chip_ops->name, + bcd2bin(smbus_read_byte(dev, MONTH_REG)), + bcd2bin(smbus_read_byte(dev, DAY_REG)), + bcd2bin(smbus_read_byte(dev, YEAR_REG)), + bcd2bin(smbus_read_byte(dev, HOUR_REG)), + bcd2bin(smbus_read_byte(dev, MINUTE_REG)), + bcd2bin(smbus_read_byte(dev, SECOND_REG)) & ~OS_BIT); +} + +static void pcf8523_init(struct device *dev) +{ + struct drivers_i2c_pcf8523_config *config = dev->chip_info; + uint8_t reg = 0; + + if (!(smbus_read_byte(dev, SECOND_REG) & OS_BIT)) { + /* Set control registers to a known good value even if no + * power loss event was recognized. There were issues with + * this RTC in the past where control registers were + * corrupted and OS bit was not set. */ + reg = smbus_read_byte(dev, CTRL_REG_1); + reg &= ~(STOP_BIT | CAP_SEL); + reg |= config->cap_sel; + smbus_write_byte(dev, CTRL_REG_1, reg); + reg = smbus_read_byte(dev, CTRL_REG_3); + reg &= ~PM_MASK; + reg |= config->power_mode; + smbus_write_byte(dev, CTRL_REG_3, reg); + reg = smbus_read_byte(dev, TMR_CLKOUT_REG); + reg &= ~COF_MASK; + reg |= config->cof_selection; + smbus_write_byte(dev, TMR_CLKOUT_REG, reg); + return; + } + + /* Initialize the RTC fully only if a power-loss event was recognized. + * In this case RTC will be set up with default date and time. */ + smbus_write_byte(dev, CTRL_REG_1, ((!!config->cap_sel) << 7) | + ((!!config->second_int_en) << 2) | + ((!!config->alarm_int_en) << 1) | + (!!config->correction_int_en)); + + smbus_write_byte(dev, CTRL_REG_2, ((!!config->wdt_int_en) << 2) | + ((!!config->tmrA_int_en) << 1) | + (!!config->tmrB_int_en)); + + smbus_write_byte(dev, CTRL_REG_3, ((config->power_mode & 0x03) << 5) | + ((!!config->bat_switch_int_en) << 1) | + (!!config->bat_low_int_en)); + + smbus_write_byte(dev, OFFSET_REG, ((!!config->offset_mode) << 7) | + (config->offset_val & 0x7f)); + + smbus_write_byte(dev, TMR_CLKOUT_REG, ((!!config->tmrA_int_mode) << 7) | + ((!!config->tmrB_int_mode) << 6) | + ((config->cof_selection & 0x38) << 3) | + ((config->tmrA_mode & 0x03) << 1) | + (!!config->tmrB_mode)); + + smbus_write_byte(dev, TMR_A_FREQ_REG, (config->tmrA_prescaler & 0x7)); + + smbus_write_byte(dev, TMR_B_FREQ_REG, (config->tmrB_prescaler & 0x7) | + ((config->tmrB_pulse_cfg & 0x7) << 4)); + + /* Before setting the clock stop oscillator. */ + reg = smbus_read_byte(dev, CTRL_REG_1); + reg |= STOP_BIT; + smbus_write_byte(dev, CTRL_REG_1, reg); + 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); + pcf8523_set_user_date(dev); + } else { + /* Set date from coreboot build. */ + printk(BIOS_DEBUG, "%s: Set to coreboot build date\n", + dev->chip_ops->name); + pcf8523_set_build_date(dev); + } + /* Set time to 01:00:00 */ + smbus_write_byte(dev, HOUR_REG, 1); + smbus_write_byte(dev, MINUTE_REG, 0); + smbus_write_byte(dev, SECOND_REG, 0); + /* Start oscillator again as the clock is set up now */ + reg &= ~STOP_BIT; + smbus_write_byte(dev, CTRL_REG_1, reg); +} + +static struct device_operations pcf8523c_ops = { + .read_resources = DEVICE_NOOP, + .set_resources = DEVICE_NOOP, + .enable_resources = DEVICE_NOOP, + .init = pcf8523_init, + .final = pcf8523_final +}; + +static void pcf8523_enable(struct device *dev) +{ + dev->ops = &pcf8523c_ops; +} + +struct chip_operations drivers_i2c_pcf8523_ops = { + CHIP_NAME("PCF8523") + .enable_dev = pcf8523_enable +}; diff --git a/src/drivers/i2c/pcf8523/pcf8523.h b/src/drivers/i2c/pcf8523/pcf8523.h new file mode 100644 index 0000000000..3d863844e2 --- /dev/null +++ b/src/drivers/i2c/pcf8523/pcf8523.h @@ -0,0 +1,97 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2016 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_PCF8523_H_ +#define _I2C_PCF8523_H_ + +/* The address of this RTC is fixed. */ +#define PCF8523_SLAVE_ADR 0x68 + +/* Register layout */ +#define CTRL_REG_1 0x00 +#define STOP_BIT (1 << 5) +#define CAP_SEL (1 << 7) +#define CTRL_REG_2 0x01 +#define CTRL_REG_3 0x02 +#define PM_MASK (7 << 5) +#define SECOND_REG 0x03 +#define OS_BIT (1 << 7) +#define MINUTE_REG 0x04 +#define HOUR_REG 0x05 +#define DAY_REG 0x06 +#define WEEKDAY_REG 0x07 +#define MONTH_REG 0x08 +#define YEAR_REG 0x09 +#define ALARM_MINUTE_REG 0x0A +#define ALARM_HOUR_REG 0x0B +#define ALARM_DAY_REG 0x0C +#define ALARM_WEEKDAY_REG 0x0D +#define OFFSET_REG 0x0E +#define TMR_CLKOUT_REG 0x0F +#define COF_MASK 0x38 +#define TMR_A_FREQ_REG 0x10 +#define TMR_A_REG 0x11 +#define TMR_B_FREQ_REG 0x12 +#define TMR_B_REG 0x13 + +/* Define used capacitor modes */ +/* Valid for parameter cap_sel */ +#define CAP_SEL_7_PF 0x00 +#define CAP_SEL_12_PF 0x01 + +/* Define supported power modes */ +/* Valid for parameter power_mode */ +#define PM_BAT_SW_STD_LOW_DETECT 0x00 +#define PM_BAT_SW_DIRECT_LOW_DETECT 0x01 +#define PM_BAT_SW_OFF_LOW_DETECT 0x02 +#define PM_BAT_SW_STD_LOW_DETECT_OFF 0x04 +#define PM_BAT_SW_DIRECT_LOW_DETECT_OFF 0x05 +#define PM_BAT_SW_OFF_LOW_DETECT_OFF 0x07 + +/* Define CLKOUT frequency divider values */ +/* Valid for parameter cof_selection */ +#define COF_32768_HZ 0x00 +#define COF_16384_HZ 0x01 +#define COF_8192_HZ 0x02 +#define COF_4096_HZ 0x03 +#define COF_1024_HZ 0x04 +#define COF_32_HZ 0x05 +#define COF_1_HZ 0x06 +#define COF_OFF 0x07 + +/* Define timer A & B set up values */ +/* Valid for parameter tmrA_prescaler and tmrB_prescaler */ +#define TMR_CLK_4096_HZ 0x00 +#define TMR_CLK_64_HZ 0x01 +#define TMR_CLK_1_HZ 0x02 +#define TMR_CLK_1_60_HZ 0x03 +#define TMR_CLK_1_3600_HZ 0x07 + +/* Valid for parameter tmrA_mode and tmrB_mode */ +#define TMR_DISABLED 0x00 +#define TMR_A_MODE_COUNTDOWN 0x01 +#define TMR_A_MODE_WATCHDOG 0x02 +#define TMR_B_MODE_ENABLED 0x01 + +/* Valid for parameter tmrB_pulse_cfg */ +#define TMR_B_PULSE_WIDTH_46_MS 0x00 +#define TMR_B_PULSE_WIDTH_62_MS 0x01 +#define TMR_B_PULSE_WIDTH_78_MS 0x02 +#define TMR_B_PULSE_WIDTH_93_MS 0x03 +#define TMR_B_PULSE_WIDTH_125_MS 0x04 +#define TMR_B_PULSE_WIDTH_156_MS 0x05 +#define TMR_B_PULSE_WIDTH_187_MS 0x06 +#define TMR_B_PULSE_WIDTH_218_MS 0x07 + +#endif /* _I2C_PCF8523_H_ */ -- cgit v1.2.3