├── dist └── .gitignore ├── tmp └── .gitignore ├── src ├── debian_scripts │ ├── header │ ├── prerm │ └── postinst ├── softpwm │ ├── Makefile │ ├── dkms.conf │ ├── DEBIAN │ │ └── control │ └── softpwm.c ├── gpio-sb8xx │ ├── Makefile │ ├── dkms.conf │ ├── DEBIAN │ │ └── control │ └── gpio-sb8xx.c └── i2c-piix4 │ ├── Makefile │ ├── dkms.conf │ ├── DEBIAN │ └── control │ └── i2c-piix4.c ├── .travis.yml ├── n40_led.sh └── README.md /dist/.gitignore: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /tmp/.gitignore: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /src/debian_scripts/header: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e -------------------------------------------------------------------------------- /src/softpwm/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += softpwm.o 2 | KVER ?= $(shell uname -r) 3 | KDIR ?= /lib/modules/$(KVER)/build 4 | all: 5 | $(MAKE) -C $(KDIR) M=$(PWD) modules 6 | clean: 7 | $(MAKE) -C $(KDIR) M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /src/gpio-sb8xx/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += gpio-sb8xx.o 2 | KVER ?= $(shell uname -r) 3 | KDIR ?= /lib/modules/$(KVER)/build 4 | all: 5 | $(MAKE) -C $(KDIR) M=$(PWD) modules 6 | clean: 7 | $(MAKE) -C $(KDIR) M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /src/i2c-piix4/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += i2c-piix4.o 2 | KVER ?= $(shell uname -r) 3 | KDIR ?= /lib/modules/$(KVER)/build 4 | all: 5 | $(MAKE) -C $(KDIR) M=$(PWD) modules 6 | clean: 7 | $(MAKE) -C $(KDIR) M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /src/debian_scripts/prerm: -------------------------------------------------------------------------------- 1 | case "$1" in 2 | remove|upgrade|deconfigure) 3 | if [ "$(dkms status -m $DKMS_NAME -v $DKMS_VERSION)" ]; then 4 | dkms remove -m $DKMS_NAME -v $DKMS_VERSION --all 5 | fi 6 | ;; 7 | esac -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: bash 4 | before_install: 5 | - 'sudo apt-get install -qq fakeroot dkms' 6 | - 'sudo apt install linux-headers-$(uname -r)' 7 | script: 8 | - '/bin/sh build_all_debs.sh' 9 | - 'sudo dpkg -i dist/*.deb' -------------------------------------------------------------------------------- /src/softpwm/dkms.conf: -------------------------------------------------------------------------------- 1 | MAKE="make all KVER=${kernelver}" 2 | CLEAN="make clean" 3 | BUILT_MODULE_NAME=softpwm 4 | BUILT_MODULE_LOCATION=. 5 | DEST_MODULE_LOCATION=/updates 6 | PACKAGE_NAME=softpwm 7 | PACKAGE_VERSION=20180507 8 | REMAKE_INITRD=yes 9 | AUTOINSTALL=yes 10 | -------------------------------------------------------------------------------- /src/i2c-piix4/dkms.conf: -------------------------------------------------------------------------------- 1 | MAKE="make all KVER=${kernelver}" 2 | CLEAN="make clean" 3 | BUILT_MODULE_NAME=i2c-piix4 4 | BUILT_MODULE_LOCATION=. 5 | DEST_MODULE_LOCATION=/updates 6 | PACKAGE_NAME=i2c-piix4 7 | PACKAGE_VERSION=20180509 8 | REMAKE_INITRD=yes 9 | AUTOINSTALL=yes 10 | -------------------------------------------------------------------------------- /src/gpio-sb8xx/dkms.conf: -------------------------------------------------------------------------------- 1 | MAKE="make all KVER=${kernelver}" 2 | CLEAN="make clean" 3 | BUILT_MODULE_NAME=gpio-sb8xx 4 | BUILT_MODULE_LOCATION=. 5 | DEST_MODULE_LOCATION=/updates 6 | PACKAGE_NAME=gpio-sb8xx 7 | PACKAGE_VERSION=20180507 8 | REMAKE_INITRD=yes 9 | AUTOINSTALL=yes 10 | -------------------------------------------------------------------------------- /src/gpio-sb8xx/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: gpio-sb8xx-dkms 2 | Maintainer: Adam Watkins 3 | Homepage: https://github.com/stupidpupil/hp_n36-40-54l_health_led_drivers 4 | Description: dkms source for the AMD SB8xx gpio driver 5 | Architecture: all 6 | Priority: optional 7 | Section: kernel 8 | Depends: dkms (>= 2) 9 | Installed-Size: 70 10 | -------------------------------------------------------------------------------- /src/softpwm/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: softpwm-dkms 2 | Maintainer: Adam Watkins 3 | Homepage: https://github.com/stupidpupil/hp_n36-40-54l_health_led_drivers 4 | Description: dkms source for the softpwm software PWM driver 5 | Architecture: all 6 | Priority: optional 7 | Section: kernel 8 | Depends: dkms (>= 2) 9 | Installed-Size: 60 10 | -------------------------------------------------------------------------------- /src/i2c-piix4/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: i2c-piix4-dkms 2 | Maintainer: Adam Watkins 3 | Homepage: https://github.com/stupidpupil/hp_n36-40-54l_health_led_drivers 4 | Description: dkms source for the backported i2c-piix4 driver 5 | Architecture: all 6 | Priority: optional 7 | Section: kernel 8 | Depends: dkms (>= 2) 9 | Installed-Size: 80 10 | -------------------------------------------------------------------------------- /src/debian_scripts/postinst: -------------------------------------------------------------------------------- 1 | postinst_found=0 2 | 3 | case "$1" in 4 | configure) 5 | for DKMS_POSTINST in /usr/lib/dkms/common.postinst /usr/share/$DKMS_PACKAGE_NAME/postinst; do 6 | if [ -f $DKMS_POSTINST ]; then 7 | $DKMS_POSTINST $DKMS_NAME $DKMS_VERSION /usr/share/$DKMS_PACKAGE_NAME "" $2 8 | postinst_found=1 9 | break 10 | fi 11 | done 12 | if [ "$postinst_found" -eq 0 ]; then 13 | echo "ERROR: DKMS version is too old and $DKMS_PACKAGE_NAME was not" 14 | echo "built with legacy DKMS support." 15 | echo "You must either rebuild $DKMS_PACKAGE_NAME with legacy postinst" 16 | echo "support or upgrade DKMS to a more current version." 17 | exit 1 18 | fi 19 | ;; 20 | esac 21 | -------------------------------------------------------------------------------- /n40_led.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | BLUE_VALUE="$1" 4 | ORANGE_VALUE="$2" 5 | 6 | BLUE_PIN=188 7 | ORANGE_PIN=187 8 | 9 | PWM_PERIOD=10000 #microseconds 10 | 11 | BIOS_YEAR=$(cut -d "/" -f 3 /sys/class/dmi/id/bios_date) 12 | BIOS_MONTH=$(cut -d "/" -f 1 /sys/class/dmi/id/bios_date) 13 | BIOS_DAY=$(cut -d "/" -f 2 /sys/class/dmi/id/bios_date) 14 | 15 | MIN_BIOS_DATE=20110729 16 | 17 | if ! (echo "$BIOS_YEAR-$BIOS_MONTH-$BIOS_DAY" | grep -Eq '^[0-9]{4}-[0-9]{2}-[0-9]{2}$') ; then 18 | echo "Couldn't understand BIOS date" 19 | echo "BIOS date should be 07/29/2011 or later" 20 | exit 1 21 | fi 22 | 23 | BIOS_DATE=$((BIOS_YEAR*10000+BIOS_MONTH*100+BIOS_DAY)) 24 | 25 | if [ $((BIOS_DATE < MIN_BIOS_DATE)) -eq 1 ]; then 26 | echo "BIOS date should be 07/29/2011 or later" 27 | exit 1 28 | fi 29 | 30 | if ! modprobe i2c-piix4; then 31 | echo "Couldn't insert i2c-piix4 driver" 32 | exit 1 33 | fi 34 | 35 | if ! modprobe gpio-sb8xx; then 36 | echo "Couldn't insert gpio-sb8xx driver" 37 | exit 1 38 | fi 39 | 40 | if ! modprobe softpwm; then 41 | echo "Couldn't insert softpwm driver" 42 | exit 1 43 | fi 44 | 45 | if ! BASE=$(cat "/sys/class/gpio/gpiochip256/base"); then 46 | echo "Couldn't determine gpio pin base" 47 | exit 1 48 | fi 49 | 50 | BLUE_PIN=$((BASE+BLUE_PIN)) 51 | ORANGE_PIN=$((BASE+ORANGE_PIN)) 52 | 53 | PWM_BASE_DIR="/sys/class/soft_pwm" 54 | 55 | for pin in $ORANGE_PIN $BLUE_PIN ; do 56 | pwmPath="$PWM_BASE_DIR/pwm$pin" 57 | 58 | if [ ! -d "$pwmPath" ]; then 59 | if ! echo "$pin" > "$PWM_BASE_DIR/export"; then 60 | echo "Couldn't export pwm for $pin" 61 | exit 62 | fi 63 | fi 64 | 65 | echo "$PWM_PERIOD" > "$pwmPath/period" 66 | 67 | done 68 | 69 | BLUE_PULSE=$(((100-BLUE_VALUE)*PWM_PERIOD/100)) 70 | ORANGE_PULSE=$(((100-ORANGE_VALUE)*PWM_PERIOD/100)) 71 | 72 | echo "$BLUE_PULSE" > "$PWM_BASE_DIR/pwm$BLUE_PIN/pulse" 73 | echo "$ORANGE_PULSE" > "$PWM_BASE_DIR/pwm$ORANGE_PIN/pulse" 74 | 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Health LED for HP Microservers N36L/N40L/N54L 2 | 3 | This repository contains 3 drivers intended for use in HP Microservers N36L/N40L/N54L to allow control of their 'Health LED'. 4 | 5 | They are packaged as [DKMS](https://en.wikipedia.org/wiki/Dynamic_Kernel_Module_Support) modules, a script is provided to cobble them into Debian packages, and another script is provided as an example. 6 | 7 | ## Installation 8 | ```sh 9 | # Install requirements 10 | sudo apt-get install -qq fakeroot dkms 11 | sudo apt install linux-headers-$(uname -r) 12 | 13 | # Install the drivers 14 | git clone https://github.com/stupidpupil/hp_n36-40-54l_health_led_drivers.git 15 | cd hp_n36-40-54l_health_led_drivers 16 | /bin/sh build_all_debs.sh 17 | sudo dpkg -i dist/*.deb 18 | 19 | # Remove the existing drivers and blacklist sp5100_tco 20 | sudo rmmod i2c_piix4 21 | sudo rmmod sp5100_tco 22 | echo "blacklist sp5100_tco" | sudo tee /etc/modprobe.d/blacklist-sp5100_tco.conf 23 | 24 | # Try the example script 25 | sudo /bin/sh n40_led.sh 0 100 # Switch the Health LED to orange 26 | sudo /bin/sh n40_led.sh 100 100 # Switch the Health LED to pink (blue+orange) 27 | sudo /bin/sh n40_led.sh 5 5 # Reduce the brightness of the pink 28 | 29 | ``` 30 | 31 | ## About the Health LED 32 | The 'Health LED' contains three different coloured LEDs - blue, orange and red - as far as I know. 33 | I don't know how to control the red LED, but the blue and orange LEDs are 34 | controlled using the general-purpose-input-output (GPIO) pins from the southbridge (AMD SB820M) of the motherboard itself. 35 | 36 | Specifically: 37 | * the blue LED is controlled using pin 188 and 38 | * the orange LED is controlled using pin 187. 39 | 40 | (These pins are also one of the SMBus/i2c pairs - 'port 4' on the main SMBus controller. 41 | In other versions of the southbridge they're also used for PS/2 ports. This is documented in the [AMD SB820M Southbridge Databook](https://support.amd.com/TechDocs/47283.pdf).) 42 | 43 | ### Older versions of the BIOS 44 | Older versions of the BIOS for N36L/N40L/N54L's don't seem to support accessing these GPIO pins. These drivers are known to work on at least the *07/29/2011* version of the BIOS. 45 | 46 | You can check what version your machine is running with the command `cat /sys/class/dmi/id/bios_date`, or by booting into the *ROM-BASED SETUP UTILITY* (by pressing F10 at startup) and checking the *BIOS Version* shown. 47 | 48 | ## The drivers 49 | 50 | ### i2c-piix4 51 | A backported version of [04b6fca](https://github.com/torvalds/linux/commit/04b6fcaba346e1ce76321ba9b0fd549da4c37ac2), likely to appear in Linux 4.17. Importantly, this allows other drivers to access shared parts of the AMD SB820M southbridge. 52 | 53 | Only very minor changes were required to get it to compile under Linux 4.9. I have also (crudely) disabled 'port 4' on the main SMBus controller in the driver, as in the N36L/N40L/N54L the relevant pins are used as GPIO to control the Health LED. 54 | 55 | You might want to investigate [fetzerch's repo for using sensors on the i2c bus](https://github.com/fetzerch/hp-n54l-drivers). Note that you don't need to use fetzerch's i2c-piix4 driver. 56 | 57 | Copyright (c) 1998 - 2002 Frodo Looijaard and Philip Edelbrock, and others shown on the GitHub link above. 58 | 59 | ### gpio-sb8xx 60 | A driver to allow controlling the GPIO pins on the AMD SB820M southbridge. It was [submitted by Tobias Diedrich in 2015](https://patchwork.kernel.org/patch/6651771/), but was not included in the mainline kernel. 61 | 62 | Minor updates were required to get it to compile under Linux 4.9. It has also been patched to ignore requests to change the direction of pins to 'out' where those pins already had a direction of 'out'; this avoids a situation where a direction change is unnecessarily requested and fails as the pin is not marked as safe for use as an 'out' pin. 63 | 64 | Copyright (c) 2015 Tobias Diedrich. 65 | 66 | ### softpwm 67 | Generic software-only driver for generating PWM signals via high resolution timers and GPIO lib interface. 68 | 69 | Copyright (C) 2010 Antonio Galea, modified by Sergio Tanzilli. 70 | 71 | ## Issues 72 | You'll need to [blacklist](https://wiki.debian.org/KernelModuleBlacklisting) the *sp5100_tco* watchdog driver. I don't believe this watchdog works on the Microservers. 73 | 74 | It's possible to provoke a kernel panic by removing the *i2c-piix4* driver while using the GPIO pins via the *gpio-sb8xx* driver. Don't do that. 75 | -------------------------------------------------------------------------------- /src/softpwm/softpwm.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2010 Antonio Galea 2 | * 3 | * May be copied or modified under the terms of the GNU General Public 4 | * License. See linux/COPYING for more information. 5 | * 6 | * Generic software-only driver for generating PWM signals via high 7 | * resolution timers and GPIO lib interface. 8 | */ 9 | 10 | /* Modified by Sergio Tanzilli 11 | http://www.acmesystems.it/soft_pwm 12 | http://www.acmesystems.it/DAISY-2 13 | */ 14 | 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | MODULE_LICENSE("GPL"); 27 | MODULE_AUTHOR("Antonio Galea"); 28 | MODULE_DESCRIPTION("Driver for kernel-generated PWM signals"); 29 | 30 | static struct hrtimer hr_timer; 31 | 32 | /* pwm_desc 33 | * 34 | * This structure maintains the information regarding a single PWM signal: its 35 | * wave period and pulse length are user-definable via sysfs. The counter is 36 | * also shown in sysfs as a debug helper. 37 | */ 38 | struct pwm_desc { 39 | unsigned int pulse; /* Pulse width, in microseconds */ 40 | unsigned int period; /* Wave period, in microseconds */ 41 | unsigned int pulses; /* Number of pwm pulses before stopping; 42 | -1 never stops, 0 stops immediately */ 43 | unsigned long counter; /* "Interrupt" counter - counts each value 44 | toggle */ 45 | int value; /* Current GPIO pin value (0 or 1 only) */ 46 | int last_value; 47 | ktime_t next_tick; /* Timer tick at which next toggling should 48 | happen */ 49 | unsigned long flags; /* Only FLAG_SOFTPWM is used, for synchronizing 50 | inside module */ 51 | #define FLAG_SOFTPWM 0 52 | #define FLAG_STATE_SET 1 53 | }; 54 | 55 | /* pwm_table 56 | * 57 | * The table will hold a description for any GPIO pin available on the system. 58 | * It's wasteful to preallocate the entire table, but avoiding race conditions 59 | * is so much easier this way ;-) 60 | */ 61 | static struct pwm_desc pwm_table[ARCH_NR_GPIOS]; 62 | 63 | /* lock protects against pwm_unexport() being called while sysfs files are 64 | * active. 65 | */ 66 | static DEFINE_MUTEX(sysfs_lock); 67 | 68 | int pwm_export(unsigned gpio); // forward definition 69 | int pwm_unexport(unsigned gpio); // forward definition 70 | 71 | /* Show attribute values for PWMs */ 72 | static ssize_t pwm_show(struct device *dev, struct device_attribute *attr, 73 | char *buf) 74 | { 75 | const struct pwm_desc *desc = dev_get_drvdata(dev); 76 | ssize_t status; 77 | 78 | mutex_lock(&sysfs_lock); 79 | if (!test_bit(FLAG_SOFTPWM, &desc->flags)) { 80 | status = -EIO; 81 | } else { 82 | if (!strcmp(attr->attr.name, "pulse")) { 83 | status = sprintf(buf, "%d usec\n", desc->pulse); 84 | } else if (!strcmp(attr->attr.name, "period")) { 85 | status = sprintf(buf, "%d usec\n", desc->period); 86 | } else if (!strcmp(attr->attr.name, "pulses")) { 87 | status = sprintf(buf, "%d\n", desc->pulses); 88 | } else if (!strcmp(attr->attr.name, "counter")) { 89 | status = sprintf(buf, "%lu\n", desc->counter); 90 | } else status = -EIO; 91 | } 92 | mutex_unlock(&sysfs_lock); 93 | 94 | return status; 95 | } 96 | 97 | /* Store attribute values for PWMs */ 98 | static ssize_t pwm_store(struct device *dev, struct device_attribute *attr, 99 | const char *buf, size_t size) 100 | { 101 | struct pwm_desc *desc = dev_get_drvdata(dev); 102 | ssize_t status; 103 | 104 | mutex_lock(&sysfs_lock); 105 | if (!test_bit(FLAG_SOFTPWM, &desc->flags)) { 106 | status = -EIO; 107 | } else { 108 | unsigned long value; 109 | status = kstrtoul(buf, 0, &value); 110 | if(!status){ 111 | if (!strcmp(attr->attr.name, "pulse")) { 112 | if(value <= desc->period) 113 | desc->pulse = value; 114 | } else if(!strcmp(attr->attr.name, "period")) { 115 | desc->period = value; 116 | } else if(!strcmp(attr->attr.name, "pulses")) { 117 | if (value>0) 118 | desc->pulses = value*2; 119 | else 120 | desc->pulses = value; 121 | } 122 | desc->next_tick = ktime_get(); 123 | /* printk(KERN_INFO "Starting timer (%s).\n", 124 | * attr->attr.name); */ 125 | hrtimer_start(&hr_timer, ktime_set(0,1), 126 | HRTIMER_MODE_REL); 127 | } 128 | } 129 | mutex_unlock(&sysfs_lock); 130 | 131 | return status ? : size; 132 | } 133 | 134 | /* Sysfs attributes definition for PWMs */ 135 | static DEVICE_ATTR(pulse, 0644, pwm_show, pwm_store); 136 | static DEVICE_ATTR(period, 0644, pwm_show, pwm_store); 137 | static DEVICE_ATTR(pulses, 0644, pwm_show, pwm_store); 138 | static DEVICE_ATTR(counter, 0444, pwm_show, NULL); 139 | static const struct attribute *soft_pwm_dev_attrs[] = { 140 | &dev_attr_pulse.attr, 141 | &dev_attr_period.attr, 142 | &dev_attr_pulses.attr, 143 | &dev_attr_counter.attr, 144 | NULL, 145 | }; 146 | static const struct attribute_group soft_pwm_dev_attr_group = { 147 | .attrs = (struct attribute **) soft_pwm_dev_attrs, 148 | }; 149 | 150 | /* Export a GPIO pin to sysfs, and claim it for PWM usage. 151 | * See the equivalent function in drivers/gpio/gpiolib.c 152 | */ 153 | static ssize_t export_store(struct class *class, struct class_attribute *attr, 154 | const char *buf, size_t len){ 155 | long gpio; 156 | int status; 157 | 158 | status = kstrtoul(buf, 0, &gpio); 159 | if (status < 0) goto done; 160 | 161 | status = gpio_request(gpio, "soft_pwm"); 162 | if (status < 0) goto done; 163 | 164 | status = gpio_direction_output(gpio, 0); 165 | if (status < 0) goto done; 166 | 167 | status = pwm_export(gpio); 168 | if (status < 0) goto done; 169 | 170 | set_bit(FLAG_SOFTPWM, &pwm_table[gpio].flags); 171 | 172 | done: 173 | if(status){ 174 | gpio_free(gpio); 175 | pr_debug("%s: status %d\n", __func__, status); 176 | } 177 | return status ? : len; 178 | } 179 | 180 | /* Unexport a PWM GPIO pin from sysfs, and unreclaim it. 181 | * See the equivalent function in drivers/gpio/gpiolib.c 182 | */ 183 | static ssize_t unexport_store(struct class *class, struct class_attribute *attr, 184 | const char *buf, size_t len) 185 | { 186 | long gpio; 187 | int status; 188 | 189 | status = kstrtol(buf, 0, &gpio); 190 | if (status<0) goto done; 191 | 192 | status = -EINVAL; 193 | if (!gpio_is_valid(gpio)) goto done; 194 | 195 | if (test_and_clear_bit(FLAG_SOFTPWM, &pwm_table[gpio].flags)){ 196 | status = pwm_unexport(gpio); 197 | if (!status) gpio_free(gpio); 198 | } 199 | done: 200 | if (status) pr_debug("%s: status %d\n", __func__, status); 201 | return status ? : len; 202 | } 203 | 204 | /* Sysfs definitions for soft_pwm class */ 205 | static struct class_attribute soft_pwm_class_attrs[] = { 206 | __ATTR(export, 0200, NULL, export_store), 207 | __ATTR(unexport, 0200, NULL, unexport_store), 208 | __ATTR_NULL, 209 | }; 210 | 211 | static struct class soft_pwm_class = { 212 | .name = "soft_pwm", 213 | .owner = THIS_MODULE, 214 | .class_attrs = soft_pwm_class_attrs, 215 | }; 216 | 217 | /* Setup the sysfs directory for a claimed PWM device */ 218 | int pwm_export(unsigned gpio) { 219 | struct pwm_desc *desc; 220 | struct device *dev; 221 | int status; 222 | 223 | mutex_lock(&sysfs_lock); 224 | 225 | desc = &pwm_table[gpio]; 226 | desc->value = 0; 227 | desc->pulses = -1; 228 | desc->last_value = -1; 229 | dev = device_create(&soft_pwm_class, NULL, MKDEV(0, 0), desc, 230 | "pwm%d", gpio); 231 | 232 | if (dev) { 233 | status = sysfs_create_group(&dev->kobj, 234 | &soft_pwm_dev_attr_group); 235 | if (!status) { 236 | dev_info(dev, "Registered device pwm%d", gpio); 237 | } else { 238 | device_unregister(dev); 239 | } 240 | } else { 241 | status = -ENODEV; 242 | } 243 | 244 | mutex_unlock(&sysfs_lock); 245 | 246 | if (status) { 247 | pr_debug("%s: pwm%d status %d\n", __func__, gpio, status); 248 | } 249 | return status; 250 | } 251 | 252 | /* Used by pwm_unexport below to find the device which should be freed */ 253 | static int match_export(struct device *dev, const void *data) { 254 | return dev_get_drvdata(dev) == data; 255 | } 256 | 257 | /* Free a claimed PWM device and unregister the sysfs directory */ 258 | int pwm_unexport(unsigned gpio){ 259 | struct pwm_desc *desc; 260 | struct device *dev; 261 | int status; 262 | 263 | mutex_lock(&sysfs_lock); 264 | 265 | desc = &pwm_table[gpio]; 266 | dev = class_find_device(&soft_pwm_class, NULL, desc, match_export); 267 | if(dev){ 268 | put_device(dev); 269 | device_unregister(dev); 270 | printk(KERN_INFO "Unregistered device pwm%d\n", gpio); 271 | status = 0; 272 | }else{ 273 | status = -ENODEV; 274 | } 275 | 276 | mutex_unlock(&sysfs_lock); 277 | 278 | if(status){ pr_debug("%s: pwm%d status %d\n", __func__, gpio, status); } 279 | return status; 280 | } 281 | 282 | /* THIS CODE IS BUGGED ! */ 283 | 284 | /* The timer callback is called only when needed (which is to 285 | * say, at the earliest PWM signal toggling time) in order to 286 | * maintain the pressure on system latency as low as possible 287 | */ 288 | enum hrtimer_restart soft_pwm_hrtimer_callback(struct hrtimer *timer) { 289 | unsigned gpio; 290 | struct pwm_desc *desc; 291 | ktime_t now = ktime_get(); 292 | ktime_t next_tick = ktime_set(0,0); 293 | int t; 294 | 295 | now = ktime_get(); 296 | 297 | /* FIXME: This is horribly inefficient -- obd */ 298 | for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++){ 299 | desc = &pwm_table[gpio]; 300 | if (!(test_bit(FLAG_SOFTPWM, &desc->flags) && 301 | desc->period > 0 && 302 | desc->pulse <= desc->period && 303 | desc->pulses != 0)) 304 | goto next; 305 | 306 | if (desc->pulse == 0) 307 | desc->value = 0; 308 | else if (desc->pulse >= desc->period) 309 | desc->value = 1; 310 | else { 311 | if (desc->next_tick.tv64 <= now.tv64) { 312 | desc->value = 1 - desc->value; 313 | 314 | desc->counter ++; 315 | 316 | if (desc->pulses > 0) desc->pulses--; 317 | 318 | if (desc->pulse == 0 || 319 | desc->pulse == desc->period || 320 | desc->pulses == 0) { 321 | desc->next_tick.tv64 = KTIME_MAX; 322 | } else { 323 | t = desc->value ? 324 | desc->pulse : 325 | desc->period - desc->pulse; 326 | desc->next_tick = ktime_add_ns(now, 327 | t * 1000); 328 | } 329 | } 330 | if (next_tick.tv64 == 0 || 331 | desc->next_tick.tv64 < next_tick.tv64) { 332 | next_tick.tv64 = desc->next_tick.tv64; 333 | } 334 | } 335 | 336 | next: 337 | if (desc->value != desc->last_value) { 338 | gpio_set_value(gpio, desc->value); 339 | desc->last_value = desc->value; 340 | } 341 | } 342 | 343 | if (next_tick.tv64 > 0) 344 | hrtimer_start(&hr_timer, next_tick, HRTIMER_MODE_ABS); 345 | 346 | return HRTIMER_NORESTART; 347 | } 348 | 349 | /* module initialization: init the hr-timer and register a driver class */ 350 | static int __init soft_pwm_init(void){ 351 | //struct timespec tp; 352 | 353 | int status; 354 | printk(KERN_INFO "SoftPWM v0.2-acme initializing.\n"); 355 | 356 | //hrtimer_get_res(CLOCK_MONOTONIC, &tp); 357 | //printk(KERN_INFO "Clock resolution is %ldns\n", tp.tv_nsec); 358 | 359 | hrtimer_init(&hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 360 | hr_timer.function = &soft_pwm_hrtimer_callback; 361 | 362 | status = class_register(&soft_pwm_class); 363 | if (status<0) goto fail_no_class; 364 | 365 | printk(KERN_INFO "SoftPWM initialized.\n"); 366 | return 0; 367 | 368 | fail_no_class: 369 | return status; 370 | } 371 | 372 | /* module finalization: cancel the hr-timer, switch off any PWM 373 | * signal and give back to GPIO the pin, then deregister our class 374 | */ 375 | static void __exit soft_pwm_exit(void){ 376 | unsigned gpio; 377 | int status; 378 | 379 | hrtimer_cancel(&hr_timer); 380 | 381 | for (gpio=0; gpioflags)) { 385 | gpio_set_value(gpio,0); 386 | status = pwm_unexport(gpio); 387 | if(!status) gpio_free(gpio); 388 | } 389 | } 390 | 391 | class_unregister(&soft_pwm_class); 392 | printk(KERN_INFO "SoftPWM disabled.\n"); 393 | } 394 | 395 | module_init(soft_pwm_init); 396 | module_exit(soft_pwm_exit); 397 | 398 | -------------------------------------------------------------------------------- /src/gpio-sb8xx/gpio-sb8xx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GPIO driver for AMD SB8XX/SB9XX/A5X/A8X south bridges 3 | * 4 | * Copyright (c) 2015 Tobias Diedrich 5 | * 6 | * Based on the AMD 8111 GPIO driver: 7 | * Copyright (c) 2012 Dmitry Eremin-Solenikov 8 | * 9 | * Based on the AMD RNG driver: 10 | * Copyright 2005 (c) MontaVista Software, Inc. 11 | * with the majority of the code coming from: 12 | * 13 | * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) 14 | * (c) Copyright 2003 Red Hat Inc 15 | * 16 | * derived from 17 | * 18 | * Hardware driver for the AMD 768 Random Number Generator (RNG) 19 | * (c) Copyright 2001 Red Hat Inc 20 | * 21 | * derived from 22 | * 23 | * Hardware driver for Intel i810 Random Number Generator (RNG) 24 | * Copyright 2000,2001 Jeff Garzik 25 | * Copyright 2000,2001 Philipp Rumpf 26 | * 27 | * This file is licensed under the terms of the GNU General Public 28 | * License version 2. This program is licensed "as is" without any 29 | * warranty of any kind, whether express or implied. 30 | */ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #define PM_REG_BASE 0xCD6 42 | #define PM_REG_SIZE 2 43 | #define PM_IDX_REG (PM_REG_BASE + 0) 44 | #define PM_DATA_REG (PM_REG_BASE + 1) 45 | #define PM_ACPI_MMIO_BAR 0x24 46 | 47 | #define GPIO_OFFSET 0x100 48 | #define GPIO_SIZE 0x100 49 | #define IOMUX_OFFSET 0xd00 50 | #define IOMUX_SIZE 0x100 51 | 52 | #define MAX_GPIO 256 53 | 54 | #define REG_GPIO(i) (0x00 + (i)) 55 | 56 | #define IOMUX_MASK 0x03 57 | #define IOMUX_FN0 0x00 58 | #define IOMUX_FN1 0x01 59 | #define IOMUX_FN2 0x02 60 | #define IOMUX_FN3 0x03 61 | 62 | #define GPIO_OWN_NONE 0x00 /* No owner */ 63 | #define GPIO_OWN_IMC 0x01 /* OwnedByImc */ 64 | #define GPIO_OWN_HOST 0x02 /* OwnedByHost */ 65 | #define GPIO_OWN_MASK (GPIO_OWN_IMC | GPIO_OWN_HOST) 66 | #define GPIO_STICKY 0x04 /* Settings sticky across reset */ 67 | #define GPIO_PULLUP_B 0x08 /* 0 to enable pull-up */ 68 | #define GPIO_PULLDOWN 0x10 /* 1 to enable pull-down */ 69 | #define GPIO_PULL_MASK (GPIO_PULLUP_B | GPIO_PULLDOWN) 70 | #define GPIO_PULL_UP 0x00 71 | #define GPIO_PULL_DOWN 0x18 72 | #define GPIO_PULL_NONE 0x08 73 | #define GPIO_OUT_EN_B 0x20 /* 0 to enable output */ 74 | #define GPIO_OUT 0x40 75 | #define GPIO_IN 0x80 76 | 77 | static int enable_unsafe_direction_change; 78 | module_param(enable_unsafe_direction_change, int, 0); 79 | MODULE_PARM_DESC(enable_unsafe_direction_change, 80 | "Enable gpio direction changes. This can be unsafe!"); 81 | 82 | static const struct pci_device_id pci_tbl[] = { 83 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, 84 | { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, 85 | { PCI_DEVICE(1002, 4385) }, 86 | { 0, }, /* terminate list */ 87 | }; 88 | 89 | /* 90 | * Explicitly register the supported PCI ids via MODULE_DEVICE_TABLE. 91 | * We do not actually register a pci_driver, because the SMBUS driver 92 | * (i2c_piix4) already does. 93 | */ 94 | MODULE_DEVICE_TABLE(pci, pci_tbl); 95 | 96 | struct amd_sb8xx_gpio { 97 | struct gpio_chip chip; 98 | u32 gpio_base; 99 | u32 iomux_base; 100 | void __iomem *gpio; 101 | void __iomem *iomux; 102 | struct device *dev; 103 | spinlock_t lock; /* guards hw registers and orig table */ 104 | u8 orig[MAX_GPIO]; 105 | }; 106 | 107 | #define to_agp(chip) container_of(chip, struct amd_sb8xx_gpio, chip) 108 | 109 | static int amd_sb8xx_gpio_request(struct gpio_chip *chip, unsigned offset) 110 | { 111 | struct amd_sb8xx_gpio *agp = to_agp(chip); 112 | u8 orig = ioread8(agp->gpio + offset); 113 | 114 | if (orig & GPIO_OWN_IMC) { 115 | dev_err(agp->dev, "Requested GPIO %d is owned by IMC\n", 116 | offset); 117 | return -EINVAL; 118 | } 119 | 120 | agp->orig[offset] = orig; 121 | dev_dbg(agp->dev, "Requested GPIO %d, data %x\n", offset, orig); 122 | return 0; 123 | } 124 | 125 | static void amd_sb8xx_gpio_free(struct gpio_chip *chip, unsigned offset) 126 | { 127 | struct amd_sb8xx_gpio *agp = to_agp(chip); 128 | 129 | dev_dbg(agp->dev, "Freed GPIO %d, data %x\n", offset, 130 | agp->orig[offset]); 131 | 132 | iowrite8(agp->orig[offset], agp->gpio + offset); 133 | } 134 | 135 | static void amd_sb8xx_gpio_set(struct gpio_chip *chip, unsigned offset, 136 | int value) 137 | { 138 | struct amd_sb8xx_gpio *agp = to_agp(chip); 139 | u8 temp; 140 | unsigned long flags; 141 | 142 | spin_lock_irqsave(&agp->lock, flags); 143 | temp = ioread8(agp->gpio + offset); 144 | if ((temp & GPIO_OUT_EN_B) == 0 || enable_unsafe_direction_change) { 145 | temp &= ~(GPIO_OUT | GPIO_OUT_EN_B); 146 | temp |= value ? GPIO_OUT : 0; 147 | iowrite8(temp, agp->gpio + offset); 148 | } 149 | spin_unlock_irqrestore(&agp->lock, flags); 150 | } 151 | 152 | static int amd_sb8xx_gpio_get(struct gpio_chip *chip, unsigned offset) 153 | { 154 | struct amd_sb8xx_gpio *agp = to_agp(chip); 155 | u8 temp; 156 | 157 | temp = ioread8(agp->gpio + offset); 158 | 159 | return (temp & GPIO_IN) ? 1 : 0; 160 | } 161 | 162 | static int amd_sb8xx_gpio_get_direction(struct gpio_chip *chip, 163 | unsigned offset) 164 | { 165 | struct amd_sb8xx_gpio *agp = to_agp(chip); 166 | u8 temp; 167 | 168 | temp = ioread8(agp->gpio + offset); 169 | 170 | return (temp & GPIO_OUT_EN_B) ? GPIOF_DIR_IN : GPIOF_DIR_OUT; 171 | } 172 | 173 | static int amd_sb8xx_gpio_dirout(struct gpio_chip *chip, unsigned offset, 174 | int value) 175 | { 176 | struct amd_sb8xx_gpio *agp = to_agp(chip); 177 | u8 temp, orig; 178 | unsigned long flags; 179 | int ret = 0; 180 | 181 | // HACK - Some pins already seem to be set to output 182 | // but aren't marked as safe for being set to output... 183 | if (amd_sb8xx_gpio_get_direction(chip, offset) == GPIOF_DIR_OUT) { 184 | return 0; 185 | } 186 | 187 | spin_lock_irqsave(&agp->lock, flags); 188 | orig = ioread8(agp->gpio + offset); 189 | temp = (orig & ~(GPIO_OUT | GPIO_OUT_EN_B)) | (value ? GPIO_OUT : 0); 190 | if (orig != temp) { 191 | if (!enable_unsafe_direction_change) 192 | ret = -EINVAL; 193 | else 194 | iowrite8(temp, agp->gpio + offset); 195 | } 196 | spin_unlock_irqrestore(&agp->lock, flags); 197 | 198 | if (ret) 199 | dev_err(agp->dev, 200 | "Cannot change GPIO %d to output: enable_unsafe_direction_change not set\n", 201 | offset); 202 | 203 | return ret; 204 | } 205 | 206 | static int amd_sb8xx_gpio_dirin(struct gpio_chip *chip, unsigned offset) 207 | { 208 | struct amd_sb8xx_gpio *agp = to_agp(chip); 209 | u8 temp, orig; 210 | unsigned long flags; 211 | int ret = 0; 212 | 213 | spin_lock_irqsave(&agp->lock, flags); 214 | orig = ioread8(agp->gpio + offset); 215 | temp = orig | GPIO_OUT_EN_B; 216 | if (orig != temp) { 217 | if (!enable_unsafe_direction_change) 218 | ret = -EINVAL; 219 | else 220 | iowrite8(temp, agp->gpio + offset); 221 | } 222 | spin_unlock_irqrestore(&agp->lock, flags); 223 | 224 | if (ret) 225 | dev_err(agp->dev, 226 | "Cannot change GPIO %d to input: enable_unsafe_direction_change not set\n", 227 | offset); 228 | return ret; 229 | } 230 | 231 | #ifdef CONFIG_DEBUG_FS 232 | static void amd_sb8xx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 233 | { 234 | struct amd_sb8xx_gpio *agp = to_agp(chip); 235 | int i; 236 | 237 | for (i = 0; i < chip->ngpio; i++) { 238 | int gpio = i + chip->base; 239 | u8 reg; 240 | const char *label, *pull, *owner, *mux; 241 | 242 | /* We report the GPIO even if it's not requested since 243 | * we're also reporting things like alternate 244 | * functions which apply even when the GPIO is not in 245 | * use as a GPIO. 246 | */ 247 | label = gpiochip_is_requested(chip, i); 248 | if (!label) { 249 | label = "Unrequested"; 250 | 251 | /* Skip known gaps in the gpio range unless they were 252 | * explicitly requested. 253 | */ 254 | if ((i > 67 && i < 96) || 255 | (i > 119 && i < 128) || 256 | (i > 150 && i < 160) || 257 | i > 228) 258 | continue; 259 | } 260 | 261 | seq_printf(s, " gpio-%-3d(%d) (%-20.20s) ", gpio, i, label); 262 | 263 | reg = ioread8(agp->gpio + i); 264 | 265 | switch (reg & GPIO_PULL_MASK) { 266 | case GPIO_PULL_NONE: 267 | pull = "nopull"; 268 | break; 269 | case GPIO_PULL_DOWN: 270 | pull = "pulldown"; 271 | break; 272 | case GPIO_PULL_UP: 273 | pull = "pullup"; 274 | break; 275 | default: 276 | pull = "INVALID PULL"; 277 | break; 278 | } 279 | 280 | switch (reg & GPIO_OWN_MASK) { 281 | case GPIO_OWN_IMC: 282 | owner = "imc"; 283 | break; 284 | case GPIO_OWN_HOST: 285 | owner = "host"; 286 | break; 287 | case GPIO_OWN_NONE: 288 | owner = NULL; 289 | break; 290 | default: 291 | owner = "INVALID OWNER"; 292 | } 293 | 294 | reg = ioread8(agp->iomux + i); 295 | switch (reg & IOMUX_MASK) { 296 | case IOMUX_FN0: 297 | mux = "fn0"; 298 | break; 299 | case IOMUX_FN1: 300 | mux = "fn1"; 301 | break; 302 | case IOMUX_FN2: 303 | mux = "fn2"; 304 | break; 305 | case IOMUX_FN3: 306 | mux = "fn3"; 307 | break; 308 | default: 309 | mux = "INVALID IOMUX"; 310 | break; 311 | } 312 | 313 | seq_printf(s, " %s %s %s %s", 314 | reg & GPIO_OUT_EN_B ? "in" : "out", 315 | reg & GPIO_IN ? "high" : "low", 316 | pull, mux); 317 | if (i >= 96 && i <= 119) 318 | seq_printf(s, " gevent%d", i - 96); 319 | if (owner) 320 | seq_printf(s, " %s", owner); 321 | seq_putc(s, '\n'); 322 | } 323 | } 324 | #else 325 | #define amd_sb8xx_gpio_dbg_show NULL 326 | #endif 327 | 328 | static struct amd_sb8xx_gpio gp = { 329 | .chip = { 330 | .label = "AMD SB8XX/SB9XX/A5X/A8X GPIO driver", 331 | .owner = THIS_MODULE, 332 | .base = -1, 333 | .ngpio = MAX_GPIO, 334 | .dbg_show = amd_sb8xx_gpio_dbg_show, 335 | .request = amd_sb8xx_gpio_request, 336 | .free = amd_sb8xx_gpio_free, 337 | .set = amd_sb8xx_gpio_set, 338 | .get = amd_sb8xx_gpio_get, 339 | .get_direction = amd_sb8xx_gpio_get_direction, 340 | .direction_output = amd_sb8xx_gpio_dirout, 341 | .direction_input = amd_sb8xx_gpio_dirin, 342 | }, 343 | }; 344 | 345 | static u8 amd_pm_read(u8 reg) 346 | { 347 | outb_p(reg, PM_IDX_REG); 348 | return inb_p(PM_DATA_REG); 349 | } 350 | 351 | static u32 amd_acpi_mmio_base(struct device *dev) 352 | { 353 | u32 bar = 0; 354 | struct resource *pm_region; 355 | 356 | pm_region = devm_request_region(dev, PM_REG_BASE, PM_REG_SIZE, 357 | "AMD PM IO"); 358 | if (!pm_region) { 359 | dev_err(dev, "AMD PM IO already in use.\n"); 360 | return 0; 361 | } 362 | 363 | bar = amd_pm_read(PM_ACPI_MMIO_BAR); 364 | bar |= amd_pm_read(PM_ACPI_MMIO_BAR + 1) << 8; 365 | bar |= amd_pm_read(PM_ACPI_MMIO_BAR + 2) << 16; 366 | bar |= amd_pm_read(PM_ACPI_MMIO_BAR + 3) << 24; 367 | 368 | if (bar == 0x00000000 || bar == 0xffffffff) { 369 | dev_err(dev, "Could not read AMD ACPI MMIO base address.\n"); 370 | bar = 0; 371 | goto out_release_pmio; 372 | } 373 | if ((bar & 3) != 1) { 374 | dev_err(dev, "ACPI registers are not memory mapped.\n"); 375 | bar = 0; 376 | goto out_release_pmio; 377 | } 378 | 379 | out_release_pmio: 380 | devm_release_region(dev, PM_REG_BASE, PM_REG_SIZE); 381 | 382 | return bar & ~3; 383 | } 384 | 385 | static struct pci_dev *amd_get_sb_pcidev(void) 386 | { 387 | struct pci_dev *dev = NULL; 388 | const struct pci_device_id *ent; 389 | 390 | /* We look for our device - AMD South Bridge 391 | * I don't know about a system with two such bridges, 392 | * so we can assume that there is max. one device. 393 | * 394 | * We can't use plain pci_driver mechanism, 395 | * as the device is really a multiple function device, 396 | * main driver that binds to the pci_device is an smbus 397 | * driver and have to find & bind to the device this way. 398 | */ 399 | for_each_pci_dev(dev) { 400 | ent = pci_match_id(pci_tbl, dev); 401 | if (ent) 402 | return dev; 403 | } 404 | 405 | return NULL; 406 | } 407 | 408 | static int __init amd_sb8xx_gpio_init(void) 409 | { 410 | int ret = -ENODEV; 411 | struct pci_dev *pcidev = amd_get_sb_pcidev(); 412 | u32 acpi_bar = 0; 413 | 414 | if (!pcidev) 415 | goto err; 416 | 417 | if (pcidev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && 418 | pcidev->revision < 0x40) { 419 | /* Southbridges before SB800 handle GPIO differently. */ 420 | goto err; 421 | } 422 | 423 | gp.dev = &pcidev->dev; 424 | 425 | acpi_bar = amd_acpi_mmio_base(gp.dev); 426 | if (!acpi_bar) { 427 | dev_err(gp.dev, "Unable to find MMIO base address!\n"); 428 | goto err; 429 | } 430 | 431 | gp.gpio_base = (acpi_bar & ~3) + GPIO_OFFSET; 432 | gp.iomux_base = (acpi_bar & ~3) + IOMUX_OFFSET; 433 | 434 | if (!devm_request_mem_region(gp.dev, gp.gpio_base, GPIO_SIZE, 435 | "AMD SB8XX GPIO")) { 436 | dev_err(gp.dev, "GPIO region 0x%x already in use!\n", 437 | gp.gpio_base); 438 | ret = -EBUSY; 439 | goto err; 440 | } 441 | if (!devm_request_mem_region(gp.dev, gp.iomux_base, IOMUX_SIZE, 442 | "AMD SB8XX IOMUX")) { 443 | dev_err(gp.dev, "IOMUX region 0x%x already in use!\n", 444 | gp.iomux_base); 445 | ret = -EBUSY; 446 | goto err_release_gpio; 447 | } 448 | 449 | gp.gpio = devm_ioremap(gp.dev, gp.gpio_base, GPIO_SIZE); 450 | if (!gp.gpio) { 451 | dev_err(gp.dev, "Couldn't map GPIO region\n"); 452 | ret = -ENOMEM; 453 | goto err_release_iomux; 454 | } 455 | gp.iomux = devm_ioremap(gp.dev, gp.iomux_base, IOMUX_SIZE); 456 | if (!gp.gpio) { 457 | dev_err(gp.dev, "Couldn't map IOMUX region\n"); 458 | ret = -ENOMEM; 459 | goto err_release_iomux; 460 | } 461 | 462 | 463 | gp.dev = gp.dev; 464 | //gp.chip.dev = gp.dev; 465 | 466 | spin_lock_init(&gp.lock); 467 | 468 | ret = gpiochip_add(&gp.chip); 469 | if (ret) { 470 | dev_err(gp.dev, "Registering gpiochip failed\n"); 471 | goto err_release_iomux; 472 | } 473 | 474 | return 0; 475 | 476 | err_release_iomux: 477 | devm_release_mem_region(gp.dev, gp.iomux_base, GPIO_SIZE); 478 | err_release_gpio: 479 | devm_release_mem_region(gp.dev, gp.gpio_base, GPIO_SIZE); 480 | err: 481 | return ret; 482 | } 483 | 484 | static void __exit amd_sb8xx_gpio_exit(void) 485 | { 486 | gpiochip_remove(&gp.chip); 487 | devm_release_mem_region(gp.dev, gp.iomux_base, GPIO_SIZE); 488 | devm_release_mem_region(gp.dev, gp.gpio_base, GPIO_SIZE); 489 | } 490 | 491 | module_init(amd_sb8xx_gpio_init); 492 | module_exit(amd_sb8xx_gpio_exit); 493 | 494 | MODULE_AUTHOR("Tobias Diedrich"); 495 | MODULE_DESCRIPTION("GPIO driver for AMD SB8XX/SB9XX/A5X/A8X chipsets"); 496 | MODULE_LICENSE("GPL"); 497 | 498 | -------------------------------------------------------------------------------- /src/i2c-piix4/i2c-piix4.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1998 - 2002 Frodo Looijaard and 3 | Philip Edelbrock 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | */ 15 | 16 | /* 17 | Supports: 18 | Intel PIIX4, 440MX 19 | Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 20 | ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800 21 | AMD Hudson-2, ML, CZ 22 | SMSC Victory66 23 | 24 | Note: we assume there can only be one device, with one or more 25 | SMBus interfaces. 26 | The device can register multiple i2c_adapters (up to PIIX4_MAX_ADAPTERS). 27 | For devices supporting multiple ports the i2c_adapter should provide 28 | an i2c_algorithm to access them. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | 45 | /* PIIX4 SMBus address offsets */ 46 | #define SMBHSTSTS (0 + piix4_smba) 47 | #define SMBHSLVSTS (1 + piix4_smba) 48 | #define SMBHSTCNT (2 + piix4_smba) 49 | #define SMBHSTCMD (3 + piix4_smba) 50 | #define SMBHSTADD (4 + piix4_smba) 51 | #define SMBHSTDAT0 (5 + piix4_smba) 52 | #define SMBHSTDAT1 (6 + piix4_smba) 53 | #define SMBBLKDAT (7 + piix4_smba) 54 | #define SMBSLVCNT (8 + piix4_smba) 55 | #define SMBSHDWCMD (9 + piix4_smba) 56 | #define SMBSLVEVT (0xA + piix4_smba) 57 | #define SMBSLVDAT (0xC + piix4_smba) 58 | 59 | /* count for request_region */ 60 | #define SMBIOSIZE 9 61 | 62 | /* PCI Address Constants */ 63 | #define SMBBA 0x090 64 | #define SMBHSTCFG 0x0D2 65 | #define SMBSLVC 0x0D3 66 | #define SMBSHDW1 0x0D4 67 | #define SMBSHDW2 0x0D5 68 | #define SMBREV 0x0D6 69 | 70 | /* Other settings */ 71 | #define MAX_TIMEOUT 500 72 | #define ENABLE_INT9 0 73 | 74 | /* PIIX4 constants */ 75 | #define PIIX4_QUICK 0x00 76 | #define PIIX4_BYTE 0x04 77 | #define PIIX4_BYTE_DATA 0x08 78 | #define PIIX4_WORD_DATA 0x0C 79 | #define PIIX4_BLOCK_DATA 0x14 80 | 81 | /* Multi-port constants */ 82 | #define PIIX4_MAX_ADAPTERS 3 //HACK: Disable port 4 (GPIO 187, 188) 83 | 84 | /* SB800 constants */ 85 | #define SB800_PIIX4_SMB_IDX 0xcd6 86 | 87 | #define KERNCZ_IMC_IDX 0x3e 88 | #define KERNCZ_IMC_DATA 0x3f 89 | 90 | /* 91 | * SB800 port is selected by bits 2:1 of the smb_en register (0x2c) 92 | * or the smb_sel register (0x2e), depending on bit 0 of register 0x2f. 93 | * Hudson-2/Bolton port is always selected by bits 2:1 of register 0x2f. 94 | */ 95 | #define SB800_PIIX4_PORT_IDX 0x2c 96 | #define SB800_PIIX4_PORT_IDX_ALT 0x2e 97 | #define SB800_PIIX4_PORT_IDX_SEL 0x2f 98 | #define SB800_PIIX4_PORT_IDX_MASK 0x06 99 | #define SB800_PIIX4_PORT_IDX_SHIFT 1 100 | 101 | /* On kerncz, SmBus0Sel is at bit 20:19 of PMx00 DecodeEn */ 102 | #define SB800_PIIX4_PORT_IDX_KERNCZ 0x02 103 | #define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18 104 | #define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3 105 | 106 | /* insmod parameters */ 107 | 108 | /* If force is set to anything different from 0, we forcibly enable the 109 | PIIX4. DANGEROUS! */ 110 | static int force; 111 | module_param (force, int, 0); 112 | MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); 113 | 114 | /* If force_addr is set to anything different from 0, we forcibly enable 115 | the PIIX4 at the given address. VERY DANGEROUS! */ 116 | static int force_addr; 117 | module_param(force_addr, int, 0); 118 | MODULE_PARM_DESC(force_addr, 119 | "Forcibly enable the PIIX4 at the given address. " 120 | "EXTREMELY DANGEROUS!"); 121 | 122 | static int srvrworks_csb5_delay; 123 | static struct pci_driver piix4_driver; 124 | 125 | static const struct dmi_system_id piix4_dmi_blacklist[] = { 126 | { 127 | .ident = "Sapphire AM2RD790", 128 | .matches = { 129 | DMI_MATCH(DMI_BOARD_VENDOR, "SAPPHIRE Inc."), 130 | DMI_MATCH(DMI_BOARD_NAME, "PC-AM2RD790"), 131 | }, 132 | }, 133 | { 134 | .ident = "DFI Lanparty UT 790FX", 135 | .matches = { 136 | DMI_MATCH(DMI_BOARD_VENDOR, "DFI Inc."), 137 | DMI_MATCH(DMI_BOARD_NAME, "LP UT 790FX"), 138 | }, 139 | }, 140 | { } 141 | }; 142 | 143 | /* The IBM entry is in a separate table because we only check it 144 | on Intel-based systems */ 145 | static const struct dmi_system_id piix4_dmi_ibm[] = { 146 | { 147 | .ident = "IBM", 148 | .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), }, 149 | }, 150 | { }, 151 | }; 152 | 153 | /* 154 | * SB800 globals 155 | */ 156 | static u8 piix4_port_sel_sb800; 157 | static u8 piix4_port_mask_sb800; 158 | static u8 piix4_port_shift_sb800; 159 | static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = { 160 | " port 0", " port 2", " port 3", " port 4" 161 | }; 162 | static const char *piix4_aux_port_name_sb800 = " port 1"; 163 | 164 | struct i2c_piix4_adapdata { 165 | unsigned short smba; 166 | 167 | /* SB800 */ 168 | bool sb800_main; 169 | bool notify_imc; 170 | u8 port; /* Port number, shifted */ 171 | }; 172 | 173 | static int piix4_setup(struct pci_dev *PIIX4_dev, 174 | const struct pci_device_id *id) 175 | { 176 | unsigned char temp; 177 | unsigned short piix4_smba; 178 | 179 | if ((PIIX4_dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && 180 | (PIIX4_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5)) 181 | srvrworks_csb5_delay = 1; 182 | 183 | /* On some motherboards, it was reported that accessing the SMBus 184 | caused severe hardware problems */ 185 | if (dmi_check_system(piix4_dmi_blacklist)) { 186 | dev_err(&PIIX4_dev->dev, 187 | "Accessing the SMBus on this system is unsafe!\n"); 188 | return -EPERM; 189 | } 190 | 191 | /* Don't access SMBus on IBM systems which get corrupted eeproms */ 192 | if (dmi_check_system(piix4_dmi_ibm) && 193 | PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) { 194 | dev_err(&PIIX4_dev->dev, "IBM system detected; this module " 195 | "may corrupt your serial eeprom! Refusing to load " 196 | "module!\n"); 197 | return -EPERM; 198 | } 199 | 200 | /* Determine the address of the SMBus areas */ 201 | if (force_addr) { 202 | piix4_smba = force_addr & 0xfff0; 203 | force = 0; 204 | } else { 205 | pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); 206 | piix4_smba &= 0xfff0; 207 | if(piix4_smba == 0) { 208 | dev_err(&PIIX4_dev->dev, "SMBus base address " 209 | "uninitialized - upgrade BIOS or use " 210 | "force_addr=0xaddr\n"); 211 | return -ENODEV; 212 | } 213 | } 214 | 215 | if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) 216 | return -ENODEV; 217 | 218 | if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { 219 | dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", 220 | piix4_smba); 221 | return -EBUSY; 222 | } 223 | 224 | pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); 225 | 226 | /* If force_addr is set, we program the new address here. Just to make 227 | sure, we disable the PIIX4 first. */ 228 | if (force_addr) { 229 | pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); 230 | pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); 231 | pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); 232 | dev_info(&PIIX4_dev->dev, "WARNING: SMBus interface set to " 233 | "new address %04x!\n", piix4_smba); 234 | } else if ((temp & 1) == 0) { 235 | if (force) { 236 | /* This should never need to be done, but has been 237 | * noted that many Dell machines have the SMBus 238 | * interface on the PIIX4 disabled!? NOTE: This assumes 239 | * I/O space and other allocations WERE done by the 240 | * Bios! Don't complain if your hardware does weird 241 | * things after enabling this. :') Check for Bios 242 | * updates before resorting to this. 243 | */ 244 | pci_write_config_byte(PIIX4_dev, SMBHSTCFG, 245 | temp | 1); 246 | dev_notice(&PIIX4_dev->dev, 247 | "WARNING: SMBus interface has been FORCEFULLY ENABLED!\n"); 248 | } else { 249 | dev_err(&PIIX4_dev->dev, 250 | "SMBus Host Controller not enabled!\n"); 251 | release_region(piix4_smba, SMBIOSIZE); 252 | return -ENODEV; 253 | } 254 | } 255 | 256 | if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2)) 257 | dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); 258 | else if ((temp & 0x0E) == 0) 259 | dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); 260 | else 261 | dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration " 262 | "(or code out of date)!\n"); 263 | 264 | pci_read_config_byte(PIIX4_dev, SMBREV, &temp); 265 | dev_info(&PIIX4_dev->dev, 266 | "SMBus Host Controller at 0x%x, revision %d\n", 267 | piix4_smba, temp); 268 | 269 | return piix4_smba; 270 | } 271 | 272 | static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, 273 | const struct pci_device_id *id, u8 aux) 274 | { 275 | unsigned short piix4_smba; 276 | u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status, port_sel; 277 | u8 i2ccfg, i2ccfg_offset = 0x10; 278 | 279 | /* SB800 and later SMBus does not support forcing address */ 280 | if (force || force_addr) { 281 | dev_err(&PIIX4_dev->dev, "SMBus does not support " 282 | "forcing address!\n"); 283 | return -EINVAL; 284 | } 285 | 286 | /* Determine the address of the SMBus areas */ 287 | if ((PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && 288 | PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && 289 | PIIX4_dev->revision >= 0x41) || 290 | (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && 291 | PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && 292 | PIIX4_dev->revision >= 0x49)) 293 | smb_en = 0x00; 294 | else 295 | smb_en = (aux) ? 0x28 : 0x2c; 296 | 297 | if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) { 298 | dev_err(&PIIX4_dev->dev, 299 | "SMB base address index region 0x%x already in use.\n", 300 | SB800_PIIX4_SMB_IDX); 301 | return -EBUSY; 302 | } 303 | 304 | outb_p(smb_en, SB800_PIIX4_SMB_IDX); 305 | smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); 306 | outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); 307 | smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); 308 | 309 | release_region(SB800_PIIX4_SMB_IDX, 2); 310 | 311 | if (!smb_en) { 312 | smb_en_status = smba_en_lo & 0x10; 313 | piix4_smba = smba_en_hi << 8; 314 | if (aux) 315 | piix4_smba |= 0x20; 316 | } else { 317 | smb_en_status = smba_en_lo & 0x01; 318 | piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; 319 | } 320 | 321 | if (!smb_en_status) { 322 | dev_err(&PIIX4_dev->dev, 323 | "SMBus Host Controller not enabled!\n"); 324 | return -ENODEV; 325 | } 326 | 327 | if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) 328 | return -ENODEV; 329 | 330 | if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { 331 | dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", 332 | piix4_smba); 333 | return -EBUSY; 334 | } 335 | 336 | /* Aux SMBus does not support IRQ information */ 337 | if (aux) { 338 | dev_info(&PIIX4_dev->dev, 339 | "Auxiliary SMBus Host Controller at 0x%x\n", 340 | piix4_smba); 341 | return piix4_smba; 342 | } 343 | 344 | /* Request the SMBus I2C bus config region */ 345 | if (!request_region(piix4_smba + i2ccfg_offset, 1, "i2ccfg")) { 346 | dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region " 347 | "0x%x already in use!\n", piix4_smba + i2ccfg_offset); 348 | release_region(piix4_smba, SMBIOSIZE); 349 | return -EBUSY; 350 | } 351 | i2ccfg = inb_p(piix4_smba + i2ccfg_offset); 352 | release_region(piix4_smba + i2ccfg_offset, 1); 353 | 354 | if (i2ccfg & 1) 355 | dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); 356 | else 357 | dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); 358 | 359 | dev_info(&PIIX4_dev->dev, 360 | "SMBus Host Controller at 0x%x, revision %d\n", 361 | piix4_smba, i2ccfg >> 4); 362 | 363 | /* Find which register is used for port selection */ 364 | if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD) { 365 | switch (PIIX4_dev->device) { 366 | case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS: 367 | piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ; 368 | piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK_KERNCZ; 369 | piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ; 370 | break; 371 | case PCI_DEVICE_ID_AMD_HUDSON2_SMBUS: 372 | default: 373 | piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT; 374 | piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; 375 | piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; 376 | break; 377 | } 378 | } else { 379 | if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, 380 | "sb800_piix4_smb")) { 381 | release_region(piix4_smba, SMBIOSIZE); 382 | return -EBUSY; 383 | } 384 | 385 | outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX); 386 | port_sel = inb_p(SB800_PIIX4_SMB_IDX + 1); 387 | piix4_port_sel_sb800 = (port_sel & 0x01) ? 388 | SB800_PIIX4_PORT_IDX_ALT : 389 | SB800_PIIX4_PORT_IDX; 390 | piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; 391 | piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; 392 | release_region(SB800_PIIX4_SMB_IDX, 2); 393 | } 394 | 395 | dev_info(&PIIX4_dev->dev, 396 | "Using register 0x%02x for SMBus port selection\n", 397 | (unsigned int)piix4_port_sel_sb800); 398 | 399 | return piix4_smba; 400 | } 401 | 402 | static int piix4_setup_aux(struct pci_dev *PIIX4_dev, 403 | const struct pci_device_id *id, 404 | unsigned short base_reg_addr) 405 | { 406 | /* Set up auxiliary SMBus controllers found on some 407 | * AMD chipsets e.g. SP5100 (SB700 derivative) */ 408 | 409 | unsigned short piix4_smba; 410 | 411 | /* Read address of auxiliary SMBus controller */ 412 | pci_read_config_word(PIIX4_dev, base_reg_addr, &piix4_smba); 413 | if ((piix4_smba & 1) == 0) { 414 | dev_dbg(&PIIX4_dev->dev, 415 | "Auxiliary SMBus controller not enabled\n"); 416 | return -ENODEV; 417 | } 418 | 419 | piix4_smba &= 0xfff0; 420 | if (piix4_smba == 0) { 421 | dev_dbg(&PIIX4_dev->dev, 422 | "Auxiliary SMBus base address uninitialized\n"); 423 | return -ENODEV; 424 | } 425 | 426 | if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) 427 | return -ENODEV; 428 | 429 | if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { 430 | dev_err(&PIIX4_dev->dev, "Auxiliary SMBus region 0x%x " 431 | "already in use!\n", piix4_smba); 432 | return -EBUSY; 433 | } 434 | 435 | dev_info(&PIIX4_dev->dev, 436 | "Auxiliary SMBus Host Controller at 0x%x\n", 437 | piix4_smba); 438 | 439 | return piix4_smba; 440 | } 441 | 442 | static int piix4_transaction(struct i2c_adapter *piix4_adapter) 443 | { 444 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter); 445 | unsigned short piix4_smba = adapdata->smba; 446 | int temp; 447 | int result = 0; 448 | int timeout = 0; 449 | 450 | dev_dbg(&piix4_adapter->dev, "Transaction (pre): CNT=%02x, CMD=%02x, " 451 | "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), 452 | inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 453 | inb_p(SMBHSTDAT1)); 454 | 455 | /* Make sure the SMBus host is ready to start transmitting */ 456 | if ((temp = inb_p(SMBHSTSTS)) != 0x00) { 457 | dev_dbg(&piix4_adapter->dev, "SMBus busy (%02x). " 458 | "Resetting...\n", temp); 459 | outb_p(temp, SMBHSTSTS); 460 | if ((temp = inb_p(SMBHSTSTS)) != 0x00) { 461 | dev_err(&piix4_adapter->dev, "Failed! (%02x)\n", temp); 462 | return -EBUSY; 463 | } else { 464 | dev_dbg(&piix4_adapter->dev, "Successful!\n"); 465 | } 466 | } 467 | 468 | /* start the transaction by setting bit 6 */ 469 | outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); 470 | 471 | /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ 472 | if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */ 473 | usleep_range(2000, 2100); 474 | else 475 | usleep_range(250, 500); 476 | 477 | while ((++timeout < MAX_TIMEOUT) && 478 | ((temp = inb_p(SMBHSTSTS)) & 0x01)) 479 | usleep_range(250, 500); 480 | 481 | /* If the SMBus is still busy, we give up */ 482 | if (timeout == MAX_TIMEOUT) { 483 | dev_err(&piix4_adapter->dev, "SMBus Timeout!\n"); 484 | result = -ETIMEDOUT; 485 | } 486 | 487 | if (temp & 0x10) { 488 | result = -EIO; 489 | dev_err(&piix4_adapter->dev, "Error: Failed bus transaction\n"); 490 | } 491 | 492 | if (temp & 0x08) { 493 | result = -EIO; 494 | dev_dbg(&piix4_adapter->dev, "Bus collision! SMBus may be " 495 | "locked until next hard reset. (sorry!)\n"); 496 | /* Clock stops and slave is stuck in mid-transmission */ 497 | } 498 | 499 | if (temp & 0x04) { 500 | result = -ENXIO; 501 | dev_dbg(&piix4_adapter->dev, "Error: no response!\n"); 502 | } 503 | 504 | if (inb_p(SMBHSTSTS) != 0x00) 505 | outb_p(inb(SMBHSTSTS), SMBHSTSTS); 506 | 507 | if ((temp = inb_p(SMBHSTSTS)) != 0x00) { 508 | dev_err(&piix4_adapter->dev, "Failed reset at end of " 509 | "transaction (%02x)\n", temp); 510 | } 511 | dev_dbg(&piix4_adapter->dev, "Transaction (post): CNT=%02x, CMD=%02x, " 512 | "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), 513 | inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 514 | inb_p(SMBHSTDAT1)); 515 | return result; 516 | } 517 | 518 | /* Return negative errno on error. */ 519 | static s32 piix4_access(struct i2c_adapter * adap, u16 addr, 520 | unsigned short flags, char read_write, 521 | u8 command, int size, union i2c_smbus_data * data) 522 | { 523 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); 524 | unsigned short piix4_smba = adapdata->smba; 525 | int i, len; 526 | int status; 527 | 528 | switch (size) { 529 | case I2C_SMBUS_QUICK: 530 | outb_p((addr << 1) | read_write, 531 | SMBHSTADD); 532 | size = PIIX4_QUICK; 533 | break; 534 | case I2C_SMBUS_BYTE: 535 | outb_p((addr << 1) | read_write, 536 | SMBHSTADD); 537 | if (read_write == I2C_SMBUS_WRITE) 538 | outb_p(command, SMBHSTCMD); 539 | size = PIIX4_BYTE; 540 | break; 541 | case I2C_SMBUS_BYTE_DATA: 542 | outb_p((addr << 1) | read_write, 543 | SMBHSTADD); 544 | outb_p(command, SMBHSTCMD); 545 | if (read_write == I2C_SMBUS_WRITE) 546 | outb_p(data->byte, SMBHSTDAT0); 547 | size = PIIX4_BYTE_DATA; 548 | break; 549 | case I2C_SMBUS_WORD_DATA: 550 | outb_p((addr << 1) | read_write, 551 | SMBHSTADD); 552 | outb_p(command, SMBHSTCMD); 553 | if (read_write == I2C_SMBUS_WRITE) { 554 | outb_p(data->word & 0xff, SMBHSTDAT0); 555 | outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); 556 | } 557 | size = PIIX4_WORD_DATA; 558 | break; 559 | case I2C_SMBUS_BLOCK_DATA: 560 | outb_p((addr << 1) | read_write, 561 | SMBHSTADD); 562 | outb_p(command, SMBHSTCMD); 563 | if (read_write == I2C_SMBUS_WRITE) { 564 | len = data->block[0]; 565 | if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) 566 | return -EINVAL; 567 | outb_p(len, SMBHSTDAT0); 568 | inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ 569 | for (i = 1; i <= len; i++) 570 | outb_p(data->block[i], SMBBLKDAT); 571 | } 572 | size = PIIX4_BLOCK_DATA; 573 | break; 574 | default: 575 | dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 576 | return -EOPNOTSUPP; 577 | } 578 | 579 | outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); 580 | 581 | status = piix4_transaction(adap); 582 | if (status) 583 | return status; 584 | 585 | if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) 586 | return 0; 587 | 588 | 589 | switch (size) { 590 | case PIIX4_BYTE: 591 | case PIIX4_BYTE_DATA: 592 | data->byte = inb_p(SMBHSTDAT0); 593 | break; 594 | case PIIX4_WORD_DATA: 595 | data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); 596 | break; 597 | case PIIX4_BLOCK_DATA: 598 | data->block[0] = inb_p(SMBHSTDAT0); 599 | if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) 600 | return -EPROTO; 601 | inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ 602 | for (i = 1; i <= data->block[0]; i++) 603 | data->block[i] = inb_p(SMBBLKDAT); 604 | break; 605 | } 606 | return 0; 607 | } 608 | 609 | static uint8_t piix4_imc_read(uint8_t idx) 610 | { 611 | outb_p(idx, KERNCZ_IMC_IDX); 612 | return inb_p(KERNCZ_IMC_DATA); 613 | } 614 | 615 | static void piix4_imc_write(uint8_t idx, uint8_t value) 616 | { 617 | outb_p(idx, KERNCZ_IMC_IDX); 618 | outb_p(value, KERNCZ_IMC_DATA); 619 | } 620 | 621 | static int piix4_imc_sleep(void) 622 | { 623 | int timeout = MAX_TIMEOUT; 624 | 625 | if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) 626 | return -EBUSY; 627 | 628 | /* clear response register */ 629 | piix4_imc_write(0x82, 0x00); 630 | /* request ownership flag */ 631 | piix4_imc_write(0x83, 0xB4); 632 | /* kick off IMC Mailbox command 96 */ 633 | piix4_imc_write(0x80, 0x96); 634 | 635 | while (timeout--) { 636 | if (piix4_imc_read(0x82) == 0xfa) { 637 | release_region(KERNCZ_IMC_IDX, 2); 638 | return 0; 639 | } 640 | usleep_range(1000, 2000); 641 | } 642 | 643 | release_region(KERNCZ_IMC_IDX, 2); 644 | return -ETIMEDOUT; 645 | } 646 | 647 | static void piix4_imc_wakeup(void) 648 | { 649 | int timeout = MAX_TIMEOUT; 650 | 651 | if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) 652 | return; 653 | 654 | /* clear response register */ 655 | piix4_imc_write(0x82, 0x00); 656 | /* release ownership flag */ 657 | piix4_imc_write(0x83, 0xB5); 658 | /* kick off IMC Mailbox command 96 */ 659 | piix4_imc_write(0x80, 0x96); 660 | 661 | while (timeout--) { 662 | if (piix4_imc_read(0x82) == 0xfa) 663 | break; 664 | usleep_range(1000, 2000); 665 | } 666 | 667 | release_region(KERNCZ_IMC_IDX, 2); 668 | } 669 | 670 | /* 671 | * Handles access to multiple SMBus ports on the SB800. 672 | * The port is selected by bits 2:1 of the smb_en register (0x2c). 673 | * Returns negative errno on error. 674 | * 675 | * Note: The selected port must be returned to the initial selection to avoid 676 | * problems on certain systems. 677 | */ 678 | static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, 679 | unsigned short flags, char read_write, 680 | u8 command, int size, union i2c_smbus_data *data) 681 | { 682 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); 683 | unsigned short piix4_smba = adapdata->smba; 684 | int retries = MAX_TIMEOUT; 685 | int smbslvcnt; 686 | u8 smba_en_lo; 687 | u8 port; 688 | int retval; 689 | 690 | if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) 691 | return -EBUSY; 692 | 693 | /* Request the SMBUS semaphore, avoid conflicts with the IMC */ 694 | smbslvcnt = inb_p(SMBSLVCNT); 695 | do { 696 | outb_p(smbslvcnt | 0x10, SMBSLVCNT); 697 | 698 | /* Check the semaphore status */ 699 | smbslvcnt = inb_p(SMBSLVCNT); 700 | if (smbslvcnt & 0x10) 701 | break; 702 | 703 | usleep_range(1000, 2000); 704 | } while (--retries); 705 | /* SMBus is still owned by the IMC, we give up */ 706 | if (!retries) { 707 | retval = -EBUSY; 708 | goto release; 709 | } 710 | 711 | /* 712 | * Notify the IMC (Integrated Micro Controller) if required. 713 | * Among other responsibilities, the IMC is in charge of monitoring 714 | * the System fans and temperature sensors, and act accordingly. 715 | * All this is done through SMBus and can/will collide 716 | * with our transactions if they are long (BLOCK_DATA). 717 | * Therefore we need to request the ownership flag during those 718 | * transactions. 719 | */ 720 | if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) { 721 | int ret; 722 | 723 | ret = piix4_imc_sleep(); 724 | switch (ret) { 725 | case -EBUSY: 726 | dev_warn(&adap->dev, 727 | "IMC base address index region 0x%x already in use.\n", 728 | KERNCZ_IMC_IDX); 729 | break; 730 | case -ETIMEDOUT: 731 | dev_warn(&adap->dev, 732 | "Failed to communicate with the IMC.\n"); 733 | break; 734 | default: 735 | break; 736 | } 737 | 738 | /* If IMC communication fails do not retry */ 739 | if (ret) { 740 | dev_warn(&adap->dev, 741 | "Continuing without IMC notification.\n"); 742 | adapdata->notify_imc = false; 743 | } 744 | } 745 | 746 | outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); 747 | smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); 748 | 749 | port = adapdata->port; 750 | if ((smba_en_lo & piix4_port_mask_sb800) != port) 751 | outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port, 752 | SB800_PIIX4_SMB_IDX + 1); 753 | 754 | retval = piix4_access(adap, addr, flags, read_write, 755 | command, size, data); 756 | 757 | outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1); 758 | 759 | /* Release the semaphore */ 760 | outb_p(smbslvcnt | 0x20, SMBSLVCNT); 761 | 762 | if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) 763 | piix4_imc_wakeup(); 764 | 765 | release: 766 | release_region(SB800_PIIX4_SMB_IDX, 2); 767 | return retval; 768 | } 769 | 770 | static u32 piix4_func(struct i2c_adapter *adapter) 771 | { 772 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 773 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 774 | I2C_FUNC_SMBUS_BLOCK_DATA; 775 | } 776 | 777 | static const struct i2c_algorithm smbus_algorithm = { 778 | .smbus_xfer = piix4_access, 779 | .functionality = piix4_func, 780 | }; 781 | 782 | static const struct i2c_algorithm piix4_smbus_algorithm_sb800 = { 783 | .smbus_xfer = piix4_access_sb800, 784 | .functionality = piix4_func, 785 | }; 786 | 787 | static const struct pci_device_id piix4_ids[] = { 788 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, 789 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, 790 | { PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3) }, 791 | { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS) }, 792 | { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, 793 | { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, 794 | { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, 795 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, 796 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) }, 797 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 798 | PCI_DEVICE_ID_SERVERWORKS_OSB4) }, 799 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 800 | PCI_DEVICE_ID_SERVERWORKS_CSB5) }, 801 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 802 | PCI_DEVICE_ID_SERVERWORKS_CSB6) }, 803 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 804 | PCI_DEVICE_ID_SERVERWORKS_HT1000SB) }, 805 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 806 | PCI_DEVICE_ID_SERVERWORKS_HT1100LD) }, 807 | { 0, } 808 | }; 809 | 810 | MODULE_DEVICE_TABLE (pci, piix4_ids); 811 | 812 | static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS]; 813 | static struct i2c_adapter *piix4_aux_adapter; 814 | 815 | static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, 816 | bool sb800_main, u8 port, bool notify_imc, 817 | const char *name, struct i2c_adapter **padap) 818 | { 819 | struct i2c_adapter *adap; 820 | struct i2c_piix4_adapdata *adapdata; 821 | int retval; 822 | 823 | adap = kzalloc(sizeof(*adap), GFP_KERNEL); 824 | if (adap == NULL) { 825 | release_region(smba, SMBIOSIZE); 826 | return -ENOMEM; 827 | } 828 | 829 | adap->owner = THIS_MODULE; 830 | adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 831 | adap->algo = sb800_main ? &piix4_smbus_algorithm_sb800 832 | : &smbus_algorithm; 833 | 834 | adapdata = kzalloc(sizeof(*adapdata), GFP_KERNEL); 835 | if (adapdata == NULL) { 836 | kfree(adap); 837 | release_region(smba, SMBIOSIZE); 838 | return -ENOMEM; 839 | } 840 | 841 | adapdata->smba = smba; 842 | adapdata->sb800_main = sb800_main; 843 | adapdata->port = port << piix4_port_shift_sb800; 844 | adapdata->notify_imc = notify_imc; 845 | 846 | /* set up the sysfs linkage to our parent device */ 847 | adap->dev.parent = &dev->dev; 848 | 849 | snprintf(adap->name, sizeof(adap->name), 850 | "SMBus PIIX4 adapter%s at %04x", name, smba); 851 | 852 | i2c_set_adapdata(adap, adapdata); 853 | 854 | retval = i2c_add_adapter(adap); 855 | if (retval) { 856 | kfree(adapdata); 857 | kfree(adap); 858 | release_region(smba, SMBIOSIZE); 859 | return retval; 860 | } 861 | 862 | *padap = adap; 863 | return 0; 864 | } 865 | 866 | static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba, 867 | bool notify_imc) 868 | { 869 | struct i2c_piix4_adapdata *adapdata; 870 | int port; 871 | int retval; 872 | 873 | for (port = 0; port < PIIX4_MAX_ADAPTERS; port++) { 874 | retval = piix4_add_adapter(dev, smba, true, port, notify_imc, 875 | piix4_main_port_names_sb800[port], 876 | &piix4_main_adapters[port]); 877 | if (retval < 0) 878 | goto error; 879 | } 880 | 881 | return retval; 882 | 883 | error: 884 | dev_err(&dev->dev, 885 | "Error setting up SB800 adapters. Unregistering!\n"); 886 | while (--port >= 0) { 887 | adapdata = i2c_get_adapdata(piix4_main_adapters[port]); 888 | if (adapdata->smba) { 889 | i2c_del_adapter(piix4_main_adapters[port]); 890 | kfree(adapdata); 891 | kfree(piix4_main_adapters[port]); 892 | piix4_main_adapters[port] = NULL; 893 | } 894 | } 895 | 896 | return retval; 897 | } 898 | 899 | static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) 900 | { 901 | int retval; 902 | bool is_sb800 = false; 903 | 904 | if ((dev->vendor == PCI_VENDOR_ID_ATI && 905 | dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && 906 | dev->revision >= 0x40) || 907 | dev->vendor == PCI_VENDOR_ID_AMD) { 908 | bool notify_imc = false; 909 | is_sb800 = true; 910 | 911 | if (dev->vendor == PCI_VENDOR_ID_AMD && 912 | dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) { 913 | u8 imc; 914 | 915 | /* 916 | * Detect if IMC is active or not, this method is 917 | * described on coreboot's AMD IMC notes 918 | */ 919 | pci_bus_read_config_byte(dev->bus, PCI_DEVFN(0x14, 3), 920 | 0x40, &imc); 921 | if (imc & 0x80) 922 | notify_imc = true; 923 | } 924 | 925 | /* base address location etc changed in SB800 */ 926 | retval = piix4_setup_sb800(dev, id, 0); 927 | if (retval < 0) 928 | return retval; 929 | 930 | /* 931 | * Try to register multiplexed main SMBus adapter, 932 | * give up if we can't 933 | */ 934 | retval = piix4_add_adapters_sb800(dev, retval, notify_imc); 935 | if (retval < 0) 936 | return retval; 937 | } else { 938 | retval = piix4_setup(dev, id); 939 | if (retval < 0) 940 | return retval; 941 | 942 | /* Try to register main SMBus adapter, give up if we can't */ 943 | retval = piix4_add_adapter(dev, retval, false, 0, false, "", 944 | &piix4_main_adapters[0]); 945 | if (retval < 0) 946 | return retval; 947 | } 948 | 949 | /* Check for auxiliary SMBus on some AMD chipsets */ 950 | retval = -ENODEV; 951 | 952 | if (dev->vendor == PCI_VENDOR_ID_ATI && 953 | dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS) { 954 | if (dev->revision < 0x40) { 955 | retval = piix4_setup_aux(dev, id, 0x58); 956 | } else { 957 | /* SB800 added aux bus too */ 958 | retval = piix4_setup_sb800(dev, id, 1); 959 | } 960 | } 961 | 962 | if (dev->vendor == PCI_VENDOR_ID_AMD && 963 | dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) { 964 | retval = piix4_setup_sb800(dev, id, 1); 965 | } 966 | 967 | if (retval > 0) { 968 | /* Try to add the aux adapter if it exists, 969 | * piix4_add_adapter will clean up if this fails */ 970 | piix4_add_adapter(dev, retval, false, 0, false, 971 | is_sb800 ? piix4_aux_port_name_sb800 : "", 972 | &piix4_aux_adapter); 973 | } 974 | 975 | return 0; 976 | } 977 | 978 | static void piix4_adap_remove(struct i2c_adapter *adap) 979 | { 980 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); 981 | 982 | if (adapdata->smba) { 983 | i2c_del_adapter(adap); 984 | if (adapdata->port == (0 << piix4_port_shift_sb800)) 985 | release_region(adapdata->smba, SMBIOSIZE); 986 | kfree(adapdata); 987 | kfree(adap); 988 | } 989 | } 990 | 991 | static void piix4_remove(struct pci_dev *dev) 992 | { 993 | int port = PIIX4_MAX_ADAPTERS; 994 | 995 | while (--port >= 0) { 996 | if (piix4_main_adapters[port]) { 997 | piix4_adap_remove(piix4_main_adapters[port]); 998 | piix4_main_adapters[port] = NULL; 999 | } 1000 | } 1001 | 1002 | if (piix4_aux_adapter) { 1003 | piix4_adap_remove(piix4_aux_adapter); 1004 | piix4_aux_adapter = NULL; 1005 | } 1006 | } 1007 | 1008 | static struct pci_driver piix4_driver = { 1009 | .name = "piix4_smbus", 1010 | .id_table = piix4_ids, 1011 | .probe = piix4_probe, 1012 | .remove = piix4_remove, 1013 | }; 1014 | 1015 | module_pci_driver(piix4_driver); 1016 | 1017 | MODULE_AUTHOR("Frodo Looijaard and " 1018 | "Philip Edelbrock "); 1019 | MODULE_DESCRIPTION("PIIX4 SMBus driver"); 1020 | MODULE_LICENSE("GPL"); 1021 | 1022 | --------------------------------------------------------------------------------