imx6_anatop.c revision 266348
1/*- 2 * Copyright (c) 2013 Ian Lepore <ian@freebsd.org> 3 * Copyright (c) 2014 Steven Lawrance <stl@koffein.net> 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 AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/10/sys/arm/freescale/imx/imx6_anatop.c 266348 2014-05-17 20:22:22Z ian $"); 30 31/* 32 * Analog PLL and power regulator driver for Freescale i.MX6 family of SoCs. 33 * Also, temperature montoring and cpu frequency control. It was Freescale who 34 * kitchen-sinked this device, not us. :) 35 * 36 * We don't really do anything with analog PLLs, but the registers for 37 * controlling them belong to the same block as the power regulator registers. 38 * Since the newbus hierarchy makes it hard for anyone other than us to get at 39 * them, we just export a couple public functions to allow the imx6 CCM clock 40 * driver to read and write those registers. 41 * 42 * We also don't do anything about power regulation yet, but when the need 43 * arises, this would be the place for that code to live. 44 * 45 * I have no idea where the "anatop" name comes from. It's in the standard DTS 46 * source describing i.MX6 SoCs, and in the linux and u-boot code which comes 47 * from Freescale, but it's not in the SoC manual. 48 * 49 * Note that temperature values throughout this code are handled in two types of 50 * units. Items with '_cnt' in the name use the hardware temperature count 51 * units (higher counts are lower temperatures). Items with '_val' in the name 52 * are deci-Celcius, which are converted to/from deci-Kelvins in the sysctl 53 * handlers (dK is the standard unit for temperature in sysctl). 54 */ 55 56#include <sys/param.h> 57#include <sys/systm.h> 58#include <sys/callout.h> 59#include <sys/kernel.h> 60#include <sys/sysctl.h> 61#include <sys/module.h> 62#include <sys/bus.h> 63#include <sys/rman.h> 64 65#include <dev/ofw/ofw_bus.h> 66#include <dev/ofw/ofw_bus_subr.h> 67 68#include <machine/bus.h> 69#include <machine/fdt.h> 70 71#include <arm/arm/mpcore_timervar.h> 72#include <arm/freescale/fsl_ocotpreg.h> 73#include <arm/freescale/fsl_ocotpvar.h> 74#include <arm/freescale/imx/imx6_anatopreg.h> 75#include <arm/freescale/imx/imx6_anatopvar.h> 76 77static struct resource_spec imx6_anatop_spec[] = { 78 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 79 { SYS_RES_IRQ, 0, RF_ACTIVE }, 80 { -1, 0 } 81}; 82#define MEMRES 0 83#define IRQRES 1 84 85struct imx6_anatop_softc { 86 device_t dev; 87 struct resource *res[2]; 88 uint32_t cpu_curhz; 89 uint32_t cpu_curmhz; 90 uint32_t cpu_curmv; 91 uint32_t cpu_minhz; 92 uint32_t cpu_minmv; 93 uint32_t cpu_maxhz; 94 uint32_t cpu_maxmv; 95 uint32_t refosc_hz; 96 void *temp_intrhand; 97 uint32_t temp_high_val; 98 uint32_t temp_high_cnt; 99 uint32_t temp_last_cnt; 100 uint32_t temp_room_cnt; 101 struct callout temp_throttle_callout; 102 sbintime_t temp_throttle_delay; 103 uint32_t temp_throttle_reset_cnt; 104 uint32_t temp_throttle_trigger_cnt; 105 uint32_t temp_throttle_val; 106}; 107 108static struct imx6_anatop_softc *imx6_anatop_sc; 109 110/* 111 * Tables of CPU max frequencies and corresponding voltages. This is indexed by 112 * the max frequency value (0-3) from the ocotp CFG3 register. 113 */ 114static uint32_t imx6_cpu_maxhz_tab[] = { 115 792000000, 852000000, 996000000, 1200000000 116}; 117static uint32_t imx6_cpu_millivolt_tab[] = { 118 1150, 1225, 1225, 1275 119}; 120 121#define TZ_ZEROC 2732 /* deci-Kelvin <-> deci-Celcius offset. */ 122 123uint32_t 124imx6_anatop_read_4(bus_size_t offset) 125{ 126 127 KASSERT(imx6_anatop_sc != NULL, ("imx6_anatop_read_4 sc NULL")); 128 129 return (bus_read_4(imx6_anatop_sc->res[MEMRES], offset)); 130} 131 132void 133imx6_anatop_write_4(bus_size_t offset, uint32_t value) 134{ 135 136 KASSERT(imx6_anatop_sc != NULL, ("imx6_anatop_write_4 sc NULL")); 137 138 bus_write_4(imx6_anatop_sc->res[MEMRES], offset, value); 139} 140 141static void 142vdd_set(struct imx6_anatop_softc *sc, int mv) 143{ 144 int newtarg, oldtarg; 145 uint32_t delay, pmureg; 146 static boolean_t init_done = false; 147 148 /* 149 * The datasheet says VDD_PU and VDD_SOC must be equal, and VDD_ARM 150 * can't be more than 50mV above or 200mV below them. For now to keep 151 * things simple we set all three to the same value. 152 */ 153 154 pmureg = imx6_anatop_read_4(IMX6_ANALOG_PMU_REG_CORE); 155 oldtarg = pmureg & IMX6_ANALOG_PMU_REG0_TARG_MASK; 156 157 /* Convert mV to target value. Clamp target to valid range. */ 158 if (mv < 725) 159 newtarg = 0x00; 160 else if (mv > 1450) 161 newtarg = 0x1F; 162 else 163 newtarg = (mv - 700) / 25; 164 165 /* 166 * The first time through the 3 voltages might not be equal so use a 167 * long conservative delay. After that we need to delay 3uS for every 168 * 25mV step upward. No need to delay at all when lowering. 169 */ 170 if (init_done) { 171 if (newtarg == oldtarg) 172 return; 173 else if (newtarg > oldtarg) 174 delay = (newtarg - oldtarg) * 3; 175 else 176 delay = 0; 177 } else { 178 delay = 700 / 25 * 3; 179 init_done = true; 180 } 181 182 /* 183 * Make the change and wait for it to take effect. 184 */ 185 pmureg &= ~(IMX6_ANALOG_PMU_REG0_TARG_MASK | 186 IMX6_ANALOG_PMU_REG1_TARG_MASK | 187 IMX6_ANALOG_PMU_REG2_TARG_MASK); 188 189 pmureg |= newtarg << IMX6_ANALOG_PMU_REG0_TARG_SHIFT; 190 pmureg |= newtarg << IMX6_ANALOG_PMU_REG1_TARG_SHIFT; 191 pmureg |= newtarg << IMX6_ANALOG_PMU_REG2_TARG_SHIFT; 192 193 imx6_anatop_write_4(IMX6_ANALOG_PMU_REG_CORE, pmureg); 194 DELAY(delay); 195 sc->cpu_curmv = newtarg * 25 + 700; 196 device_printf(sc->dev, "voltage set to %u\n", sc->cpu_curmv); 197} 198 199static inline uint32_t 200cpufreq_hz_from_div(struct imx6_anatop_softc *sc, uint32_t div) 201{ 202 203 return (sc->refosc_hz * (div / 2)); 204} 205 206static inline uint32_t 207cpufreq_hz_to_div(struct imx6_anatop_softc *sc, uint32_t cpu_hz) 208{ 209 210 return (cpu_hz / (sc->refosc_hz / 2)); 211} 212 213static inline uint32_t 214cpufreq_actual_hz(struct imx6_anatop_softc *sc, uint32_t cpu_hz) 215{ 216 217 return (cpufreq_hz_from_div(sc, cpufreq_hz_to_div(sc, cpu_hz))); 218} 219 220static void 221cpufreq_set_clock(struct imx6_anatop_softc * sc, uint32_t cpu_newhz) 222{ 223 uint32_t div, timeout, wrk32; 224 const uint32_t mindiv = 54; 225 const uint32_t maxdiv = 108; 226 227 /* 228 * Clip the requested frequency to the configured max, then clip the 229 * resulting divisor to the documented min/max values. 230 */ 231 cpu_newhz = min(cpu_newhz, sc->cpu_maxhz); 232 div = cpufreq_hz_to_div(sc, cpu_newhz); 233 if (div < mindiv) 234 div = mindiv; 235 else if (div > maxdiv) 236 div = maxdiv; 237 sc->cpu_curhz = cpufreq_hz_from_div(sc, div); 238 sc->cpu_curmhz = sc->cpu_curhz / 1000000; 239 240 /* 241 * I can't find a documented procedure for changing the ARM PLL divisor, 242 * but some trial and error came up with this: 243 * - Set the bypass clock source to REF_CLK_24M (source #0). 244 * - Set the PLL into bypass mode; cpu should now be running at 24mhz. 245 * - Change the divisor. 246 * - Wait for the LOCK bit to come on; it takes ~50 loop iterations. 247 * - Turn off bypass mode; cpu should now be running at cpu_newhz. 248 */ 249 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR, 250 IMX6_ANALOG_CCM_PLL_ARM_CLK_SRC_MASK); 251 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_SET, 252 IMX6_ANALOG_CCM_PLL_ARM_BYPASS); 253 254 wrk32 = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM); 255 wrk32 &= ~IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK; 256 wrk32 |= div; 257 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM, wrk32); 258 259 timeout = 10000; 260 while ((imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) & 261 IMX6_ANALOG_CCM_PLL_ARM_LOCK) == 0) 262 if (--timeout == 0) 263 panic("imx6_set_cpu_clock(): PLL never locked"); 264 265 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR, 266 IMX6_ANALOG_CCM_PLL_ARM_BYPASS); 267 268 arm_tmr_change_frequency(sc->cpu_curhz / 2); 269} 270 271static void 272cpufreq_initialize(struct imx6_anatop_softc *sc) 273{ 274 uint32_t cfg3speed; 275 struct sysctl_ctx_list *ctx; 276 277 ctx = device_get_sysctl_ctx(sc->dev); 278 SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), 279 OID_AUTO, "cpu_mhz", CTLFLAG_RD, &sc->cpu_curmhz, 0, 280 "CPU frequency in MHz"); 281 282 /* 283 * XXX 24mhz shouldn't be hard-coded, should get this from imx6_ccm 284 * (even though in the real world it will always be 24mhz). Oh wait a 285 * sec, I never wrote imx6_ccm. 286 */ 287 sc->refosc_hz = 24000000; 288 289 /* 290 * Get the maximum speed this cpu can be set to. The values in the 291 * OCOTP CFG3 register are not documented in the reference manual. 292 * The following info was in an archived email found via web search: 293 * - 2b'11: 1200000000Hz; 294 * - 2b'10: 996000000Hz; 295 * - 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz. 296 * - 2b'00: 792000000Hz; 297 */ 298 cfg3speed = (fsl_ocotp_read_4(FSL_OCOTP_CFG3) & 299 FSL_OCOTP_CFG3_SPEED_MASK) >> FSL_OCOTP_CFG3_SPEED_SHIFT; 300 301 sc->cpu_minhz = cpufreq_actual_hz(sc, imx6_cpu_maxhz_tab[0]); 302 sc->cpu_minmv = imx6_cpu_millivolt_tab[0]; 303 sc->cpu_maxhz = cpufreq_actual_hz(sc, imx6_cpu_maxhz_tab[cfg3speed]); 304 sc->cpu_maxmv = imx6_cpu_millivolt_tab[cfg3speed]; 305 306 /* 307 * Set the CPU to maximum speed. 308 * 309 * We won't have thermal throttling until interrupts are enabled, but we 310 * want to run at full speed through all the device init stuff. This 311 * basically assumes that a single core can't overheat before interrupts 312 * are enabled; empirical testing shows that to be a safe assumption. 313 */ 314 vdd_set(sc, sc->cpu_maxmv); 315 cpufreq_set_clock(sc, sc->cpu_maxhz); 316 device_printf(sc->dev, "CPU frequency %uMHz\n", sc->cpu_curmhz); 317} 318 319static inline uint32_t 320temp_from_count(struct imx6_anatop_softc *sc, uint32_t count) 321{ 322 323 return (((sc->temp_high_val - (count - sc->temp_high_cnt) * 324 (sc->temp_high_val - 250) / 325 (sc->temp_room_cnt - sc->temp_high_cnt)))); 326} 327 328static inline uint32_t 329temp_to_count(struct imx6_anatop_softc *sc, uint32_t temp) 330{ 331 332 return ((sc->temp_room_cnt - sc->temp_high_cnt) * 333 (sc->temp_high_val - temp) / (sc->temp_high_val - 250) + 334 sc->temp_high_cnt); 335} 336 337static void 338temp_update_count(struct imx6_anatop_softc *sc) 339{ 340 uint32_t val; 341 342 val = imx6_anatop_read_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0); 343 if (!(val & IMX6_ANALOG_TEMPMON_TEMPSENSE0_VALID)) 344 return; 345 sc->temp_last_cnt = 346 (val & IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_MASK) >> 347 IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT; 348} 349 350static int 351temp_sysctl_handler(SYSCTL_HANDLER_ARGS) 352{ 353 struct imx6_anatop_softc *sc = arg1; 354 uint32_t t; 355 356 temp_update_count(sc); 357 358 t = temp_from_count(sc, sc->temp_last_cnt) + TZ_ZEROC; 359 360 return (sysctl_handle_int(oidp, &t, 0, req)); 361} 362 363static int 364temp_throttle_sysctl_handler(SYSCTL_HANDLER_ARGS) 365{ 366 struct imx6_anatop_softc *sc = arg1; 367 int err; 368 uint32_t temp; 369 370 temp = sc->temp_throttle_val + TZ_ZEROC; 371 err = sysctl_handle_int(oidp, &temp, 0, req); 372 if (temp < TZ_ZEROC) 373 return (ERANGE); 374 temp -= TZ_ZEROC; 375 if (err != 0 || req->newptr == NULL || temp == sc->temp_throttle_val) 376 return (err); 377 378 /* Value changed, update counts in softc and hardware. */ 379 sc->temp_throttle_val = temp; 380 sc->temp_throttle_trigger_cnt = temp_to_count(sc, sc->temp_throttle_val); 381 sc->temp_throttle_reset_cnt = temp_to_count(sc, sc->temp_throttle_val - 100); 382 imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0_CLR, 383 IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_MASK); 384 imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0_SET, 385 (sc->temp_throttle_trigger_cnt << 386 IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT)); 387 return (err); 388} 389 390static void 391tempmon_gofast(struct imx6_anatop_softc *sc) 392{ 393 394 if (sc->cpu_curhz < sc->cpu_maxhz) { 395 vdd_set(sc, sc->cpu_maxmv); 396 cpufreq_set_clock(sc, sc->cpu_maxhz); 397 } 398} 399 400static void 401tempmon_goslow(struct imx6_anatop_softc *sc) 402{ 403 404 if (sc->cpu_curhz > sc->cpu_minhz) { 405 cpufreq_set_clock(sc, sc->cpu_minhz); 406 vdd_set(sc, sc->cpu_minmv); 407 } 408} 409 410static int 411tempmon_intr(void *arg) 412{ 413 struct imx6_anatop_softc *sc = arg; 414 415 /* 416 * XXX Note that this code doesn't currently run (for some mysterious 417 * reason we just never get an interrupt), so the real monitoring is 418 * done by tempmon_throttle_check(). 419 */ 420 tempmon_goslow(sc); 421 /* XXX Schedule callout to speed back up eventually. */ 422 return (FILTER_HANDLED); 423} 424 425static void 426tempmon_throttle_check(void *arg) 427{ 428 struct imx6_anatop_softc *sc = arg; 429 430 /* Lower counts are higher temperatures. */ 431 if (sc->temp_last_cnt < sc->temp_throttle_trigger_cnt) 432 tempmon_goslow(sc); 433 else if (sc->temp_last_cnt > (sc->temp_throttle_reset_cnt)) 434 tempmon_gofast(sc); 435 436 callout_reset_sbt(&sc->temp_throttle_callout, sc->temp_throttle_delay, 437 0, tempmon_throttle_check, sc, 0); 438 439} 440 441static void 442initialize_tempmon(struct imx6_anatop_softc *sc) 443{ 444 uint32_t cal; 445 struct sysctl_ctx_list *ctx; 446 447 /* 448 * Fetch calibration data: a sensor count at room temperature (25C), 449 * a sensor count at a high temperature, and that temperature 450 */ 451 cal = fsl_ocotp_read_4(FSL_OCOTP_ANA1); 452 sc->temp_room_cnt = (cal & 0xFFF00000) >> 20; 453 sc->temp_high_cnt = (cal & 0x000FFF00) >> 8; 454 sc->temp_high_val = (cal & 0x000000FF) * 10; 455 456 /* 457 * Throttle to a lower cpu freq at 10C below the "hot" temperature, and 458 * reset back to max cpu freq at 5C below the trigger. 459 */ 460 sc->temp_throttle_val = sc->temp_high_val - 100; 461 sc->temp_throttle_trigger_cnt = 462 temp_to_count(sc, sc->temp_throttle_val); 463 sc->temp_throttle_reset_cnt = 464 temp_to_count(sc, sc->temp_throttle_val - 50); 465 466 /* 467 * Set the sensor to sample automatically at 16Hz (32.768KHz/0x800), set 468 * the throttle count, and begin making measurements. 469 */ 470 imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE1, 0x0800); 471 imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0, 472 (sc->temp_throttle_trigger_cnt << 473 IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT) | 474 IMX6_ANALOG_TEMPMON_TEMPSENSE0_MEASURE); 475 476 /* 477 * XXX Note that the alarm-interrupt feature isn't working yet, so 478 * we'll use a callout handler to check at 10Hz. Make sure we have an 479 * initial temperature reading before starting up the callouts so we 480 * don't get a bogus reading of zero. 481 */ 482 while (sc->temp_last_cnt == 0) 483 temp_update_count(sc); 484 sc->temp_throttle_delay = 100 * SBT_1MS; 485 callout_init(&sc->temp_throttle_callout, 0); 486 callout_reset_sbt(&sc->temp_throttle_callout, sc->temp_throttle_delay, 487 0, tempmon_throttle_check, sc, 0); 488 489 ctx = device_get_sysctl_ctx(sc->dev); 490 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), 491 OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, 492 temp_sysctl_handler, "IK", "Current die temperature"); 493 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), 494 OID_AUTO, "throttle_temperature", CTLTYPE_INT | CTLFLAG_RW, sc, 495 0, temp_throttle_sysctl_handler, "IK", 496 "Throttle CPU when exceeding this temperature"); 497} 498 499static int 500imx6_anatop_detach(device_t dev) 501{ 502 503 return (EBUSY); 504} 505 506static int 507imx6_anatop_attach(device_t dev) 508{ 509 struct imx6_anatop_softc *sc; 510 int err; 511 512 sc = device_get_softc(dev); 513 sc->dev = dev; 514 515 /* Allocate bus_space resources. */ 516 if (bus_alloc_resources(dev, imx6_anatop_spec, sc->res)) { 517 device_printf(dev, "Cannot allocate resources\n"); 518 err = ENXIO; 519 goto out; 520 } 521 522 err = bus_setup_intr(dev, sc->res[IRQRES], INTR_TYPE_MISC | INTR_MPSAFE, 523 tempmon_intr, NULL, sc, &sc->temp_intrhand); 524 if (err != 0) 525 goto out; 526 527 SYSCTL_ADD_UINT(device_get_sysctl_ctx(sc->dev), 528 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), 529 OID_AUTO, "cpu_voltage", CTLFLAG_RD, 530 &sc->cpu_curmv, 0, "Current CPU voltage in millivolts"); 531 532 imx6_anatop_sc = sc; 533 534 /* 535 * Other code seen on the net sets this SELFBIASOFF flag around the same 536 * time the temperature sensor is set up, although it's unclear how the 537 * two are related (if at all). 538 */ 539 imx6_anatop_write_4(IMX6_ANALOG_PMU_MISC0_SET, 540 IMX6_ANALOG_PMU_MISC0_SELFBIASOFF); 541 542 cpufreq_initialize(sc); 543 initialize_tempmon(sc); 544 545 err = 0; 546 547out: 548 549 if (err != 0) { 550 bus_release_resources(dev, imx6_anatop_spec, sc->res); 551 } 552 553 return (err); 554} 555 556static int 557imx6_anatop_probe(device_t dev) 558{ 559 560 if (!ofw_bus_status_okay(dev)) 561 return (ENXIO); 562 563 if (ofw_bus_is_compatible(dev, "fsl,imx6q-anatop") == 0) 564 return (ENXIO); 565 566 device_set_desc(dev, "Freescale i.MX6 Analog PLLs and Power"); 567 568 return (BUS_PROBE_DEFAULT); 569} 570 571uint32_t 572imx6_get_cpu_clock() 573{ 574 uint32_t div; 575 576 div = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) & 577 IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK; 578 return (cpufreq_hz_from_div(imx6_anatop_sc, div)); 579} 580 581static device_method_t imx6_anatop_methods[] = { 582 /* Device interface */ 583 DEVMETHOD(device_probe, imx6_anatop_probe), 584 DEVMETHOD(device_attach, imx6_anatop_attach), 585 DEVMETHOD(device_detach, imx6_anatop_detach), 586 587 DEVMETHOD_END 588}; 589 590static driver_t imx6_anatop_driver = { 591 "imx6_anatop", 592 imx6_anatop_methods, 593 sizeof(struct imx6_anatop_softc) 594}; 595 596static devclass_t imx6_anatop_devclass; 597 598DRIVER_MODULE(imx6_anatop, simplebus, imx6_anatop_driver, imx6_anatop_devclass, 0, 0); 599 600