Hi Anton,
I finally got HTC Apache up and going with 2.6.19, and I was able to
test your patch.
It seems to work great!
In the past, the device would lockup when I enabled cpu-freq. So far,
everything is stable with your patch.
-Kevin
On Wed, Dec 06, 2006 at 07:28:03PM +0300, Anton Vorontsov wrote:
> Hi all,
>
> It seems like we already have 3 devices which have MAX158xx PMICs:
> a730, hx4700 and some HTC one. I think it's time to unify things.
> Patch with mach-unspecific driver attached. Tested on hx4700.
>
> I'm asking for comments and suggestions, especially about files
> names and placement. :-)
>
> And...
>
> Michal, I see that a730 not using sequencer, but fully capable I2C
> controller. Is it mandatory for the a730, or you've just using it
> because it's a bit faster (for sending one byte :-). If it's really
> mandatory for the a730, then we should write three another drivers:
>
> - drivers/i2c/busses/i2c-pwr-pxa.c (using drivers/i2c/busses/i2c-pxa.c
> as reference)
> - drivers/i2c/chips/max158xx.c
> - and some glue driver, which using two above plus hooks onto cpufreq
> notifier.
>
> I see no need for doing this if solution with less entropy exists.
>
> Thanks!
>
> -- Anton (irc: bd2)
> Index: arch/arm/mach-pxa/Kconfig
> ===================================================================
> RCS file: /cvs/linux/kernel26/arch/arm/mach-pxa/Kconfig,v
> retrieving revision 1.73
> diff -u -p -r1.73 Kconfig
> --- arch/arm/mach-pxa/Kconfig 21 Nov 2006 03:13:34 -0000 1.73
> +++ arch/arm/mach-pxa/Kconfig 6 Dec 2006 14:50:26 -0000
> @@ -181,4 +181,12 @@ config PXA_SUSPEND_SAVE_EXTRA_REGS
> to work around certain bootloaders overwriting them
> during resume.
>
> +config PXA27x_VOLTAGE
> + tristate "Support PXA27x CPU Core Voltage Change Sequencer"
> + depends PXA27x && CPU_FREQ_PXA
> +
> +config PXA27x_VOLTAGE_MAX1586B
> + tristate "MAX1586B PMIC"
> + depends on PXA27x_VOLTAGE
> +
> endif
> Index: arch/arm/mach-pxa/Makefile
> ===================================================================
> RCS file: /cvs/linux/kernel26/arch/arm/mach-pxa/Makefile,v
> retrieving revision 1.73
> diff -u -p -r1.73 Makefile
> --- arch/arm/mach-pxa/Makefile 12 Nov 2006 15:44:12 -0000 1.73
> +++ arch/arm/mach-pxa/Makefile 6 Dec 2006 14:50:26 -0000
> @@ -67,3 +67,6 @@ obj-$(CONFIG_PM) += standby.o
> endif
>
> obj-$(CONFIG_DMABOUNCE) += dma_needs_bounce.o
> +
> +obj-$(CONFIG_PXA27x_VOLTAGE) += pxa27x_voltage.o
> +obj-$(CONFIG_PXA27x_VOLTAGE_MAX1586B) += pxa27x_voltage_max1586b.o
> --- /dev/null 2006-05-15 11:34:26.000000000 +0400
> +++ include/asm-arm/arch-pxa/pxa27x_voltage.h 2006-12-06 18:51:25.000000000 +0300
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (c) 2006 Michal Sroczynski <msroczyn_at_elka.pw.edu.pl>
> + * Copyright (c) 2006 Anton Vorontsov <cbou_at_mail.ru>
> + *
> + * 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; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/cpufreq.h>
> +
> +struct pxa27x_voltage_chip {
> + u_int8_t address:7;
> + u_int8_t (*mv2cmd)(int mv);
> +
> + // for chips without or improper hardware ramp-rate control
> + unsigned int step; // fill voltage ramp by step (usually 50 mV)
> + unsigned int delay; // 2^delay 13 MHz cycles delay after each command
> +
> + // private
> + unsigned int delay_ms;
> + struct notifier_block freq_transition;
> +};
> --- /dev/null 2006-05-15 11:34:26.000000000 +0400
> +++ arch/arm/mach-pxa/pxa27x_voltage.c 2006-12-06 18:42:00.000000000 +0300
> @@ -0,0 +1,264 @@
> +/* Helper module for the cpufreq. It is responsible for changing core
> + * voltage on CPU through Intel(R) PXA27x EMTS compliant PMIC. Commands
> + * sent to dumb PMIC through PWR_I2C sequencer integrated in PXA27x.
> + * That driver do not expose PWR_I2C as general purpose I2C bus and
> + * so far it supports single-byte commands only.
> + *
> + * Copyright (c) 2006 Michal Sroczynski <msroczyn_at_elka.pw.edu.pl>
> + * Copyright (c) 2006 Anton Vorontsov <cbou_at_mail.ru>
> + *
> + * 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; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * History:
> + * 2006-02-04 : Michal Sroczynski <msroczyn_at_elka.pw.edu.pl>
> + * initial driver for Asus730
> + * 2006-06-05 : Anton Vorontsov <cbou_at_mail.ru>
> + * hx4700 port, various changes
> + * 2006-12-06 : Anton Vorontsov <cbou_at_mail.ru>
> + * Convert to the generic PXA driver.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/cpufreq.h>
> +#include <linux/platform_device.h>
> +#include <asm/hardware.h>
> +#include <asm/arch/pxa-regs.h>
> +#include <asm/arch/pxa27x_voltage.h>
> +
> +#define DEBUG
> +#ifdef DEBUG
> +#define dbg(msg...) do {printk("pxa27x_voltage:%s: ", __FUNCTION__);\
> + printk(msg);} while(0);
> +#else
> +#define dbg(msg...)
> +#endif
> +
> +struct freq2mv_table {
> + unsigned int khz;
> + unsigned int mv;
> +};
> +
> +static struct freq2mv_table freq2mv_table[] = {
> + // kHz, mV
> + {104000, 900},
> + {156000, 1050},
> + {208000, 1100},
> + {312000, 1250},
> + {416000, 1350},
> + {520000, 1450},
> + {624000, 1550}
> +};
> +
> +#define MAX_VOLTAGE_DROP (freq2mv_table[ARRAY_SIZE(freq2mv_table)-1].mv - \
> + freq2mv_table[0].mv)
> +
> +static inline int freq2mv(int khz)
> +{
> + int i;
> + for (i = 0; i < ARRAY_SIZE(freq2mv_table); i++) {
> + if (freq2mv_table[i].khz == khz)
> + return freq2mv_table[i].mv;
> + }
> + // Return maximal voltage we know if no freq2mv record found
> + printk(KERN_WARNING "pxa27x_voltage: unknown freq: %d\n", khz);
> + return freq2mv_table[ARRAY_SIZE(freq2mv_table)-1].mv;
> +}
> +
> +
> +/* This is Errata 28 workaround, see Intel Specification Update
> + * POWER MANAGER: Core hangs during voltage change when there are
> + * outstanding transactions on the bus */
> +static void start_voltage_change(void)
> +{
> + unsigned int unused;
> + unsigned long flags;
> + local_irq_save(flags);
> + __asm__ __volatile__(" \n\
> + b 1f \n\
> + nop \n\
> + .align 5 \n\
> +1: \n\
> + ldr r0, [%1] @ APB register read and compare \n\
> + cmp r0, #0 @ fence for pending slow apb reads \n\
> + \n\
> + mov r0, #0x8 @ VC bit for PWRMODE \n\
> + movs r1, #1 @ don't execute mcr on 1st pass \n\
> + \n\
> +retry: \n\
> + ldreq r3, [%2] @ only stall on the 2nd pass \n\
> + cmpeq r3, #0 @ cmp causes fence on mem transfers\n\
> + cmp r1, #0 @ is this the 2nd pass? \n\
> + mcreq p14, 0, r0, c7, c0, 0 @ write to PWRMODE on 2nd pass only\n\
> + \n\
> + @ Read VC bit until it is 0, indicates that the VoltageChange is done.\n\
> + @ On first pass, we never set the VC bit, so it will be clear already.\n\
> +VoltageChange_loop: \n\
> + mrc p14, 0, r3, c7, c0, 0 \n\
> + tst r3, #0x8 \n\
> + bne VoltageChange_loop \n\
> + \n\
> + subs r1, r1, #1 \n\
> + beq retry"
> + :"=&r"(unused)
> + :"r"(&CCCR), "r"(UNCACHED_ADDR)
> + :"r0", "r1", "r3");
> + local_irq_restore(flags);
> + return;
> +}
> +
> +static int fill_ramp(int old_mv, int new_mv,
> + struct pxa27x_voltage_chip *chip)
> +{
> + int delta;
> + int sign;
> + int steps;
> + int i;
> +
> + delta = new_mv - old_mv;
> + if (!delta) return 0;
> +
> + sign = abs(delta)/delta;
> + steps = abs(delta)/chip->step;
> +
> + if (sign > 0 && old_mv + chip->step*steps < new_mv) steps++;
> + else if (!steps) return 0;
> +
> + i = 0;
> + while (steps > i) {
> + old_mv += sign*chip->step;
> + dbg("i:%d mv: %d lc:%d\n",
> + i, old_mv, steps <= i+1 ? 1 : 0);
> + PCMD(i) = chip->mv2cmd(old_mv) |
> + (chip->delay ? PCMD_DCE : 0) |
> + (steps <= i+1 ? PCMD_LC : 0);
> + i++;
> + }
> +
> + return steps;
> +}
> +
> +static int change_voltage(struct cpufreq_freqs *f,
> + struct pxa27x_voltage_chip *chip)
> +{
> + int ret = 0;
> + int steps = 1;
> +
> + PVCR = chip->address;
> + if (chip->step) {
> + PVCR = PVCR | (chip->delay << 7 & PVCR_CommandDelay);
> + steps = fill_ramp(freq2mv(f->old), freq2mv(f->new), chip);
> + if (steps == 0) return ret;
> + }
> + else PCMD(0) = chip->mv2cmd(freq2mv(f->new)) | PCMD_LC;
> +
> + start_voltage_change();
> +
> + while (PVCR & PVCR_VCSA) {
> + dbg("read pointer at %d\n", PVCR >> 20);
> + if (steps-- <= 0) {
> + printk(KERN_WARNING "pxa27x_voltage: tired to wait, "
> + "read pointer at %d\n", PVCR >> 20);
> + ret = 1;
> + break;
> + }
> + msleep(chip->delay_ms);
> + }
> + return ret;
> +}
> +
> +static int do_freq_transition(struct notifier_block *nb, unsigned long val,
> + void *data)
> +{
> + int ret = 0;
> + struct pxa27x_voltage_chip *chip = container_of(nb,
> + struct pxa27x_voltage_chip, freq_transition);
> + struct cpufreq_freqs *f = data;
> +
> + dbg("v=%ld cpu=%u old=%u new=%u flags=%hu\n",
> + val, f->cpu, f->old, f->new, f->flags);
> +
> + switch (val) {
> + case CPUFREQ_PRECHANGE:
> + if (f->new < f->old) break;
> + ret = change_voltage(f, chip);
> + break;
> + case CPUFREQ_POSTCHANGE:
> + if (f->new > f->old) break;
> + ret = change_voltage(f, chip);
> + break;
> + default:
> + printk(KERN_WARNING "pxa27x_voltage: "
> + "Unhandled cpufreq event\n");
> + ret = 1;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int pxa27x_voltage_probe(struct platform_device *pdev)
> +{
> + struct pxa27x_voltage_chip *chip = pdev->dev.platform_data;
> +
> + if (chip->step && (MAX_VOLTAGE_DROP + chip->step)/chip->step > 32) {
> + printk(KERN_ERR "pxa27x_voltage: step too small, commands "
> + "will not fit in sequencer");
> + return -EINVAL;
> + }
> + else if (chip->delay) {
> + int i;
> + if (chip->delay > 24) chip->delay = 24;
> + chip->delay_ms = 2;
> + for (i = 1; i < chip->delay; i++)
> + chip->delay_ms *= 2;
> + chip->delay_ms = chip->delay_ms/13000 + 1;
> + dbg("delay_ms = %d\n", chip->delay_ms)
> + }
> +
> + PCFR |= PCFR_PI2C_EN;
> + pxa_set_cken(CKEN15_PWRI2C,1);
> + chip->freq_transition.notifier_call = do_freq_transition;
> + return cpufreq_register_notifier(&chip->freq_transition,
> + CPUFREQ_TRANSITION_NOTIFIER);
> +}
> +
> +static int pxa27x_voltage_remove(struct platform_device *pdev)
> +{
> + struct pxa27x_voltage_chip *chip = pdev->dev.platform_data;
> + int ret;
> + ret = cpufreq_unregister_notifier(&chip->freq_transition,
> + CPUFREQ_TRANSITION_NOTIFIER);
> + pxa_set_cken(CKEN15_PWRI2C,0);
> + return ret;
> +}
> +
> +static struct platform_driver pxa27x_voltage_driver = {
> + .probe = pxa27x_voltage_probe,
> + .remove = pxa27x_voltage_remove,
> + .driver = {
> + .name = "pxa27x-voltage",
> + },
> +};
> +
> +static int __init pxa27x_voltage_init(void)
> +{
> + return platform_driver_register(&pxa27x_voltage_driver);
> +}
> +
> +static void __exit pxa27x_voltage_exit(void)
> +{
> + platform_driver_unregister(&pxa27x_voltage_driver);
> + return;
> +}
> +
> +module_init(pxa27x_voltage_init);
> +module_exit(pxa27x_voltage_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Michal Sroczynski <msroczyn_at_elka.pw.edu.pl>, "
> + "Anton Vorontsov <cbou_at_mail.ru>");
> +MODULE_DESCRIPTION("Voltage change for PXA27x");
> --- /dev/null 2006-05-15 11:34:26.000000000 +0400
> +++ arch/arm/mach-pxa/pxa27x_voltage_max1586b.c 2006-12-06 18:52:24.000000000 +0300
> @@ -0,0 +1,69 @@
> +/* Driver for the Maxim 1586B PMIC, connected to PXA27x PWR_I2C.
> + *
> + * Copyright (c) 2006 Michal Sroczynski <msroczyn_at_elka.pw.edu.pl>
> + * Copyright (c) 2006 Anton Vorontsov <cbou_at_mail.ru>
> + *
> + * 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; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * History:
> + * 2006-02-04 : Michal Sroczynski <msroczyn_at_elka.pw.edu.pl>
> + * initial driver for Asus730
> + * 2006-06-05 : Anton Vorontsov <cbou_at_mail.ru>
> + * hx4700 port, various changes
> + * 2006-12-06 : Anton Vorontsov <cbou_at_mail.ru>
> + * Convert to the generic PXA driver.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <asm/arch/pxa27x_voltage.h>
> +
> +static inline u_int8_t mv2cmd (int mv)
> +{
> + u_int8_t val = (mv - 700) / 25;
> + if (val > 31) val = 31;
> + return val;
> +}
> +
> +static struct platform_device *pdev;
> +
> +static int __init max1586b_init(void)
> +{
> + int ret;
> + struct pxa27x_voltage_chip chip;
> +
> + memset(&chip, 0, sizeof(chip));
> + chip.address = 0x14;
> + chip.mv2cmd = mv2cmd;
> +
> + pdev = platform_device_alloc("pxa27x-voltage", -1);
> + if (!pdev) return -ENOMEM;
> +
> + ret = platform_device_add_data(pdev, &chip, sizeof(chip));
> + if (ret) goto failed;
> +
> + ret = platform_device_register(pdev);
> + if (ret) goto failed;
> +
> + return 0;
> +failed:
> + platform_device_put(pdev);
> + return ret;
> +}
> +
> +static void __exit max1586b_exit(void)
> +{
> + platform_device_unregister(pdev);
> + return;
> +}
> +
> +module_init(max1586b_init);
> +module_exit(max1586b_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Michal Sroczynski <msroczyn_at_elka.pw.edu.pl>, "
> + "Anton Vorontsov <cbou_at_mail.ru>");
> +MODULE_DESCRIPTION("Driver for the Maxim 1586B PMIC");
Received on Thu Dec 14 2006 - 20:19:15 EST
This archive was generated by hypermail 2.2.0 : Thu Dec 14 2006 - 20:19:34 EST