Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2016 RnDity Sp. z o.o.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <errno.h>
#include <misc/__assert.h>
#include <clock_control/stm32_clock_control.h>

#include "flash_stm32f3x.h"

static int flash_stm32_erase(struct device *dev, off_t offset, size_t size)
{
	uint32_t first_page_addr = 0;
	uint32_t last_page_addr = 0;
	uint16_t no_of_pages = size / CONFIG_FLASH_PAGE_SIZE;
	uint16_t page_index = 0;

	/* Check offset and size alignment. */
	if (((offset % CONFIG_FLASH_PAGE_SIZE) != 0) ||
	    ((size % CONFIG_FLASH_PAGE_SIZE) != 0) ||
	    (no_of_pages == 0)) {
		return -EINVAL;
	}

	/* Find address of the first page to be erased. */
	page_index = offset / CONFIG_FLASH_PAGE_SIZE;

	first_page_addr = CONFIG_FLASH_BASE_ADDRESS +
			  page_index * CONFIG_FLASH_PAGE_SIZE;

	__ASSERT_NO_MSG(IS_FLASH_PROGRAM_ADDRESS(first_page_addr));

	/* Find address of the last page to be erased. */
	page_index = ((offset + size) / CONFIG_FLASH_PAGE_SIZE) - 1;

	last_page_addr = CONFIG_FLASH_BASE_ADDRESS +
			 page_index * CONFIG_FLASH_PAGE_SIZE;

	__ASSERT_NO_MSG(IS_FLASH_PROGRAM_ADDRESS(last_page_addr));

	while (no_of_pages) {
		if (flash_stm32_erase_page(dev, first_page_addr)
				!= FLASH_COMPLETE) {
			return -EINVAL;
		}
		no_of_pages--;
		first_page_addr += CONFIG_FLASH_PAGE_SIZE;
	}

	return 0;
}

static int flash_stm32_read(struct device *dev, off_t offset,
			    void *data, size_t len)
{
	uint32_t address = CONFIG_FLASH_BASE_ADDRESS + offset;

	__ASSERT_NO_MSG(IS_FLASH_PROGRAM_ADDRESS(address));

	flash_stm32_read_data(data, address, len);

	return 0;
}

static int flash_stm32_write(struct device *dev, off_t offset,
			     const void *data, size_t len)
{
	uint16_t halfword = 0;

	uint32_t address =
		CONFIG_FLASH_BASE_ADDRESS + offset;

	uint8_t remainder = 0;

	if ((len % 2) != 0) {
		remainder = 1;
	}

	len = len / 2;

	while (len--) {
		halfword = *((uint8_t *)data++);
		halfword |= *((uint8_t *)data++) << 8;
		if (flash_stm32_program_halfword(dev, address, halfword)
				!= FLASH_COMPLETE) {
			return -EINVAL;
		}
		address += 2;
	}

	if (remainder) {
		halfword = (*((uint16_t *)data)) & 0x00FF;
		if (flash_stm32_program_halfword(dev, address, halfword)
				!= FLASH_COMPLETE) {
			return -EINVAL;
		}
	}

	return 0;
}

static int flash_stm32_protection_set(struct device *dev, bool enable)
{
	if (enable) {
		flash_stm32_lock(dev);
	} else {
		flash_stm32_unlock(dev);
	}

	return 0;
}

static int flash_stm32_init(struct device *dev)
{
	const struct flash_stm32_dev_config *cfg = FLASH_CFG(dev);

	struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);

	if (clock_control_on(clk, (clock_control_subsys_t *) &cfg->pclken) != 0)
		return -ENODEV;

	return 0;
}

static const struct flash_driver_api flash_stm32_api = {
	.read = flash_stm32_read,
	.write = flash_stm32_write,
	.erase = flash_stm32_erase,
	.write_protection = flash_stm32_protection_set,
};

static const struct flash_stm32_dev_config flash_device_config = {
	.base = (uint32_t *)FLASH_R_BASE,
	.pclken = { .bus = STM32_CLOCK_BUS_APB1,
		    .enr =  LL_AHB1_GRP1_PERIPH_FLASH},
};

static struct flash_stm32_dev_data flash_device_data = {

};

DEVICE_AND_API_INIT(flash_stm32, CONFIG_SOC_FLASH_STM32_DEV_NAME,
		    flash_stm32_init,
		    &flash_device_data,
		    &flash_device_config,
		    POST_KERNEL,
		    CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
		    &flash_stm32_api);