1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <errno.h> 14#include <platsupport/io.h> 15#include <platsupport/plat/pit.h> 16 17#include <inttypes.h> 18#include <stdbool.h> 19#include <stdio.h> 20#include <stdlib.h> 21 22#include <utils/util.h> 23 24#define PIT_IOPORT_CHANNEL(x) (0x40 + x) /* valid channels are 0, 1, 2. we'll be using 0 exclusively, though */ 25#define PIT_IOPORT_COMMAND 0x43 26#define PIT_IOPORT_PITCR PIT_IOPORT_COMMAND 27 28/* PIT command register macros */ 29#define PITCR_SET_CHANNEL(x, channel) (((channel) << 6) | x) 30#define PITCR_SET_OP_MODE(x, mode) (((mode) << 1) | x) 31#define PITCR_SET_ACCESS_MODE(x, mode) (((mode) << 4) | x) 32 33#define PITCR_LATCH_CHANNEL(channel) PITCR_SET_CHANNEL(0, channel) 34 35#define PITCR_ACCESS_LOW 0x1 36#define PITCR_ACCESS_HIGH 0x2 37#define PITCR_ACCESS_LOHI 0x3 38 39#define PITCR_MODE_ONESHOT 0x0 40#define PITCR_MODE_PERIODIC 0x2 41#define PITCR_MODE_SQUARE 0x3 42#define PITCR_MODE_SWSTROBE 0x4 43 44/* helper functions */ 45static inline int 46set_pit_mode(ps_io_port_ops_t *ops, uint8_t channel, uint8_t mode) 47{ 48 return ps_io_port_out(ops, PIT_IOPORT_PITCR, 1, 49 PITCR_SET_CHANNEL(PITCR_SET_OP_MODE(PITCR_SET_ACCESS_MODE(0, PITCR_ACCESS_LOHI), mode), channel)); 50} 51 52static inline int 53configure_pit(pit_t *pit, uint8_t mode, uint64_t ns) 54{ 55 56 int error; 57 58 if (ns > PIT_MAX_NS || ns < PIT_MIN_NS) { 59 ZF_LOGV("ns invalid for programming PIT %"PRIu64" <= %"PRIu64" <= %"PRIu64"\n", 60 (uint64_t)PIT_MIN_NS, ns, (uint64_t)PIT_MAX_NS); 61 return EINVAL; 62 } 63 64 uint64_t ticks = PIT_NS_TO_TICKS(ns); 65 66 /* configure correct mode */ 67 error = set_pit_mode(&pit->ops, 0, mode); 68 if (error) { 69 ZF_LOGE("ps_io_port_out failed on channel %u\n", PIT_IOPORT_CHANNEL(0)); 70 return EIO; 71 } 72 73 /* program timeout */ 74 error = ps_io_port_out(&pit->ops, PIT_IOPORT_CHANNEL(0), 1, (uint8_t) (ticks & 0xFF)); 75 if (error) { 76 ZF_LOGE("ps_io_port_out failed on channel %u\n", PIT_IOPORT_CHANNEL(0)); 77 return EIO; 78 } 79 80 error = ps_io_port_out(&pit->ops, PIT_IOPORT_CHANNEL(0), 1, (uint8_t) (ticks >> 8) & 0xFF); 81 if (error) { 82 ZF_LOGE("ps_io_port_out failed on channel %u\n", PIT_IOPORT_CHANNEL(0)); 83 return EIO; 84 } 85 86 return 0; 87} 88 89/* interface functions */ 90 91int pit_cancel_timeout(pit_t *pit) 92{ 93 /* There's no way to disable the PIT, so we set it up in mode 0 and don't 94 * start it 95 */ 96 return set_pit_mode(&pit->ops, 0, PITCR_MODE_ONESHOT); 97} 98 99uint64_t pit_get_time(pit_t *pit) 100{ 101 int error = ps_io_port_out(&pit->ops, PIT_IOPORT_CHANNEL(3), 1, 0); 102 if (error) { 103 return 0; 104 } 105 106 uint32_t low, high; 107 108 /* Read the low 8 bits of the current timer value. */ 109 error = ps_io_port_in(&pit->ops, PIT_IOPORT_CHANNEL(0), 1, &low); 110 if (error) { 111 return 0; 112 } 113 114 /* Read the high 8 bits of the current timer value. */ 115 error = ps_io_port_in(&pit->ops, PIT_IOPORT_CHANNEL(0), 1, &high); 116 if (error) { 117 return 0; 118 } 119 120 /* Assemble the high and low 8 bits using (high << 8) + low, and then convert to nanoseconds. */ 121 return ((high << 8) + low) * NS_IN_S / TICKS_PER_SECOND; 122} 123 124int pit_set_timeout(pit_t *pit, uint64_t ns, bool periodic) 125{ 126 uint32_t mode = periodic ? PITCR_MODE_PERIODIC : PITCR_MODE_ONESHOT; 127 return configure_pit(pit, mode, ns); 128} 129 130/* initialisation function */ 131int pit_init(pit_t *pit, ps_io_port_ops_t io_port_ops) 132{ 133 if (pit == NULL) { 134 return EINVAL; 135 } 136 137 /* check that the io port ops are at least implemented as these are absolutely required */ 138 if (!io_port_ops.io_port_in_fn || !io_port_ops.io_port_out_fn) { 139 return EINVAL; 140 } 141 142 pit->ops = io_port_ops; 143 return 0; 144} 145