树莓派 -- oled
硬件
SPI0,CE0
SPI Master Driver
设备树
arch\arm\boot\dts\bcm2710-rpi-3-b.dts
&gpio {
spi0_pins: spi0_pins {
brcm,pins = <9 10 11>;
brcm,function = <4>; /* alt0 */
};
spi0_cs_pins: spi0_cs_pins {
brcm,pins = <8 7>;
brcm,function = <1>; /* output */
};
}
&spi0 {
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
spidev0: spidev@0{
compatible = "spidev";
reg = <0>; /* CE0 */
#address-cells = <1>;
#size-cells = <0>;
spi-max-frequency = <125000000>;
};
spidev1: spidev@1{
compatible = "spidev";
reg = <1>; /* CE1 */
#address-cells = <1>;
#size-cells = <0>;
spi-max-frequency = <125000000>;
};
};
spi0
arch\arm\boot\dts\bcm283x.dtsi
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
spi: spi@7e204000 {
compatible = "brcm,bcm2835-spi";
reg = <0x7e204000 0x1000>;
interrupts = <2 22>;
clocks = <&clocks BCM2835_CLOCK_VPU>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
}
arch\arm\boot\dts\bcm270x.dtsi
soc: soc {
spi0: spi@7e204000 {
/* Add alias */
dmas = <&dma 6>, <&dma 7>;
dma-names = "tx", "rx";
};
}
bcm2835-spi
drivers\spi\spi-bcm2835.c
static struct platform_driver bcm2835_spi_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = bcm2835_spi_match,
},
.probe = bcm2835_spi_probe,
.remove = bcm2835_spi_remove,
};
compatible
static const struct of_device_id bcm2835_spi_match[] = {
{ .compatible = "brcm,bcm2835-spi", },
{}
};
probe函数
static int bcm2835_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct bcm2835_spi *bs;
struct resource *res;
int err;
master = spi_alloc_master(&pdev->dev, sizeof(*bs));
if (!master) {
dev_err(&pdev->dev, "spi_alloc_master() failed\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, master);
master->mode_bits = BCM2835_SPI_MODE_BITS;
master->bits_per_word_mask = SPI_BPW_MASK(8);
master->num_chipselect = 3;
master->setup = bcm2835_spi_setup;
master->set_cs = bcm2835_spi_set_cs;
master->transfer_one = bcm2835_spi_transfer_one;
master->handle_err = bcm2835_spi_handle_err;
master->prepare_message = bcm2835_spi_prepare_message;
master->dev.of_node = pdev->dev.of_node;
bs = spi_master_get_devdata(master);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
bs->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(bs->regs)) {
err = PTR_ERR(bs->regs);
goto out_master_put;
}
bs->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(bs->clk)) {
err = PTR_ERR(bs->clk);
dev_err(&pdev->dev, "could not get clk: %d\n", err);
goto out_master_put;
}
bs->irq = platform_get_irq(pdev, 0);
if (bs->irq <= 0) {
dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq);
err = bs->irq ? bs->irq : -ENODEV;
goto out_master_put;
}
clk_prepare_enable(bs->clk);
bcm2835_dma_init(master, &pdev->dev);
/* initialise the hardware with the default polarities */
bcm2835_wr(bs, BCM2835_SPI_CS,
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0,
dev_name(&pdev->dev), master);
if (err) {
dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
goto out_clk_disable;
}
err = devm_spi_register_master(&pdev->dev, master);
if (err) {
dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
goto out_clk_disable;
}
return 0;
out_clk_disable:
clk_disable_unprepare(bs->clk);
out_master_put:
spi_master_put(master);
return err;
}
在probe函数中调用了devm_spi_register_master
spi device
#define devm_spi_register_master(_dev, _ctlr) \
devm_spi_register_controller(_dev, _ctlr)
在drivers/spi/spi.c中,定义了devm_spi_register_controller
/**
* devm_spi_register_controller - register managed SPI master or slave
* controller
* @dev: device managing SPI controller
* @ctlr: initialized controller, originally from spi_alloc_master() or
* spi_alloc_slave()
* Context: can sleep
*
* Register a SPI device as with spi_register_controller() which will
* automatically be unregistered and freed.
*
* Return: zero on success, else a negative error code.
*/
int devm_spi_register_controller(struct device *dev,
struct spi_controller *ctlr)
{
struct spi_controller **ptr;
int ret;
ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = spi_register_controller(ctlr);
if (!ret) {
*ptr = ctlr;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return ret;
}
在spi_register_controller中调用
status = device_add(&ctlr->dev);
spi device的file operations在drivers/spi/spidev.c中定义
static const struct file_operations spidev_fops = {
.owner = THIS_MODULE,
/* REVISIT switch to aio primitives, so that userspace
* gets more complete API coverage. It'll simplify things
* too, except for the locking.
*/
.write = spidev_write,
.read = spidev_read,
.unlocked_ioctl = spidev_ioctl,
.compat_ioctl = spidev_compat_ioctl,
.open = spidev_open,
.release = spidev_release,
.llseek = no_llseek,
};
wiringPi oled例程
wiringPi在用户空间通过SPI master device的API, open, release, ioctl, read, write来驱动oled.
static const char *spiDev0 = "/dev/spidev0.0" ;
static const char *spiDev1 = "/dev/spidev0.1" ;
/*
* wiringPiSPISetupMode:
* Open the SPI device, and set it up, with the mode, etc.
*********************************************************************************
*/
int wiringPiSPISetupMode (int channel, int speed, int mode)
{
int fd ;
mode &= 3 ; // Mode is 0, 1, 2 or 3
channel &= 1 ; // Channel is 0 or 1
if ((fd = open (channel == 0 ? spiDev0 : spiDev1, O_RDWR)) < 0)
return wiringPiFailure (WPI_ALMOST, "Unable to open SPI device: %s\n", strerror (errno)) ;
spiSpeeds [channel] = speed ;
spiFds [channel] = fd ;
// Set SPI parameters.
if (ioctl (fd, SPI_IOC_WR_MODE, &mode) < 0)
return wiringPiFailure (WPI_ALMOST, "SPI Mode Change failure: %s\n", strerror (errno)) ;
if (ioctl (fd, SPI_IOC_WR_BITS_PER_WORD, &spiBPW) < 0)
return wiringPiFailure (WPI_ALMOST, "SPI BPW Change failure: %s\n", strerror (errno)) ;
if (ioctl (fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0)
return wiringPiFailure (WPI_ALMOST, "SPI Speed Change failure: %s\n", strerror (errno)) ;
return fd ;
}
树莓派 devfs sysfs中SPI设备
查看/dev
pi@raspberrypi:~ $ ls /dev | grep spi
spidev0.0
spidev0.1
查看/sys/class
pi@raspberrypi:/sys/class/spi_master $ ls
spi0
pi@raspberrypi:/sys/class/spi_master $ cd spi0
pi@raspberrypi:/sys/class/spi_master/spi0 $ ls
device of_node power spi0.0 spi0.1 statistics subsystem uevent
pi@raspberrypi:/sys/class/spi_master/spi0 $ cd of_node
pi@raspberrypi:/sys/class/spi_master/spi0/of_node $ ls
#address-cells cs-gpios interrupts pinctrl-0 #size-cells status
clocks dma-names name pinctrl-names spidev@0
compatible dmas phandle reg spidev@1
pi@raspberrypi:/sys/class/spi_master/spi0/of_node $ cat compatible
brcm,bcm2835-spi
pi@raspberrypi:/sys/class/spi_master/spi0/of_node $ cd spidev@0
pi@raspberrypi:/sys/class/spi_master/spi0/of_node/spidev@0 $ ls
#address-cells compatible name phandle reg #size-cells spi-max-frequency
pi@raspberrypi:/sys/class/spi_master/spi0/of_node/spidev@0 $ cat compatible
spidev