# CDS9K Linux Kernel Drivers This repo houses Linux kernel drivers for the CDS9K FPGA/CPLD. This is a semi-fictional piece of hardware I'm developing for classroom/demo sessions on various aspects of Linux kernel driver development - it's not on any real boards, it's just a teaching tool. I do have a VHDL implementation which I will include once the IP blocks are more stable. Unless otherwise noted, for all reserved bits in registers, read as "don't care" and write as 0. ## IP Block: `cds9k-fan` The CDS9K fan control block allows PWM speed control of a single fan, and readback of the fan's tachometer. ### Register 0x0: FAN\_PWM Mode: read-write Write this register to set the fan PWM duty. | Bits | 15 - 8 | 7 - 0 | | ---- | -------- | --------- | | Use | reserved | duty\_val | The value for `duty_val` can be determined as: duty_val = duty_percent * 2.55 i.e. a linear scaling from 0-100% to 0x0-0xFF ### Register 0x1: FAN\_TACH Mode: read-only Read this register to determine the fan tachometer reading in revolutions per minute (RPM). | Bits | 15 - 0 | | ---- | -------- | | Use | fan\_rpm | ## IP Block: `cds9k-led` The CDS9K LED control block allows PWM brightness control of a single LED (or LED die, in the case of multi-colour LEDs) as well as hardware-accelerated blinking with 50% duty and variable period. Two signals are generated internally: blink and PWM. These are ANDed together to form the LED output. A sample waveform diagram (relative timings not to scale): blink ---------------- ---------------- - ---------------- ---------------- PWM - - - - - - - - - - - - - ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- led - - - - - - - drive ---- ---- ---- ------------------- ---- ---- ------------------- The `blink` signal has a fixed duty of 50% and a period programmable between 20 milliseconds and 5.1 seconds. The PWM signal has a fixed period of 40 microseconds (i.e. 25 kHz), and a duty programmable between 0 and 100%. ### Register 0x0: LED\_PWM\_DUTY Mode: read-write Write this register to set the LED PWM duty. The PWM duty can be calculated as: | Bits | 15 - 8 | 7 - 0 | | ---- | -------- | --------- | | Use | reserved | duty\_val | The value for `duty_val` can be determined as: duty_val = duty_percent * 2.55 i.e. a linear scaling from 0-100% to 0x0-0xFF ### Register 0x0: LED\_BLINK\_PERIOD Mode: read-write Write this register to set the LED blink period. When a non-zero value is written to this register, the LED output is gated by a 50% duty square wave with period: period_val = 20 milliseconds * LED_BLINK_PERIOD In this way, the LED can be blinked on and off with 50% duty at a programmable frequency. When the LED should be enabled during each cycle, it is driven at the PWM brightness programmed in the `LED_PWM_DUTY` register. | Bits | 15 - 8 | 7 - 0 | | ---- | -------- | ----------- | | Use | reserved | period\_val | ## IP Block: `cds9k-gpio` The CDS9K GPIO block provides access to 16 basic I/O lines. Interrupts and pull-ups/pull-downs are not available. GPIO lines are individually selectable between input/hi-z and output. ### Register: 0x0: PORT Mode: read-write | Bits | 15 - 0 | | ---- | ------ | | Use | port | ### Register: 0x1: DIRECTION Mode: read-write | Bits | 15 - 0 | | ---- | --------------- | | Use | direction\_mask | ## IP Block: `cds9k-reset` The CDS9K reset control block exposes a single reset line which can be asserted and deasserted by writing different magic values to a single 16-bit register. ### Register: 0x0: REG0 Mode: read-write Write as 0xDEAD to assert the reset, and write as 0x0000 to deassert the reset. | Bits | 15 - 0 | | ---- | ------------ | | Use | reset\_magic | ### Register: 0x1: RESERVED Mode: read-only Write as 0xDEAD to assert the reset, and write as 0x0000 to deassert the reset. | Bits | 15 - 0 | | ---- | -------- | | Use | reserved | ## Misc drivers There is currently one misc driver in this repo too: `simple-reset-consumer`. On probe, this driver takes a single reset from the device-tree and asks it to reset. This is the quickest way I had for hackily debugging the effect of a reset driver such as cds9k-reset, as there doesn't appear to be any sysfs or debugfs nodes exposed for reset controllers in general. If you include a segment in your device tree like: some_reset_consumer { compatible = "david,simple-reset-consumer"; resets = <&some_reset>; status = "okay"; }; Then firing the resets looks like: root@de10-nano:~# rmmod simple-reset-consumer root@de10-nano:~# modprobe simple-reset-consumer [ 995.241046] simple-reset-consumer some_reset_consumer: firing reset root@de10-nano:~#