summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/boot/planetcore.h
blob: d53c733cc463b16888ed96f41514d057daf486e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef _PPC_BOOT_PLANETCORE_H_
#define _PPC_BOOT_PLANETCORE_H_

#include "types.h"

#define PLANETCORE_KEY_BOARD_TYPE   "BO"
#define PLANETCORE_KEY_BOARD_REV    "BR"
#define PLANETCORE_KEY_MB_RAM       "D1"
#define PLANETCORE_KEY_MAC_ADDR     "EA"
#define PLANETCORE_KEY_FLASH_SPEED  "FS"
#define PLANETCORE_KEY_IP_ADDR      "IP"
#define PLANETCORE_KEY_KB_NVRAM     "NV"
#define PLANETCORE_KEY_PROCESSOR    "PR"
#define PLANETCORE_KEY_PROC_VARIANT "PV"
#define PLANETCORE_KEY_SERIAL_BAUD  "SB"
#define PLANETCORE_KEY_SERIAL_PORT  "SP"
#define PLANETCORE_KEY_SWITCH       "SW"
#define PLANETCORE_KEY_TEMP_OFFSET  "TC"
#define PLANETCORE_KEY_TARGET_IP    "TIP"
#define PLANETCORE_KEY_CRYSTAL_HZ   "XT"

/* Prepare the table for processing, by turning all newlines
 * into NULL bytes.
 */
void planetcore_prepare_table(char *table);

/* Return the value associated with a given key in text,
 * decimal, or hex format.
 *
 * Returns zero/NULL on failure, non-zero on success.
 */
const char *planetcore_get_key(const char *table, const char *key);
int planetcore_get_decimal(const char *table, const char *key, u64 *val);
int planetcore_get_hex(const char *table, const char *key, u64 *val);

/* Updates the device tree local-mac-address properties based
 * on the EA tag.
 */
void planetcore_set_mac_addrs(const char *table);

/* Sets the linux,stdout-path in the /chosen node.  This requires the
 * linux,planetcore-label property in each serial node.
 */
void planetcore_set_stdout_path(const char *table);

#endif
nt-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * Motorola CPCAP PMIC RTC driver
 *
 * Based on cpcap-regulator.c from Motorola Linux kernel tree
 * Copyright (C) 2009 Motorola, Inc.
 *
 * Rewritten for mainline kernel
 *  - use DT
 *  - use regmap
 *  - use standard interrupt framework
 *  - use managed device resources
 *  - remove custom "secure clock daemon" helpers
 *
 * Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/mfd/motorola-cpcap.h>
#include <linux/slab.h>
#include <linux/sched.h>

#define SECS_PER_DAY 86400
#define DAY_MASK  0x7FFF
#define TOD1_MASK 0x00FF
#define TOD2_MASK 0x01FF

struct cpcap_time {
	int day;
	int tod1;
	int tod2;
};

struct cpcap_rtc {
	struct regmap *regmap;
	struct rtc_device *rtc_dev;
	u16 vendor;
	int alarm_irq;
	bool alarm_enabled;
	int update_irq;
	bool update_enabled;
};

static void cpcap2rtc_time(struct rtc_time *rtc, struct cpcap_time *cpcap)
{
	unsigned long int tod;
	unsigned long int time;

	tod = (cpcap->tod1 & TOD1_MASK) | ((cpcap->tod2 & TOD2_MASK) << 8);
	time = tod + ((cpcap->day & DAY_MASK) * SECS_PER_DAY);

	rtc_time_to_tm(time, rtc);
}

static void rtc2cpcap_time(struct cpcap_time *cpcap, struct rtc_time *rtc)
{
	unsigned long time;

	rtc_tm_to_time(rtc, &time);

	cpcap->day = time / SECS_PER_DAY;
	time %= SECS_PER_DAY;
	cpcap->tod2 = (time >> 8) & TOD2_MASK;
	cpcap->tod1 = time & TOD1_MASK;
}

static int cpcap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
	struct cpcap_rtc *rtc = dev_get_drvdata(dev);

	if (rtc->alarm_enabled == enabled)
		return 0;

	if (enabled)
		enable_irq(rtc->alarm_irq);
	else
		disable_irq(rtc->alarm_irq);

	rtc->alarm_enabled = !!enabled;

	return 0;
}

static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
	struct cpcap_rtc *rtc;
	struct cpcap_time cpcap_tm;
	int temp_tod2;
	int ret;

	rtc = dev_get_drvdata(dev);

	ret = regmap_read(rtc->regmap, CPCAP_REG_TOD2, &temp_tod2);
	ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
	ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD1, &cpcap_tm.tod1);
	ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD2, &cpcap_tm.tod2);

	if (temp_tod2 > cpcap_tm.tod2)
		ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);

	if (ret) {
		dev_err(dev, "Failed to read time\n");
		return -EIO;
	}

	cpcap2rtc_time(tm, &cpcap_tm);

	return 0;
}

static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
	struct cpcap_rtc *rtc;
	struct cpcap_time cpcap_tm;
	int ret = 0;

	rtc = dev_get_drvdata(dev);

	rtc2cpcap_time(&cpcap_tm, tm);

	if (rtc->alarm_enabled)
		disable_irq(rtc->alarm_irq);
	if (rtc->update_enabled)
		disable_irq(rtc->update_irq);

	if (rtc->vendor == CPCAP_VENDOR_ST) {
		/* The TOD1 and TOD2 registers MUST be written in this order
		 * for the change to properly set.
		 */
		ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
					  TOD1_MASK, cpcap_tm.tod1);
		ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
					  TOD2_MASK, cpcap_tm.tod2);
		ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
					  DAY_MASK, cpcap_tm.day);
	} else {
		/* Clearing the upper lower 8 bits of the TOD guarantees that
		 * the upper half of TOD (TOD2) will not increment for 0xFF RTC
		 * ticks (255 seconds).  During this time we can safely write
		 * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be
		 * synchronized to the exact time requested upon the final write
		 * to TOD1.
		 */
		ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
					  TOD1_MASK, 0);
		ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
					  DAY_MASK, cpcap_tm.day);
		ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
					  TOD2_MASK, cpcap_tm.tod2);
		ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
					  TOD1_MASK, cpcap_tm.tod1);
	}

	if (rtc->update_enabled)
		enable_irq(rtc->update_irq);
	if (rtc->alarm_enabled)
		enable_irq(rtc->alarm_irq);

	return ret;
}

static int cpcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
	struct cpcap_rtc *rtc;
	struct cpcap_time cpcap_tm;
	int ret;

	rtc = dev_get_drvdata(dev);

	alrm->enabled = rtc->alarm_enabled;

	ret = regmap_read(rtc->regmap, CPCAP_REG_DAYA, &cpcap_tm.day);
	ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA2, &cpcap_tm.tod2);
	ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA1, &cpcap_tm.tod1);

	if (ret) {
		dev_err(dev, "Failed to read time\n");
		return -EIO;
	}

	cpcap2rtc_time(&alrm->time, &cpcap_tm);
	return rtc_valid_tm(&alrm->time);
}

static int cpcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
	struct cpcap_rtc *rtc;
	struct cpcap_time cpcap_tm;
	int ret;

	rtc = dev_get_drvdata(dev);

	rtc2cpcap_time(&cpcap_tm, &alrm->time);

	if (rtc->alarm_enabled)
		disable_irq(rtc->alarm_irq);

	ret = regmap_update_bits(rtc->regmap, CPCAP_REG_DAYA, DAY_MASK,
				 cpcap_tm.day);
	ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA2, TOD2_MASK,
				  cpcap_tm.tod2);
	ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA1, TOD1_MASK,
				  cpcap_tm.tod1);

	if (!ret) {
		enable_irq(rtc->alarm_irq);
		rtc->alarm_enabled = true;
	}

	return ret;
}

static const struct rtc_class_ops cpcap_rtc_ops = {
	.read_time		= cpcap_rtc_read_time,
	.set_time		= cpcap_rtc_set_time,
	.read_alarm		= cpcap_rtc_read_alarm,
	.set_alarm		= cpcap_rtc_set_alarm,
	.alarm_irq_enable	= cpcap_rtc_alarm_irq_enable,
};

static irqreturn_t cpcap_rtc_alarm_irq(int irq, void *data)
{
	struct cpcap_rtc *rtc = data;

	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
	return IRQ_HANDLED;
}

static irqreturn_t cpcap_rtc_update_irq(int irq, void *data)
{
	struct cpcap_rtc *rtc = data;

	rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
	return IRQ_HANDLED;
}

static int cpcap_rtc_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct cpcap_rtc *rtc;
	int err;

	rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
	if (!rtc)
		return -ENOMEM;

	rtc->regmap = dev_get_regmap(dev->parent, NULL);
	if (!rtc->regmap)
		return -ENODEV;

	platform_set_drvdata(pdev, rtc);
	rtc->rtc_dev = devm_rtc_device_register(dev, "cpcap_rtc",
						&cpcap_rtc_ops, THIS_MODULE);

	if (IS_ERR(rtc->rtc_dev))
		return PTR_ERR(rtc->rtc_dev);

	err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor);
	if (err)
		return err;

	rtc->alarm_irq = platform_get_irq(pdev, 0);
	err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
					cpcap_rtc_alar