aboutsummaryrefslogtreecommitdiff

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

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:~#