clpssoc.c revision 1.2
1/* $NetBSD: clpssoc.c,v 1.2 2021/04/24 23:36:26 thorpej Exp $ */ 2/* 3 * Copyright (c) 2013 KIYOHARA Takashi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__KERNEL_RCSID(0, "$NetBSD: clpssoc.c,v 1.2 2021/04/24 23:36:26 thorpej Exp $"); 30 31#include "opt_com.h" 32 33#define _INTR_PRIVATE 34 35#include <sys/param.h> 36#include <sys/bus.h> 37#include <sys/device.h> 38#include <sys/errno.h> 39#include <sys/kernel.h> 40#include <sys/systm.h> 41#include <sys/timetc.h> 42#include <sys/types.h> 43 44#include <arm/mainbus/mainbus.h> 45#include <arm/pic/picvar.h> 46 47#include <machine/epoc32.h> 48#include <machine/limits.h> 49 50#include <arm/clps711x/clps711xreg.h> 51#include <arm/clps711x/clpssocvar.h> 52 53#include "locators.h" 54 55 56static int clpssoc_match(device_t, cfdata_t, void *); 57static void clpssoc_attach(device_t parent, device_t self, void *aux); 58 59static int clpssoc_print(void *, const char *); 60static int clpssoc_submatch(device_t, cfdata_t, const int *, void *); 61 62static int clpssoc_find_pending_irqs(void); 63 64static void clpssoc_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t); 65static void clpssoc_pic_block_irqs(struct pic_softc *, size_t, uint32_t); 66static void clpssoc_pic_establish_irq(struct pic_softc *, struct intrsource *); 67 68#define INTSR (*((volatile uint16_t *)(ARM7XX_INTRREG_VBASE + PS711X_INTSR))) 69#define INTMR (*((volatile uint16_t *)(ARM7XX_INTRREG_VBASE + PS711X_INTMR))) 70#define SYSCON (*((volatile uint32_t *)(ARM7XX_INTRREG_VBASE + PS711X_SYSCON))) 71#define TC1D (*((volatile uint16_t *)(ARM7XX_INTRREG_VBASE + PS711X_TC1D))) 72#define TC2D (*((volatile uint16_t *)(ARM7XX_INTRREG_VBASE + PS711X_TC2D))) 73static void clpssoc_initclocks(void); 74static int clpssoc_clockintr(void *); 75static void clpssoc_tc_init(void); 76static u_int clpssoc_get_timecount(struct timecounter *); 77 78static void clpssoc_delay(unsigned int); 79 80CFATTACH_DECL_NEW(clpssoc, 0, clpssoc_match, clpssoc_attach, NULL, NULL); 81 82static const struct { 83 const char *name; 84 int irq[3]; 85} clpssoc_periphs[] = { 86 { "clpsaudio", {CLPSSOCCF_IRQ_DEFAULT} }, 87 { "clpscom", {12, 13, 14} }, 88 { "clpskbd", {CLPSSOCCF_IRQ_DEFAULT} }, 89 { "clpslcd", {CLPSSOCCF_IRQ_DEFAULT} }, 90 { "clpspm", {CLPSSOCCF_IRQ_DEFAULT} }, 91 { "clpsrtc", {10, CLPSSOCCF_IRQ_DEFAULT} }, 92}; 93 94extern struct bus_space clps711x_bs_tag; 95 96static struct pic_ops clpssoc_picops = { 97 .pic_unblock_irqs = clpssoc_pic_unblock_irqs, 98 .pic_block_irqs = clpssoc_pic_block_irqs, 99 .pic_establish_irq = clpssoc_pic_establish_irq, 100}; 101static struct pic_softc clpssoc_pic = { 102 .pic_ops = &clpssoc_picops, 103 .pic_maxsources = 16, 104 .pic_name = "clpssoc", 105}; 106 107/* ARGSUSED */ 108static int 109clpssoc_match(device_t parent, cfdata_t match, void *aux) 110{ 111 112 return 1; 113} 114 115/* ARGSUSED */ 116static void 117clpssoc_attach(device_t parent, device_t self, void *aux) 118{ 119 struct mainbus_attach_args *maa = aux; 120 struct clpssoc_attach_args aa; 121 bus_space_handle_t ioh; 122 uint32_t sysflg; 123 int i, j; 124 125 if (bus_space_map(&clps711x_bs_tag, maa->mb_iobase, ARM7XX_INTRREG_SIZE, 126 0, &ioh) != 0) { 127 aprint_error_dev(self, "can't map registers\n"); 128 return; 129 } 130 sysflg = bus_space_read_4(&clps711x_bs_tag, ioh, PS711X_SYSFLG); 131 aprint_normal(": CL PS-711x rev %d\n", SYSFLG_VERID(sysflg)); 132 aprint_naive("\n"); 133 134 INTMR = 0; 135 136 pic_add(&clpssoc_pic, 0); 137 soc_find_pending_irqs = clpssoc_find_pending_irqs; 138 soc_initclocks = clpssoc_initclocks; 139 soc_delay = clpssoc_delay; 140 141 for (i = 0; i < __arraycount(clpssoc_periphs); i++) { 142 aa.aa_name = clpssoc_periphs[i].name; 143 aa.aa_iot = &clps711x_bs_tag; 144 aa.aa_ioh = &ioh; 145 for (j = 0; j < __arraycount(clpssoc_periphs[i].irq); j++) 146 aa.aa_irq[j] = clpssoc_periphs[i].irq[j]; 147 148 config_found(self, &aa, clpssoc_print, 149 CFARG_SUBMATCH, clpssoc_submatch, 150 CFARG_EOL); 151 } 152} 153 154static int 155clpssoc_print(void *aux, const char *pnp) 156{ 157 struct clpssoc_attach_args *aa = aux; 158 int i; 159 160 if (pnp) 161 aprint_normal("%s at %s", aa->aa_name, pnp); 162 else 163 if (aa->aa_irq[0] != CLPSSOCCF_IRQ_DEFAULT) { 164 aprint_normal(" irq %d", aa->aa_irq[0]); 165 for (i = 1; i < __arraycount(aa->aa_irq); i++) { 166 if (aa->aa_irq[i] == CLPSSOCCF_IRQ_DEFAULT) 167 break; 168 aprint_normal(",%d", aa->aa_irq[i]); 169 } 170 } 171 172 return UNCONF; 173} 174 175/* ARGSUSED */ 176static int 177clpssoc_submatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 178{ 179 struct clpssoc_attach_args *aa = aux; 180 181 if (strcmp(aa->aa_name, "clpskbd") == 0 && 182 strcmp(cf->cf_name + strlen(cf->cf_name) - 3, "kbd") == 0) 183 return config_match(parent, cf, aux); 184 185 if (strcmp(cf->cf_name, aa->aa_name) != 0) 186 return 0; 187 return config_match(parent, cf, aux); 188} 189 190 191static int 192clpssoc_find_pending_irqs(void) 193{ 194 uint16_t pending; 195 196 pending = INTSR; 197 pending &= INTMR; 198 if (pending == 0) 199 return 0; 200 201 return pic_mark_pending_sources(&clpssoc_pic, 0, pending); 202} 203 204/* ARGSUSED */ 205static void 206clpssoc_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase, 207 uint32_t irq_mask) 208{ 209 210 INTMR |= irq_mask; 211} 212 213/* ARGSUSED */ 214static void 215clpssoc_pic_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask) 216{ 217 218 INTMR &= ~irq_mask; 219} 220 221static void 222clpssoc_pic_establish_irq(struct pic_softc *pic, struct intrsource *is) 223{ 224 /* Nothing */ 225} 226 227static void 228clpssoc_initclocks(void) 229{ 230 231 /* set prescale mode to TC1 and free-running mode TC2 */ 232 SYSCON |= (SYSCON_TC1M | SYSCON_TC1S | SYSCON_TC2S); 233 TC1D = 512 * 1000 / hz - 1; /* 512kHz / hz - 1 */ 234 intr_establish(IRQ_TC1DI, IPL_CLOCK, 0, clpssoc_clockintr, NULL); 235 236 clpssoc_tc_init(); 237} 238 239static int 240clpssoc_clockintr(void *arg) 241{ 242 243 *(volatile uint32_t *)(ARM7XX_INTRREG_VBASE + PS711X_TC1EOI) = 1; 244 245 hardclock(arg); 246 247 return 1; 248} 249 250static void 251clpssoc_tc_init(void) 252{ 253 static struct timecounter clpssoc_tc = { 254 .tc_get_timecount = clpssoc_get_timecount, 255 .tc_counter_mask = UINT16_MAX, 256 .tc_frequency = 512000, 257 .tc_name = "clpssoc", 258 .tc_quality = 100, 259 }; 260 261 tc_init(&clpssoc_tc); 262} 263 264static u_int 265clpssoc_get_timecount(struct timecounter *tc) 266{ 267 268 return TC2D ^ UINT16_MAX; /* It is decremental counter */ 269} 270 271static void 272clpssoc_delay(unsigned int us) 273{ 274 int prev, now, remaining; 275 276 prev = TC2D & UINT16_MAX; 277 remaining = us * 512 / 1000 + 1; 278 279 while (remaining > 0) { 280 now = TC2D & UINT16_MAX; 281 if (now >= prev) 282 remaining -= (now - prev); 283 else 284 remaining -= (UINT16_MAX - now + prev + 1); 285 prev = now; 286 } 287} 288